summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/CMakeLists.txt1
-rw-r--r--src/plugins/alsa/alsa.json3
-rw-r--r--src/plugins/alsa/alsa.pro23
-rw-r--r--src/plugins/alsa/qalsaaudiodeviceinfo.cpp430
-rw-r--r--src/plugins/alsa/qalsaaudiodeviceinfo.h120
-rw-r--r--src/plugins/alsa/qalsaaudioinput.cpp872
-rw-r--r--src/plugins/alsa/qalsaaudioinput.h185
-rw-r--r--src/plugins/alsa/qalsaaudiooutput.cpp813
-rw-r--r--src/plugins/alsa/qalsaaudiooutput.h164
-rw-r--r--src/plugins/alsa/qalsaplugin.cpp77
-rw-r--r--src/plugins/alsa/qalsaplugin.h68
-rw-r--r--src/plugins/android/android.pro4
-rw-r--r--src/plugins/android/jar/AndroidManifest.xml6
-rw-r--r--src/plugins/android/jar/jar.pro21
-rw-r--r--src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer.java590
-rw-r--r--src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtCameraListener.java216
-rw-r--r--src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtMediaRecorderListener.java67
-rw-r--r--src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtMultimediaUtils.java138
-rw-r--r--src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceHolderCallback.java73
-rw-r--r--src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureHolder.java125
-rw-r--r--src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureListener.java60
-rw-r--r--src/plugins/android/src/android_mediaservice.json4
-rw-r--r--src/plugins/android/src/common/common.pri10
-rw-r--r--src/plugins/android/src/common/qandroidglobal.h52
-rw-r--r--src/plugins/android/src/common/qandroidmultimediautils.cpp152
-rw-r--r--src/plugins/android/src/common/qandroidmultimediautils.h63
-rw-r--r--src/plugins/android/src/common/qandroidvideooutput.cpp504
-rw-r--r--src/plugins/android/src/common/qandroidvideooutput.h145
-rw-r--r--src/plugins/android/src/mediacapture/mediacapture.pri53
-rw-r--r--src/plugins/android/src/mediacapture/qandroidaudioencodersettingscontrol.cpp96
-rw-r--r--src/plugins/android/src/mediacapture/qandroidaudioencodersettingscontrol.h67
-rw-r--r--src/plugins/android/src/mediacapture/qandroidaudioinputselectorcontrol.cpp111
-rw-r--r--src/plugins/android/src/mediacapture/qandroidaudioinputselectorcontrol.h71
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameracapturebufferformatcontrol.cpp64
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameracapturebufferformatcontrol.h60
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameracapturedestinationcontrol.cpp69
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameracapturedestinationcontrol.h65
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameracontrol.cpp112
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameracontrol.h74
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameraexposurecontrol.cpp293
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameraexposurecontrol.h83
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameraflashcontrol.cpp128
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameraflashcontrol.h71
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.cpp309
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.h109
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameraimagecapturecontrol.cpp84
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameraimagecapturecontrol.h69
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameraimageprocessingcontrol.cpp140
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameraimageprocessingcontrol.h75
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcamerainfocontrol.cpp80
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcamerainfocontrol.h60
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameralockscontrol.cpp252
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameralockscontrol.h85
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcamerasession.cpp942
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcamerasession.h205
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.cpp281
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.h72
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcamerazoomcontrol.cpp133
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcamerazoomcontrol.h78
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcaptureservice.cpp249
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcaptureservice.h112
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcapturesession.cpp594
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcapturesession.h182
-rw-r--r--src/plugins/android/src/mediacapture/qandroidimageencodercontrol.cpp93
-rw-r--r--src/plugins/android/src/mediacapture/qandroidimageencodercontrol.h72
-rw-r--r--src/plugins/android/src/mediacapture/qandroidmediacontainercontrol.cpp84
-rw-r--r--src/plugins/android/src/mediacapture/qandroidmediacontainercontrol.h66
-rw-r--r--src/plugins/android/src/mediacapture/qandroidmediarecordercontrol.cpp118
-rw-r--r--src/plugins/android/src/mediacapture/qandroidmediarecordercontrol.h75
-rw-r--r--src/plugins/android/src/mediacapture/qandroidmediavideoprobecontrol.cpp61
-rw-r--r--src/plugins/android/src/mediacapture/qandroidmediavideoprobecontrol.h61
-rw-r--r--src/plugins/android/src/mediacapture/qandroidvideodeviceselectorcontrol.cpp99
-rw-r--r--src/plugins/android/src/mediacapture/qandroidvideodeviceselectorcontrol.h74
-rw-r--r--src/plugins/android/src/mediacapture/qandroidvideoencodersettingscontrol.cpp97
-rw-r--r--src/plugins/android/src/mediacapture/qandroidvideoencodersettingscontrol.h68
-rw-r--r--src/plugins/android/src/mediacapture/qandroidviewfindersettingscontrol.cpp89
-rw-r--r--src/plugins/android/src/mediacapture/qandroidviewfindersettingscontrol.h71
-rw-r--r--src/plugins/android/src/mediaplayer/mediaplayer.pri17
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidaudiorolecontrol.cpp77
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidaudiorolecontrol.h63
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidcustomaudiorolecontrol.cpp87
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidcustomaudiorolecontrol.h63
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp797
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h140
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp76
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.h68
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp111
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaservice.h73
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp248
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h79
-rw-r--r--src/plugins/android/src/qandroidmediaserviceplugin.cpp184
-rw-r--r--src/plugins/android/src/qandroidmediaserviceplugin.h81
-rw-r--r--src/plugins/android/src/src.pro20
-rw-r--r--src/plugins/android/src/wrappers/jni/androidcamera.cpp1712
-rw-r--r--src/plugins/android/src/wrappers/jni/androidcamera.h237
-rw-r--r--src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp193
-rw-r--r--src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h89
-rw-r--r--src/plugins/android/src/wrappers/jni/androidmediaplayer.cpp435
-rw-r--r--src/plugins/android/src/wrappers/jni/androidmediaplayer.h141
-rw-r--r--src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp405
-rw-r--r--src/plugins/android/src/wrappers/jni/androidmediarecorder.h176
-rw-r--r--src/plugins/android/src/wrappers/jni/androidmultimediautils.cpp78
-rw-r--r--src/plugins/android/src/wrappers/jni/androidmultimediautils.h66
-rw-r--r--src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp211
-rw-r--r--src/plugins/android/src/wrappers/jni/androidsurfacetexture.h84
-rw-r--r--src/plugins/android/src/wrappers/jni/androidsurfaceview.cpp195
-rw-r--r--src/plugins/android/src/wrappers/jni/androidsurfaceview.h102
-rw-r--r--src/plugins/android/src/wrappers/jni/jni.pri21
-rw-r--r--src/plugins/audiocapture/audiocapture.json4
-rw-r--r--src/plugins/audiocapture/audiocapture.pro27
-rw-r--r--src/plugins/audiocapture/audiocaptureprobecontrol.cpp62
-rw-r--r--src/plugins/audiocapture/audiocaptureprobecontrol.h61
-rw-r--r--src/plugins/audiocapture/audiocaptureservice.cpp97
-rw-r--r--src/plugins/audiocapture/audiocaptureservice.h74
-rw-r--r--src/plugins/audiocapture/audiocaptureserviceplugin.cpp62
-rw-r--r--src/plugins/audiocapture/audiocaptureserviceplugin.h61
-rw-r--r--src/plugins/audiocapture/audiocapturesession.cpp458
-rw-r--r--src/plugins/audiocapture/audiocapturesession.h194
-rw-r--r--src/plugins/audiocapture/audiocontainercontrol.cpp82
-rw-r--r--src/plugins/audiocapture/audiocontainercontrol.h70
-rw-r--r--src/plugins/audiocapture/audioencodercontrol.cpp174
-rw-r--r--src/plugins/audiocapture/audioencodercontrol.h77
-rw-r--r--src/plugins/audiocapture/audioinputselector.cpp111
-rw-r--r--src/plugins/audiocapture/audioinputselector.h77
-rw-r--r--src/plugins/audiocapture/audiomediarecordercontrol.cpp121
-rw-r--r--src/plugins/audiocapture/audiomediarecordercontrol.h82
-rw-r--r--src/plugins/avfoundation/avfoundation.pro4
-rw-r--r--src/plugins/avfoundation/camera/avfaudioencodersettingscontrol.h75
-rw-r--r--src/plugins/avfoundation/camera/avfaudioencodersettingscontrol.mm226
-rw-r--r--src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.h83
-rw-r--r--src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.mm119
-rw-r--r--src/plugins/avfoundation/camera/avfcamera.json4
-rw-r--r--src/plugins/avfoundation/camera/avfcameracontrol.h83
-rw-r--r--src/plugins/avfoundation/camera/avfcameracontrol.mm134
-rw-r--r--src/plugins/avfoundation/camera/avfcameradebug.h57
-rw-r--r--src/plugins/avfoundation/camera/avfcameradevicecontrol.h85
-rw-r--r--src/plugins/avfoundation/camera/avfcameradevicecontrol.mm123
-rw-r--r--src/plugins/avfoundation/camera/avfcameraexposurecontrol.h89
-rw-r--r--src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm661
-rw-r--r--src/plugins/avfoundation/camera/avfcameraflashcontrol.h83
-rw-r--r--src/plugins/avfoundation/camera/avfcameraflashcontrol.mm233
-rw-r--r--src/plugins/avfoundation/camera/avfcamerafocuscontrol.h87
-rw-r--r--src/plugins/avfoundation/camera/avfcamerafocuscontrol.mm306
-rw-r--r--src/plugins/avfoundation/camera/avfcamerainfocontrol.h59
-rw-r--r--src/plugins/avfoundation/camera/avfcamerainfocontrol.mm60
-rw-r--r--src/plugins/avfoundation/camera/avfcamerametadatacontrol.h69
-rw-r--r--src/plugins/avfoundation/camera/avfcamerametadatacontrol.mm83
-rw-r--r--src/plugins/avfoundation/camera/avfcamerarenderercontrol.h108
-rw-r--r--src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm411
-rw-r--r--src/plugins/avfoundation/camera/avfcameraservice.h132
-rw-r--r--src/plugins/avfoundation/camera/avfcameraservice.mm267
-rw-r--r--src/plugins/avfoundation/camera/avfcameraserviceplugin.h76
-rw-r--r--src/plugins/avfoundation/camera/avfcameraserviceplugin.mm113
-rw-r--r--src/plugins/avfoundation/camera/avfcamerasession.h144
-rw-r--r--src/plugins/avfoundation/camera/avfcamerasession.mm492
-rw-r--r--src/plugins/avfoundation/camera/avfcamerautility.h179
-rw-r--r--src/plugins/avfoundation/camera/avfcamerautility.mm575
-rw-r--r--src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h117
-rw-r--r--src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm599
-rw-r--r--src/plugins/avfoundation/camera/avfcamerawindowcontrol.h129
-rw-r--r--src/plugins/avfoundation/camera/avfcamerawindowcontrol.mm262
-rw-r--r--src/plugins/avfoundation/camera/avfcamerazoomcontrol.h85
-rw-r--r--src/plugins/avfoundation/camera/avfcamerazoomcontrol.mm179
-rw-r--r--src/plugins/avfoundation/camera/avfcapturedestinationcontrol.h63
-rw-r--r--src/plugins/avfoundation/camera/avfcapturedestinationcontrol.mm62
-rw-r--r--src/plugins/avfoundation/camera/avfimagecapturecontrol.h100
-rw-r--r--src/plugins/avfoundation/camera/avfimagecapturecontrol.mm266
-rw-r--r--src/plugins/avfoundation/camera/avfimageencodercontrol.h85
-rw-r--r--src/plugins/avfoundation/camera/avfimageencodercontrol.mm240
-rw-r--r--src/plugins/avfoundation/camera/avfmediaassetwriter.h75
-rw-r--r--src/plugins/avfoundation/camera/avfmediaassetwriter.mm514
-rw-r--r--src/plugins/avfoundation/camera/avfmediacontainercontrol.h69
-rw-r--r--src/plugins/avfoundation/camera/avfmediacontainercontrol.mm112
-rw-r--r--src/plugins/avfoundation/camera/avfmediarecordercontrol.h120
-rw-r--r--src/plugins/avfoundation/camera/avfmediarecordercontrol.mm430
-rw-r--r--src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h115
-rw-r--r--src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm414
-rw-r--r--src/plugins/avfoundation/camera/avfmediavideoprobecontrol.h60
-rw-r--r--src/plugins/avfoundation/camera/avfmediavideoprobecontrol.mm61
-rw-r--r--src/plugins/avfoundation/camera/avfstoragelocation.h71
-rw-r--r--src/plugins/avfoundation/camera/avfstoragelocation.mm136
-rw-r--r--src/plugins/avfoundation/camera/avfvideoencodersettingscontrol.h88
-rw-r--r--src/plugins/avfoundation/camera/avfvideoencodersettingscontrol.mm385
-rw-r--r--src/plugins/avfoundation/camera/camera.pro94
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfdisplaylink.h90
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfdisplaylink.mm241
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfmediaplayer.json4
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.h99
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.mm192
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.h74
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.mm161
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.h70
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.mm134
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfmediaplayerserviceplugin.h77
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfmediaplayerserviceplugin.mm107
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h161
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm1067
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.h92
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm276
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfvideoframerenderer_ios.h120
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfvideoframerenderer_ios.mm307
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfvideooutput.h60
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfvideooutput.mm42
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.h92
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.mm301
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfvideowidget.h85
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfvideowidget.mm187
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.h95
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.mm145
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfvideowindowcontrol.h118
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfvideowindowcontrol.mm255
-rw-r--r--src/plugins/avfoundation/mediaplayer/mediaplayer.pro76
-rw-r--r--src/plugins/common/evr.pri20
-rw-r--r--src/plugins/common/evr/evrcustompresenter.cpp2062
-rw-r--r--src/plugins/common/evr/evrcustompresenter.h377
-rw-r--r--src/plugins/common/evr/evrd3dpresentengine.cpp413
-rw-r--r--src/plugins/common/evr/evrd3dpresentengine.h152
-rw-r--r--src/plugins/common/evr/evrdefs.cpp48
-rw-r--r--src/plugins/common/evr/evrdefs.h353
-rw-r--r--src/plugins/common/evr/evrhelpers.cpp186
-rw-r--r--src/plugins/common/evr/evrhelpers.h101
-rw-r--r--src/plugins/common/evr/evrvideowindowcontrol.cpp362
-rw-r--r--src/plugins/common/evr/evrvideowindowcontrol.h109
-rw-r--r--src/plugins/coreaudio/coreaudio.json3
-rw-r--r--src/plugins/coreaudio/coreaudio.pro39
-rw-r--r--src/plugins/coreaudio/coreaudiodeviceinfo.h82
-rw-r--r--src/plugins/coreaudio/coreaudiodeviceinfo.mm346
-rw-r--r--src/plugins/coreaudio/coreaudioinput.h266
-rw-r--r--src/plugins/coreaudio/coreaudioinput.mm1005
-rw-r--r--src/plugins/coreaudio/coreaudiooutput.h205
-rw-r--r--src/plugins/coreaudio/coreaudiooutput.mm752
-rw-r--r--src/plugins/coreaudio/coreaudioplugin.h66
-rw-r--r--src/plugins/coreaudio/coreaudioplugin.mm82
-rw-r--r--src/plugins/coreaudio/coreaudiosessionmanager.h125
-rw-r--r--src/plugins/coreaudio/coreaudiosessionmanager.mm472
-rw-r--r--src/plugins/coreaudio/coreaudioutils.h93
-rw-r--r--src/plugins/coreaudio/coreaudioutils.mm198
-rw-r--r--src/plugins/directshow/camera/camera.pri37
-rw-r--r--src/plugins/directshow/camera/directshowcameracapturebufferformatcontrol.cpp65
-rw-r--r--src/plugins/directshow/camera/directshowcameracapturebufferformatcontrol.h60
-rw-r--r--src/plugins/directshow/camera/directshowcameracapturedestinationcontrol.cpp68
-rw-r--r--src/plugins/directshow/camera/directshowcameracapturedestinationcontrol.h66
-rw-r--r--src/plugins/directshow/camera/directshowcameraexposurecontrol.cpp413
-rw-r--r--src/plugins/directshow/camera/directshowcameraexposurecontrol.h100
-rw-r--r--src/plugins/directshow/camera/directshowcameraglobal.h231
-rw-r--r--src/plugins/directshow/camera/directshowcameraimageencodercontrol.cpp95
-rw-r--r--src/plugins/directshow/camera/directshowcameraimageencodercontrol.h70
-rw-r--r--src/plugins/directshow/camera/directshowcamerazoomcontrol.cpp209
-rw-r--r--src/plugins/directshow/camera/directshowcamerazoomcontrol.h88
-rw-r--r--src/plugins/directshow/camera/dscameracontrol.cpp120
-rw-r--r--src/plugins/directshow/camera/dscameracontrol.h80
-rw-r--r--src/plugins/directshow/camera/dscameraimageprocessingcontrol.cpp78
-rw-r--r--src/plugins/directshow/camera/dscameraimageprocessingcontrol.h70
-rw-r--r--src/plugins/directshow/camera/dscameraservice.cpp161
-rw-r--r--src/plugins/directshow/camera/dscameraservice.h91
-rw-r--r--src/plugins/directshow/camera/dscamerasession.cpp1177
-rw-r--r--src/plugins/directshow/camera/dscamerasession.h242
-rw-r--r--src/plugins/directshow/camera/dscameraviewfindersettingscontrol.cpp66
-rw-r--r--src/plugins/directshow/camera/dscameraviewfindersettingscontrol.h65
-rw-r--r--src/plugins/directshow/camera/dsimagecapturecontrol.cpp88
-rw-r--r--src/plugins/directshow/camera/dsimagecapturecontrol.h69
-rw-r--r--src/plugins/directshow/camera/dsvideodevicecontrol.cpp186
-rw-r--r--src/plugins/directshow/camera/dsvideodevicecontrol.h79
-rw-r--r--src/plugins/directshow/camera/dsvideorenderer.cpp67
-rw-r--r--src/plugins/directshow/camera/dsvideorenderer.h68
-rw-r--r--src/plugins/directshow/common/common.pri29
-rw-r--r--src/plugins/directshow/common/directshowaudioprobecontrol.cpp57
-rw-r--r--src/plugins/directshow/common/directshowaudioprobecontrol.h64
-rw-r--r--src/plugins/directshow/common/directshowbasefilter.cpp251
-rw-r--r--src/plugins/directshow/common/directshowbasefilter.h95
-rw-r--r--src/plugins/directshow/common/directshoweventloop.cpp150
-rw-r--r--src/plugins/directshow/common/directshoweventloop.h80
-rw-r--r--src/plugins/directshow/common/directshowglobal.h160
-rw-r--r--src/plugins/directshow/common/directshowmediatype.cpp352
-rw-r--r--src/plugins/directshow/common/directshowmediatype.h97
-rw-r--r--src/plugins/directshow/common/directshowmediatypeenum.cpp113
-rw-r--r--src/plugins/directshow/common/directshowmediatypeenum.h77
-rw-r--r--src/plugins/directshow/common/directshowobject.h64
-rw-r--r--src/plugins/directshow/common/directshowpin.cpp696
-rw-r--r--src/plugins/directshow/common/directshowpin.h180
-rw-r--r--src/plugins/directshow/common/directshowpinenum.cpp125
-rw-r--r--src/plugins/directshow/common/directshowpinenum.h78
-rw-r--r--src/plugins/directshow/common/directshowsamplegrabber.cpp197
-rw-r--r--src/plugins/directshow/common/directshowsamplegrabber.h89
-rw-r--r--src/plugins/directshow/common/directshowutils.cpp346
-rw-r--r--src/plugins/directshow/common/directshowutils.h91
-rw-r--r--src/plugins/directshow/common/directshowvideobuffer.cpp88
-rw-r--r--src/plugins/directshow/common/directshowvideobuffer.h70
-rw-r--r--src/plugins/directshow/common/directshowvideoprobecontrol.cpp69
-rw-r--r--src/plugins/directshow/common/directshowvideoprobecontrol.h67
-rw-r--r--src/plugins/directshow/directshow.json4
-rw-r--r--src/plugins/directshow/directshow.pro32
-rw-r--r--src/plugins/directshow/dsserviceplugin.cpp148
-rw-r--r--src/plugins/directshow/dsserviceplugin.h73
-rw-r--r--src/plugins/directshow/player/directshowaudioendpointcontrol.cpp161
-rw-r--r--src/plugins/directshow/player/directshowaudioendpointcontrol.h82
-rw-r--r--src/plugins/directshow/player/directshowevrvideowindowcontrol.cpp66
-rw-r--r--src/plugins/directshow/player/directshowevrvideowindowcontrol.h63
-rw-r--r--src/plugins/directshow/player/directshowioreader.cpp462
-rw-r--r--src/plugins/directshow/player/directshowioreader.h120
-rw-r--r--src/plugins/directshow/player/directshowiosource.cpp530
-rw-r--r--src/plugins/directshow/player/directshowiosource.h140
-rw-r--r--src/plugins/directshow/player/directshowmetadatacontrol.cpp697
-rw-r--r--src/plugins/directshow/player/directshowmetadatacontrol.h84
-rw-r--r--src/plugins/directshow/player/directshowplayercontrol.cpp397
-rw-r--r--src/plugins/directshow/player/directshowplayercontrol.h153
-rw-r--r--src/plugins/directshow/player/directshowplayerservice.cpp1812
-rw-r--r--src/plugins/directshow/player/directshowplayerservice.h240
-rw-r--r--src/plugins/directshow/player/directshowvideorenderercontrol.cpp121
-rw-r--r--src/plugins/directshow/player/directshowvideorenderercontrol.h84
-rw-r--r--src/plugins/directshow/player/player.pri45
-rw-r--r--src/plugins/directshow/player/videosurfacefilter.cpp810
-rw-r--r--src/plugins/directshow/player/videosurfacefilter.h161
-rw-r--r--src/plugins/directshow/player/vmr9videowindowcontrol.cpp326
-rw-r--r--src/plugins/directshow/player/vmr9videowindowcontrol.h108
-rw-r--r--src/plugins/gstreamer/audiodecoder/audiodecoder.json4
-rw-r--r--src/plugins/gstreamer/audiodecoder/audiodecoder.pro24
-rw-r--r--src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodercontrol.cpp139
-rw-r--r--src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodercontrol.h95
-rw-r--r--src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderservice.cpp73
-rw-r--r--src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderservice.h69
-rw-r--r--src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderserviceplugin.cpp99
-rw-r--r--src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderserviceplugin.h72
-rw-r--r--src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp614
-rw-r--r--src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.h157
-rw-r--r--src/plugins/gstreamer/camerabin/camerabin.json4
-rw-r--r--src/plugins/gstreamer/camerabin/camerabin.pro85
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinaudioencoder.cpp165
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinaudioencoder.h109
-rw-r--r--src/plugins/gstreamer/camerabin/camerabincapturebufferformat.cpp76
-rw-r--r--src/plugins/gstreamer/camerabin/camerabincapturebufferformat.h74
-rw-r--r--src/plugins/gstreamer/camerabin/camerabincapturedestination.cpp76
-rw-r--r--src/plugins/gstreamer/camerabin/camerabincapturedestination.h70
-rw-r--r--src/plugins/gstreamer/camerabin/camerabincontainer.cpp152
-rw-r--r--src/plugins/gstreamer/camerabin/camerabincontainer.h94
-rw-r--r--src/plugins/gstreamer/camerabin/camerabincontrol.cpp309
-rw-r--r--src/plugins/gstreamer/camerabin/camerabincontrol.h101
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinexposure.cpp285
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinexposure.h75
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinflash.cpp109
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinflash.h73
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinfocus.cpp540
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinfocus.h134
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp380
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinimagecapture.h123
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinimageencoder.cpp88
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinimageencoder.h86
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp441
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinimageprocessing.h108
-rw-r--r--src/plugins/gstreamer/camerabin/camerabininfocontrol.cpp69
-rw-r--r--src/plugins/gstreamer/camerabin/camerabininfocontrol.h65
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinlocks.cpp259
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinlocks.h98
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinmetadata.cpp234
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinmetadata.h71
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinrecorder.cpp298
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinrecorder.h94
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinresourcepolicy.cpp242
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinresourcepolicy.h95
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinservice.cpp271
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinservice.h104
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinserviceplugin.cpp163
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinserviceplugin.h89
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinsession.cpp1587
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinsession.h291
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.cpp316
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.h90
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinvideoencoder.cpp239
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinvideoencoder.h109
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinviewfindersettings.cpp118
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinviewfindersettings.h67
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinviewfindersettings2.cpp73
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinviewfindersettings2.h67
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinzoom.cpp147
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinzoom.h78
-rw-r--r--src/plugins/gstreamer/common.pri15
-rw-r--r--src/plugins/gstreamer/gstreamer.json4
-rw-r--r--src/plugins/gstreamer/gstreamer.pro10
-rw-r--r--src/plugins/gstreamer/mediacapture/mediacapture.json4
-rw-r--r--src/plugins/gstreamer/mediacapture/mediacapture.pro52
-rw-r--r--src/plugins/gstreamer/mediacapture/mediacapturecamera.json4
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreameraudioencode.cpp239
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreameraudioencode.h94
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.cpp197
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.h94
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.cpp158
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.h71
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp227
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h103
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamercaptureserviceplugin.cpp142
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamercaptureserviceplugin.h95
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp1032
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h244
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.cpp113
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.h74
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamerimageencode.cpp88
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamerimageencode.h81
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.cpp60
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.h80
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp372
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.h97
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.cpp286
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.h83
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp296
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamervideoencode.h97
-rw-r--r--src/plugins/gstreamer/mediaplayer/mediaplayer.json4
-rw-r--r--src/plugins/gstreamer/mediaplayer/mediaplayer.pro26
-rw-r--r--src/plugins/gstreamer/mediaplayer/qgstreameravailabilitycontrol.cpp64
-rw-r--r--src/plugins/gstreamer/mediaplayer/qgstreameravailabilitycontrol.h65
-rw-r--r--src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp190
-rw-r--r--src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.h72
-rw-r--r--src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp193
-rw-r--r--src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h99
-rw-r--r--src/plugins/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.cpp113
-rw-r--r--src/plugins/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.h78
-rw-r--r--src/plugins/gstreamer/mediaplayer/qgstreamerstreamscontrol.cpp87
-rw-r--r--src/plugins/gstreamer/mediaplayer/qgstreamerstreamscontrol.h71
-rw-r--r--src/plugins/m3u/m3u.json3
-rw-r--r--src/plugins/m3u/m3u.pro12
-rw-r--r--src/plugins/m3u/qm3uhandler.cpp229
-rw-r--r--src/plugins/m3u/qm3uhandler.h68
-rw-r--r--src/plugins/multimedia/CMakeLists.txt24
-rw-r--r--src/plugins/multimedia/android/CMakeLists.txt62
-rw-r--r--src/plugins/multimedia/android/android.json3
-rw-r--r--src/plugins/multimedia/android/audio/qandroidaudiodecoder.cpp437
-rw-r--r--src/plugins/multimedia/android/audio/qandroidaudiodecoder_p.h118
-rw-r--r--src/plugins/multimedia/android/common/qandroidaudioinput.cpp47
-rw-r--r--src/plugins/multimedia/android/common/qandroidaudioinput_p.h47
-rw-r--r--src/plugins/multimedia/android/common/qandroidaudiooutput_p.h30
-rw-r--r--src/plugins/multimedia/android/common/qandroidglobal_p.h28
-rw-r--r--src/plugins/multimedia/android/common/qandroidmultimediautils.cpp125
-rw-r--r--src/plugins/multimedia/android/common/qandroidmultimediautils_p.h40
-rw-r--r--src/plugins/multimedia/android/common/qandroidvideooutput.cpp468
-rw-r--r--src/plugins/multimedia/android/common/qandroidvideooutput_p.h93
-rw-r--r--src/plugins/multimedia/android/common/qandroidvideosink.cpp35
-rw-r--r--src/plugins/multimedia/android/common/qandroidvideosink_p.h41
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidcamera.cpp562
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidcamera_p.h99
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidcamerasession.cpp808
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidcamerasession_p.h166
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidcapturesession.cpp473
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidcapturesession_p.h158
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidimagecapture.cpp73
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidimagecapture_p.h48
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidmediacapturesession.cpp115
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidmediacapturesession_p.h66
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidmediaencoder.cpp72
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidmediaencoder_p.h50
-rw-r--r--src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer.cpp999
-rw-r--r--src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer_p.h127
-rw-r--r--src/plugins/multimedia/android/mediaplayer/qandroidmetadata.cpp163
-rw-r--r--src/plugins/multimedia/android/mediaplayer/qandroidmetadata_p.h47
-rw-r--r--src/plugins/multimedia/android/qandroidformatsinfo.cpp160
-rw-r--r--src/plugins/multimedia/android/qandroidformatsinfo_p.h40
-rw-r--r--src/plugins/multimedia/android/qandroidintegration.cpp136
-rw-r--r--src/plugins/multimedia/android/qandroidintegration_p.h48
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidcamera.cpp1797
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidcamera_p.h208
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidmediametadataretriever.cpp136
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidmediametadataretriever_p.h66
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidmediaplayer.cpp535
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidmediaplayer_p.h135
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidmediarecorder.cpp337
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidmediarecorder_p.h161
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidmultimediautils.cpp43
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidmultimediautils_p.h40
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidsurfacetexture.cpp152
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidsurfacetexture_p.h61
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidsurfaceview.cpp152
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidsurfaceview_p.h78
-rw-r--r--src/plugins/multimedia/darwin/CMakeLists.txt70
-rw-r--r--src/plugins/multimedia/darwin/avfaudiodecoder.mm544
-rw-r--r--src/plugins/multimedia/darwin/avfaudiodecoder_p.h99
-rw-r--r--src/plugins/multimedia/darwin/avfvideobuffer.mm207
-rw-r--r--src/plugins/multimedia/darwin/avfvideobuffer_p.h64
-rw-r--r--src/plugins/multimedia/darwin/avfvideosink.mm228
-rw-r--r--src/plugins/multimedia/darwin/avfvideosink_p.h99
-rw-r--r--src/plugins/multimedia/darwin/camera/avfaudiopreviewdelegate.mm98
-rw-r--r--src/plugins/multimedia/darwin/camera/avfaudiopreviewdelegate_p.h40
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcamera.mm89
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcamera_p.h48
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcameradebug_p.h26
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcamerarenderer.mm292
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcamerarenderer_p.h95
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcameraservice.mm169
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcameraservice_p.h84
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcamerasession.mm513
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcamerasession_p.h132
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcamerautility.mm730
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcamerautility_p.h165
-rw-r--r--src/plugins/multimedia/darwin/camera/avfimagecapture.mm385
-rw-r--r--src/plugins/multimedia/darwin/camera/avfimagecapture_p.h81
-rw-r--r--src/plugins/multimedia/darwin/camera/avfmediaassetwriter.mm556
-rw-r--r--src/plugins/multimedia/darwin/camera/avfmediaassetwriter_p.h54
-rw-r--r--src/plugins/multimedia/darwin/camera/avfmediaencoder.mm664
-rw-r--r--src/plugins/multimedia/darwin/camera/avfmediaencoder_p.h96
-rw-r--r--src/plugins/multimedia/darwin/camera/qavfcamerabase.mm1084
-rw-r--r--src/plugins/multimedia/darwin/camera/qavfcamerabase_p.h110
-rw-r--r--src/plugins/multimedia/darwin/common/avfmetadata.mm382
-rw-r--r--src/plugins/multimedia/darwin/common/avfmetadata_p.h37
-rw-r--r--src/plugins/multimedia/darwin/darwin.json3
-rw-r--r--src/plugins/multimedia/darwin/mediaplayer/avfdisplaylink.mm207
-rw-r--r--src/plugins/multimedia/darwin/mediaplayer/avfdisplaylink_p.h65
-rw-r--r--src/plugins/multimedia/darwin/mediaplayer/avfmediaplayer.mm1270
-rw-r--r--src/plugins/multimedia/darwin/mediaplayer/avfmediaplayer_p.h151
-rw-r--r--src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol.mm222
-rw-r--r--src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol_p.h72
-rw-r--r--src/plugins/multimedia/darwin/qavfhelpers.mm143
-rw-r--r--src/plugins/multimedia/darwin/qavfhelpers_p.h41
-rw-r--r--src/plugins/multimedia/darwin/qdarwinformatsinfo.mm211
-rw-r--r--src/plugins/multimedia/darwin/qdarwinformatsinfo_p.h38
-rw-r--r--src/plugins/multimedia/darwin/qdarwinintegration.mm93
-rw-r--r--src/plugins/multimedia/darwin/qdarwinintegration_p.h45
-rw-r--r--src/plugins/multimedia/ffmpeg/CMakeLists.txt260
-rw-r--r--src/plugins/multimedia/ffmpeg/cmake/QtAddFFmpegStubs.cmake199
-rw-r--r--src/plugins/multimedia/ffmpeg/cmake/QtDeployFFmpeg.cmake43
-rw-r--r--src/plugins/multimedia/ffmpeg/ffmpeg.json3
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegaudiorenderer.cpp407
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegaudiorenderer_p.h132
-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.cpp390
-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.cpp79
-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.cpp224
-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.mm349
-rw-r--r--src/plugins/multimedia/ffmpeg/qavfcamera_p.h90
-rw-r--r--src/plugins/multimedia/ffmpeg/qavfsamplebufferdelegate.mm224
-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.mm203
-rw-r--r--src/plugins/multimedia/ffmpeg/qcgwindowcapture_p.h43
-rw-r--r--src/plugins/multimedia/ffmpeg/qeglfsscreencapture.cpp180
-rw-r--r--src/plugins/multimedia/ffmpeg/qeglfsscreencapture_p.h48
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeg.cpp645
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeg_p.h280
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder.cpp247
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder_p.h68
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegaudioinput.cpp195
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegaudioinput_p.h57
-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/qffmpegconverter.cpp272
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegconverter_p.h30
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegdefs_p.h41
-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.cpp504
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11.cpp309
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11_p.h104
-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.h130
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi.cpp364
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi_p.h48
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox.mm288
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox_p.h63
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp271
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegimagecapture_p.h71
-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.cpp318
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession_p.h112
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo.cpp517
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo_p.h52
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediaintegration.cpp375
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediaintegration_p.h57
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediametadata.cpp182
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediametadata_p.h35
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediaplayer.cpp411
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediaplayer_p.h119
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediarecorder.cpp200
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediarecorder_p.h73
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegplaybackengine.cpp649
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegplaybackengine_p.h234
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp112
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegresampler_p.h62
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi.cpp467
-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/qffmpegthread.cpp53
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegthread_p.h96
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp363
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegvideobuffer_p.h71
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegvideosink.cpp34
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegvideosink_p.h44
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegwindowcapture_uwp.cpp508
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegwindowcapture_uwp_p.h46
-rw-r--r--src/plugins/multimedia/ffmpeg/qgdiwindowcapture.cpp197
-rw-r--r--src/plugins/multimedia/ffmpeg/qgdiwindowcapture_p.h43
-rw-r--r--src/plugins/multimedia/ffmpeg/qgrabwindowsurfacecapture.cpp221
-rw-r--r--src/plugins/multimedia/ffmpeg/qgrabwindowsurfacecapture_p.h48
-rw-r--r--src/plugins/multimedia/ffmpeg/qopenglvideobuffer.cpp96
-rw-r--r--src/plugins/multimedia/ffmpeg/qopenglvideobuffer_p.h44
-rw-r--r--src/plugins/multimedia/ffmpeg/qv4l2camera.cpp708
-rw-r--r--src/plugins/multimedia/ffmpeg/qv4l2camera_p.h133
-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.cpp330
-rw-r--r--src/plugins/multimedia/ffmpeg/qwindowscamera_p.h45
-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.cpp343
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder_p.h77
-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.cpp362
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderoptions_p.h32
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread.cpp40
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread_p.h72
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencodinginitializer.cpp165
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencodinginitializer_p.h77
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer.cpp64
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer_p.h41
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine.cpp278
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine_p.h121
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengineutils.cpp63
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengineutils_p.h81
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder.cpp245
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder_p.h64
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoderutils.cpp214
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoderutils_p.h64
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoframeencoder.cpp477
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoframeencoder_p.h96
-rw-r--r--src/plugins/multimedia/ffmpeg/symbolstubs/openssl3.ver7
-rw-r--r--src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-crypto.cpp6
-rw-r--r--src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-ssl.cpp300
-rw-r--r--src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-va-drm.cpp14
-rw-r--r--src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-va-x11.cpp14
-rw-r--r--src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-va.cpp150
-rw-r--r--src/plugins/multimedia/ffmpeg/symbolstubs/va.ver7
-rw-r--r--src/plugins/multimedia/gstreamer/CMakeLists.txt73
-rw-r--r--src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder.cpp535
-rw-r--r--src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder_p.h116
-rw-r--r--src/plugins/multimedia/gstreamer/audio/qgstreameraudiodevice.cpp77
-rw-r--r--src/plugins/multimedia/gstreamer/audio/qgstreameraudiodevice_p.h52
-rw-r--r--src/plugins/multimedia/gstreamer/common/qglist_helper_p.h82
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgst.cpp1392
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgst_debug.cpp565
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgst_debug_p.h74
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgst_handle_types_p.h270
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgst_p.h853
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstappsource.cpp319
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstappsource_p.h96
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstpipeline.cpp414
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstpipeline_p.h119
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreameraudioinput.cpp137
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreameraudioinput_p.h62
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp138
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput_p.h63
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamerbufferprobe.cpp88
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamerbufferprobe_p.h56
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp1114
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer_p.h199
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamermessage_p.h55
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamermetadata.cpp489
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamermetadata_p.h35
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamervideooutput.cpp220
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamervideooutput_p.h80
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamervideooverlay.cpp218
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamervideooverlay_p.h74
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamervideosink.cpp314
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamervideosink_p.h76
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstsubtitlesink.cpp155
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstsubtitlesink_p.h70
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstutils.cpp141
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstutils_p.h41
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstvideobuffer.cpp393
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstvideobuffer_p.h55
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstvideorenderersink.cpp499
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstvideorenderersink_p.h138
-rw-r--r--src/plugins/multimedia/gstreamer/gstreamer.json3
-rw-r--r--src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera.cpp771
-rw-r--r--src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera_p.h152
-rw-r--r--src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp450
-rw-r--r--src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h109
-rw-r--r--src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp326
-rw-r--r--src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture_p.h97
-rw-r--r--src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder.cpp419
-rw-r--r--src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder_p.h91
-rw-r--r--src/plugins/multimedia/gstreamer/qgstreamerformatinfo.cpp445
-rw-r--r--src/plugins/multimedia/gstreamer/qgstreamerformatinfo_p.h44
-rw-r--r--src/plugins/multimedia/gstreamer/qgstreamerintegration.cpp242
-rw-r--r--src/plugins/multimedia/gstreamer/qgstreamerintegration_p.h79
-rw-r--r--src/plugins/multimedia/gstreamer/qgstreamerplugin.cpp28
-rw-r--r--src/plugins/multimedia/gstreamer/qgstreamervideodevices.cpp158
-rw-r--r--src/plugins/multimedia/gstreamer/qgstreamervideodevices_p.h54
-rw-r--r--src/plugins/multimedia/qnx/CMakeLists.txt39
-rw-r--r--src/plugins/multimedia/qnx/camera/qqnxcamera.cpp820
-rw-r--r--src/plugins/multimedia/qnx/camera/qqnxcamera_p.h201
-rw-r--r--src/plugins/multimedia/qnx/camera/qqnxcameraframebuffer.cpp299
-rw-r--r--src/plugins/multimedia/qnx/camera/qqnxcameraframebuffer_p.h60
-rw-r--r--src/plugins/multimedia/qnx/camera/qqnxcamerahandle_p.h102
-rw-r--r--src/plugins/multimedia/qnx/camera/qqnximagecapture.cpp257
-rw-r--r--src/plugins/multimedia/qnx/camera/qqnximagecapture_p.h63
-rw-r--r--src/plugins/multimedia/qnx/camera/qqnxplatformcamera.cpp426
-rw-r--r--src/plugins/multimedia/qnx/camera/qqnxplatformcamera_p.h113
-rw-r--r--src/plugins/multimedia/qnx/capture/qqnxaudiorecorder.cpp284
-rw-r--r--src/plugins/multimedia/qnx/capture/qqnxaudiorecorder_p.h103
-rw-r--r--src/plugins/multimedia/qnx/capture/qqnxmediacapturesession.cpp121
-rw-r--r--src/plugins/multimedia/qnx/capture/qqnxmediacapturesession_p.h67
-rw-r--r--src/plugins/multimedia/qnx/capture/qqnxmediarecorder.cpp115
-rw-r--r--src/plugins/multimedia/qnx/capture/qqnxmediarecorder_p.h51
-rw-r--r--src/plugins/multimedia/qnx/common/mmrenderertypes.h95
-rw-r--r--src/plugins/multimedia/qnx/common/qqnxaudioinput.cpp25
-rw-r--r--src/plugins/multimedia/qnx/common/qqnxaudioinput_p.h33
-rw-r--r--src/plugins/multimedia/qnx/common/qqnxaudiooutput.cpp52
-rw-r--r--src/plugins/multimedia/qnx/common/qqnxaudiooutput_p.h39
-rw-r--r--src/plugins/multimedia/qnx/common/qqnxmediaeventthread.cpp98
-rw-r--r--src/plugins/multimedia/qnx/common/qqnxmediaeventthread_p.h55
-rw-r--r--src/plugins/multimedia/qnx/common/qqnxwindowgrabber.cpp435
-rw-r--r--src/plugins/multimedia/qnx/common/qqnxwindowgrabber_p.h114
-rw-r--r--src/plugins/multimedia/qnx/mediaplayer/qqnxmediametadata.cpp262
-rw-r--r--src/plugins/multimedia/qnx/mediaplayer/qqnxmediametadata_p.h74
-rw-r--r--src/plugins/multimedia/qnx/mediaplayer/qqnxmediaplayer.cpp887
-rw-r--r--src/plugins/multimedia/qnx/mediaplayer/qqnxmediaplayer_p.h167
-rw-r--r--src/plugins/multimedia/qnx/mediaplayer/qqnxmediautil.cpp126
-rw-r--r--src/plugins/multimedia/qnx/mediaplayer/qqnxmediautil_p.h32
-rw-r--r--src/plugins/multimedia/qnx/mediaplayer/qqnxvideosink.cpp26
-rw-r--r--src/plugins/multimedia/qnx/mediaplayer/qqnxvideosink_p.h41
-rw-r--r--src/plugins/multimedia/qnx/qnx.json3
-rw-r--r--src/plugins/multimedia/qnx/qqnxformatinfo.cpp36
-rw-r--r--src/plugins/multimedia/qnx/qqnxformatinfo_p.h33
-rw-r--r--src/plugins/multimedia/qnx/qqnxmediaintegration.cpp79
-rw-r--r--src/plugins/multimedia/qnx/qqnxmediaintegration_p.h50
-rw-r--r--src/plugins/multimedia/qnx/qqnxvideodevices.cpp111
-rw-r--r--src/plugins/multimedia/qnx/qqnxvideodevices_p.h32
-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.cpp1071
-rw-r--r--src/plugins/multimedia/wasm/common/qwasmvideooutput_p.h153
-rw-r--r--src/plugins/multimedia/wasm/mediacapture/qwasmcamera.cpp478
-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.txt69
-rw-r--r--src/plugins/multimedia/windows/common/mfmetadata.cpp408
-rw-r--r--src/plugins/multimedia/windows/common/mfmetadata_p.h30
-rw-r--r--src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol.cpp225
-rw-r--r--src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol_p.h75
-rw-r--r--src/plugins/multimedia/windows/decoder/mfdecodersourcereader.cpp103
-rw-r--r--src/plugins/multimedia/windows/decoder/mfdecodersourcereader_p.h63
-rw-r--r--src/plugins/multimedia/windows/evr/evrcustompresenter.cpp1849
-rw-r--r--src/plugins/multimedia/windows/evr/evrcustompresenter_p.h357
-rw-r--r--src/plugins/multimedia/windows/evr/evrd3dpresentengine.cpp699
-rw-r--r--src/plugins/multimedia/windows/evr/evrd3dpresentengine_p.h153
-rw-r--r--src/plugins/multimedia/windows/evr/evrhelpers.cpp140
-rw-r--r--src/plugins/multimedia/windows/evr/evrhelpers_p.h93
-rw-r--r--src/plugins/multimedia/windows/evr/evrvideowindowcontrol.cpp228
-rw-r--r--src/plugins/multimedia/windows/evr/evrvideowindowcontrol_p.h72
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowscamera.cpp101
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowscamera_p.h55
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsimagecapture.cpp207
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsimagecapture_p.h64
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsmediacapture.cpp109
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsmediacapture_p.h62
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader.cpp1019
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader_p.h154
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession.cpp376
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession_p.h100
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder.cpp225
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder_p.h71
-rw-r--r--src/plugins/multimedia/windows/mfstream.cpp326
-rw-r--r--src/plugins/multimedia/windows/mfstream_p.h124
-rw-r--r--src/plugins/multimedia/windows/player/mfactivate.cpp17
-rw-r--r--src/plugins/multimedia/windows/player/mfactivate_p.h202
-rw-r--r--src/plugins/multimedia/windows/player/mfevrvideowindowcontrol.cpp55
-rw-r--r--src/plugins/multimedia/windows/player/mfevrvideowindowcontrol_p.h38
-rw-r--r--src/plugins/multimedia/windows/player/mfplayercontrol.cpp306
-rw-r--r--src/plugins/multimedia/windows/player/mfplayercontrol_p.h103
-rw-r--r--src/plugins/multimedia/windows/player/mfplayersession.cpp1736
-rw-r--r--src/plugins/multimedia/windows/player/mfplayersession_p.h240
-rw-r--r--src/plugins/multimedia/windows/player/mfvideorenderercontrol.cpp152
-rw-r--r--src/plugins/multimedia/windows/player/mfvideorenderercontrol_p.h47
-rw-r--r--src/plugins/multimedia/windows/qwindowsformatinfo.cpp187
-rw-r--r--src/plugins/multimedia/windows/qwindowsformatinfo_p.h31
-rw-r--r--src/plugins/multimedia/windows/qwindowsintegration.cpp96
-rw-r--r--src/plugins/multimedia/windows/qwindowsintegration_p.h51
-rw-r--r--src/plugins/multimedia/windows/qwindowsvideodevices.cpp228
-rw-r--r--src/plugins/multimedia/windows/qwindowsvideodevices_p.h44
-rw-r--r--src/plugins/multimedia/windows/sourceresolver.cpp294
-rw-r--r--src/plugins/multimedia/windows/sourceresolver_p.h83
-rw-r--r--src/plugins/multimedia/windows/windows.json3
-rw-r--r--src/plugins/opensles/opensles.json3
-rw-r--r--src/plugins/opensles/opensles.pro25
-rw-r--r--src/plugins/opensles/qopenslesaudioinput.cpp548
-rw-r--r--src/plugins/opensles/qopenslesaudioinput.h133
-rw-r--r--src/plugins/opensles/qopenslesaudiooutput.cpp733
-rw-r--r--src/plugins/opensles/qopenslesaudiooutput.h159
-rw-r--r--src/plugins/opensles/qopenslesdeviceinfo.cpp113
-rw-r--r--src/plugins/opensles/qopenslesdeviceinfo.h75
-rw-r--r--src/plugins/opensles/qopenslesengine.cpp369
-rw-r--r--src/plugins/opensles/qopenslesengine.h90
-rw-r--r--src/plugins/opensles/qopenslesplugin.cpp81
-rw-r--r--src/plugins/opensles/qopenslesplugin.h73
-rw-r--r--src/plugins/plugins.pro54
-rw-r--r--src/plugins/pulseaudio/pulseaudio.json3
-rw-r--r--src/plugins/pulseaudio/pulseaudio.pro25
-rw-r--r--src/plugins/pulseaudio/qaudiodeviceinfo_pulse.cpp103
-rw-r--r--src/plugins/pulseaudio/qaudiodeviceinfo_pulse.h90
-rw-r--r--src/plugins/pulseaudio/qaudioinput_pulse.cpp682
-rw-r--r--src/plugins/pulseaudio/qaudioinput_pulse.h161
-rw-r--r--src/plugins/pulseaudio/qaudiooutput_pulse.cpp739
-rw-r--r--src/plugins/pulseaudio/qaudiooutput_pulse.h166
-rw-r--r--src/plugins/pulseaudio/qpulseaudioengine.cpp482
-rw-r--r--src/plugins/pulseaudio/qpulseaudioengine.h129
-rw-r--r--src/plugins/pulseaudio/qpulseaudioplugin.cpp85
-rw-r--r--src/plugins/pulseaudio/qpulseaudioplugin.h73
-rw-r--r--src/plugins/pulseaudio/qpulsehelpers.cpp211
-rw-r--r--src/plugins/pulseaudio/qpulsehelpers.h71
-rw-r--r--src/plugins/qnx-audio/audio/audio.pro22
-rw-r--r--src/plugins/qnx-audio/audio/qnx_audio.json3
-rw-r--r--src/plugins/qnx-audio/audio/qnxaudiodeviceinfo.cpp148
-rw-r--r--src/plugins/qnx-audio/audio/qnxaudiodeviceinfo.h72
-rw-r--r--src/plugins/qnx-audio/audio/qnxaudioinput.cpp453
-rw-r--r--src/plugins/qnx-audio/audio/qnxaudioinput.h138
-rw-r--r--src/plugins/qnx-audio/audio/qnxaudiooutput.cpp578
-rw-r--r--src/plugins/qnx-audio/audio/qnxaudiooutput.h150
-rw-r--r--src/plugins/qnx-audio/audio/qnxaudioplugin.cpp91
-rw-r--r--src/plugins/qnx-audio/audio/qnxaudioplugin.h67
-rw-r--r--src/plugins/qnx-audio/audio/qnxaudioutils.cpp128
-rw-r--r--src/plugins/qnx-audio/audio/qnxaudioutils.h55
-rw-r--r--src/plugins/qnx-audio/qnx-audio.pro3
-rw-r--r--src/plugins/qnx/camera/bbcameraaudioencodersettingscontrol.cpp87
-rw-r--r--src/plugins/qnx/camera/bbcameraaudioencodersettingscontrol.h66
-rw-r--r--src/plugins/qnx/camera/bbcameracapturebufferformatcontrol.cpp64
-rw-r--r--src/plugins/qnx/camera/bbcameracapturebufferformatcontrol.h59
-rw-r--r--src/plugins/qnx/camera/bbcameracapturedestinationcontrol.cpp68
-rw-r--r--src/plugins/qnx/camera/bbcameracapturedestinationcontrol.h64
-rw-r--r--src/plugins/qnx/camera/bbcameracontrol.cpp90
-rw-r--r--src/plugins/qnx/camera/bbcameracontrol.h71
-rw-r--r--src/plugins/qnx/camera/bbcameraexposurecontrol.cpp232
-rw-r--r--src/plugins/qnx/camera/bbcameraexposurecontrol.h71
-rw-r--r--src/plugins/qnx/camera/bbcameraflashcontrol.cpp111
-rw-r--r--src/plugins/qnx/camera/bbcameraflashcontrol.h66
-rw-r--r--src/plugins/qnx/camera/bbcamerafocuscontrol.cpp329
-rw-r--r--src/plugins/qnx/camera/bbcamerafocuscontrol.h77
-rw-r--r--src/plugins/qnx/camera/bbcameraimagecapturecontrol.cpp83
-rw-r--r--src/plugins/qnx/camera/bbcameraimagecapturecontrol.h68
-rw-r--r--src/plugins/qnx/camera/bbcameraimageprocessingcontrol.cpp144
-rw-r--r--src/plugins/qnx/camera/bbcameraimageprocessingcontrol.h65
-rw-r--r--src/plugins/qnx/camera/bbcamerainfocontrol.cpp81
-rw-r--r--src/plugins/qnx/camera/bbcamerainfocontrol.h63
-rw-r--r--src/plugins/qnx/camera/bbcameralockscontrol.cpp256
-rw-r--r--src/plugins/qnx/camera/bbcameralockscontrol.h84
-rw-r--r--src/plugins/qnx/camera/bbcameramediarecordercontrol.cpp156
-rw-r--r--src/plugins/qnx/camera/bbcameramediarecordercontrol.h74
-rw-r--r--src/plugins/qnx/camera/bbcameraorientationhandler.cpp113
-rw-r--r--src/plugins/qnx/camera/bbcameraorientationhandler.h69
-rw-r--r--src/plugins/qnx/camera/bbcameraservice.cpp143
-rw-r--r--src/plugins/qnx/camera/bbcameraservice.h104
-rw-r--r--src/plugins/qnx/camera/bbcamerasession.cpp1154
-rw-r--r--src/plugins/qnx/camera/bbcamerasession.h215
-rw-r--r--src/plugins/qnx/camera/bbcameravideoencodersettingscontrol.cpp88
-rw-r--r--src/plugins/qnx/camera/bbcameravideoencodersettingscontrol.h67
-rw-r--r--src/plugins/qnx/camera/bbcameraviewfindersettingscontrol.cpp245
-rw-r--r--src/plugins/qnx/camera/bbcameraviewfindersettingscontrol.h64
-rw-r--r--src/plugins/qnx/camera/bbcamerazoomcontrol.cpp155
-rw-r--r--src/plugins/qnx/camera/bbcamerazoomcontrol.h77
-rw-r--r--src/plugins/qnx/camera/bbimageencodercontrol.cpp79
-rw-r--r--src/plugins/qnx/camera/bbimageencodercontrol.h66
-rw-r--r--src/plugins/qnx/camera/bbmediastoragelocation.cpp118
-rw-r--r--src/plugins/qnx/camera/bbmediastoragelocation.h64
-rw-r--r--src/plugins/qnx/camera/bbvideodeviceselectorcontrol.cpp145
-rw-r--r--src/plugins/qnx/camera/bbvideodeviceselectorcontrol.h78
-rw-r--r--src/plugins/qnx/camera/bbvideorenderercontrol.cpp62
-rw-r--r--src/plugins/qnx/camera/bbvideorenderercontrol.h63
-rw-r--r--src/plugins/qnx/camera/camera.pri52
-rw-r--r--src/plugins/qnx/common/common.pri7
-rw-r--r--src/plugins/qnx/common/windowgrabber.cpp419
-rw-r--r--src/plugins/qnx/common/windowgrabber.h144
-rw-r--r--src/plugins/qnx/mediaplayer/mediaplayer.pri28
-rw-r--r--src/plugins/qnx/mediaplayer/mmrendereraudiorolecontrol.cpp68
-rw-r--r--src/plugins/qnx/mediaplayer/mmrendereraudiorolecontrol.h63
-rw-r--r--src/plugins/qnx/mediaplayer/mmrenderercustomaudiorolecontrol.cpp67
-rw-r--r--src/plugins/qnx/mediaplayer/mmrenderercustomaudiorolecontrol.h63
-rw-r--r--src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp653
-rw-r--r--src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.h180
-rw-r--r--src/plugins/qnx/mediaplayer/mmrenderermediaplayerservice.cpp164
-rw-r--r--src/plugins/qnx/mediaplayer/mmrenderermediaplayerservice.h80
-rw-r--r--src/plugins/qnx/mediaplayer/mmrenderermetadata.cpp298
-rw-r--r--src/plugins/qnx/mediaplayer/mmrenderermetadata.h99
-rw-r--r--src/plugins/qnx/mediaplayer/mmrenderermetadatareadercontrol.cpp170
-rw-r--r--src/plugins/qnx/mediaplayer/mmrenderermetadatareadercontrol.h66
-rw-r--r--src/plugins/qnx/mediaplayer/mmrendererplayervideorenderercontrol.cpp218
-rw-r--r--src/plugins/qnx/mediaplayer/mmrendererplayervideorenderercontrol.h84
-rw-r--r--src/plugins/qnx/mediaplayer/mmrendererutil.cpp246
-rw-r--r--src/plugins/qnx/mediaplayer/mmrendererutil.h60
-rw-r--r--src/plugins/qnx/mediaplayer/mmrenderervideowindowcontrol.cpp413
-rw-r--r--src/plugins/qnx/mediaplayer/mmrenderervideowindowcontrol.h118
-rw-r--r--src/plugins/qnx/mediaplayer/mmreventmediaplayercontrol.cpp229
-rw-r--r--src/plugins/qnx/mediaplayer/mmreventmediaplayercontrol.h84
-rw-r--r--src/plugins/qnx/mediaplayer/mmreventthread.cpp121
-rw-r--r--src/plugins/qnx/mediaplayer/mmreventthread.h79
-rw-r--r--src/plugins/qnx/neutrino_mediaservice.json4
-rw-r--r--src/plugins/qnx/neutrinoserviceplugin.cpp68
-rw-r--r--src/plugins/qnx/neutrinoserviceplugin.h63
-rw-r--r--src/plugins/qnx/qnx.pro15
-rw-r--r--src/plugins/resourcepolicy/resourcepolicy.json4
-rw-r--r--src/plugins/resourcepolicy/resourcepolicy.pro22
-rw-r--r--src/plugins/resourcepolicy/resourcepolicyimpl.cpp100
-rw-r--r--src/plugins/resourcepolicy/resourcepolicyimpl.h67
-rw-r--r--src/plugins/resourcepolicy/resourcepolicyint.cpp405
-rw-r--r--src/plugins/resourcepolicy/resourcepolicyint.h106
-rw-r--r--src/plugins/resourcepolicy/resourcepolicyplugin.cpp62
-rw-r--r--src/plugins/resourcepolicy/resourcepolicyplugin.h59
-rw-r--r--src/plugins/videonode/CMakeLists.txt8
-rw-r--r--src/plugins/videonode/imx6/CMakeLists.txt43
-rw-r--r--src/plugins/videonode/imx6/imx6.pro28
-rw-r--r--src/plugins/videonode/imx6/imx6.qrc6
-rw-r--r--src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp59
-rw-r--r--src/plugins/videonode/imx6/qsgvivantevideomaterial.h42
-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.cpp90
-rw-r--r--src/plugins/videonode/imx6/qsgvivantevideonode.h56
-rw-r--r--src/plugins/videonode/imx6/qsgvivantevideonodefactory.cpp50
-rw-r--r--src/plugins/videonode/imx6/qsgvivantevideonodefactory.h44
-rwxr-xr-xsrc/plugins/videonode/imx6/shaders/compile.bat38
-rw-r--r--src/plugins/videonode/videonode.pro6
-rw-r--r--src/plugins/windowsaudio/qwindowsaudiodeviceinfo.cpp469
-rw-r--r--src/plugins/windowsaudio/qwindowsaudiodeviceinfo.h109
-rw-r--r--src/plugins/windowsaudio/qwindowsaudioinput.cpp732
-rw-r--r--src/plugins/windowsaudio/qwindowsaudioinput.h177
-rw-r--r--src/plugins/windowsaudio/qwindowsaudiooutput.cpp676
-rw-r--r--src/plugins/windowsaudio/qwindowsaudiooutput.h168
-rw-r--r--src/plugins/windowsaudio/qwindowsaudioplugin.cpp77
-rw-r--r--src/plugins/windowsaudio/qwindowsaudioplugin.h68
-rw-r--r--src/plugins/windowsaudio/qwindowsaudioutils.cpp117
-rw-r--r--src/plugins/windowsaudio/qwindowsaudioutils.h73
-rw-r--r--src/plugins/windowsaudio/windowsaudio.json3
-rw-r--r--src/plugins/windowsaudio/windowsaudio.pro25
-rw-r--r--src/plugins/wmf/decoder/decoder.pri14
-rw-r--r--src/plugins/wmf/decoder/mfaudiodecodercontrol.cpp486
-rw-r--r--src/plugins/wmf/decoder/mfaudiodecodercontrol.h108
-rw-r--r--src/plugins/wmf/decoder/mfdecoderservice.cpp65
-rw-r--r--src/plugins/wmf/decoder/mfdecoderservice.h56
-rw-r--r--src/plugins/wmf/decoder/mfdecodersourcereader.cpp197
-rw-r--r--src/plugins/wmf/decoder/mfdecodersourcereader.h89
-rw-r--r--src/plugins/wmf/mfstream.cpp361
-rw-r--r--src/plugins/wmf/mfstream.h148
-rw-r--r--src/plugins/wmf/player/mfactivate.cpp87
-rw-r--r--src/plugins/wmf/player/mfactivate.h212
-rw-r--r--src/plugins/wmf/player/mfaudioendpointcontrol.cpp180
-rw-r--r--src/plugins/wmf/player/mfaudioendpointcontrol.h82
-rw-r--r--src/plugins/wmf/player/mfaudioprobecontrol.cpp75
-rw-r--r--src/plugins/wmf/player/mfaudioprobecontrol.h66
-rw-r--r--src/plugins/wmf/player/mfevrvideowindowcontrol.cpp91
-rw-r--r--src/plugins/wmf/player/mfevrvideowindowcontrol.h63
-rw-r--r--src/plugins/wmf/player/mfmetadatacontrol.cpp431
-rw-r--r--src/plugins/wmf/player/mfmetadatacontrol.h72
-rw-r--r--src/plugins/wmf/player/mfplayercontrol.cpp316
-rw-r--r--src/plugins/wmf/player/mfplayercontrol.h122
-rw-r--r--src/plugins/wmf/player/mfplayerservice.cpp167
-rw-r--r--src/plugins/wmf/player/mfplayerservice.h87
-rw-r--r--src/plugins/wmf/player/mfplayersession.cpp1818
-rw-r--r--src/plugins/wmf/player/mfplayersession.h239
-rw-r--r--src/plugins/wmf/player/mftvideo.cpp753
-rw-r--r--src/plugins/wmf/player/mftvideo.h117
-rw-r--r--src/plugins/wmf/player/mfvideoprobecontrol.cpp54
-rw-r--r--src/plugins/wmf/player/mfvideoprobecontrol.h59
-rw-r--r--src/plugins/wmf/player/mfvideorenderercontrol.cpp2426
-rw-r--r--src/plugins/wmf/player/mfvideorenderercontrol.h81
-rw-r--r--src/plugins/wmf/player/player.pri34
-rw-r--r--src/plugins/wmf/player/samplegrabber.cpp173
-rw-r--r--src/plugins/wmf/player/samplegrabber.h96
-rw-r--r--src/plugins/wmf/sourceresolver.cpp325
-rw-r--r--src/plugins/wmf/sourceresolver.h104
-rw-r--r--src/plugins/wmf/wmf.json4
-rw-r--r--src/plugins/wmf/wmf.pro28
-rw-r--r--src/plugins/wmf/wmfserviceplugin.cpp117
-rw-r--r--src/plugins/wmf/wmfserviceplugin.h71
1022 files changed, 78299 insertions, 105325 deletions
diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
new file mode 100644
index 000000000..6f1602cee
--- /dev/null
+++ b/src/plugins/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(multimedia)
diff --git a/src/plugins/alsa/alsa.json b/src/plugins/alsa/alsa.json
deleted file mode 100644
index c2b22dfec..000000000
--- a/src/plugins/alsa/alsa.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "Keys": ["alsa"]
-}
diff --git a/src/plugins/alsa/alsa.pro b/src/plugins/alsa/alsa.pro
deleted file mode 100644
index 4012bb8f6..000000000
--- a/src/plugins/alsa/alsa.pro
+++ /dev/null
@@ -1,23 +0,0 @@
-TARGET = qtaudio_alsa
-QT += multimedia-private
-
-QMAKE_USE += alsa
-
-HEADERS += \
- qalsaplugin.h \
- qalsaaudiodeviceinfo.h \
- qalsaaudioinput.h \
- qalsaaudiooutput.h
-
-SOURCES += \
- qalsaplugin.cpp \
- qalsaaudiodeviceinfo.cpp \
- qalsaaudioinput.cpp \
- qalsaaudiooutput.cpp
-
-OTHER_FILES += \
- alsa.json
-
-PLUGIN_TYPE = audio
-PLUGIN_CLASS_NAME = QAlsaPlugin
-load(qt_plugin)
diff --git a/src/plugins/alsa/qalsaaudiodeviceinfo.cpp b/src/plugins/alsa/qalsaaudiodeviceinfo.cpp
deleted file mode 100644
index 5cfcb27e0..000000000
--- a/src/plugins/alsa/qalsaaudiodeviceinfo.cpp
+++ /dev/null
@@ -1,430 +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$
-**
-****************************************************************************/
-
-//
-// 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.
-//
-// INTERNAL USE ONLY: Do NOT use for any other purpose.
-//
-
-#include "qalsaaudiodeviceinfo.h"
-
-#include <alsa/version.h>
-
-QT_BEGIN_NAMESPACE
-
-QAlsaAudioDeviceInfo::QAlsaAudioDeviceInfo(const QByteArray &dev, QAudio::Mode mode)
-{
- handle = 0;
-
- device = QLatin1String(dev);
- this->mode = mode;
-
- checkSurround();
-}
-
-QAlsaAudioDeviceInfo::~QAlsaAudioDeviceInfo()
-{
- close();
-}
-
-bool QAlsaAudioDeviceInfo::isFormatSupported(const QAudioFormat& format) const
-{
- return testSettings(format);
-}
-
-QAudioFormat QAlsaAudioDeviceInfo::preferredFormat() const
-{
- QAudioFormat nearest;
- if(mode == QAudio::AudioOutput) {
- nearest.setSampleRate(44100);
- nearest.setChannelCount(2);
- nearest.setByteOrder(QAudioFormat::LittleEndian);
- nearest.setSampleType(QAudioFormat::SignedInt);
- nearest.setSampleSize(16);
- nearest.setCodec(QLatin1String("audio/pcm"));
- } else {
- nearest.setSampleRate(8000);
- nearest.setChannelCount(1);
- nearest.setSampleType(QAudioFormat::UnSignedInt);
- nearest.setSampleSize(8);
- nearest.setCodec(QLatin1String("audio/pcm"));
- if(!testSettings(nearest)) {
- nearest.setChannelCount(2);
- nearest.setSampleSize(16);
- nearest.setSampleType(QAudioFormat::SignedInt);
- }
- }
- return nearest;
-}
-
-QString QAlsaAudioDeviceInfo::deviceName() const
-{
- return device;
-}
-
-QStringList QAlsaAudioDeviceInfo::supportedCodecs()
-{
- updateLists();
- return codecz;
-}
-
-QList<int> QAlsaAudioDeviceInfo::supportedSampleRates()
-{
- updateLists();
- return sampleRatez;
-}
-
-QList<int> QAlsaAudioDeviceInfo::supportedChannelCounts()
-{
- updateLists();
- return channelz;
-}
-
-QList<int> QAlsaAudioDeviceInfo::supportedSampleSizes()
-{
- updateLists();
- return sizez;
-}
-
-QList<QAudioFormat::Endian> QAlsaAudioDeviceInfo::supportedByteOrders()
-{
- updateLists();
- return byteOrderz;
-}
-
-QList<QAudioFormat::SampleType> QAlsaAudioDeviceInfo::supportedSampleTypes()
-{
- updateLists();
- return typez;
-}
-
-QByteArray QAlsaAudioDeviceInfo::defaultDevice(QAudio::Mode mode)
-{
- const auto &devices = availableDevices(mode);
- if (devices.size() == 0)
- return QByteArray();
-
- return devices.first();
-}
-
-bool QAlsaAudioDeviceInfo::open()
-{
- int err = 0;
- QString dev;
-
- if (!availableDevices(mode).contains(device.toLocal8Bit()))
- return false;
-
-#if SND_LIB_VERSION < 0x1000e // 1.0.14
- if (device.compare(QLatin1String("default")) != 0)
- dev = deviceFromCardName(device);
- else
-#endif
- dev = device;
-
- if(mode == QAudio::AudioOutput) {
- err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0);
- } else {
- err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0);
- }
- if(err < 0) {
- handle = 0;
- return false;
- }
- return true;
-}
-
-void QAlsaAudioDeviceInfo::close()
-{
- if(handle)
- snd_pcm_close(handle);
- handle = 0;
-}
-
-bool QAlsaAudioDeviceInfo::testSettings(const QAudioFormat& format) const
-{
- // Set nearest to closest settings that do work.
- // See if what is in settings will work (return value).
- int err = -1;
- snd_pcm_t* pcmHandle;
- snd_pcm_hw_params_t *params;
- QString dev;
-
-#if SND_LIB_VERSION < 0x1000e // 1.0.14
- if (device.compare(QLatin1String("default")) != 0)
- dev = deviceFromCardName(device);
- else
-#endif
- dev = device;
-
- snd_pcm_stream_t stream = mode == QAudio::AudioOutput
- ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE;
-
- if (snd_pcm_open(&pcmHandle, dev.toLocal8Bit().constData(), stream, 0) < 0)
- return false;
-
- snd_pcm_nonblock(pcmHandle, 0);
- snd_pcm_hw_params_alloca(&params);
- snd_pcm_hw_params_any(pcmHandle, params);
-
- // set the values!
- snd_pcm_hw_params_set_channels(pcmHandle, params, format.channelCount());
- snd_pcm_hw_params_set_rate(pcmHandle, params, format.sampleRate(), 0);
-
- snd_pcm_format_t pcmFormat = SND_PCM_FORMAT_UNKNOWN;
- switch (format.sampleSize()) {
- case 8:
- if (format.sampleType() == QAudioFormat::SignedInt)
- pcmFormat = SND_PCM_FORMAT_S8;
- else if (format.sampleType() == QAudioFormat::UnSignedInt)
- pcmFormat = SND_PCM_FORMAT_U8;
- break;
- case 16:
- if (format.sampleType() == QAudioFormat::SignedInt) {
- pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian
- ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_S16_BE;
- } else if (format.sampleType() == QAudioFormat::UnSignedInt) {
- pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian
- ? SND_PCM_FORMAT_U16_LE : SND_PCM_FORMAT_U16_BE;
- }
- break;
- case 32:
- if (format.sampleType() == QAudioFormat::SignedInt) {
- pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian
- ? SND_PCM_FORMAT_S32_LE : SND_PCM_FORMAT_S32_BE;
- } else if (format.sampleType() == QAudioFormat::UnSignedInt) {
- pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian
- ? SND_PCM_FORMAT_U32_LE : SND_PCM_FORMAT_U32_BE;
- } else if (format.sampleType() == QAudioFormat::Float) {
- pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian
- ? SND_PCM_FORMAT_FLOAT_LE : SND_PCM_FORMAT_FLOAT_BE;
- }
- }
-
- if (pcmFormat != SND_PCM_FORMAT_UNKNOWN)
- err = snd_pcm_hw_params_set_format(pcmHandle, params, pcmFormat);
-
- // For now, just accept only audio/pcm codec
- if (!format.codec().startsWith(QLatin1String("audio/pcm")))
- err = -1;
-
- if (err >= 0 && format.channelCount() != -1) {
- err = snd_pcm_hw_params_test_channels(pcmHandle, params, format.channelCount());
- if (err >= 0)
- err = snd_pcm_hw_params_set_channels(pcmHandle, params, format.channelCount());
- }
-
- if (err >= 0 && format.sampleRate() != -1) {
- err = snd_pcm_hw_params_test_rate(pcmHandle, params, format.sampleRate(), 0);
- if (err >= 0)
- err = snd_pcm_hw_params_set_rate(pcmHandle, params, format.sampleRate(), 0);
- }
-
- if (err >= 0 && pcmFormat != SND_PCM_FORMAT_UNKNOWN)
- err = snd_pcm_hw_params_set_format(pcmHandle, params, pcmFormat);
-
- if (err >= 0)
- err = snd_pcm_hw_params(pcmHandle, params);
-
- snd_pcm_close(pcmHandle);
-
- return (err == 0);
-}
-
-void QAlsaAudioDeviceInfo::updateLists()
-{
- // redo all lists based on current settings
- sampleRatez.clear();
- channelz.clear();
- sizez.clear();
- byteOrderz.clear();
- typez.clear();
- codecz.clear();
-
- if(!handle)
- open();
-
- if(!handle)
- return;
-
- for(int i=0; i<(int)MAX_SAMPLE_RATES; i++) {
- //if(snd_pcm_hw_params_test_rate(handle, params, SAMPLE_RATES[i], dir) == 0)
- sampleRatez.append(SAMPLE_RATES[i]);
- }
- channelz.append(1);
- channelz.append(2);
- if (surround40) channelz.append(4);
- if (surround51) channelz.append(6);
- if (surround71) channelz.append(8);
- sizez.append(8);
- sizez.append(16);
- sizez.append(32);
- byteOrderz.append(QAudioFormat::LittleEndian);
- byteOrderz.append(QAudioFormat::BigEndian);
- typez.append(QAudioFormat::SignedInt);
- typez.append(QAudioFormat::UnSignedInt);
- typez.append(QAudioFormat::Float);
- codecz.append(QLatin1String("audio/pcm"));
- close();
-}
-
-QList<QByteArray> QAlsaAudioDeviceInfo::availableDevices(QAudio::Mode mode)
-{
- QList<QByteArray> devices;
- bool hasDefault = false;
-
-#if SND_LIB_VERSION >= 0x1000e // 1.0.14
- 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) {
- qWarning() << "no alsa devices available";
- return devices;
- }
- n = hints;
-
- if(mode == QAudio::AudioInput) {
- filter = "Input";
- } else {
- filter = "Output";
- }
-
- 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))) {
- devices.append(name);
- if (strcmp(name, "default") == 0)
- hasDefault = true;
- }
-
- free(descr);
- free(io);
- }
- free(name);
- ++n;
- }
- snd_device_name_free_hint(hints);
-#else
- int idx = 0;
- char* name;
-
- while(snd_card_get_name(idx,&name) == 0) {
- devices.append(name);
- if (strcmp(name, "default") == 0)
- hasDefault = true;
- idx++;
- }
-#endif
-
- if (!hasDefault && devices.size() > 0)
- devices.prepend("default");
-
- return devices;
-}
-
-void QAlsaAudioDeviceInfo::checkSurround()
-{
- surround40 = false;
- surround51 = false;
- surround71 = false;
-
- void **hints, **n;
- char *name, *descr, *io;
-
- if(snd_device_name_hint(-1, "pcm", &hints) < 0)
- return;
-
- n = hints;
-
- while (*n != NULL) {
- name = snd_device_name_get_hint(*n, "NAME");
- descr = snd_device_name_get_hint(*n, "DESC");
- io = snd_device_name_get_hint(*n, "IOID");
- if((name != NULL) && (descr != NULL)) {
- QString deviceName = QLatin1String(name);
- if (mode == QAudio::AudioOutput) {
- if(deviceName.contains(QLatin1String("surround40")))
- surround40 = true;
- if(deviceName.contains(QLatin1String("surround51")))
- surround51 = true;
- if(deviceName.contains(QLatin1String("surround71")))
- surround71 = true;
- }
- }
- if(name != NULL)
- free(name);
- if(descr != NULL)
- free(descr);
- if(io != NULL)
- free(io);
- ++n;
- }
- snd_device_name_free_hint(hints);
-}
-
-QString QAlsaAudioDeviceInfo::deviceFromCardName(const QString &card)
-{
- int idx = 0;
- char *name;
-
- QStringView shortName = QStringView{card}.mid(card.indexOf(QLatin1String("="), 0) + 1);
-
- while (snd_card_get_name(idx, &name) == 0) {
- if (shortName.compare(QLatin1String(name)) == 0)
- break;
- idx++;
- }
-
- return QString(QLatin1String("hw:%1,0")).arg(idx);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/alsa/qalsaaudiodeviceinfo.h b/src/plugins/alsa/qalsaaudiodeviceinfo.h
deleted file mode 100644
index cdf08bfab..000000000
--- a/src/plugins/alsa/qalsaaudiodeviceinfo.h
+++ /dev/null
@@ -1,120 +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$
-**
-****************************************************************************/
-
-//
-// 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.
-//
-
-
-#ifndef QALSAAUDIODEVICEINFO_H
-#define QALSAAUDIODEVICEINFO_H
-
-#include <alsa/asoundlib.h>
-
-#include <QtCore/qbytearray.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qlist.h>
-#include <QtCore/qdebug.h>
-
-#include <QtMultimedia/qaudio.h>
-#include <QtMultimedia/qaudiodeviceinfo.h>
-#include <QtMultimedia/qaudiosystem.h>
-
-QT_BEGIN_NAMESPACE
-
-
-const unsigned int MAX_SAMPLE_RATES = 5;
-const unsigned int SAMPLE_RATES[] =
- { 8000, 11025, 22050, 44100, 48000 };
-
-class QAlsaAudioDeviceInfo : public QAbstractAudioDeviceInfo
-{
- Q_OBJECT
-public:
- QAlsaAudioDeviceInfo(const QByteArray &dev,QAudio::Mode mode);
- ~QAlsaAudioDeviceInfo();
-
- bool testSettings(const QAudioFormat& format) const;
- void updateLists();
- QAudioFormat preferredFormat() const override;
- bool isFormatSupported(const QAudioFormat& format) const override;
- QString deviceName() const override;
- QStringList supportedCodecs() override;
- QList<int> supportedSampleRates() override;
- QList<int> supportedChannelCounts() override;
- QList<int> supportedSampleSizes() override;
- QList<QAudioFormat::Endian> supportedByteOrders() override;
- QList<QAudioFormat::SampleType> supportedSampleTypes() override;
- static QByteArray defaultDevice(QAudio::Mode mode);
- static QList<QByteArray> availableDevices(QAudio::Mode);
- static QString deviceFromCardName(const QString &card);
-
-private:
- bool open();
- void close();
-
- void checkSurround();
- bool surround40;
- bool surround51;
- bool surround71;
-
- QString device;
- QAudio::Mode mode;
- QAudioFormat nearest;
- QList<int> sampleRatez;
- QList<int> channelz;
- QList<int> sizez;
- QList<QAudioFormat::Endian> byteOrderz;
- QStringList codecz;
- QList<QAudioFormat::SampleType> typez;
- snd_pcm_t* handle;
- snd_pcm_hw_params_t *params;
-};
-
-QT_END_NAMESPACE
-
-
-#endif // QALSAAUDIODEVICEINFO_H
diff --git a/src/plugins/alsa/qalsaaudioinput.cpp b/src/plugins/alsa/qalsaaudioinput.cpp
deleted file mode 100644
index bff23dfe1..000000000
--- a/src/plugins/alsa/qalsaaudioinput.cpp
+++ /dev/null
@@ -1,872 +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$
-**
-****************************************************************************/
-
-//
-// 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.
-//
-// INTERNAL USE ONLY: Do NOT use for any other purpose.
-//
-
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qvarlengtharray.h>
-#include <QtMultimedia/private/qaudiohelpers_p.h>
-#include "qalsaaudioinput.h"
-#include "qalsaaudiodeviceinfo.h"
-
-QT_BEGIN_NAMESPACE
-
-//#define DEBUG_AUDIO 1
-
-QAlsaAudioInput::QAlsaAudioInput(const QByteArray &device)
-{
- bytesAvailable = 0;
- handle = 0;
- access = SND_PCM_ACCESS_RW_INTERLEAVED;
- pcmformat = SND_PCM_FORMAT_S16;
- buffer_size = 0;
- period_size = 0;
- buffer_time = 100000;
- period_time = 20000;
- totalTimeValue = 0;
- intervalTime = 1000;
- errorState = QAudio::NoError;
- deviceState = QAudio::StoppedState;
- audioSource = 0;
- pullMode = true;
- resuming = false;
-
- m_volume = 1.0f;
-
- m_device = device;
-
- timer = new QTimer(this);
- connect(timer,SIGNAL(timeout()),SLOT(userFeed()));
-}
-
-QAlsaAudioInput::~QAlsaAudioInput()
-{
- close();
- disconnect(timer, SIGNAL(timeout()));
- QCoreApplication::processEvents();
- delete timer;
-}
-
-void QAlsaAudioInput::setVolume(qreal vol)
-{
- m_volume = vol;
-}
-
-qreal QAlsaAudioInput::volume() const
-{
- return m_volume;
-}
-
-QAudio::Error QAlsaAudioInput::error() const
-{
- return errorState;
-}
-
-QAudio::State QAlsaAudioInput::state() const
-{
- return deviceState;
-}
-
-void QAlsaAudioInput::setFormat(const QAudioFormat& fmt)
-{
- if (deviceState == QAudio::StoppedState)
- settings = fmt;
-}
-
-QAudioFormat QAlsaAudioInput::format() const
-{
- return settings;
-}
-
-int QAlsaAudioInput::xrun_recovery(int err)
-{
- int count = 0;
- bool reset = false;
-
- // ESTRPIPE is not available in all OSes where ALSA is available
- int estrpipe = EIO;
-#ifdef ESTRPIPE
- estrpipe = ESTRPIPE;
-#endif
-
- if(err == -EPIPE) {
- errorState = QAudio::UnderrunError;
- err = snd_pcm_prepare(handle);
- if(err < 0)
- reset = true;
- else {
- bytesAvailable = checkBytesReady();
- if (bytesAvailable <= 0)
- reset = true;
- }
- } else if ((err == -estrpipe)||(err == -EIO)) {
- errorState = QAudio::IOError;
- while((err = snd_pcm_resume(handle)) == -EAGAIN){
- usleep(100);
- count++;
- if(count > 5) {
- reset = true;
- break;
- }
- }
- if(err < 0) {
- err = snd_pcm_prepare(handle);
- if(err < 0)
- reset = true;
- }
- }
- if(reset) {
- close();
- open();
- snd_pcm_prepare(handle);
- return 0;
- }
- return err;
-}
-
-int QAlsaAudioInput::setFormat()
-{
- snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
-
- if(settings.sampleSize() == 8) {
- format = SND_PCM_FORMAT_U8;
- } else if(settings.sampleSize() == 16) {
- if(settings.sampleType() == QAudioFormat::SignedInt) {
- if(settings.byteOrder() == QAudioFormat::LittleEndian)
- format = SND_PCM_FORMAT_S16_LE;
- else
- format = SND_PCM_FORMAT_S16_BE;
- } else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
- if(settings.byteOrder() == QAudioFormat::LittleEndian)
- format = SND_PCM_FORMAT_U16_LE;
- else
- format = SND_PCM_FORMAT_U16_BE;
- }
- } else if(settings.sampleSize() == 24) {
- if(settings.sampleType() == QAudioFormat::SignedInt) {
- if(settings.byteOrder() == QAudioFormat::LittleEndian)
- format = SND_PCM_FORMAT_S24_LE;
- else
- format = SND_PCM_FORMAT_S24_BE;
- } else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
- if(settings.byteOrder() == QAudioFormat::LittleEndian)
- format = SND_PCM_FORMAT_U24_LE;
- else
- format = SND_PCM_FORMAT_U24_BE;
- }
- } else if(settings.sampleSize() == 32) {
- if(settings.sampleType() == QAudioFormat::SignedInt) {
- if(settings.byteOrder() == QAudioFormat::LittleEndian)
- format = SND_PCM_FORMAT_S32_LE;
- else
- format = SND_PCM_FORMAT_S32_BE;
- } else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
- if(settings.byteOrder() == QAudioFormat::LittleEndian)
- format = SND_PCM_FORMAT_U32_LE;
- else
- format = SND_PCM_FORMAT_U32_BE;
- } else if(settings.sampleType() == QAudioFormat::Float) {
- if(settings.byteOrder() == QAudioFormat::LittleEndian)
- format = SND_PCM_FORMAT_FLOAT_LE;
- else
- format = SND_PCM_FORMAT_FLOAT_BE;
- }
- } else if(settings.sampleSize() == 64) {
- if(settings.byteOrder() == QAudioFormat::LittleEndian)
- format = SND_PCM_FORMAT_FLOAT64_LE;
- else
- format = SND_PCM_FORMAT_FLOAT64_BE;
- }
-
- return format != SND_PCM_FORMAT_UNKNOWN
- ? snd_pcm_hw_params_set_format( handle, hwparams, format)
- : -1;
-}
-
-void QAlsaAudioInput::start(QIODevice* device)
-{
- if(deviceState != QAudio::StoppedState)
- close();
-
- if(!pullMode && audioSource)
- delete audioSource;
-
- pullMode = true;
- audioSource = device;
-
- deviceState = QAudio::ActiveState;
-
- if( !open() )
- return;
-
- emit stateChanged(deviceState);
-}
-
-QIODevice* QAlsaAudioInput::start()
-{
- if(deviceState != QAudio::StoppedState)
- close();
-
- if(!pullMode && audioSource)
- delete audioSource;
-
- pullMode = false;
- audioSource = new AlsaInputPrivate(this);
- audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
-
- deviceState = QAudio::IdleState;
-
- if( !open() )
- return 0;
-
- emit stateChanged(deviceState);
-
- return audioSource;
-}
-
-void QAlsaAudioInput::stop()
-{
- if(deviceState == QAudio::StoppedState)
- return;
-
- deviceState = QAudio::StoppedState;
-
- close();
- emit stateChanged(deviceState);
-}
-
-bool QAlsaAudioInput::open()
-{
-#ifdef DEBUG_AUDIO
- QTime now(QTime::currentTime());
- qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
-#endif
- clockStamp.restart();
- timeStamp.restart();
- elapsedTimeOffset = 0;
-
- int dir;
- int err = 0;
- int count=0;
- unsigned int sampleRate=settings.sampleRate();
-
- if (!settings.isValid()) {
- qWarning("QAudioInput: open error, invalid format.");
- } else if (settings.sampleRate() <= 0) {
- qWarning("QAudioInput: open error, invalid sample rate (%d).",
- settings.sampleRate());
- } else {
- err = -1;
- }
-
- if (err == 0) {
- errorState = QAudio::OpenError;
- deviceState = QAudio::StoppedState;
- emit errorChanged(errorState);
- return false;
- }
-
-
- if (!QAlsaAudioDeviceInfo::availableDevices(QAudio::AudioInput).contains(m_device))
- return false;
-
- QString dev;
-#if SND_LIB_VERSION < 0x1000e // 1.0.14
- if (m_device != "default")
- dev = QAlsaAudioDeviceInfo::deviceFromCardName(m_device);
- else
-#endif
- dev = m_device;
-
- // Step 1: try and open the device
- while((count < 5) && (err < 0)) {
- err=snd_pcm_open(&handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0);
- if(err < 0)
- count++;
- }
- if (( err < 0)||(handle == 0)) {
- errorState = QAudio::OpenError;
- deviceState = QAudio::StoppedState;
- emit stateChanged(deviceState);
- return false;
- }
- snd_pcm_nonblock( handle, 0 );
-
- // Step 2: Set the desired HW parameters.
- snd_pcm_hw_params_alloca( &hwparams );
-
- bool fatal = false;
- QString errMessage;
- unsigned int chunks = 8;
-
- err = snd_pcm_hw_params_any( handle, hwparams );
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_any: err = %1").arg(err);
- }
- if ( !fatal ) {
- err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 );
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_rate_resample: err = %1").arg(err);
- }
- }
- if ( !fatal ) {
- err = snd_pcm_hw_params_set_access( handle, hwparams, access );
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_access: err = %1").arg(err);
- }
- }
- if ( !fatal ) {
- err = setFormat();
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_format: err = %1").arg(err);
- }
- }
- if ( !fatal ) {
- err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channelCount() );
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_channels: err = %1").arg(err);
- }
- }
- if ( !fatal ) {
- err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &sampleRate, 0 );
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_rate_near: err = %1").arg(err);
- }
- }
- if ( !fatal ) {
- err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir);
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err);
- }
- }
- if ( !fatal ) {
- err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir);
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_period_time_near: err = %1").arg(err);
- }
- }
- if ( !fatal ) {
- err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir);
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_periods_near: err = %1").arg(err);
- }
- }
- if ( !fatal ) {
- err = snd_pcm_hw_params(handle, hwparams);
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params: err = %1").arg(err);
- }
- }
- if( err < 0) {
- qWarning()<<errMessage;
- errorState = QAudio::OpenError;
- deviceState = QAudio::StoppedState;
- emit stateChanged(deviceState);
- return false;
- }
- snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames);
- buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames);
- snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir);
- period_size = snd_pcm_frames_to_bytes(handle,period_frames);
- snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir);
- snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir);
-
- // Step 3: Set the desired SW parameters.
- snd_pcm_sw_params_t *swparams;
- snd_pcm_sw_params_alloca(&swparams);
- snd_pcm_sw_params_current(handle, swparams);
- snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames);
- snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames);
- snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames);
- snd_pcm_sw_params(handle, swparams);
-
- // Step 4: Prepare audio
- ringBuffer.resize(buffer_size);
- snd_pcm_prepare( handle );
- snd_pcm_start(handle);
-
- // Step 5: Setup timer
- bytesAvailable = checkBytesReady();
-
- if(pullMode)
- connect(audioSource,SIGNAL(readyRead()),this,SLOT(userFeed()));
-
- // Step 6: Start audio processing
- chunks = buffer_size/period_size;
- timer->start(period_time*chunks/2000);
-
- errorState = QAudio::NoError;
-
- totalTimeValue = 0;
-
- return true;
-}
-
-void QAlsaAudioInput::close()
-{
- timer->stop();
-
- if ( handle ) {
- snd_pcm_drop( handle );
- snd_pcm_close( handle );
- handle = 0;
- }
-}
-
-int QAlsaAudioInput::checkBytesReady()
-{
- if(resuming)
- bytesAvailable = period_size;
- else if(deviceState != QAudio::ActiveState
- && deviceState != QAudio::IdleState)
- bytesAvailable = 0;
- else {
- int frames = snd_pcm_avail_update(handle);
- if (frames < 0) {
- bytesAvailable = frames;
- } else {
- if((int)frames > (int)buffer_frames)
- frames = buffer_frames;
- bytesAvailable = snd_pcm_frames_to_bytes(handle, frames);
- }
- }
- return bytesAvailable;
-}
-
-int QAlsaAudioInput::bytesReady() const
-{
- return qMax(bytesAvailable, 0);
-}
-
-qint64 QAlsaAudioInput::read(char* data, qint64 len)
-{
- // Read in some audio data and write it to QIODevice, pull mode
- if ( !handle )
- return 0;
-
- int bytesRead = 0;
- int bytesInRingbufferBeforeRead = ringBuffer.bytesOfDataInBuffer();
-
- if (ringBuffer.bytesOfDataInBuffer() < len) {
-
- // bytesAvaiable is saved as a side effect of checkBytesReady().
- int bytesToRead = checkBytesReady();
-
- if (bytesToRead < 0) {
- // bytesAvailable as negative is error code, try to recover from it.
- xrun_recovery(bytesToRead);
- bytesToRead = checkBytesReady();
- if (bytesToRead < 0) {
- // recovery failed must stop and set error.
- close();
- errorState = QAudio::IOError;
- deviceState = QAudio::StoppedState;
- emit stateChanged(deviceState);
- return 0;
- }
- }
-
- bytesToRead = qMin<qint64>(len, bytesToRead);
- bytesToRead = qMin<qint64>(ringBuffer.freeBytes(), bytesToRead);
- bytesToRead -= bytesToRead % period_size;
-
- int count=0;
- int err = 0;
- QVarLengthArray<char, 4096> buffer(bytesToRead);
- while(count < 5 && bytesToRead > 0) {
- int chunks = bytesToRead / period_size;
- int frames = chunks * period_frames;
- if (frames > (int)buffer_frames)
- frames = buffer_frames;
-
- int readFrames = snd_pcm_readi(handle, buffer.data(), frames);
- bytesRead = snd_pcm_frames_to_bytes(handle, readFrames);
- if (m_volume < 1.0f)
- QAudioHelperInternal::qMultiplySamples(m_volume, settings,
- buffer.constData(),
- buffer.data(), bytesRead);
-
- if (readFrames >= 0) {
- ringBuffer.write(buffer.data(), bytesRead);
-#ifdef DEBUG_AUDIO
- qDebug() << QString::fromLatin1("read in bytes = %1 (frames=%2)").arg(bytesRead).arg(readFrames).toLatin1().constData();
-#endif
- break;
- } else if((readFrames == -EAGAIN) || (readFrames == -EINTR)) {
- errorState = QAudio::IOError;
- err = 0;
- break;
- } else {
- if(readFrames == -EPIPE) {
- errorState = QAudio::UnderrunError;
- err = snd_pcm_prepare(handle);
-#ifdef ESTRPIPE
- } else if(readFrames == -ESTRPIPE) {
- err = snd_pcm_prepare(handle);
-#endif
- }
- if(err != 0) break;
- }
- count++;
- }
-
- }
-
- bytesRead += bytesInRingbufferBeforeRead;
-
- if (bytesRead > 0) {
- // got some send it onward
-#ifdef DEBUG_AUDIO
- qDebug() << "frames to write to QIODevice = " <<
- snd_pcm_bytes_to_frames( handle, (int)bytesRead ) << " (" << bytesRead << ") bytes";
-#endif
- if (deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
- return 0;
-
- if (pullMode) {
- qint64 l = 0;
- qint64 bytesWritten = 0;
- while (ringBuffer.bytesOfDataInBuffer() > 0) {
- l = audioSource->write(ringBuffer.availableData(), ringBuffer.availableDataBlockSize());
- if (l > 0) {
- ringBuffer.readBytes(l);
- bytesWritten += l;
- } else {
- break;
- }
- }
-
- if (l < 0) {
- close();
- errorState = QAudio::IOError;
- deviceState = QAudio::StoppedState;
- emit stateChanged(deviceState);
- } else if (l == 0 && bytesWritten == 0) {
- if (deviceState != QAudio::IdleState) {
- errorState = QAudio::NoError;
- deviceState = QAudio::IdleState;
- emit stateChanged(deviceState);
- }
- } else {
- bytesAvailable -= bytesWritten;
- totalTimeValue += bytesWritten;
- resuming = false;
- if (deviceState != QAudio::ActiveState) {
- errorState = QAudio::NoError;
- deviceState = QAudio::ActiveState;
- emit stateChanged(deviceState);
- }
- }
-
- return bytesWritten;
- } else {
- while (ringBuffer.bytesOfDataInBuffer() > 0) {
- int size = ringBuffer.availableDataBlockSize();
- memcpy(data, ringBuffer.availableData(), size);
- data += size;
- ringBuffer.readBytes(size);
- }
-
- bytesAvailable -= bytesRead;
- totalTimeValue += bytesRead;
- resuming = false;
- if (deviceState != QAudio::ActiveState) {
- errorState = QAudio::NoError;
- deviceState = QAudio::ActiveState;
- emit stateChanged(deviceState);
- }
-
- return bytesRead;
- }
- }
-
- return 0;
-}
-
-void QAlsaAudioInput::resume()
-{
- if(deviceState == QAudio::SuspendedState) {
- int err = 0;
-
- if(handle) {
- err = snd_pcm_prepare( handle );
- if(err < 0)
- xrun_recovery(err);
-
- err = snd_pcm_start(handle);
- if(err < 0)
- xrun_recovery(err);
-
- bytesAvailable = buffer_size;
- }
- resuming = true;
- deviceState = QAudio::ActiveState;
- int chunks = buffer_size/period_size;
- timer->start(period_time*chunks/2000);
- emit stateChanged(deviceState);
- }
-}
-
-void QAlsaAudioInput::setBufferSize(int value)
-{
- buffer_size = value;
-}
-
-int QAlsaAudioInput::bufferSize() const
-{
- return buffer_size;
-}
-
-int QAlsaAudioInput::periodSize() const
-{
- return period_size;
-}
-
-void QAlsaAudioInput::setNotifyInterval(int ms)
-{
- intervalTime = qMax(0, ms);
-}
-
-int QAlsaAudioInput::notifyInterval() const
-{
- return intervalTime;
-}
-
-qint64 QAlsaAudioInput::processedUSecs() const
-{
- qint64 result = qint64(1000000) * totalTimeValue /
- (settings.channelCount()*(settings.sampleSize()/8)) /
- settings.sampleRate();
-
- return result;
-}
-
-void QAlsaAudioInput::suspend()
-{
- if(deviceState == QAudio::ActiveState||resuming) {
- snd_pcm_drain(handle);
- timer->stop();
- deviceState = QAudio::SuspendedState;
- emit stateChanged(deviceState);
- }
-}
-
-void QAlsaAudioInput::userFeed()
-{
- if(deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState)
- return;
-#ifdef DEBUG_AUDIO
- QTime now(QTime::currentTime());
- qDebug()<<now.second()<<"s "<<now.msec()<<"ms :userFeed() IN";
-#endif
- deviceReady();
-}
-
-bool QAlsaAudioInput::deviceReady()
-{
- if(pullMode) {
- // reads some audio data and writes it to QIODevice
- read(0, buffer_size);
- } else {
- // emits readyRead() so user will call read() on QIODevice to get some audio data
- AlsaInputPrivate* a = qobject_cast<AlsaInputPrivate*>(audioSource);
- a->trigger();
- }
- bytesAvailable = checkBytesReady();
-
- if(deviceState != QAudio::ActiveState)
- return true;
-
- if (bytesAvailable < 0) {
- // bytesAvailable as negative is error code, try to recover from it.
- xrun_recovery(bytesAvailable);
- bytesAvailable = checkBytesReady();
- if (bytesAvailable < 0) {
- // recovery failed must stop and set error.
- close();
- errorState = QAudio::IOError;
- deviceState = QAudio::StoppedState;
- emit stateChanged(deviceState);
- return 0;
- }
- }
-
- if(intervalTime && (timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) {
- emit notify();
- elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime;
- timeStamp.restart();
- }
- return true;
-}
-
-qint64 QAlsaAudioInput::elapsedUSecs() const
-{
- if (deviceState == QAudio::StoppedState)
- return 0;
-
- return clockStamp.elapsed() * qint64(1000);
-}
-
-void QAlsaAudioInput::reset()
-{
- if(handle)
- snd_pcm_reset(handle);
- stop();
- bytesAvailable = 0;
-}
-
-void QAlsaAudioInput::drain()
-{
- if(handle)
- snd_pcm_drain(handle);
-}
-
-AlsaInputPrivate::AlsaInputPrivate(QAlsaAudioInput* audio)
-{
- audioDevice = qobject_cast<QAlsaAudioInput*>(audio);
-}
-
-AlsaInputPrivate::~AlsaInputPrivate()
-{
-}
-
-qint64 AlsaInputPrivate::readData( char* data, qint64 len)
-{
- return audioDevice->read(data,len);
-}
-
-qint64 AlsaInputPrivate::writeData(const char* data, qint64 len)
-{
- Q_UNUSED(data);
- Q_UNUSED(len);
- return 0;
-}
-
-void AlsaInputPrivate::trigger()
-{
- emit readyRead();
-}
-
-RingBuffer::RingBuffer() :
- m_head(0),
- m_tail(0)
-{
-}
-
-void RingBuffer::resize(int size)
-{
- m_data.resize(size);
-}
-
-int RingBuffer::bytesOfDataInBuffer() const
-{
- if (m_head < m_tail)
- return m_tail - m_head;
- else if (m_tail < m_head)
- return m_data.size() + m_tail - m_head;
- else
- return 0;
-}
-
-int RingBuffer::freeBytes() const
-{
- if (m_head > m_tail)
- return m_head - m_tail - 1;
- else if (m_tail > m_head)
- return m_data.size() - m_tail + m_head - 1;
- else
- return m_data.size() - 1;
-}
-
-const char *RingBuffer::availableData() const
-{
- return (m_data.constData() + m_head);
-}
-
-int RingBuffer::availableDataBlockSize() const
-{
- if (m_head > m_tail)
- return m_data.size() - m_head;
- else if (m_tail > m_head)
- return m_tail - m_head;
- else
- return 0;
-}
-
-void RingBuffer::readBytes(int bytes)
-{
- m_head = (m_head + bytes) % m_data.size();
-}
-
-void RingBuffer::write(char *data, int len)
-{
- if (m_tail + len < m_data.size()) {
- memcpy(m_data.data() + m_tail, data, len);
- m_tail += len;
- } else {
- int bytesUntilEnd = m_data.size() - m_tail;
- memcpy(m_data.data() + m_tail, data, bytesUntilEnd);
- if (len - bytesUntilEnd > 0)
- memcpy(m_data.data(), data + bytesUntilEnd, len - bytesUntilEnd);
- m_tail = len - bytesUntilEnd;
- }
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qalsaaudioinput.cpp"
diff --git a/src/plugins/alsa/qalsaaudioinput.h b/src/plugins/alsa/qalsaaudioinput.h
deleted file mode 100644
index 62e1be039..000000000
--- a/src/plugins/alsa/qalsaaudioinput.h
+++ /dev/null
@@ -1,185 +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$
-**
-****************************************************************************/
-
-//
-// 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.
-//
-
-
-#ifndef QAUDIOINPUTALSA_H
-#define QAUDIOINPUTALSA_H
-
-#include <alsa/asoundlib.h>
-
-#include <QtCore/qfile.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qtimer.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qelapsedtimer.h>
-#include <QtCore/qiodevice.h>
-
-#include <QtMultimedia/qaudio.h>
-#include <QtMultimedia/qaudiodeviceinfo.h>
-#include <QtMultimedia/qaudiosystem.h>
-
-QT_BEGIN_NAMESPACE
-
-
-class AlsaInputPrivate;
-
-class RingBuffer
-{
-public:
- RingBuffer();
-
- void resize(int size);
-
- int bytesOfDataInBuffer() const;
- int freeBytes() const;
-
- const char *availableData() const;
- int availableDataBlockSize() const;
- void readBytes(int bytes);
-
- void write(char *data, int len);
-
-private:
- int m_head;
- int m_tail;
-
- QByteArray m_data;
-};
-
-class QAlsaAudioInput : public QAbstractAudioInput
-{
- Q_OBJECT
-public:
- QAlsaAudioInput(const QByteArray &device);
- ~QAlsaAudioInput();
-
- qint64 read(char* data, qint64 len);
-
- void start(QIODevice* device) override;
- QIODevice* start() override;
- void stop() override;
- void reset() override;
- void suspend() override;
- void resume() override;
- int bytesReady() const override;
- int periodSize() const override;
- void setBufferSize(int value) override;
- int bufferSize() const override;
- void setNotifyInterval(int milliSeconds) override;
- int notifyInterval() const override;
- qint64 processedUSecs() const override;
- qint64 elapsedUSecs() const override;
- QAudio::Error error() const override;
- QAudio::State state() const override;
- void setFormat(const QAudioFormat& fmt) override;
- QAudioFormat format() const override;
- void setVolume(qreal) override;
- qreal volume() const override;
- bool resuming;
- snd_pcm_t* handle;
- qint64 totalTimeValue;
- QIODevice* audioSource;
- QAudioFormat settings;
- QAudio::Error errorState;
- QAudio::State deviceState;
-
-private slots:
- void userFeed();
- bool deviceReady();
-
-private:
- int checkBytesReady();
- int xrun_recovery(int err);
- int setFormat();
- bool open();
- void close();
- void drain();
-
- QTimer* timer;
- QElapsedTimer timeStamp;
- QElapsedTimer clockStamp;
- qint64 elapsedTimeOffset;
- int intervalTime;
- RingBuffer ringBuffer;
- int bytesAvailable;
- QByteArray m_device;
- bool pullMode;
- int buffer_size;
- int period_size;
- unsigned int buffer_time;
- unsigned int period_time;
- snd_pcm_uframes_t buffer_frames;
- snd_pcm_uframes_t period_frames;
- snd_pcm_access_t access;
- snd_pcm_format_t pcmformat;
- snd_pcm_hw_params_t *hwparams;
- qreal m_volume;
-};
-
-class AlsaInputPrivate : public QIODevice
-{
- Q_OBJECT
-public:
- AlsaInputPrivate(QAlsaAudioInput* audio);
- ~AlsaInputPrivate();
-
- qint64 readData( char* data, qint64 len) override;
- qint64 writeData(const char* data, qint64 len) override;
-
- void trigger();
-private:
- QAlsaAudioInput *audioDevice;
-};
-
-QT_END_NAMESPACE
-
-
-#endif
diff --git a/src/plugins/alsa/qalsaaudiooutput.cpp b/src/plugins/alsa/qalsaaudiooutput.cpp
deleted file mode 100644
index f66e5e6e8..000000000
--- a/src/plugins/alsa/qalsaaudiooutput.cpp
+++ /dev/null
@@ -1,813 +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$
-**
-****************************************************************************/
-
-//
-// 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.
-//
-// INTERNAL USE ONLY: Do NOT use for any other purpose.
-//
-
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qvarlengtharray.h>
-#include <QtMultimedia/private/qaudiohelpers_p.h>
-#include "qalsaaudiooutput.h"
-#include "qalsaaudiodeviceinfo.h"
-#include <QLoggingCategory>
-
-QT_BEGIN_NAMESPACE
-
-Q_LOGGING_CATEGORY(lcAlsaOutput, "qt.multimedia.alsa.output")
-//#define DEBUG_AUDIO 1
-
-QAlsaAudioOutput::QAlsaAudioOutput(const QByteArray &device)
-{
- 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;
- intervalTime = 1000;
- 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()));
-}
-
-QAlsaAudioOutput::~QAlsaAudioOutput()
-{
- close();
- disconnect(timer, SIGNAL(timeout()));
- QCoreApplication::processEvents();
- delete timer;
-}
-
-void QAlsaAudioOutput::setVolume(qreal vol)
-{
- m_volume = vol;
-}
-
-qreal QAlsaAudioOutput::volume() const
-{
- return m_volume;
-}
-
-QAudio::Error QAlsaAudioOutput::error() const
-{
- return errorState;
-}
-
-QAudio::State QAlsaAudioOutput::state() const
-{
- return deviceState;
-}
-
-int QAlsaAudioOutput::xrun_recovery(int err)
-{
- int count = 0;
- bool reset = false;
-
- // ESTRPIPE is not available in all OSes where ALSA is available
- int estrpipe = EIO;
-#ifdef ESTRPIPE
- estrpipe = ESTRPIPE;
-#endif
-
- if(err == -EPIPE) {
- errorState = QAudio::UnderrunError;
- emit errorChanged(errorState);
- err = snd_pcm_prepare(handle);
- if(err < 0)
- reset = true;
-
- } else if ((err == -estrpipe)||(err == -EIO)) {
- errorState = QAudio::IOError;
- emit errorChanged(errorState);
- while((err = snd_pcm_resume(handle)) == -EAGAIN){
- usleep(100);
- count++;
- if(count > 5) {
- reset = true;
- break;
- }
- }
- if(err < 0) {
- err = snd_pcm_prepare(handle);
- if(err < 0)
- reset = true;
- }
- }
- if(reset) {
- close();
- open();
- snd_pcm_prepare(handle);
- return 0;
- }
- return err;
-}
-
-int QAlsaAudioOutput::setFormat()
-{
- snd_pcm_format_t pcmformat = SND_PCM_FORMAT_UNKNOWN;
-
- if(settings.sampleSize() == 8) {
- pcmformat = SND_PCM_FORMAT_U8;
-
- } else if(settings.sampleSize() == 16) {
- if(settings.sampleType() == QAudioFormat::SignedInt) {
- if(settings.byteOrder() == QAudioFormat::LittleEndian)
- pcmformat = SND_PCM_FORMAT_S16_LE;
- else
- pcmformat = SND_PCM_FORMAT_S16_BE;
- } else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
- if(settings.byteOrder() == QAudioFormat::LittleEndian)
- pcmformat = SND_PCM_FORMAT_U16_LE;
- else
- pcmformat = SND_PCM_FORMAT_U16_BE;
- }
- } else if(settings.sampleSize() == 24) {
- if(settings.sampleType() == QAudioFormat::SignedInt) {
- if(settings.byteOrder() == QAudioFormat::LittleEndian)
- pcmformat = SND_PCM_FORMAT_S24_LE;
- else
- pcmformat = SND_PCM_FORMAT_S24_BE;
- } else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
- if(settings.byteOrder() == QAudioFormat::LittleEndian)
- pcmformat = SND_PCM_FORMAT_U24_LE;
- else
- pcmformat = SND_PCM_FORMAT_U24_BE;
- }
- } else if(settings.sampleSize() == 32) {
- if(settings.sampleType() == QAudioFormat::SignedInt) {
- if(settings.byteOrder() == QAudioFormat::LittleEndian)
- pcmformat = SND_PCM_FORMAT_S32_LE;
- else
- pcmformat = SND_PCM_FORMAT_S32_BE;
- } else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
- if(settings.byteOrder() == QAudioFormat::LittleEndian)
- pcmformat = SND_PCM_FORMAT_U32_LE;
- else
- pcmformat = SND_PCM_FORMAT_U32_BE;
- } else if(settings.sampleType() == QAudioFormat::Float) {
- if(settings.byteOrder() == QAudioFormat::LittleEndian)
- pcmformat = SND_PCM_FORMAT_FLOAT_LE;
- else
- pcmformat = SND_PCM_FORMAT_FLOAT_BE;
- }
- } else if(settings.sampleSize() == 64) {
- if(settings.byteOrder() == QAudioFormat::LittleEndian)
- pcmformat = SND_PCM_FORMAT_FLOAT64_LE;
- else
- pcmformat = SND_PCM_FORMAT_FLOAT64_BE;
- }
-
- return pcmformat != SND_PCM_FORMAT_UNKNOWN
- ? snd_pcm_hw_params_set_format( handle, hwparams, pcmformat)
- : -1;
-}
-
-void QAlsaAudioOutput::start(QIODevice* device)
-{
- if(deviceState != QAudio::StoppedState)
- deviceState = QAudio::StoppedState;
-
- errorState = QAudio::NoError;
-
- // Handle change of mode
- if(audioSource && !pullMode) {
- delete audioSource;
- audioSource = 0;
- }
-
- close();
-
- pullMode = true;
- audioSource = device;
-
- deviceState = QAudio::ActiveState;
-
- open();
-
- emit stateChanged(deviceState);
-}
-
-QIODevice* QAlsaAudioOutput::start()
-{
- if(deviceState != QAudio::StoppedState)
- deviceState = QAudio::StoppedState;
-
- errorState = QAudio::NoError;
-
- // Handle change of mode
- if(audioSource && !pullMode) {
- delete audioSource;
- audioSource = 0;
- }
-
- close();
-
- audioSource = new AlsaOutputPrivate(this);
- audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered);
- pullMode = false;
-
- deviceState = QAudio::IdleState;
-
- open();
-
- emit stateChanged(deviceState);
-
- return audioSource;
-}
-
-void QAlsaAudioOutput::stop()
-{
- if(deviceState == QAudio::StoppedState)
- return;
- errorState = QAudio::NoError;
- deviceState = QAudio::StoppedState;
- close();
- emit stateChanged(deviceState);
-}
-
-bool QAlsaAudioOutput::open()
-{
- if(opened)
- return true;
-
-#ifdef DEBUG_AUDIO
- QTime now(QTime::currentTime());
- qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
-#endif
- timeStamp.restart();
- elapsedTimeOffset = 0;
-
- int dir;
- int err = 0;
- int count=0;
- unsigned int sampleRate=settings.sampleRate();
-
- if (!settings.isValid()) {
- qWarning("QAudioOutput: open error, invalid format.");
- } else if (settings.sampleRate() <= 0) {
- qWarning("QAudioOutput: open error, invalid sample rate (%d).",
- settings.sampleRate());
- } else {
- err = -1;
- }
-
- if (err == 0) {
- errorState = QAudio::OpenError;
- deviceState = QAudio::StoppedState;
- emit errorChanged(errorState);
- return false;
- }
-
- QString dev;
-#if SND_LIB_VERSION < 0x1000e // 1.0.14
- if (m_device != "default")
- dev = QAlsaAudioDeviceInfo::deviceFromCardName(m_device);
- else
-#endif
- dev = m_device;
-
- // Step 1: try and open the device
- while((count < 5) && (err < 0)) {
- err=snd_pcm_open(&handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0);
- if(err < 0)
- count++;
- }
- if (( err < 0)||(handle == 0)) {
- errorState = QAudio::OpenError;
- emit errorChanged(errorState);
- deviceState = QAudio::StoppedState;
- return false;
- }
- snd_pcm_nonblock( handle, 0 );
-
- // Step 2: Set the desired HW parameters.
- snd_pcm_hw_params_alloca( &hwparams );
-
- bool fatal = false;
- QString errMessage;
- unsigned int chunks = 8;
-
- err = snd_pcm_hw_params_any( handle, hwparams );
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_any: err = %1").arg(err);
- }
- if ( !fatal ) {
- err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 );
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_rate_resample: err = %1").arg(err);
- }
- }
- if ( !fatal ) {
- err = snd_pcm_hw_params_set_access( handle, hwparams, access );
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_access: err = %1").arg(err);
- }
- }
- if ( !fatal ) {
- err = setFormat();
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_format: err = %1").arg(err);
- }
- }
- if ( !fatal ) {
- err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channelCount() );
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_channels: err = %1").arg(err);
- }
- }
- if ( !fatal ) {
- err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &sampleRate, 0 );
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_rate_near: err = %1").arg(err);
- }
- }
- if ( !fatal ) {
- unsigned int maxBufferTime = 0;
- unsigned int minBufferTime = 0;
- unsigned int maxPeriodTime = 0;
- unsigned int minPeriodTime = 0;
-
- err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &maxBufferTime, &dir);
- if ( err >= 0)
- err = snd_pcm_hw_params_get_buffer_time_min(hwparams, &minBufferTime, &dir);
- if ( err >= 0)
- err = snd_pcm_hw_params_get_period_time_max(hwparams, &maxPeriodTime, &dir);
- if ( err >= 0)
- err = snd_pcm_hw_params_get_period_time_min(hwparams, &minPeriodTime, &dir);
-
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioOutput: buffer/period min and max: err = %1").arg(err);
- } else {
- static unsigned user_buffer_time = qEnvironmentVariableIntValue("QT_ALSA_OUTPUT_BUFFER_TIME");
- static unsigned user_period_time = qEnvironmentVariableIntValue("QT_ALSA_OUTPUT_PERIOD_TIME");
- const bool outOfRange = maxBufferTime < buffer_time || buffer_time < minBufferTime || maxPeriodTime < period_time || minPeriodTime > period_time;
- if (outOfRange || user_period_time || user_buffer_time) {
- period_time = user_period_time ? user_period_time : minPeriodTime;
- if (!user_buffer_time) {
- chunks = maxBufferTime / period_time;
- buffer_time = period_time * chunks;
- } else {
- buffer_time = user_buffer_time;
- chunks = buffer_time / period_time;
- }
- }
- qCDebug(lcAlsaOutput) << "buffer time: [" << minBufferTime << "-" << maxBufferTime << "] =" << buffer_time;
- qCDebug(lcAlsaOutput) << "period time: [" << minPeriodTime << "-" << maxPeriodTime << "] =" << period_time;
- qCDebug(lcAlsaOutput) << "chunks =" << chunks;
- }
- }
- if ( !fatal ) {
- err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir);
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err);
- }
- }
- if ( !fatal ) {
- err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir);
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_period_time_near: err = %1").arg(err);
- }
- }
- if ( !fatal ) {
- err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir);
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_periods_near: err = %1").arg(err);
- }
- }
- if ( !fatal ) {
- err = snd_pcm_hw_params(handle, hwparams);
- if ( err < 0 ) {
- fatal = true;
- errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params: err = %1").arg(err);
- }
- }
- if( err < 0) {
- qWarning()<<errMessage;
- errorState = QAudio::OpenError;
- emit errorChanged(errorState);
- deviceState = QAudio::StoppedState;
- return false;
- }
- snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames);
- buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames);
- snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir);
- period_size = snd_pcm_frames_to_bytes(handle,period_frames);
- snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir);
- snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir);
-
- // Step 3: Set the desired SW parameters.
- snd_pcm_sw_params_t *swparams;
- snd_pcm_sw_params_alloca(&swparams);
- snd_pcm_sw_params_current(handle, swparams);
- snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames);
- snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames);
- snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames);
- snd_pcm_sw_params(handle, swparams);
-
- // Step 4: Prepare audio
- if(audioBuffer == 0)
- audioBuffer = new char[snd_pcm_frames_to_bytes(handle,buffer_frames)];
- snd_pcm_prepare( handle );
- snd_pcm_start(handle);
-
- // Step 5: Setup timer
- bytesAvailable = bytesFree();
-
- // Step 6: Start audio processing
- timer->start(period_time/1000);
-
- clockStamp.restart();
- timeStamp.restart();
- elapsedTimeOffset = 0;
- errorState = QAudio::NoError;
- totalTimeValue = 0;
- opened = true;
-
- return true;
-}
-
-void QAlsaAudioOutput::close()
-{
- timer->stop();
-
- if ( handle ) {
- snd_pcm_drain( handle );
- snd_pcm_close( handle );
- handle = 0;
- delete [] audioBuffer;
- audioBuffer=0;
- }
- if(!pullMode && audioSource) {
- delete audioSource;
- audioSource = 0;
- }
- opened = false;
-}
-
-int QAlsaAudioOutput::bytesFree() const
-{
- if(resuming)
- return period_size;
-
- if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
- return 0;
-
- int frames = snd_pcm_avail_update(handle);
- if (frames == -EPIPE) {
- // Try and handle buffer underrun
- int err = snd_pcm_recover(handle, frames, 0);
- if (err < 0)
- return 0;
- else
- frames = snd_pcm_avail_update(handle);
- } else if (frames < 0) {
- return 0;
- }
-
- if ((int)frames > (int)buffer_frames)
- frames = buffer_frames;
-
- return snd_pcm_frames_to_bytes(handle, frames);
-}
-
-qint64 QAlsaAudioOutput::write( const char *data, qint64 len )
-{
- // Write out some audio data
- if ( !handle )
- return 0;
-#ifdef DEBUG_AUDIO
- qDebug()<<"frames to write out = "<<
- snd_pcm_bytes_to_frames( handle, (int)len )<<" ("<<len<<") bytes";
-#endif
- int frames, err;
- int space = bytesFree();
-
- if (!space)
- return 0;
-
- if (len < space)
- space = len;
-
- frames = snd_pcm_bytes_to_frames(handle, space);
-
- if (m_volume < 1.0f) {
- QVarLengthArray<char, 4096> out(space);
- QAudioHelperInternal::qMultiplySamples(m_volume, settings, data, out.data(), space);
- err = snd_pcm_writei(handle, out.constData(), frames);
- } else {
- err = snd_pcm_writei(handle, data, frames);
- }
-
- if(err > 0) {
- totalTimeValue += err;
- resuming = false;
- errorState = QAudio::NoError;
- if (deviceState != QAudio::ActiveState) {
- deviceState = QAudio::ActiveState;
- emit stateChanged(deviceState);
- }
- return snd_pcm_frames_to_bytes( handle, err );
- } else
- err = xrun_recovery(err);
-
- if(err < 0) {
- close();
- errorState = QAudio::FatalError;
- emit errorChanged(errorState);
- deviceState = QAudio::StoppedState;
- emit stateChanged(deviceState);
- }
- return 0;
-}
-
-int QAlsaAudioOutput::periodSize() const
-{
- return period_size;
-}
-
-void QAlsaAudioOutput::setBufferSize(int value)
-{
- if(deviceState == QAudio::StoppedState)
- buffer_size = value;
-}
-
-int QAlsaAudioOutput::bufferSize() const
-{
- return buffer_size;
-}
-
-void QAlsaAudioOutput::setNotifyInterval(int ms)
-{
- intervalTime = qMax(0, ms);
-}
-
-int QAlsaAudioOutput::notifyInterval() const
-{
- return intervalTime;
-}
-
-qint64 QAlsaAudioOutput::processedUSecs() const
-{
- return qint64(1000000) * totalTimeValue / settings.sampleRate();
-}
-
-void QAlsaAudioOutput::resume()
-{
- if(deviceState == QAudio::SuspendedState) {
- int err = 0;
-
- if(handle) {
- err = snd_pcm_prepare( handle );
- if(err < 0)
- xrun_recovery(err);
-
- err = snd_pcm_start(handle);
- if(err < 0)
- xrun_recovery(err);
-
- bytesAvailable = (int)snd_pcm_frames_to_bytes(handle, buffer_frames);
- }
- resuming = true;
-
- deviceState = pullMode ? QAudio::ActiveState : QAudio::IdleState;
-
- errorState = QAudio::NoError;
- timer->start(period_time/1000);
- emit stateChanged(deviceState);
- }
-}
-
-void QAlsaAudioOutput::setFormat(const QAudioFormat& fmt)
-{
- if (deviceState == QAudio::StoppedState)
- settings = fmt;
-}
-
-QAudioFormat QAlsaAudioOutput::format() const
-{
- return settings;
-}
-
-void QAlsaAudioOutput::suspend()
-{
- if(deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState || resuming) {
- snd_pcm_drain(handle);
- timer->stop();
- deviceState = QAudio::SuspendedState;
- errorState = QAudio::NoError;
- emit stateChanged(deviceState);
- }
-}
-
-void QAlsaAudioOutput::userFeed()
-{
- if(deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState)
- return;
-#ifdef DEBUG_AUDIO
- QTime now(QTime::currentTime());
- qDebug()<<now.second()<<"s "<<now.msec()<<"ms :userFeed() OUT";
-#endif
- if(deviceState == QAudio::IdleState)
- bytesAvailable = bytesFree();
-
- deviceReady();
-}
-
-bool QAlsaAudioOutput::deviceReady()
-{
- if(pullMode) {
- int l = 0;
- int chunks = bytesAvailable/period_size;
- if(chunks==0) {
- bytesAvailable = bytesFree();
- return false;
- }
-#ifdef DEBUG_AUDIO
- qDebug()<<"deviceReady() avail="<<bytesAvailable<<" bytes, period size="<<period_size<<" bytes";
- qDebug()<<"deviceReady() no. of chunks that can fit ="<<chunks<<", chunks in bytes ="<<period_size*chunks;
-#endif
- int input = period_frames*chunks;
- if(input > (int)buffer_frames)
- input = buffer_frames;
- l = audioSource->read(audioBuffer,snd_pcm_frames_to_bytes(handle, input));
-
- // reading can take a while and stream may have been stopped
- if (!handle)
- return false;
-
- if(l > 0) {
- // Got some data to output
- if (deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
- return true;
- qint64 bytesWritten = write(audioBuffer,l);
- if (bytesWritten != l)
- audioSource->seek(audioSource->pos()-(l-bytesWritten));
- bytesAvailable = bytesFree();
-
- } else if(l == 0) {
- // Did not get any data to output
- bytesAvailable = bytesFree();
- if(bytesAvailable > snd_pcm_frames_to_bytes(handle, buffer_frames-period_frames)) {
- // Underrun
- if (deviceState != QAudio::IdleState) {
- errorState = QAudio::UnderrunError;
- emit errorChanged(errorState);
- deviceState = QAudio::IdleState;
- emit stateChanged(deviceState);
- }
- }
-
- } else if(l < 0) {
- close();
- deviceState = QAudio::StoppedState;
- errorState = QAudio::IOError;
- emit errorChanged(errorState);
- emit stateChanged(deviceState);
- }
- } else {
- bytesAvailable = bytesFree();
- if(bytesAvailable > snd_pcm_frames_to_bytes(handle, buffer_frames-period_frames)) {
- // Underrun
- if (deviceState != QAudio::IdleState) {
- errorState = QAudio::UnderrunError;
- emit errorChanged(errorState);
- deviceState = QAudio::IdleState;
- emit stateChanged(deviceState);
- }
- }
- }
-
- if(deviceState != QAudio::ActiveState)
- return true;
-
- if(intervalTime && (timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) {
- emit notify();
- elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime;
- timeStamp.restart();
- }
- return true;
-}
-
-qint64 QAlsaAudioOutput::elapsedUSecs() const
-{
- if (deviceState == QAudio::StoppedState)
- return 0;
-
- return clockStamp.elapsed() * qint64(1000);
-}
-
-void QAlsaAudioOutput::reset()
-{
- if(handle)
- snd_pcm_reset(handle);
-
- stop();
-}
-
-AlsaOutputPrivate::AlsaOutputPrivate(QAlsaAudioOutput* audio)
-{
- audioDevice = qobject_cast<QAlsaAudioOutput*>(audio);
-}
-
-AlsaOutputPrivate::~AlsaOutputPrivate() {}
-
-qint64 AlsaOutputPrivate::readData( char* data, qint64 len)
-{
- Q_UNUSED(data);
- Q_UNUSED(len);
-
- return 0;
-}
-
-qint64 AlsaOutputPrivate::writeData(const char* data, qint64 len)
-{
- int retry = 0;
- qint64 written = 0;
- if((audioDevice->deviceState == QAudio::ActiveState)
- ||(audioDevice->deviceState == QAudio::IdleState)) {
- while(written < len) {
- int chunk = audioDevice->write(data+written,(len-written));
- if(chunk <= 0)
- retry++;
- written+=chunk;
- if(retry > 10)
- return written;
- }
- }
- return written;
-
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qalsaaudiooutput.cpp"
diff --git a/src/plugins/alsa/qalsaaudiooutput.h b/src/plugins/alsa/qalsaaudiooutput.h
deleted file mode 100644
index 72b9c2e4c..000000000
--- a/src/plugins/alsa/qalsaaudiooutput.h
+++ /dev/null
@@ -1,164 +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$
-**
-****************************************************************************/
-
-//
-// 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.
-//
-
-#ifndef QAUDIOOUTPUTALSA_H
-#define QAUDIOOUTPUTALSA_H
-
-#include <alsa/asoundlib.h>
-
-#include <QtCore/qfile.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qtimer.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qelapsedtimer.h>
-#include <QtCore/qiodevice.h>
-
-#include <QtMultimedia/qaudio.h>
-#include <QtMultimedia/qaudiodeviceinfo.h>
-#include <QtMultimedia/qaudiosystem.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAlsaAudioOutput : public QAbstractAudioOutput
-{
- friend class AlsaOutputPrivate;
- Q_OBJECT
-public:
- QAlsaAudioOutput(const QByteArray &device);
- ~QAlsaAudioOutput();
-
- qint64 write( const char *data, qint64 len );
-
- void start(QIODevice* device) override;
- QIODevice* start() override;
- void stop() override;
- void reset() override;
- void suspend() override;
- void resume() override;
- int bytesFree() const override;
- int periodSize() const override;
- void setBufferSize(int value) override;
- int bufferSize() const override;
- void setNotifyInterval(int milliSeconds) override;
- int notifyInterval() const override;
- qint64 processedUSecs() const override;
- qint64 elapsedUSecs() const override;
- QAudio::Error error() const override;
- QAudio::State state() const override;
- void setFormat(const QAudioFormat& fmt) override;
- QAudioFormat format() const override;
- void setVolume(qreal) override;
- qreal volume() const override;
-
-
- QIODevice* audioSource;
- QAudioFormat settings;
- QAudio::Error errorState;
- QAudio::State deviceState;
-
-private slots:
- void userFeed();
- bool deviceReady();
-
-signals:
- void processMore();
-
-private:
- bool opened;
- bool pullMode;
- bool resuming;
- int buffer_size;
- int period_size;
- int intervalTime;
- qint64 totalTimeValue;
- unsigned int buffer_time;
- unsigned int period_time;
- snd_pcm_uframes_t buffer_frames;
- snd_pcm_uframes_t period_frames;
- int xrun_recovery(int err);
-
- int setFormat();
- bool open();
- void close();
-
- QTimer* timer;
- QByteArray m_device;
- int bytesAvailable;
- QElapsedTimer timeStamp;
- QElapsedTimer clockStamp;
- 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;
-};
-
-class AlsaOutputPrivate : public QIODevice
-{
- friend class QAlsaAudioOutput;
- Q_OBJECT
-public:
- AlsaOutputPrivate(QAlsaAudioOutput* audio);
- ~AlsaOutputPrivate();
-
- qint64 readData( char* data, qint64 len) override;
- qint64 writeData(const char* data, qint64 len) override;
-
-private:
- QAlsaAudioOutput *audioDevice;
-};
-
-QT_END_NAMESPACE
-
-
-#endif
diff --git a/src/plugins/alsa/qalsaplugin.cpp b/src/plugins/alsa/qalsaplugin.cpp
deleted file mode 100644
index e52e9ee83..000000000
--- a/src/plugins/alsa/qalsaplugin.cpp
+++ /dev/null
@@ -1,77 +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 "qalsaplugin.h"
-#include "qalsaaudiodeviceinfo.h"
-#include "qalsaaudioinput.h"
-#include "qalsaaudiooutput.h"
-
-QT_BEGIN_NAMESPACE
-
-QAlsaPlugin::QAlsaPlugin(QObject *parent)
- : QAudioSystemPlugin(parent)
-{
-}
-
-QByteArray QAlsaPlugin::defaultDevice(QAudio::Mode mode) const
-{
- return QAlsaAudioDeviceInfo::defaultDevice(mode);
-}
-
-QList<QByteArray> QAlsaPlugin::availableDevices(QAudio::Mode mode) const
-{
- return QAlsaAudioDeviceInfo::availableDevices(mode);
-}
-
-QAbstractAudioInput *QAlsaPlugin::createInput(const QByteArray &device)
-{
- return new QAlsaAudioInput(device);
-}
-
-QAbstractAudioOutput *QAlsaPlugin::createOutput(const QByteArray &device)
-{
- return new QAlsaAudioOutput(device);
-}
-
-QAbstractAudioDeviceInfo *QAlsaPlugin::createDeviceInfo(const QByteArray &device, QAudio::Mode mode)
-{
- return new QAlsaAudioDeviceInfo(device, mode);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/alsa/qalsaplugin.h b/src/plugins/alsa/qalsaplugin.h
deleted file mode 100644
index cebede13b..000000000
--- a/src/plugins/alsa/qalsaplugin.h
+++ /dev/null
@@ -1,68 +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 QALSAPLUGIN_H
-#define QALSAPLUGIN_H
-
-#include <QtMultimedia/qaudiosystemplugin.h>
-#include <QtMultimedia/private/qaudiosystempluginext_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAlsaPlugin : public QAudioSystemPlugin, public QAudioSystemPluginExtension
-{
- Q_OBJECT
-
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.audiosystemfactory/5.0" FILE "alsa.json")
- Q_INTERFACES(QAudioSystemPluginExtension)
-
-public:
- QAlsaPlugin(QObject *parent = 0);
- ~QAlsaPlugin() {}
-
- QByteArray defaultDevice(QAudio::Mode mode) const override;
- QList<QByteArray> availableDevices(QAudio::Mode mode) const override;
- QAbstractAudioInput *createInput(const QByteArray &device) override;
- QAbstractAudioOutput *createOutput(const QByteArray &device) override;
- QAbstractAudioDeviceInfo *createDeviceInfo(const QByteArray &device, QAudio::Mode mode) override;
-};
-
-QT_END_NAMESPACE
-
-#endif // QALSAPLUGIN_H
diff --git a/src/plugins/android/android.pro b/src/plugins/android/android.pro
deleted file mode 100644
index 37d11c86b..000000000
--- a/src/plugins/android/android.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-TEMPLATE = subdirs
-
-SUBDIRS += src
-android: SUBDIRS += jar
diff --git a/src/plugins/android/jar/AndroidManifest.xml b/src/plugins/android/jar/AndroidManifest.xml
deleted file mode 100644
index 17019fb34..000000000
--- a/src/plugins/android/jar/AndroidManifest.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="org.qtproject.qt.android.multimedia"
- android:versionCode="1"
- android:versionName="1.0" >
- <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
-</manifest>
diff --git a/src/plugins/android/jar/jar.pro b/src/plugins/android/jar/jar.pro
deleted file mode 100644
index 0ef830f32..000000000
--- a/src/plugins/android/jar/jar.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-TARGET = Qt$${QT_MAJOR_VERSION}AndroidMultimedia
-
-load(qt_build_paths)
-CONFIG += java
-DESTDIR = $$MODULE_BASE_OUTDIR/jar
-
-JAVACLASSPATH += $$PWD/src
-
-JAVASOURCES += $$PWD/src/org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer.java \
- $$PWD/src/org/qtproject/qt/android/multimedia/QtCameraListener.java \
- $$PWD/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureListener.java \
- $$PWD/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureHolder.java \
- $$PWD/src/org/qtproject/qt/android/multimedia/QtMultimediaUtils.java \
- $$PWD/src/org/qtproject/qt/android/multimedia/QtMediaRecorderListener.java \
- $$PWD/src/org/qtproject/qt/android/multimedia/QtSurfaceHolderCallback.java
-
-# install
-target.path = $$[QT_INSTALL_PREFIX]/jar
-INSTALLS += target
-
-OTHER_FILES += $$JAVASOURCES
diff --git a/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer.java b/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer.java
deleted file mode 100644
index aa706179c..000000000
--- a/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer.java
+++ /dev/null
@@ -1,590 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-package org.qtproject.qt.android.multimedia;
-
-import java.io.IOException;
-import java.lang.String;
-import java.util.HashMap;
-import java.io.FileInputStream;
-
-// API is level is < 9 unless marked otherwise.
-import android.content.Context;
-import android.media.MediaPlayer;
-import android.media.AudioAttributes;
-import android.net.Uri;
-import android.util.Log;
-import java.io.FileDescriptor;
-import android.content.res.AssetFileDescriptor;
-import android.content.res.AssetManager;
-import android.view.SurfaceHolder;
-
-public class QtAndroidMediaPlayer
-{
- // Native callback functions for MediaPlayer
- native public void onErrorNative(int what, int extra, long id);
- native public void onBufferingUpdateNative(int percent, long id);
- native public void onProgressUpdateNative(int progress, long id);
- native public void onDurationChangedNative(int duration, long id);
- native public void onInfoNative(int what, int extra, long id);
- native public void onVideoSizeChangedNative(int width, int height, long id);
- native public void onStateChangedNative(int state, long id);
-
- private MediaPlayer mMediaPlayer = null;
- private AudioAttributes mAudioAttributes = null;
- private HashMap<String, String> mHeaders = null;
- private Uri mUri = null;
- private final long mID;
- private final Context mContext;
- private boolean mMuted = false;
- private int mVolume = 100;
- private static final String TAG = "Qt MediaPlayer";
- private SurfaceHolder mSurfaceHolder = null;
-
- private class State {
- final static int Uninitialized = 0x1 /* End */;
- final static int Idle = 0x2;
- final static int Preparing = 0x4;
- final static int Prepared = 0x8;
- final static int Initialized = 0x10;
- final static int Started = 0x20;
- final static int Stopped = 0x40;
- final static int Paused = 0x80;
- final static int PlaybackCompleted = 0x100;
- final static int Error = 0x200;
- }
-
- private volatile int mState = State.Uninitialized;
-
- /**
- * MediaPlayer OnErrorListener
- */
- private class MediaPlayerErrorListener
- implements MediaPlayer.OnErrorListener
- {
- @Override
- public boolean onError(final MediaPlayer mp,
- final int what,
- final int extra)
- {
- setState(State.Error);
- onErrorNative(what, extra, mID);
- return true;
- }
-
- }
-
- /**
- * MediaPlayer OnBufferingListener
- */
- private class MediaPlayerBufferingListener
- implements MediaPlayer.OnBufferingUpdateListener
- {
- private int mBufferPercent = -1;
- @Override
- public void onBufferingUpdate(final android.media.MediaPlayer mp,
- final int percent)
- {
- // Avoid updates when percent is unchanged.
- // E.g., we keep getting updates when percent == 100
- if (mBufferPercent == percent)
- return;
-
- onBufferingUpdateNative((mBufferPercent = percent), mID);
- }
-
- }
-
- /**
- * MediaPlayer OnCompletionListener
- */
- private class MediaPlayerCompletionListener
- implements MediaPlayer.OnCompletionListener
- {
- @Override
- public void onCompletion(final MediaPlayer mp)
- {
- setState(State.PlaybackCompleted);
- }
-
- }
-
- /**
- * MediaPlayer OnInfoListener
- */
- private class MediaPlayerInfoListener
- implements MediaPlayer.OnInfoListener
- {
- @Override
- public boolean onInfo(final MediaPlayer mp,
- final int what,
- final int extra)
- {
- onInfoNative(what, extra, mID);
- return true;
- }
-
- }
-
- /**
- * MediaPlayer OnPreparedListener
- */
- private class MediaPlayerPreparedListener
- implements MediaPlayer.OnPreparedListener
- {
-
- @Override
- public void onPrepared(final MediaPlayer mp)
- {
- setState(State.Prepared);
- onDurationChangedNative(getDuration(), mID);
- }
-
- }
-
- /**
- * MediaPlayer OnSeekCompleteListener
- */
- private class MediaPlayerSeekCompleteListener
- implements MediaPlayer.OnSeekCompleteListener
- {
-
- @Override
- public void onSeekComplete(final MediaPlayer mp)
- {
- onProgressUpdateNative(getCurrentPosition(), mID);
- }
-
- }
-
- /**
- * MediaPlayer OnVideoSizeChangedListener
- */
- private class MediaPlayerVideoSizeChangedListener
- implements MediaPlayer.OnVideoSizeChangedListener
- {
-
- @Override
- public void onVideoSizeChanged(final MediaPlayer mp,
- final int width,
- final int height)
- {
- onVideoSizeChangedNative(width, height, mID);
- }
-
- }
-
- public QtAndroidMediaPlayer(final Context context, final long id)
- {
- mID = id;
- mContext = context;
- }
-
- public MediaPlayer getMediaPlayerHandle()
- {
- return mMediaPlayer;
- }
-
- private void setState(int state)
- {
- if (mState == state)
- return;
-
- mState = state;
-
- onStateChangedNative(mState, mID);
- }
-
-
- private void init()
- {
- if (mMediaPlayer == null) {
- mMediaPlayer = new MediaPlayer();
- setState(State.Idle);
- // Make sure the new media player has the volume that was set on the QMediaPlayer
- setVolumeHelper(mMuted ? 0 : mVolume);
- setAudioAttributes(mMediaPlayer, mAudioAttributes);
- }
- }
-
- public void start()
- {
- if ((mState & (State.Prepared
- | State.Started
- | State.Paused
- | State.PlaybackCompleted)) == 0) {
- return;
- }
-
- try {
- mMediaPlayer.start();
- setState(State.Started);
- } catch (final IllegalStateException e) {
- Log.d(TAG, "" + e.getMessage());
- }
- }
-
-
- public void pause()
- {
- if ((mState & (State.Started | State.Paused | State.PlaybackCompleted)) == 0)
- return;
-
- try {
- mMediaPlayer.pause();
- setState(State.Paused);
- } catch (final IllegalStateException e) {
- Log.d(TAG, "" + e.getMessage());
- }
- }
-
-
- public void stop()
- {
- if ((mState & (State.Prepared
- | State.Started
- | State.Stopped
- | State.Paused
- | State.PlaybackCompleted)) == 0) {
- return;
- }
-
- try {
- mMediaPlayer.stop();
- setState(State.Stopped);
- } catch (final IllegalStateException e) {
- Log.d(TAG, "" + e.getMessage());
- }
- }
-
-
- public void seekTo(final int msec)
- {
- if ((mState & (State.Prepared
- | State.Started
- | State.Paused
- | State.PlaybackCompleted)) == 0) {
- return;
- }
-
- try {
- mMediaPlayer.seekTo(msec);
- } catch (final IllegalStateException e) {
- Log.d(TAG, "" + e.getMessage());
- }
- }
-
-
- public boolean isPlaying()
- {
- boolean playing = false;
- if ((mState & (State.Idle
- | State.Initialized
- | State.Prepared
- | State.Started
- | State.Paused
- | State.Stopped
- | State.PlaybackCompleted)) == 0) {
- return playing;
- }
-
- try {
- playing = mMediaPlayer.isPlaying();
- } catch (final IllegalStateException e) {
- Log.d(TAG, "" + e.getMessage());
- }
-
- return playing;
- }
-
- public void prepareAsync()
- {
- if ((mState & (State.Initialized | State.Stopped)) == 0)
- return;
-
- try {
- mMediaPlayer.prepareAsync();
- setState(State.Preparing);
- } catch (final IllegalStateException e) {
- Log.d(TAG, "" + e.getMessage());
- }
- }
-
- public void initHeaders()
- {
- mHeaders = new HashMap<String, String>();
- }
-
- public void setHeader(final String header, final String value)
- {
- mHeaders.put(header, value);
- }
-
- public void setDataSource(final String path)
- {
- if ((mState & State.Uninitialized) != 0)
- init();
-
- if ((mState & State.Idle) == 0)
- return;
-
- mMediaPlayer.setOnBufferingUpdateListener(new MediaPlayerBufferingListener());
- mMediaPlayer.setOnCompletionListener(new MediaPlayerCompletionListener());
- mMediaPlayer.setOnInfoListener(new MediaPlayerInfoListener());
- mMediaPlayer.setOnSeekCompleteListener(new MediaPlayerSeekCompleteListener());
- mMediaPlayer.setOnVideoSizeChangedListener(new MediaPlayerVideoSizeChangedListener());
- mMediaPlayer.setOnErrorListener(new MediaPlayerErrorListener());
- mMediaPlayer.setOnPreparedListener(new MediaPlayerPreparedListener());
-
- if (mSurfaceHolder != null)
- mMediaPlayer.setDisplay(mSurfaceHolder);
-
- AssetFileDescriptor afd = null;
- FileInputStream fis = null;
- try {
- mUri = Uri.parse(path);
- final boolean inAssets = (mUri.getScheme().compareTo("assets") == 0);
- if (inAssets) {
- final String asset = mUri.getPath().substring(1 /* Remove first '/' */);
- final AssetManager am = mContext.getAssets();
- afd = am.openFd(asset);
- final long offset = afd.getStartOffset();
- final long length = afd.getLength();
- FileDescriptor fd = afd.getFileDescriptor();
- mMediaPlayer.setDataSource(fd, offset, length);
- } else if (mUri.getScheme().compareTo("file") == 0) {
- fis = new FileInputStream(mUri.getPath());
- FileDescriptor fd = fis.getFD();
- mMediaPlayer.setDataSource(fd);
- } else {
- if (mHeaders.isEmpty())
- mMediaPlayer.setDataSource(path);
- else
- mMediaPlayer.setDataSource(mContext, mUri, mHeaders);
- }
- setState(State.Initialized);
- } catch (final IOException e) {
- Log.d(TAG, "" + e.getMessage());
- } catch (final IllegalArgumentException e) {
- Log.d(TAG, "" + e.getMessage());
- } catch (final SecurityException e) {
- Log.d(TAG, "" + e.getMessage());
- } catch (final IllegalStateException e) {
- Log.d(TAG, "" + e.getMessage());
- } catch (final NullPointerException e) {
- Log.d(TAG, "" + e.getMessage());
- } finally {
- try {
- if (afd != null)
- afd.close();
- if (fis != null)
- fis.close();
- } catch (final IOException ioe) { /* Ignore... */ }
-
- if ((mState & State.Initialized) == 0) {
- setState(State.Error);
- onErrorNative(MediaPlayer.MEDIA_ERROR_UNKNOWN,
- -1004 /*MEDIA_ERROR_IO*/,
- mID);
- return;
- }
- }
- }
-
-
- public int getCurrentPosition()
- {
- int currentPosition = 0;
- if ((mState & (State.Idle
- | State.Initialized
- | State.Prepared
- | State.Started
- | State.Paused
- | State.Stopped
- | State.PlaybackCompleted)) == 0) {
- return currentPosition;
- }
-
- try {
- currentPosition = mMediaPlayer.getCurrentPosition();
- } catch (final IllegalStateException e) {
- Log.d(TAG, "" + e.getMessage());
- }
-
- return currentPosition;
- }
-
-
- public int getDuration()
- {
- int duration = 0;
- if ((mState & (State.Prepared
- | State.Started
- | State.Paused
- | State.Stopped
- | State.PlaybackCompleted)) == 0) {
- return duration;
- }
-
- try {
- duration = mMediaPlayer.getDuration();
- } catch (final IllegalStateException e) {
- Log.d(TAG, "" + e.getMessage());
- }
-
- return duration;
- }
-
- public void setVolume(int volume)
- {
- if (volume < 0)
- volume = 0;
-
- if (volume > 100)
- volume = 100;
-
- mVolume = volume;
-
- if (!mMuted)
- setVolumeHelper(mVolume);
- }
-
- private void setVolumeHelper(int volume)
- {
- if ((mState & (State.Idle
- | State.Initialized
- | State.Stopped
- | State.Prepared
- | State.Started
- | State.Paused
- | State.PlaybackCompleted)) == 0) {
- return;
- }
-
- try {
- float newVolume = (float)volume / 100;
- mMediaPlayer.setVolume(newVolume, newVolume);
- } catch (final IllegalStateException e) {
- Log.d(TAG, "" + e.getMessage());
- }
- }
-
- public SurfaceHolder display()
- {
- return mSurfaceHolder;
- }
-
- public void setDisplay(SurfaceHolder sh)
- {
- mSurfaceHolder = sh;
-
- if ((mState & State.Uninitialized) != 0)
- return;
-
- mMediaPlayer.setDisplay(mSurfaceHolder);
- }
-
-
- public int getVolume()
- {
- return mVolume;
- }
-
- public void mute(final boolean mute)
- {
- mMuted = mute;
- setVolumeHelper(mute ? 0 : mVolume);
- }
-
- public boolean isMuted()
- {
- return mMuted;
- }
-
-
- public void reset()
- {
- if ((mState & (State.Idle
- | State.Initialized
- | State.Prepared
- | State.Started
- | State.Paused
- | State.Stopped
- | State.PlaybackCompleted
- | State.Error)) == 0) {
- return;
- }
-
- mMediaPlayer.reset();
- setState(State.Idle);
- }
-
- public void release()
- {
- if (mMediaPlayer != null) {
- mMediaPlayer.reset();
- mMediaPlayer.release();
- mMediaPlayer = null;
- }
-
- setState(State.Uninitialized);
- }
-
- public void setAudioAttributes(int type, int usage)
- {
- mAudioAttributes = new AudioAttributes.Builder()
- .setUsage(usage)
- .setContentType(type)
- .build();
-
- setAudioAttributes(mMediaPlayer, mAudioAttributes);
- }
-
- static private void setAudioAttributes(MediaPlayer player, AudioAttributes attr)
- {
- if (player == null || attr == null)
- return;
-
- try {
- player.setAudioAttributes(attr);
- } catch (final IllegalArgumentException e) {
- Log.d(TAG, "" + e.getMessage());
- }
- }
-}
diff --git a/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtCameraListener.java b/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtCameraListener.java
deleted file mode 100644
index ff26d90c3..000000000
--- a/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtCameraListener.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-package org.qtproject.qt.android.multimedia;
-
-import android.hardware.Camera;
-import android.graphics.ImageFormat;
-import android.graphics.SurfaceTexture;
-import android.util.Log;
-import java.lang.Math;
-
-public class QtCameraListener implements Camera.ShutterCallback,
- Camera.PictureCallback,
- Camera.AutoFocusCallback,
- Camera.PreviewCallback
-{
- private static final String TAG = "Qt Camera";
-
- private static final int BUFFER_POOL_SIZE = 2;
-
- private int m_cameraId = -1;
-
- private boolean m_notifyNewFrames = false;
- private boolean m_notifyWhenFrameAvailable = false;
- private byte[][] m_previewBuffers = null;
- private byte[] m_lastPreviewBuffer = null;
- private Camera.Size m_previewSize = null;
- private int m_previewFormat = ImageFormat.NV21; // Default preview format on all devices
- private int m_previewBytesPerLine = -1;
-
- private QtCameraListener(int id)
- {
- m_cameraId = id;
- }
-
- public void notifyNewFrames(boolean notify)
- {
- m_notifyNewFrames = notify;
- }
-
- public void notifyWhenFrameAvailable(boolean notify)
- {
- m_notifyWhenFrameAvailable = notify;
- }
-
- public byte[] lastPreviewBuffer()
- {
- return m_lastPreviewBuffer;
- }
-
- public int previewWidth()
- {
- if (m_previewSize == null)
- return -1;
-
- return m_previewSize.width;
- }
-
- public int previewHeight()
- {
- if (m_previewSize == null)
- return -1;
-
- return m_previewSize.height;
- }
-
- public int previewFormat()
- {
- return m_previewFormat;
- }
-
- public int previewBytesPerLine()
- {
- return m_previewBytesPerLine;
- }
-
- public void clearPreviewCallback(Camera camera)
- {
- camera.setPreviewCallbackWithBuffer(null);
- }
-
- public void setupPreviewCallback(Camera camera)
- {
- // Clear previous callback (also clears added buffers)
- clearPreviewCallback(camera);
- m_lastPreviewBuffer = null;
-
- final Camera.Parameters params = camera.getParameters();
- m_previewSize = params.getPreviewSize();
- m_previewFormat = params.getPreviewFormat();
-
- int bufferSizeNeeded = 0;
- if (m_previewFormat == ImageFormat.YV12) {
- // For YV12, bytes per line must be a multiple of 16
- final int yStride = (int) Math.ceil(m_previewSize.width / 16.0) * 16;
- final int uvStride = (int) Math.ceil((yStride / 2) / 16.0) * 16;
- final int ySize = yStride * m_previewSize.height;
- final int uvSize = uvStride * m_previewSize.height / 2;
- bufferSizeNeeded = ySize + uvSize * 2;
-
- m_previewBytesPerLine = yStride;
-
- } else {
- double bytesPerPixel = ImageFormat.getBitsPerPixel(m_previewFormat) / 8.0;
- bufferSizeNeeded = (int) Math.ceil(bytesPerPixel * m_previewSize.width * m_previewSize.height);
-
- // bytes per line are calculated only for the first plane
- switch (m_previewFormat) {
- case ImageFormat.NV21:
- m_previewBytesPerLine = m_previewSize.width; // 1 byte per sample and tightly packed
- break;
- case ImageFormat.RGB_565:
- case ImageFormat.YUY2:
- m_previewBytesPerLine = m_previewSize.width * 2; // 2 bytes per pixel
- break;
- default:
- m_previewBytesPerLine = -1;
- break;
- }
- }
-
- // We could keep the same buffers when they are already bigger than the required size
- // but the Android doc says the size must match, so in doubt just replace them.
- if (m_previewBuffers == null || m_previewBuffers[0].length != bufferSizeNeeded)
- m_previewBuffers = new byte[BUFFER_POOL_SIZE][bufferSizeNeeded];
-
- // Add callback and queue all buffers
- camera.setPreviewCallbackWithBuffer(this);
- for (byte[] buffer : m_previewBuffers)
- camera.addCallbackBuffer(buffer);
- }
-
- @Override
- public void onPreviewFrame(byte[] data, Camera camera)
- {
- // Re-enqueue the last buffer
- if (m_lastPreviewBuffer != null)
- camera.addCallbackBuffer(m_lastPreviewBuffer);
-
- m_lastPreviewBuffer = data;
-
- if (data != null) {
- if (m_notifyWhenFrameAvailable) {
- m_notifyWhenFrameAvailable = false;
- notifyFrameAvailable(m_cameraId);
- }
- if (m_notifyNewFrames) {
- notifyNewPreviewFrame(m_cameraId, data,
- m_previewSize.width, m_previewSize.height,
- m_previewFormat,
- m_previewBytesPerLine);
- }
- }
- }
-
- @Override
- public void onShutter()
- {
- notifyPictureExposed(m_cameraId);
- }
-
- @Override
- public void onPictureTaken(byte[] data, Camera camera)
- {
- notifyPictureCaptured(m_cameraId, data);
- }
-
- @Override
- public void onAutoFocus(boolean success, Camera camera)
- {
- notifyAutoFocusComplete(m_cameraId, success);
- }
-
- private static native void notifyAutoFocusComplete(int id, boolean success);
- private static native void notifyPictureExposed(int id);
- private static native void notifyPictureCaptured(int id, byte[] data);
- private static native void notifyNewPreviewFrame(int id, byte[] data, int width, int height,
- int pixelFormat, int bytesPerLine);
- private static native void notifyFrameAvailable(int id);
-}
diff --git a/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtMediaRecorderListener.java b/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtMediaRecorderListener.java
deleted file mode 100644
index bf1763dee..000000000
--- a/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtMediaRecorderListener.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-package org.qtproject.qt.android.multimedia;
-
-import android.media.MediaRecorder;
-
-public class QtMediaRecorderListener implements MediaRecorder.OnErrorListener, MediaRecorder.OnInfoListener
-{
- private long m_id = -1;
-
- public QtMediaRecorderListener(long id)
- {
- m_id = id;
- }
-
- @Override
- public void onError(MediaRecorder mr, int what, int extra)
- {
- notifyError(m_id, what, extra);
- }
-
- @Override
- public void onInfo(MediaRecorder mr, int what, int extra)
- {
- notifyInfo(m_id, what, extra);
- }
-
- private static native void notifyError(long id, int what, int extra);
- private static native void notifyInfo(long id, int what, int extra);
-}
diff --git a/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtMultimediaUtils.java b/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtMultimediaUtils.java
deleted file mode 100644
index 28d56e0dd..000000000
--- a/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtMultimediaUtils.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-package org.qtproject.qt.android.multimedia;
-
-import android.app.Activity;
-import android.content.Context;
-import android.view.OrientationEventListener;
-import android.os.Environment;
-import android.media.MediaScannerConnection;
-import java.lang.String;
-import java.io.File;
-
-public class QtMultimediaUtils
-{
- static private class OrientationListener extends OrientationEventListener
- {
- static public int deviceOrientation = 0;
-
- public OrientationListener(Context context)
- {
- super(context);
- }
-
- @Override
- public void onOrientationChanged(int orientation)
- {
- if (orientation == ORIENTATION_UNKNOWN)
- return;
-
- deviceOrientation = orientation;
- }
- }
-
- static private Context m_context = null;
- static private OrientationListener m_orientationListener = null;
-
- static public void setActivity(Activity qtMainActivity, Object qtActivityDelegate)
- {
- }
-
- static public void setContext(Context context)
- {
- m_context = context;
- m_orientationListener = new OrientationListener(context);
- }
-
- public QtMultimediaUtils()
- {
- }
-
- static void enableOrientationListener(boolean enable)
- {
- if (enable)
- m_orientationListener.enable();
- else
- m_orientationListener.disable();
- }
-
- static int getDeviceOrientation()
- {
- return m_orientationListener.deviceOrientation;
- }
-
- static String getDefaultMediaDirectory(int type)
- {
- String dirType = new String();
- switch (type) {
- case 0:
- dirType = Environment.DIRECTORY_MUSIC;
- break;
- case 1:
- dirType = Environment.DIRECTORY_MOVIES;
- break;
- case 2:
- dirType = Environment.DIRECTORY_DCIM;
- break;
- default:
- break;
- }
-
- File path = new File("");
- if (type == 3) {
- // There is no API for knowing the standard location for sounds
- // such as voice recording. Though, it's typically in the 'Sounds'
- // directory at the root of the external storage
- path = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
- + File.separator + "Sounds");
- } else {
- path = Environment.getExternalStoragePublicDirectory(dirType);
- }
-
- path.mkdirs(); // make sure the directory exists
-
- return path.getAbsolutePath();
- }
-
- static void registerMediaFile(String file)
- {
- MediaScannerConnection.scanFile(m_context, new String[] { file }, null, null);
- }
-}
diff --git a/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceHolderCallback.java b/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceHolderCallback.java
deleted file mode 100644
index 62000716b..000000000
--- a/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceHolderCallback.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-package org.qtproject.qt.android.multimedia;
-
-import android.view.SurfaceHolder;
-
-public class QtSurfaceHolderCallback implements SurfaceHolder.Callback
-{
- private long m_id = -1;
-
- public QtSurfaceHolderCallback(long id)
- {
- m_id = id;
- }
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
- {
- }
-
- @Override
- public void surfaceCreated(SurfaceHolder holder)
- {
- notifySurfaceCreated(m_id);
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder)
- {
- notifySurfaceDestroyed(m_id);
- }
-
-
- private static native void notifySurfaceCreated(long id);
- private static native void notifySurfaceDestroyed(long id);
-}
diff --git a/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureHolder.java b/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureHolder.java
deleted file mode 100644
index ea7a41505..000000000
--- a/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureHolder.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-package org.qtproject.qt.android.multimedia;
-
-import android.view.SurfaceHolder;
-import android.view.Surface;
-import android.graphics.Rect;
-import android.graphics.Canvas;
-
-public class QtSurfaceTextureHolder implements SurfaceHolder
-{
- private Surface surfaceTexture;
-
- public QtSurfaceTextureHolder(Surface surface)
- {
- surfaceTexture = surface;
- }
-
- @Override
- public void addCallback(SurfaceHolder.Callback callback)
- {
- }
-
- @Override
- public Surface getSurface()
- {
- return surfaceTexture;
- }
-
- @Override
- public Rect getSurfaceFrame()
- {
- return new Rect();
- }
-
- @Override
- public boolean isCreating()
- {
- return false;
- }
-
- @Override
- public Canvas lockCanvas(Rect dirty)
- {
- return new Canvas();
- }
-
- @Override
- public Canvas lockCanvas()
- {
- return new Canvas();
- }
-
- @Override
- public void removeCallback(SurfaceHolder.Callback callback)
- {
- }
-
- @Override
- public void setFixedSize(int width, int height)
- {
- }
-
- @Override
- public void setFormat(int format)
- {
- }
-
- @Override
- public void setKeepScreenOn(boolean screenOn)
- {
- }
-
- @Override
- public void setSizeFromLayout()
- {
- }
-
- @Override
- public void setType(int type)
- {
- }
-
- @Override
- public void unlockCanvasAndPost(Canvas canvas)
- {
- }
-}
diff --git a/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureListener.java b/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureListener.java
deleted file mode 100644
index 4d929c6ad..000000000
--- a/src/plugins/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureListener.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-package org.qtproject.qt.android.multimedia;
-
-import android.graphics.SurfaceTexture;
-
-public class QtSurfaceTextureListener implements SurfaceTexture.OnFrameAvailableListener
-{
- private final long m_id;
-
- public QtSurfaceTextureListener(long id)
- {
- m_id = id;
- }
-
- @Override
- public void onFrameAvailable(SurfaceTexture surfaceTexture)
- {
- notifyFrameAvailable(m_id);
- }
-
- private static native void notifyFrameAvailable(long id);
-}
diff --git a/src/plugins/android/src/android_mediaservice.json b/src/plugins/android/src/android_mediaservice.json
deleted file mode 100644
index df4bccb2b..000000000
--- a/src/plugins/android/src/android_mediaservice.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Keys": ["androidmultimedia"],
- "Services": ["org.qt-project.qt.camera", "org.qt-project.qt.mediaplayer", "org.qt-project.qt.audiosource"]
-}
diff --git a/src/plugins/android/src/common/common.pri b/src/plugins/android/src/common/common.pri
deleted file mode 100644
index 1b02b99ea..000000000
--- a/src/plugins/android/src/common/common.pri
+++ /dev/null
@@ -1,10 +0,0 @@
-INCLUDEPATH += $$PWD
-
-HEADERS += \
- $$PWD/qandroidglobal.h \
- $$PWD/qandroidvideooutput.h \
- $$PWD/qandroidmultimediautils.h
-
-SOURCES += \
- $$PWD/qandroidvideooutput.cpp \
- $$PWD/qandroidmultimediautils.cpp
diff --git a/src/plugins/android/src/common/qandroidglobal.h b/src/plugins/android/src/common/qandroidglobal.h
deleted file mode 100644
index e7342be97..000000000
--- a/src/plugins/android/src/common/qandroidglobal.h
+++ /dev/null
@@ -1,52 +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 QANDROIDGLOBAL_H
-#define QANDROIDGLOBAL_H
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qloggingcategory.h>
-
-QT_BEGIN_NAMESPACE
-
-Q_DECLARE_LOGGING_CATEGORY(qtAndroidMediaPlugin)
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDGLOBAL_H
diff --git a/src/plugins/android/src/common/qandroidmultimediautils.cpp b/src/plugins/android/src/common/qandroidmultimediautils.cpp
deleted file mode 100644
index 1f03d5d29..000000000
--- a/src/plugins/android/src/common/qandroidmultimediautils.cpp
+++ /dev/null
@@ -1,152 +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 "qandroidmultimediautils.h"
-#include "qandroidglobal.h"
-
-#include <qlist.h>
-#include <QtCore/private/qjni_p.h>
-#include <QtCore/private/qjnihelpers_p.h>
-
-QT_BEGIN_NAMESPACE
-
-int qt_findClosestValue(const QList<int> &list, int value)
-{
- if (list.size() < 2)
- return 0;
-
- int begin = 0;
- int end = list.size() - 1;
- int pivot = begin + (end - begin) / 2;
- int v = list.at(pivot);
-
- while (end - begin > 1) {
- if (value == v)
- return pivot;
-
- if (value > v)
- begin = pivot;
- else
- end = pivot;
-
- pivot = begin + (end - begin) / 2;
- v = list.at(pivot);
- }
-
- return value - v >= list.at(pivot + 1) - value ? pivot + 1 : pivot;
-}
-
-bool qt_sizeLessThan(const QSize &s1, const QSize &s2)
-{
- return s1.width() * s1.height() < s2.width() * s2.height();
-}
-
-QVideoFrame::PixelFormat qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat f)
-{
- switch (f) {
- case AndroidCamera::NV21:
- return QVideoFrame::Format_NV21;
- case AndroidCamera::YV12:
- return QVideoFrame::Format_YV12;
- case AndroidCamera::RGB565:
- return QVideoFrame::Format_RGB565;
- case AndroidCamera::YUY2:
- return QVideoFrame::Format_YUYV;
- case AndroidCamera::JPEG:
- return QVideoFrame::Format_Jpeg;
- default:
- return QVideoFrame::Format_Invalid;
- }
-}
-
-AndroidCamera::ImageFormat qt_androidImageFormatFromPixelFormat(QVideoFrame::PixelFormat f)
-{
- switch (f) {
- case QVideoFrame::Format_NV21:
- return AndroidCamera::NV21;
- case QVideoFrame::Format_YV12:
- return AndroidCamera::YV12;
- case QVideoFrame::Format_RGB565:
- return AndroidCamera::RGB565;
- case QVideoFrame::Format_YUYV:
- return AndroidCamera::YUY2;
- case QVideoFrame::Format_Jpeg:
- return AndroidCamera::JPEG;
- default:
- return AndroidCamera::UnknownImageFormat;
- }
-}
-
-static bool androidRequestPermission(const QString &key)
-{
- using namespace QtAndroidPrivate;
-
- if (androidSdkVersion() < 23)
- return true;
-
- PermissionsResult res = checkPermission(key);
- if (res == PermissionsResult::Granted) // Permission already granted?
- return true;
-
- QJNIEnvironmentPrivate env;
- const auto &results = requestPermissionsSync(env, QStringList() << key);
- if (!results.contains(key)) {
- qCWarning(qtAndroidMediaPlugin, "No permission found for key: %s", qPrintable(key));
- return false;
- }
-
- if (results[key] == PermissionsResult::Denied) {
- qCDebug(qtAndroidMediaPlugin, "%s - Permission denied by user!", qPrintable(key));
- return false;
- }
-
- return true;
-}
-
-bool qt_androidRequestCameraPermission()
-{
- return androidRequestPermission(QLatin1String("android.permission.CAMERA"));
-}
-
-bool qt_androidRequestRecordingPermission()
-{
- return androidRequestPermission(QLatin1String("android.permission.RECORD_AUDIO"));
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/common/qandroidmultimediautils.h b/src/plugins/android/src/common/qandroidmultimediautils.h
deleted file mode 100644
index 381671cb8..000000000
--- a/src/plugins/android/src/common/qandroidmultimediautils.h
+++ /dev/null
@@ -1,63 +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 QANDROIDMULTIMEDIAUTILS_H
-#define QANDROIDMULTIMEDIAUTILS_H
-
-#include <qglobal.h>
-#include <qsize.h>
-#include "androidcamera.h"
-
-QT_BEGIN_NAMESPACE
-
-// return the index of the closest value to <value> in <list>
-// (binary search)
-int qt_findClosestValue(const QList<int> &list, int value);
-
-bool qt_sizeLessThan(const QSize &s1, const QSize &s2);
-
-QVideoFrame::PixelFormat qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat f);
-AndroidCamera::ImageFormat qt_androidImageFormatFromPixelFormat(QVideoFrame::PixelFormat f);
-
-bool qt_androidRequestCameraPermission();
-bool qt_androidRequestRecordingPermission();
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDMULTIMEDIAUTILS_H
diff --git a/src/plugins/android/src/common/qandroidvideooutput.cpp b/src/plugins/android/src/common/qandroidvideooutput.cpp
deleted file mode 100644
index 27cea76ad..000000000
--- a/src/plugins/android/src/common/qandroidvideooutput.cpp
+++ /dev/null
@@ -1,504 +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 "qandroidvideooutput.h"
-
-#include "androidsurfacetexture.h"
-#include <QAbstractVideoSurface>
-#include <QVideoSurfaceFormat>
-#include <qevent.h>
-#include <qcoreapplication.h>
-#include <qopenglcontext.h>
-#include <qopenglfunctions.h>
-#include <qopenglshaderprogram.h>
-#include <qopenglframebufferobject.h>
-#include <QtCore/private/qjnihelpers_p.h>
-#include <QtGui/QWindow>
-#include <QtGui/QOffscreenSurface>
-
-QT_BEGIN_NAMESPACE
-
-static const GLfloat g_vertex_data[] = {
- -1.f, 1.f,
- 1.f, 1.f,
- 1.f, -1.f,
- -1.f, -1.f
-};
-
-static const GLfloat g_texture_data[] = {
- 0.f, 0.f,
- 1.f, 0.f,
- 1.f, 1.f,
- 0.f, 1.f
-};
-
-void OpenGLResourcesDeleter::deleteTextureHelper(quint32 id)
-{
- if (id != 0)
- glDeleteTextures(1, &id);
-}
-
-void OpenGLResourcesDeleter::deleteFboHelper(void *fbo)
-{
- delete reinterpret_cast<QOpenGLFramebufferObject *>(fbo);
-}
-
-void OpenGLResourcesDeleter::deleteShaderProgramHelper(void *prog)
-{
- delete reinterpret_cast<QOpenGLShaderProgram *>(prog);
-}
-
-void OpenGLResourcesDeleter::deleteThisHelper()
-{
- delete this;
-}
-
-class AndroidTextureVideoBuffer : public QAbstractVideoBuffer
-{
-public:
- AndroidTextureVideoBuffer(QAndroidTextureVideoOutput *output, const QSize &size)
- : QAbstractVideoBuffer(GLTextureHandle)
- , m_mapMode(NotMapped)
- , m_output(output)
- , m_size(size)
- , m_textureUpdated(false)
- {
- }
-
- virtual ~AndroidTextureVideoBuffer() {}
-
- MapMode mapMode() const { return m_mapMode; }
-
- uchar *map(MapMode mode, int *numBytes, int *bytesPerLine)
- {
- if (m_mapMode == NotMapped && mode == ReadOnly && updateFrame()) {
- m_mapMode = mode;
- m_image = m_output->m_fbo->toImage();
-
- if (numBytes)
- *numBytes = static_cast<int>(m_image.sizeInBytes());
-
- if (bytesPerLine)
- *bytesPerLine = m_image.bytesPerLine();
-
- return m_image.bits();
- } else {
- return 0;
- }
- }
-
- void unmap()
- {
- m_image = QImage();
- m_mapMode = NotMapped;
- }
-
- QVariant handle() const
- {
- AndroidTextureVideoBuffer *that = const_cast<AndroidTextureVideoBuffer*>(this);
- if (!that->updateFrame())
- return QVariant();
-
- return m_output->m_fbo->texture();
- }
-
-private:
- bool updateFrame()
- {
- // 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_fbo;
-
- if (m_textureUpdated && !forceUpdate)
- return true;
-
- // update the video texture (called from the render thread)
- return (m_textureUpdated = m_output->renderFrameToFbo());
- }
-
- MapMode m_mapMode;
- QAndroidTextureVideoOutput *m_output;
- QImage m_image;
- QSize m_size;
- bool m_textureUpdated;
-};
-
-QAndroidTextureVideoOutput::QAndroidTextureVideoOutput(QObject *parent)
- : QAndroidVideoOutput(parent)
- , m_surface(0)
- , m_surfaceTexture(0)
- , m_externalTex(0)
- , m_fbo(0)
- , m_program(0)
- , m_glDeleter(0)
- , m_surfaceTextureCanAttachToContext(QtAndroidPrivate::androidSdkVersion() >= 16)
-{
-
-}
-
-QAndroidTextureVideoOutput::~QAndroidTextureVideoOutput()
-{
- delete m_offscreenSurface;
- delete m_glContext;
- clearSurfaceTexture();
-
- if (m_glDeleter) { // Make sure all of these are deleted on the render thread.
- m_glDeleter->deleteFbo(m_fbo);
- m_glDeleter->deleteShaderProgram(m_program);
- m_glDeleter->deleteTexture(m_externalTex);
- m_glDeleter->deleteThis();
- }
-}
-
-QAbstractVideoSurface *QAndroidTextureVideoOutput::surface() const
-{
- return m_surface;
-}
-
-void QAndroidTextureVideoOutput::setSurface(QAbstractVideoSurface *surface)
-{
- if (surface == m_surface)
- return;
-
- if (m_surface) {
- if (m_surface->isActive())
- m_surface->stop();
-
- if (!m_surfaceTextureCanAttachToContext)
- m_surface->setProperty("_q_GLThreadCallback", QVariant());
- }
-
- m_surface = surface;
-
- if (m_surface && !m_surfaceTextureCanAttachToContext) {
- m_surface->setProperty("_q_GLThreadCallback",
- QVariant::fromValue<QObject*>(this));
- }
-}
-
-bool QAndroidTextureVideoOutput::isReady()
-{
- return m_surfaceTextureCanAttachToContext || QOpenGLContext::currentContext() || m_externalTex;
-}
-
-bool QAndroidTextureVideoOutput::initSurfaceTexture()
-{
- if (m_surfaceTexture)
- return true;
-
- if (!m_surface)
- return false;
-
- if (!m_surfaceTextureCanAttachToContext) {
- // if we have an OpenGL context in the current thread, create a texture. Otherwise, wait
- // for the GL render thread to call us back to do it.
- if (QOpenGLContext::currentContext()) {
- glGenTextures(1, &m_externalTex);
- if (!m_glDeleter)
- m_glDeleter = new OpenGLResourcesDeleter;
- } else if (!m_externalTex) {
- return false;
- }
- }
-
- QMutexLocker locker(&m_mutex);
-
- m_surfaceTexture = new AndroidSurfaceTexture(m_externalTex);
-
- if (m_surfaceTexture->surfaceTexture() != 0) {
- connect(m_surfaceTexture, SIGNAL(frameAvailable()), this, SLOT(onFrameAvailable()));
- } else {
- delete m_surfaceTexture;
- m_surfaceTexture = 0;
- if (m_glDeleter)
- m_glDeleter->deleteTexture(m_externalTex);
- m_externalTex = 0;
- }
-
- return m_surfaceTexture != 0;
-}
-
-void QAndroidTextureVideoOutput::clearSurfaceTexture()
-{
- QMutexLocker locker(&m_mutex);
- if (m_surfaceTexture) {
- delete m_surfaceTexture;
- m_surfaceTexture = 0;
- }
-
- // Also reset the attached OpenGL texture
- // Note: The Android SurfaceTexture class does not release the texture on deletion,
- // only if detachFromGLContext() called (API level >= 16), so we'll do it manually,
- // on the render thread.
- if (m_surfaceTextureCanAttachToContext) {
- if (m_glDeleter)
- m_glDeleter->deleteTexture(m_externalTex);
- m_externalTex = 0;
- }
-}
-
-AndroidSurfaceTexture *QAndroidTextureVideoOutput::surfaceTexture()
-{
- if (!initSurfaceTexture())
- return 0;
-
- return m_surfaceTexture;
-}
-
-void QAndroidTextureVideoOutput::setVideoSize(const QSize &size)
-{
- QMutexLocker locker(&m_mutex);
- if (m_nativeSize == size)
- return;
-
- stop();
-
- m_nativeSize = size;
-}
-
-void QAndroidTextureVideoOutput::stop()
-{
- if (m_surface && m_surface->isActive())
- m_surface->stop();
- m_nativeSize = QSize();
-}
-
-void QAndroidTextureVideoOutput::reset()
-{
- // flush pending frame
- if (m_surface)
- m_surface->present(QVideoFrame());
-
- clearSurfaceTexture();
-}
-
-void QAndroidTextureVideoOutput::onFrameAvailable()
-{
- if (!m_nativeSize.isValid() || !m_surface)
- return;
-
- QAbstractVideoBuffer *buffer = new AndroidTextureVideoBuffer(this, m_nativeSize);
- QVideoFrame frame(buffer, m_nativeSize, QVideoFrame::Format_ABGR32);
-
- if (m_surface->isActive() && (m_surface->surfaceFormat().pixelFormat() != frame.pixelFormat()
- || m_surface->surfaceFormat().frameSize() != frame.size())) {
- m_surface->stop();
- }
-
- if (!m_surface->isActive()) {
- QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(),
- QAbstractVideoBuffer::GLTextureHandle);
-
- m_surface->start(format);
- }
-
- if (m_surface->isActive())
- m_surface->present(frame);
-}
-
-bool QAndroidTextureVideoOutput::renderFrameToFbo()
-{
- QMutexLocker locker(&m_mutex);
-
- if (!m_nativeSize.isValid() || !m_surfaceTexture)
- return false;
-
- QOpenGLContext *shareContext = !m_glContext && m_surface
- ? qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>())
- : nullptr;
-
- // Make sure we have an OpenGL context to make current.
- if (shareContext || (!QOpenGLContext::currentContext() && !m_glContext)) {
- // Create Hidden QWindow surface to create context in this thread.
- m_offscreenSurface = new QWindow();
- m_offscreenSurface->setSurfaceType(QWindow::OpenGLSurface);
- // Needs geometry to be a valid surface, but size is not important.
- m_offscreenSurface->setGeometry(0, 0, 1, 1);
- m_offscreenSurface->create();
- m_offscreenSurface->moveToThread(m_surface->thread());
-
- // Create OpenGL context and set share context from surface.
- m_glContext = new QOpenGLContext();
- m_glContext->setFormat(m_offscreenSurface->requestedFormat());
-
- auto surface = qobject_cast<QAbstractVideoSurface *>(m_surface->property("videoSurface").value<QObject *>());
- if (!surface)
- surface = m_surface;
- if (shareContext)
- m_glContext->setShareContext(shareContext);
-
- if (!m_glContext->create()) {
- qWarning("Failed to create QOpenGLContext");
- return false;
- }
- }
-
- if (m_glContext)
- m_glContext->makeCurrent(m_offscreenSurface);
-
- createGLResources();
-
- m_surfaceTexture->updateTexImage();
-
- // save current render states
- GLboolean stencilTestEnabled;
- GLboolean depthTestEnabled;
- GLboolean scissorTestEnabled;
- GLboolean blendEnabled;
- glGetBooleanv(GL_STENCIL_TEST, &stencilTestEnabled);
- glGetBooleanv(GL_DEPTH_TEST, &depthTestEnabled);
- glGetBooleanv(GL_SCISSOR_TEST, &scissorTestEnabled);
- glGetBooleanv(GL_BLEND, &blendEnabled);
-
- if (stencilTestEnabled)
- glDisable(GL_STENCIL_TEST);
- if (depthTestEnabled)
- glDisable(GL_DEPTH_TEST);
- if (scissorTestEnabled)
- glDisable(GL_SCISSOR_TEST);
- if (blendEnabled)
- glDisable(GL_BLEND);
-
- m_fbo->bind();
-
- glViewport(0, 0, m_nativeSize.width(), m_nativeSize.height());
-
- m_program->bind();
- m_program->enableAttributeArray(0);
- m_program->enableAttributeArray(1);
- m_program->setUniformValue("frameTexture", GLuint(0));
- m_program->setUniformValue("texMatrix", m_surfaceTexture->getTransformMatrix());
-
- glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, g_vertex_data);
- glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, g_texture_data);
-
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-
- m_program->disableAttributeArray(0);
- m_program->disableAttributeArray(1);
-
- glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
- m_fbo->release();
-
- // restore render states
- if (stencilTestEnabled)
- glEnable(GL_STENCIL_TEST);
- if (depthTestEnabled)
- glEnable(GL_DEPTH_TEST);
- if (scissorTestEnabled)
- glEnable(GL_SCISSOR_TEST);
- if (blendEnabled)
- glEnable(GL_BLEND);
-
- return true;
-}
-
-void QAndroidTextureVideoOutput::createGLResources()
-{
- Q_ASSERT(QOpenGLContext::currentContext() != NULL);
-
- if (!m_glDeleter)
- m_glDeleter = new OpenGLResourcesDeleter;
-
- if (m_surfaceTextureCanAttachToContext && !m_externalTex) {
- m_surfaceTexture->detachFromGLContext();
- glGenTextures(1, &m_externalTex);
- m_surfaceTexture->attachToGLContext(m_externalTex);
- }
-
- if (!m_fbo || m_fbo->size() != m_nativeSize) {
- delete m_fbo;
- m_fbo = new QOpenGLFramebufferObject(m_nativeSize);
- }
-
- if (!m_program) {
- m_program = new QOpenGLShaderProgram;
-
- QOpenGLShader *vertexShader = new QOpenGLShader(QOpenGLShader::Vertex, m_program);
- vertexShader->compileSourceCode("attribute highp vec4 vertexCoordsArray; \n" \
- "attribute highp vec2 textureCoordArray; \n" \
- "uniform highp mat4 texMatrix; \n" \
- "varying highp vec2 textureCoords; \n" \
- "void main(void) \n" \
- "{ \n" \
- " gl_Position = vertexCoordsArray; \n" \
- " textureCoords = (texMatrix * vec4(textureCoordArray, 0.0, 1.0)).xy; \n" \
- "}\n");
- m_program->addShader(vertexShader);
-
- QOpenGLShader *fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment, m_program);
- fragmentShader->compileSourceCode("#extension GL_OES_EGL_image_external : require \n" \
- "varying highp vec2 textureCoords; \n" \
- "uniform samplerExternalOES frameTexture; \n" \
- "void main() \n" \
- "{ \n" \
- " gl_FragColor = texture2D(frameTexture, textureCoords); \n" \
- "}\n");
- m_program->addShader(fragmentShader);
-
- m_program->bindAttributeLocation("vertexCoordsArray", 0);
- m_program->bindAttributeLocation("textureCoordArray", 1);
- m_program->link();
- }
-}
-
-void QAndroidTextureVideoOutput::customEvent(QEvent *e)
-{
- if (e->type() == QEvent::User) {
- // This is running in the render thread (OpenGL enabled)
- if (!m_surfaceTextureCanAttachToContext && !m_externalTex) {
- glGenTextures(1, &m_externalTex);
- if (!m_glDeleter) // We'll use this to cleanup GL resources in the correct thread
- m_glDeleter = new OpenGLResourcesDeleter;
- emit readyChanged(true);
- }
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/common/qandroidvideooutput.h b/src/plugins/android/src/common/qandroidvideooutput.h
deleted file mode 100644
index 456fe8e22..000000000
--- a/src/plugins/android/src/common/qandroidvideooutput.h
+++ /dev/null
@@ -1,145 +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 QANDROIDVIDEOOUTPUT_H
-#define QANDROIDVIDEOOUTPUT_H
-
-#include <qobject.h>
-#include <qsize.h>
-#include <qmutex.h>
-
-QT_BEGIN_NAMESPACE
-
-class AndroidSurfaceTexture;
-class AndroidSurfaceHolder;
-class QOpenGLFramebufferObject;
-class QOpenGLShaderProgram;
-class QAbstractVideoSurface;
-class QWindow;
-class QOpenGLContext;
-
-class QAndroidVideoOutput : public QObject
-{
- Q_OBJECT
-public:
- virtual ~QAndroidVideoOutput() { }
-
- virtual AndroidSurfaceTexture *surfaceTexture() { return 0; }
- virtual AndroidSurfaceHolder *surfaceHolder() { return 0; }
-
- virtual bool isReady() { return true; }
-
- virtual void setVideoSize(const QSize &) { }
- virtual void stop() { }
- virtual void reset() { }
-
-Q_SIGNALS:
- void readyChanged(bool);
-
-protected:
- QAndroidVideoOutput(QObject *parent) : QObject(parent) { }
-};
-
-class OpenGLResourcesDeleter : public QObject
-{
- Q_OBJECT
-public:
- void deleteTexture(quint32 id) { QMetaObject::invokeMethod(this, "deleteTextureHelper", Qt::AutoConnection, Q_ARG(quint32, id)); }
- void deleteFbo(QOpenGLFramebufferObject *fbo) { QMetaObject::invokeMethod(this, "deleteFboHelper", Qt::AutoConnection, Q_ARG(void *, fbo)); }
- void deleteShaderProgram(QOpenGLShaderProgram *prog) { QMetaObject::invokeMethod(this, "deleteShaderProgramHelper", Qt::AutoConnection, Q_ARG(void *, prog)); }
- void deleteThis() { QMetaObject::invokeMethod(this, "deleteThisHelper"); }
-
-private:
- Q_INVOKABLE void deleteTextureHelper(quint32 id);
- Q_INVOKABLE void deleteFboHelper(void *fbo);
- Q_INVOKABLE void deleteShaderProgramHelper(void *prog);
- Q_INVOKABLE void deleteThisHelper();
-};
-
-class QAndroidTextureVideoOutput : public QAndroidVideoOutput
-{
- Q_OBJECT
-public:
- explicit QAndroidTextureVideoOutput(QObject *parent = 0);
- ~QAndroidTextureVideoOutput() override;
-
- QAbstractVideoSurface *surface() const;
- void setSurface(QAbstractVideoSurface *surface);
-
- AndroidSurfaceTexture *surfaceTexture() override;
-
- bool isReady() override;
- void setVideoSize(const QSize &) override;
- void stop() override;
- void reset() override;
-
- void customEvent(QEvent *) override;
-
-private Q_SLOTS:
- void onFrameAvailable();
-
-private:
- bool initSurfaceTexture();
- bool renderFrameToFbo();
- void createGLResources();
-
- QMutex m_mutex;
- void clearSurfaceTexture();
-
- QAbstractVideoSurface *m_surface;
- QSize m_nativeSize;
-
- AndroidSurfaceTexture *m_surfaceTexture;
-
- quint32 m_externalTex;
- QOpenGLFramebufferObject *m_fbo;
- QOpenGLShaderProgram *m_program;
- OpenGLResourcesDeleter *m_glDeleter;
-
- bool m_surfaceTextureCanAttachToContext;
-
- QWindow *m_offscreenSurface = nullptr;
- QOpenGLContext *m_glContext = nullptr;
-
- friend class AndroidTextureVideoBuffer;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDVIDEOOUTPUT_H
diff --git a/src/plugins/android/src/mediacapture/mediacapture.pri b/src/plugins/android/src/mediacapture/mediacapture.pri
deleted file mode 100644
index a42a6a40c..000000000
--- a/src/plugins/android/src/mediacapture/mediacapture.pri
+++ /dev/null
@@ -1,53 +0,0 @@
-INCLUDEPATH += $$PWD
-
-SOURCES += \
- $$PWD/qandroidcaptureservice.cpp \
- $$PWD/qandroidcameracontrol.cpp \
- $$PWD/qandroidvideodeviceselectorcontrol.cpp \
- $$PWD/qandroidcamerasession.cpp \
- $$PWD/qandroidcamerazoomcontrol.cpp \
- $$PWD/qandroidcameraexposurecontrol.cpp \
- $$PWD/qandroidcameraimageprocessingcontrol.cpp \
- $$PWD/qandroidimageencodercontrol.cpp \
- $$PWD/qandroidcameraimagecapturecontrol.cpp \
- $$PWD/qandroidcameracapturedestinationcontrol.cpp \
- $$PWD/qandroidcameracapturebufferformatcontrol.cpp \
- $$PWD/qandroidcameraflashcontrol.cpp \
- $$PWD/qandroidcamerafocuscontrol.cpp \
- $$PWD/qandroidviewfindersettingscontrol.cpp \
- $$PWD/qandroidcameralockscontrol.cpp \
- $$PWD/qandroidcapturesession.cpp \
- $$PWD/qandroidmediarecordercontrol.cpp \
- $$PWD/qandroidaudioencodersettingscontrol.cpp \
- $$PWD/qandroidmediacontainercontrol.cpp \
- $$PWD/qandroidvideoencodersettingscontrol.cpp \
- $$PWD/qandroidaudioinputselectorcontrol.cpp \
- $$PWD/qandroidmediavideoprobecontrol.cpp \
- $$PWD/qandroidcamerainfocontrol.cpp \
- $$PWD/qandroidcameravideorenderercontrol.cpp
-
-HEADERS += \
- $$PWD/qandroidcaptureservice.h \
- $$PWD/qandroidcameracontrol.h \
- $$PWD/qandroidvideodeviceselectorcontrol.h \
- $$PWD/qandroidcamerasession.h \
- $$PWD/qandroidcamerazoomcontrol.h \
- $$PWD/qandroidcameraexposurecontrol.h \
- $$PWD/qandroidcameraimageprocessingcontrol.h \
- $$PWD/qandroidimageencodercontrol.h \
- $$PWD/qandroidcameraimagecapturecontrol.h \
- $$PWD/qandroidcameracapturedestinationcontrol.h \
- $$PWD/qandroidcameracapturebufferformatcontrol.h \
- $$PWD/qandroidcameraflashcontrol.h \
- $$PWD/qandroidcamerafocuscontrol.h \
- $$PWD/qandroidviewfindersettingscontrol.h \
- $$PWD/qandroidcameralockscontrol.h \
- $$PWD/qandroidcapturesession.h \
- $$PWD/qandroidmediarecordercontrol.h \
- $$PWD/qandroidaudioencodersettingscontrol.h \
- $$PWD/qandroidmediacontainercontrol.h \
- $$PWD/qandroidvideoencodersettingscontrol.h \
- $$PWD/qandroidaudioinputselectorcontrol.h \
- $$PWD/qandroidmediavideoprobecontrol.h \
- $$PWD/qandroidcamerainfocontrol.h \
- $$PWD/qandroidcameravideorenderercontrol.h
diff --git a/src/plugins/android/src/mediacapture/qandroidaudioencodersettingscontrol.cpp b/src/plugins/android/src/mediacapture/qandroidaudioencodersettingscontrol.cpp
deleted file mode 100644
index 4bd94425d..000000000
--- a/src/plugins/android/src/mediacapture/qandroidaudioencodersettingscontrol.cpp
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "qandroidaudioencodersettingscontrol.h"
-
-#include "qandroidcapturesession.h"
-
-QT_BEGIN_NAMESPACE
-
-QAndroidAudioEncoderSettingsControl::QAndroidAudioEncoderSettingsControl(QAndroidCaptureSession *session)
- : QAudioEncoderSettingsControl()
- , m_session(session)
-{
-}
-
-QStringList QAndroidAudioEncoderSettingsControl::supportedAudioCodecs() const
-{
- return QStringList() << QLatin1String("amr-nb") << QLatin1String("amr-wb") << QLatin1String("aac");
-}
-
-QString QAndroidAudioEncoderSettingsControl::codecDescription(const QString &codecName) const
-{
- if (codecName == QLatin1String("amr-nb"))
- return tr("Adaptive Multi-Rate Narrowband (AMR-NB) audio codec");
- else if (codecName == QLatin1String("amr-wb"))
- return tr("Adaptive Multi-Rate Wideband (AMR-WB) audio codec");
- else if (codecName == QLatin1String("aac"))
- return tr("AAC Low Complexity (AAC-LC) audio codec");
-
- return QString();
-}
-
-QList<int> QAndroidAudioEncoderSettingsControl::supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous) const
-{
- if (continuous)
- *continuous = false;
-
- if (settings.isNull() || settings.codec().isNull() || settings.codec() == QLatin1String("aac")) {
- return QList<int>() << 8000 << 11025 << 12000 << 16000 << 22050
- << 24000 << 32000 << 44100 << 48000 << 96000;
- } else if (settings.codec() == QLatin1String("amr-nb")) {
- return QList<int>() << 8000;
- } else if (settings.codec() == QLatin1String("amr-wb")) {
- return QList<int>() << 16000;
- }
-
- return QList<int>();
-}
-
-QAudioEncoderSettings QAndroidAudioEncoderSettingsControl::audioSettings() const
-{
- return m_session->audioSettings();
-}
-
-void QAndroidAudioEncoderSettingsControl::setAudioSettings(const QAudioEncoderSettings &settings)
-{
- m_session->setAudioSettings(settings);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidaudioencodersettingscontrol.h b/src/plugins/android/src/mediacapture/qandroidaudioencodersettingscontrol.h
deleted file mode 100644
index e68bf6ef7..000000000
--- a/src/plugins/android/src/mediacapture/qandroidaudioencodersettingscontrol.h
+++ /dev/null
@@ -1,67 +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 QANDROIDAUDIOENCODERSETTINGSCONTROL_H
-#define QANDROIDAUDIOENCODERSETTINGSCONTROL_H
-
-#include <qaudioencodersettingscontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCaptureSession;
-
-class QAndroidAudioEncoderSettingsControl : public QAudioEncoderSettingsControl
-{
- Q_OBJECT
-public:
- explicit QAndroidAudioEncoderSettingsControl(QAndroidCaptureSession *session);
-
- QStringList supportedAudioCodecs() const override;
- QString codecDescription(const QString &codecName) const override;
- QList<int> supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous = 0) const override;
- QAudioEncoderSettings audioSettings() const override;
- void setAudioSettings(const QAudioEncoderSettings &settings) override;
-
-private:
- QAndroidCaptureSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDAUDIOENCODERSETTINGSCONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidaudioinputselectorcontrol.cpp b/src/plugins/android/src/mediacapture/qandroidaudioinputselectorcontrol.cpp
deleted file mode 100644
index bf2161a7e..000000000
--- a/src/plugins/android/src/mediacapture/qandroidaudioinputselectorcontrol.cpp
+++ /dev/null
@@ -1,111 +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 "qandroidaudioinputselectorcontrol.h"
-
-#include "qandroidcapturesession.h"
-
-QT_BEGIN_NAMESPACE
-
-QAndroidAudioInputSelectorControl::QAndroidAudioInputSelectorControl(QAndroidCaptureSession *session)
- : QAudioInputSelectorControl()
- , m_session(session)
-{
- connect(m_session, SIGNAL(audioInputChanged(QString)), this, SIGNAL(activeInputChanged(QString)));
-}
-
-QList<QString> QAndroidAudioInputSelectorControl::availableInputs() const
-{
- return QList<QString>() << QLatin1String("default")
- << QLatin1String("mic")
- << QLatin1String("voice_uplink")
- << QLatin1String("voice_downlink")
- << QLatin1String("voice_call")
- << QLatin1String("voice_recognition");
-}
-
-QString QAndroidAudioInputSelectorControl::inputDescription(const QString& name) const
-{
- return availableDeviceDescription(name.toLatin1());
-}
-
-QString QAndroidAudioInputSelectorControl::defaultInput() const
-{
- return QLatin1String("default");
-}
-
-QString QAndroidAudioInputSelectorControl::activeInput() const
-{
- return m_session->audioInput();
-}
-
-void QAndroidAudioInputSelectorControl::setActiveInput(const QString& name)
-{
- m_session->setAudioInput(name);
-}
-
-QList<QByteArray> QAndroidAudioInputSelectorControl::availableDevices()
-{
- return QList<QByteArray>() << "default"
- << "mic"
- << "voice_uplink"
- << "voice_downlink"
- << "voice_call"
- << "voice_recognition";
-}
-
-QString QAndroidAudioInputSelectorControl::availableDeviceDescription(const QByteArray &device)
-{
- if (device == "default")
- return QLatin1String("Default audio source");
- else if (device == "mic")
- return QLatin1String("Microphone audio source");
- else if (device == "voice_uplink")
- return QLatin1String("Voice call uplink (Tx) audio source");
- else if (device == "voice_downlink")
- return QLatin1String("Voice call downlink (Rx) audio source");
- else if (device == "voice_call")
- return QLatin1String("Voice call uplink + downlink audio source");
- else if (device == "voice_recognition")
- return QLatin1String("Microphone audio source tuned for voice recognition");
- else
- return QString();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidaudioinputselectorcontrol.h b/src/plugins/android/src/mediacapture/qandroidaudioinputselectorcontrol.h
deleted file mode 100644
index c24167fb3..000000000
--- a/src/plugins/android/src/mediacapture/qandroidaudioinputselectorcontrol.h
+++ /dev/null
@@ -1,71 +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 QANDROIDAUDIOINPUTSELECTORCONTROL_H
-#define QANDROIDAUDIOINPUTSELECTORCONTROL_H
-
-#include <qaudioinputselectorcontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCaptureSession;
-
-class QAndroidAudioInputSelectorControl : public QAudioInputSelectorControl
-{
- Q_OBJECT
-public:
- explicit QAndroidAudioInputSelectorControl(QAndroidCaptureSession *session);
-
- QList<QString> availableInputs() const;
- QString inputDescription(const QString& name) const;
- QString defaultInput() const;
-
- QString activeInput() const;
- void setActiveInput(const QString& name);
-
- static QList<QByteArray> availableDevices();
- static QString availableDeviceDescription(const QByteArray &device);
-
-private:
- QAndroidCaptureSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDAUDIOINPUTSELECTORCONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidcameracapturebufferformatcontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcameracapturebufferformatcontrol.cpp
deleted file mode 100644
index a47fa4f72..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcameracapturebufferformatcontrol.cpp
+++ /dev/null
@@ -1,64 +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 "qandroidcameracapturebufferformatcontrol.h"
-
-QT_BEGIN_NAMESPACE
-
-QAndroidCameraCaptureBufferFormatControl::QAndroidCameraCaptureBufferFormatControl()
- : QCameraCaptureBufferFormatControl()
-{
-}
-
-QList<QVideoFrame::PixelFormat> QAndroidCameraCaptureBufferFormatControl::supportedBufferFormats() const
-{
- return (QList<QVideoFrame::PixelFormat>() << QVideoFrame::Format_Jpeg);
-}
-
-QVideoFrame::PixelFormat QAndroidCameraCaptureBufferFormatControl::bufferFormat() const
-{
- return QVideoFrame::Format_Jpeg;
-}
-
-void QAndroidCameraCaptureBufferFormatControl::setBufferFormat(QVideoFrame::PixelFormat format)
-{
- Q_UNUSED(format);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcameracapturebufferformatcontrol.h b/src/plugins/android/src/mediacapture/qandroidcameracapturebufferformatcontrol.h
deleted file mode 100644
index b27727543..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcameracapturebufferformatcontrol.h
+++ /dev/null
@@ -1,60 +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 QANDROIDCAMERACAPTUREBUFFERFORMATCONTROL_H
-#define QANDROIDCAMERACAPTUREBUFFERFORMATCONTROL_H
-
-#include <qcameracapturebufferformatcontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCameraCaptureBufferFormatControl : public QCameraCaptureBufferFormatControl
-{
- Q_OBJECT
-public:
- QAndroidCameraCaptureBufferFormatControl();
-
- QList<QVideoFrame::PixelFormat> supportedBufferFormats() const override;
- QVideoFrame::PixelFormat bufferFormat() const override;
- void setBufferFormat(QVideoFrame::PixelFormat format) override;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDCAMERACAPTUREBUFFERFORMATCONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidcameracapturedestinationcontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcameracapturedestinationcontrol.cpp
deleted file mode 100644
index 6a6847007..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcameracapturedestinationcontrol.cpp
+++ /dev/null
@@ -1,69 +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 "qandroidcameracapturedestinationcontrol.h"
-
-#include "qandroidcamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-QAndroidCameraCaptureDestinationControl::QAndroidCameraCaptureDestinationControl(QAndroidCameraSession *session)
- : QCameraCaptureDestinationControl()
- , m_session(session)
-{
- connect(m_session, SIGNAL(captureDestinationChanged(QCameraImageCapture::CaptureDestinations)),
- this, SIGNAL(captureDestinationChanged(QCameraImageCapture::CaptureDestinations)));
-}
-
-bool QAndroidCameraCaptureDestinationControl::isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const
-{
- return m_session->isCaptureDestinationSupported(destination);
-}
-
-QCameraImageCapture::CaptureDestinations QAndroidCameraCaptureDestinationControl::captureDestination() const
-{
- return m_session->captureDestination();;
-}
-
-void QAndroidCameraCaptureDestinationControl::setCaptureDestination(QCameraImageCapture::CaptureDestinations destination)
-{
- m_session->setCaptureDestination(destination);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcameracapturedestinationcontrol.h b/src/plugins/android/src/mediacapture/qandroidcameracapturedestinationcontrol.h
deleted file mode 100644
index 1edfdab12..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcameracapturedestinationcontrol.h
+++ /dev/null
@@ -1,65 +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 QANDROIDCAMERACAPTUREDESTINATIONCONTROL_H
-#define QANDROIDCAMERACAPTUREDESTINATIONCONTROL_H
-
-#include <qcameracapturedestinationcontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCameraSession;
-
-class QAndroidCameraCaptureDestinationControl : public QCameraCaptureDestinationControl
-{
- Q_OBJECT
-public:
- explicit QAndroidCameraCaptureDestinationControl(QAndroidCameraSession *session);
-
- bool isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const override;
- QCameraImageCapture::CaptureDestinations captureDestination() const override;
- void setCaptureDestination(QCameraImageCapture::CaptureDestinations destination) override;
-
-private:
- QAndroidCameraSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDCAMERACAPTUREDESTINATIONCONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidcameracontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcameracontrol.cpp
deleted file mode 100644
index a75215920..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcameracontrol.cpp
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "qandroidcameracontrol.h"
-
-#include "qandroidcamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-QAndroidCameraControl::QAndroidCameraControl(QAndroidCameraSession *session)
- : QCameraControl(0)
- , m_cameraSession(session)
-
-{
- connect(m_cameraSession, SIGNAL(statusChanged(QCamera::Status)),
- this, SIGNAL(statusChanged(QCamera::Status)));
-
- connect(m_cameraSession, SIGNAL(stateChanged(QCamera::State)),
- this, SIGNAL(stateChanged(QCamera::State)));
-
- connect(m_cameraSession, SIGNAL(error(int,QString)), this, SIGNAL(error(int,QString)));
-
- connect(m_cameraSession, SIGNAL(captureModeChanged(QCamera::CaptureModes)),
- this, SIGNAL(captureModeChanged(QCamera::CaptureModes)));
-}
-
-QAndroidCameraControl::~QAndroidCameraControl()
-{
-}
-
-QCamera::CaptureModes QAndroidCameraControl::captureMode() const
-{
- return m_cameraSession->captureMode();
-}
-
-void QAndroidCameraControl::setCaptureMode(QCamera::CaptureModes mode)
-{
- m_cameraSession->setCaptureMode(mode);
-}
-
-bool QAndroidCameraControl::isCaptureModeSupported(QCamera::CaptureModes mode) const
-{
- return m_cameraSession->isCaptureModeSupported(mode);
-}
-
-void QAndroidCameraControl::setState(QCamera::State state)
-{
- m_cameraSession->setState(state);
-}
-
-QCamera::State QAndroidCameraControl::state() const
-{
- return m_cameraSession->state();
-}
-
-QCamera::Status QAndroidCameraControl::status() const
-{
- return m_cameraSession->status();
-}
-
-bool QAndroidCameraControl::canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const
-{
- Q_UNUSED(status);
-
- switch (changeType) {
- case QCameraControl::CaptureMode:
- case QCameraControl::ImageEncodingSettings:
- case QCameraControl::VideoEncodingSettings:
- case QCameraControl::Viewfinder:
- return true;
- default:
- return false;
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcameracontrol.h b/src/plugins/android/src/mediacapture/qandroidcameracontrol.h
deleted file mode 100644
index 80eead627..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcameracontrol.h
+++ /dev/null
@@ -1,74 +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 QANDROIDCAMERACONTROL_H
-#define QANDROIDCAMERACONTROL_H
-
-#include <qcameracontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCameraSession;
-
-class QAndroidCameraControl : public QCameraControl
-{
- Q_OBJECT
-public:
- explicit QAndroidCameraControl(QAndroidCameraSession *session);
- virtual ~QAndroidCameraControl();
-
- QCamera::State state() const;
- void setState(QCamera::State state);
-
- QCamera::Status status() const;
-
- QCamera::CaptureModes captureMode() const;
- void setCaptureMode(QCamera::CaptureModes mode);
- bool isCaptureModeSupported(QCamera::CaptureModes mode) const;
-
- bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const;
-
-private:
- QAndroidCameraSession *m_cameraSession;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDCAMERACONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidcameraexposurecontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcameraexposurecontrol.cpp
deleted file mode 100644
index cf4587379..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcameraexposurecontrol.cpp
+++ /dev/null
@@ -1,293 +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 "qandroidcameraexposurecontrol.h"
-
-#include "qandroidcamerasession.h"
-#include "androidcamera.h"
-
-QT_BEGIN_NAMESPACE
-
-QAndroidCameraExposureControl::QAndroidCameraExposureControl(QAndroidCameraSession *session)
- : QCameraExposureControl()
- , m_session(session)
- , m_minExposureCompensationIndex(0)
- , m_maxExposureCompensationIndex(0)
- , m_exposureCompensationStep(0.0)
- , m_requestedExposureCompensation(0.0)
- , m_actualExposureCompensation(0.0)
- , m_requestedExposureMode(QCameraExposure::ExposureAuto)
- , m_actualExposureMode(QCameraExposure::ExposureAuto)
-{
- connect(m_session, SIGNAL(opened()),
- this, SLOT(onCameraOpened()));
-}
-
-bool QAndroidCameraExposureControl::isParameterSupported(ExposureParameter parameter) const
-{
- if (!m_session->camera())
- return false;
-
- switch (parameter) {
- case QCameraExposureControl::ISO:
- return false;
- case QCameraExposureControl::Aperture:
- return false;
- case QCameraExposureControl::ShutterSpeed:
- return false;
- case QCameraExposureControl::ExposureCompensation:
- return !m_supportedExposureCompensations.isEmpty();
- case QCameraExposureControl::FlashPower:
- return false;
- case QCameraExposureControl::FlashCompensation:
- return false;
- case QCameraExposureControl::TorchPower:
- return false;
- case QCameraExposureControl::SpotMeteringPoint:
- return false;
- case QCameraExposureControl::ExposureMode:
- return !m_supportedExposureModes.isEmpty();
- case QCameraExposureControl::MeteringMode:
- return false;
- default:
- return false;
- }
-}
-
-QVariantList QAndroidCameraExposureControl::supportedParameterRange(ExposureParameter parameter, bool *continuous) const
-{
- if (!m_session->camera())
- return QVariantList();
-
- if (continuous)
- *continuous = false;
-
- if (parameter == QCameraExposureControl::ExposureCompensation)
- return m_supportedExposureCompensations;
- else if (parameter == QCameraExposureControl::ExposureMode)
- return m_supportedExposureModes;
-
- return QVariantList();
-}
-
-QVariant QAndroidCameraExposureControl::requestedValue(ExposureParameter parameter) const
-{
- if (parameter == QCameraExposureControl::ExposureCompensation)
- return QVariant::fromValue(m_requestedExposureCompensation);
- else if (parameter == QCameraExposureControl::ExposureMode)
- return QVariant::fromValue(m_requestedExposureMode);
-
- return QVariant();
-}
-
-QVariant QAndroidCameraExposureControl::actualValue(ExposureParameter parameter) const
-{
- if (parameter == QCameraExposureControl::ExposureCompensation)
- return QVariant::fromValue(m_actualExposureCompensation);
- else if (parameter == QCameraExposureControl::ExposureMode)
- return QVariant::fromValue(m_actualExposureMode);
-
- return QVariant();
-}
-
-bool QAndroidCameraExposureControl::setValue(ExposureParameter parameter, const QVariant& value)
-{
- if (!value.isValid())
- return false;
-
- if (parameter == QCameraExposureControl::ExposureCompensation) {
- qreal expComp = value.toReal();
- if (!qFuzzyCompare(m_requestedExposureCompensation, expComp)) {
- m_requestedExposureCompensation = expComp;
- emit requestedValueChanged(QCameraExposureControl::ExposureCompensation);
- }
-
- if (!m_session->camera())
- return true;
-
- int expCompIndex = qRound(m_requestedExposureCompensation / m_exposureCompensationStep);
- if (expCompIndex >= m_minExposureCompensationIndex
- && expCompIndex <= m_maxExposureCompensationIndex) {
- qreal comp = expCompIndex * m_exposureCompensationStep;
- m_session->camera()->setExposureCompensation(expCompIndex);
- if (!qFuzzyCompare(m_actualExposureCompensation, comp)) {
- m_actualExposureCompensation = expCompIndex * m_exposureCompensationStep;
- emit actualValueChanged(QCameraExposureControl::ExposureCompensation);
- }
-
- return true;
- }
-
- } else if (parameter == QCameraExposureControl::ExposureMode) {
- QCameraExposure::ExposureMode expMode = value.value<QCameraExposure::ExposureMode>();
- if (m_requestedExposureMode != expMode) {
- m_requestedExposureMode = expMode;
- emit requestedValueChanged(QCameraExposureControl::ExposureMode);
- }
-
- if (!m_session->camera())
- return true;
-
- if (!m_supportedExposureModes.isEmpty()) {
- m_actualExposureMode = m_requestedExposureMode;
-
- QString sceneMode;
- switch (m_requestedExposureMode) {
- case QCameraExposure::ExposureAuto:
- sceneMode = QLatin1String("auto");
- break;
- case QCameraExposure::ExposureSports:
- sceneMode = QLatin1String("sports");
- break;
- case QCameraExposure::ExposurePortrait:
- sceneMode = QLatin1String("portrait");
- break;
- case QCameraExposure::ExposureBeach:
- sceneMode = QLatin1String("beach");
- break;
- case QCameraExposure::ExposureSnow:
- sceneMode = QLatin1String("snow");
- break;
- case QCameraExposure::ExposureNight:
- sceneMode = QLatin1String("night");
- break;
- case QCameraExposure::ExposureAction:
- sceneMode = QLatin1String("action");
- break;
- case QCameraExposure::ExposureLandscape:
- sceneMode = QLatin1String("landscape");
- break;
- case QCameraExposure::ExposureNightPortrait:
- sceneMode = QLatin1String("night-portrait");
- break;
- case QCameraExposure::ExposureTheatre:
- sceneMode = QLatin1String("theatre");
- break;
- case QCameraExposure::ExposureSunset:
- sceneMode = QLatin1String("sunset");
- break;
- case QCameraExposure::ExposureSteadyPhoto:
- sceneMode = QLatin1String("steadyphoto");
- break;
- case QCameraExposure::ExposureFireworks:
- sceneMode = QLatin1String("fireworks");
- break;
- case QCameraExposure::ExposureParty:
- sceneMode = QLatin1String("party");
- break;
- case QCameraExposure::ExposureCandlelight:
- sceneMode = QLatin1String("candlelight");
- break;
- case QCameraExposure::ExposureBarcode:
- sceneMode = QLatin1String("barcode");
- break;
- default:
- sceneMode = QLatin1String("auto");
- m_actualExposureMode = QCameraExposure::ExposureAuto;
- break;
- }
-
- m_session->camera()->setSceneMode(sceneMode);
- emit actualValueChanged(QCameraExposureControl::ExposureMode);
-
- return true;
- }
- }
-
- return false;
-}
-
-void QAndroidCameraExposureControl::onCameraOpened()
-{
- m_supportedExposureCompensations.clear();
- m_minExposureCompensationIndex = m_session->camera()->getMinExposureCompensation();
- m_maxExposureCompensationIndex = m_session->camera()->getMaxExposureCompensation();
- m_exposureCompensationStep = m_session->camera()->getExposureCompensationStep();
- if (m_minExposureCompensationIndex != 0 || m_maxExposureCompensationIndex != 0) {
- for (int i = m_minExposureCompensationIndex; i <= m_maxExposureCompensationIndex; ++i)
- m_supportedExposureCompensations.append(i * m_exposureCompensationStep);
- emit parameterRangeChanged(QCameraExposureControl::ExposureCompensation);
- }
-
- m_supportedExposureModes.clear();
- QStringList sceneModes = m_session->camera()->getSupportedSceneModes();
- if (!sceneModes.isEmpty()) {
- for (int i = 0; i < sceneModes.size(); ++i) {
- const QString &sceneMode = sceneModes.at(i);
- if (sceneMode == QLatin1String("auto"))
- m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureAuto);
- else if (sceneMode == QLatin1String("beach"))
- m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureBeach);
- else if (sceneMode == QLatin1String("night"))
- m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureNight);
- else if (sceneMode == QLatin1String("portrait"))
- m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposurePortrait);
- else if (sceneMode == QLatin1String("snow"))
- m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureSnow);
- else if (sceneMode == QLatin1String("sports"))
- m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureSports);
- else if (sceneMode == QLatin1String("action"))
- m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureAction);
- else if (sceneMode == QLatin1String("landscape"))
- m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureLandscape);
- else if (sceneMode == QLatin1String("night-portrait"))
- m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureNightPortrait);
- else if (sceneMode == QLatin1String("theatre"))
- m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureTheatre);
- else if (sceneMode == QLatin1String("sunset"))
- m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureSunset);
- else if (sceneMode == QLatin1String("steadyphoto"))
- m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureSteadyPhoto);
- else if (sceneMode == QLatin1String("fireworks"))
- m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureFireworks);
- else if (sceneMode == QLatin1String("party"))
- m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureParty);
- else if (sceneMode == QLatin1String("candlelight"))
- m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureCandlelight);
- else if (sceneMode == QLatin1String("barcode"))
- m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureBarcode);
- }
- emit parameterRangeChanged(QCameraExposureControl::ExposureMode);
- }
-
- setValue(QCameraExposureControl::ExposureCompensation, QVariant::fromValue(m_requestedExposureCompensation));
- setValue(QCameraExposureControl::ExposureMode, QVariant::fromValue(m_requestedExposureMode));
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcameraexposurecontrol.h b/src/plugins/android/src/mediacapture/qandroidcameraexposurecontrol.h
deleted file mode 100644
index e87e6cd3a..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcameraexposurecontrol.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 QANDROIDCAMERAEXPOSURECONTROL_H
-#define QANDROIDCAMERAEXPOSURECONTROL_H
-
-#include <qcameraexposurecontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCameraSession;
-
-class QAndroidCameraExposureControl : public QCameraExposureControl
-{
- Q_OBJECT
-public:
- explicit QAndroidCameraExposureControl(QAndroidCameraSession *session);
-
- bool isParameterSupported(ExposureParameter parameter) const override;
- QVariantList supportedParameterRange(ExposureParameter parameter, bool *continuous) const override;
-
- QVariant requestedValue(ExposureParameter parameter) const override;
- QVariant actualValue(ExposureParameter parameter) const override;
- bool setValue(ExposureParameter parameter, const QVariant& value) override;
-
-private Q_SLOTS:
- void onCameraOpened();
-
-private:
- QAndroidCameraSession *m_session;
-
- QVariantList m_supportedExposureCompensations;
- QVariantList m_supportedExposureModes;
-
- int m_minExposureCompensationIndex;
- int m_maxExposureCompensationIndex;
- qreal m_exposureCompensationStep;
-
- qreal m_requestedExposureCompensation;
- qreal m_actualExposureCompensation;
- QCameraExposure::ExposureMode m_requestedExposureMode;
- QCameraExposure::ExposureMode m_actualExposureMode;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDCAMERAEXPOSURECONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidcameraflashcontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcameraflashcontrol.cpp
deleted file mode 100644
index 20a4b005c..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcameraflashcontrol.cpp
+++ /dev/null
@@ -1,128 +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 "qandroidcameraflashcontrol.h"
-
-#include "qandroidcamerasession.h"
-#include "androidcamera.h"
-
-QT_BEGIN_NAMESPACE
-
-QAndroidCameraFlashControl::QAndroidCameraFlashControl(QAndroidCameraSession *session)
- : QCameraFlashControl()
- , m_session(session)
- , m_flashMode(QCameraExposure::FlashOff)
-{
- connect(m_session, SIGNAL(opened()),
- this, SLOT(onCameraOpened()));
-}
-
-QCameraExposure::FlashModes QAndroidCameraFlashControl::flashMode() const
-{
- return m_flashMode;
-}
-
-void QAndroidCameraFlashControl::setFlashMode(QCameraExposure::FlashModes mode)
-{
- if (!m_session->camera()) {
- m_flashMode = mode;
- return;
- }
-
- if (!isFlashModeSupported(mode))
- return;
-
- // if torch was enabled, it first needs to be turned off before setting another mode
- if (m_flashMode == QCameraExposure::FlashVideoLight)
- m_session->camera()->setFlashMode(QLatin1String("off"));
-
- m_flashMode = mode;
-
- QString flashMode;
- if (mode.testFlag(QCameraExposure::FlashAuto))
- flashMode = QLatin1String("auto");
- else if (mode.testFlag(QCameraExposure::FlashOn))
- flashMode = QLatin1String("on");
- else if (mode.testFlag(QCameraExposure::FlashRedEyeReduction))
- flashMode = QLatin1String("red-eye");
- else if (mode.testFlag(QCameraExposure::FlashVideoLight))
- flashMode = QLatin1String("torch");
- else // FlashOff
- flashMode = QLatin1String("off");
-
- m_session->camera()->setFlashMode(flashMode);
-}
-
-bool QAndroidCameraFlashControl::isFlashModeSupported(QCameraExposure::FlashModes mode) const
-{
- return m_session->camera() ? m_supportedFlashModes.contains(mode) : false;
-}
-
-bool QAndroidCameraFlashControl::isFlashReady() const
-{
- // Android doesn't have an API for that
- return true;
-}
-
-void QAndroidCameraFlashControl::onCameraOpened()
-{
- m_supportedFlashModes.clear();
-
- QStringList flashModes = m_session->camera()->getSupportedFlashModes();
- for (int i = 0; i < flashModes.size(); ++i) {
- const QString &flashMode = flashModes.at(i);
- if (flashMode == QLatin1String("off"))
- m_supportedFlashModes << QCameraExposure::FlashOff;
- else if (flashMode == QLatin1String("auto"))
- m_supportedFlashModes << QCameraExposure::FlashAuto;
- else if (flashMode == QLatin1String("on"))
- m_supportedFlashModes << QCameraExposure::FlashOn;
- else if (flashMode == QLatin1String("red-eye"))
- m_supportedFlashModes << QCameraExposure::FlashRedEyeReduction;
- else if (flashMode == QLatin1String("torch"))
- m_supportedFlashModes << QCameraExposure::FlashVideoLight;
- }
-
- if (!m_supportedFlashModes.contains(m_flashMode))
- m_flashMode = QCameraExposure::FlashOff;
-
- setFlashMode(m_flashMode);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcameraflashcontrol.h b/src/plugins/android/src/mediacapture/qandroidcameraflashcontrol.h
deleted file mode 100644
index 071c45c72..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcameraflashcontrol.h
+++ /dev/null
@@ -1,71 +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 QANDROIDCAMERAFLASHCONTROL_H
-#define QANDROIDCAMERAFLASHCONTROL_H
-
-#include <qcameraflashcontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCameraSession;
-
-class QAndroidCameraFlashControl : public QCameraFlashControl
-{
- Q_OBJECT
-public:
- explicit QAndroidCameraFlashControl(QAndroidCameraSession *session);
-
- QCameraExposure::FlashModes flashMode() const override;
- void setFlashMode(QCameraExposure::FlashModes mode) override;
- bool isFlashModeSupported(QCameraExposure::FlashModes mode) const override;
- bool isFlashReady() const override;
-
-private Q_SLOTS:
- void onCameraOpened();
-
-private:
- QAndroidCameraSession *m_session;
- QList<QCameraExposure::FlashModes> m_supportedFlashModes;
- QCameraExposure::FlashModes m_flashMode;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDCAMERAFLASHCONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.cpp
deleted file mode 100644
index 4b8a94976..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.cpp
+++ /dev/null
@@ -1,309 +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 "qandroidcamerafocuscontrol.h"
-
-#include "qandroidcamerasession.h"
-#include "androidcamera.h"
-
-QT_BEGIN_NAMESPACE
-
-static QRect adjustedArea(const QRectF &area)
-{
- // Qt maps focus points in the range (0.0, 0.0) -> (1.0, 1.0)
- // Android maps focus points in the range (-1000, -1000) -> (1000, 1000)
- // Converts an area in Qt coordinates to Android coordinates
- return QRect(-1000 + qRound(area.x() * 2000),
- -1000 + qRound(area.y() * 2000),
- qRound(area.width() * 2000),
- qRound(area.height() * 2000))
- .intersected(QRect(-1000, -1000, 2000, 2000));
-}
-
-QAndroidCameraFocusControl::QAndroidCameraFocusControl(QAndroidCameraSession *session)
- : QCameraFocusControl()
- , m_session(session)
- , m_focusMode(QCameraFocus::AutoFocus)
- , m_focusPointMode(QCameraFocus::FocusPointAuto)
- , m_actualFocusPoint(0.5, 0.5)
- , m_continuousPictureFocusSupported(false)
- , m_continuousVideoFocusSupported(false)
-{
- connect(m_session, SIGNAL(opened()),
- this, SLOT(onCameraOpened()));
- connect(m_session, SIGNAL(captureModeChanged(QCamera::CaptureModes)),
- this, SLOT(onCameraCaptureModeChanged()));
-}
-
-QCameraFocus::FocusModes QAndroidCameraFocusControl::focusMode() const
-{
- return m_focusMode;
-}
-
-void QAndroidCameraFocusControl::setFocusMode(QCameraFocus::FocusModes mode)
-{
- if (!m_session->camera()) {
- setFocusModeHelper(mode);
- return;
- }
-
- if (isFocusModeSupported(mode)) {
- QString focusMode = QLatin1String("fixed");
-
- if (mode.testFlag(QCameraFocus::HyperfocalFocus)) {
- focusMode = QLatin1String("edof");
- } else if (mode.testFlag(QCameraFocus::ManualFocus)) {
- focusMode = QLatin1String("fixed");
- } else if (mode.testFlag(QCameraFocus::AutoFocus)) {
- focusMode = QLatin1String("auto");
- } else if (mode.testFlag(QCameraFocus::MacroFocus)) {
- focusMode = QLatin1String("macro");
- } else if (mode.testFlag(QCameraFocus::ContinuousFocus)) {
- if ((m_session->captureMode().testFlag(QCamera::CaptureVideo) && m_continuousVideoFocusSupported)
- || !m_continuousPictureFocusSupported) {
- focusMode = QLatin1String("continuous-video");
- } else {
- focusMode = QLatin1String("continuous-picture");
- }
- } else if (mode.testFlag(QCameraFocus::InfinityFocus)) {
- focusMode = QLatin1String("infinity");
- }
-
- m_session->camera()->setFocusMode(focusMode);
-
- // reset focus position
- m_session->camera()->cancelAutoFocus();
-
- setFocusModeHelper(mode);
- }
-}
-
-bool QAndroidCameraFocusControl::isFocusModeSupported(QCameraFocus::FocusModes mode) const
-{
- return m_session->camera() ? m_supportedFocusModes.contains(mode) : false;
-}
-
-QCameraFocus::FocusPointMode QAndroidCameraFocusControl::focusPointMode() const
-{
- return m_focusPointMode;
-}
-
-void QAndroidCameraFocusControl::setFocusPointMode(QCameraFocus::FocusPointMode mode)
-{
- if (!m_session->camera()) {
- setFocusPointModeHelper(mode);
- return;
- }
-
- if (isFocusPointModeSupported(mode)) {
- if (mode == QCameraFocus::FocusPointCustom) {
- m_actualFocusPoint = m_customFocusPoint;
- } else {
- // FocusPointAuto | FocusPointCenter
- // note: there is no way to know the actual focus point in FocusPointAuto mode,
- // so just report the focus point to be at the center of the frame
- m_actualFocusPoint = QPointF(0.5, 0.5);
- }
-
- setFocusPointModeHelper(mode);
-
- updateFocusZones();
- setCameraFocusArea();
- }
-}
-
-bool QAndroidCameraFocusControl::isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const
-{
- return m_session->camera() ? m_supportedFocusPointModes.contains(mode) : false;
-}
-
-QPointF QAndroidCameraFocusControl::customFocusPoint() const
-{
- return m_customFocusPoint;
-}
-
-void QAndroidCameraFocusControl::setCustomFocusPoint(const QPointF &point)
-{
- if (m_customFocusPoint != point) {
- m_customFocusPoint = point;
- emit customFocusPointChanged(m_customFocusPoint);
- }
-
- if (m_session->camera() && m_focusPointMode == QCameraFocus::FocusPointCustom) {
- m_actualFocusPoint = m_customFocusPoint;
- updateFocusZones();
- setCameraFocusArea();
- }
-}
-
-QCameraFocusZoneList QAndroidCameraFocusControl::focusZones() const
-{
- return m_focusZones;
-}
-
-void QAndroidCameraFocusControl::onCameraOpened()
-{
- connect(m_session->camera(), SIGNAL(previewSizeChanged()),
- this, SLOT(onViewportSizeChanged()));
- connect(m_session->camera(), SIGNAL(autoFocusStarted()),
- this, SLOT(onAutoFocusStarted()));
- connect(m_session->camera(), SIGNAL(autoFocusComplete(bool)),
- this, SLOT(onAutoFocusComplete(bool)));
-
- m_supportedFocusModes.clear();
- m_continuousPictureFocusSupported = false;
- m_continuousVideoFocusSupported = false;
- m_supportedFocusPointModes.clear();
-
- QStringList focusModes = m_session->camera()->getSupportedFocusModes();
- for (int i = 0; i < focusModes.size(); ++i) {
- const QString &focusMode = focusModes.at(i);
- if (focusMode == QLatin1String("auto")) {
- m_supportedFocusModes << QCameraFocus::AutoFocus;
- } else if (focusMode == QLatin1String("continuous-picture")) {
- m_supportedFocusModes << QCameraFocus::ContinuousFocus;
- m_continuousPictureFocusSupported = true;
- } else if (focusMode == QLatin1String("continuous-video")) {
- m_supportedFocusModes << QCameraFocus::ContinuousFocus;
- m_continuousVideoFocusSupported = true;
- } else if (focusMode == QLatin1String("edof")) {
- m_supportedFocusModes << QCameraFocus::HyperfocalFocus;
- } else if (focusMode == QLatin1String("fixed")) {
- m_supportedFocusModes << QCameraFocus::ManualFocus;
- } else if (focusMode == QLatin1String("infinity")) {
- m_supportedFocusModes << QCameraFocus::InfinityFocus;
- } else if (focusMode == QLatin1String("macro")) {
- m_supportedFocusModes << QCameraFocus::MacroFocus;
- }
- }
-
- m_supportedFocusPointModes << QCameraFocus::FocusPointAuto;
- if (m_session->camera()->getMaxNumFocusAreas() > 0)
- m_supportedFocusPointModes << QCameraFocus::FocusPointCenter << QCameraFocus::FocusPointCustom;
-
- if (!m_supportedFocusModes.contains(m_focusMode))
- setFocusModeHelper(QCameraFocus::AutoFocus);
- if (!m_supportedFocusPointModes.contains(m_focusPointMode))
- setFocusPointModeHelper(QCameraFocus::FocusPointAuto);
-
- setFocusMode(m_focusMode);
- setCustomFocusPoint(m_customFocusPoint);
- setFocusPointMode(m_focusPointMode);
-}
-
-void QAndroidCameraFocusControl::updateFocusZones(QCameraFocusZone::FocusZoneStatus status)
-{
- if (!m_session->camera())
- return;
-
- // create a focus zone (50x50 pixel) around the focus point
- m_focusZones.clear();
-
- if (!m_actualFocusPoint.isNull()) {
- QSize viewportSize = m_session->camera()->previewSize();
-
- if (!viewportSize.isValid())
- return;
-
- QSizeF focusSize(50.f / viewportSize.width(), 50.f / viewportSize.height());
- float x = qBound(qreal(0),
- m_actualFocusPoint.x() - (focusSize.width() / 2),
- 1.f - focusSize.width());
- float y = qBound(qreal(0),
- m_actualFocusPoint.y() - (focusSize.height() / 2),
- 1.f - focusSize.height());
-
- QRectF area(QPointF(x, y), focusSize);
-
- m_focusZones.append(QCameraFocusZone(area, status));
- }
-
- emit focusZonesChanged();
-}
-
-void QAndroidCameraFocusControl::setCameraFocusArea()
-{
- QList<QRect> areas;
- if (m_focusPointMode != QCameraFocus::FocusPointAuto) {
- // in FocusPointAuto mode, leave the area list empty
- // to let the driver choose the focus point.
-
- for (int i = 0; i < m_focusZones.size(); ++i)
- areas.append(adjustedArea(m_focusZones.at(i).area()));
-
- }
- m_session->camera()->setFocusAreas(areas);
-}
-
-void QAndroidCameraFocusControl::onViewportSizeChanged()
-{
- QCameraFocusZone::FocusZoneStatus status = QCameraFocusZone::Selected;
- if (!m_focusZones.isEmpty())
- status = m_focusZones.at(0).status();
- updateFocusZones(status);
- setCameraFocusArea();
-}
-
-void QAndroidCameraFocusControl::onCameraCaptureModeChanged()
-{
- if (m_session->camera() && m_focusMode == QCameraFocus::ContinuousFocus) {
- QString focusMode;
- if ((m_session->captureMode().testFlag(QCamera::CaptureVideo) && m_continuousVideoFocusSupported)
- || !m_continuousPictureFocusSupported) {
- focusMode = QLatin1String("continuous-video");
- } else {
- focusMode = QLatin1String("continuous-picture");
- }
- m_session->camera()->setFocusMode(focusMode);
- m_session->camera()->cancelAutoFocus();
- }
-}
-
-void QAndroidCameraFocusControl::onAutoFocusStarted()
-{
- updateFocusZones(QCameraFocusZone::Selected);
-}
-
-void QAndroidCameraFocusControl::onAutoFocusComplete(bool success)
-{
- if (success)
- updateFocusZones(QCameraFocusZone::Focused);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.h b/src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.h
deleted file mode 100644
index 9c606cf7d..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 QANDROIDCAMERAFOCUSCONTROL_H
-#define QANDROIDCAMERAFOCUSCONTROL_H
-
-#include <qcamerafocuscontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCameraSession;
-
-class QAndroidCameraFocusControl : public QCameraFocusControl
-{
- Q_OBJECT
-public:
- explicit QAndroidCameraFocusControl(QAndroidCameraSession *session);
-
- QCameraFocus::FocusModes focusMode() const override;
- void setFocusMode(QCameraFocus::FocusModes mode) override;
- bool isFocusModeSupported(QCameraFocus::FocusModes mode) const override;
- QCameraFocus::FocusPointMode focusPointMode() const override;
- void setFocusPointMode(QCameraFocus::FocusPointMode mode) override;
- bool isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const override;
- QPointF customFocusPoint() const override;
- void setCustomFocusPoint(const QPointF &point) override;
- QCameraFocusZoneList focusZones() const override;
-
-private Q_SLOTS:
- void onCameraOpened();
- void onViewportSizeChanged();
- void onCameraCaptureModeChanged();
- void onAutoFocusStarted();
- void onAutoFocusComplete(bool success);
-
-private:
- inline void setFocusModeHelper(QCameraFocus::FocusModes mode)
- {
- if (m_focusMode != mode) {
- m_focusMode = mode;
- emit focusModeChanged(mode);
- }
- }
-
- inline void setFocusPointModeHelper(QCameraFocus::FocusPointMode mode)
- {
- if (m_focusPointMode != mode) {
- m_focusPointMode = mode;
- emit focusPointModeChanged(mode);
- }
- }
-
- void updateFocusZones(QCameraFocusZone::FocusZoneStatus status = QCameraFocusZone::Selected);
- void setCameraFocusArea();
-
- QAndroidCameraSession *m_session;
-
- QCameraFocus::FocusModes m_focusMode;
- QCameraFocus::FocusPointMode m_focusPointMode;
- QPointF m_actualFocusPoint;
- QPointF m_customFocusPoint;
- QCameraFocusZoneList m_focusZones;
-
- QList<QCameraFocus::FocusModes> m_supportedFocusModes;
- bool m_continuousPictureFocusSupported;
- bool m_continuousVideoFocusSupported;
-
- QList<QCameraFocus::FocusPointMode> m_supportedFocusPointModes;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDCAMERAFOCUSCONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidcameraimagecapturecontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcameraimagecapturecontrol.cpp
deleted file mode 100644
index f39a53665..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcameraimagecapturecontrol.cpp
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "qandroidcameraimagecapturecontrol.h"
-
-#include "qandroidcamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-QAndroidCameraImageCaptureControl::QAndroidCameraImageCaptureControl(QAndroidCameraSession *session)
- : QCameraImageCaptureControl()
- , m_session(session)
-{
- connect(m_session, SIGNAL(readyForCaptureChanged(bool)), this, SIGNAL(readyForCaptureChanged(bool)));
- connect(m_session, SIGNAL(imageExposed(int)), this, SIGNAL(imageExposed(int)));
- connect(m_session, SIGNAL(imageCaptured(int,QImage)), this, SIGNAL(imageCaptured(int,QImage)));
- connect(m_session, SIGNAL(imageMetadataAvailable(int,QString,QVariant)), this, SIGNAL(imageMetadataAvailable(int,QString,QVariant)));
- connect(m_session, SIGNAL(imageAvailable(int,QVideoFrame)), this, SIGNAL(imageAvailable(int,QVideoFrame)));
- connect(m_session, SIGNAL(imageSaved(int,QString)), this, SIGNAL(imageSaved(int,QString)));
- connect(m_session, SIGNAL(imageCaptureError(int,int,QString)), this, SIGNAL(error(int,int,QString)));
-}
-
-bool QAndroidCameraImageCaptureControl::isReadyForCapture() const
-{
- return m_session->isReadyForCapture();
-}
-
-QCameraImageCapture::DriveMode QAndroidCameraImageCaptureControl::driveMode() const
-{
- return m_session->driveMode();
-}
-
-void QAndroidCameraImageCaptureControl::setDriveMode(QCameraImageCapture::DriveMode mode)
-{
- m_session->setDriveMode(mode);
-}
-
-int QAndroidCameraImageCaptureControl::capture(const QString &fileName)
-{
- return m_session->capture(fileName);
-}
-
-void QAndroidCameraImageCaptureControl::cancelCapture()
-{
- m_session->cancelCapture();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcameraimagecapturecontrol.h b/src/plugins/android/src/mediacapture/qandroidcameraimagecapturecontrol.h
deleted file mode 100644
index cfe748c39..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcameraimagecapturecontrol.h
+++ /dev/null
@@ -1,69 +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 QANDROIDCAMERAIMAGECAPTURECONTROL_H
-#define QANDROIDCAMERAIMAGECAPTURECONTROL_H
-
-#include <qcameraimagecapturecontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCameraSession;
-
-class QAndroidCameraImageCaptureControl : public QCameraImageCaptureControl
-{
- Q_OBJECT
-public:
- explicit QAndroidCameraImageCaptureControl(QAndroidCameraSession *session);
-
- bool isReadyForCapture() const override;
-
- QCameraImageCapture::DriveMode driveMode() const override;
- void setDriveMode(QCameraImageCapture::DriveMode mode) override;
-
- int capture(const QString &fileName) override;
- void cancelCapture() override;
-
-private:
- QAndroidCameraSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDCAMERAIMAGECAPTURECONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidcameraimageprocessingcontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcameraimageprocessingcontrol.cpp
deleted file mode 100644
index c69f1946b..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcameraimageprocessingcontrol.cpp
+++ /dev/null
@@ -1,140 +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 "qandroidcameraimageprocessingcontrol.h"
-
-#include "qandroidcamerasession.h"
-#include "androidcamera.h"
-
-QT_BEGIN_NAMESPACE
-
-QAndroidCameraImageProcessingControl::QAndroidCameraImageProcessingControl(QAndroidCameraSession *session)
- : QCameraImageProcessingControl()
- , m_session(session)
- , m_whiteBalanceMode(QCameraImageProcessing::WhiteBalanceAuto)
-{
- connect(m_session, SIGNAL(opened()),
- this, SLOT(onCameraOpened()));
-}
-
-bool QAndroidCameraImageProcessingControl::isParameterSupported(ProcessingParameter parameter) const
-{
- return parameter == QCameraImageProcessingControl::WhiteBalancePreset
- && m_session->camera()
- && !m_supportedWhiteBalanceModes.isEmpty();
-}
-
-bool QAndroidCameraImageProcessingControl::isParameterValueSupported(ProcessingParameter parameter,
- const QVariant &value) const
-{
- return parameter == QCameraImageProcessingControl::WhiteBalancePreset
- && m_session->camera()
- && m_supportedWhiteBalanceModes.contains(value.value<QCameraImageProcessing::WhiteBalanceMode>());
-}
-
-QVariant QAndroidCameraImageProcessingControl::parameter(ProcessingParameter parameter) const
-{
- if (parameter != QCameraImageProcessingControl::WhiteBalancePreset)
- return QVariant();
-
- return QVariant::fromValue(m_whiteBalanceMode);
-}
-
-void QAndroidCameraImageProcessingControl::setParameter(ProcessingParameter parameter, const QVariant &value)
-{
- if (parameter != QCameraImageProcessingControl::WhiteBalancePreset)
- return;
-
- QCameraImageProcessing::WhiteBalanceMode mode = value.value<QCameraImageProcessing::WhiteBalanceMode>();
-
- if (m_session->camera())
- setWhiteBalanceModeHelper(mode);
- else
- m_whiteBalanceMode = mode;
-}
-
-void QAndroidCameraImageProcessingControl::setWhiteBalanceModeHelper(QCameraImageProcessing::WhiteBalanceMode mode)
-{
- QString wb = m_supportedWhiteBalanceModes.value(mode, QString());
- if (!wb.isEmpty()) {
- m_session->camera()->setWhiteBalance(wb);
- m_whiteBalanceMode = mode;
- }
-}
-
-void QAndroidCameraImageProcessingControl::onCameraOpened()
-{
- m_supportedWhiteBalanceModes.clear();
- QStringList whiteBalanceModes = m_session->camera()->getSupportedWhiteBalance();
- for (int i = 0; i < whiteBalanceModes.size(); ++i) {
- const QString &wb = whiteBalanceModes.at(i);
- if (wb == QLatin1String("auto")) {
- m_supportedWhiteBalanceModes.insert(QCameraImageProcessing::WhiteBalanceAuto,
- QStringLiteral("auto"));
- } else if (wb == QLatin1String("cloudy-daylight")) {
- m_supportedWhiteBalanceModes.insert(QCameraImageProcessing::WhiteBalanceCloudy,
- QStringLiteral("cloudy-daylight"));
- } else if (wb == QLatin1String("daylight")) {
- m_supportedWhiteBalanceModes.insert(QCameraImageProcessing::WhiteBalanceSunlight,
- QStringLiteral("daylight"));
- } else if (wb == QLatin1String("fluorescent")) {
- m_supportedWhiteBalanceModes.insert(QCameraImageProcessing::WhiteBalanceFluorescent,
- QStringLiteral("fluorescent"));
- } else if (wb == QLatin1String("incandescent")) {
- m_supportedWhiteBalanceModes.insert(QCameraImageProcessing::WhiteBalanceTungsten,
- QStringLiteral("incandescent"));
- } else if (wb == QLatin1String("shade")) {
- m_supportedWhiteBalanceModes.insert(QCameraImageProcessing::WhiteBalanceShade,
- QStringLiteral("shade"));
- } else if (wb == QLatin1String("twilight")) {
- m_supportedWhiteBalanceModes.insert(QCameraImageProcessing::WhiteBalanceSunset,
- QStringLiteral("twilight"));
- } else if (wb == QLatin1String("warm-fluorescent")) {
- m_supportedWhiteBalanceModes.insert(QCameraImageProcessing::WhiteBalanceFlash,
- QStringLiteral("warm-fluorescent"));
- }
- }
-
- if (!m_supportedWhiteBalanceModes.contains(m_whiteBalanceMode))
- m_whiteBalanceMode = QCameraImageProcessing::WhiteBalanceAuto;
-
- setWhiteBalanceModeHelper(m_whiteBalanceMode);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcameraimageprocessingcontrol.h b/src/plugins/android/src/mediacapture/qandroidcameraimageprocessingcontrol.h
deleted file mode 100644
index 9845c80dc..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcameraimageprocessingcontrol.h
+++ /dev/null
@@ -1,75 +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 QANDROIDCAMERAIMAGEPROCESSINGCONTROL_H
-#define QANDROIDCAMERAIMAGEPROCESSINGCONTROL_H
-
-#include <qcameraimageprocessingcontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCameraSession;
-
-class QAndroidCameraImageProcessingControl : public QCameraImageProcessingControl
-{
- Q_OBJECT
-public:
- explicit QAndroidCameraImageProcessingControl(QAndroidCameraSession *session);
-
- bool isParameterSupported(ProcessingParameter) const override;
- bool isParameterValueSupported(ProcessingParameter parameter, const QVariant &value) const override;
- QVariant parameter(ProcessingParameter parameter) const override;
- void setParameter(ProcessingParameter parameter, const QVariant &value) override;
-
-private Q_SLOTS:
- void onCameraOpened();
-
-private:
- void setWhiteBalanceModeHelper(QCameraImageProcessing::WhiteBalanceMode mode);
-
- QAndroidCameraSession *m_session;
-
- QCameraImageProcessing::WhiteBalanceMode m_whiteBalanceMode;
-
- QMap<QCameraImageProcessing::WhiteBalanceMode, QString> m_supportedWhiteBalanceModes;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDCAMERAIMAGEPROCESSINGCONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidcamerainfocontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcamerainfocontrol.cpp
deleted file mode 100644
index 911ffc3ca..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcamerainfocontrol.cpp
+++ /dev/null
@@ -1,80 +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 "qandroidcamerainfocontrol.h"
-
-#include "qandroidcamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-QCamera::Position QAndroidCameraInfoControl::position(const QString &deviceName)
-{
- const QList<AndroidCameraInfo> &cameras = QAndroidCameraSession::availableCameras();
- for (int i = 0; i < cameras.count(); ++i) {
- const AndroidCameraInfo &info = cameras.at(i);
- if (QString::fromLatin1(info.name) == deviceName)
- return info.position;
- }
-
- return QCamera::UnspecifiedPosition;
-}
-
-int QAndroidCameraInfoControl::orientation(const QString &deviceName)
-{
- const QList<AndroidCameraInfo> &cameras = QAndroidCameraSession::availableCameras();
- for (int i = 0; i < cameras.count(); ++i) {
- const AndroidCameraInfo &info = cameras.at(i);
- if (QString::fromLatin1(info.name) == deviceName)
- return info.orientation;
- }
-
- return 0;
-}
-
-QCamera::Position QAndroidCameraInfoControl::cameraPosition(const QString &deviceName) const
-{
- return position(deviceName);
-}
-
-int QAndroidCameraInfoControl::cameraOrientation(const QString &deviceName) const
-{
- return orientation(deviceName);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcamerainfocontrol.h b/src/plugins/android/src/mediacapture/qandroidcamerainfocontrol.h
deleted file mode 100644
index 349c73c97..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcamerainfocontrol.h
+++ /dev/null
@@ -1,60 +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 QANDROIDCAMERAINFOCONTROL_H
-#define QANDROIDCAMERAINFOCONTROL_H
-
-#include <qcamerainfocontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCameraInfoControl : public QCameraInfoControl
-{
- Q_OBJECT
-public:
- QCamera::Position cameraPosition(const QString &deviceName) const;
- int cameraOrientation(const QString &deviceName) const;
-
- static QCamera::Position position(const QString &deviceName);
- static int orientation(const QString &deviceName);
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDCAMERAINFOCONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidcameralockscontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcameralockscontrol.cpp
deleted file mode 100644
index 180a2f483..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcameralockscontrol.cpp
+++ /dev/null
@@ -1,252 +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 "qandroidcameralockscontrol.h"
-
-#include "qandroidcamerasession.h"
-#include "androidcamera.h"
-#include <qtimer.h>
-
-QT_BEGIN_NAMESPACE
-
-QAndroidCameraLocksControl::QAndroidCameraLocksControl(QAndroidCameraSession *session)
- : QCameraLocksControl()
- , m_session(session)
- , m_supportedLocks(QCamera::NoLock)
- , m_focusLockStatus(QCamera::Unlocked)
- , m_exposureLockStatus(QCamera::Unlocked)
- , m_whiteBalanceLockStatus(QCamera::Unlocked)
-{
- connect(m_session, SIGNAL(opened()),
- this, SLOT(onCameraOpened()));
-
- m_recalculateTimer = new QTimer(this);
- m_recalculateTimer->setInterval(1000);
- m_recalculateTimer->setSingleShot(true);
- connect(m_recalculateTimer, SIGNAL(timeout()), this, SLOT(onRecalculateTimeOut()));
-}
-
-QCamera::LockTypes QAndroidCameraLocksControl::supportedLocks() const
-{
- return m_supportedLocks;
-}
-
-QCamera::LockStatus QAndroidCameraLocksControl::lockStatus(QCamera::LockType lock) const
-{
- if (!m_supportedLocks.testFlag(lock) || !m_session->camera())
- return QCamera::Unlocked;
-
- if (lock == QCamera::LockFocus)
- return m_focusLockStatus;
-
- if (lock == QCamera::LockExposure)
- return m_exposureLockStatus;
-
- if (lock == QCamera::LockWhiteBalance)
- return m_whiteBalanceLockStatus;
-
- return QCamera::Unlocked;
-}
-
-void QAndroidCameraLocksControl::searchAndLock(QCamera::LockTypes locks)
-{
- if (!m_session->camera())
- return;
-
- // filter out unsupported locks
- locks &= m_supportedLocks;
-
- if (locks.testFlag(QCamera::LockFocus)) {
- QString focusMode = m_session->camera()->getFocusMode();
- if (focusMode == QLatin1String("auto")
- || focusMode == QLatin1String("macro")
- || focusMode == QLatin1String("continuous-picture")
- || focusMode == QLatin1String("continuous-video")) {
-
- if (m_focusLockStatus == QCamera::Searching)
- m_session->camera()->cancelAutoFocus();
- else
- setFocusLockStatus(QCamera::Searching, QCamera::UserRequest);
-
- m_session->camera()->autoFocus();
-
- } else {
- setFocusLockStatus(QCamera::Locked, QCamera::LockAcquired);
- }
- }
-
- if (locks.testFlag(QCamera::LockExposure) && m_exposureLockStatus != QCamera::Searching) {
- if (m_session->camera()->getAutoExposureLock()) {
- // if already locked, unlock and give some time to recalculate exposure
- m_session->camera()->setAutoExposureLock(false);
- setExposureLockStatus(QCamera::Searching, QCamera::UserRequest);
- } else {
- m_session->camera()->setAutoExposureLock(true);
- setExposureLockStatus(QCamera::Locked, QCamera::LockAcquired);
- }
- }
-
- if (locks.testFlag(QCamera::LockWhiteBalance) && m_whiteBalanceLockStatus != QCamera::Searching) {
- if (m_session->camera()->getAutoWhiteBalanceLock()) {
- // if already locked, unlock and give some time to recalculate white balance
- m_session->camera()->setAutoWhiteBalanceLock(false);
- setWhiteBalanceLockStatus(QCamera::Searching, QCamera::UserRequest);
- } else {
- m_session->camera()->setAutoWhiteBalanceLock(true);
- setWhiteBalanceLockStatus(QCamera::Locked, QCamera::LockAcquired);
- }
- }
-
- if (m_exposureLockStatus == QCamera::Searching || m_whiteBalanceLockStatus == QCamera::Searching)
- m_recalculateTimer->start();
-}
-
-void QAndroidCameraLocksControl::unlock(QCamera::LockTypes locks)
-{
- if (!m_session->camera())
- return;
-
- if (m_recalculateTimer->isActive())
- m_recalculateTimer->stop();
-
- // filter out unsupported locks
- locks &= m_supportedLocks;
-
- if (locks.testFlag(QCamera::LockFocus)) {
- m_session->camera()->cancelAutoFocus();
- setFocusLockStatus(QCamera::Unlocked, QCamera::UserRequest);
- }
-
- if (locks.testFlag(QCamera::LockExposure)) {
- m_session->camera()->setAutoExposureLock(false);
- setExposureLockStatus(QCamera::Unlocked, QCamera::UserRequest);
- }
-
- if (locks.testFlag(QCamera::LockWhiteBalance)) {
- m_session->camera()->setAutoWhiteBalanceLock(false);
- setWhiteBalanceLockStatus(QCamera::Unlocked, QCamera::UserRequest);
- }
-}
-
-void QAndroidCameraLocksControl::onCameraOpened()
-{
- m_supportedLocks = QCamera::NoLock;
- m_focusLockStatus = QCamera::Unlocked;
- m_exposureLockStatus = QCamera::Unlocked;
- m_whiteBalanceLockStatus = QCamera::Unlocked;
-
- // check if focus lock is supported
- QStringList focusModes = m_session->camera()->getSupportedFocusModes();
- for (int i = 0; i < focusModes.size(); ++i) {
- const QString &focusMode = focusModes.at(i);
- if (focusMode == QLatin1String("auto")
- || focusMode == QLatin1String("continuous-picture")
- || focusMode == QLatin1String("continuous-video")
- || focusMode == QLatin1String("macro")) {
-
- m_supportedLocks |= QCamera::LockFocus;
- setFocusLockStatus(QCamera::Unlocked, QCamera::UserRequest);
-
- connect(m_session->camera(), SIGNAL(autoFocusComplete(bool)),
- this, SLOT(onCameraAutoFocusComplete(bool)));
-
- break;
- }
- }
-
- if (m_session->camera()->isAutoExposureLockSupported()) {
- m_supportedLocks |= QCamera::LockExposure;
- setExposureLockStatus(QCamera::Unlocked, QCamera::UserRequest);
- }
-
- if (m_session->camera()->isAutoWhiteBalanceLockSupported()) {
- m_supportedLocks |= QCamera::LockWhiteBalance;
- setWhiteBalanceLockStatus(QCamera::Unlocked, QCamera::UserRequest);
-
- connect(m_session->camera(), SIGNAL(whiteBalanceChanged()),
- this, SLOT(onWhiteBalanceChanged()));
- }
-}
-
-void QAndroidCameraLocksControl::onCameraAutoFocusComplete(bool success)
-{
- m_focusLockStatus = success ? QCamera::Locked : QCamera::Unlocked;
- QCamera::LockChangeReason reason = success ? QCamera::LockAcquired : QCamera::LockFailed;
- emit lockStatusChanged(QCamera::LockFocus, m_focusLockStatus, reason);
-}
-
-void QAndroidCameraLocksControl::onRecalculateTimeOut()
-{
- if (m_exposureLockStatus == QCamera::Searching) {
- m_session->camera()->setAutoExposureLock(true);
- setExposureLockStatus(QCamera::Locked, QCamera::LockAcquired);
- }
-
- if (m_whiteBalanceLockStatus == QCamera::Searching) {
- m_session->camera()->setAutoWhiteBalanceLock(true);
- setWhiteBalanceLockStatus(QCamera::Locked, QCamera::LockAcquired);
- }
-}
-
-void QAndroidCameraLocksControl::onWhiteBalanceChanged()
-{
- // changing the white balance mode releases the white balance lock
- if (m_whiteBalanceLockStatus != QCamera::Unlocked)
- setWhiteBalanceLockStatus(QCamera::Unlocked, QCamera::LockLost);
-}
-
-void QAndroidCameraLocksControl::setFocusLockStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason)
-{
- m_focusLockStatus = status;
- emit lockStatusChanged(QCamera::LockFocus, m_focusLockStatus, reason);
-}
-
-void QAndroidCameraLocksControl::setWhiteBalanceLockStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason)
-{
- m_whiteBalanceLockStatus = status;
- emit lockStatusChanged(QCamera::LockWhiteBalance, m_whiteBalanceLockStatus, reason);
-}
-
-void QAndroidCameraLocksControl::setExposureLockStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason)
-{
- m_exposureLockStatus = status;
- emit lockStatusChanged(QCamera::LockExposure, m_exposureLockStatus, reason);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcameralockscontrol.h b/src/plugins/android/src/mediacapture/qandroidcameralockscontrol.h
deleted file mode 100644
index 149da0f32..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcameralockscontrol.h
+++ /dev/null
@@ -1,85 +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 QANDROIDCAMERALOCKSCONTROL_H
-#define QANDROIDCAMERALOCKSCONTROL_H
-
-#include <qcameralockscontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCameraSession;
-class QTimer;
-
-class QAndroidCameraLocksControl : public QCameraLocksControl
-{
- Q_OBJECT
-public:
- explicit QAndroidCameraLocksControl(QAndroidCameraSession *session);
-
- QCamera::LockTypes supportedLocks() const override;
- QCamera::LockStatus lockStatus(QCamera::LockType lock) const override;
- void searchAndLock(QCamera::LockTypes locks) override;
- void unlock(QCamera::LockTypes locks) override;
-
-private Q_SLOTS:
- void onCameraOpened();
- void onCameraAutoFocusComplete(bool success);
- void onRecalculateTimeOut();
- void onWhiteBalanceChanged();
-
-private:
- void setFocusLockStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason);
- void setWhiteBalanceLockStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason);
- void setExposureLockStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason);
-
- QAndroidCameraSession *m_session;
-
- QTimer *m_recalculateTimer;
-
- QCamera::LockTypes m_supportedLocks;
-
- QCamera::LockStatus m_focusLockStatus;
- QCamera::LockStatus m_exposureLockStatus;
- QCamera::LockStatus m_whiteBalanceLockStatus;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDCAMERALOCKSCONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp
deleted file mode 100644
index ff5c7be15..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp
+++ /dev/null
@@ -1,942 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "qandroidcamerasession.h"
-
-#include "androidcamera.h"
-#include "androidmultimediautils.h"
-#include "qandroidvideooutput.h"
-#include "qandroidmediavideoprobecontrol.h"
-#include "qandroidmultimediautils.h"
-#include "qandroidcameravideorenderercontrol.h"
-#include <qabstractvideosurface.h>
-#include <QtConcurrent/qtconcurrentrun.h>
-#include <qfile.h>
-#include <qguiapplication.h>
-#include <qdebug.h>
-#include <qvideoframe.h>
-#include <private/qmemoryvideobuffer_p.h>
-#include <QtCore/private/qjnihelpers_p.h>
-
-QT_BEGIN_NAMESPACE
-
-Q_GLOBAL_STATIC(QList<AndroidCameraInfo>, g_availableCameras)
-
-QAndroidCameraSession::QAndroidCameraSession(QObject *parent)
- : QObject(parent)
- , m_selectedCamera(0)
- , m_camera(0)
- , m_nativeOrientation(0)
- , m_videoOutput(0)
- , m_captureMode(QCamera::CaptureStillImage)
- , m_state(QCamera::UnloadedState)
- , m_savedState(-1)
- , m_status(QCamera::UnloadedStatus)
- , m_previewStarted(false)
- , m_captureDestination(QCameraImageCapture::CaptureToFile)
- , m_captureImageDriveMode(QCameraImageCapture::SingleImageCapture)
- , m_lastImageCaptureId(0)
- , m_readyForCapture(false)
- , m_captureCanceled(false)
- , m_currentImageCaptureId(-1)
- , m_previewCallback(0)
- , m_keepActive(false)
-{
- m_mediaStorageLocation.addStorageLocation(
- QMediaStorageLocation::Pictures,
- AndroidMultimediaUtils::getDefaultMediaDirectory(AndroidMultimediaUtils::DCIM));
-
- if (qApp) {
- connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
- this, SLOT(onApplicationStateChanged(Qt::ApplicationState)));
- }
-}
-
-QAndroidCameraSession::~QAndroidCameraSession()
-{
- close();
-}
-
-void QAndroidCameraSession::setCaptureMode(QCamera::CaptureModes mode)
-{
- if (m_captureMode == mode || !isCaptureModeSupported(mode))
- return;
-
- m_captureMode = mode;
- emit captureModeChanged(m_captureMode);
-
- if (m_previewStarted && m_captureMode.testFlag(QCamera::CaptureStillImage))
- applyViewfinderSettings(m_actualImageSettings.resolution());
-}
-
-bool QAndroidCameraSession::isCaptureModeSupported(QCamera::CaptureModes mode) const
-{
- if (mode & (QCamera::CaptureStillImage & QCamera::CaptureVideo))
- return false;
-
- return true;
-}
-
-void QAndroidCameraSession::setState(QCamera::State state)
-{
- if (m_state == state)
- return;
-
- m_state = state;
- emit stateChanged(m_state);
-
- // If the application is inactive, the camera shouldn't be started. Save the desired state
- // instead and it will be set when the application becomes active.
- if (qApp->applicationState() == Qt::ApplicationActive)
- setStateHelper(state);
- else
- m_savedState = state;
-}
-
-void QAndroidCameraSession::setStateHelper(QCamera::State state)
-{
- switch (state) {
- case QCamera::UnloadedState:
- close();
- break;
- case QCamera::LoadedState:
- case QCamera::ActiveState:
- if (!m_camera && !open()) {
- m_state = QCamera::UnloadedState;
- emit stateChanged(m_state);
- emit error(QCamera::CameraError, QStringLiteral("Failed to open camera"));
- m_status = QCamera::UnloadedStatus;
- emit statusChanged(m_status);
- return;
- }
- if (state == QCamera::ActiveState)
- startPreview();
- else if (state == QCamera::LoadedState)
- stopPreview();
- break;
- }
-}
-
-void QAndroidCameraSession::updateAvailableCameras()
-{
- g_availableCameras->clear();
-
- const int numCameras = AndroidCamera::getNumberOfCameras();
- for (int i = 0; i < numCameras; ++i) {
- AndroidCameraInfo info;
- AndroidCamera::getCameraInfo(i, &info);
-
- if (!info.name.isNull())
- g_availableCameras->append(info);
- }
-}
-
-const QList<AndroidCameraInfo> &QAndroidCameraSession::availableCameras()
-{
- if (g_availableCameras->isEmpty())
- updateAvailableCameras();
-
- return *g_availableCameras;
-}
-
-bool QAndroidCameraSession::open()
-{
- close();
-
- m_status = QCamera::LoadingStatus;
- emit statusChanged(m_status);
-
- m_camera = AndroidCamera::open(m_selectedCamera);
-
- if (m_camera) {
- connect(m_camera, SIGNAL(pictureExposed()), this, SLOT(onCameraPictureExposed()));
- connect(m_camera, SIGNAL(lastPreviewFrameFetched(QVideoFrame)),
- this, SLOT(onLastPreviewFrameFetched(QVideoFrame)),
- Qt::DirectConnection);
- connect(m_camera, SIGNAL(newPreviewFrame(QVideoFrame)),
- this, SLOT(onNewPreviewFrame(QVideoFrame)),
- Qt::DirectConnection);
- connect(m_camera, SIGNAL(pictureCaptured(QByteArray)), this, SLOT(onCameraPictureCaptured(QByteArray)));
- connect(m_camera, SIGNAL(previewStarted()), this, SLOT(onCameraPreviewStarted()));
- connect(m_camera, SIGNAL(previewStopped()), this, SLOT(onCameraPreviewStopped()));
- connect(m_camera, &AndroidCamera::previewFailedToStart, this, &QAndroidCameraSession::onCameraPreviewFailedToStart);
- connect(m_camera, &AndroidCamera::takePictureFailed, this, &QAndroidCameraSession::onCameraTakePictureFailed);
-
- m_nativeOrientation = m_camera->getNativeOrientation();
-
- m_status = QCamera::LoadedStatus;
-
- if (m_camera->getPreviewFormat() != AndroidCamera::NV21)
- m_camera->setPreviewFormat(AndroidCamera::NV21);
-
- m_camera->notifyNewFrames(m_videoProbes.count() || m_previewCallback);
-
- emit opened();
- emit statusChanged(m_status);
- }
-
- return m_camera != 0;
-}
-
-void QAndroidCameraSession::close()
-{
- if (!m_camera)
- return;
-
- stopPreview();
-
- m_status = QCamera::UnloadingStatus;
- emit statusChanged(m_status);
-
- m_readyForCapture = false;
- m_currentImageCaptureId = -1;
- m_currentImageCaptureFileName.clear();
- m_actualImageSettings = m_requestedImageSettings;
- m_actualViewfinderSettings = m_requestedViewfinderSettings;
-
- m_camera->release();
- delete m_camera;
- m_camera = 0;
-
- m_status = QCamera::UnloadedStatus;
- emit statusChanged(m_status);
-}
-
-void QAndroidCameraSession::setVideoOutput(QAndroidVideoOutput *output)
-{
- if (m_videoOutput) {
- m_videoOutput->stop();
- m_videoOutput->reset();
- }
-
- if (output) {
- m_videoOutput = output;
- if (m_videoOutput->isReady())
- onVideoOutputReady(true);
- else
- connect(m_videoOutput, SIGNAL(readyChanged(bool)), this, SLOT(onVideoOutputReady(bool)));
- } else {
- m_videoOutput = 0;
- }
-}
-
-void QAndroidCameraSession::setViewfinderSettings(const QCameraViewfinderSettings &settings)
-{
- if (m_requestedViewfinderSettings == settings)
- return;
-
- m_requestedViewfinderSettings = m_actualViewfinderSettings = settings;
-
- if (m_readyForCapture)
- applyViewfinderSettings();
-}
-
-void QAndroidCameraSession::applyViewfinderSettings(const QSize &captureSize, bool restartPreview)
-{
- if (!m_camera)
- return;
-
- const QSize currentViewfinderResolution = m_camera->previewSize();
- const AndroidCamera::ImageFormat currentPreviewFormat = m_camera->getPreviewFormat();
- const AndroidCamera::FpsRange currentFpsRange = m_camera->getPreviewFpsRange();
-
- // -- adjust resolution
- QSize adjustedViewfinderResolution;
- const bool validCaptureSize = captureSize.width() > 0 && captureSize.height() > 0;
- if (m_captureMode.testFlag(QCamera::CaptureVideo)
- && validCaptureSize
- && m_camera->getPreferredPreviewSizeForVideo().isEmpty()) {
- // According to the Android doc, if getPreferredPreviewSizeForVideo() returns null, it means
- // the preview size cannot be different from the capture size
- adjustedViewfinderResolution = captureSize;
- } else {
- qreal captureAspectRatio = 0;
- if (validCaptureSize)
- captureAspectRatio = qreal(captureSize.width()) / qreal(captureSize.height());
-
- const QList<QSize> previewSizes = m_camera->getSupportedPreviewSizes();
-
- const QSize vfRes = m_requestedViewfinderSettings.resolution();
- if (vfRes.width() > 0 && vfRes.height() > 0
- && (!validCaptureSize || qAbs(captureAspectRatio - (qreal(vfRes.width()) / vfRes.height())) < 0.01)
- && previewSizes.contains(vfRes)) {
- adjustedViewfinderResolution = vfRes;
- } else if (validCaptureSize) {
- // search for viewfinder resolution with the same aspect ratio
- qreal minAspectDiff = 1;
- QSize closestResolution;
- for (int i = previewSizes.count() - 1; i >= 0; --i) {
- const QSize &size = previewSizes.at(i);
- const qreal sizeAspect = qreal(size.width()) / size.height();
- if (qFuzzyCompare(captureAspectRatio, sizeAspect)) {
- adjustedViewfinderResolution = size;
- break;
- } else if (minAspectDiff > qAbs(sizeAspect - captureAspectRatio)) {
- closestResolution = size;
- minAspectDiff = qAbs(sizeAspect - captureAspectRatio);
- }
- }
- if (!adjustedViewfinderResolution.isValid()) {
- qWarning("Cannot find a viewfinder resolution matching the capture aspect ratio.");
- if (closestResolution.isValid()) {
- adjustedViewfinderResolution = closestResolution;
- qWarning("Using closest viewfinder resolution.");
- } else {
- return;
- }
- }
- } else {
- adjustedViewfinderResolution = previewSizes.last();
- }
- }
- m_actualViewfinderSettings.setResolution(adjustedViewfinderResolution);
-
- // -- adjust pixel format
-
- AndroidCamera::ImageFormat adjustedPreviewFormat = AndroidCamera::NV21;
- if (m_requestedViewfinderSettings.pixelFormat() != QVideoFrame::Format_Invalid) {
- const AndroidCamera::ImageFormat f = AndroidImageFormatFromQtPixelFormat(m_requestedViewfinderSettings.pixelFormat());
- if (f == AndroidCamera::UnknownImageFormat || !m_camera->getSupportedPreviewFormats().contains(f))
- qWarning("Unsupported viewfinder pixel format");
- else
- adjustedPreviewFormat = f;
- }
- m_actualViewfinderSettings.setPixelFormat(QtPixelFormatFromAndroidImageFormat(adjustedPreviewFormat));
-
- // -- adjust FPS
-
- AndroidCamera::FpsRange adjustedFps = currentFpsRange;
- const AndroidCamera::FpsRange requestedFpsRange = AndroidCamera::FpsRange::makeFromQReal(m_requestedViewfinderSettings.minimumFrameRate(),
- m_requestedViewfinderSettings.maximumFrameRate());
- if (requestedFpsRange.min > 0 || requestedFpsRange.max > 0) {
- int minDist = INT_MAX;
- const QList<AndroidCamera::FpsRange> supportedFpsRanges = m_camera->getSupportedPreviewFpsRange();
- auto it = supportedFpsRanges.rbegin(), end = supportedFpsRanges.rend();
- for (; it != end; ++it) {
- int dist = (requestedFpsRange.min > 0 ? qAbs(requestedFpsRange.min - it->min) : 0)
- + (requestedFpsRange.max > 0 ? qAbs(requestedFpsRange.max - it->max) : 0);
- if (dist < minDist) {
- minDist = dist;
- adjustedFps = *it;
- if (minDist == 0)
- break; // exact match
- }
- }
- }
- m_actualViewfinderSettings.setMinimumFrameRate(adjustedFps.getMinReal());
- m_actualViewfinderSettings.setMaximumFrameRate(adjustedFps.getMaxReal());
-
- // -- Set values on camera
-
- if (currentViewfinderResolution != adjustedViewfinderResolution
- || currentPreviewFormat != adjustedPreviewFormat
- || currentFpsRange.min != adjustedFps.min
- || currentFpsRange.max != adjustedFps.max) {
-
- if (m_videoOutput)
- m_videoOutput->setVideoSize(adjustedViewfinderResolution);
-
- // if preview is started, we have to stop it first before changing its size
- if (m_previewStarted && restartPreview)
- m_camera->stopPreview();
-
- m_camera->setPreviewSize(adjustedViewfinderResolution);
- m_camera->setPreviewFormat(adjustedPreviewFormat);
- m_camera->setPreviewFpsRange(adjustedFps);
-
- // restart preview
- if (m_previewStarted && restartPreview)
- m_camera->startPreview();
- }
-}
-
-QList<QSize> QAndroidCameraSession::getSupportedPreviewSizes() const
-{
- return m_camera ? m_camera->getSupportedPreviewSizes() : QList<QSize>();
-}
-
-QList<QVideoFrame::PixelFormat> QAndroidCameraSession::getSupportedPixelFormats() const
-{
- QList<QVideoFrame::PixelFormat> formats;
-
- if (!m_camera)
- return formats;
-
- const QList<AndroidCamera::ImageFormat> nativeFormats = m_camera->getSupportedPreviewFormats();
-
- formats.reserve(nativeFormats.size());
-
- for (AndroidCamera::ImageFormat nativeFormat : nativeFormats) {
- QVideoFrame::PixelFormat format = QtPixelFormatFromAndroidImageFormat(nativeFormat);
- if (format != QVideoFrame::Format_Invalid)
- formats.append(format);
- }
-
- return formats;
-}
-
-QList<AndroidCamera::FpsRange> QAndroidCameraSession::getSupportedPreviewFpsRange() const
-{
- return m_camera ? m_camera->getSupportedPreviewFpsRange() : QList<AndroidCamera::FpsRange>();
-}
-
-struct NullSurface : QAbstractVideoSurface
-{
- NullSurface(QObject *parent = nullptr) : QAbstractVideoSurface(parent) { }
- QList<QVideoFrame::PixelFormat> supportedPixelFormats(
- QAbstractVideoBuffer::HandleType type = QAbstractVideoBuffer::NoHandle) const override
- {
- QList<QVideoFrame::PixelFormat> result;
- if (type == QAbstractVideoBuffer::NoHandle)
- result << QVideoFrame::Format_NV21;
-
- return result;
- }
-
- bool present(const QVideoFrame &) { return false; }
-};
-
-bool QAndroidCameraSession::startPreview()
-{
- if (!m_camera)
- return false;
-
- if (m_previewStarted)
- return true;
-
- if (m_videoOutput) {
- if (!m_videoOutput->isReady())
- return true; // delay starting until the video output is ready
-
- Q_ASSERT(m_videoOutput->surfaceTexture() || m_videoOutput->surfaceHolder());
-
- if ((m_videoOutput->surfaceTexture() && !m_camera->setPreviewTexture(m_videoOutput->surfaceTexture()))
- || (m_videoOutput->surfaceHolder() && !m_camera->setPreviewDisplay(m_videoOutput->surfaceHolder())))
- return false;
- } else {
- auto control = new QAndroidCameraVideoRendererControl(this, this);
- control->setSurface(new NullSurface(this));
- qWarning() << "Starting camera without viewfinder available";
-
- return true;
- }
-
- m_status = QCamera::StartingStatus;
- emit statusChanged(m_status);
-
- applyImageSettings();
- applyViewfinderSettings(m_captureMode.testFlag(QCamera::CaptureStillImage) ? m_actualImageSettings.resolution()
- : QSize());
-
- AndroidMultimediaUtils::enableOrientationListener(true);
-
- // Before API level 24 the orientation was always 0, which is what we're expecting, so
- // we'll enforce that here.
- if (QtAndroidPrivate::androidSdkVersion() > 23)
- m_camera->setDisplayOrientation(0);
-
- m_camera->startPreview();
- m_previewStarted = true;
-
- return true;
-}
-
-void QAndroidCameraSession::stopPreview()
-{
- if (!m_camera || !m_previewStarted)
- return;
-
- m_status = QCamera::StoppingStatus;
- emit statusChanged(m_status);
-
- AndroidMultimediaUtils::enableOrientationListener(false);
-
- m_camera->stopPreview();
- m_camera->setPreviewSize(QSize());
- m_camera->setPreviewTexture(0);
- m_camera->setPreviewDisplay(0);
-
- if (m_videoOutput) {
- m_videoOutput->stop();
- m_videoOutput->reset();
- }
- m_previewStarted = false;
-}
-
-void QAndroidCameraSession::setImageSettings(const QImageEncoderSettings &settings)
-{
- if (m_requestedImageSettings == settings)
- return;
-
- m_requestedImageSettings = m_actualImageSettings = settings;
-
- applyImageSettings();
-
- if (m_readyForCapture && m_captureMode.testFlag(QCamera::CaptureStillImage))
- applyViewfinderSettings(m_actualImageSettings.resolution());
-}
-
-int QAndroidCameraSession::currentCameraRotation() const
-{
- if (!m_camera)
- return 0;
-
- // subtract natural camera orientation and physical device orientation
- int rotation = 0;
- int deviceOrientation = (AndroidMultimediaUtils::getDeviceOrientation() + 45) / 90 * 90;
- if (m_camera->getFacing() == AndroidCamera::CameraFacingFront)
- rotation = (m_nativeOrientation - deviceOrientation + 360) % 360;
- else // back-facing camera
- rotation = (m_nativeOrientation + deviceOrientation) % 360;
-
- return rotation;
-}
-
-void QAndroidCameraSession::addProbe(QAndroidMediaVideoProbeControl *probe)
-{
- m_videoProbesMutex.lock();
- if (probe)
- m_videoProbes << probe;
- if (m_camera)
- m_camera->notifyNewFrames(m_videoProbes.count() || m_previewCallback);
- m_videoProbesMutex.unlock();
-}
-
-void QAndroidCameraSession::removeProbe(QAndroidMediaVideoProbeControl *probe)
-{
- m_videoProbesMutex.lock();
- m_videoProbes.remove(probe);
- if (m_camera)
- m_camera->notifyNewFrames(m_videoProbes.count() || m_previewCallback);
- m_videoProbesMutex.unlock();
-}
-
-void QAndroidCameraSession::setPreviewFormat(AndroidCamera::ImageFormat format)
-{
- if (format == AndroidCamera::UnknownImageFormat)
- return;
-
- m_camera->setPreviewFormat(format);
-}
-
-void QAndroidCameraSession::setPreviewCallback(PreviewCallback *callback)
-{
- m_videoProbesMutex.lock();
- m_previewCallback = callback;
- if (m_camera)
- m_camera->notifyNewFrames(m_videoProbes.count() || m_previewCallback);
- m_videoProbesMutex.unlock();
-}
-
-void QAndroidCameraSession::applyImageSettings()
-{
- if (!m_camera)
- return;
-
- if (m_actualImageSettings.codec().isEmpty())
- m_actualImageSettings.setCodec(QLatin1String("jpeg"));
-
- const QSize requestedResolution = m_requestedImageSettings.resolution();
- const QList<QSize> supportedResolutions = m_camera->getSupportedPictureSizes();
- if (!requestedResolution.isValid()) {
- // if the viewfinder resolution is explicitly set, pick the highest available capture
- // resolution with the same aspect ratio
- if (m_requestedViewfinderSettings.resolution().isValid()) {
- const QSize vfResolution = m_actualViewfinderSettings.resolution();
- const qreal vfAspectRatio = qreal(vfResolution.width()) / vfResolution.height();
-
- auto it = supportedResolutions.rbegin(), end = supportedResolutions.rend();
- for (; it != end; ++it) {
- if (qAbs(vfAspectRatio - (qreal(it->width()) / it->height())) < 0.01) {
- m_actualImageSettings.setResolution(*it);
- break;
- }
- }
- } else {
- // otherwise, use the highest supported one
- m_actualImageSettings.setResolution(supportedResolutions.last());
- }
- } else if (!supportedResolutions.contains(requestedResolution)) {
- // if the requested resolution is not supported, find the closest one
- int reqPixelCount = requestedResolution.width() * requestedResolution.height();
- QList<int> supportedPixelCounts;
- for (int i = 0; i < supportedResolutions.size(); ++i) {
- const QSize &s = supportedResolutions.at(i);
- supportedPixelCounts.append(s.width() * s.height());
- }
- int closestIndex = qt_findClosestValue(supportedPixelCounts, reqPixelCount);
- m_actualImageSettings.setResolution(supportedResolutions.at(closestIndex));
- }
- m_camera->setPictureSize(m_actualImageSettings.resolution());
-
- int jpegQuality = 100;
- switch (m_requestedImageSettings.quality()) {
- case QMultimedia::VeryLowQuality:
- jpegQuality = 20;
- break;
- case QMultimedia::LowQuality:
- jpegQuality = 40;
- break;
- case QMultimedia::NormalQuality:
- jpegQuality = 60;
- break;
- case QMultimedia::HighQuality:
- jpegQuality = 80;
- break;
- case QMultimedia::VeryHighQuality:
- jpegQuality = 100;
- break;
- }
- m_camera->setJpegQuality(jpegQuality);
-}
-
-bool QAndroidCameraSession::isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const
-{
- return destination & (QCameraImageCapture::CaptureToFile | QCameraImageCapture::CaptureToBuffer);
-}
-
-QCameraImageCapture::CaptureDestinations QAndroidCameraSession::captureDestination() const
-{
- return m_captureDestination;
-}
-
-void QAndroidCameraSession::setCaptureDestination(QCameraImageCapture::CaptureDestinations destination)
-{
- if (m_captureDestination != destination) {
- m_captureDestination = destination;
- emit captureDestinationChanged(m_captureDestination);
- }
-}
-
-bool QAndroidCameraSession::isReadyForCapture() const
-{
- return m_status == QCamera::ActiveStatus && m_readyForCapture;
-}
-
-void QAndroidCameraSession::setReadyForCapture(bool ready)
-{
- if (m_readyForCapture == ready)
- return;
-
- m_readyForCapture = ready;
- emit readyForCaptureChanged(ready);
-}
-
-QCameraImageCapture::DriveMode QAndroidCameraSession::driveMode() const
-{
- return m_captureImageDriveMode;
-}
-
-void QAndroidCameraSession::setDriveMode(QCameraImageCapture::DriveMode mode)
-{
- m_captureImageDriveMode = mode;
-}
-
-int QAndroidCameraSession::capture(const QString &fileName)
-{
- ++m_lastImageCaptureId;
-
- if (!isReadyForCapture()) {
- emit imageCaptureError(m_lastImageCaptureId, QCameraImageCapture::NotReadyError,
- tr("Camera not ready"));
- return m_lastImageCaptureId;
- }
-
- if (m_captureImageDriveMode == QCameraImageCapture::SingleImageCapture) {
- setReadyForCapture(false);
-
- m_currentImageCaptureId = m_lastImageCaptureId;
- m_currentImageCaptureFileName = fileName;
-
- applyImageSettings();
- applyViewfinderSettings(m_actualImageSettings.resolution());
-
- // adjust picture rotation depending on the device orientation
- m_camera->setRotation(currentCameraRotation());
-
- m_camera->takePicture();
- } else {
- //: Drive mode is the camera's shutter mode, for example single shot, continuos exposure, etc.
- emit imageCaptureError(m_lastImageCaptureId, QCameraImageCapture::NotSupportedFeatureError,
- tr("Drive mode not supported"));
- }
-
- return m_lastImageCaptureId;
-}
-
-void QAndroidCameraSession::cancelCapture()
-{
- if (m_readyForCapture)
- return;
-
- m_captureCanceled = true;
-}
-
-void QAndroidCameraSession::onCameraTakePictureFailed()
-{
- emit imageCaptureError(m_currentImageCaptureId, QCameraImageCapture::ResourceError,
- tr("Failed to capture image"));
-
- // Preview needs to be restarted and the preview call back must be setup again
- m_camera->startPreview();
-}
-
-void QAndroidCameraSession::onCameraPictureExposed()
-{
- if (m_captureCanceled || !m_camera)
- return;
-
- emit imageExposed(m_currentImageCaptureId);
- m_camera->fetchLastPreviewFrame();
-}
-
-void QAndroidCameraSession::onLastPreviewFrameFetched(const QVideoFrame &frame)
-{
- if (m_captureCanceled || !m_camera)
- return;
-
- QtConcurrent::run(&QAndroidCameraSession::processPreviewImage, this,
- m_currentImageCaptureId,
- frame,
- m_camera->getRotation());
-}
-
-void QAndroidCameraSession::processPreviewImage(int id, const QVideoFrame &frame, int rotation)
-{
- // Preview display of front-facing cameras is flipped horizontally, but the frame data
- // we get here is not. Flip it ourselves if the camera is front-facing to match what the user
- // sees on the viewfinder.
- QTransform transform;
- if (m_camera->getFacing() == AndroidCamera::CameraFacingFront)
- transform.scale(-1, 1);
- transform.rotate(rotation);
-
- emit imageCaptured(id, frame.image().transformed(transform));
-}
-
-void QAndroidCameraSession::onNewPreviewFrame(const QVideoFrame &frame)
-{
- if (!m_camera)
- return;
-
- m_videoProbesMutex.lock();
-
- for (QAndroidMediaVideoProbeControl *probe : qAsConst(m_videoProbes))
- probe->newFrameProbed(frame);
-
- if (m_previewCallback)
- m_previewCallback->onFrameAvailable(frame);
-
- m_videoProbesMutex.unlock();
-}
-
-void QAndroidCameraSession::onCameraPictureCaptured(const QByteArray &data)
-{
- if (!m_captureCanceled) {
- // Loading and saving the captured image can be slow, do it in a separate thread
- QtConcurrent::run(&QAndroidCameraSession::processCapturedImage, this,
- m_currentImageCaptureId,
- data,
- m_actualImageSettings.resolution(),
- m_captureDestination,
- m_currentImageCaptureFileName);
- }
-
- m_captureCanceled = false;
-
- // Preview needs to be restarted after taking a picture
- if (m_camera)
- m_camera->startPreview();
-}
-
-void QAndroidCameraSession::onCameraPreviewStarted()
-{
- if (m_status == QCamera::StartingStatus) {
- m_status = QCamera::ActiveStatus;
- emit statusChanged(m_status);
- }
-
- setReadyForCapture(true);
-}
-
-void QAndroidCameraSession::onCameraPreviewFailedToStart()
-{
- if (m_status == QCamera::StartingStatus) {
- Q_EMIT error(QCamera::CameraError, tr("Camera preview failed to start."));
-
- AndroidMultimediaUtils::enableOrientationListener(false);
- m_camera->setPreviewSize(QSize());
- m_camera->setPreviewTexture(0);
- if (m_videoOutput) {
- m_videoOutput->stop();
- m_videoOutput->reset();
- }
- m_previewStarted = false;
-
- m_status = QCamera::LoadedStatus;
- emit statusChanged(m_status);
-
- setReadyForCapture(false);
- }
-}
-
-void QAndroidCameraSession::onCameraPreviewStopped()
-{
- if (m_status == QCamera::StoppingStatus) {
- m_status = QCamera::LoadedStatus;
- emit statusChanged(m_status);
- }
-
- setReadyForCapture(false);
-}
-
-void QAndroidCameraSession::processCapturedImage(int id,
- const QByteArray &data,
- const QSize &resolution,
- QCameraImageCapture::CaptureDestinations dest,
- const QString &fileName)
-{
-
-
- if (dest & QCameraImageCapture::CaptureToFile) {
- const QString actualFileName = m_mediaStorageLocation.generateFileName(fileName,
- QMediaStorageLocation::Pictures,
- QLatin1String("IMG_"),
- QLatin1String("jpg"));
-
- QFile file(actualFileName);
- if (file.open(QFile::WriteOnly)) {
- if (file.write(data) == data.size()) {
- // if the picture is saved into the standard picture location, register it
- // with the Android media scanner so it appears immediately in apps
- // such as the gallery.
- QString standardLoc = AndroidMultimediaUtils::getDefaultMediaDirectory(AndroidMultimediaUtils::DCIM);
- if (actualFileName.startsWith(standardLoc))
- AndroidMultimediaUtils::registerMediaFile(actualFileName);
-
- emit imageSaved(id, actualFileName);
- } else {
- emit imageCaptureError(id, QCameraImageCapture::OutOfSpaceError, file.errorString());
- }
- } else {
- const QString errorMessage = tr("Could not open destination file: %1").arg(actualFileName);
- emit imageCaptureError(id, QCameraImageCapture::ResourceError, errorMessage);
- }
- }
-
- if (dest & QCameraImageCapture::CaptureToBuffer) {
- QVideoFrame frame(new QMemoryVideoBuffer(data, -1), resolution, QVideoFrame::Format_Jpeg);
- emit imageAvailable(id, frame);
- }
-}
-
-QVideoFrame::PixelFormat QAndroidCameraSession::QtPixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat format)
-{
- switch (format) {
- case AndroidCamera::RGB565:
- return QVideoFrame::Format_RGB565;
- case AndroidCamera::NV21:
- return QVideoFrame::Format_NV21;
- case AndroidCamera::YUY2:
- return QVideoFrame::Format_YUYV;
- case AndroidCamera::JPEG:
- return QVideoFrame::Format_Jpeg;
- case AndroidCamera::YV12:
- return QVideoFrame::Format_YV12;
- default:
- return QVideoFrame::Format_Invalid;
- }
-}
-
-AndroidCamera::ImageFormat QAndroidCameraSession::AndroidImageFormatFromQtPixelFormat(QVideoFrame::PixelFormat format)
-{
- switch (format) {
- case QVideoFrame::Format_RGB565:
- return AndroidCamera::RGB565;
- case QVideoFrame::Format_NV21:
- return AndroidCamera::NV21;
- case QVideoFrame::Format_YUYV:
- return AndroidCamera::YUY2;
- case QVideoFrame::Format_Jpeg:
- return AndroidCamera::JPEG;
- case QVideoFrame::Format_YV12:
- return AndroidCamera::YV12;
- default:
- return AndroidCamera::UnknownImageFormat;
- }
-}
-
-void QAndroidCameraSession::onVideoOutputReady(bool ready)
-{
- if (ready && m_state == QCamera::ActiveState)
- startPreview();
-}
-
-void QAndroidCameraSession::onApplicationStateChanged(Qt::ApplicationState state)
-{
- switch (state) {
- case Qt::ApplicationInactive:
- if (!m_keepActive && m_state != QCamera::UnloadedState) {
- m_savedState = m_state;
- close();
- m_state = QCamera::UnloadedState;
- emit stateChanged(m_state);
- }
- break;
- case Qt::ApplicationActive:
- if (m_savedState != -1) {
- setStateHelper(QCamera::State(m_savedState));
- m_savedState = -1;
- }
- break;
- default:
- break;
- }
-}
-
-bool QAndroidCameraSession::requestRecordingPermission()
-{
- m_keepActive = true;
- const bool result = qt_androidRequestRecordingPermission();
- m_keepActive = false;
- return result;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcamerasession.h b/src/plugins/android/src/mediacapture/qandroidcamerasession.h
deleted file mode 100644
index 728dc484e..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcamerasession.h
+++ /dev/null
@@ -1,205 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef QANDROIDCAMERASESSION_H
-#define QANDROIDCAMERASESSION_H
-
-#include <qcamera.h>
-#include <qmediaencodersettings.h>
-#include <QCameraImageCapture>
-#include <QSet>
-#include <QMutex>
-#include <private/qmediastoragelocation_p.h>
-#include "androidcamera.h"
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidVideoOutput;
-class QAndroidMediaVideoProbeControl;
-
-class QAndroidCameraSession : public QObject
-{
- Q_OBJECT
-public:
- explicit QAndroidCameraSession(QObject *parent = 0);
- ~QAndroidCameraSession();
-
- static const QList<AndroidCameraInfo> &availableCameras();
-
- void setSelectedCamera(int cameraId) { m_selectedCamera = cameraId; }
- AndroidCamera *camera() const { return m_camera; }
-
- QCamera::State state() const { return m_state; }
- void setState(QCamera::State state);
-
- QCamera::Status status() const { return m_status; }
-
- QCamera::CaptureModes captureMode() const { return m_captureMode; }
- void setCaptureMode(QCamera::CaptureModes mode);
- bool isCaptureModeSupported(QCamera::CaptureModes mode) const;
-
- QCameraViewfinderSettings viewfinderSettings() const { return m_actualViewfinderSettings; }
- void setViewfinderSettings(const QCameraViewfinderSettings &settings);
- void applyViewfinderSettings(const QSize &captureSize = QSize(), bool restartPreview = true);
-
- QAndroidVideoOutput *videoOutput() const { return m_videoOutput; }
- void setVideoOutput(QAndroidVideoOutput *output);
-
- QList<QSize> getSupportedPreviewSizes() const;
- QList<QVideoFrame::PixelFormat> getSupportedPixelFormats() const;
- QList<AndroidCamera::FpsRange> getSupportedPreviewFpsRange() const;
-
- QImageEncoderSettings imageSettings() const { return m_actualImageSettings; }
- void setImageSettings(const QImageEncoderSettings &settings);
-
- bool isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const;
- QCameraImageCapture::CaptureDestinations captureDestination() const;
- void setCaptureDestination(QCameraImageCapture::CaptureDestinations destination);
-
- bool isReadyForCapture() const;
- void setReadyForCapture(bool ready);
- QCameraImageCapture::DriveMode driveMode() const;
- void setDriveMode(QCameraImageCapture::DriveMode mode);
- int capture(const QString &fileName);
- void cancelCapture();
-
- int currentCameraRotation() const;
-
- void addProbe(QAndroidMediaVideoProbeControl *probe);
- void removeProbe(QAndroidMediaVideoProbeControl *probe);
-
- void setPreviewFormat(AndroidCamera::ImageFormat format);
-
- struct PreviewCallback
- {
- virtual void onFrameAvailable(const QVideoFrame &frame) = 0;
- };
- void setPreviewCallback(PreviewCallback *callback);
- bool requestRecordingPermission();
-
-Q_SIGNALS:
- void statusChanged(QCamera::Status status);
- void stateChanged(QCamera::State);
- void error(int error, const QString &errorString);
- void captureModeChanged(QCamera::CaptureModes);
- void opened();
-
- void captureDestinationChanged(QCameraImageCapture::CaptureDestinations destination);
-
- void readyForCaptureChanged(bool);
- void imageExposed(int id);
- void imageCaptured(int id, const QImage &preview);
- void imageMetadataAvailable(int id, const QString &key, const QVariant &value);
- void imageAvailable(int id, const QVideoFrame &buffer);
- void imageSaved(int id, const QString &fileName);
- void imageCaptureError(int id, int error, const QString &errorString);
-
-private Q_SLOTS:
- void onVideoOutputReady(bool ready);
-
- void onApplicationStateChanged(Qt::ApplicationState state);
-
- void onCameraTakePictureFailed();
- void onCameraPictureExposed();
- void onCameraPictureCaptured(const QByteArray &data);
- void onLastPreviewFrameFetched(const QVideoFrame &frame);
- void onNewPreviewFrame(const QVideoFrame &frame);
- void onCameraPreviewStarted();
- void onCameraPreviewFailedToStart();
- void onCameraPreviewStopped();
-
-private:
- static void updateAvailableCameras();
-
- bool open();
- void close();
-
- bool startPreview();
- void stopPreview();
-
- void applyImageSettings();
-
- void processPreviewImage(int id, const QVideoFrame &frame, int rotation);
- void processCapturedImage(int id,
- const QByteArray &data,
- const QSize &resolution,
- QCameraImageCapture::CaptureDestinations dest,
- const QString &fileName);
-
- static QVideoFrame::PixelFormat QtPixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat);
- static AndroidCamera::ImageFormat AndroidImageFormatFromQtPixelFormat(QVideoFrame::PixelFormat);
-
- void setStateHelper(QCamera::State state);
-
- int m_selectedCamera;
- AndroidCamera *m_camera;
- int m_nativeOrientation;
- QAndroidVideoOutput *m_videoOutput;
-
- QCamera::CaptureModes m_captureMode;
- QCamera::State m_state;
- int m_savedState;
- QCamera::Status m_status;
- bool m_previewStarted;
-
- QCameraViewfinderSettings m_requestedViewfinderSettings;
- QCameraViewfinderSettings m_actualViewfinderSettings;
-
- QImageEncoderSettings m_requestedImageSettings;
- QImageEncoderSettings m_actualImageSettings;
- QCameraImageCapture::CaptureDestinations m_captureDestination;
- QCameraImageCapture::DriveMode m_captureImageDriveMode;
- int m_lastImageCaptureId;
- bool m_readyForCapture;
- bool m_captureCanceled;
- int m_currentImageCaptureId;
- QString m_currentImageCaptureFileName;
-
- QMediaStorageLocation m_mediaStorageLocation;
-
- QSet<QAndroidMediaVideoProbeControl *> m_videoProbes;
- QMutex m_videoProbesMutex;
- PreviewCallback *m_previewCallback;
- bool m_keepActive;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDCAMERASESSION_H
diff --git a/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.cpp
deleted file mode 100644
index 2243df732..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.cpp
+++ /dev/null
@@ -1,281 +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 "qandroidcameravideorenderercontrol.h"
-
-#include "qandroidcamerasession.h"
-#include "qandroidvideooutput.h"
-#include "androidsurfaceview.h"
-#include "qandroidmultimediautils.h"
-#include <qabstractvideosurface.h>
-#include <qvideosurfaceformat.h>
-#include <qcoreapplication.h>
-#include <qthread.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCameraDataVideoOutput : public QAndroidVideoOutput
- , public QAndroidCameraSession::PreviewCallback
-{
- Q_OBJECT
-public:
- explicit QAndroidCameraDataVideoOutput(QAndroidCameraVideoRendererControl *control);
- ~QAndroidCameraDataVideoOutput() override;
-
- AndroidSurfaceHolder *surfaceHolder() override;
-
- bool isReady() override;
-
- void stop() override;
-
-private Q_SLOTS:
- void onSurfaceCreated();
- void configureFormat();
-
-private:
- void onFrameAvailable(const QVideoFrame &frame);
- void presentFrame();
- bool event(QEvent *);
-
- QAndroidCameraVideoRendererControl *m_control;
- AndroidSurfaceView *m_surfaceView;
- QMutex m_mutex;
- QVideoFrame::PixelFormat m_pixelFormat;
- QVideoFrame m_lastFrame;
-};
-
-QAndroidCameraDataVideoOutput::QAndroidCameraDataVideoOutput(QAndroidCameraVideoRendererControl *control)
- : QAndroidVideoOutput(control)
- , m_control(control)
- , m_pixelFormat(QVideoFrame::Format_Invalid)
-{
- // The camera preview cannot be started unless we set a SurfaceTexture or a
- // SurfaceHolder. In this case we don't actually care about either of these, but since
- // we need to, we setup an offscreen dummy SurfaceView in order to be able to start
- // the camera preview. We'll then be able to use setPreviewCallbackWithBuffer() to
- // get the raw data.
-
- m_surfaceView = new AndroidSurfaceView;
-
- connect(m_surfaceView, &AndroidSurfaceView::surfaceCreated,
- this, &QAndroidCameraDataVideoOutput::onSurfaceCreated);
-
- m_surfaceView->setGeometry(-1, -1, 1, 1);
- m_surfaceView->setVisible(true);
-
- connect(m_control->cameraSession(), &QAndroidCameraSession::opened,
- this, &QAndroidCameraDataVideoOutput::configureFormat);
- connect(m_control->surface(), &QAbstractVideoSurface::supportedFormatsChanged,
- this, &QAndroidCameraDataVideoOutput::configureFormat);
- configureFormat();
-}
-
-QAndroidCameraDataVideoOutput::~QAndroidCameraDataVideoOutput()
-{
- m_control->cameraSession()->setPreviewCallback(nullptr);
- delete m_surfaceView;
-}
-
-AndroidSurfaceHolder *QAndroidCameraDataVideoOutput::surfaceHolder()
-{
- return m_surfaceView->holder();
-}
-
-bool QAndroidCameraDataVideoOutput::isReady()
-{
- return m_surfaceView->holder() && m_surfaceView->holder()->isSurfaceCreated();
-}
-
-void QAndroidCameraDataVideoOutput::onSurfaceCreated()
-{
- emit readyChanged(true);
-}
-
-void QAndroidCameraDataVideoOutput::configureFormat()
-{
- m_pixelFormat = QVideoFrame::Format_Invalid;
-
- if (!m_control->cameraSession()->camera())
- return;
-
- QList<QVideoFrame::PixelFormat> surfaceFormats = m_control->surface()->supportedPixelFormats();
- QList<AndroidCamera::ImageFormat> previewFormats = m_control->cameraSession()->camera()->getSupportedPreviewFormats();
- for (int i = 0; i < surfaceFormats.size(); ++i) {
- QVideoFrame::PixelFormat pixFormat = surfaceFormats.at(i);
- AndroidCamera::ImageFormat f = qt_androidImageFormatFromPixelFormat(pixFormat);
- if (previewFormats.contains(f)) {
- m_pixelFormat = pixFormat;
- break;
- }
- }
-
- if (m_pixelFormat == QVideoFrame::Format_Invalid) {
- m_control->cameraSession()->setPreviewCallback(nullptr);
- qWarning("The video surface is not compatible with any format supported by the camera");
- } else {
- m_control->cameraSession()->setPreviewCallback(this);
-
- if (m_control->cameraSession()->status() > QCamera::LoadedStatus)
- m_control->cameraSession()->camera()->stopPreview();
-
- m_control->cameraSession()->setPreviewFormat(qt_androidImageFormatFromPixelFormat(m_pixelFormat));
-
- if (m_control->cameraSession()->status() > QCamera::LoadedStatus)
- m_control->cameraSession()->camera()->startPreview();
- }
-}
-
-void QAndroidCameraDataVideoOutput::stop()
-{
- m_mutex.lock();
- m_lastFrame = QVideoFrame();
- m_mutex.unlock();
-
- if (m_control->surface() && m_control->surface()->isActive())
- m_control->surface()->stop();
-}
-
-void QAndroidCameraDataVideoOutput::onFrameAvailable(const QVideoFrame &frame)
-{
- m_mutex.lock();
- m_lastFrame = frame;
- m_mutex.unlock();
-
- if (thread() == QThread::currentThread())
- presentFrame();
- else
- QCoreApplication::postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority);
-}
-
-bool QAndroidCameraDataVideoOutput::event(QEvent *e)
-{
- if (e->type() == QEvent::User) {
- presentFrame();
- return true;
- }
-
- return QObject::event(e);
-}
-
-void QAndroidCameraDataVideoOutput::presentFrame()
-{
- Q_ASSERT(thread() == QThread::currentThread());
-
- QMutexLocker locker(&m_mutex);
-
- if (m_control->surface() && m_lastFrame.isValid() && m_lastFrame.pixelFormat() == m_pixelFormat) {
-
- if (m_control->surface()->isActive() && (m_control->surface()->surfaceFormat().pixelFormat() != m_lastFrame.pixelFormat()
- || m_control->surface()->surfaceFormat().frameSize() != m_lastFrame.size())) {
- m_control->surface()->stop();
- }
-
- if (!m_control->surface()->isActive()) {
- QVideoSurfaceFormat format(m_lastFrame.size(), m_lastFrame.pixelFormat(), m_lastFrame.handleType());
- // Front camera frames are automatically mirrored when using SurfaceTexture or SurfaceView,
- // but the buffers we get from the data callback are not. Tell the QAbstractVideoSurface
- // that it needs to mirror the frames.
- if (m_control->cameraSession()->camera()->getFacing() == AndroidCamera::CameraFacingFront)
- format.setProperty("mirrored", true);
-
- m_control->surface()->start(format);
- }
-
- if (m_control->surface()->isActive())
- m_control->surface()->present(m_lastFrame);
- }
-
- m_lastFrame = QVideoFrame();
-}
-
-
-QAndroidCameraVideoRendererControl::QAndroidCameraVideoRendererControl(QAndroidCameraSession *session, QObject *parent)
- : QVideoRendererControl(parent)
- , m_cameraSession(session)
- , m_surface(0)
- , m_textureOutput(0)
- , m_dataOutput(0)
-{
-}
-
-QAndroidCameraVideoRendererControl::~QAndroidCameraVideoRendererControl()
-{
- m_cameraSession->setVideoOutput(0);
-}
-
-QAbstractVideoSurface *QAndroidCameraVideoRendererControl::surface() const
-{
- return m_surface;
-}
-
-void QAndroidCameraVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
-{
- if (m_surface == surface)
- return;
-
- m_surface = surface;
- QAndroidVideoOutput *oldOutput = m_textureOutput ? static_cast<QAndroidVideoOutput*>(m_textureOutput)
- : static_cast<QAndroidVideoOutput*>(m_dataOutput);
- QAndroidVideoOutput *newOutput = 0;
-
- if (m_surface) {
- if (!m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty()) {
- if (!m_textureOutput) {
- m_dataOutput = 0;
- newOutput = m_textureOutput = new QAndroidTextureVideoOutput(this);
- }
- } else if (!m_dataOutput) {
- m_textureOutput = 0;
- newOutput = m_dataOutput = new QAndroidCameraDataVideoOutput(this);
- }
-
- if (m_textureOutput)
- m_textureOutput->setSurface(m_surface);
- }
-
- if (newOutput != oldOutput) {
- m_cameraSession->setVideoOutput(newOutput);
- delete oldOutput;
- }
-}
-
-QT_END_NAMESPACE
-
-#include "qandroidcameravideorenderercontrol.moc"
-
diff --git a/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.h b/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.h
deleted file mode 100644
index 538226239..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.h
+++ /dev/null
@@ -1,72 +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 QANDROIDCAMERAVIDEORENDERERCONTROL_H
-#define QANDROIDCAMERAVIDEORENDERERCONTROL_H
-
-#include <qvideorenderercontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCameraSession;
-class QAndroidTextureVideoOutput;
-class QAndroidCameraDataVideoOutput;
-
-class QAndroidCameraVideoRendererControl : public QVideoRendererControl
-{
- Q_OBJECT
-public:
- QAndroidCameraVideoRendererControl(QAndroidCameraSession *session, QObject *parent = 0);
- ~QAndroidCameraVideoRendererControl() override;
-
- QAbstractVideoSurface *surface() const override;
- void setSurface(QAbstractVideoSurface *surface) override;
-
- QAndroidCameraSession *cameraSession() const { return m_cameraSession; }
-
-private:
- QAndroidCameraSession *m_cameraSession;
- QAbstractVideoSurface *m_surface;
- QAndroidTextureVideoOutput *m_textureOutput;
- QAndroidCameraDataVideoOutput *m_dataOutput;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDCAMERAVIDEORENDERERCONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidcamerazoomcontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcamerazoomcontrol.cpp
deleted file mode 100644
index 6f94bdfe7..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcamerazoomcontrol.cpp
+++ /dev/null
@@ -1,133 +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 "qandroidcamerazoomcontrol.h"
-
-#include "qandroidcamerasession.h"
-#include "androidcamera.h"
-#include "qandroidmultimediautils.h"
-#include <qmath.h>
-
-QT_BEGIN_NAMESPACE
-
-QAndroidCameraZoomControl::QAndroidCameraZoomControl(QAndroidCameraSession *session)
- : QCameraZoomControl()
- , m_cameraSession(session)
- , m_maximumZoom(1.0)
- , m_requestedZoom(1.0)
- , m_currentZoom(1.0)
-{
- connect(m_cameraSession, SIGNAL(opened()),
- this, SLOT(onCameraOpened()));
-}
-
-qreal QAndroidCameraZoomControl::maximumOpticalZoom() const
-{
- // Optical zoom not supported
- return 1.0;
-}
-
-qreal QAndroidCameraZoomControl::maximumDigitalZoom() const
-{
- return m_maximumZoom;
-}
-
-qreal QAndroidCameraZoomControl::requestedOpticalZoom() const
-{
- // Optical zoom not supported
- return 1.0;
-}
-
-qreal QAndroidCameraZoomControl::requestedDigitalZoom() const
-{
- return m_requestedZoom;
-}
-
-qreal QAndroidCameraZoomControl::currentOpticalZoom() const
-{
- // Optical zoom not supported
- return 1.0;
-}
-
-qreal QAndroidCameraZoomControl::currentDigitalZoom() const
-{
- return m_currentZoom;
-}
-
-void QAndroidCameraZoomControl::zoomTo(qreal optical, qreal digital)
-{
- Q_UNUSED(optical);
-
- if (!qFuzzyCompare(m_requestedZoom, digital)) {
- m_requestedZoom = digital;
- emit requestedDigitalZoomChanged(m_requestedZoom);
- }
-
- if (m_cameraSession->camera()) {
- digital = qBound(qreal(1), digital, m_maximumZoom);
- int validZoomIndex = qt_findClosestValue(m_zoomRatios, qRound(digital * 100));
- qreal newZoom = m_zoomRatios.at(validZoomIndex) / qreal(100);
- if (!qFuzzyCompare(m_currentZoom, newZoom)) {
- m_cameraSession->camera()->setZoom(validZoomIndex);
- m_currentZoom = newZoom;
- emit currentDigitalZoomChanged(m_currentZoom);
- }
- }
-}
-
-void QAndroidCameraZoomControl::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;
- emit maximumDigitalZoomChanged(m_maximumZoom);
- }
- zoomTo(1, m_requestedZoom);
- } else {
- m_zoomRatios.clear();
- if (!qFuzzyCompare(m_maximumZoom, qreal(1))) {
- m_maximumZoom = 1.0;
- emit maximumDigitalZoomChanged(m_maximumZoom);
- }
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcamerazoomcontrol.h b/src/plugins/android/src/mediacapture/qandroidcamerazoomcontrol.h
deleted file mode 100644
index 96fc4f77b..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcamerazoomcontrol.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 QANDROIDCAMERAZOOMCONTROL_H
-#define QANDROIDCAMERAZOOMCONTROL_H
-
-#include <qcamerazoomcontrol.h>
-#include <qcamera.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCameraSession;
-
-class QAndroidCameraZoomControl : public QCameraZoomControl
-{
- Q_OBJECT
-public:
- explicit QAndroidCameraZoomControl(QAndroidCameraSession *session);
-
- qreal maximumOpticalZoom() const override;
- qreal maximumDigitalZoom() const override;
- qreal requestedOpticalZoom() const override;
- qreal requestedDigitalZoom() const override;
- qreal currentOpticalZoom() const override;
- qreal currentDigitalZoom() const override;
- void zoomTo(qreal optical, qreal digital) override;
-
-private Q_SLOTS:
- void onCameraOpened();
-
-private:
- QAndroidCameraSession *m_cameraSession;
-
- qreal m_maximumZoom;
- QList<int> m_zoomRatios;
- qreal m_requestedZoom;
- qreal m_currentZoom;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDCAMERAZOOMCONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidcaptureservice.cpp b/src/plugins/android/src/mediacapture/qandroidcaptureservice.cpp
deleted file mode 100644
index 65df54c3f..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcaptureservice.cpp
+++ /dev/null
@@ -1,249 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "qandroidcaptureservice.h"
-
-#include "qandroidmediarecordercontrol.h"
-#include "qandroidcapturesession.h"
-#include "qandroidcameracontrol.h"
-#include "qandroidcamerainfocontrol.h"
-#include "qandroidvideodeviceselectorcontrol.h"
-#include "qandroidaudioinputselectorcontrol.h"
-#include "qandroidcamerasession.h"
-#include "qandroidcameravideorenderercontrol.h"
-#include "qandroidcamerazoomcontrol.h"
-#include "qandroidcameraexposurecontrol.h"
-#include "qandroidcameraflashcontrol.h"
-#include "qandroidcamerafocuscontrol.h"
-#include "qandroidviewfindersettingscontrol.h"
-#include "qandroidcameralockscontrol.h"
-#include "qandroidcameraimageprocessingcontrol.h"
-#include "qandroidimageencodercontrol.h"
-#include "qandroidcameraimagecapturecontrol.h"
-#include "qandroidcameracapturedestinationcontrol.h"
-#include "qandroidcameracapturebufferformatcontrol.h"
-#include "qandroidaudioencodersettingscontrol.h"
-#include "qandroidvideoencodersettingscontrol.h"
-#include "qandroidmediacontainercontrol.h"
-#include "qandroidmediavideoprobecontrol.h"
-
-#include <qmediaserviceproviderplugin.h>
-
-QT_BEGIN_NAMESPACE
-
-QAndroidCaptureService::QAndroidCaptureService(const QString &service, QObject *parent)
- : QMediaService(parent)
- , m_service(service)
- , m_videoRendererControl(0)
-{
- if (m_service == QLatin1String(Q_MEDIASERVICE_CAMERA)) {
- m_cameraSession = new QAndroidCameraSession;
- m_cameraControl = new QAndroidCameraControl(m_cameraSession);
- m_cameraInfoControl = new QAndroidCameraInfoControl;
- m_videoInputControl = new QAndroidVideoDeviceSelectorControl(m_cameraSession);
- m_cameraZoomControl = new QAndroidCameraZoomControl(m_cameraSession);
- m_cameraExposureControl = new QAndroidCameraExposureControl(m_cameraSession);
- m_cameraFlashControl = new QAndroidCameraFlashControl(m_cameraSession);
- m_cameraFocusControl = new QAndroidCameraFocusControl(m_cameraSession);
- m_viewfinderSettingsControl2 = new QAndroidViewfinderSettingsControl2(m_cameraSession);
- m_cameraLocksControl = new QAndroidCameraLocksControl(m_cameraSession);
- m_cameraImageProcessingControl = new QAndroidCameraImageProcessingControl(m_cameraSession);
- m_imageEncoderControl = new QAndroidImageEncoderControl(m_cameraSession);
- m_imageCaptureControl = new QAndroidCameraImageCaptureControl(m_cameraSession);
- m_captureDestinationControl = new QAndroidCameraCaptureDestinationControl(m_cameraSession);
- m_captureBufferFormatControl = new QAndroidCameraCaptureBufferFormatControl;
- m_audioInputControl = 0;
- } else {
- m_cameraSession = 0;
- m_cameraControl = 0;
- m_cameraInfoControl = 0;
- m_videoInputControl = 0;
- m_cameraZoomControl = 0;
- m_cameraExposureControl = 0;
- m_cameraFlashControl = 0;
- m_cameraFocusControl = 0;
- m_viewfinderSettingsControl2 = 0;
- m_cameraLocksControl = 0;
- m_cameraImageProcessingControl = 0;
- m_imageEncoderControl = 0;
- m_imageCaptureControl = 0;
- m_captureDestinationControl = 0;
- m_captureBufferFormatControl = 0;
- m_videoEncoderSettingsControl = 0;
- }
-
- m_captureSession = new QAndroidCaptureSession(m_cameraSession);
- m_recorderControl = new QAndroidMediaRecorderControl(m_captureSession);
- m_audioEncoderSettingsControl = new QAndroidAudioEncoderSettingsControl(m_captureSession);
- m_mediaContainerControl = new QAndroidMediaContainerControl(m_captureSession);
-
- if (m_service == QLatin1String(Q_MEDIASERVICE_CAMERA)) {
- m_videoEncoderSettingsControl = new QAndroidVideoEncoderSettingsControl(m_captureSession);
- } else {
- m_audioInputControl = new QAndroidAudioInputSelectorControl(m_captureSession);
- m_captureSession->setAudioInput(m_audioInputControl->defaultInput());
- }
-}
-
-QAndroidCaptureService::~QAndroidCaptureService()
-{
- delete m_audioEncoderSettingsControl;
- delete m_videoEncoderSettingsControl;
- delete m_mediaContainerControl;
- delete m_recorderControl;
- delete m_captureSession;
- delete m_cameraControl;
- delete m_cameraInfoControl;
- delete m_audioInputControl;
- delete m_videoInputControl;
- delete m_videoRendererControl;
- delete m_cameraZoomControl;
- delete m_cameraExposureControl;
- delete m_cameraFlashControl;
- delete m_cameraFocusControl;
- delete m_viewfinderSettingsControl2;
- delete m_cameraLocksControl;
- delete m_cameraImageProcessingControl;
- delete m_imageEncoderControl;
- delete m_imageCaptureControl;
- delete m_captureDestinationControl;
- delete m_captureBufferFormatControl;
- delete m_cameraSession;
-}
-
-QMediaControl *QAndroidCaptureService::requestControl(const char *name)
-{
- if (qstrcmp(name, QMediaRecorderControl_iid) == 0)
- return m_recorderControl;
-
- if (qstrcmp(name, QMediaContainerControl_iid) == 0)
- return m_mediaContainerControl;
-
- if (qstrcmp(name, QAudioEncoderSettingsControl_iid) == 0)
- return m_audioEncoderSettingsControl;
-
- if (qstrcmp(name, QVideoEncoderSettingsControl_iid) == 0)
- return m_videoEncoderSettingsControl;
-
- if (qstrcmp(name, QCameraControl_iid) == 0)
- return m_cameraControl;
-
- if (qstrcmp(name, QCameraInfoControl_iid) == 0)
- return m_cameraInfoControl;
-
- if (qstrcmp(name, QAudioInputSelectorControl_iid) == 0)
- return m_audioInputControl;
-
- if (qstrcmp(name, QVideoDeviceSelectorControl_iid) == 0)
- return m_videoInputControl;
-
- if (qstrcmp(name, QCameraZoomControl_iid) == 0)
- return m_cameraZoomControl;
-
- if (qstrcmp(name, QCameraExposureControl_iid) == 0)
- return m_cameraExposureControl;
-
- if (qstrcmp(name, QCameraFlashControl_iid) == 0)
- return m_cameraFlashControl;
-
- if (qstrcmp(name, QCameraFocusControl_iid) == 0)
- return m_cameraFocusControl;
-
- if (qstrcmp(name, QCameraViewfinderSettingsControl2_iid) == 0)
- return m_viewfinderSettingsControl2;
-
- if (qstrcmp(name, QCameraLocksControl_iid) == 0)
- return m_cameraLocksControl;
-
- if (qstrcmp(name, QCameraImageProcessingControl_iid) == 0)
- return m_cameraImageProcessingControl;
-
- if (qstrcmp(name, QImageEncoderControl_iid) == 0)
- return m_imageEncoderControl;
-
- if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0)
- return m_imageCaptureControl;
-
- if (qstrcmp(name, QCameraCaptureDestinationControl_iid) == 0)
- return m_captureDestinationControl;
-
- if (qstrcmp(name, QCameraCaptureBufferFormatControl_iid) == 0)
- return m_captureBufferFormatControl;
-
- if (qstrcmp(name, QVideoRendererControl_iid) == 0
- && m_service == QLatin1String(Q_MEDIASERVICE_CAMERA)
- && !m_videoRendererControl) {
- m_videoRendererControl = new QAndroidCameraVideoRendererControl(m_cameraSession);
- return m_videoRendererControl;
- }
-
- if (qstrcmp(name,QMediaVideoProbeControl_iid) == 0) {
- QAndroidMediaVideoProbeControl *videoProbe = 0;
- if (m_cameraSession) {
- videoProbe = new QAndroidMediaVideoProbeControl(this);
- m_cameraSession->addProbe(videoProbe);
- }
- return videoProbe;
- }
-
- return 0;
-}
-
-void QAndroidCaptureService::releaseControl(QMediaControl *control)
-{
- if (control) {
- if (control == m_videoRendererControl) {
- delete m_videoRendererControl;
- m_videoRendererControl = 0;
- return;
- }
-
- QAndroidMediaVideoProbeControl *videoProbe = qobject_cast<QAndroidMediaVideoProbeControl *>(control);
- if (videoProbe) {
- if (m_cameraSession)
- m_cameraSession->removeProbe(videoProbe);
- delete videoProbe;
- return;
- }
- }
-
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcaptureservice.h b/src/plugins/android/src/mediacapture/qandroidcaptureservice.h
deleted file mode 100644
index 85816fdd6..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcaptureservice.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef QANDROIDCAPTURESERVICE_H
-#define QANDROIDCAPTURESERVICE_H
-
-#include <qmediaservice.h>
-#include <qmediacontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidMediaRecorderControl;
-class QAndroidCaptureSession;
-class QAndroidCameraControl;
-class QAndroidCameraInfoControl;
-class QAndroidVideoDeviceSelectorControl;
-class QAndroidAudioInputSelectorControl;
-class QAndroidCameraSession;
-class QAndroidCameraVideoRendererControl;
-class QAndroidCameraZoomControl;
-class QAndroidCameraExposureControl;
-class QAndroidCameraFlashControl;
-class QAndroidCameraFocusControl;
-class QAndroidViewfinderSettingsControl2;
-class QAndroidCameraLocksControl;
-class QAndroidCameraImageProcessingControl;
-class QAndroidImageEncoderControl;
-class QAndroidCameraImageCaptureControl;
-class QAndroidCameraCaptureDestinationControl;
-class QAndroidCameraCaptureBufferFormatControl;
-class QAndroidAudioEncoderSettingsControl;
-class QAndroidVideoEncoderSettingsControl;
-class QAndroidMediaContainerControl;
-
-class QAndroidCaptureService : public QMediaService
-{
- Q_OBJECT
-
-public:
- explicit QAndroidCaptureService(const QString &service, QObject *parent = 0);
- virtual ~QAndroidCaptureService();
-
- QMediaControl *requestControl(const char *name);
- void releaseControl(QMediaControl *);
-
-private:
- QString m_service;
-
- QAndroidMediaRecorderControl *m_recorderControl;
- QAndroidCaptureSession *m_captureSession;
- QAndroidCameraControl *m_cameraControl;
- QAndroidCameraInfoControl *m_cameraInfoControl;
- QAndroidVideoDeviceSelectorControl *m_videoInputControl;
- QAndroidAudioInputSelectorControl *m_audioInputControl;
- QAndroidCameraSession *m_cameraSession;
- QAndroidCameraVideoRendererControl *m_videoRendererControl;
- QAndroidCameraZoomControl *m_cameraZoomControl;
- QAndroidCameraExposureControl *m_cameraExposureControl;
- QAndroidCameraFlashControl *m_cameraFlashControl;
- QAndroidCameraFocusControl *m_cameraFocusControl;
- QAndroidViewfinderSettingsControl2 *m_viewfinderSettingsControl2;
- QAndroidCameraLocksControl *m_cameraLocksControl;
- QAndroidCameraImageProcessingControl *m_cameraImageProcessingControl;
- QAndroidImageEncoderControl *m_imageEncoderControl;
- QAndroidCameraImageCaptureControl *m_imageCaptureControl;
- QAndroidCameraCaptureDestinationControl *m_captureDestinationControl;
- QAndroidCameraCaptureBufferFormatControl *m_captureBufferFormatControl;
- QAndroidAudioEncoderSettingsControl *m_audioEncoderSettingsControl;
- QAndroidVideoEncoderSettingsControl *m_videoEncoderSettingsControl;
- QAndroidMediaContainerControl *m_mediaContainerControl;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDCAPTURESERVICE_H
diff --git a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp
deleted file mode 100644
index 768bb4442..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp
+++ /dev/null
@@ -1,594 +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 "qandroidcapturesession.h"
-
-#include "androidcamera.h"
-#include "qandroidcamerasession.h"
-#include "androidmultimediautils.h"
-#include "qandroidmultimediautils.h"
-#include "qandroidvideooutput.h"
-#include "qandroidglobal.h"
-
-#include <algorithm>
-
-QT_BEGIN_NAMESPACE
-
-QAndroidCaptureSession::QAndroidCaptureSession(QAndroidCameraSession *cameraSession)
- : QObject()
- , m_mediaRecorder(0)
- , m_cameraSession(cameraSession)
- , m_audioSource(AndroidMediaRecorder::DefaultAudioSource)
- , m_duration(0)
- , m_state(QMediaRecorder::StoppedState)
- , m_status(QMediaRecorder::UnloadedStatus)
- , m_containerFormatDirty(true)
- , m_videoSettingsDirty(true)
- , m_audioSettingsDirty(true)
- , m_outputFormat(AndroidMediaRecorder::DefaultOutputFormat)
- , m_audioEncoder(AndroidMediaRecorder::DefaultAudioEncoder)
- , m_videoEncoder(AndroidMediaRecorder::DefaultVideoEncoder)
-{
- m_mediaStorageLocation.addStorageLocation(
- QMediaStorageLocation::Movies,
- AndroidMultimediaUtils::getDefaultMediaDirectory(AndroidMultimediaUtils::DCIM));
-
- m_mediaStorageLocation.addStorageLocation(
- QMediaStorageLocation::Sounds,
- AndroidMultimediaUtils::getDefaultMediaDirectory(AndroidMultimediaUtils::Sounds));
-
- if (cameraSession) {
- connect(cameraSession, SIGNAL(opened()), this, SLOT(onCameraOpened()));
- connect(cameraSession, &QAndroidCameraSession::statusChanged, this,
- [this](QCamera::Status status) {
- if (status == QCamera::UnavailableStatus) {
- setState(QMediaRecorder::StoppedState);
- setStatus(QMediaRecorder::UnavailableStatus);
- return;
- }
-
- // Stop recording when stopping the camera.
- if (status == QCamera::StoppingStatus) {
- setState(QMediaRecorder::StoppedState);
- setStatus(QMediaRecorder::UnloadedStatus);
- return;
- }
-
- if (status == QCamera::LoadingStatus)
- setStatus(QMediaRecorder::LoadingStatus);
- });
- connect(cameraSession, &QAndroidCameraSession::captureModeChanged, this,
- [this](QCamera::CaptureModes mode) {
- if (!mode.testFlag(QCamera::CaptureVideo)) {
- setState(QMediaRecorder::StoppedState);
- setStatus(QMediaRecorder::UnloadedStatus);
- }
- });
- connect(cameraSession, &QAndroidCameraSession::readyForCaptureChanged, this,
- [this](bool ready) {
- if (ready)
- setStatus(QMediaRecorder::LoadedStatus);
- });
- } else {
- // Audio-only recording.
- setStatus(QMediaRecorder::LoadedStatus);
- }
-
- m_notifyTimer.setInterval(1000);
- connect(&m_notifyTimer, SIGNAL(timeout()), this, SLOT(updateDuration()));
-}
-
-QAndroidCaptureSession::~QAndroidCaptureSession()
-{
- stop();
- delete m_mediaRecorder;
-}
-
-void QAndroidCaptureSession::setAudioInput(const QString &input)
-{
- if (m_audioInput == input)
- return;
-
- m_audioInput = input;
-
- if (m_audioInput == QLatin1String("default"))
- m_audioSource = AndroidMediaRecorder::DefaultAudioSource;
- else if (m_audioInput == QLatin1String("mic"))
- m_audioSource = AndroidMediaRecorder::Mic;
- else if (m_audioInput == QLatin1String("voice_uplink"))
- m_audioSource = AndroidMediaRecorder::VoiceUplink;
- else if (m_audioInput == QLatin1String("voice_downlink"))
- m_audioSource = AndroidMediaRecorder::VoiceDownlink;
- else if (m_audioInput == QLatin1String("voice_call"))
- m_audioSource = AndroidMediaRecorder::VoiceCall;
- else if (m_audioInput == QLatin1String("voice_recognition"))
- m_audioSource = AndroidMediaRecorder::VoiceRecognition;
- else
- m_audioSource = AndroidMediaRecorder::DefaultAudioSource;
-
- emit audioInputChanged(m_audioInput);
-}
-
-QUrl QAndroidCaptureSession::outputLocation() const
-{
- return m_actualOutputLocation;
-}
-
-bool QAndroidCaptureSession::setOutputLocation(const QUrl &location)
-{
- if (m_requestedOutputLocation == location)
- return false;
-
- m_actualOutputLocation = QUrl();
- m_requestedOutputLocation = location;
-
- if (m_requestedOutputLocation.isEmpty())
- return true;
-
- if (m_requestedOutputLocation.isValid()
- && (m_requestedOutputLocation.isLocalFile() || m_requestedOutputLocation.isRelative())) {
- return true;
- }
-
- m_requestedOutputLocation = QUrl();
- return false;
-}
-
-QMediaRecorder::State QAndroidCaptureSession::state() const
-{
- return m_state;
-}
-
-void QAndroidCaptureSession::setState(QMediaRecorder::State state)
-{
- if (m_state == state)
- return;
-
- switch (state) {
- case QMediaRecorder::StoppedState:
- stop();
- break;
- case QMediaRecorder::RecordingState:
- start();
- break;
- case QMediaRecorder::PausedState:
- // Not supported by Android API
- qWarning("QMediaRecorder::PausedState is not supported on Android");
- break;
- }
-}
-
-void QAndroidCaptureSession::start()
-{
- if (m_state == QMediaRecorder::RecordingState || m_status != QMediaRecorder::LoadedStatus)
- return;
-
- setStatus(QMediaRecorder::StartingStatus);
-
- if (m_mediaRecorder) {
- m_mediaRecorder->release();
- delete m_mediaRecorder;
- }
-
- const bool granted = m_cameraSession
- ? m_cameraSession->requestRecordingPermission()
- : qt_androidRequestRecordingPermission();
- if (!granted) {
- setStatus(QMediaRecorder::UnavailableStatus);
- Q_EMIT error(QMediaRecorder::ResourceError, QLatin1String("Permission denied."));
- return;
- }
-
- m_mediaRecorder = new AndroidMediaRecorder;
- connect(m_mediaRecorder, SIGNAL(error(int,int)), this, SLOT(onError(int,int)));
- connect(m_mediaRecorder, SIGNAL(info(int,int)), this, SLOT(onInfo(int,int)));
-
- // Set audio/video sources
- if (m_cameraSession) {
- updateViewfinder();
- m_cameraSession->camera()->unlock();
- m_mediaRecorder->setCamera(m_cameraSession->camera());
- m_mediaRecorder->setAudioSource(AndroidMediaRecorder::Camcorder);
- m_mediaRecorder->setVideoSource(AndroidMediaRecorder::Camera);
- } else {
- m_mediaRecorder->setAudioSource(m_audioSource);
- }
-
- // Set output format
- m_mediaRecorder->setOutputFormat(m_outputFormat);
-
- // Set audio encoder settings
- m_mediaRecorder->setAudioChannels(m_audioSettings.channelCount());
- m_mediaRecorder->setAudioEncodingBitRate(m_audioSettings.bitRate());
- m_mediaRecorder->setAudioSamplingRate(m_audioSettings.sampleRate());
- m_mediaRecorder->setAudioEncoder(m_audioEncoder);
-
- // Set video encoder settings
- if (m_cameraSession) {
- m_mediaRecorder->setVideoSize(m_videoSettings.resolution());
- m_mediaRecorder->setVideoFrameRate(qRound(m_videoSettings.frameRate()));
- m_mediaRecorder->setVideoEncodingBitRate(m_videoSettings.bitRate());
- m_mediaRecorder->setVideoEncoder(m_videoEncoder);
-
- m_mediaRecorder->setOrientationHint(m_cameraSession->currentCameraRotation());
- }
-
- // Set output file
- QString filePath = m_mediaStorageLocation.generateFileName(
- m_requestedOutputLocation.isLocalFile() ? m_requestedOutputLocation.toLocalFile()
- : m_requestedOutputLocation.toString(),
- m_cameraSession ? QMediaStorageLocation::Movies
- : QMediaStorageLocation::Sounds,
- m_cameraSession ? QLatin1String("VID_")
- : QLatin1String("REC_"),
- m_containerFormat);
-
- m_usedOutputLocation = QUrl::fromLocalFile(filePath);
- 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.setPreviewDispaly() is not called.
- if (m_cameraSession) {
- // 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());
- }
-
- if (!m_mediaRecorder->prepare()) {
- emit error(QMediaRecorder::FormatError, QLatin1String("Unable to prepare the media recorder."));
- if (m_cameraSession)
- restartViewfinder();
- return;
- }
-
- if (!m_mediaRecorder->start()) {
- emit error(QMediaRecorder::FormatError, QLatin1String("Unable to start the media recorder."));
- if (m_cameraSession)
- restartViewfinder();
- return;
- }
-
- m_elapsedTime.start();
- m_notifyTimer.start();
- updateDuration();
-
- if (m_cameraSession) {
- m_cameraSession->setReadyForCapture(false);
-
- // Preview frame callback is cleared when setting up the camera with the media recorder.
- // We need to reset it.
- m_cameraSession->camera()->setupPreviewFrameCallback();
- }
-
- m_state = QMediaRecorder::RecordingState;
- emit stateChanged(m_state);
- setStatus(QMediaRecorder::RecordingStatus);
-}
-
-void QAndroidCaptureSession::stop(bool error)
-{
- if (m_state == QMediaRecorder::StoppedState || m_mediaRecorder == 0)
- return;
-
- setStatus(QMediaRecorder::FinalizingStatus);
-
- m_mediaRecorder->stop();
- m_notifyTimer.stop();
- updateDuration();
- m_elapsedTime.invalidate();
- m_mediaRecorder->release();
- delete m_mediaRecorder;
- m_mediaRecorder = 0;
-
- if (m_cameraSession && m_cameraSession->status() == QCamera::ActiveStatus) {
- // Viewport needs to be restarted after recording
- restartViewfinder();
- }
-
- if (!error) {
- // if the media is saved into the standard media location, register it
- // with the Android media scanner so it appears immediately in apps
- // such as the gallery.
- QString mediaPath = m_usedOutputLocation.toLocalFile();
- QString standardLoc = m_cameraSession ? AndroidMultimediaUtils::getDefaultMediaDirectory(AndroidMultimediaUtils::DCIM)
- : AndroidMultimediaUtils::getDefaultMediaDirectory(AndroidMultimediaUtils::Sounds);
- if (mediaPath.startsWith(standardLoc))
- AndroidMultimediaUtils::registerMediaFile(mediaPath);
-
- m_actualOutputLocation = m_usedOutputLocation;
- emit actualLocationChanged(m_actualOutputLocation);
- }
-
- m_state = QMediaRecorder::StoppedState;
- emit stateChanged(m_state);
- if (!m_cameraSession)
- setStatus(QMediaRecorder::LoadedStatus);
-}
-
-void QAndroidCaptureSession::setStatus(QMediaRecorder::Status status)
-{
- if (m_status == status)
- return;
-
- m_status = status;
- emit statusChanged(m_status);
-}
-
-QMediaRecorder::Status QAndroidCaptureSession::status() const
-{
- return m_status;
-}
-
-qint64 QAndroidCaptureSession::duration() const
-{
- return m_duration;
-}
-
-void QAndroidCaptureSession::setContainerFormat(const QString &format)
-{
- if (m_containerFormat == format)
- return;
-
- m_containerFormat = format;
- m_containerFormatDirty = true;
-}
-
-void QAndroidCaptureSession::setAudioSettings(const QAudioEncoderSettings &settings)
-{
- if (m_audioSettings == settings)
- return;
-
- m_audioSettings = settings;
- m_audioSettingsDirty = true;
-}
-
-void QAndroidCaptureSession::setVideoSettings(const QVideoEncoderSettings &settings)
-{
- if (!m_cameraSession || m_videoSettings == settings)
- return;
-
- m_videoSettings = settings;
- m_videoSettingsDirty = true;
-}
-
-void QAndroidCaptureSession::applySettings()
-{
- // container settings
- if (m_containerFormatDirty) {
- if (m_containerFormat.isEmpty()) {
- m_containerFormat = m_defaultSettings.outputFileExtension;
- m_outputFormat = m_defaultSettings.outputFormat;
- } else if (m_containerFormat == QLatin1String("3gp")) {
- m_outputFormat = AndroidMediaRecorder::THREE_GPP;
- } else if (!m_cameraSession && m_containerFormat == QLatin1String("amr")) {
- m_outputFormat = AndroidMediaRecorder::AMR_NB_Format;
- } else if (!m_cameraSession && m_containerFormat == QLatin1String("awb")) {
- m_outputFormat = AndroidMediaRecorder::AMR_WB_Format;
- } else {
- m_containerFormat = QStringLiteral("mp4");
- m_outputFormat = AndroidMediaRecorder::MPEG_4;
- }
-
- m_containerFormatDirty = false;
- }
-
- // audio settings
- if (m_audioSettingsDirty) {
- if (m_audioSettings.channelCount() <= 0)
- m_audioSettings.setChannelCount(m_defaultSettings.audioChannels);
- if (m_audioSettings.bitRate() <= 0)
- m_audioSettings.setBitRate(m_defaultSettings.audioBitRate);
- if (m_audioSettings.sampleRate() <= 0)
- m_audioSettings.setSampleRate(m_defaultSettings.audioSampleRate);
-
- if (m_audioSettings.codec().isEmpty())
- m_audioEncoder = m_defaultSettings.audioEncoder;
- else if (m_audioSettings.codec() == QLatin1String("aac"))
- m_audioEncoder = AndroidMediaRecorder::AAC;
- else if (m_audioSettings.codec() == QLatin1String("amr-nb"))
- m_audioEncoder = AndroidMediaRecorder::AMR_NB_Encoder;
- else if (m_audioSettings.codec() == QLatin1String("amr-wb"))
- m_audioEncoder = AndroidMediaRecorder::AMR_WB_Encoder;
- else
- m_audioEncoder = m_defaultSettings.audioEncoder;
-
- m_audioSettingsDirty = false;
- }
-
- // video settings
- if (m_cameraSession && m_cameraSession->camera() && m_videoSettingsDirty) {
- if (m_videoSettings.resolution().isEmpty()) {
- m_videoSettings.setResolution(m_defaultSettings.videoResolution);
- } else if (!m_supportedResolutions.contains(m_videoSettings.resolution())) {
- // if the requested resolution is not supported, find the closest one
- QSize reqSize = m_videoSettings.resolution();
- int reqPixelCount = reqSize.width() * reqSize.height();
- QList<int> supportedPixelCounts;
- for (int i = 0; i < m_supportedResolutions.size(); ++i) {
- const QSize &s = m_supportedResolutions.at(i);
- supportedPixelCounts.append(s.width() * s.height());
- }
- int closestIndex = qt_findClosestValue(supportedPixelCounts, reqPixelCount);
- m_videoSettings.setResolution(m_supportedResolutions.at(closestIndex));
- }
-
- if (m_videoSettings.frameRate() <= 0)
- m_videoSettings.setFrameRate(m_defaultSettings.videoFrameRate);
- if (m_videoSettings.bitRate() <= 0)
- m_videoSettings.setBitRate(m_defaultSettings.videoBitRate);
-
- if (m_videoSettings.codec().isEmpty())
- m_videoEncoder = m_defaultSettings.videoEncoder;
- else if (m_videoSettings.codec() == QLatin1String("h263"))
- m_videoEncoder = AndroidMediaRecorder::H263;
- else if (m_videoSettings.codec() == QLatin1String("h264"))
- m_videoEncoder = AndroidMediaRecorder::H264;
- else if (m_videoSettings.codec() == QLatin1String("mpeg4_sp"))
- m_videoEncoder = AndroidMediaRecorder::MPEG_4_SP;
- else
- m_videoEncoder = m_defaultSettings.videoEncoder;
-
- m_videoSettingsDirty = false;
- }
-}
-
-void QAndroidCaptureSession::updateViewfinder()
-{
- m_cameraSession->camera()->stopPreviewSynchronous();
- m_cameraSession->applyViewfinderSettings(m_videoSettings.resolution(), false);
-}
-
-void QAndroidCaptureSession::restartViewfinder()
-{
- if (!m_cameraSession)
- return;
-
- 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();
- 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);
-}
-
-void QAndroidCaptureSession::updateDuration()
-{
- if (m_elapsedTime.isValid())
- m_duration = m_elapsedTime.elapsed();
-
- emit durationChanged(m_duration);
-}
-
-void QAndroidCaptureSession::onCameraOpened()
-{
- m_supportedResolutions.clear();
- m_supportedFramerates.clear();
-
- // get supported resolutions from predefined profiles
- for (int i = 0; i < 8; ++i) {
- CaptureProfile profile = getProfile(i);
- if (!profile.isNull) {
- if (i == AndroidCamcorderProfile::QUALITY_HIGH)
- m_defaultSettings = profile;
-
- if (!m_supportedResolutions.contains(profile.videoResolution))
- m_supportedResolutions.append(profile.videoResolution);
- if (!m_supportedFramerates.contains(profile.videoFrameRate))
- m_supportedFramerates.append(profile.videoFrameRate);
- }
- }
-
- std::sort(m_supportedResolutions.begin(), m_supportedResolutions.end(), qt_sizeLessThan);
- std::sort(m_supportedFramerates.begin(), m_supportedFramerates.end());
-
- applySettings();
-}
-
-QAndroidCaptureSession::CaptureProfile QAndroidCaptureSession::getProfile(int id)
-{
- CaptureProfile profile;
- const bool hasProfile = AndroidCamcorderProfile::hasProfile(m_cameraSession->camera()->cameraId(),
- AndroidCamcorderProfile::Quality(id));
-
- if (hasProfile) {
- AndroidCamcorderProfile camProfile = AndroidCamcorderProfile::get(m_cameraSession->camera()->cameraId(),
- AndroidCamcorderProfile::Quality(id));
-
- profile.outputFormat = AndroidMediaRecorder::OutputFormat(camProfile.getValue(AndroidCamcorderProfile::fileFormat));
- profile.audioEncoder = AndroidMediaRecorder::AudioEncoder(camProfile.getValue(AndroidCamcorderProfile::audioCodec));
- profile.audioBitRate = camProfile.getValue(AndroidCamcorderProfile::audioBitRate);
- profile.audioChannels = camProfile.getValue(AndroidCamcorderProfile::audioChannels);
- profile.audioSampleRate = camProfile.getValue(AndroidCamcorderProfile::audioSampleRate);
- profile.videoEncoder = AndroidMediaRecorder::VideoEncoder(camProfile.getValue(AndroidCamcorderProfile::videoCodec));
- profile.videoBitRate = camProfile.getValue(AndroidCamcorderProfile::videoBitRate);
- profile.videoFrameRate = camProfile.getValue(AndroidCamcorderProfile::videoFrameRate);
- profile.videoResolution = QSize(camProfile.getValue(AndroidCamcorderProfile::videoFrameWidth),
- camProfile.getValue(AndroidCamcorderProfile::videoFrameHeight));
-
- if (profile.outputFormat == AndroidMediaRecorder::MPEG_4)
- profile.outputFileExtension = QStringLiteral("mp4");
- else if (profile.outputFormat == AndroidMediaRecorder::THREE_GPP)
- profile.outputFileExtension = QStringLiteral("3gp");
- else if (profile.outputFormat == AndroidMediaRecorder::AMR_NB_Format)
- profile.outputFileExtension = QStringLiteral("amr");
- else if (profile.outputFormat == AndroidMediaRecorder::AMR_WB_Format)
- profile.outputFileExtension = QStringLiteral("awb");
-
- profile.isNull = false;
- }
-
- return profile;
-}
-
-void QAndroidCaptureSession::onError(int what, int extra)
-{
- Q_UNUSED(what);
- Q_UNUSED(extra);
- stop(true);
- emit error(QMediaRecorder::ResourceError, QLatin1String("Unknown error."));
-}
-
-void QAndroidCaptureSession::onInfo(int what, int extra)
-{
- Q_UNUSED(extra);
- if (what == 800) {
- // MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
- setState(QMediaRecorder::StoppedState);
- emit error(QMediaRecorder::OutOfSpaceError, QLatin1String("Maximum duration reached."));
- } else if (what == 801) {
- // MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED
- setState(QMediaRecorder::StoppedState);
- emit error(QMediaRecorder::OutOfSpaceError, QLatin1String("Maximum file size reached."));
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcapturesession.h b/src/plugins/android/src/mediacapture/qandroidcapturesession.h
deleted file mode 100644
index 8cfb9ad2a..000000000
--- a/src/plugins/android/src/mediacapture/qandroidcapturesession.h
+++ /dev/null
@@ -1,182 +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 QANDROIDCAPTURESESSION_H
-#define QANDROIDCAPTURESESSION_H
-
-#include <qobject.h>
-#include <qmediarecorder.h>
-#include <qurl.h>
-#include <qelapsedtimer.h>
-#include <qtimer.h>
-#include <private/qmediastoragelocation_p.h>
-#include "androidmediarecorder.h"
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCameraSession;
-
-class QAndroidCaptureSession : public QObject
-{
- Q_OBJECT
-public:
- explicit QAndroidCaptureSession(QAndroidCameraSession *cameraSession = 0);
- ~QAndroidCaptureSession();
-
- QList<QSize> supportedResolutions() const { return m_supportedResolutions; }
- QList<qreal> supportedFrameRates() const { return m_supportedFramerates; }
-
- QString audioInput() const { return m_audioInput; }
- void setAudioInput(const QString &input);
-
- QUrl outputLocation() const;
- bool setOutputLocation(const QUrl &location);
-
- QMediaRecorder::State state() const;
- void setState(QMediaRecorder::State state);
-
- QMediaRecorder::Status status() const;
-
- qint64 duration() const;
-
- QString containerFormat() const { return m_containerFormat; }
- void setContainerFormat(const QString &format);
-
- QAudioEncoderSettings audioSettings() const { return m_audioSettings; }
- void setAudioSettings(const QAudioEncoderSettings &settings);
-
- QVideoEncoderSettings videoSettings() const { return m_videoSettings; }
- void setVideoSettings(const QVideoEncoderSettings &settings);
-
- void applySettings();
-
-Q_SIGNALS:
- void audioInputChanged(const QString& name);
- void stateChanged(QMediaRecorder::State state);
- void statusChanged(QMediaRecorder::Status status);
- void durationChanged(qint64 position);
- void actualLocationChanged(const QUrl &location);
- void error(int error, const QString &errorString);
-
-private Q_SLOTS:
- void updateDuration();
- void onCameraOpened();
-
- void onError(int what, int extra);
- void onInfo(int what, int extra);
-
-private:
- struct CaptureProfile {
- AndroidMediaRecorder::OutputFormat outputFormat;
- QString outputFileExtension;
-
- AndroidMediaRecorder::AudioEncoder audioEncoder;
- int audioBitRate;
- int audioChannels;
- int audioSampleRate;
-
- AndroidMediaRecorder::VideoEncoder videoEncoder;
- int videoBitRate;
- int videoFrameRate;
- QSize videoResolution;
-
- bool isNull;
-
- CaptureProfile()
- : outputFormat(AndroidMediaRecorder::MPEG_4)
- , outputFileExtension(QLatin1String("mp4"))
- , audioEncoder(AndroidMediaRecorder::DefaultAudioEncoder)
- , audioBitRate(128000)
- , audioChannels(2)
- , audioSampleRate(44100)
- , videoEncoder(AndroidMediaRecorder::DefaultVideoEncoder)
- , videoBitRate(1)
- , videoFrameRate(-1)
- , videoResolution(320, 240)
- , isNull(true)
- { }
- };
-
- CaptureProfile getProfile(int id);
-
- void start();
- void stop(bool error = false);
-
- void setStatus(QMediaRecorder::Status status);
-
- void updateViewfinder();
- void restartViewfinder();
-
- AndroidMediaRecorder *m_mediaRecorder;
- QAndroidCameraSession *m_cameraSession;
-
- QString m_audioInput;
- AndroidMediaRecorder::AudioSource m_audioSource;
-
- QMediaStorageLocation m_mediaStorageLocation;
-
- QElapsedTimer m_elapsedTime;
- QTimer m_notifyTimer;
- qint64 m_duration;
-
- QMediaRecorder::State m_state;
- QMediaRecorder::Status m_status;
- QUrl m_requestedOutputLocation;
- QUrl m_usedOutputLocation;
- QUrl m_actualOutputLocation;
-
- CaptureProfile m_defaultSettings;
-
- QString m_containerFormat;
- QAudioEncoderSettings m_audioSettings;
- QVideoEncoderSettings m_videoSettings;
- bool m_containerFormatDirty;
- bool m_videoSettingsDirty;
- bool m_audioSettingsDirty;
- AndroidMediaRecorder::OutputFormat m_outputFormat;
- AndroidMediaRecorder::AudioEncoder m_audioEncoder;
- AndroidMediaRecorder::VideoEncoder m_videoEncoder;
-
- QList<QSize> m_supportedResolutions;
- QList<qreal> m_supportedFramerates;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDCAPTURESESSION_H
diff --git a/src/plugins/android/src/mediacapture/qandroidimageencodercontrol.cpp b/src/plugins/android/src/mediacapture/qandroidimageencodercontrol.cpp
deleted file mode 100644
index 666f553e5..000000000
--- a/src/plugins/android/src/mediacapture/qandroidimageencodercontrol.cpp
+++ /dev/null
@@ -1,93 +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 "qandroidimageencodercontrol.h"
-
-#include "qandroidcamerasession.h"
-#include "androidcamera.h"
-
-QT_BEGIN_NAMESPACE
-
-QAndroidImageEncoderControl::QAndroidImageEncoderControl(QAndroidCameraSession *session)
- : QImageEncoderControl()
- , m_session(session)
-{
- connect(m_session, SIGNAL(opened()),
- this, SLOT(onCameraOpened()));
-}
-
-QStringList QAndroidImageEncoderControl::supportedImageCodecs() const
-{
- return QStringList() << QLatin1String("jpeg");
-}
-
-QString QAndroidImageEncoderControl::imageCodecDescription(const QString &codecName) const
-{
- if (codecName == QLatin1String("jpeg"))
- return tr("JPEG image");
-
- return QString();
-}
-
-QList<QSize> QAndroidImageEncoderControl::supportedResolutions(const QImageEncoderSettings &settings, bool *continuous) const
-{
- Q_UNUSED(settings);
-
- if (continuous)
- *continuous = false;
-
- return m_supportedResolutions;
-}
-
-QImageEncoderSettings QAndroidImageEncoderControl::imageSettings() const
-{
- return m_session->imageSettings();
-}
-
-void QAndroidImageEncoderControl::setImageSettings(const QImageEncoderSettings &settings)
-{
- m_session->setImageSettings(settings);
-}
-
-void QAndroidImageEncoderControl::onCameraOpened()
-{
- m_supportedResolutions = m_session->camera()->getSupportedPictureSizes();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidimageencodercontrol.h b/src/plugins/android/src/mediacapture/qandroidimageencodercontrol.h
deleted file mode 100644
index 52f602e78..000000000
--- a/src/plugins/android/src/mediacapture/qandroidimageencodercontrol.h
+++ /dev/null
@@ -1,72 +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 QANDROIDIMAGEENCODERCONTROL_H
-#define QANDROIDIMAGEENCODERCONTROL_H
-
-#include <qimageencodercontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCameraSession;
-
-class QAndroidImageEncoderControl : public QImageEncoderControl
-{
- Q_OBJECT
-public:
- explicit QAndroidImageEncoderControl(QAndroidCameraSession *session);
-
- QStringList supportedImageCodecs() const override;
- QString imageCodecDescription(const QString &codecName) const override;
- QList<QSize> supportedResolutions(const QImageEncoderSettings &settings, bool *continuous = 0) const override;
- QImageEncoderSettings imageSettings() const override;
- void setImageSettings(const QImageEncoderSettings &settings) override;
-
-private Q_SLOTS:
- void onCameraOpened();
-
-private:
- QAndroidCameraSession *m_session;
-
- QList<QSize> m_supportedResolutions;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDIMAGEENCODERCONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidmediacontainercontrol.cpp b/src/plugins/android/src/mediacapture/qandroidmediacontainercontrol.cpp
deleted file mode 100644
index bda711367..000000000
--- a/src/plugins/android/src/mediacapture/qandroidmediacontainercontrol.cpp
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "qandroidmediacontainercontrol.h"
-
-#include "qandroidcapturesession.h"
-
-QT_BEGIN_NAMESPACE
-
-QAndroidMediaContainerControl::QAndroidMediaContainerControl(QAndroidCaptureSession *session)
- : QMediaContainerControl()
- , m_session(session)
-{
-}
-
-QStringList QAndroidMediaContainerControl::supportedContainers() const
-{
- return QStringList() << QLatin1String("mp4")
- << QLatin1String("3gp")
- << QLatin1String("amr")
- << QLatin1String("awb");
-}
-
-QString QAndroidMediaContainerControl::containerFormat() const
-{
- return m_session->containerFormat();
-}
-
-void QAndroidMediaContainerControl::setContainerFormat(const QString &format)
-{
- m_session->setContainerFormat(format);
-}
-
-QString QAndroidMediaContainerControl::containerDescription(const QString &formatMimeType) const
-{
- if (formatMimeType == QLatin1String("mp4"))
- return tr("MPEG4 media file format");
- else if (formatMimeType == QLatin1String("3gp"))
- return tr("3GPP media file format");
- else if (formatMimeType == QLatin1String("amr"))
- return tr("AMR NB file format");
- else if (formatMimeType == QLatin1String("awb"))
- return tr("AMR WB file format");
-
- return QString();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidmediacontainercontrol.h b/src/plugins/android/src/mediacapture/qandroidmediacontainercontrol.h
deleted file mode 100644
index 1d90fb6cd..000000000
--- a/src/plugins/android/src/mediacapture/qandroidmediacontainercontrol.h
+++ /dev/null
@@ -1,66 +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 QANDROIDMEDIACONTAINERCONTROL_H
-#define QANDROIDMEDIACONTAINERCONTROL_H
-
-#include <qmediacontainercontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCaptureSession;
-
-class QAndroidMediaContainerControl : public QMediaContainerControl
-{
- Q_OBJECT
-public:
- QAndroidMediaContainerControl(QAndroidCaptureSession *session);
-
- QStringList supportedContainers() const override;
- QString containerFormat() const override;
- void setContainerFormat(const QString &format) override;
- QString containerDescription(const QString &formatMimeType) const override;
-
-private:
- QAndroidCaptureSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDMEDIACONTAINERCONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidmediarecordercontrol.cpp b/src/plugins/android/src/mediacapture/qandroidmediarecordercontrol.cpp
deleted file mode 100644
index fa68409d3..000000000
--- a/src/plugins/android/src/mediacapture/qandroidmediarecordercontrol.cpp
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "qandroidmediarecordercontrol.h"
-
-#include "qandroidcapturesession.h"
-
-QT_BEGIN_NAMESPACE
-
-QAndroidMediaRecorderControl::QAndroidMediaRecorderControl(QAndroidCaptureSession *session)
- : QMediaRecorderControl()
- , m_session(session)
-{
- connect(m_session, SIGNAL(stateChanged(QMediaRecorder::State)), this, SIGNAL(stateChanged(QMediaRecorder::State)));
- connect(m_session, SIGNAL(statusChanged(QMediaRecorder::Status)), this, SIGNAL(statusChanged(QMediaRecorder::Status)));
- connect(m_session, SIGNAL(durationChanged(qint64)), this, SIGNAL(durationChanged(qint64)));
- connect(m_session, SIGNAL(actualLocationChanged(QUrl)), this, SIGNAL(actualLocationChanged(QUrl)));
- connect(m_session, SIGNAL(error(int,QString)), this, SIGNAL(error(int,QString)));
-}
-
-QUrl QAndroidMediaRecorderControl::outputLocation() const
-{
- return m_session->outputLocation();
-}
-
-bool QAndroidMediaRecorderControl::setOutputLocation(const QUrl &location)
-{
- return m_session->setOutputLocation(location);
-}
-
-QMediaRecorder::State QAndroidMediaRecorderControl::state() const
-{
- return m_session->state();
-}
-
-QMediaRecorder::Status QAndroidMediaRecorderControl::status() const
-{
- return m_session->status();
-}
-
-qint64 QAndroidMediaRecorderControl::duration() const
-{
- return m_session->duration();
-}
-
-bool QAndroidMediaRecorderControl::isMuted() const
-{
- // No API for this in Android
- return false;
-}
-
-qreal QAndroidMediaRecorderControl::volume() const
-{
- // No API for this in Android
- return 1.0;
-}
-
-void QAndroidMediaRecorderControl::applySettings()
-{
- m_session->applySettings();
-}
-
-void QAndroidMediaRecorderControl::setState(QMediaRecorder::State state)
-{
- m_session->setState(state);
-}
-
-void QAndroidMediaRecorderControl::setMuted(bool muted)
-{
- // No API for this in Android
- Q_UNUSED(muted);
- qWarning("QMediaRecorder::setMuted() is not supported on Android.");
-}
-
-void QAndroidMediaRecorderControl::setVolume(qreal volume)
-{
- // No API for this in Android
- Q_UNUSED(volume);
- qWarning("QMediaRecorder::setVolume() is not supported on Android.");
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidmediarecordercontrol.h b/src/plugins/android/src/mediacapture/qandroidmediarecordercontrol.h
deleted file mode 100644
index 6da59a50d..000000000
--- a/src/plugins/android/src/mediacapture/qandroidmediarecordercontrol.h
+++ /dev/null
@@ -1,75 +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 QANDROIDMEDIARECORDERCONTROL_H
-#define QANDROIDMEDIARECORDERCONTROL_H
-
-#include <qmediarecordercontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCaptureSession;
-
-class QAndroidMediaRecorderControl : public QMediaRecorderControl
-{
- Q_OBJECT
-public:
- explicit QAndroidMediaRecorderControl(QAndroidCaptureSession *session);
-
- QUrl outputLocation() const override;
- bool setOutputLocation(const QUrl &location) override;
- QMediaRecorder::State state() const override;
- QMediaRecorder::Status status() const override;
- qint64 duration() const override;
- bool isMuted() const override;
- qreal volume() const override;
- void applySettings() override;
-
-public Q_SLOTS:
- void setState(QMediaRecorder::State state) override;
- void setMuted(bool muted) override;
- void setVolume(qreal volume) override;
-
-private:
- QAndroidCaptureSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDMEDIARECORDERCONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidmediavideoprobecontrol.cpp b/src/plugins/android/src/mediacapture/qandroidmediavideoprobecontrol.cpp
deleted file mode 100644
index 1995ebf6a..000000000
--- a/src/plugins/android/src/mediacapture/qandroidmediavideoprobecontrol.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Integrated Computer Solutions, Inc
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "qandroidmediavideoprobecontrol.h"
-#include <qvideoframe.h>
-
-QT_BEGIN_NAMESPACE
-
-QAndroidMediaVideoProbeControl::QAndroidMediaVideoProbeControl(QObject *parent) :
- QMediaVideoProbeControl(parent)
-{
-}
-
-QAndroidMediaVideoProbeControl::~QAndroidMediaVideoProbeControl()
-{
-
-}
-
-void QAndroidMediaVideoProbeControl::newFrameProbed(const QVideoFrame &frame)
-{
- emit videoFrameProbed(frame);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidmediavideoprobecontrol.h b/src/plugins/android/src/mediacapture/qandroidmediavideoprobecontrol.h
deleted file mode 100644
index 3306ad224..000000000
--- a/src/plugins/android/src/mediacapture/qandroidmediavideoprobecontrol.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Integrated Computer Solutions, Inc
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 QANDROIDMEDIAVIDEOPROBECONTROL_H
-#define QANDROIDMEDIAVIDEOPROBECONTROL_H
-
-#include <qmediavideoprobecontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidMediaVideoProbeControl : public QMediaVideoProbeControl
-{
- Q_OBJECT
-public:
- explicit QAndroidMediaVideoProbeControl(QObject *parent = 0);
- virtual ~QAndroidMediaVideoProbeControl();
-
- void newFrameProbed(const QVideoFrame& frame);
-
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDMEDIAVIDEOPROBECONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidvideodeviceselectorcontrol.cpp b/src/plugins/android/src/mediacapture/qandroidvideodeviceselectorcontrol.cpp
deleted file mode 100644
index 65005c796..000000000
--- a/src/plugins/android/src/mediacapture/qandroidvideodeviceselectorcontrol.cpp
+++ /dev/null
@@ -1,99 +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 "qandroidvideodeviceselectorcontrol.h"
-
-#include "qandroidcamerasession.h"
-#include "androidcamera.h"
-
-QT_BEGIN_NAMESPACE
-
-QAndroidVideoDeviceSelectorControl::QAndroidVideoDeviceSelectorControl(QAndroidCameraSession *session)
- : QVideoDeviceSelectorControl(0)
- , m_selectedDevice(0)
- , m_cameraSession(session)
-{
-}
-
-QAndroidVideoDeviceSelectorControl::~QAndroidVideoDeviceSelectorControl()
-{
-}
-
-int QAndroidVideoDeviceSelectorControl::deviceCount() const
-{
- return QAndroidCameraSession::availableCameras().count();
-}
-
-QString QAndroidVideoDeviceSelectorControl::deviceName(int index) const
-{
- if (index < 0 || index >= QAndroidCameraSession::availableCameras().count())
- return QString();
-
- return QString::fromLatin1(QAndroidCameraSession::availableCameras().at(index).name);
-}
-
-QString QAndroidVideoDeviceSelectorControl::deviceDescription(int index) const
-{
- if (index < 0 || index >= QAndroidCameraSession::availableCameras().count())
- return QString();
-
- return QAndroidCameraSession::availableCameras().at(index).description;
-}
-
-int QAndroidVideoDeviceSelectorControl::defaultDevice() const
-{
- return 0;
-}
-
-int QAndroidVideoDeviceSelectorControl::selectedDevice() const
-{
- return m_selectedDevice;
-}
-
-void QAndroidVideoDeviceSelectorControl::setSelectedDevice(int index)
-{
- if (index != m_selectedDevice) {
- m_selectedDevice = index;
- m_cameraSession->setSelectedCamera(m_selectedDevice);
- emit selectedDeviceChanged(index);
- emit selectedDeviceChanged(deviceName(index));
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidvideodeviceselectorcontrol.h b/src/plugins/android/src/mediacapture/qandroidvideodeviceselectorcontrol.h
deleted file mode 100644
index b6cc30b2d..000000000
--- a/src/plugins/android/src/mediacapture/qandroidvideodeviceselectorcontrol.h
+++ /dev/null
@@ -1,74 +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 QANDROIDVIDEODEVICESELECTORCONTROL_H
-#define QANDROIDVIDEODEVICESELECTORCONTROL_H
-
-#include <qvideodeviceselectorcontrol.h>
-#include <QtCore/qstringlist.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCameraSession;
-
-class QAndroidVideoDeviceSelectorControl : public QVideoDeviceSelectorControl
-{
- Q_OBJECT
-public:
- explicit QAndroidVideoDeviceSelectorControl(QAndroidCameraSession *session);
- ~QAndroidVideoDeviceSelectorControl();
-
- int deviceCount() const;
-
- QString deviceName(int index) const;
- QString deviceDescription(int index) const;
-
- int defaultDevice() const;
- int selectedDevice() const;
- void setSelectedDevice(int index);
-
-private:
- int m_selectedDevice;
-
- QAndroidCameraSession *m_cameraSession;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDVIDEODEVICESELECTORCONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidvideoencodersettingscontrol.cpp b/src/plugins/android/src/mediacapture/qandroidvideoencodersettingscontrol.cpp
deleted file mode 100644
index 3d67e8cfa..000000000
--- a/src/plugins/android/src/mediacapture/qandroidvideoencodersettingscontrol.cpp
+++ /dev/null
@@ -1,97 +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 "qandroidvideoencodersettingscontrol.h"
-
-#include "qandroidcapturesession.h"
-
-QT_BEGIN_NAMESPACE
-
-QAndroidVideoEncoderSettingsControl::QAndroidVideoEncoderSettingsControl(QAndroidCaptureSession *session)
- : QVideoEncoderSettingsControl()
- , m_session(session)
-{
-}
-
-QList<QSize> QAndroidVideoEncoderSettingsControl::supportedResolutions(const QVideoEncoderSettings &, bool *continuous) const
-{
- if (continuous)
- *continuous = false;
-
- return m_session->supportedResolutions();
-}
-
-QList<qreal> QAndroidVideoEncoderSettingsControl::supportedFrameRates(const QVideoEncoderSettings &, bool *continuous) const
-{
- if (continuous)
- *continuous = false;
-
- return m_session->supportedFrameRates();
-}
-
-QStringList QAndroidVideoEncoderSettingsControl::supportedVideoCodecs() const
-{
- return QStringList() << QLatin1String("h263")
- << QLatin1String("h264")
- << QLatin1String("mpeg4_sp");
-}
-
-QString QAndroidVideoEncoderSettingsControl::videoCodecDescription(const QString &codecName) const
-{
- if (codecName == QLatin1String("h263"))
- return tr("H.263 compression");
- else if (codecName == QLatin1String("h264"))
- return tr("H.264 compression");
- else if (codecName == QLatin1String("mpeg4_sp"))
- return tr("MPEG-4 SP compression");
-
- return QString();
-}
-
-QVideoEncoderSettings QAndroidVideoEncoderSettingsControl::videoSettings() const
-{
- return m_session->videoSettings();
-}
-
-void QAndroidVideoEncoderSettingsControl::setVideoSettings(const QVideoEncoderSettings &settings)
-{
- m_session->setVideoSettings(settings);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidvideoencodersettingscontrol.h b/src/plugins/android/src/mediacapture/qandroidvideoencodersettingscontrol.h
deleted file mode 100644
index 146b44ac4..000000000
--- a/src/plugins/android/src/mediacapture/qandroidvideoencodersettingscontrol.h
+++ /dev/null
@@ -1,68 +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 QANDROIDVIDEOENCODERSETTINGSCONTROL_H
-#define QANDROIDVIDEOENCODERSETTINGSCONTROL_H
-
-#include <qvideoencodersettingscontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCaptureSession;
-
-class QAndroidVideoEncoderSettingsControl : public QVideoEncoderSettingsControl
-{
- Q_OBJECT
-public:
- explicit QAndroidVideoEncoderSettingsControl(QAndroidCaptureSession *session);
-
- QList<QSize> supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous = 0) const override;
- QList<qreal> supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous = 0) const override;
- QStringList supportedVideoCodecs() const override;
- QString videoCodecDescription(const QString &codecName) const override;
- QVideoEncoderSettings videoSettings() const override;
- void setVideoSettings(const QVideoEncoderSettings &settings) override;
-
-private:
- QAndroidCaptureSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDVIDEOENCODERSETTINGSCONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidviewfindersettingscontrol.cpp b/src/plugins/android/src/mediacapture/qandroidviewfindersettingscontrol.cpp
deleted file mode 100644
index 01a826064..000000000
--- a/src/plugins/android/src/mediacapture/qandroidviewfindersettingscontrol.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "qandroidviewfindersettingscontrol.h"
-#include "qandroidcamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-QAndroidViewfinderSettingsControl2::QAndroidViewfinderSettingsControl2(QAndroidCameraSession *session)
- : m_cameraSession(session)
-{
-}
-
-QList<QCameraViewfinderSettings> QAndroidViewfinderSettingsControl2::supportedViewfinderSettings() const
-{
- QList<QCameraViewfinderSettings> viewfinderSettings;
-
- const QList<QSize> previewSizes = m_cameraSession->getSupportedPreviewSizes();
- const QList<QVideoFrame::PixelFormat> pixelFormats = m_cameraSession->getSupportedPixelFormats();
- const QList<AndroidCamera::FpsRange> fpsRanges = m_cameraSession->getSupportedPreviewFpsRange();
-
- viewfinderSettings.reserve(previewSizes.size() * pixelFormats.size() * fpsRanges.size());
-
- for (const QSize& size : previewSizes) {
- for (QVideoFrame::PixelFormat pixelFormat : pixelFormats) {
- for (const AndroidCamera::FpsRange& fpsRange : fpsRanges) {
- QCameraViewfinderSettings s;
- s.setResolution(size);
- s.setPixelAspectRatio(QSize(1, 1));
- s.setPixelFormat(pixelFormat);
- s.setMinimumFrameRate(fpsRange.getMinReal());
- s.setMaximumFrameRate(fpsRange.getMaxReal());
- viewfinderSettings << s;
- }
- }
- }
- return viewfinderSettings;
-}
-
-QCameraViewfinderSettings QAndroidViewfinderSettingsControl2::viewfinderSettings() const
-{
- return m_cameraSession->viewfinderSettings();
-}
-
-void QAndroidViewfinderSettingsControl2::setViewfinderSettings(const QCameraViewfinderSettings &settings)
-{
- m_cameraSession->setViewfinderSettings(settings);
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qandroidviewfindersettingscontrol.cpp"
diff --git a/src/plugins/android/src/mediacapture/qandroidviewfindersettingscontrol.h b/src/plugins/android/src/mediacapture/qandroidviewfindersettingscontrol.h
deleted file mode 100644
index c7863a144..000000000
--- a/src/plugins/android/src/mediacapture/qandroidviewfindersettingscontrol.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef QANDROIDVIEWFINDERSETTINGSCONTROL_H
-#define QANDROIDVIEWFINDERSETTINGSCONTROL_H
-
-#include <QtMultimedia/qcameraviewfindersettingscontrol.h>
-#include <QtMultimedia/qcameraviewfindersettings.h>
-
-#include <QtCore/qpointer.h>
-#include <QtCore/qglobal.h>
-#include <QtCore/qsize.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCameraSession;
-
-class QAndroidViewfinderSettingsControl2 : public QCameraViewfinderSettingsControl2
-{
- Q_OBJECT
-public:
- explicit QAndroidViewfinderSettingsControl2(QAndroidCameraSession *session);
-
- QList<QCameraViewfinderSettings> supportedViewfinderSettings() const override;
- QCameraViewfinderSettings viewfinderSettings() const override;
- void setViewfinderSettings(const QCameraViewfinderSettings &settings) override;
-
-private:
- QAndroidCameraSession *m_cameraSession;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDVIEWFINDERSETTINGSCONTROL_H
diff --git a/src/plugins/android/src/mediaplayer/mediaplayer.pri b/src/plugins/android/src/mediaplayer/mediaplayer.pri
deleted file mode 100644
index 3a9cef3a1..000000000
--- a/src/plugins/android/src/mediaplayer/mediaplayer.pri
+++ /dev/null
@@ -1,17 +0,0 @@
-INCLUDEPATH += $$PWD
-
-HEADERS += \
- $$PWD/qandroidmediaplayercontrol.h \
- $$PWD/qandroidmediaservice.h \
- $$PWD/qandroidmetadatareadercontrol.h \
- $$PWD/qandroidaudiorolecontrol.h \
- $$PWD/qandroidcustomaudiorolecontrol.h \
- $$PWD/qandroidmediaplayervideorenderercontrol.h
-
-SOURCES += \
- $$PWD/qandroidmediaplayercontrol.cpp \
- $$PWD/qandroidmediaservice.cpp \
- $$PWD/qandroidmetadatareadercontrol.cpp \
- $$PWD/qandroidaudiorolecontrol.cpp \
- $$PWD/qandroidcustomaudiorolecontrol.cpp \
- $$PWD/qandroidmediaplayervideorenderercontrol.cpp
diff --git a/src/plugins/android/src/mediaplayer/qandroidaudiorolecontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidaudiorolecontrol.cpp
deleted file mode 100644
index 5879b0cb9..000000000
--- a/src/plugins/android/src/mediaplayer/qandroidaudiorolecontrol.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "qandroidaudiorolecontrol.h"
-
-QT_BEGIN_NAMESPACE
-
-QAndroidAudioRoleControl::QAndroidAudioRoleControl(QObject *parent)
- : QAudioRoleControl(parent)
-{
-}
-
-QAudio::Role QAndroidAudioRoleControl::audioRole() const
-{
- return m_role;
-}
-
-void QAndroidAudioRoleControl::setAudioRole(QAudio::Role role)
-{
- if (m_role == role)
- return;
-
- m_role = role;
- emit audioRoleChanged(m_role);
-}
-
-QList<QAudio::Role> QAndroidAudioRoleControl::supportedAudioRoles() const
-{
- return QList<QAudio::Role>()
- << QAudio::VoiceCommunicationRole
- << QAudio::MusicRole
- << QAudio::VideoRole
- << QAudio::SonificationRole
- << QAudio::AlarmRole
- << QAudio::NotificationRole
- << QAudio::RingtoneRole
- << QAudio::AccessibilityRole
- << QAudio::GameRole;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediaplayer/qandroidaudiorolecontrol.h b/src/plugins/android/src/mediaplayer/qandroidaudiorolecontrol.h
deleted file mode 100644
index 89219e026..000000000
--- a/src/plugins/android/src/mediaplayer/qandroidaudiorolecontrol.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef QANDROIDAUDIOROLECONTROL_H
-#define QANDROIDAUDIOROLECONTROL_H
-
-#include <qaudiorolecontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidAudioRoleControl : public QAudioRoleControl
-{
- Q_OBJECT
-public:
- explicit QAndroidAudioRoleControl(QObject *parent = nullptr);
-
- QAudio::Role audioRole() const override;
- void setAudioRole(QAudio::Role role) override;
- QList<QAudio::Role> supportedAudioRoles() const override;
-
-private:
- QAudio::Role m_role = QAudio::UnknownRole;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDAUDIOROLECONTROL_H
diff --git a/src/plugins/android/src/mediaplayer/qandroidcustomaudiorolecontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidcustomaudiorolecontrol.cpp
deleted file mode 100644
index cb7b5d0af..000000000
--- a/src/plugins/android/src/mediaplayer/qandroidcustomaudiorolecontrol.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "qandroidcustomaudiorolecontrol.h"
-
-QT_BEGIN_NAMESPACE
-
-QAndroidCustomAudioRoleControl::QAndroidCustomAudioRoleControl(QObject *parent)
- : QCustomAudioRoleControl(parent)
-{
-}
-
-QString QAndroidCustomAudioRoleControl::customAudioRole() const
-{
- return m_role;
-}
-
-void QAndroidCustomAudioRoleControl::setCustomAudioRole(const QString &role)
-{
- if (m_role == role)
- return;
-
- m_role = role;
- emit customAudioRoleChanged(m_role);
-}
-
-QStringList QAndroidCustomAudioRoleControl::supportedCustomAudioRoles() const
-{
- return QStringList()
- << "CONTENT_TYPE_MOVIE"
- << "CONTENT_TYPE_MUSIC"
- << "CONTENT_TYPE_SONIFICATION"
- << "CONTENT_TYPE_SPEECH"
- << "USAGE_ALARM"
- << "USAGE_ASSISTANCE_ACCESSIBILITY"
- << "USAGE_ASSISTANCE_NAVIGATION_GUIDANCE"
- << "USAGE_ASSISTANCE_SONIFICATION"
- << "USAGE_ASSISTANT"
- << "USAGE_GAME"
- << "USAGE_MEDIA"
- << "USAGE_NOTIFICATION"
- << "USAGE_NOTIFICATION_COMMUNICATION_DELAYED"
- << "USAGE_NOTIFICATION_COMMUNICATION_INSTANT"
- << "USAGE_NOTIFICATION_COMMUNICATION_REQUEST"
- << "USAGE_NOTIFICATION_EVENT"
- << "USAGE_NOTIFICATION_RINGTONE"
- << "USAGE_VOICE_COMMUNICATION"
- << "USAGE_VOICE_COMMUNICATION_SIGNALLING";
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediaplayer/qandroidcustomaudiorolecontrol.h b/src/plugins/android/src/mediaplayer/qandroidcustomaudiorolecontrol.h
deleted file mode 100644
index 4604ebe67..000000000
--- a/src/plugins/android/src/mediaplayer/qandroidcustomaudiorolecontrol.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef QANDROIDCUSTOMAUDIOROLECONTROL_H
-#define QANDROIDCUSTOMAUDIOROLECONTROL_H
-
-#include <qcustomaudiorolecontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidCustomAudioRoleControl : public QCustomAudioRoleControl
-{
- Q_OBJECT
-public:
- explicit QAndroidCustomAudioRoleControl(QObject *parent = nullptr);
-
- QString customAudioRole() const override;
- void setCustomAudioRole(const QString &role) override;
- QStringList supportedCustomAudioRoles() const override;
-
-private:
- QString m_role;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDCUSTOMAUDIOROLECONTROL_H
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp
deleted file mode 100644
index 82250b654..000000000
--- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp
+++ /dev/null
@@ -1,797 +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 "qandroidmediaplayercontrol.h"
-#include "androidmediaplayer.h"
-#include "qandroidvideooutput.h"
-
-QT_BEGIN_NAMESPACE
-
-class StateChangeNotifier
-{
-public:
- StateChangeNotifier(QAndroidMediaPlayerControl *mp)
- : mControl(mp)
- , mPreviousState(mp->state())
- , mPreviousMediaStatus(mp->mediaStatus())
- {
- ++mControl->mActiveStateChangeNotifiers;
- }
-
- ~StateChangeNotifier()
- {
- if (--mControl->mActiveStateChangeNotifiers)
- return;
-
- if (mPreviousMediaStatus != mControl->mediaStatus())
- Q_EMIT mControl->mediaStatusChanged(mControl->mediaStatus());
-
- if (mPreviousState != mControl->state())
- Q_EMIT mControl->stateChanged(mControl->state());
- }
-
-private:
- QAndroidMediaPlayerControl *mControl;
- QMediaPlayer::State mPreviousState;
- QMediaPlayer::MediaStatus mPreviousMediaStatus;
-};
-
-
-QAndroidMediaPlayerControl::QAndroidMediaPlayerControl(QObject *parent)
- : QMediaPlayerControl(parent),
- mMediaPlayer(new AndroidMediaPlayer),
- mCurrentState(QMediaPlayer::StoppedState),
- mCurrentMediaStatus(QMediaPlayer::NoMedia),
- mMediaStream(0),
- mVideoOutput(0),
- mSeekable(true),
- mBufferPercent(-1),
- mBufferFilled(false),
- mAudioAvailable(false),
- mVideoAvailable(false),
- mBuffering(false),
- mState(AndroidMediaPlayer::Uninitialized),
- mPendingState(-1),
- mPendingPosition(-1),
- mPendingSetMedia(false),
- mPendingVolume(-1),
- mPendingMute(-1),
- mReloadingMedia(false),
- mActiveStateChangeNotifiers(0),
- mPendingPlaybackRate(1.0),
- mHasPendingPlaybackRate(false)
-{
- connect(mMediaPlayer,SIGNAL(bufferingChanged(qint32)),
- this,SLOT(onBufferingChanged(qint32)));
- connect(mMediaPlayer,SIGNAL(info(qint32,qint32)),
- this,SLOT(onInfo(qint32,qint32)));
- connect(mMediaPlayer,SIGNAL(error(qint32,qint32)),
- this,SLOT(onError(qint32,qint32)));
- connect(mMediaPlayer,SIGNAL(stateChanged(qint32)),
- this,SLOT(onStateChanged(qint32)));
- connect(mMediaPlayer,SIGNAL(videoSizeChanged(qint32,qint32)),
- this,SLOT(onVideoSizeChanged(qint32,qint32)));
- connect(mMediaPlayer,SIGNAL(progressChanged(qint64)),
- this,SIGNAL(positionChanged(qint64)));
- connect(mMediaPlayer,SIGNAL(durationChanged(qint64)),
- this,SIGNAL(durationChanged(qint64)));
-}
-
-QAndroidMediaPlayerControl::~QAndroidMediaPlayerControl()
-{
- mMediaPlayer->release();
- delete mMediaPlayer;
-}
-
-QMediaPlayer::State QAndroidMediaPlayerControl::state() const
-{
- return mCurrentState;
-}
-
-QMediaPlayer::MediaStatus QAndroidMediaPlayerControl::mediaStatus() const
-{
- return mCurrentMediaStatus;
-}
-
-qint64 QAndroidMediaPlayerControl::duration() const
-{
- if ((mState & (AndroidMediaPlayer::Prepared
- | AndroidMediaPlayer::Started
- | AndroidMediaPlayer::Paused
- | AndroidMediaPlayer::Stopped
- | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
- return 0;
- }
-
- return mMediaPlayer->getDuration();
-}
-
-qint64 QAndroidMediaPlayerControl::position() const
-{
- if (mCurrentMediaStatus == QMediaPlayer::EndOfMedia)
- return duration();
-
- if ((mState & (AndroidMediaPlayer::Prepared
- | AndroidMediaPlayer::Started
- | AndroidMediaPlayer::Paused
- | AndroidMediaPlayer::PlaybackCompleted))) {
- return mMediaPlayer->getCurrentPosition();
- }
-
- return (mPendingPosition == -1) ? 0 : mPendingPosition;
-}
-
-void QAndroidMediaPlayerControl::setPosition(qint64 position)
-{
- if (!mSeekable)
- return;
-
- const int seekPosition = (position > INT_MAX) ? INT_MAX : position;
-
- if (seekPosition == this->position())
- return;
-
- StateChangeNotifier notifier(this);
-
- if (mCurrentMediaStatus == QMediaPlayer::EndOfMedia)
- setMediaStatus(QMediaPlayer::LoadedMedia);
-
- if ((mState & (AndroidMediaPlayer::Prepared
- | AndroidMediaPlayer::Started
- | AndroidMediaPlayer::Paused
- | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
- mPendingPosition = seekPosition;
- } else {
- mMediaPlayer->seekTo(seekPosition);
-
- if (mPendingPosition != -1) {
- mPendingPosition = -1;
- }
- }
-
- Q_EMIT positionChanged(seekPosition);
-}
-
-int QAndroidMediaPlayerControl::volume() const
-{
- return (mPendingVolume == -1) ? mMediaPlayer->volume() : mPendingVolume;
-}
-
-void QAndroidMediaPlayerControl::setVolume(int volume)
-{
- if ((mState & (AndroidMediaPlayer::Idle
- | AndroidMediaPlayer::Initialized
- | AndroidMediaPlayer::Stopped
- | AndroidMediaPlayer::Prepared
- | AndroidMediaPlayer::Started
- | AndroidMediaPlayer::Paused
- | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
- if (mPendingVolume != volume) {
- mPendingVolume = volume;
- Q_EMIT volumeChanged(volume);
- }
- return;
- }
-
- mMediaPlayer->setVolume(volume);
-
- if (mPendingVolume != -1) {
- mPendingVolume = -1;
- return;
- }
-
- Q_EMIT volumeChanged(volume);
-}
-
-bool QAndroidMediaPlayerControl::isMuted() const
-{
- return (mPendingMute == -1) ? mMediaPlayer->isMuted() : (mPendingMute == 1);
-}
-
-void QAndroidMediaPlayerControl::setMuted(bool muted)
-{
- if ((mState & (AndroidMediaPlayer::Idle
- | AndroidMediaPlayer::Initialized
- | AndroidMediaPlayer::Stopped
- | AndroidMediaPlayer::Prepared
- | AndroidMediaPlayer::Started
- | AndroidMediaPlayer::Paused
- | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
- if (mPendingMute != muted) {
- mPendingMute = muted;
- Q_EMIT mutedChanged(muted);
- }
- return;
- }
-
- mMediaPlayer->setMuted(muted);
-
- if (mPendingMute != -1) {
- mPendingMute = -1;
- return;
- }
-
- Q_EMIT mutedChanged(muted);
-}
-
-void QAndroidMediaPlayerControl::setAudioRole(QAudio::Role role)
-{
- mMediaPlayer->setAudioRole(role);
-}
-
-void QAndroidMediaPlayerControl::setCustomAudioRole(const QString &role)
-{
- mMediaPlayer->setCustomAudioRole(role);
-}
-
-int QAndroidMediaPlayerControl::bufferStatus() const
-{
- return mBufferFilled ? 100 : 0;
-}
-
-bool QAndroidMediaPlayerControl::isAudioAvailable() const
-{
- return mAudioAvailable;
-}
-
-bool QAndroidMediaPlayerControl::isVideoAvailable() const
-{
- return mVideoAvailable;
-}
-
-bool QAndroidMediaPlayerControl::isSeekable() const
-{
- return mSeekable;
-}
-
-QMediaTimeRange QAndroidMediaPlayerControl::availablePlaybackRanges() const
-{
- return mAvailablePlaybackRange;
-}
-
-void QAndroidMediaPlayerControl::updateAvailablePlaybackRanges()
-{
- if (mBuffering) {
- const qint64 pos = position();
- const qint64 end = (duration() / 100) * mBufferPercent;
- mAvailablePlaybackRange.addInterval(pos, end);
- } else if (mSeekable) {
- mAvailablePlaybackRange = QMediaTimeRange(0, duration());
- } else {
- mAvailablePlaybackRange = QMediaTimeRange();
- }
-
- Q_EMIT availablePlaybackRangesChanged(mAvailablePlaybackRange);
-}
-
-qreal QAndroidMediaPlayerControl::playbackRate() const
-{
- if (mHasPendingPlaybackRate ||
- (mState & (AndroidMediaPlayer::Initialized
- | AndroidMediaPlayer::Prepared
- | AndroidMediaPlayer::Started
- | AndroidMediaPlayer::Paused
- | AndroidMediaPlayer::PlaybackCompleted
- | AndroidMediaPlayer::Error)) == 0) {
- return mPendingPlaybackRate;
- }
-
- return mMediaPlayer->playbackRate();
-}
-
-void QAndroidMediaPlayerControl::setPlaybackRate(qreal rate)
-{
- if ((mState & (AndroidMediaPlayer::Initialized
- | AndroidMediaPlayer::Prepared
- | AndroidMediaPlayer::Started
- | AndroidMediaPlayer::Paused
- | AndroidMediaPlayer::PlaybackCompleted
- | AndroidMediaPlayer::Error)) == 0) {
- if (mPendingPlaybackRate != rate) {
- mPendingPlaybackRate = 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) {
- Q_EMIT playbackRateChanged(rate);
- }
-}
-
-QMediaContent QAndroidMediaPlayerControl::media() const
-{
- return mMediaContent;
-}
-
-const QIODevice *QAndroidMediaPlayerControl::mediaStream() const
-{
- return mMediaStream;
-}
-
-void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent,
- QIODevice *stream)
-{
- StateChangeNotifier notifier(this);
-
- mReloadingMedia = (mMediaContent == mediaContent) && !mPendingSetMedia;
-
- if (!mReloadingMedia) {
- mMediaContent = mediaContent;
- mMediaStream = stream;
- }
-
- // Release the mediaplayer if it's not in in Idle or Uninitialized state
- if ((mState & (AndroidMediaPlayer::Idle | AndroidMediaPlayer::Uninitialized)) == 0)
- mMediaPlayer->release();
-
- if (mediaContent.isNull()) {
- setMediaStatus(QMediaPlayer::NoMedia);
- } else {
- if (mVideoOutput && !mVideoOutput->isReady()) {
- // if a video output is set but the video texture is not ready, delay loading the media
- // since it can cause problems on some hardware
- mPendingSetMedia = true;
- return;
- }
-
- if (mVideoSize.isValid() && mVideoOutput)
- mVideoOutput->setVideoSize(mVideoSize);
-
- if ((mMediaPlayer->display() == 0) && mVideoOutput)
- mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
- mMediaPlayer->setDataSource(mediaContent.request());
- mMediaPlayer->prepareAsync();
- }
-
- if (!mReloadingMedia)
- Q_EMIT mediaChanged(mMediaContent);
-
- resetBufferingProgress();
-
- mReloadingMedia = false;
-}
-
-void QAndroidMediaPlayerControl::setVideoOutput(QAndroidVideoOutput *videoOutput)
-{
- if (mVideoOutput) {
- mMediaPlayer->setDisplay(0);
- mVideoOutput->stop();
- mVideoOutput->reset();
- }
-
- mVideoOutput = videoOutput;
-
- if (!mVideoOutput)
- return;
-
- if (mVideoOutput->isReady())
- mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
-
- connect(videoOutput, SIGNAL(readyChanged(bool)), this, SLOT(onVideoOutputReady(bool)));
-}
-
-void QAndroidMediaPlayerControl::play()
-{
- StateChangeNotifier notifier(this);
-
- // We need to prepare the mediaplayer again.
- if ((mState & AndroidMediaPlayer::Stopped) && !mMediaContent.isNull()) {
- setMedia(mMediaContent, mMediaStream);
- }
-
- if (!mMediaContent.isNull())
- setState(QMediaPlayer::PlayingState);
-
- if ((mState & (AndroidMediaPlayer::Prepared
- | AndroidMediaPlayer::Started
- | AndroidMediaPlayer::Paused
- | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
- mPendingState = QMediaPlayer::PlayingState;
- return;
- }
-
- mMediaPlayer->play();
-}
-
-void QAndroidMediaPlayerControl::pause()
-{
- StateChangeNotifier notifier(this);
-
- setState(QMediaPlayer::PausedState);
-
- if ((mState & (AndroidMediaPlayer::Started
- | AndroidMediaPlayer::Paused
- | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
- mPendingState = QMediaPlayer::PausedState;
- return;
- }
-
- mMediaPlayer->pause();
-}
-
-void QAndroidMediaPlayerControl::stop()
-{
- StateChangeNotifier notifier(this);
-
- setState(QMediaPlayer::StoppedState);
-
- if ((mState & (AndroidMediaPlayer::Prepared
- | AndroidMediaPlayer::Started
- | AndroidMediaPlayer::Stopped
- | AndroidMediaPlayer::Paused
- | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
- if ((mState & (AndroidMediaPlayer::Idle | AndroidMediaPlayer::Uninitialized | AndroidMediaPlayer::Error)) == 0)
- mPendingState = QMediaPlayer::StoppedState;
- return;
- }
-
- mMediaPlayer->stop();
-}
-
-void QAndroidMediaPlayerControl::onInfo(qint32 what, qint32 extra)
-{
- StateChangeNotifier notifier(this);
-
- Q_UNUSED(extra);
- switch (what) {
- case AndroidMediaPlayer::MEDIA_INFO_UNKNOWN:
- break;
- case AndroidMediaPlayer::MEDIA_INFO_VIDEO_TRACK_LAGGING:
- // IGNORE
- break;
- case AndroidMediaPlayer::MEDIA_INFO_VIDEO_RENDERING_START:
- break;
- case AndroidMediaPlayer::MEDIA_INFO_BUFFERING_START:
- mPendingState = mCurrentState;
- setState(QMediaPlayer::PausedState);
- setMediaStatus(QMediaPlayer::StalledMedia);
- break;
- case AndroidMediaPlayer::MEDIA_INFO_BUFFERING_END:
- if (mCurrentState != QMediaPlayer::StoppedState)
- flushPendingStates();
- break;
- case AndroidMediaPlayer::MEDIA_INFO_BAD_INTERLEAVING:
- break;
- case AndroidMediaPlayer::MEDIA_INFO_NOT_SEEKABLE:
- setSeekable(false);
- break;
- case AndroidMediaPlayer::MEDIA_INFO_METADATA_UPDATE:
- Q_EMIT metaDataUpdated();
- break;
- }
-}
-
-void QAndroidMediaPlayerControl::onError(qint32 what, qint32 extra)
-{
- StateChangeNotifier notifier(this);
-
- QString errorString;
- QMediaPlayer::Error error = QMediaPlayer::ResourceError;
-
- switch (what) {
- case AndroidMediaPlayer::MEDIA_ERROR_UNKNOWN:
- errorString = QLatin1String("Error:");
- break;
- case AndroidMediaPlayer::MEDIA_ERROR_SERVER_DIED:
- errorString = QLatin1String("Error: Server died");
- error = QMediaPlayer::ServiceMissingError;
- break;
- case AndroidMediaPlayer::MEDIA_ERROR_INVALID_STATE:
- errorString = QLatin1String("Error: Invalid state");
- error = QMediaPlayer::ServiceMissingError;
- break;
- }
-
- switch (extra) {
- case AndroidMediaPlayer::MEDIA_ERROR_IO: // Network OR file error
- errorString += QLatin1String(" (I/O operation failed)");
- error = QMediaPlayer::NetworkError;
- setMediaStatus(QMediaPlayer::InvalidMedia);
- break;
- case AndroidMediaPlayer::MEDIA_ERROR_MALFORMED:
- errorString += QLatin1String(" (Malformed bitstream)");
- error = QMediaPlayer::FormatError;
- setMediaStatus(QMediaPlayer::InvalidMedia);
- break;
- case AndroidMediaPlayer::MEDIA_ERROR_UNSUPPORTED:
- errorString += QLatin1String(" (Unsupported media)");
- error = QMediaPlayer::FormatError;
- setMediaStatus(QMediaPlayer::InvalidMedia);
- break;
- case AndroidMediaPlayer::MEDIA_ERROR_TIMED_OUT:
- errorString += QLatin1String(" (Timed out)");
- break;
- case AndroidMediaPlayer::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
- errorString += QLatin1String(" (Unable to start progressive playback')");
- error = QMediaPlayer::FormatError;
- setMediaStatus(QMediaPlayer::InvalidMedia);
- break;
- case AndroidMediaPlayer::MEDIA_ERROR_BAD_THINGS_ARE_GOING_TO_HAPPEN:
- errorString += QLatin1String(" (Unknown error/Insufficient resources)");
- error = QMediaPlayer::ServiceMissingError;
- break;
- }
-
- Q_EMIT QMediaPlayerControl::error(error, errorString);
-}
-
-void QAndroidMediaPlayerControl::onBufferingChanged(qint32 percent)
-{
- StateChangeNotifier notifier(this);
-
- mBuffering = percent != 100;
- mBufferPercent = percent;
-
- updateAvailablePlaybackRanges();
-
- if (mCurrentState != QMediaPlayer::StoppedState)
- setMediaStatus(mBuffering ? QMediaPlayer::BufferingMedia : QMediaPlayer::BufferedMedia);
-}
-
-void QAndroidMediaPlayerControl::onVideoSizeChanged(qint32 width, qint32 height)
-{
- QSize newSize(width, height);
-
- if (width == 0 || height == 0 || newSize == mVideoSize)
- return;
-
- setVideoAvailable(true);
- mVideoSize = newSize;
-
- if (mVideoOutput)
- mVideoOutput->setVideoSize(mVideoSize);
-}
-
-void QAndroidMediaPlayerControl::onStateChanged(qint32 state)
-{
- // If reloading, don't report state changes unless the new state is Prepared or Error.
- if ((mState & AndroidMediaPlayer::Stopped)
- && (state & (AndroidMediaPlayer::Prepared | AndroidMediaPlayer::Error | AndroidMediaPlayer::Uninitialized)) == 0) {
- return;
- }
-
- StateChangeNotifier notifier(this);
-
- mState = state;
- switch (mState) {
- case AndroidMediaPlayer::Idle:
- break;
- case AndroidMediaPlayer::Initialized:
- break;
- case AndroidMediaPlayer::Preparing:
- if (!mReloadingMedia)
- setMediaStatus(QMediaPlayer::LoadingMedia);
- break;
- case AndroidMediaPlayer::Prepared:
- setMediaStatus(QMediaPlayer::LoadedMedia);
- if (mBuffering) {
- setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
- : QMediaPlayer::BufferingMedia);
- } else {
- onBufferingChanged(100);
- }
- Q_EMIT metaDataUpdated();
- setAudioAvailable(true);
- flushPendingStates();
- break;
- case AndroidMediaPlayer::Started:
- setState(QMediaPlayer::PlayingState);
- if (mBuffering) {
- setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
- : QMediaPlayer::BufferingMedia);
- } else {
- setMediaStatus(QMediaPlayer::BufferedMedia);
- }
- Q_EMIT positionChanged(position());
- break;
- case AndroidMediaPlayer::Paused:
- setState(QMediaPlayer::PausedState);
- break;
- case AndroidMediaPlayer::Error:
- setState(QMediaPlayer::StoppedState);
- setMediaStatus(QMediaPlayer::UnknownMediaStatus);
- mMediaPlayer->release();
- Q_EMIT positionChanged(0);
- break;
- case AndroidMediaPlayer::Stopped:
- setState(QMediaPlayer::StoppedState);
- setMediaStatus(QMediaPlayer::LoadedMedia);
- Q_EMIT positionChanged(0);
- break;
- case AndroidMediaPlayer::PlaybackCompleted:
- setState(QMediaPlayer::StoppedState);
- setMediaStatus(QMediaPlayer::EndOfMedia);
- break;
- case AndroidMediaPlayer::Uninitialized:
- // reset some properties (unless we reload the same media)
- if (!mReloadingMedia) {
- resetBufferingProgress();
- mPendingPosition = -1;
- mPendingSetMedia = false;
- mPendingState = -1;
-
- Q_EMIT durationChanged(0);
- Q_EMIT positionChanged(0);
-
- setAudioAvailable(false);
- setVideoAvailable(false);
- setSeekable(true);
- }
- break;
- default:
- break;
- }
-
- if ((mState & (AndroidMediaPlayer::Stopped | AndroidMediaPlayer::Uninitialized)) != 0) {
- mMediaPlayer->setDisplay(0);
- if (mVideoOutput) {
- mVideoOutput->stop();
- mVideoOutput->reset();
- }
- }
-}
-
-void QAndroidMediaPlayerControl::onVideoOutputReady(bool ready)
-{
- if ((mMediaPlayer->display() == 0) && mVideoOutput && ready)
- mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
-
- flushPendingStates();
-}
-
-void QAndroidMediaPlayerControl::setState(QMediaPlayer::State state)
-{
- if (mCurrentState == state)
- return;
-
- if (mCurrentState == QMediaPlayer::StoppedState && state == QMediaPlayer::PausedState)
- return;
-
- mCurrentState = state;
-}
-
-void QAndroidMediaPlayerControl::setMediaStatus(QMediaPlayer::MediaStatus status)
-{
- if (mCurrentMediaStatus == status)
- return;
-
- mCurrentMediaStatus = status;
-
- if (status == QMediaPlayer::NoMedia || status == QMediaPlayer::InvalidMedia)
- Q_EMIT durationChanged(0);
-
- if (status == QMediaPlayer::EndOfMedia)
- Q_EMIT positionChanged(position());
-
- updateBufferStatus();
-}
-
-void QAndroidMediaPlayerControl::setSeekable(bool seekable)
-{
- if (mSeekable == seekable)
- return;
-
- mSeekable = seekable;
- Q_EMIT seekableChanged(mSeekable);
-}
-
-void QAndroidMediaPlayerControl::setAudioAvailable(bool available)
-{
- if (mAudioAvailable == available)
- return;
-
- mAudioAvailable = available;
- Q_EMIT audioAvailableChanged(mAudioAvailable);
-}
-
-void QAndroidMediaPlayerControl::setVideoAvailable(bool available)
-{
- if (mVideoAvailable == available)
- return;
-
- if (!available)
- mVideoSize = QSize();
-
- mVideoAvailable = available;
- Q_EMIT videoAvailableChanged(mVideoAvailable);
-}
-
-void QAndroidMediaPlayerControl::resetBufferingProgress()
-{
- mBuffering = false;
- mBufferPercent = 0;
- mAvailablePlaybackRange = QMediaTimeRange();
-}
-
-void QAndroidMediaPlayerControl::flushPendingStates()
-{
- if (mPendingSetMedia) {
- setMedia(mMediaContent, 0);
- mPendingSetMedia = false;
- return;
- }
-
- const int newState = mPendingState;
- mPendingState = -1;
-
- if (mPendingPosition != -1)
- setPosition(mPendingPosition);
- if (mPendingVolume != -1)
- setVolume(mPendingVolume);
- if (mPendingMute != -1)
- setMuted((mPendingMute == 1));
- if (mHasPendingPlaybackRate)
- setPlaybackRate(mPendingPlaybackRate);
-
- switch (newState) {
- case QMediaPlayer::PlayingState:
- play();
- break;
- case QMediaPlayer::PausedState:
- pause();
- break;
- case QMediaPlayer::StoppedState:
- stop();
- break;
- default:
- break;
- }
-}
-
-void QAndroidMediaPlayerControl::updateBufferStatus()
-{
- bool bufferFilled = (mCurrentMediaStatus == QMediaPlayer::BufferedMedia
- || mCurrentMediaStatus == QMediaPlayer::BufferingMedia);
-
- if (mBufferFilled != bufferFilled) {
- mBufferFilled = bufferFilled;
- Q_EMIT bufferStatusChanged(bufferStatus());
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h
deleted file mode 100644
index 35f56145f..000000000
--- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h
+++ /dev/null
@@ -1,140 +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 QANDROIDMEDIAPLAYERCONTROL_H
-#define QANDROIDMEDIAPLAYERCONTROL_H
-
-#include <qglobal.h>
-#include <QMediaPlayerControl>
-#include <qsize.h>
-
-QT_BEGIN_NAMESPACE
-
-class AndroidMediaPlayer;
-class QAndroidVideoOutput;
-
-class QAndroidMediaPlayerControl : public QMediaPlayerControl
-{
- Q_OBJECT
-public:
- explicit QAndroidMediaPlayerControl(QObject *parent = 0);
- ~QAndroidMediaPlayerControl() override;
-
- QMediaPlayer::State state() const override;
- QMediaPlayer::MediaStatus mediaStatus() const override;
- qint64 duration() const override;
- qint64 position() const override;
- int volume() const override;
- bool isMuted() const override;
- int bufferStatus() const override;
- bool isAudioAvailable() const override;
- bool isVideoAvailable() const override;
- bool isSeekable() const override;
- QMediaTimeRange availablePlaybackRanges() const override;
- qreal playbackRate() const override;
- void setPlaybackRate(qreal rate) override;
- QMediaContent media() const override;
- const QIODevice *mediaStream() const override;
- void setMedia(const QMediaContent &mediaContent, QIODevice *stream) override;
-
- void setVideoOutput(QAndroidVideoOutput *videoOutput);
-
-Q_SIGNALS:
- void metaDataUpdated();
-
-public Q_SLOTS:
- void setPosition(qint64 position) override;
- void play() override;
- void pause() override;
- void stop() override;
- void setVolume(int volume) override;
- void setMuted(bool muted) override;
- void setAudioRole(QAudio::Role role);
- void setCustomAudioRole(const QString &role);
-
-private Q_SLOTS:
- void onVideoOutputReady(bool ready);
- void onError(qint32 what, qint32 extra);
- void onInfo(qint32 what, qint32 extra);
- void onBufferingChanged(qint32 percent);
- void onVideoSizeChanged(qint32 width, qint32 height);
- void onStateChanged(qint32 state);
-
-private:
- AndroidMediaPlayer *mMediaPlayer;
- QMediaPlayer::State mCurrentState;
- QMediaPlayer::MediaStatus mCurrentMediaStatus;
- QMediaContent mMediaContent;
- QIODevice *mMediaStream;
- QAndroidVideoOutput *mVideoOutput;
- bool mSeekable;
- int mBufferPercent;
- bool mBufferFilled;
- bool mAudioAvailable;
- bool mVideoAvailable;
- QSize mVideoSize;
- bool mBuffering;
- QMediaTimeRange mAvailablePlaybackRange;
- int mState;
- int mPendingState;
- qint64 mPendingPosition;
- bool mPendingSetMedia;
- int mPendingVolume;
- int mPendingMute;
- bool mReloadingMedia;
- int mActiveStateChangeNotifiers;
- qreal mPendingPlaybackRate;
- bool mHasPendingPlaybackRate; // we need this because the rate can theoretically be negative
-
- void setState(QMediaPlayer::State state);
- void setMediaStatus(QMediaPlayer::MediaStatus status);
- void setSeekable(bool seekable);
- void setAudioAvailable(bool available);
- void setVideoAvailable(bool available);
- void updateAvailablePlaybackRanges();
- void resetBufferingProgress();
- void flushPendingStates();
- void updateBufferStatus();
-
- friend class StateChangeNotifier;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDMEDIAPLAYERCONTROL_H
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp
deleted file mode 100644
index 5252d60ad..000000000
--- a/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp
+++ /dev/null
@@ -1,76 +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 "qandroidmediaplayervideorenderercontrol.h"
-
-#include "qandroidmediaplayercontrol.h"
-#include "qandroidvideooutput.h"
-#include <qabstractvideosurface.h>
-
-QT_BEGIN_NAMESPACE
-
-QAndroidMediaPlayerVideoRendererControl::QAndroidMediaPlayerVideoRendererControl(QAndroidMediaPlayerControl *mediaPlayer, QObject *parent)
- : QVideoRendererControl(parent)
- , m_mediaPlayerControl(mediaPlayer)
- , m_surface(0)
- , m_textureOutput(new QAndroidTextureVideoOutput(this))
-{
- m_mediaPlayerControl->setVideoOutput(m_textureOutput);
-}
-
-QAndroidMediaPlayerVideoRendererControl::~QAndroidMediaPlayerVideoRendererControl()
-{
- m_mediaPlayerControl->setVideoOutput(0);
-}
-
-QAbstractVideoSurface *QAndroidMediaPlayerVideoRendererControl::surface() const
-{
- return m_surface;
-}
-
-void QAndroidMediaPlayerVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
-{
- if (m_surface == surface)
- return;
-
- m_surface = surface;
- m_textureOutput->setSurface(m_surface);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.h
deleted file mode 100644
index ef213cc57..000000000
--- a/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.h
+++ /dev/null
@@ -1,68 +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 QANDROIDMEDIAPLAYERVIDEORENDERERCONTROL_H
-#define QANDROIDMEDIAPLAYERVIDEORENDERERCONTROL_H
-
-#include <qvideorenderercontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidMediaPlayerControl;
-class QAndroidTextureVideoOutput;
-
-class QAndroidMediaPlayerVideoRendererControl : public QVideoRendererControl
-{
- Q_OBJECT
-public:
- QAndroidMediaPlayerVideoRendererControl(QAndroidMediaPlayerControl *mediaPlayer, QObject *parent = 0);
- ~QAndroidMediaPlayerVideoRendererControl() override;
-
- QAbstractVideoSurface *surface() const override;
- void setSurface(QAbstractVideoSurface *surface) override;
-
-private:
- QAndroidMediaPlayerControl *m_mediaPlayerControl;
- QAbstractVideoSurface *m_surface;
- QAndroidTextureVideoOutput *m_textureOutput;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDMEDIAPLAYERVIDEORENDERERCONTROL_H
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp
deleted file mode 100644
index c057f530a..000000000
--- a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp
+++ /dev/null
@@ -1,111 +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 "qandroidmediaservice.h"
-
-#include "qandroidmediaplayercontrol.h"
-#include "qandroidmetadatareadercontrol.h"
-#include "qandroidaudiorolecontrol.h"
-#include "qandroidcustomaudiorolecontrol.h"
-#include "qandroidmediaplayervideorenderercontrol.h"
-
-QT_BEGIN_NAMESPACE
-
-QAndroidMediaService::QAndroidMediaService(QObject *parent)
- : QMediaService(parent)
- , mAudioRoleControl(nullptr)
- , mCustomAudioRoleControl(nullptr)
- , mVideoRendererControl(0)
-{
- mMediaControl = new QAndroidMediaPlayerControl;
- mMetadataControl = new QAndroidMetaDataReaderControl;
- mAudioRoleControl = new QAndroidAudioRoleControl;
- mCustomAudioRoleControl = new QAndroidCustomAudioRoleControl;
- connect(mAudioRoleControl, &QAndroidAudioRoleControl::audioRoleChanged,
- mMediaControl, &QAndroidMediaPlayerControl::setAudioRole);
- connect(mCustomAudioRoleControl, &QAndroidCustomAudioRoleControl::customAudioRoleChanged,
- mMediaControl, &QAndroidMediaPlayerControl::setCustomAudioRole);
- connect(mMediaControl, SIGNAL(mediaChanged(QMediaContent)),
- mMetadataControl, SLOT(onMediaChanged(QMediaContent)));
- connect(mMediaControl, SIGNAL(metaDataUpdated()),
- mMetadataControl, SLOT(onUpdateMetaData()));
-}
-
-QAndroidMediaService::~QAndroidMediaService()
-{
- delete mVideoRendererControl;
- delete mCustomAudioRoleControl;
- delete mAudioRoleControl;
- delete mMetadataControl;
- delete mMediaControl;
-}
-
-QMediaControl *QAndroidMediaService::requestControl(const char *name)
-{
- if (qstrcmp(name, QMediaPlayerControl_iid) == 0)
- return mMediaControl;
-
- if (qstrcmp(name, QMetaDataReaderControl_iid) == 0)
- return mMetadataControl;
-
- if (qstrcmp(name, QAudioRoleControl_iid) == 0)
- return mAudioRoleControl;
-
- if (qstrcmp(name, QCustomAudioRoleControl_iid) == 0)
- return mCustomAudioRoleControl;
-
- if (qstrcmp(name, QVideoRendererControl_iid) == 0) {
- if (!mVideoRendererControl) {
- mVideoRendererControl = new QAndroidMediaPlayerVideoRendererControl(mMediaControl);
- return mVideoRendererControl;
- }
- }
-
- return 0;
-}
-
-void QAndroidMediaService::releaseControl(QMediaControl *control)
-{
- if (control == mVideoRendererControl) {
- delete mVideoRendererControl;
- mVideoRendererControl = 0;
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.h b/src/plugins/android/src/mediaplayer/qandroidmediaservice.h
deleted file mode 100644
index 788c11098..000000000
--- a/src/plugins/android/src/mediaplayer/qandroidmediaservice.h
+++ /dev/null
@@ -1,73 +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 QANDROIDMEDIASERVICE_H
-#define QANDROIDMEDIASERVICE_H
-
-#include <QMediaService>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidMediaPlayerControl;
-class QAndroidMetaDataReaderControl;
-class QAndroidAudioRoleControl;
-class QAndroidCustomAudioRoleControl;
-class QAndroidMediaPlayerVideoRendererControl;
-
-class QAndroidMediaService : public QMediaService
-{
- Q_OBJECT
-public:
- explicit QAndroidMediaService(QObject *parent = 0);
- ~QAndroidMediaService() override;
-
- QMediaControl* requestControl(const char *name) override;
- void releaseControl(QMediaControl *control) override;
-
-private:
- QAndroidMediaPlayerControl *mMediaControl;
- QAndroidMetaDataReaderControl *mMetadataControl;
- QAndroidAudioRoleControl *mAudioRoleControl;
- QAndroidCustomAudioRoleControl *mCustomAudioRoleControl;
- QAndroidMediaPlayerVideoRendererControl *mVideoRendererControl;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDMEDIASERVICE_H
diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp
deleted file mode 100644
index de7a3aab1..000000000
--- a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp
+++ /dev/null
@@ -1,248 +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 "qandroidmetadatareadercontrol.h"
-
-#include "androidmediametadataretriever.h"
-#include <QtMultimedia/qmediametadata.h>
-#include <qsize.h>
-#include <QDate>
-#include <QtCore/qlist.h>
-#include <QtConcurrent/qtconcurrentrun.h>
-
-QT_BEGIN_NAMESPACE
-
-// Genre name ordered by ID
-// see: http://id3.org/id3v2.3.0#Appendix_A_-_Genre_List_from_ID3v1
-static const char* qt_ID3GenreNames[] =
-{
- "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz",
- "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno",
- "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno",
- "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental",
- "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass", "Soul", "Punk",
- "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave",
- "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy",
- "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American",
- "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal",
- "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk",
- "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", "Celtic",
- "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock",
- "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour",
- "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
- "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad",
- "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", "A capella",
- "Euro-House", "Dance Hall"
-};
-
-typedef QList<QAndroidMetaDataReaderControl *> AndroidMetaDataReaders;
-Q_GLOBAL_STATIC(AndroidMetaDataReaders, g_metaDataReaders)
-Q_GLOBAL_STATIC(QMutex, g_metaDataReadersMtx)
-
-QAndroidMetaDataReaderControl::QAndroidMetaDataReaderControl(QObject *parent)
- : QMetaDataReaderControl(parent)
- , m_available(false)
-{
-}
-
-QAndroidMetaDataReaderControl::~QAndroidMetaDataReaderControl()
-{
- QMutexLocker l(g_metaDataReadersMtx);
- const int idx = g_metaDataReaders->indexOf(this);
- if (idx != -1)
- g_metaDataReaders->remove(idx);
-}
-
-bool QAndroidMetaDataReaderControl::isMetaDataAvailable() const
-{
- const QMutexLocker l(&m_mtx);
- return m_available && !m_metadata.isEmpty();
-}
-
-QVariant QAndroidMetaDataReaderControl::metaData(const QString &key) const
-{
- const QMutexLocker l(&m_mtx);
- return m_metadata.value(key);
-}
-
-QStringList QAndroidMetaDataReaderControl::availableMetaData() const
-{
- const QMutexLocker l(&m_mtx);
- return m_metadata.keys();
-}
-
-void QAndroidMetaDataReaderControl::onMediaChanged(const QMediaContent &media)
-{
- const QMutexLocker l(&m_mtx);
- m_metadata.clear();
- m_mediaContent = media;
-}
-
-void QAndroidMetaDataReaderControl::onUpdateMetaData()
-{
- {
- const QMutexLocker l(g_metaDataReadersMtx);
- if (!g_metaDataReaders->contains(this))
- g_metaDataReaders->append(this);
- }
-
- const QMutexLocker ml(&m_mtx);
- if (m_mediaContent.isNull())
- return;
-
- const QUrl &url = m_mediaContent.request().url();
- QtConcurrent::run(&extractMetadata, this, url);
-}
-
-void QAndroidMetaDataReaderControl::updateData(const QVariantMap &metadata, const QUrl &url)
-{
- const QMutexLocker l(&m_mtx);
-
- if (m_mediaContent.request().url() != url)
- return;
-
- const bool oldAvailable = m_available;
- m_metadata = metadata;
- m_available = !m_metadata.isEmpty();
-
- if (m_available != oldAvailable)
- Q_EMIT metaDataAvailableChanged(m_available);
-
- Q_EMIT metaDataChanged();
-}
-
-void QAndroidMetaDataReaderControl::extractMetadata(QAndroidMetaDataReaderControl *caller,
- const QUrl &url)
-{
- QVariantMap metadata;
-
- if (!url.isEmpty()) {
- AndroidMediaMetadataRetriever retriever;
- if (!retriever.setDataSource(url))
- return;
-
- QString mimeType = retriever.extractMetadata(AndroidMediaMetadataRetriever::MimeType);
- if (!mimeType.isNull())
- metadata.insert(QMediaMetaData::MediaType, mimeType);
-
- bool isVideo = !retriever.extractMetadata(AndroidMediaMetadataRetriever::HasVideo).isNull()
- || mimeType.startsWith(QStringLiteral("video"));
-
- QString string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Album);
- if (!string.isNull())
- metadata.insert(QMediaMetaData::AlbumTitle, string);
-
- string = retriever.extractMetadata(AndroidMediaMetadataRetriever::AlbumArtist);
- if (!string.isNull())
- metadata.insert(QMediaMetaData::AlbumArtist, string);
-
- string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Artist);
- if (!string.isNull()) {
- metadata.insert(isVideo ? QMediaMetaData::LeadPerformer
- : QMediaMetaData::ContributingArtist,
- string.split('/', Qt::SkipEmptyParts));
- }
-
- string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Author);
- if (!string.isNull())
- metadata.insert(QMediaMetaData::Author, string.split('/', Qt::SkipEmptyParts));
-
- string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Bitrate);
- if (!string.isNull()) {
- metadata.insert(isVideo ? QMediaMetaData::VideoBitRate
- : QMediaMetaData::AudioBitRate,
- string.toInt());
- }
-
- string = retriever.extractMetadata(AndroidMediaMetadataRetriever::CDTrackNumber);
- if (!string.isNull())
- metadata.insert(QMediaMetaData::TrackNumber, string.toInt());
-
- string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Composer);
- if (!string.isNull())
- metadata.insert(QMediaMetaData::Composer, string.split('/', Qt::SkipEmptyParts));
-
- string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Date);
- if (!string.isNull())
- metadata.insert(QMediaMetaData::Date, QDateTime::fromString(string, QStringLiteral("yyyyMMddTHHmmss.zzzZ")).date());
-
- string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Duration);
- if (!string.isNull())
- metadata.insert(QMediaMetaData::Duration, string.toLongLong());
-
- string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Genre);
- if (!string.isNull()) {
- // The genre can be returned as an ID3v2 id, get the name for it in that case
- if (string.startsWith('(') && string.endsWith(')')) {
- bool ok = false;
- const int genreId = QStringView{string}.mid(1, string.length() - 2).toInt(&ok);
- if (ok && genreId >= 0 && genreId <= 125)
- string = QLatin1String(qt_ID3GenreNames[genreId]);
- }
- metadata.insert(QMediaMetaData::Genre, string);
- }
-
- string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Title);
- if (!string.isNull())
- metadata.insert(QMediaMetaData::Title, string);
-
- string = retriever.extractMetadata(AndroidMediaMetadataRetriever::VideoHeight);
- if (!string.isNull()) {
- const int height = string.toInt();
- const int width = retriever.extractMetadata(AndroidMediaMetadataRetriever::VideoWidth).toInt();
- metadata.insert(QMediaMetaData::Resolution, QSize(width, height));
- }
-
- string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Writer);
- if (!string.isNull())
- metadata.insert(QMediaMetaData::Writer, string.split('/', Qt::SkipEmptyParts));
-
- string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Year);
- if (!string.isNull())
- metadata.insert(QMediaMetaData::Year, string.toInt());
- }
-
- const QMutexLocker lock(g_metaDataReadersMtx);
- if (!g_metaDataReaders->contains(caller))
- return;
-
- caller->updateData(metadata, url);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h
deleted file mode 100644
index f2b1b47f1..000000000
--- a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h
+++ /dev/null
@@ -1,79 +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 QANDROIDMETADATAREADERCONTROL_H
-#define QANDROIDMETADATAREADERCONTROL_H
-
-#include <QMetaDataReaderControl>
-#include <qmediacontent.h>
-#include <QMutex>
-
-QT_BEGIN_NAMESPACE
-
-class AndroidMediaMetadataRetriever;
-
-class QAndroidMetaDataReaderControl : public QMetaDataReaderControl
-{
- Q_OBJECT
-public:
- explicit QAndroidMetaDataReaderControl(QObject *parent = 0);
- ~QAndroidMetaDataReaderControl() override;
-
- bool isMetaDataAvailable() const override;
-
- QVariant metaData(const QString &key) const override;
- QStringList availableMetaData() const override;
-
-public Q_SLOTS:
- void onMediaChanged(const QMediaContent &media);
- void onUpdateMetaData();
-
-private:
- void updateData(const QVariantMap &metadata, const QUrl &url);
- static void extractMetadata(QAndroidMetaDataReaderControl *caller, const QUrl &url);
-
- mutable QMutex m_mtx;
- QMediaContent m_mediaContent;
- bool m_available;
- QVariantMap m_metadata;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDMETADATAREADERCONTROL_H
diff --git a/src/plugins/android/src/qandroidmediaserviceplugin.cpp b/src/plugins/android/src/qandroidmediaserviceplugin.cpp
deleted file mode 100644
index a66b85f98..000000000
--- a/src/plugins/android/src/qandroidmediaserviceplugin.cpp
+++ /dev/null
@@ -1,184 +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 "qandroidmediaserviceplugin.h"
-
-#include "qandroidmediaservice.h"
-#include "qandroidcaptureservice.h"
-#include "qandroidaudioinputselectorcontrol.h"
-#include "qandroidcamerainfocontrol.h"
-#include "qandroidcamerasession.h"
-#include "androidmediaplayer.h"
-#include "androidsurfacetexture.h"
-#include "androidcamera.h"
-#include "androidmultimediautils.h"
-#include "androidmediarecorder.h"
-#include "androidsurfaceview.h"
-#include "qandroidglobal.h"
-
-QT_BEGIN_NAMESPACE
-
-Q_LOGGING_CATEGORY(qtAndroidMediaPlugin, "qt.multimedia.plugins.android")
-
-QAndroidMediaServicePlugin::QAndroidMediaServicePlugin()
-{
-}
-
-QAndroidMediaServicePlugin::~QAndroidMediaServicePlugin()
-{
-}
-
-QMediaService *QAndroidMediaServicePlugin::create(const QString &key)
-{
- if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER))
- return new QAndroidMediaService;
-
- if (key == QLatin1String(Q_MEDIASERVICE_CAMERA)
- || key == QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE)) {
- return new QAndroidCaptureService(key);
- }
-
- qCWarning(qtAndroidMediaPlugin) << "Android service plugin: unsupported key:" << key;
- return 0;
-}
-
-void QAndroidMediaServicePlugin::release(QMediaService *service)
-{
- delete service;
-}
-
-QMediaServiceProviderHint::Features QAndroidMediaServicePlugin::supportedFeatures(const QByteArray &service) const
-{
- if (service == Q_MEDIASERVICE_MEDIAPLAYER)
- return QMediaServiceProviderHint::VideoSurface;
-
- if (service == Q_MEDIASERVICE_CAMERA)
- return QMediaServiceProviderHint::VideoSurface | QMediaServiceProviderHint::RecordingSupport;
-
- if (service == Q_MEDIASERVICE_AUDIOSOURCE)
- return QMediaServiceProviderHint::RecordingSupport;
-
- return QMediaServiceProviderHint::Features();
-}
-
-QByteArray QAndroidMediaServicePlugin::defaultDevice(const QByteArray &service) const
-{
- if (service == Q_MEDIASERVICE_CAMERA && !QAndroidCameraSession::availableCameras().isEmpty())
- return QAndroidCameraSession::availableCameras().first().name;
-
- return QByteArray();
-}
-
-QList<QByteArray> QAndroidMediaServicePlugin::devices(const QByteArray &service) const
-{
- if (service == Q_MEDIASERVICE_CAMERA) {
- QList<QByteArray> devices;
- const QList<AndroidCameraInfo> &cameras = QAndroidCameraSession::availableCameras();
- for (int i = 0; i < cameras.count(); ++i)
- devices.append(cameras.at(i).name);
- return devices;
- }
-
- if (service == Q_MEDIASERVICE_AUDIOSOURCE)
- return QAndroidAudioInputSelectorControl::availableDevices();
-
- return QList<QByteArray>();
-}
-
-QString QAndroidMediaServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device)
-{
- if (service == Q_MEDIASERVICE_CAMERA) {
- const QList<AndroidCameraInfo> &cameras = QAndroidCameraSession::availableCameras();
- for (int i = 0; i < cameras.count(); ++i) {
- const AndroidCameraInfo &info = cameras.at(i);
- if (info.name == device)
- return info.description;
- }
- }
-
- if (service == Q_MEDIASERVICE_AUDIOSOURCE)
- return QAndroidAudioInputSelectorControl::availableDeviceDescription(device);
-
- return QString();
-}
-
-QCamera::Position QAndroidMediaServicePlugin::cameraPosition(const QByteArray &device) const
-{
- return QAndroidCameraInfoControl::position(device);
-}
-
-int QAndroidMediaServicePlugin::cameraOrientation(const QByteArray &device) const
-{
- return QAndroidCameraInfoControl::orientation(device);
-}
-
-QT_END_NAMESPACE
-
-Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/)
-{
- static bool initialized = false;
- if (initialized)
- return JNI_VERSION_1_6;
- initialized = true;
-
- QT_USE_NAMESPACE
- typedef union {
- JNIEnv *nativeEnvironment;
- void *venv;
- } UnionJNIEnvToVoid;
-
- UnionJNIEnvToVoid uenv;
- uenv.venv = NULL;
-
- if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK)
- return JNI_ERR;
-
- JNIEnv *jniEnv = uenv.nativeEnvironment;
-
- if (!AndroidMediaPlayer::initJNI(jniEnv) ||
- !AndroidCamera::initJNI(jniEnv) ||
- !AndroidMediaRecorder::initJNI(jniEnv) ||
- !AndroidSurfaceHolder::initJNI(jniEnv)) {
- return JNI_ERR;
- }
-
- AndroidSurfaceTexture::initJNI(jniEnv);
-
- return JNI_VERSION_1_6;
-}
diff --git a/src/plugins/android/src/qandroidmediaserviceplugin.h b/src/plugins/android/src/qandroidmediaserviceplugin.h
deleted file mode 100644
index 3ae1a36b6..000000000
--- a/src/plugins/android/src/qandroidmediaserviceplugin.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 QANDROIDMEDIASERVICEPLUGIN_H
-#define QANDROIDMEDIASERVICEPLUGIN_H
-
-#include <QMediaServiceProviderPlugin>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidMediaServicePlugin
- : public QMediaServiceProviderPlugin
- , public QMediaServiceSupportedDevicesInterface
- , public QMediaServiceDefaultDeviceInterface
- , public QMediaServiceCameraInfoInterface
- , public QMediaServiceFeaturesInterface
-{
- Q_OBJECT
- Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
- Q_INTERFACES(QMediaServiceDefaultDeviceInterface)
- Q_INTERFACES(QMediaServiceCameraInfoInterface)
- Q_INTERFACES(QMediaServiceFeaturesInterface)
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0"
- FILE "android_mediaservice.json")
-
-public:
- QAndroidMediaServicePlugin();
- ~QAndroidMediaServicePlugin();
-
- QMediaService* create(QString const& key) override;
- void release(QMediaService *service) override;
-
- QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const override;
-
- QByteArray defaultDevice(const QByteArray &service) const override;
- QList<QByteArray> devices(const QByteArray &service) const override;
- QString deviceDescription(const QByteArray &service, const QByteArray &device) override;
-
- QCamera::Position cameraPosition(const QByteArray &device) const override;
- int cameraOrientation(const QByteArray &device) const override;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDMEDIASERVICEPLUGIN_H
diff --git a/src/plugins/android/src/src.pro b/src/plugins/android/src/src.pro
deleted file mode 100644
index 5e47a7d09..000000000
--- a/src/plugins/android/src/src.pro
+++ /dev/null
@@ -1,20 +0,0 @@
-TARGET = qtmedia_android
-
-QT += opengl multimedia-private core-private network
-
-HEADERS += \
- qandroidmediaserviceplugin.h
-
-SOURCES += \
- qandroidmediaserviceplugin.cpp
-
-include (wrappers/jni/jni.pri)
-include (common/common.pri)
-include (mediaplayer/mediaplayer.pri)
-include (mediacapture/mediacapture.pri)
-
-OTHER_FILES += android_mediaservice.json
-
-PLUGIN_TYPE = mediaservice
-PLUGIN_CLASS_NAME = QAndroidMediaServicePlugin
-load(qt_plugin)
diff --git a/src/plugins/android/src/wrappers/jni/androidcamera.cpp b/src/plugins/android/src/wrappers/jni/androidcamera.cpp
deleted file mode 100644
index 33e819d78..000000000
--- a/src/plugins/android/src/wrappers/jni/androidcamera.cpp
+++ /dev/null
@@ -1,1712 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "androidcamera.h"
-#include "androidsurfacetexture.h"
-#include "androidsurfaceview.h"
-#include "qandroidmultimediautils.h"
-#include "qandroidglobal.h"
-
-#include <qstringlist.h>
-#include <qdebug.h>
-#include <QtCore/private/qjnihelpers_p.h>
-#include <QtCore/qthread.h>
-#include <QtCore/qreadwritelock.h>
-#include <QtCore/qmutex.h>
-#include <QtMultimedia/private/qmemoryvideobuffer_p.h>
-
-#include <mutex>
-
-QT_BEGIN_NAMESPACE
-
-static const char QtCameraListenerClassName[] = "org/qtproject/qt/android/multimedia/QtCameraListener";
-
-typedef QHash<int, AndroidCamera *> CameraMap;
-Q_GLOBAL_STATIC(CameraMap, cameras)
-Q_GLOBAL_STATIC(QReadWriteLock, rwLock)
-
-static inline bool exceptionCheckAndClear(JNIEnv *env)
-{
- if (Q_UNLIKELY(env->ExceptionCheck())) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif // QT_DEBUG
- env->ExceptionClear();
- return true;
- }
-
- return false;
-}
-
-static QRect areaToRect(jobject areaObj)
-{
- QJNIObjectPrivate area(areaObj);
- QJNIObjectPrivate rect = area.getObjectField("rect", "Landroid/graphics/Rect;");
-
- return QRect(rect.getField<jint>("left"),
- rect.getField<jint>("top"),
- rect.callMethod<jint>("width"),
- rect.callMethod<jint>("height"));
-}
-
-static QJNIObjectPrivate rectToArea(const QRect &rect)
-{
- QJNIObjectPrivate jrect("android/graphics/Rect",
- "(IIII)V",
- rect.left(), rect.top(), rect.right(), rect.bottom());
-
- QJNIObjectPrivate area("android/hardware/Camera$Area",
- "(Landroid/graphics/Rect;I)V",
- jrect.object(), 500);
-
- return area;
-}
-
-// native method for QtCameraLisener.java
-static void notifyAutoFocusComplete(JNIEnv* , jobject, int id, jboolean success)
-{
- QReadLocker locker(rwLock);
- const auto it = cameras->constFind(id);
- if (Q_UNLIKELY(it == cameras->cend()))
- return;
-
- Q_EMIT (*it)->autoFocusComplete(success);
-}
-
-static void notifyPictureExposed(JNIEnv* , jobject, int id)
-{
- QReadLocker locker(rwLock);
- const auto it = cameras->constFind(id);
- if (Q_UNLIKELY(it == cameras->cend()))
- return;
-
- Q_EMIT (*it)->pictureExposed();
-}
-
-static void notifyPictureCaptured(JNIEnv *env, jobject, int id, jbyteArray data)
-{
- QReadLocker locker(rwLock);
- const auto it = cameras->constFind(id);
- if (Q_UNLIKELY(it == cameras->cend()))
- return;
-
- const int arrayLength = env->GetArrayLength(data);
- QByteArray bytes(arrayLength, Qt::Uninitialized);
- env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data());
- Q_EMIT (*it)->pictureCaptured(bytes);
-}
-
-static void notifyNewPreviewFrame(JNIEnv *env, jobject, int id, jbyteArray data,
- int width, int height, int format, int bpl)
-{
- QReadLocker locker(rwLock);
- const auto it = cameras->constFind(id);
- if (Q_UNLIKELY(it == cameras->cend()))
- return;
-
- const int arrayLength = env->GetArrayLength(data);
- if (arrayLength == 0)
- return;
-
- QByteArray bytes(arrayLength, Qt::Uninitialized);
- env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data());
-
- QVideoFrame frame(new QMemoryVideoBuffer(bytes, bpl),
- QSize(width, height),
- qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat(format)));
-
- Q_EMIT (*it)->newPreviewFrame(frame);
-}
-
-static void notifyFrameAvailable(JNIEnv *, jobject, int id)
-{
- QReadLocker locker(rwLock);
- const auto it = cameras->constFind(id);
- if (Q_UNLIKELY(it == cameras->cend()))
- return;
-
- (*it)->fetchLastPreviewFrame();
-}
-
-class AndroidCameraPrivate : public QObject
-{
- Q_OBJECT
-public:
- AndroidCameraPrivate();
- ~AndroidCameraPrivate();
-
- Q_INVOKABLE bool init(int cameraId);
-
- Q_INVOKABLE void release();
- Q_INVOKABLE bool lock();
- Q_INVOKABLE bool unlock();
- Q_INVOKABLE bool reconnect();
-
- Q_INVOKABLE AndroidCamera::CameraFacing getFacing();
- Q_INVOKABLE int getNativeOrientation();
-
- Q_INVOKABLE QSize getPreferredPreviewSizeForVideo();
- Q_INVOKABLE QList<QSize> getSupportedPreviewSizes();
-
- Q_INVOKABLE QList<AndroidCamera::FpsRange> getSupportedPreviewFpsRange();
-
- Q_INVOKABLE AndroidCamera::FpsRange getPreviewFpsRange();
- Q_INVOKABLE void setPreviewFpsRange(int min, int max);
-
- Q_INVOKABLE AndroidCamera::ImageFormat getPreviewFormat();
- Q_INVOKABLE void setPreviewFormat(AndroidCamera::ImageFormat fmt);
- Q_INVOKABLE QList<AndroidCamera::ImageFormat> getSupportedPreviewFormats();
-
- Q_INVOKABLE QSize previewSize() const { return m_previewSize; }
- Q_INVOKABLE QSize getPreviewSize();
- Q_INVOKABLE void updatePreviewSize();
- Q_INVOKABLE bool setPreviewTexture(void *surfaceTexture);
- Q_INVOKABLE bool setPreviewDisplay(void *surfaceHolder);
- Q_INVOKABLE void setDisplayOrientation(int degrees);
-
- Q_INVOKABLE bool isZoomSupported();
- Q_INVOKABLE int getMaxZoom();
- Q_INVOKABLE QList<int> getZoomRatios();
- Q_INVOKABLE int getZoom();
- Q_INVOKABLE void setZoom(int value);
-
- Q_INVOKABLE QString getFlashMode();
- Q_INVOKABLE void setFlashMode(const QString &value);
-
- Q_INVOKABLE QString getFocusMode();
- Q_INVOKABLE void setFocusMode(const QString &value);
-
- Q_INVOKABLE int getMaxNumFocusAreas();
- Q_INVOKABLE QList<QRect> getFocusAreas();
- Q_INVOKABLE void setFocusAreas(const QList<QRect> &areas);
-
- Q_INVOKABLE void autoFocus();
- Q_INVOKABLE void cancelAutoFocus();
-
- Q_INVOKABLE bool isAutoExposureLockSupported();
- Q_INVOKABLE bool getAutoExposureLock();
- Q_INVOKABLE void setAutoExposureLock(bool toggle);
-
- Q_INVOKABLE bool isAutoWhiteBalanceLockSupported();
- Q_INVOKABLE bool getAutoWhiteBalanceLock();
- Q_INVOKABLE void setAutoWhiteBalanceLock(bool toggle);
-
- Q_INVOKABLE int getExposureCompensation();
- Q_INVOKABLE void setExposureCompensation(int value);
- Q_INVOKABLE float getExposureCompensationStep();
- Q_INVOKABLE int getMinExposureCompensation();
- Q_INVOKABLE int getMaxExposureCompensation();
-
- Q_INVOKABLE QString getSceneMode();
- Q_INVOKABLE void setSceneMode(const QString &value);
-
- Q_INVOKABLE QString getWhiteBalance();
- Q_INVOKABLE void setWhiteBalance(const QString &value);
-
- Q_INVOKABLE void updateRotation();
-
- Q_INVOKABLE QList<QSize> getSupportedPictureSizes();
- Q_INVOKABLE void setPictureSize(const QSize &size);
- Q_INVOKABLE void setJpegQuality(int quality);
-
- Q_INVOKABLE void startPreview();
- Q_INVOKABLE void stopPreview();
-
- Q_INVOKABLE void takePicture();
-
- Q_INVOKABLE void setupPreviewFrameCallback();
- Q_INVOKABLE void notifyNewFrames(bool notify);
- Q_INVOKABLE void fetchLastPreviewFrame();
-
- Q_INVOKABLE void applyParameters();
-
- Q_INVOKABLE QStringList callParametersStringListMethod(const QByteArray &methodName);
-
- int m_cameraId;
- QRecursiveMutex m_parametersMutex;
- QSize m_previewSize;
- int m_rotation;
- QJNIObjectPrivate m_info;
- QJNIObjectPrivate m_parameters;
- QJNIObjectPrivate m_camera;
- QJNIObjectPrivate m_cameraListener;
-
-Q_SIGNALS:
- void previewSizeChanged();
- void previewStarted();
- void previewFailedToStart();
- void previewStopped();
-
- void autoFocusStarted();
-
- void whiteBalanceChanged();
-
- void takePictureFailed();
-
- void lastPreviewFrameFetched(const QVideoFrame &frame);
-};
-
-AndroidCamera::AndroidCamera(AndroidCameraPrivate *d, QThread *worker)
- : QObject(),
- d_ptr(d),
- m_worker(worker)
-
-{
- qRegisterMetaType<QList<int> >();
- qRegisterMetaType<QList<QSize> >();
- qRegisterMetaType<QList<QRect> >();
- qRegisterMetaType<ImageFormat>();
-
- connect(d, &AndroidCameraPrivate::previewSizeChanged, this, &AndroidCamera::previewSizeChanged);
- connect(d, &AndroidCameraPrivate::previewStarted, this, &AndroidCamera::previewStarted);
- connect(d, &AndroidCameraPrivate::previewFailedToStart, this, &AndroidCamera::previewFailedToStart);
- connect(d, &AndroidCameraPrivate::previewStopped, this, &AndroidCamera::previewStopped);
- connect(d, &AndroidCameraPrivate::autoFocusStarted, this, &AndroidCamera::autoFocusStarted);
- connect(d, &AndroidCameraPrivate::whiteBalanceChanged, this, &AndroidCamera::whiteBalanceChanged);
- connect(d, &AndroidCameraPrivate::takePictureFailed, this, &AndroidCamera::takePictureFailed);
- connect(d, &AndroidCameraPrivate::lastPreviewFrameFetched, this, &AndroidCamera::lastPreviewFrameFetched);
-}
-
-AndroidCamera::~AndroidCamera()
-{
- Q_D(AndroidCamera);
- if (d->m_camera.isValid()) {
- release();
- QWriteLocker locker(rwLock);
- cameras->remove(cameraId());
- }
-
- m_worker->exit();
- m_worker->wait(5000);
-}
-
-AndroidCamera *AndroidCamera::open(int cameraId)
-{
- if (!qt_androidRequestCameraPermission())
- return nullptr;
-
- AndroidCameraPrivate *d = new AndroidCameraPrivate();
- QThread *worker = new QThread;
- worker->start();
- d->moveToThread(worker);
- connect(worker, &QThread::finished, d, &AndroidCameraPrivate::deleteLater);
- bool ok = true;
- QMetaObject::invokeMethod(d, "init", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok), Q_ARG(int, cameraId));
- if (!ok) {
- worker->quit();
- worker->wait(5000);
- delete worker;
- return 0;
- }
-
- AndroidCamera *q = new AndroidCamera(d, worker);
- QWriteLocker locker(rwLock);
- cameras->insert(cameraId, q);
-
- return q;
-}
-
-int AndroidCamera::cameraId() const
-{
- Q_D(const AndroidCamera);
- return d->m_cameraId;
-}
-
-bool AndroidCamera::lock()
-{
- Q_D(AndroidCamera);
- bool ok = true;
- QMetaObject::invokeMethod(d, "lock", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok));
- return ok;
-}
-
-bool AndroidCamera::unlock()
-{
- Q_D(AndroidCamera);
- bool ok = true;
- QMetaObject::invokeMethod(d, "unlock", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok));
- return ok;
-}
-
-bool AndroidCamera::reconnect()
-{
- Q_D(AndroidCamera);
- bool ok = true;
- QMetaObject::invokeMethod(d, "reconnect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok));
- return ok;
-}
-
-void AndroidCamera::release()
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "release", Qt::BlockingQueuedConnection);
-}
-
-AndroidCamera::CameraFacing AndroidCamera::getFacing()
-{
- Q_D(AndroidCamera);
- return d->getFacing();
-}
-
-int AndroidCamera::getNativeOrientation()
-{
- Q_D(AndroidCamera);
- return d->getNativeOrientation();
-}
-
-QSize AndroidCamera::getPreferredPreviewSizeForVideo()
-{
- Q_D(AndroidCamera);
- return d->getPreferredPreviewSizeForVideo();
-}
-
-QList<QSize> AndroidCamera::getSupportedPreviewSizes()
-{
- Q_D(AndroidCamera);
- return d->getSupportedPreviewSizes();
-}
-
-QList<AndroidCamera::FpsRange> AndroidCamera::getSupportedPreviewFpsRange()
-{
- Q_D(AndroidCamera);
- return d->getSupportedPreviewFpsRange();
-}
-
-AndroidCamera::FpsRange AndroidCamera::getPreviewFpsRange()
-{
- Q_D(AndroidCamera);
- return d->getPreviewFpsRange();
-}
-
-void AndroidCamera::setPreviewFpsRange(FpsRange range)
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "setPreviewFpsRange", Q_ARG(int, range.min), Q_ARG(int, range.max));
-}
-
-AndroidCamera::ImageFormat AndroidCamera::getPreviewFormat()
-{
- Q_D(AndroidCamera);
- return d->getPreviewFormat();
-}
-
-void AndroidCamera::setPreviewFormat(ImageFormat fmt)
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "setPreviewFormat", Q_ARG(AndroidCamera::ImageFormat, fmt));
-}
-
-QList<AndroidCamera::ImageFormat> AndroidCamera::getSupportedPreviewFormats()
-{
- Q_D(AndroidCamera);
- return d->getSupportedPreviewFormats();
-}
-
-QSize AndroidCamera::previewSize() const
-{
- Q_D(const AndroidCamera);
- return d->m_previewSize;
-}
-
-QSize AndroidCamera::actualPreviewSize()
-{
- Q_D(AndroidCamera);
- return d->getPreviewSize();
-}
-
-void AndroidCamera::setPreviewSize(const QSize &size)
-{
- Q_D(AndroidCamera);
- d->m_parametersMutex.lock();
- bool areParametersValid = d->m_parameters.isValid();
- d->m_parametersMutex.unlock();
- if (!areParametersValid)
- return;
-
- d->m_previewSize = size;
- QMetaObject::invokeMethod(d, "updatePreviewSize");
-}
-
-bool AndroidCamera::setPreviewTexture(AndroidSurfaceTexture *surfaceTexture)
-{
- Q_D(AndroidCamera);
- bool ok = true;
- QMetaObject::invokeMethod(d,
- "setPreviewTexture",
- Qt::BlockingQueuedConnection,
- Q_RETURN_ARG(bool, ok),
- Q_ARG(void *, surfaceTexture ? surfaceTexture->surfaceTexture() : 0));
- return ok;
-}
-
-bool AndroidCamera::setPreviewDisplay(AndroidSurfaceHolder *surfaceHolder)
-{
- Q_D(AndroidCamera);
- bool ok = true;
- QMetaObject::invokeMethod(d,
- "setPreviewDisplay",
- Qt::BlockingQueuedConnection,
- Q_RETURN_ARG(bool, ok),
- Q_ARG(void *, surfaceHolder ? surfaceHolder->surfaceHolder() : 0));
- return ok;
-}
-
-void AndroidCamera::setDisplayOrientation(int degrees)
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "setDisplayOrientation", Qt::QueuedConnection, Q_ARG(int, degrees));
-}
-
-bool AndroidCamera::isZoomSupported()
-{
- Q_D(AndroidCamera);
- return d->isZoomSupported();
-}
-
-int AndroidCamera::getMaxZoom()
-{
- Q_D(AndroidCamera);
- return d->getMaxZoom();
-}
-
-QList<int> AndroidCamera::getZoomRatios()
-{
- Q_D(AndroidCamera);
- return d->getZoomRatios();
-}
-
-int AndroidCamera::getZoom()
-{
- Q_D(AndroidCamera);
- return d->getZoom();
-}
-
-void AndroidCamera::setZoom(int value)
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "setZoom", Q_ARG(int, value));
-}
-
-QStringList AndroidCamera::getSupportedFlashModes()
-{
- Q_D(AndroidCamera);
- return d->callParametersStringListMethod("getSupportedFlashModes");
-}
-
-QString AndroidCamera::getFlashMode()
-{
- Q_D(AndroidCamera);
- return d->getFlashMode();
-}
-
-void AndroidCamera::setFlashMode(const QString &value)
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "setFlashMode", Q_ARG(QString, value));
-}
-
-QStringList AndroidCamera::getSupportedFocusModes()
-{
- Q_D(AndroidCamera);
- return d->callParametersStringListMethod("getSupportedFocusModes");
-}
-
-QString AndroidCamera::getFocusMode()
-{
- Q_D(AndroidCamera);
- return d->getFocusMode();
-}
-
-void AndroidCamera::setFocusMode(const QString &value)
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "setFocusMode", Q_ARG(QString, value));
-}
-
-int AndroidCamera::getMaxNumFocusAreas()
-{
- Q_D(AndroidCamera);
- return d->getMaxNumFocusAreas();
-}
-
-QList<QRect> AndroidCamera::getFocusAreas()
-{
- Q_D(AndroidCamera);
- return d->getFocusAreas();
-}
-
-void AndroidCamera::setFocusAreas(const QList<QRect> &areas)
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "setFocusAreas", Q_ARG(QList<QRect>, areas));
-}
-
-void AndroidCamera::autoFocus()
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "autoFocus");
-}
-
-void AndroidCamera::cancelAutoFocus()
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "cancelAutoFocus", Qt::QueuedConnection);
-}
-
-bool AndroidCamera::isAutoExposureLockSupported()
-{
- Q_D(AndroidCamera);
- return d->isAutoExposureLockSupported();
-}
-
-bool AndroidCamera::getAutoExposureLock()
-{
- Q_D(AndroidCamera);
- return d->getAutoExposureLock();
-}
-
-void AndroidCamera::setAutoExposureLock(bool toggle)
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "setAutoExposureLock", Q_ARG(bool, toggle));
-}
-
-bool AndroidCamera::isAutoWhiteBalanceLockSupported()
-{
- Q_D(AndroidCamera);
- return d->isAutoWhiteBalanceLockSupported();
-}
-
-bool AndroidCamera::getAutoWhiteBalanceLock()
-{
- Q_D(AndroidCamera);
- return d->getAutoWhiteBalanceLock();
-}
-
-void AndroidCamera::setAutoWhiteBalanceLock(bool toggle)
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "setAutoWhiteBalanceLock", Q_ARG(bool, toggle));
-}
-
-int AndroidCamera::getExposureCompensation()
-{
- Q_D(AndroidCamera);
- return d->getExposureCompensation();
-}
-
-void AndroidCamera::setExposureCompensation(int value)
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "setExposureCompensation", Q_ARG(int, value));
-}
-
-float AndroidCamera::getExposureCompensationStep()
-{
- Q_D(AndroidCamera);
- return d->getExposureCompensationStep();
-}
-
-int AndroidCamera::getMinExposureCompensation()
-{
- Q_D(AndroidCamera);
- return d->getMinExposureCompensation();
-}
-
-int AndroidCamera::getMaxExposureCompensation()
-{
- Q_D(AndroidCamera);
- return d->getMaxExposureCompensation();
-}
-
-QStringList AndroidCamera::getSupportedSceneModes()
-{
- Q_D(AndroidCamera);
- return d->callParametersStringListMethod("getSupportedSceneModes");
-}
-
-QString AndroidCamera::getSceneMode()
-{
- Q_D(AndroidCamera);
- return d->getSceneMode();
-}
-
-void AndroidCamera::setSceneMode(const QString &value)
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "setSceneMode", Q_ARG(QString, value));
-}
-
-QStringList AndroidCamera::getSupportedWhiteBalance()
-{
- Q_D(AndroidCamera);
- return d->callParametersStringListMethod("getSupportedWhiteBalance");
-}
-
-QString AndroidCamera::getWhiteBalance()
-{
- Q_D(AndroidCamera);
- return d->getWhiteBalance();
-}
-
-void AndroidCamera::setWhiteBalance(const QString &value)
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "setWhiteBalance", Q_ARG(QString, value));
-}
-
-void AndroidCamera::setRotation(int rotation)
-{
- Q_D(AndroidCamera);
- //We need to do it here and not in worker class because we cache rotation
- d->m_parametersMutex.lock();
- bool areParametersValid = d->m_parameters.isValid();
- d->m_parametersMutex.unlock();
- if (!areParametersValid)
- return;
-
- d->m_rotation = rotation;
- QMetaObject::invokeMethod(d, "updateRotation");
-}
-
-int AndroidCamera::getRotation() const
-{
- Q_D(const AndroidCamera);
- return d->m_rotation;
-}
-
-QList<QSize> AndroidCamera::getSupportedPictureSizes()
-{
- Q_D(AndroidCamera);
- return d->getSupportedPictureSizes();
-}
-
-void AndroidCamera::setPictureSize(const QSize &size)
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "setPictureSize", Q_ARG(QSize, size));
-}
-
-void AndroidCamera::setJpegQuality(int quality)
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "setJpegQuality", Q_ARG(int, quality));
-}
-
-void AndroidCamera::takePicture()
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "takePicture", Qt::BlockingQueuedConnection);
-}
-
-void AndroidCamera::setupPreviewFrameCallback()
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "setupPreviewFrameCallback");
-}
-
-void AndroidCamera::notifyNewFrames(bool notify)
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "notifyNewFrames", Q_ARG(bool, notify));
-}
-
-void AndroidCamera::fetchLastPreviewFrame()
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "fetchLastPreviewFrame");
-}
-
-QJNIObjectPrivate AndroidCamera::getCameraObject()
-{
- Q_D(AndroidCamera);
- return d->m_camera;
-}
-
-int AndroidCamera::getNumberOfCameras()
-{
- if (!qt_androidRequestCameraPermission())
- return 0;
-
- return QJNIObjectPrivate::callStaticMethod<jint>("android/hardware/Camera",
- "getNumberOfCameras");
-}
-
-void AndroidCamera::getCameraInfo(int id, AndroidCameraInfo *info)
-{
- Q_ASSERT(info);
-
- QJNIObjectPrivate cameraInfo("android/hardware/Camera$CameraInfo");
- QJNIObjectPrivate::callStaticMethod<void>("android/hardware/Camera",
- "getCameraInfo",
- "(ILandroid/hardware/Camera$CameraInfo;)V",
- id, cameraInfo.object());
-
- AndroidCamera::CameraFacing facing = AndroidCamera::CameraFacing(cameraInfo.getField<jint>("facing"));
- // The orientation provided by Android is counter-clockwise, we need it clockwise
- info->orientation = (360 - cameraInfo.getField<jint>("orientation")) % 360;
-
- switch (facing) {
- case AndroidCamera::CameraFacingBack:
- info->name = QByteArray("back");
- info->description = QStringLiteral("Rear-facing camera");
- info->position = QCamera::BackFace;
- break;
- case AndroidCamera::CameraFacingFront:
- info->name = QByteArray("front");
- info->description = QStringLiteral("Front-facing camera");
- info->position = QCamera::FrontFace;
- break;
- default:
- break;
- }
-}
-
-void AndroidCamera::startPreview()
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "startPreview");
-}
-
-void AndroidCamera::stopPreview()
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "stopPreview");
-}
-
-void AndroidCamera::stopPreviewSynchronous()
-{
- Q_D(AndroidCamera);
- QMetaObject::invokeMethod(d, "stopPreview", Qt::BlockingQueuedConnection);
-}
-
-AndroidCameraPrivate::AndroidCameraPrivate()
- : QObject()
-{
-}
-
-AndroidCameraPrivate::~AndroidCameraPrivate()
-{
-}
-
-static qint32 s_activeCameras = 0;
-
-bool AndroidCameraPrivate::init(int cameraId)
-{
- m_cameraId = cameraId;
- QJNIEnvironmentPrivate env;
-
- const bool opened = s_activeCameras & (1 << cameraId);
- if (opened)
- return false;
-
- m_camera = QJNIObjectPrivate::callStaticObjectMethod("android/hardware/Camera",
- "open",
- "(I)Landroid/hardware/Camera;",
- cameraId);
- if (exceptionCheckAndClear(env) || !m_camera.isValid())
- return false;
-
- m_cameraListener = QJNIObjectPrivate(QtCameraListenerClassName, "(I)V", m_cameraId);
- m_info = QJNIObjectPrivate("android/hardware/Camera$CameraInfo");
- m_camera.callStaticMethod<void>("android/hardware/Camera",
- "getCameraInfo",
- "(ILandroid/hardware/Camera$CameraInfo;)V",
- cameraId,
- m_info.object());
-
- QJNIObjectPrivate params = m_camera.callObjectMethod("getParameters",
- "()Landroid/hardware/Camera$Parameters;");
- m_parameters = QJNIObjectPrivate(params);
- s_activeCameras |= 1 << cameraId;
-
- return true;
-}
-
-void AndroidCameraPrivate::release()
-{
- m_previewSize = QSize();
- m_parametersMutex.lock();
- m_parameters = QJNIObjectPrivate();
- m_parametersMutex.unlock();
- if (m_camera.isValid()) {
- m_camera.callMethod<void>("release");
- s_activeCameras &= ~(1 << m_cameraId);
- }
-}
-
-bool AndroidCameraPrivate::lock()
-{
- QJNIEnvironmentPrivate env;
- m_camera.callMethod<void>("lock");
- return !exceptionCheckAndClear(env);
-}
-
-bool AndroidCameraPrivate::unlock()
-{
- QJNIEnvironmentPrivate env;
- m_camera.callMethod<void>("unlock");
- return !exceptionCheckAndClear(env);
-}
-
-bool AndroidCameraPrivate::reconnect()
-{
- QJNIEnvironmentPrivate env;
- m_camera.callMethod<void>("reconnect");
- return !exceptionCheckAndClear(env);
-}
-
-AndroidCamera::CameraFacing AndroidCameraPrivate::getFacing()
-{
- return AndroidCamera::CameraFacing(m_info.getField<jint>("facing"));
-}
-
-int AndroidCameraPrivate::getNativeOrientation()
-{
- return m_info.getField<jint>("orientation");
-}
-
-QSize AndroidCameraPrivate::getPreferredPreviewSizeForVideo()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return QSize();
-
- QJNIObjectPrivate size = m_parameters.callObjectMethod("getPreferredPreviewSizeForVideo",
- "()Landroid/hardware/Camera$Size;");
-
- if (!size.isValid())
- return QSize();
-
- return QSize(size.getField<jint>("width"), size.getField<jint>("height"));
-}
-
-QList<QSize> AndroidCameraPrivate::getSupportedPreviewSizes()
-{
- QList<QSize> list;
-
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (m_parameters.isValid()) {
- QJNIObjectPrivate sizeList = m_parameters.callObjectMethod("getSupportedPreviewSizes",
- "()Ljava/util/List;");
- int count = sizeList.callMethod<jint>("size");
- for (int i = 0; i < count; ++i) {
- QJNIObjectPrivate size = sizeList.callObjectMethod("get",
- "(I)Ljava/lang/Object;",
- i);
- list.append(QSize(size.getField<jint>("width"), size.getField<jint>("height")));
- }
-
- std::sort(list.begin(), list.end(), qt_sizeLessThan);
- }
-
- return list;
-}
-
-QList<AndroidCamera::FpsRange> AndroidCameraPrivate::getSupportedPreviewFpsRange()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- QJNIEnvironmentPrivate env;
-
- QList<AndroidCamera::FpsRange> rangeList;
-
- if (m_parameters.isValid()) {
- QJNIObjectPrivate rangeListNative = m_parameters.callObjectMethod("getSupportedPreviewFpsRange",
- "()Ljava/util/List;");
- int count = rangeListNative.callMethod<jint>("size");
-
- rangeList.reserve(count);
-
- for (int i = 0; i < count; ++i) {
- QJNIObjectPrivate range = rangeListNative.callObjectMethod("get",
- "(I)Ljava/lang/Object;",
- i);
-
- jintArray jRange = static_cast<jintArray>(range.object());
- jint* rangeArray = env->GetIntArrayElements(jRange, 0);
-
- AndroidCamera::FpsRange fpsRange;
-
- fpsRange.min = rangeArray[0];
- fpsRange.max = rangeArray[1];
-
- env->ReleaseIntArrayElements(jRange, rangeArray, 0);
-
- rangeList << fpsRange;
- }
- }
-
- return rangeList;
-}
-
-AndroidCamera::FpsRange AndroidCameraPrivate::getPreviewFpsRange()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- QJNIEnvironmentPrivate env;
-
- AndroidCamera::FpsRange range;
-
- if (!m_parameters.isValid())
- return range;
-
- jintArray jRangeArray = env->NewIntArray(2);
- m_parameters.callMethod<void>("getPreviewFpsRange", "([I)V", jRangeArray);
-
- jint* jRangeElements = env->GetIntArrayElements(jRangeArray, 0);
-
- range.min = jRangeElements[0];
- range.max = jRangeElements[1];
-
- env->ReleaseIntArrayElements(jRangeArray, jRangeElements, 0);
- env->DeleteLocalRef(jRangeArray);
-
- return range;
-}
-
-void AndroidCameraPrivate::setPreviewFpsRange(int min, int max)
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return;
-
- QJNIEnvironmentPrivate env;
- m_parameters.callMethod<void>("setPreviewFpsRange", "(II)V", min, max);
- exceptionCheckAndClear(env);
-}
-
-AndroidCamera::ImageFormat AndroidCameraPrivate::getPreviewFormat()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return AndroidCamera::UnknownImageFormat;
-
- return AndroidCamera::ImageFormat(m_parameters.callMethod<jint>("getPreviewFormat"));
-}
-
-void AndroidCameraPrivate::setPreviewFormat(AndroidCamera::ImageFormat fmt)
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return;
-
- m_parameters.callMethod<void>("setPreviewFormat", "(I)V", jint(fmt));
- applyParameters();
-}
-
-QList<AndroidCamera::ImageFormat> AndroidCameraPrivate::getSupportedPreviewFormats()
-{
- QList<AndroidCamera::ImageFormat> list;
-
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (m_parameters.isValid()) {
- QJNIObjectPrivate formatList = m_parameters.callObjectMethod("getSupportedPreviewFormats",
- "()Ljava/util/List;");
- int count = formatList.callMethod<jint>("size");
- for (int i = 0; i < count; ++i) {
- QJNIObjectPrivate format = formatList.callObjectMethod("get",
- "(I)Ljava/lang/Object;",
- i);
- list.append(AndroidCamera::ImageFormat(format.callMethod<jint>("intValue")));
- }
- }
-
- return list;
-}
-
-QSize AndroidCameraPrivate::getPreviewSize()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return QSize();
-
- QJNIObjectPrivate size = m_parameters.callObjectMethod("getPreviewSize",
- "()Landroid/hardware/Camera$Size;");
-
- if (!size.isValid())
- return QSize();
-
- return QSize(size.getField<jint>("width"), size.getField<jint>("height"));
-}
-
-void AndroidCameraPrivate::updatePreviewSize()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (m_previewSize.isValid()) {
- m_parameters.callMethod<void>("setPreviewSize", "(II)V", m_previewSize.width(), m_previewSize.height());
- applyParameters();
- }
-
- emit previewSizeChanged();
-}
-
-bool AndroidCameraPrivate::setPreviewTexture(void *surfaceTexture)
-{
- QJNIEnvironmentPrivate env;
- m_camera.callMethod<void>("setPreviewTexture",
- "(Landroid/graphics/SurfaceTexture;)V",
- static_cast<jobject>(surfaceTexture));
- return !exceptionCheckAndClear(env);
-}
-
-bool AndroidCameraPrivate::setPreviewDisplay(void *surfaceHolder)
-{
- QJNIEnvironmentPrivate env;
- m_camera.callMethod<void>("setPreviewDisplay",
- "(Landroid/view/SurfaceHolder;)V",
- static_cast<jobject>(surfaceHolder));
- return !exceptionCheckAndClear(env);
-}
-
-void AndroidCameraPrivate::setDisplayOrientation(int degrees)
-{
- m_camera.callMethod<void>("setDisplayOrientation", "(I)V", degrees);
-}
-
-bool AndroidCameraPrivate::isZoomSupported()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return false;
-
- return m_parameters.callMethod<jboolean>("isZoomSupported");
-}
-
-int AndroidCameraPrivate::getMaxZoom()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return 0;
-
- return m_parameters.callMethod<jint>("getMaxZoom");
-}
-
-QList<int> AndroidCameraPrivate::getZoomRatios()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- QList<int> ratios;
-
- if (m_parameters.isValid()) {
- QJNIObjectPrivate ratioList = m_parameters.callObjectMethod("getZoomRatios",
- "()Ljava/util/List;");
- int count = ratioList.callMethod<jint>("size");
- for (int i = 0; i < count; ++i) {
- QJNIObjectPrivate zoomRatio = ratioList.callObjectMethod("get",
- "(I)Ljava/lang/Object;",
- i);
-
- ratios.append(zoomRatio.callMethod<jint>("intValue"));
- }
- }
-
- return ratios;
-}
-
-int AndroidCameraPrivate::getZoom()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return 0;
-
- return m_parameters.callMethod<jint>("getZoom");
-}
-
-void AndroidCameraPrivate::setZoom(int value)
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return;
-
- m_parameters.callMethod<void>("setZoom", "(I)V", value);
- applyParameters();
-}
-
-QString AndroidCameraPrivate::getFlashMode()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- QString value;
-
- if (m_parameters.isValid()) {
- QJNIObjectPrivate flashMode = m_parameters.callObjectMethod("getFlashMode",
- "()Ljava/lang/String;");
- if (flashMode.isValid())
- value = flashMode.toString();
- }
-
- return value;
-}
-
-void AndroidCameraPrivate::setFlashMode(const QString &value)
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return;
-
- m_parameters.callMethod<void>("setFlashMode",
- "(Ljava/lang/String;)V",
- QJNIObjectPrivate::fromString(value).object());
- applyParameters();
-}
-
-QString AndroidCameraPrivate::getFocusMode()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- QString value;
-
- if (m_parameters.isValid()) {
- QJNIObjectPrivate focusMode = m_parameters.callObjectMethod("getFocusMode",
- "()Ljava/lang/String;");
- if (focusMode.isValid())
- value = focusMode.toString();
- }
-
- return value;
-}
-
-void AndroidCameraPrivate::setFocusMode(const QString &value)
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return;
-
- m_parameters.callMethod<void>("setFocusMode",
- "(Ljava/lang/String;)V",
- QJNIObjectPrivate::fromString(value).object());
- applyParameters();
-}
-
-int AndroidCameraPrivate::getMaxNumFocusAreas()
-{
- if (QtAndroidPrivate::androidSdkVersion() < 14)
- return 0;
-
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return 0;
-
- return m_parameters.callMethod<jint>("getMaxNumFocusAreas");
-}
-
-QList<QRect> AndroidCameraPrivate::getFocusAreas()
-{
- QList<QRect> areas;
-
- if (QtAndroidPrivate::androidSdkVersion() < 14)
- return areas;
-
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (m_parameters.isValid()) {
- QJNIObjectPrivate list = m_parameters.callObjectMethod("getFocusAreas",
- "()Ljava/util/List;");
-
- if (list.isValid()) {
- int count = list.callMethod<jint>("size");
- for (int i = 0; i < count; ++i) {
- QJNIObjectPrivate area = list.callObjectMethod("get",
- "(I)Ljava/lang/Object;",
- i);
-
- areas.append(areaToRect(area.object()));
- }
- }
- }
-
- return areas;
-}
-
-void AndroidCameraPrivate::setFocusAreas(const QList<QRect> &areas)
-{
- if (QtAndroidPrivate::androidSdkVersion() < 14)
- return;
-
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return;
-
- QJNIObjectPrivate list;
-
- if (!areas.isEmpty()) {
- QJNIEnvironmentPrivate env;
- QJNIObjectPrivate arrayList("java/util/ArrayList", "(I)V", areas.size());
- for (int i = 0; i < areas.size(); ++i) {
- arrayList.callMethod<jboolean>("add",
- "(Ljava/lang/Object;)Z",
- rectToArea(areas.at(i)).object());
- exceptionCheckAndClear(env);
- }
- list = arrayList;
- }
-
- m_parameters.callMethod<void>("setFocusAreas", "(Ljava/util/List;)V", list.object());
-
- applyParameters();
-}
-
-void AndroidCameraPrivate::autoFocus()
-{
- QJNIEnvironmentPrivate env;
-
- m_camera.callMethod<void>("autoFocus",
- "(Landroid/hardware/Camera$AutoFocusCallback;)V",
- m_cameraListener.object());
-
- if (!exceptionCheckAndClear(env))
- emit autoFocusStarted();
-}
-
-void AndroidCameraPrivate::cancelAutoFocus()
-{
- QJNIEnvironmentPrivate env;
- m_camera.callMethod<void>("cancelAutoFocus");
- exceptionCheckAndClear(env);
-}
-
-bool AndroidCameraPrivate::isAutoExposureLockSupported()
-{
- if (QtAndroidPrivate::androidSdkVersion() < 14)
- return false;
-
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return false;
-
- return m_parameters.callMethod<jboolean>("isAutoExposureLockSupported");
-}
-
-bool AndroidCameraPrivate::getAutoExposureLock()
-{
- if (QtAndroidPrivate::androidSdkVersion() < 14)
- return false;
-
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return false;
-
- return m_parameters.callMethod<jboolean>("getAutoExposureLock");
-}
-
-void AndroidCameraPrivate::setAutoExposureLock(bool toggle)
-{
- if (QtAndroidPrivate::androidSdkVersion() < 14)
- return;
-
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return;
-
- m_parameters.callMethod<void>("setAutoExposureLock", "(Z)V", toggle);
- applyParameters();
-}
-
-bool AndroidCameraPrivate::isAutoWhiteBalanceLockSupported()
-{
- if (QtAndroidPrivate::androidSdkVersion() < 14)
- return false;
-
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return false;
-
- return m_parameters.callMethod<jboolean>("isAutoWhiteBalanceLockSupported");
-}
-
-bool AndroidCameraPrivate::getAutoWhiteBalanceLock()
-{
- if (QtAndroidPrivate::androidSdkVersion() < 14)
- return false;
-
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return false;
-
- return m_parameters.callMethod<jboolean>("getAutoWhiteBalanceLock");
-}
-
-void AndroidCameraPrivate::setAutoWhiteBalanceLock(bool toggle)
-{
- if (QtAndroidPrivate::androidSdkVersion() < 14)
- return;
-
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return;
-
- m_parameters.callMethod<void>("setAutoWhiteBalanceLock", "(Z)V", toggle);
- applyParameters();
-}
-
-int AndroidCameraPrivate::getExposureCompensation()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return 0;
-
- return m_parameters.callMethod<jint>("getExposureCompensation");
-}
-
-void AndroidCameraPrivate::setExposureCompensation(int value)
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return;
-
- m_parameters.callMethod<void>("setExposureCompensation", "(I)V", value);
- applyParameters();
-}
-
-float AndroidCameraPrivate::getExposureCompensationStep()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return 0;
-
- return m_parameters.callMethod<jfloat>("getExposureCompensationStep");
-}
-
-int AndroidCameraPrivate::getMinExposureCompensation()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return 0;
-
- return m_parameters.callMethod<jint>("getMinExposureCompensation");
-}
-
-int AndroidCameraPrivate::getMaxExposureCompensation()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return 0;
-
- return m_parameters.callMethod<jint>("getMaxExposureCompensation");
-}
-
-QString AndroidCameraPrivate::getSceneMode()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- QString value;
-
- if (m_parameters.isValid()) {
- QJNIObjectPrivate sceneMode = m_parameters.callObjectMethod("getSceneMode",
- "()Ljava/lang/String;");
- if (sceneMode.isValid())
- value = sceneMode.toString();
- }
-
- return value;
-}
-
-void AndroidCameraPrivate::setSceneMode(const QString &value)
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return;
-
- m_parameters.callMethod<void>("setSceneMode",
- "(Ljava/lang/String;)V",
- QJNIObjectPrivate::fromString(value).object());
- applyParameters();
-}
-
-QString AndroidCameraPrivate::getWhiteBalance()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- QString value;
-
- if (m_parameters.isValid()) {
- QJNIObjectPrivate wb = m_parameters.callObjectMethod("getWhiteBalance",
- "()Ljava/lang/String;");
- if (wb.isValid())
- value = wb.toString();
- }
-
- return value;
-}
-
-void AndroidCameraPrivate::setWhiteBalance(const QString &value)
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return;
-
- m_parameters.callMethod<void>("setWhiteBalance",
- "(Ljava/lang/String;)V",
- QJNIObjectPrivate::fromString(value).object());
- applyParameters();
-
- emit whiteBalanceChanged();
-}
-
-void AndroidCameraPrivate::updateRotation()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- m_parameters.callMethod<void>("setRotation", "(I)V", m_rotation);
- applyParameters();
-}
-
-QList<QSize> AndroidCameraPrivate::getSupportedPictureSizes()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- QList<QSize> list;
-
- if (m_parameters.isValid()) {
- QJNIObjectPrivate sizeList = m_parameters.callObjectMethod("getSupportedPictureSizes",
- "()Ljava/util/List;");
- int count = sizeList.callMethod<jint>("size");
- for (int i = 0; i < count; ++i) {
- QJNIObjectPrivate size = sizeList.callObjectMethod("get",
- "(I)Ljava/lang/Object;",
- i);
- list.append(QSize(size.getField<jint>("width"), size.getField<jint>("height")));
- }
-
- std::sort(list.begin(), list.end(), qt_sizeLessThan);
- }
-
- return list;
-}
-
-void AndroidCameraPrivate::setPictureSize(const QSize &size)
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return;
-
- m_parameters.callMethod<void>("setPictureSize", "(II)V", size.width(), size.height());
- applyParameters();
-}
-
-void AndroidCameraPrivate::setJpegQuality(int quality)
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- if (!m_parameters.isValid())
- return;
-
- m_parameters.callMethod<void>("setJpegQuality", "(I)V", quality);
- applyParameters();
-}
-
-void AndroidCameraPrivate::startPreview()
-{
- QJNIEnvironmentPrivate env;
-
- setupPreviewFrameCallback();
- m_camera.callMethod<void>("startPreview");
-
- if (exceptionCheckAndClear(env))
- emit previewFailedToStart();
- else
- emit previewStarted();
-}
-
-void AndroidCameraPrivate::stopPreview()
-{
- QJNIEnvironmentPrivate env;
-
- // cancel any pending new frame notification
- m_cameraListener.callMethod<void>("notifyWhenFrameAvailable", "(Z)V", false);
-
- m_camera.callMethod<void>("stopPreview");
-
- exceptionCheckAndClear(env);
- emit previewStopped();
-}
-
-void AndroidCameraPrivate::takePicture()
-{
- QJNIEnvironmentPrivate env;
-
- // We must clear the preview callback before calling takePicture(), otherwise the call will
- // block and the camera server will be frozen until the next device restart...
- // That problem only happens on some devices and on the emulator
- m_cameraListener.callMethod<void>("clearPreviewCallback", "(Landroid/hardware/Camera;)V", m_camera.object());
-
- m_camera.callMethod<void>("takePicture", "(Landroid/hardware/Camera$ShutterCallback;"
- "Landroid/hardware/Camera$PictureCallback;"
- "Landroid/hardware/Camera$PictureCallback;)V",
- m_cameraListener.object(),
- jobject(0),
- m_cameraListener.object());
-
- if (exceptionCheckAndClear(env))
- emit takePictureFailed();
-}
-
-void AndroidCameraPrivate::setupPreviewFrameCallback()
-{
- m_cameraListener.callMethod<void>("setupPreviewCallback", "(Landroid/hardware/Camera;)V", m_camera.object());
-}
-
-void AndroidCameraPrivate::notifyNewFrames(bool notify)
-{
- m_cameraListener.callMethod<void>("notifyNewFrames", "(Z)V", notify);
-}
-
-void AndroidCameraPrivate::fetchLastPreviewFrame()
-{
- QJNIEnvironmentPrivate env;
- QJNIObjectPrivate data = m_cameraListener.callObjectMethod("lastPreviewBuffer", "()[B");
-
- if (!data.isValid()) {
- // If there's no buffer received yet, retry when the next one arrives
- m_cameraListener.callMethod<void>("notifyWhenFrameAvailable", "(Z)V", true);
- return;
- }
-
- const int arrayLength = env->GetArrayLength(static_cast<jbyteArray>(data.object()));
- if (arrayLength == 0)
- return;
-
- QByteArray bytes(arrayLength, Qt::Uninitialized);
- env->GetByteArrayRegion(static_cast<jbyteArray>(data.object()),
- 0,
- arrayLength,
- reinterpret_cast<jbyte *>(bytes.data()));
-
- const int width = m_cameraListener.callMethod<jint>("previewWidth");
- const int height = m_cameraListener.callMethod<jint>("previewHeight");
- const int format = m_cameraListener.callMethod<jint>("previewFormat");
- const int bpl = m_cameraListener.callMethod<jint>("previewBytesPerLine");
-
- QVideoFrame frame(new QMemoryVideoBuffer(bytes, bpl),
- QSize(width, height),
- qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat(format)));
-
- emit lastPreviewFrameFetched(frame);
-}
-
-void AndroidCameraPrivate::applyParameters()
-{
- QJNIEnvironmentPrivate env;
- m_camera.callMethod<void>("setParameters",
- "(Landroid/hardware/Camera$Parameters;)V",
- m_parameters.object());
- exceptionCheckAndClear(env);
-}
-
-QStringList AndroidCameraPrivate::callParametersStringListMethod(const QByteArray &methodName)
-{
- const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
-
- QStringList stringList;
-
- if (m_parameters.isValid()) {
- QJNIObjectPrivate list = m_parameters.callObjectMethod(methodName.constData(),
- "()Ljava/util/List;");
-
- if (list.isValid()) {
- int count = list.callMethod<jint>("size");
- for (int i = 0; i < count; ++i) {
- QJNIObjectPrivate string = list.callObjectMethod("get",
- "(I)Ljava/lang/Object;",
- i);
- stringList.append(string.toString());
- }
- }
- }
-
- return stringList;
-}
-
-bool AndroidCamera::initJNI(JNIEnv *env)
-{
- jclass clazz = QJNIEnvironmentPrivate::findClass(QtCameraListenerClassName,
- env);
-
- static const JNINativeMethod methods[] = {
- {"notifyAutoFocusComplete", "(IZ)V", (void *)notifyAutoFocusComplete},
- {"notifyPictureExposed", "(I)V", (void *)notifyPictureExposed},
- {"notifyPictureCaptured", "(I[B)V", (void *)notifyPictureCaptured},
- {"notifyNewPreviewFrame", "(I[BIIII)V", (void *)notifyNewPreviewFrame},
- {"notifyFrameAvailable", "(I)V", (void *)notifyFrameAvailable}
- };
-
- if (clazz && env->RegisterNatives(clazz,
- methods,
- sizeof(methods) / sizeof(methods[0])) != JNI_OK) {
- return false;
- }
-
- return true;
-}
-
-QT_END_NAMESPACE
-
-#include "androidcamera.moc"
diff --git a/src/plugins/android/src/wrappers/jni/androidcamera.h b/src/plugins/android/src/wrappers/jni/androidcamera.h
deleted file mode 100644
index 5ae141f01..000000000
--- a/src/plugins/android/src/wrappers/jni/androidcamera.h
+++ /dev/null
@@ -1,237 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef ANDROIDCAMERA_H
-#define ANDROIDCAMERA_H
-
-#include <qobject.h>
-#include <QtCore/private/qjni_p.h>
-#include <qsize.h>
-#include <qrect.h>
-#include <QtMultimedia/qcamera.h>
-
-QT_BEGIN_NAMESPACE
-
-class QThread;
-
-class AndroidCameraPrivate;
-class AndroidSurfaceTexture;
-class AndroidSurfaceHolder;
-
-struct AndroidCameraInfo
-{
- QByteArray name;
- QString description;
- QCamera::Position position;
- int orientation;
-};
-Q_DECLARE_TYPEINFO(AndroidCameraInfo, Q_MOVABLE_TYPE);
-
-class AndroidCamera : public QObject
-{
- Q_OBJECT
- Q_ENUMS(CameraFacing)
- Q_ENUMS(ImageFormat)
-public:
- enum CameraFacing {
- CameraFacingBack = 0,
- CameraFacingFront = 1
- };
-
- enum ImageFormat { // same values as in android.graphics.ImageFormat Java class
- UnknownImageFormat = 0,
- RGB565 = 4,
- NV16 = 16,
- NV21 = 17,
- YUY2 = 20,
- JPEG = 256,
- YV12 = 842094169
- };
-
- // http://developer.android.com/reference/android/hardware/Camera.Parameters.html#getSupportedPreviewFpsRange%28%29
- // "The values are multiplied by 1000 and represented in integers"
- struct FpsRange {
- int min;
- int max;
-
- FpsRange(): min(0), max(0) {}
-
- qreal getMinReal() const { return min / 1000.0; }
- qreal getMaxReal() const { return max / 1000.0; }
-
- static FpsRange makeFromQReal(qreal min, qreal max)
- {
- FpsRange range;
- range.min = static_cast<int>(min * 1000.0);
- range.max = static_cast<int>(max * 1000.0);
- return range;
- }
- };
-
- ~AndroidCamera();
-
- static AndroidCamera *open(int cameraId);
-
- int cameraId() const;
-
- bool lock();
- bool unlock();
- bool reconnect();
- void release();
-
- CameraFacing getFacing();
- int getNativeOrientation();
-
- QSize getPreferredPreviewSizeForVideo();
- QList<QSize> getSupportedPreviewSizes();
-
- QList<FpsRange> getSupportedPreviewFpsRange();
-
- FpsRange getPreviewFpsRange();
- void setPreviewFpsRange(FpsRange);
-
- ImageFormat getPreviewFormat();
- void setPreviewFormat(ImageFormat fmt);
- QList<ImageFormat> getSupportedPreviewFormats();
-
- QSize previewSize() const;
- QSize actualPreviewSize();
- void setPreviewSize(const QSize &size);
- bool setPreviewTexture(AndroidSurfaceTexture *surfaceTexture);
- bool setPreviewDisplay(AndroidSurfaceHolder *surfaceHolder);
- void setDisplayOrientation(int degrees);
-
- bool isZoomSupported();
- int getMaxZoom();
- QList<int> getZoomRatios();
- int getZoom();
- void setZoom(int value);
-
- QStringList getSupportedFlashModes();
- QString getFlashMode();
- void setFlashMode(const QString &value);
-
- QStringList getSupportedFocusModes();
- QString getFocusMode();
- void setFocusMode(const QString &value);
-
- int getMaxNumFocusAreas();
- QList<QRect> getFocusAreas();
- void setFocusAreas(const QList<QRect> &areas);
-
- void autoFocus();
- void cancelAutoFocus();
-
- bool isAutoExposureLockSupported();
- bool getAutoExposureLock();
- void setAutoExposureLock(bool toggle);
-
- bool isAutoWhiteBalanceLockSupported();
- bool getAutoWhiteBalanceLock();
- void setAutoWhiteBalanceLock(bool toggle);
-
- int getExposureCompensation();
- void setExposureCompensation(int value);
- float getExposureCompensationStep();
- int getMinExposureCompensation();
- int getMaxExposureCompensation();
-
- QStringList getSupportedSceneModes();
- QString getSceneMode();
- void setSceneMode(const QString &value);
-
- QStringList getSupportedWhiteBalance();
- QString getWhiteBalance();
- void setWhiteBalance(const QString &value);
-
- void setRotation(int rotation);
- int getRotation() const;
-
- QList<QSize> getSupportedPictureSizes();
- void setPictureSize(const QSize &size);
- void setJpegQuality(int quality);
-
- void startPreview();
- void stopPreview();
- void stopPreviewSynchronous();
-
- void takePicture();
-
- void setupPreviewFrameCallback();
- void notifyNewFrames(bool notify);
- void fetchLastPreviewFrame();
- QJNIObjectPrivate getCameraObject();
-
- static int getNumberOfCameras();
- static void getCameraInfo(int id, AndroidCameraInfo *info);
- static bool requestCameraPermission();
-
- static bool initJNI(JNIEnv *env);
-
-Q_SIGNALS:
- void previewSizeChanged();
- void previewStarted();
- void previewFailedToStart();
- void previewStopped();
-
- void autoFocusStarted();
- void autoFocusComplete(bool success);
-
- void whiteBalanceChanged();
-
- void takePictureFailed();
- void pictureExposed();
- void pictureCaptured(const QByteArray &data);
- void lastPreviewFrameFetched(const QVideoFrame &frame);
- void newPreviewFrame(const QVideoFrame &frame);
-
-private:
- AndroidCamera(AndroidCameraPrivate *d, QThread *worker);
-
- Q_DECLARE_PRIVATE(AndroidCamera)
- AndroidCameraPrivate *d_ptr;
- QScopedPointer<QThread> m_worker;
-};
-
-Q_DECLARE_METATYPE(AndroidCamera::ImageFormat)
-
-QT_END_NAMESPACE
-
-#endif // ANDROIDCAMERA_H
diff --git a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp
deleted file mode 100644
index ce6144167..000000000
--- a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp
+++ /dev/null
@@ -1,193 +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 "androidmediametadataretriever.h"
-
-#include <QtCore/private/qjnihelpers_p.h>
-#include <QtCore/private/qjni_p.h>
-#include <QtCore/QUrl>
-#include <qdebug.h>
-
-QT_BEGIN_NAMESPACE
-
-static bool exceptionCheckAndClear(JNIEnv *env)
-{
- if (Q_UNLIKELY(env->ExceptionCheck())) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif // QT_DEBUG
- env->ExceptionClear();
- return true;
- }
-
- return false;
-}
-
-AndroidMediaMetadataRetriever::AndroidMediaMetadataRetriever()
-{
- m_metadataRetriever = QJNIObjectPrivate("android/media/MediaMetadataRetriever");
-}
-
-AndroidMediaMetadataRetriever::~AndroidMediaMetadataRetriever()
-{
- release();
-}
-
-QString AndroidMediaMetadataRetriever::extractMetadata(MetadataKey key)
-{
- QString value;
-
- QJNIObjectPrivate metadata = m_metadataRetriever.callObjectMethod("extractMetadata",
- "(I)Ljava/lang/String;",
- jint(key));
- if (metadata.isValid())
- value = metadata.toString();
-
- return value;
-}
-
-void AndroidMediaMetadataRetriever::release()
-{
- if (!m_metadataRetriever.isValid())
- return;
-
- m_metadataRetriever.callMethod<void>("release");
-}
-
-bool AndroidMediaMetadataRetriever::setDataSource(const QUrl &url)
-{
- if (!m_metadataRetriever.isValid())
- return false;
-
- QJNIEnvironmentPrivate env;
-
- if (url.isLocalFile()) { // also includes qrc files (copied to a temp file by QMediaPlayer)
- QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.path());
- QJNIObjectPrivate fileInputStream("java/io/FileInputStream",
- "(Ljava/lang/String;)V",
- string.object());
-
- if (exceptionCheckAndClear(env))
- return false;
-
- QJNIObjectPrivate fd = fileInputStream.callObjectMethod("getFD",
- "()Ljava/io/FileDescriptor;");
- if (exceptionCheckAndClear(env)) {
- fileInputStream.callMethod<void>("close");
- exceptionCheckAndClear(env);
- return false;
- }
-
- m_metadataRetriever.callMethod<void>("setDataSource",
- "(Ljava/io/FileDescriptor;)V",
- fd.object());
-
- bool ok = !exceptionCheckAndClear(env);
-
- fileInputStream.callMethod<void>("close");
- exceptionCheckAndClear(env);
-
- if (!ok)
- return false;
- } else if (url.scheme() == QLatin1String("assets")) {
- QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.path().mid(1)); // remove first '/'
- QJNIObjectPrivate activity(QtAndroidPrivate::activity());
- QJNIObjectPrivate assetManager = activity.callObjectMethod("getAssets",
- "()Landroid/content/res/AssetManager;");
- QJNIObjectPrivate assetFd = assetManager.callObjectMethod("openFd",
- "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;",
- string.object());
- if (exceptionCheckAndClear(env))
- return false;
-
- QJNIObjectPrivate fd = assetFd.callObjectMethod("getFileDescriptor",
- "()Ljava/io/FileDescriptor;");
- if (exceptionCheckAndClear(env)) {
- assetFd.callMethod<void>("close");
- exceptionCheckAndClear(env);
- return false;
- }
-
- m_metadataRetriever.callMethod<void>("setDataSource",
- "(Ljava/io/FileDescriptor;JJ)V",
- fd.object(),
- assetFd.callMethod<jlong>("getStartOffset"),
- assetFd.callMethod<jlong>("getLength"));
-
- bool ok = !exceptionCheckAndClear(env);
-
- assetFd.callMethod<void>("close");
- exceptionCheckAndClear(env);
-
- if (!ok)
- return false;
- } else if (QtAndroidPrivate::androidSdkVersion() >= 14) {
- // On API levels >= 14, only setDataSource(String, Map<String, String>) accepts remote media
- QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.toString(QUrl::FullyEncoded));
- QJNIObjectPrivate hash("java/util/HashMap");
-
- m_metadataRetriever.callMethod<void>("setDataSource",
- "(Ljava/lang/String;Ljava/util/Map;)V",
- string.object(),
- hash.object());
- if (exceptionCheckAndClear(env))
- return false;
- } else {
- // While on API levels < 14, only setDataSource(Context, Uri) is available and works for
- // remote media...
- QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.toString(QUrl::FullyEncoded));
- QJNIObjectPrivate uri = m_metadataRetriever.callStaticObjectMethod("android/net/Uri",
- "parse",
- "(Ljava/lang/String;)Landroid/net/Uri;",
- string.object());
- if (exceptionCheckAndClear(env))
- return false;
-
- m_metadataRetriever.callMethod<void>("setDataSource",
- "(Landroid/content/Context;Landroid/net/Uri;)V",
- QtAndroidPrivate::activity(),
- uri.object());
- if (exceptionCheckAndClear(env))
- return false;
- }
-
- return true;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h
deleted file mode 100644
index 1e141813d..000000000
--- a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 ANDROIDMEDIAMETADATARETRIEVER_H
-#define ANDROIDMEDIAMETADATARETRIEVER_H
-
-#include <QtCore/private/qjni_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class AndroidMediaMetadataRetriever
-{
-public:
- enum MetadataKey {
- Album = 1,
- AlbumArtist = 13,
- Artist = 2,
- Author = 3,
- Bitrate = 20,
- CDTrackNumber = 0,
- Compilation = 15,
- Composer = 4,
- Date = 5,
- DiscNumber = 14,
- Duration = 9,
- Genre = 6,
- HasAudio = 16,
- HasVideo = 17,
- Location = 23,
- MimeType = 12,
- NumTracks = 10,
- Title = 7,
- VideoHeight = 19,
- VideoWidth = 18,
- VideoRotation = 24,
- Writer = 11,
- Year = 8
- };
-
- AndroidMediaMetadataRetriever();
- ~AndroidMediaMetadataRetriever();
-
- QString extractMetadata(MetadataKey key);
- bool setDataSource(const QUrl &url);
-
-private:
- void release();
- QJNIObjectPrivate m_metadataRetriever;
-};
-
-QT_END_NAMESPACE
-
-#endif // ANDROIDMEDIAMETADATARETRIEVER_H
diff --git a/src/plugins/android/src/wrappers/jni/androidmediaplayer.cpp b/src/plugins/android/src/wrappers/jni/androidmediaplayer.cpp
deleted file mode 100644
index c94695de4..000000000
--- a/src/plugins/android/src/wrappers/jni/androidmediaplayer.cpp
+++ /dev/null
@@ -1,435 +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 "androidmediaplayer.h"
-
-#include <QString>
-#include <QtCore/private/qjni_p.h>
-#include <QtCore/private/qjnihelpers_p.h>
-#include "androidsurfacetexture.h"
-#include <QList>
-#include <QReadWriteLock>
-
-static const char QtAndroidMediaPlayerClassName[] = "org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer";
-typedef QList<AndroidMediaPlayer *> MediaPlayerList;
-Q_GLOBAL_STATIC(MediaPlayerList, mediaPlayers)
-Q_GLOBAL_STATIC(QReadWriteLock, rwLock)
-
-QT_BEGIN_NAMESPACE
-
-AndroidMediaPlayer::AndroidMediaPlayer()
- : QObject()
-{
- QWriteLocker locker(rwLock);
- auto context = QtAndroidPrivate::activity() ? QtAndroidPrivate::activity() : QtAndroidPrivate::service();
- const jlong id = reinterpret_cast<jlong>(this);
- mMediaPlayer = QJNIObjectPrivate(QtAndroidMediaPlayerClassName,
- "(Landroid/content/Context;J)V",
- context,
- id);
- mediaPlayers->append(this);
-}
-
-AndroidMediaPlayer::~AndroidMediaPlayer()
-{
- QWriteLocker locker(rwLock);
- const int i = mediaPlayers->indexOf(this);
- Q_ASSERT(i != -1);
- mediaPlayers->remove(i);
-}
-
-void AndroidMediaPlayer::release()
-{
- mMediaPlayer.callMethod<void>("release");
-}
-
-void AndroidMediaPlayer::reset()
-{
- mMediaPlayer.callMethod<void>("reset");
-}
-
-int AndroidMediaPlayer::getCurrentPosition()
-{
- return mMediaPlayer.callMethod<jint>("getCurrentPosition");
-}
-
-int AndroidMediaPlayer::getDuration()
-{
- return mMediaPlayer.callMethod<jint>("getDuration");
-}
-
-bool AndroidMediaPlayer::isPlaying()
-{
- return mMediaPlayer.callMethod<jboolean>("isPlaying");
-}
-
-int AndroidMediaPlayer::volume()
-{
- return mMediaPlayer.callMethod<jint>("getVolume");
-}
-
-bool AndroidMediaPlayer::isMuted()
-{
- return mMediaPlayer.callMethod<jboolean>("isMuted");
-}
-
-qreal AndroidMediaPlayer::playbackRate()
-{
- qreal rate(1.0);
-
- if (QtAndroidPrivate::androidSdkVersion() < 23)
- return rate;
-
- QJNIObjectPrivate player = mMediaPlayer.callObjectMethod("getMediaPlayerHandle", "()Landroid/media/MediaPlayer;");
- if (player.isValid()) {
- QJNIObjectPrivate playbackParams = player.callObjectMethod("getPlaybackParams", "()Landroid/media/PlaybackParams;");
- if (playbackParams.isValid()) {
- const qreal speed = playbackParams.callMethod<jfloat>("getSpeed", "()F");
- QJNIEnvironmentPrivate env;
- if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif // QT_DEBUG
- env->ExceptionClear();
- } else {
- rate = speed;
- }
- }
- }
-
- return rate;
-}
-
-jobject AndroidMediaPlayer::display()
-{
- return mMediaPlayer.callObjectMethod("display", "()Landroid/view/SurfaceHolder;").object();
-}
-
-void AndroidMediaPlayer::play()
-{
- mMediaPlayer.callMethod<void>("start");
-}
-
-void AndroidMediaPlayer::pause()
-{
- mMediaPlayer.callMethod<void>("pause");
-}
-
-void AndroidMediaPlayer::stop()
-{
- mMediaPlayer.callMethod<void>("stop");
-}
-
-void AndroidMediaPlayer::seekTo(qint32 msec)
-{
- mMediaPlayer.callMethod<void>("seekTo", "(I)V", jint(msec));
-}
-
-void AndroidMediaPlayer::setMuted(bool mute)
-{
- mMediaPlayer.callMethod<void>("mute", "(Z)V", jboolean(mute));
-}
-
-void AndroidMediaPlayer::setDataSource(const QNetworkRequest &request)
-{
- QJNIObjectPrivate string = QJNIObjectPrivate::fromString(request.url().toString(QUrl::FullyEncoded));
-
- mMediaPlayer.callMethod<void>("initHeaders", "()V");
- for (auto &header : request.rawHeaderList()) {
- auto value = request.rawHeader(header);
- mMediaPlayer.callMethod<void>("setHeader", "(Ljava/lang/String;Ljava/lang/String;)V",
- QJNIObjectPrivate::fromString(header).object(), QJNIObjectPrivate::fromString(value).object());
- }
-
- mMediaPlayer.callMethod<void>("setDataSource", "(Ljava/lang/String;)V", string.object());
-}
-
-void AndroidMediaPlayer::prepareAsync()
-{
- mMediaPlayer.callMethod<void>("prepareAsync");
-}
-
-void AndroidMediaPlayer::setVolume(int volume)
-{
- mMediaPlayer.callMethod<void>("setVolume", "(I)V", jint(volume));
-}
-
-bool AndroidMediaPlayer::setPlaybackRate(qreal rate)
-{
- if (QtAndroidPrivate::androidSdkVersion() < 23) {
- qWarning("Setting the playback rate on a media player requires Android 6.0 (API level 23) or later");
- return false;
- }
-
- QJNIEnvironmentPrivate env;
-
- QJNIObjectPrivate player = mMediaPlayer.callObjectMethod("getMediaPlayerHandle", "()Landroid/media/MediaPlayer;");
- if (player.isValid()) {
- QJNIObjectPrivate 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)));
- player.callMethod<void>("setPlaybackParams", "(Landroid/media/PlaybackParams;)V", playbackParams.object());
- if (Q_UNLIKELY(env->ExceptionCheck())) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif // QT_DEBUG
- env->ExceptionClear();
- qWarning() << "Invalid playback rate" << rate;
- return false;
- } else {
- return true;
- }
- }
- }
-
- return false;
-}
-
-void AndroidMediaPlayer::setDisplay(AndroidSurfaceTexture *surfaceTexture)
-{
- mMediaPlayer.callMethod<void>("setDisplay",
- "(Landroid/view/SurfaceHolder;)V",
- surfaceTexture ? surfaceTexture->surfaceHolder() : 0);
-}
-
-void AndroidMediaPlayer::setAudioRole(QAudio::Role role)
-{
- QString str;
- switch (role) {
- case QAudio::MusicRole:
- str = QLatin1String("CONTENT_TYPE_MUSIC");
- break;
- case QAudio::VideoRole:
- str = QLatin1String("CONTENT_TYPE_MOVIE");
- break;
- case QAudio::VoiceCommunicationRole:
- str = QLatin1String("USAGE_VOICE_COMMUNICATION");
- break;
- case QAudio::AlarmRole:
- str = QLatin1String("USAGE_ALARM");
- break;
- case QAudio::NotificationRole:
- str = QLatin1String("USAGE_NOTIFICATION");
- break;
- case QAudio::RingtoneRole:
- str = QLatin1String("USAGE_NOTIFICATION_RINGTONE");
- break;
- case QAudio::AccessibilityRole:
- str = QLatin1String("USAGE_ASSISTANCE_ACCESSIBILITY");
- break;
- case QAudio::SonificationRole:
- str = QLatin1String("CONTENT_TYPE_SONIFICATION");
- break;
- case QAudio::GameRole:
- str = QLatin1String("USAGE_GAME");
- break;
- default:
- break;
- }
-
- setCustomAudioRole(str);
-}
-
-void AndroidMediaPlayer::setCustomAudioRole(const QString &role)
-{
- QStringList roles = role.split(",", Qt::SkipEmptyParts);
-
- int type = 0; // CONTENT_TYPE_UNKNOWN
- int usage = 0; // USAGE_UNKNOWN
- for (int i = 0; i < qMin(2, roles.size()); ++i) {
- auto r = roles[i];
- if (r == QLatin1String("CONTENT_TYPE_MOVIE"))
- type = 3;
- else if (r == QLatin1String("CONTENT_TYPE_MUSIC"))
- type = 2;
- else if (r == QLatin1String("CONTENT_TYPE_SONIFICATION"))
- type = 4;
- else if (r == QLatin1String("CONTENT_TYPE_SPEECH"))
- type = 1;
- else if (r == QLatin1String("USAGE_ALARM"))
- usage = 4;
- else if (r == QLatin1String("USAGE_ASSISTANCE_ACCESSIBILITY"))
- usage = 11;
- else if (r == QLatin1String("USAGE_ASSISTANCE_NAVIGATION_GUIDANCE"))
- usage = 12;
- else if (r == QLatin1String("USAGE_ASSISTANCE_SONIFICATION"))
- usage = 13;
- else if (r == QLatin1String("USAGE_ASSISTANT"))
- usage = 16;
- else if (r == QLatin1String("USAGE_GAME"))
- usage = 14;
- else if (r == QLatin1String("USAGE_MEDIA"))
- usage = 1;
- else if (r == QLatin1String("USAGE_NOTIFICATION"))
- usage = 5;
- else if (r == QLatin1String("USAGE_NOTIFICATION_COMMUNICATION_DELAYED"))
- usage = 9;
- else if (r == QLatin1String("USAGE_NOTIFICATION_COMMUNICATION_INSTANT"))
- usage = 8;
- else if (r == QLatin1String("USAGE_NOTIFICATION_COMMUNICATION_REQUEST"))
- usage = 7;
- else if (r == QLatin1String("USAGE_NOTIFICATION_EVENT"))
- usage = 10;
- else if (r == QLatin1String("USAGE_NOTIFICATION_RINGTONE"))
- usage = 6;
- else if (r == QLatin1String("USAGE_VOICE_COMMUNICATION"))
- usage = 2;
- else if (r == QLatin1String("USAGE_VOICE_COMMUNICATION_SIGNALLING"))
- usage = 3;
- }
-
- mMediaPlayer.callMethod<void>("setAudioAttributes", "(II)V", jint(type), jint(usage));
-}
-
-static void onErrorNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id)
-{
- Q_UNUSED(env);
- Q_UNUSED(thiz);
- QReadLocker locker(rwLock);
- const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
- if (Q_UNLIKELY(i == -1))
- return;
-
- Q_EMIT (*mediaPlayers)[i]->error(what, extra);
-}
-
-static void onBufferingUpdateNative(JNIEnv *env, jobject thiz, jint percent, jlong id)
-{
- Q_UNUSED(env);
- Q_UNUSED(thiz);
- QReadLocker locker(rwLock);
- const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
- if (Q_UNLIKELY(i == -1))
- return;
-
- Q_EMIT (*mediaPlayers)[i]->bufferingChanged(percent);
-}
-
-static void onProgressUpdateNative(JNIEnv *env, jobject thiz, jint progress, jlong id)
-{
- Q_UNUSED(env);
- Q_UNUSED(thiz);
- QReadLocker locker(rwLock);
- const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
- if (Q_UNLIKELY(i == -1))
- return;
-
- Q_EMIT (*mediaPlayers)[i]->progressChanged(progress);
-}
-
-static void onDurationChangedNative(JNIEnv *env, jobject thiz, jint duration, jlong id)
-{
- Q_UNUSED(env);
- Q_UNUSED(thiz);
- QReadLocker locker(rwLock);
- const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
- if (Q_UNLIKELY(i == -1))
- return;
-
- Q_EMIT (*mediaPlayers)[i]->durationChanged(duration);
-}
-
-static void onInfoNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id)
-{
- Q_UNUSED(env);
- Q_UNUSED(thiz);
- QReadLocker locker(rwLock);
- const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
- if (Q_UNLIKELY(i == -1))
- return;
-
- Q_EMIT (*mediaPlayers)[i]->info(what, extra);
-}
-
-static void onStateChangedNative(JNIEnv *env, jobject thiz, jint state, jlong id)
-{
- Q_UNUSED(env);
- Q_UNUSED(thiz);
- QReadLocker locker(rwLock);
- const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
- if (Q_UNLIKELY(i == -1))
- return;
-
- Q_EMIT (*mediaPlayers)[i]->stateChanged(state);
-}
-
-static void onVideoSizeChangedNative(JNIEnv *env,
- jobject thiz,
- jint width,
- jint height,
- jlong id)
-{
- Q_UNUSED(env);
- Q_UNUSED(thiz);
- QReadLocker locker(rwLock);
- const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
- if (Q_UNLIKELY(i == -1))
- return;
-
- Q_EMIT (*mediaPlayers)[i]->videoSizeChanged(width, height);
-}
-
-bool AndroidMediaPlayer::initJNI(JNIEnv *env)
-{
- jclass clazz = QJNIEnvironmentPrivate::findClass(QtAndroidMediaPlayerClassName,
- env);
-
- static const JNINativeMethod methods[] = {
- {"onErrorNative", "(IIJ)V", reinterpret_cast<void *>(onErrorNative)},
- {"onBufferingUpdateNative", "(IJ)V", reinterpret_cast<void *>(onBufferingUpdateNative)},
- {"onProgressUpdateNative", "(IJ)V", reinterpret_cast<void *>(onProgressUpdateNative)},
- {"onDurationChangedNative", "(IJ)V", reinterpret_cast<void *>(onDurationChangedNative)},
- {"onInfoNative", "(IIJ)V", reinterpret_cast<void *>(onInfoNative)},
- {"onVideoSizeChangedNative", "(IIJ)V", reinterpret_cast<void *>(onVideoSizeChangedNative)},
- {"onStateChangedNative", "(IJ)V", reinterpret_cast<void *>(onStateChangedNative)}
- };
-
- if (clazz && env->RegisterNatives(clazz,
- methods,
- sizeof(methods) / sizeof(methods[0])) != JNI_OK) {
- return false;
- }
-
- return true;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/wrappers/jni/androidmediaplayer.h b/src/plugins/android/src/wrappers/jni/androidmediaplayer.h
deleted file mode 100644
index 37c7456f7..000000000
--- a/src/plugins/android/src/wrappers/jni/androidmediaplayer.h
+++ /dev/null
@@ -1,141 +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 ANDROIDMEDIAPLAYER_H
-#define ANDROIDMEDIAPLAYER_H
-
-#include <QObject>
-#include <QNetworkRequest>
-#include <QtCore/private/qjni_p.h>
-#include <QAudio>
-
-QT_BEGIN_NAMESPACE
-
-class AndroidSurfaceTexture;
-
-class AndroidMediaPlayer : public QObject
-{
- Q_OBJECT
-public:
- AndroidMediaPlayer();
- ~AndroidMediaPlayer();
-
- enum MediaError
- {
- // What
- MEDIA_ERROR_UNKNOWN = 1,
- MEDIA_ERROR_SERVER_DIED = 100,
- MEDIA_ERROR_INVALID_STATE = -38, // Undocumented
- // Extra
- MEDIA_ERROR_IO = -1004,
- MEDIA_ERROR_MALFORMED = -1007,
- MEDIA_ERROR_UNSUPPORTED = -1010,
- MEDIA_ERROR_TIMED_OUT = -110,
- MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200,
- MEDIA_ERROR_BAD_THINGS_ARE_GOING_TO_HAPPEN = -2147483648 // Undocumented
- };
-
- enum MediaInfo
- {
- MEDIA_INFO_UNKNOWN = 1,
- MEDIA_INFO_VIDEO_TRACK_LAGGING = 700,
- MEDIA_INFO_VIDEO_RENDERING_START = 3,
- MEDIA_INFO_BUFFERING_START = 701,
- MEDIA_INFO_BUFFERING_END = 702,
- MEDIA_INFO_BAD_INTERLEAVING = 800,
- MEDIA_INFO_NOT_SEEKABLE = 801,
- MEDIA_INFO_METADATA_UPDATE = 802
- };
-
- enum MediaPlayerState
- {
- Uninitialized = 0x1, /* End */
- Idle = 0x2,
- Preparing = 0x4,
- Prepared = 0x8,
- Initialized = 0x10,
- Started = 0x20,
- Stopped = 0x40,
- Paused = 0x80,
- PlaybackCompleted = 0x100,
- Error = 0x200
- };
-
- void release();
- void reset();
-
- int getCurrentPosition();
- int getDuration();
- bool isPlaying();
- int volume();
- bool isMuted();
- qreal playbackRate();
- jobject display();
-
- void play();
- void pause();
- void stop();
- void seekTo(qint32 msec);
- void setMuted(bool mute);
- void setDataSource(const QNetworkRequest &request);
- void prepareAsync();
- void setVolume(int volume);
- bool setPlaybackRate(qreal rate);
- void setDisplay(AndroidSurfaceTexture *surfaceTexture);
- void setAudioRole(QAudio::Role role);
- void setCustomAudioRole(const QString &role);
-
- static bool initJNI(JNIEnv *env);
-
-Q_SIGNALS:
- void error(qint32 what, qint32 extra);
- void bufferingChanged(qint32 percent);
- void durationChanged(qint64 duration);
- void progressChanged(qint64 progress);
- void stateChanged(qint32 state);
- void info(qint32 what, qint32 extra);
- void videoSizeChanged(qint32 width, qint32 height);
-
-private:
- QJNIObjectPrivate mMediaPlayer;
-};
-
-QT_END_NAMESPACE
-
-#endif // ANDROIDMEDIAPLAYER_H
diff --git a/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp b/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp
deleted file mode 100644
index e5f8846b9..000000000
--- a/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp
+++ /dev/null
@@ -1,405 +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 "androidmediarecorder.h"
-
-#include "androidcamera.h"
-#include "androidsurfacetexture.h"
-#include "androidsurfaceview.h"
-#include "qandroidglobal.h"
-#include "qandroidmultimediautils.h"
-#include <QtCore/private/qjni_p.h>
-#include <qmap.h>
-
-QT_BEGIN_NAMESPACE
-
-typedef QMap<QString, QJNIObjectPrivate> CamcorderProfiles;
-Q_GLOBAL_STATIC(CamcorderProfiles, g_camcorderProfiles)
-
-static QString profileKey()
-{
- return QStringLiteral("%1-%2");
-}
-
-bool AndroidCamcorderProfile::hasProfile(jint cameraId, Quality quality)
-{
- if (g_camcorderProfiles->contains(profileKey().arg(cameraId).arg(quality)))
- return true;
-
- return QJNIObjectPrivate::callStaticMethod<jboolean>("android/media/CamcorderProfile",
- "hasProfile",
- "(II)Z",
- cameraId,
- quality);
-}
-
-AndroidCamcorderProfile AndroidCamcorderProfile::get(jint cameraId, Quality quality)
-{
- const QString key = profileKey().arg(cameraId).arg(quality);
- QMap<QString, QJNIObjectPrivate>::const_iterator it = g_camcorderProfiles->constFind(key);
-
- if (it != g_camcorderProfiles->constEnd())
- return AndroidCamcorderProfile(*it);
-
- QJNIObjectPrivate camProfile = QJNIObjectPrivate::callStaticObjectMethod("android/media/CamcorderProfile",
- "get",
- "(II)Landroid/media/CamcorderProfile;",
- cameraId,
- quality);
-
- return AndroidCamcorderProfile((*g_camcorderProfiles)[key] = camProfile);
-}
-
-int AndroidCamcorderProfile::getValue(AndroidCamcorderProfile::Field field) const
-{
- switch (field) {
- case audioBitRate:
- return m_camcorderProfile.getField<jint>("audioBitRate");
- case audioChannels:
- return m_camcorderProfile.getField<jint>("audioChannels");
- case audioCodec:
- return m_camcorderProfile.getField<jint>("audioCodec");
- case audioSampleRate:
- return m_camcorderProfile.getField<jint>("audioSampleRate");
- case duration:
- return m_camcorderProfile.getField<jint>("duration");
- case fileFormat:
- return m_camcorderProfile.getField<jint>("fileFormat");
- case quality:
- return m_camcorderProfile.getField<jint>("quality");
- case videoBitRate:
- return m_camcorderProfile.getField<jint>("videoBitRate");
- case videoCodec:
- return m_camcorderProfile.getField<jint>("videoCodec");
- case videoFrameHeight:
- return m_camcorderProfile.getField<jint>("videoFrameHeight");
- case videoFrameRate:
- return m_camcorderProfile.getField<jint>("videoFrameRate");
- case videoFrameWidth:
- return m_camcorderProfile.getField<jint>("videoFrameWidth");
- }
-
- return 0;
-}
-
-AndroidCamcorderProfile::AndroidCamcorderProfile(const QJNIObjectPrivate &camcorderProfile)
-{
- m_camcorderProfile = camcorderProfile;
-}
-
-static const char QtMediaRecorderListenerClassName[] = "org/qtproject/qt/android/multimedia/QtMediaRecorderListener";
-typedef QMap<jlong, AndroidMediaRecorder*> MediaRecorderMap;
-Q_GLOBAL_STATIC(MediaRecorderMap, mediaRecorders)
-
-static void notifyError(JNIEnv* , jobject, jlong id, jint what, jint extra)
-{
- AndroidMediaRecorder *obj = mediaRecorders->value(id, 0);
- if (obj)
- emit obj->error(what, extra);
-}
-
-static void notifyInfo(JNIEnv* , jobject, jlong id, jint what, jint extra)
-{
- AndroidMediaRecorder *obj = mediaRecorders->value(id, 0);
- if (obj)
- emit obj->info(what, extra);
-}
-
-AndroidMediaRecorder::AndroidMediaRecorder()
- : QObject()
- , m_id(reinterpret_cast<jlong>(this))
-{
- m_mediaRecorder = QJNIObjectPrivate("android/media/MediaRecorder");
- if (m_mediaRecorder.isValid()) {
- QJNIObjectPrivate listener(QtMediaRecorderListenerClassName, "(J)V", m_id);
- m_mediaRecorder.callMethod<void>("setOnErrorListener",
- "(Landroid/media/MediaRecorder$OnErrorListener;)V",
- listener.object());
- m_mediaRecorder.callMethod<void>("setOnInfoListener",
- "(Landroid/media/MediaRecorder$OnInfoListener;)V",
- listener.object());
- mediaRecorders->insert(m_id, this);
- }
-}
-
-AndroidMediaRecorder::~AndroidMediaRecorder()
-{
- mediaRecorders->remove(m_id);
-}
-
-void AndroidMediaRecorder::release()
-{
- m_mediaRecorder.callMethod<void>("release");
-}
-
-bool AndroidMediaRecorder::prepare()
-{
- QJNIEnvironmentPrivate env;
- m_mediaRecorder.callMethod<void>("prepare");
- if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif
- env->ExceptionClear();
- return false;
- }
- return true;
-}
-
-void AndroidMediaRecorder::reset()
-{
- m_mediaRecorder.callMethod<void>("reset");
-}
-
-bool AndroidMediaRecorder::start()
-{
- QJNIEnvironmentPrivate env;
- m_mediaRecorder.callMethod<void>("start");
- if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif
- env->ExceptionClear();
- return false;
- }
- return true;
-}
-
-void AndroidMediaRecorder::stop()
-{
- QJNIEnvironmentPrivate env;
- m_mediaRecorder.callMethod<void>("stop");
- if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif
- env->ExceptionClear();
- }
-}
-
-void AndroidMediaRecorder::setAudioChannels(int numChannels)
-{
- m_mediaRecorder.callMethod<void>("setAudioChannels", "(I)V", numChannels);
-}
-
-void AndroidMediaRecorder::setAudioEncoder(AudioEncoder encoder)
-{
- QJNIEnvironmentPrivate env;
- m_mediaRecorder.callMethod<void>("setAudioEncoder", "(I)V", int(encoder));
- if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif
- env->ExceptionClear();
- }
-}
-
-void AndroidMediaRecorder::setAudioEncodingBitRate(int bitRate)
-{
- m_mediaRecorder.callMethod<void>("setAudioEncodingBitRate", "(I)V", bitRate);
-}
-
-void AndroidMediaRecorder::setAudioSamplingRate(int samplingRate)
-{
- m_mediaRecorder.callMethod<void>("setAudioSamplingRate", "(I)V", samplingRate);
-}
-
-void AndroidMediaRecorder::setAudioSource(AudioSource source)
-{
- QJNIEnvironmentPrivate env;
- m_mediaRecorder.callMethod<void>("setAudioSource", "(I)V", int(source));
- if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif
- env->ExceptionClear();
- }
-}
-
-void AndroidMediaRecorder::setCamera(AndroidCamera *camera)
-{
- QJNIObjectPrivate cam = camera->getCameraObject();
- m_mediaRecorder.callMethod<void>("setCamera", "(Landroid/hardware/Camera;)V", cam.object());
-}
-
-void AndroidMediaRecorder::setVideoEncoder(VideoEncoder encoder)
-{
- QJNIEnvironmentPrivate env;
- m_mediaRecorder.callMethod<void>("setVideoEncoder", "(I)V", int(encoder));
- if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif
- env->ExceptionClear();
- }
-}
-
-void AndroidMediaRecorder::setVideoEncodingBitRate(int bitRate)
-{
- m_mediaRecorder.callMethod<void>("setVideoEncodingBitRate", "(I)V", bitRate);
-}
-
-void AndroidMediaRecorder::setVideoFrameRate(int rate)
-{
- QJNIEnvironmentPrivate env;
- m_mediaRecorder.callMethod<void>("setVideoFrameRate", "(I)V", rate);
- if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif
- env->ExceptionClear();
- }
-}
-
-void AndroidMediaRecorder::setVideoSize(const QSize &size)
-{
- QJNIEnvironmentPrivate env;
- m_mediaRecorder.callMethod<void>("setVideoSize", "(II)V", size.width(), size.height());
- if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif
- env->ExceptionClear();
- }
-}
-
-void AndroidMediaRecorder::setVideoSource(VideoSource source)
-{
- QJNIEnvironmentPrivate env;
- m_mediaRecorder.callMethod<void>("setVideoSource", "(I)V", int(source));
- if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif
- env->ExceptionClear();
- }
-}
-
-void AndroidMediaRecorder::setOrientationHint(int degrees)
-{
- QJNIEnvironmentPrivate env;
- m_mediaRecorder.callMethod<void>("setOrientationHint", "(I)V", degrees);
- if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif
- env->ExceptionClear();
- }
-}
-
-void AndroidMediaRecorder::setOutputFormat(OutputFormat format)
-{
- QJNIEnvironmentPrivate env;
- m_mediaRecorder.callMethod<void>("setOutputFormat", "(I)V", int(format));
- if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif
- env->ExceptionClear();
- }
-}
-
-void AndroidMediaRecorder::setOutputFile(const QString &path)
-{
- QJNIEnvironmentPrivate env;
- m_mediaRecorder.callMethod<void>("setOutputFile",
- "(Ljava/lang/String;)V",
- QJNIObjectPrivate::fromString(path).object());
- if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif
- env->ExceptionClear();
- }
-}
-
-void AndroidMediaRecorder::setSurfaceTexture(AndroidSurfaceTexture *texture)
-{
- QJNIEnvironmentPrivate env;
- m_mediaRecorder.callMethod<void>("setPreviewDisplay",
- "(Landroid/view/Surface;)V",
- texture->surface());
- if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif
- env->ExceptionClear();
- }
-}
-
-void AndroidMediaRecorder::setSurfaceHolder(AndroidSurfaceHolder *holder)
-{
- QJNIEnvironmentPrivate env;
- QJNIObjectPrivate surfaceHolder(holder->surfaceHolder());
- QJNIObjectPrivate surface = surfaceHolder.callObjectMethod("getSurface",
- "()Landroid/view/Surface;");
- if (!surface.isValid())
- return;
-
- m_mediaRecorder.callMethod<void>("setPreviewDisplay",
- "(Landroid/view/Surface;)V",
- surface.object());
- if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif
- env->ExceptionClear();
- }
-}
-
-bool AndroidMediaRecorder::initJNI(JNIEnv *env)
-{
- jclass clazz = QJNIEnvironmentPrivate::findClass(QtMediaRecorderListenerClassName,
- env);
-
- static const JNINativeMethod methods[] = {
- {"notifyError", "(JII)V", (void *)notifyError},
- {"notifyInfo", "(JII)V", (void *)notifyInfo}
- };
-
- if (clazz && env->RegisterNatives(clazz,
- methods,
- sizeof(methods) / sizeof(methods[0])) != JNI_OK) {
- return false;
- }
-
- return true;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/wrappers/jni/androidmediarecorder.h b/src/plugins/android/src/wrappers/jni/androidmediarecorder.h
deleted file mode 100644
index e4b3a80ea..000000000
--- a/src/plugins/android/src/wrappers/jni/androidmediarecorder.h
+++ /dev/null
@@ -1,176 +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 ANDROIDMEDIARECORDER_H
-#define ANDROIDMEDIARECORDER_H
-
-#include <qobject.h>
-#include <QtCore/private/qjni_p.h>
-#include <qsize.h>
-
-QT_BEGIN_NAMESPACE
-
-class AndroidCamera;
-class AndroidSurfaceTexture;
-class AndroidSurfaceHolder;
-
-class AndroidCamcorderProfile
-{
-public:
- enum Quality { // Needs to match CamcorderProfile
- QUALITY_LOW,
- QUALITY_HIGH,
- QUALITY_QCIF,
- QUALITY_CIF,
- QUALITY_480P,
- QUALITY_720P,
- QUALITY_1080P,
- QUALITY_QVGA
- };
-
- enum Field {
- audioBitRate,
- audioChannels,
- audioCodec,
- audioSampleRate,
- duration,
- fileFormat,
- quality,
- videoBitRate,
- videoCodec,
- videoFrameHeight,
- videoFrameRate,
- videoFrameWidth
- };
-
- static bool hasProfile(jint cameraId, Quality quality);
- static AndroidCamcorderProfile get(jint cameraId, Quality quality);
- int getValue(Field field) const;
-
-private:
- AndroidCamcorderProfile(const QJNIObjectPrivate &camcorderProfile);
- QJNIObjectPrivate m_camcorderProfile;
-};
-
-class AndroidMediaRecorder : public QObject
-{
- Q_OBJECT
-public:
- enum AudioEncoder {
- DefaultAudioEncoder = 0,
- AMR_NB_Encoder = 1,
- AMR_WB_Encoder = 2,
- AAC = 3
- };
-
- enum AudioSource {
- DefaultAudioSource = 0,
- Mic = 1,
- VoiceUplink = 2,
- VoiceDownlink = 3,
- VoiceCall = 4,
- Camcorder = 5,
- VoiceRecognition = 6
- };
-
- enum VideoEncoder {
- DefaultVideoEncoder = 0,
- H263 = 1,
- H264 = 2,
- MPEG_4_SP = 3
- };
-
- enum VideoSource {
- DefaultVideoSource = 0,
- Camera = 1
- };
-
- enum OutputFormat {
- DefaultOutputFormat = 0,
- THREE_GPP = 1,
- MPEG_4 = 2,
- AMR_NB_Format = 3,
- AMR_WB_Format = 4
- };
-
- AndroidMediaRecorder();
- ~AndroidMediaRecorder();
-
- void release();
- bool prepare();
- void reset();
-
- bool start();
- void stop();
-
- void setAudioChannels(int numChannels);
- void setAudioEncoder(AudioEncoder encoder);
- void setAudioEncodingBitRate(int bitRate);
- void setAudioSamplingRate(int samplingRate);
- void setAudioSource(AudioSource source);
-
- void setCamera(AndroidCamera *camera);
- void setVideoEncoder(VideoEncoder encoder);
- void setVideoEncodingBitRate(int bitRate);
- void setVideoFrameRate(int rate);
- void setVideoSize(const QSize &size);
- void setVideoSource(VideoSource source);
-
- void setOrientationHint(int degrees);
-
- void setOutputFormat(OutputFormat format);
- void setOutputFile(const QString &path);
-
- void setSurfaceTexture(AndroidSurfaceTexture *texture);
- void setSurfaceHolder(AndroidSurfaceHolder *holder);
-
- static bool initJNI(JNIEnv *env);
-
-Q_SIGNALS:
- void error(int what, int extra);
- void info(int what, int extra);
-
-private:
- jlong m_id;
- QJNIObjectPrivate m_mediaRecorder;
-};
-
-QT_END_NAMESPACE
-
-#endif // ANDROIDMEDIARECORDER_H
diff --git a/src/plugins/android/src/wrappers/jni/androidmultimediautils.cpp b/src/plugins/android/src/wrappers/jni/androidmultimediautils.cpp
deleted file mode 100644
index 19dfbd7d2..000000000
--- a/src/plugins/android/src/wrappers/jni/androidmultimediautils.cpp
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "androidmultimediautils.h"
-
-#include <QtCore/private/qjni_p.h>
-
-QT_BEGIN_NAMESPACE
-
-
-void AndroidMultimediaUtils::enableOrientationListener(bool enable)
-{
- QJNIObjectPrivate::callStaticMethod<void>("org/qtproject/qt/android/multimedia/QtMultimediaUtils",
- "enableOrientationListener",
- "(Z)V",
- enable);
-}
-
-int AndroidMultimediaUtils::getDeviceOrientation()
-{
- return QJNIObjectPrivate::callStaticMethod<jint>("org/qtproject/qt/android/multimedia/QtMultimediaUtils",
- "getDeviceOrientation");
-}
-
-QString AndroidMultimediaUtils::getDefaultMediaDirectory(MediaType type)
-{
- QJNIObjectPrivate path = QJNIObjectPrivate::callStaticObjectMethod("org/qtproject/qt/android/multimedia/QtMultimediaUtils",
- "getDefaultMediaDirectory",
- "(I)Ljava/lang/String;",
- jint(type));
- return path.toString();
-}
-
-void AndroidMultimediaUtils::registerMediaFile(const QString &file)
-{
- QJNIObjectPrivate::callStaticMethod<void>("org/qtproject/qt/android/multimedia/QtMultimediaUtils",
- "registerMediaFile",
- "(Ljava/lang/String;)V",
- QJNIObjectPrivate::fromString(file).object());
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/wrappers/jni/androidmultimediautils.h b/src/plugins/android/src/wrappers/jni/androidmultimediautils.h
deleted file mode 100644
index 152d849e4..000000000
--- a/src/plugins/android/src/wrappers/jni/androidmultimediautils.h
+++ /dev/null
@@ -1,66 +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 ANDROIDMULTIMEDIAUTILS_H
-#define ANDROIDMULTIMEDIAUTILS_H
-
-#include <qobject.h>
-#include <QtCore/private/qjni_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class AndroidMultimediaUtils
-{
-public:
- enum MediaType {
- Music = 0,
- Movies = 1,
- DCIM = 2,
- Sounds = 3
- };
-
- static void enableOrientationListener(bool enable);
- static int getDeviceOrientation();
- static QString getDefaultMediaDirectory(MediaType type);
- static void registerMediaFile(const QString &file);
-};
-
-QT_END_NAMESPACE
-
-#endif // ANDROIDMULTIMEDIAUTILS_H
diff --git a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp b/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp
deleted file mode 100644
index c5b670142..000000000
--- a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp
+++ /dev/null
@@ -1,211 +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 "androidsurfacetexture.h"
-#include <QtCore/private/qjni_p.h>
-#include <QtCore/private/qjnihelpers_p.h>
-#include <QtCore/qmutex.h>
-
-QT_BEGIN_NAMESPACE
-
-static const char QtSurfaceTextureListenerClassName[] = "org/qtproject/qt/android/multimedia/QtSurfaceTextureListener";
-typedef QList<jlong> SurfaceTextures;
-Q_GLOBAL_STATIC(SurfaceTextures, g_surfaceTextures);
-Q_GLOBAL_STATIC(QMutex, g_textureMutex);
-
-// native method for QtSurfaceTexture.java
-static void notifyFrameAvailable(JNIEnv* , jobject, jlong id)
-{
- const QMutexLocker lock(g_textureMutex);
- const int idx = g_surfaceTextures->indexOf(id);
- if (idx == -1)
- return;
-
- AndroidSurfaceTexture *obj = reinterpret_cast<AndroidSurfaceTexture *>(g_surfaceTextures->at(idx));
- if (obj)
- Q_EMIT obj->frameAvailable();
-}
-
-AndroidSurfaceTexture::AndroidSurfaceTexture(quint32 texName)
- : QObject()
-{
- Q_STATIC_ASSERT(sizeof (jlong) >= sizeof (void *));
- // API level 11 or higher is required
- if (QtAndroidPrivate::androidSdkVersion() < 11) {
- qWarning("Camera preview and video playback require Android 3.0 (API level 11) or later.");
- return;
- }
-
- QJNIEnvironmentPrivate env;
- m_surfaceTexture = QJNIObjectPrivate("android/graphics/SurfaceTexture", "(I)V", jint(texName));
- if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif // QT_DEBUG
- env->ExceptionClear();
- }
-
- if (!m_surfaceTexture.isValid())
- return;
-
- const QMutexLocker lock(g_textureMutex);
- g_surfaceTextures->append(jlong(this));
- QJNIObjectPrivate listener(QtSurfaceTextureListenerClassName, "(J)V", jlong(this));
- setOnFrameAvailableListener(listener);
-}
-
-AndroidSurfaceTexture::~AndroidSurfaceTexture()
-{
- if (QtAndroidPrivate::androidSdkVersion() > 13 && m_surface.isValid())
- m_surface.callMethod<void>("release");
-
- if (m_surfaceTexture.isValid()) {
- release();
- const QMutexLocker lock(g_textureMutex);
- const int idx = g_surfaceTextures->indexOf(jlong(this));
- if (idx != -1)
- g_surfaceTextures->remove(idx);
- }
-}
-
-QMatrix4x4 AndroidSurfaceTexture::getTransformMatrix()
-{
- QMatrix4x4 matrix;
- if (!m_surfaceTexture.isValid())
- return matrix;
-
- QJNIEnvironmentPrivate env;
-
- jfloatArray array = env->NewFloatArray(16);
- m_surfaceTexture.callMethod<void>("getTransformMatrix", "([F)V", array);
- env->GetFloatArrayRegion(array, 0, 16, matrix.data());
- env->DeleteLocalRef(array);
-
- return matrix;
-}
-
-void AndroidSurfaceTexture::release()
-{
- if (QtAndroidPrivate::androidSdkVersion() < 14)
- return;
-
- m_surfaceTexture.callMethod<void>("release");
-}
-
-void AndroidSurfaceTexture::updateTexImage()
-{
- if (!m_surfaceTexture.isValid())
- return;
-
- m_surfaceTexture.callMethod<void>("updateTexImage");
-}
-
-jobject AndroidSurfaceTexture::surfaceTexture()
-{
- return m_surfaceTexture.object();
-}
-
-jobject AndroidSurfaceTexture::surface()
-{
- if (!m_surface.isValid()) {
- m_surface = QJNIObjectPrivate("android/view/Surface",
- "(Landroid/graphics/SurfaceTexture;)V",
- m_surfaceTexture.object());
- }
-
- return m_surface.object();
-}
-
-jobject AndroidSurfaceTexture::surfaceHolder()
-{
- if (!m_surfaceHolder.isValid()) {
- m_surfaceHolder = QJNIObjectPrivate("org/qtproject/qt/android/multimedia/QtSurfaceTextureHolder",
- "(Landroid/view/Surface;)V",
- surface());
- }
-
- return m_surfaceHolder.object();
-}
-
-void AndroidSurfaceTexture::attachToGLContext(quint32 texName)
-{
- if (QtAndroidPrivate::androidSdkVersion() < 16 || !m_surfaceTexture.isValid())
- return;
-
- m_surfaceTexture.callMethod<void>("attachToGLContext", "(I)V", texName);
-}
-
-void AndroidSurfaceTexture::detachFromGLContext()
-{
- if (QtAndroidPrivate::androidSdkVersion() < 16 || !m_surfaceTexture.isValid())
- return;
-
- m_surfaceTexture.callMethod<void>("detachFromGLContext");
-}
-
-bool AndroidSurfaceTexture::initJNI(JNIEnv *env)
-{
- // SurfaceTexture is available since API 11.
- if (QtAndroidPrivate::androidSdkVersion() < 11)
- return false;
-
- jclass clazz = QJNIEnvironmentPrivate::findClass(QtSurfaceTextureListenerClassName,
- env);
-
- static const JNINativeMethod methods[] = {
- {"notifyFrameAvailable", "(J)V", (void *)notifyFrameAvailable}
- };
-
- if (clazz && env->RegisterNatives(clazz,
- methods,
- sizeof(methods) / sizeof(methods[0])) != JNI_OK) {
- return false;
- }
-
- return true;
-}
-
-void AndroidSurfaceTexture::setOnFrameAvailableListener(const QJNIObjectPrivate &listener)
-{
- m_surfaceTexture.callMethod<void>("setOnFrameAvailableListener",
- "(Landroid/graphics/SurfaceTexture$OnFrameAvailableListener;)V",
- listener.object());
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h b/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h
deleted file mode 100644
index 911711774..000000000
--- a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 ANDROIDSURFACETEXTURE_H
-#define ANDROIDSURFACETEXTURE_H
-
-#include <qobject.h>
-#include <QtCore/private/qjni_p.h>
-
-#include <QMatrix4x4>
-
-QT_BEGIN_NAMESPACE
-
-class AndroidSurfaceTexture : public QObject
-{
- Q_OBJECT
-public:
- explicit AndroidSurfaceTexture(quint32 texName);
- ~AndroidSurfaceTexture();
-
- jobject surfaceTexture();
- jobject surface();
- jobject surfaceHolder();
- inline bool isValid() const { return m_surfaceTexture.isValid(); }
-
- QMatrix4x4 getTransformMatrix();
- void release(); // API level 14
- void updateTexImage();
-
- void attachToGLContext(quint32 texName); // API level 16
- void detachFromGLContext(); // API level 16
-
- static bool initJNI(JNIEnv *env);
-
-Q_SIGNALS:
- void frameAvailable();
-
-private:
- void setOnFrameAvailableListener(const QJNIObjectPrivate &listener);
-
- QJNIObjectPrivate m_surfaceTexture;
- QJNIObjectPrivate m_surface;
- QJNIObjectPrivate m_surfaceHolder;
-};
-
-QT_END_NAMESPACE
-
-#endif // ANDROIDSURFACETEXTURE_H
diff --git a/src/plugins/android/src/wrappers/jni/androidsurfaceview.cpp b/src/plugins/android/src/wrappers/jni/androidsurfaceview.cpp
deleted file mode 100644
index d7587f479..000000000
--- a/src/plugins/android/src/wrappers/jni/androidsurfaceview.cpp
+++ /dev/null
@@ -1,195 +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 "androidsurfaceview.h"
-
-#include <QtCore/private/qjnihelpers_p.h>
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qlist.h>
-#include <QtCore/qmutex.h>
-#include <QtGui/qwindow.h>
-
-QT_BEGIN_NAMESPACE
-
-static const char QtSurfaceHolderCallbackClassName[] = "org/qtproject/qt/android/multimedia/QtSurfaceHolderCallback";
-typedef QList<AndroidSurfaceHolder *> SurfaceHolders;
-Q_GLOBAL_STATIC(SurfaceHolders, surfaceHolders)
-Q_GLOBAL_STATIC(QMutex, shLock)
-
-AndroidSurfaceHolder::AndroidSurfaceHolder(QJNIObjectPrivate object)
- : m_surfaceHolder(object)
- , m_surfaceCreated(false)
-{
- if (!m_surfaceHolder.isValid())
- return;
-
- {
- QMutexLocker locker(shLock);
- surfaceHolders->append(this);
- }
-
- QJNIObjectPrivate callback(QtSurfaceHolderCallbackClassName, "(J)V", reinterpret_cast<jlong>(this));
- m_surfaceHolder.callMethod<void>("addCallback",
- "(Landroid/view/SurfaceHolder$Callback;)V",
- callback.object());
-}
-
-AndroidSurfaceHolder::~AndroidSurfaceHolder()
-{
- QMutexLocker locker(shLock);
- const int i = surfaceHolders->indexOf(this);
- if (Q_UNLIKELY(i == -1))
- return;
-
- surfaceHolders->remove(i);
-}
-
-jobject AndroidSurfaceHolder::surfaceHolder() const
-{
- return m_surfaceHolder.object();
-}
-
-bool AndroidSurfaceHolder::isSurfaceCreated() const
-{
- QMutexLocker locker(shLock);
- return m_surfaceCreated;
-}
-
-void AndroidSurfaceHolder::handleSurfaceCreated(JNIEnv*, jobject, jlong id)
-{
- QMutexLocker locker(shLock);
- const int i = surfaceHolders->indexOf(reinterpret_cast<AndroidSurfaceHolder *>(id));
- if (Q_UNLIKELY(i == -1))
- return;
-
- (*surfaceHolders)[i]->m_surfaceCreated = true;
- Q_EMIT (*surfaceHolders)[i]->surfaceCreated();
-}
-
-void AndroidSurfaceHolder::handleSurfaceDestroyed(JNIEnv*, jobject, jlong id)
-{
- QMutexLocker locker(shLock);
- const int i = surfaceHolders->indexOf(reinterpret_cast<AndroidSurfaceHolder *>(id));
- if (Q_UNLIKELY(i == -1))
- return;
-
- (*surfaceHolders)[i]->m_surfaceCreated = false;
-}
-
-bool AndroidSurfaceHolder::initJNI(JNIEnv *env)
-{
- jclass clazz = QJNIEnvironmentPrivate::findClass(QtSurfaceHolderCallbackClassName,
- env);
-
- static const JNINativeMethod methods[] = {
- {"notifySurfaceCreated", "(J)V", (void *)AndroidSurfaceHolder::handleSurfaceCreated},
- {"notifySurfaceDestroyed", "(J)V", (void *)AndroidSurfaceHolder::handleSurfaceDestroyed}
- };
-
- if (clazz && env->RegisterNatives(clazz,
- methods,
- sizeof(methods) / sizeof(methods[0])) != JNI_OK) {
- return false;
- }
-
- return true;
-}
-
-AndroidSurfaceView::AndroidSurfaceView()
- : m_window(0)
- , m_surfaceHolder(0)
- , m_pendingVisible(-1)
-{
- QtAndroidPrivate::runOnAndroidThreadSync([this] {
- m_surfaceView = QJNIObjectPrivate("android/view/SurfaceView",
- "(Landroid/content/Context;)V",
- QtAndroidPrivate::activity());
- }, QJNIEnvironmentPrivate());
-
- Q_ASSERT(m_surfaceView.isValid());
-
- QJNIObjectPrivate holder = m_surfaceView.callObjectMethod("getHolder",
- "()Landroid/view/SurfaceHolder;");
- if (!holder.isValid()) {
- m_surfaceView = QJNIObjectPrivate();
- } else {
- m_surfaceHolder = new AndroidSurfaceHolder(holder);
- connect(m_surfaceHolder, &AndroidSurfaceHolder::surfaceCreated,
- this, &AndroidSurfaceView::surfaceCreated);
- { // Lock now to avoid a race with handleSurfaceCreated()
- QMutexLocker locker(shLock);
- m_window = QWindow::fromWinId(WId(m_surfaceView.object()));
-
- if (m_pendingVisible != -1)
- m_window->setVisible(m_pendingVisible);
- if (m_pendingGeometry.isValid())
- m_window->setGeometry(m_pendingGeometry);
- }
- }
-}
-
-AndroidSurfaceView::~AndroidSurfaceView()
-{
- delete m_surfaceHolder;
- delete m_window;
-}
-
-AndroidSurfaceHolder *AndroidSurfaceView::holder() const
-{
- return m_surfaceHolder;
-}
-
-void AndroidSurfaceView::setVisible(bool v)
-{
- if (m_window)
- m_window->setVisible(v);
- else
- m_pendingVisible = int(v);
-}
-
-void AndroidSurfaceView::setGeometry(int x, int y, int width, int height)
-{
- if (m_window)
- m_window->setGeometry(x, y, width, height);
- else
- m_pendingGeometry = QRect(x, y, width, height);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/android/src/wrappers/jni/androidsurfaceview.h b/src/plugins/android/src/wrappers/jni/androidsurfaceview.h
deleted file mode 100644
index ef603061d..000000000
--- a/src/plugins/android/src/wrappers/jni/androidsurfaceview.h
+++ /dev/null
@@ -1,102 +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 ANDROIDSURFACEVIEW_H
-#define ANDROIDSURFACEVIEW_H
-
-#include <QtCore/private/qjni_p.h>
-#include <qrect.h>
-#include <QtCore/qrunnable.h>
-
-QT_BEGIN_NAMESPACE
-
-class QWindow;
-
-class AndroidSurfaceHolder : public QObject
-{
- Q_OBJECT
-public:
- ~AndroidSurfaceHolder();
-
- jobject surfaceHolder() const;
- bool isSurfaceCreated() const;
-
- static bool initJNI(JNIEnv *env);
-
-Q_SIGNALS:
- void surfaceCreated();
-
-private:
- AndroidSurfaceHolder(QJNIObjectPrivate object);
-
- static void handleSurfaceCreated(JNIEnv*, jobject, jlong id);
- static void handleSurfaceDestroyed(JNIEnv*, jobject, jlong id);
-
- QJNIObjectPrivate m_surfaceHolder;
- bool m_surfaceCreated;
-
- friend class AndroidSurfaceView;
-};
-
-class AndroidSurfaceView : public QObject
-{
- Q_OBJECT
-public:
- AndroidSurfaceView();
- ~AndroidSurfaceView();
-
- AndroidSurfaceHolder *holder() const;
-
- void setVisible(bool v);
- void setGeometry(int x, int y, int width, int height);
-
-Q_SIGNALS:
- void surfaceCreated();
-
-private:
- QJNIObjectPrivate m_surfaceView;
- QWindow *m_window;
- AndroidSurfaceHolder *m_surfaceHolder;
- int m_pendingVisible;
- QRect m_pendingGeometry;
-};
-
-QT_END_NAMESPACE
-
-#endif // ANDROIDSURFACEVIEW_H
diff --git a/src/plugins/android/src/wrappers/jni/jni.pri b/src/plugins/android/src/wrappers/jni/jni.pri
deleted file mode 100644
index 930d7e922..000000000
--- a/src/plugins/android/src/wrappers/jni/jni.pri
+++ /dev/null
@@ -1,21 +0,0 @@
-QT += core-private
-
-INCLUDEPATH += $$PWD
-
-HEADERS += \
- $$PWD/androidmediaplayer.h \
- $$PWD/androidsurfacetexture.h \
- $$PWD/androidmediametadataretriever.h \
- $$PWD/androidcamera.h \
- $$PWD/androidmultimediautils.h \
- $$PWD/androidmediarecorder.h \
- $$PWD/androidsurfaceview.h
-
-SOURCES += \
- $$PWD/androidmediaplayer.cpp \
- $$PWD/androidsurfacetexture.cpp \
- $$PWD/androidmediametadataretriever.cpp \
- $$PWD/androidcamera.cpp \
- $$PWD/androidmultimediautils.cpp \
- $$PWD/androidmediarecorder.cpp \
- $$PWD/androidsurfaceview.cpp
diff --git a/src/plugins/audiocapture/audiocapture.json b/src/plugins/audiocapture/audiocapture.json
deleted file mode 100644
index 4c7673da2..000000000
--- a/src/plugins/audiocapture/audiocapture.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Keys": ["audiocapture"],
- "Services": ["org.qt-project.qt.audiosource"]
-}
diff --git a/src/plugins/audiocapture/audiocapture.pro b/src/plugins/audiocapture/audiocapture.pro
deleted file mode 100644
index ba2e5c802..000000000
--- a/src/plugins/audiocapture/audiocapture.pro
+++ /dev/null
@@ -1,27 +0,0 @@
-TARGET = qtmedia_audioengine
-QT += multimedia-private
-
-HEADERS += audioencodercontrol.h \
- audiocontainercontrol.h \
- audiomediarecordercontrol.h \
- audioinputselector.h \
- audiocaptureservice.h \
- audiocaptureserviceplugin.h \
- audiocapturesession.h \
- audiocaptureprobecontrol.h
-
-SOURCES += audioencodercontrol.cpp \
- audiocontainercontrol.cpp \
- audiomediarecordercontrol.cpp \
- audioinputselector.cpp \
- audiocaptureservice.cpp \
- audiocaptureserviceplugin.cpp \
- audiocapturesession.cpp \
- audiocaptureprobecontrol.cpp
-
-OTHER_FILES += \
- audiocapture.json
-
-PLUGIN_TYPE = mediaservice
-PLUGIN_CLASS_NAME = AudioCaptureServicePlugin
-load(qt_plugin)
diff --git a/src/plugins/audiocapture/audiocaptureprobecontrol.cpp b/src/plugins/audiocapture/audiocaptureprobecontrol.cpp
deleted file mode 100644
index dbd0da0a1..000000000
--- a/src/plugins/audiocapture/audiocaptureprobecontrol.cpp
+++ /dev/null
@@ -1,62 +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 "audiocaptureprobecontrol.h"
-
-QT_BEGIN_NAMESPACE
-
-AudioCaptureProbeControl::AudioCaptureProbeControl(QObject *parent):
- QMediaAudioProbeControl(parent)
-{
-}
-
-AudioCaptureProbeControl::~AudioCaptureProbeControl()
-{
-}
-
-void AudioCaptureProbeControl::bufferProbed(const char *data, quint32 size, const QAudioFormat& format)
-{
- if (!format.isValid())
- return;
-
- QAudioBuffer audioBuffer = QAudioBuffer(QByteArray::fromRawData(data, size), format);
- QMetaObject::invokeMethod(this, "audioBufferProbed", Qt::QueuedConnection, Q_ARG(QAudioBuffer, audioBuffer));
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/audiocapture/audiocaptureprobecontrol.h b/src/plugins/audiocapture/audiocaptureprobecontrol.h
deleted file mode 100644
index 00a21f031..000000000
--- a/src/plugins/audiocapture/audiocaptureprobecontrol.h
+++ /dev/null
@@ -1,61 +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 AUDIOCAPTUREPROBECONTROL_H
-#define AUDIOCAPTUREPROBECONTROL_H
-
-#include <qmediaaudioprobecontrol.h>
-#include <QtCore/qmutex.h>
-#include <qaudiobuffer.h>
-
-QT_BEGIN_NAMESPACE
-
-class AudioCaptureProbeControl : public QMediaAudioProbeControl
-{
- Q_OBJECT
-public:
- explicit AudioCaptureProbeControl(QObject *parent);
- virtual ~AudioCaptureProbeControl();
-
- void bufferProbed(const char *data, quint32 size, const QAudioFormat& format);
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/audiocapture/audiocaptureservice.cpp b/src/plugins/audiocapture/audiocaptureservice.cpp
deleted file mode 100644
index f06871519..000000000
--- a/src/plugins/audiocapture/audiocaptureservice.cpp
+++ /dev/null
@@ -1,97 +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 "audiocaptureservice.h"
-#include "audiocapturesession.h"
-#include "audioinputselector.h"
-#include "audioencodercontrol.h"
-#include "audiocontainercontrol.h"
-#include "audiomediarecordercontrol.h"
-#include "audiocaptureprobecontrol.h"
-
-QT_BEGIN_NAMESPACE
-
-AudioCaptureService::AudioCaptureService(QObject *parent):
- QMediaService(parent)
-{
- m_session = new AudioCaptureSession(this);
- m_encoderControl = new AudioEncoderControl(m_session);
- m_containerControl = new AudioContainerControl(m_session);
- m_mediaControl = new AudioMediaRecorderControl(m_session);
- m_inputSelector = new AudioInputSelector(m_session);
-}
-
-AudioCaptureService::~AudioCaptureService()
-{
- delete m_encoderControl;
- delete m_containerControl;
- delete m_inputSelector;
- delete m_mediaControl;
- delete m_session;
-}
-
-QMediaControl *AudioCaptureService::requestControl(const char *name)
-{
- if (qstrcmp(name,QMediaRecorderControl_iid) == 0)
- return m_mediaControl;
-
- if (qstrcmp(name,QAudioEncoderSettingsControl_iid) == 0)
- return m_encoderControl;
-
- if (qstrcmp(name,QAudioInputSelectorControl_iid) == 0)
- return m_inputSelector;
-
- if (qstrcmp(name,QMediaContainerControl_iid) == 0)
- return m_containerControl;
-
- if (qstrcmp(name,QMediaAudioProbeControl_iid) == 0) {
- AudioCaptureProbeControl *probe = new AudioCaptureProbeControl(this);
- m_session->addProbe(probe);
- return probe;
- }
-
- return 0;
-}
-
-void AudioCaptureService::releaseControl(QMediaControl *control)
-{
- Q_UNUSED(control);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/audiocapture/audiocaptureservice.h b/src/plugins/audiocapture/audiocaptureservice.h
deleted file mode 100644
index fa2a35feb..000000000
--- a/src/plugins/audiocapture/audiocaptureservice.h
+++ /dev/null
@@ -1,74 +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 AUDIOCAPTURESERVICE_H
-#define AUDIOCAPTURESERVICE_H
-
-#include <QtCore/qobject.h>
-
-#include "qmediaservice.h"
-
-QT_BEGIN_NAMESPACE
-
-class AudioCaptureSession;
-class AudioEncoderControl;
-class AudioContainerControl;
-class AudioMediaRecorderControl;
-class AudioInputSelector;
-
-class AudioCaptureService : public QMediaService
-{
- Q_OBJECT
-public:
- AudioCaptureService(QObject *parent = 0);
- ~AudioCaptureService();
-
- QMediaControl *requestControl(const char *interface);
- void releaseControl(QMediaControl *control);
-private:
- AudioCaptureSession *m_session;
- AudioEncoderControl *m_encoderControl;
- AudioContainerControl *m_containerControl;
- AudioInputSelector *m_inputSelector;
- AudioMediaRecorderControl *m_mediaControl;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/audiocapture/audiocaptureserviceplugin.cpp b/src/plugins/audiocapture/audiocaptureserviceplugin.cpp
deleted file mode 100644
index f45e4b7c5..000000000
--- a/src/plugins/audiocapture/audiocaptureserviceplugin.cpp
+++ /dev/null
@@ -1,62 +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 <QtCore/qstring.h>
-
-#include "audiocaptureserviceplugin.h"
-#include "audiocaptureservice.h"
-
-#include "qmediaserviceproviderplugin.h"
-
-QT_BEGIN_NAMESPACE
-
-QMediaService* AudioCaptureServicePlugin::create(QString const& key)
-{
- if (key == QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE))
- return new AudioCaptureService;
-
- return 0;
-}
-
-void AudioCaptureServicePlugin::release(QMediaService *service)
-{
- delete service;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/audiocapture/audiocaptureserviceplugin.h b/src/plugins/audiocapture/audiocaptureserviceplugin.h
deleted file mode 100644
index 8d2e41924..000000000
--- a/src/plugins/audiocapture/audiocaptureserviceplugin.h
+++ /dev/null
@@ -1,61 +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 AUDIOCAPTURESERVICEPLUGIN_H
-#define AUDIOCAPTURESERVICEPLUGIN_H
-
-#include "qmediaserviceproviderplugin.h"
-
-QT_BEGIN_NAMESPACE
-
-class AudioCaptureServicePlugin : public QMediaServiceProviderPlugin
-{
- Q_OBJECT
-
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "audiocapture.json")
-
-public:
- QMediaService* create(QString const& key);
- void release(QMediaService *service);
-};
-
-QT_END_NAMESPACE
-
-#endif // AUDIOCAPTURESERVICEPLUGIN_H
diff --git a/src/plugins/audiocapture/audiocapturesession.cpp b/src/plugins/audiocapture/audiocapturesession.cpp
deleted file mode 100644
index 9bdb5a92d..000000000
--- a/src/plugins/audiocapture/audiocapturesession.cpp
+++ /dev/null
@@ -1,458 +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 <QtCore/qdebug.h>
-#include <QtCore/qurl.h>
-#include <QtCore/qdir.h>
-#include <qaudiodeviceinfo.h>
-
-#include "qmediarecorder.h"
-
-#include "audiocapturesession.h"
-#include "audiocaptureprobecontrol.h"
-
-QT_BEGIN_NAMESPACE
-
-void FileProbeProxy::startProbes(const QAudioFormat &format)
-{
- m_format = format;
-}
-
-void FileProbeProxy::stopProbes()
-{
- m_format = QAudioFormat();
-}
-
-void FileProbeProxy::addProbe(AudioCaptureProbeControl *probe)
-{
- QMutexLocker locker(&m_probeMutex);
-
- if (m_probes.contains(probe))
- return;
-
- m_probes.append(probe);
-}
-
-void FileProbeProxy::removeProbe(AudioCaptureProbeControl *probe)
-{
- QMutexLocker locker(&m_probeMutex);
- m_probes.removeOne(probe);
-}
-
-qint64 FileProbeProxy::writeData(const char *data, qint64 len)
-{
- if (m_format.isValid()) {
- QMutexLocker locker(&m_probeMutex);
-
- for (AudioCaptureProbeControl* probe : qAsConst(m_probes))
- probe->bufferProbed(data, len, m_format);
- }
-
- return QFile::writeData(data, len);
-}
-
-AudioCaptureSession::AudioCaptureSession(QObject *parent)
- : QObject(parent)
- , m_state(QMediaRecorder::StoppedState)
- , m_status(QMediaRecorder::UnloadedStatus)
- , m_audioInput(0)
- , m_deviceInfo(QAudioDeviceInfo::defaultInputDevice())
- , m_wavFile(true)
- , m_volume(1.0)
- , m_muted(false)
-{
- m_format = m_deviceInfo.preferredFormat();
-}
-
-AudioCaptureSession::~AudioCaptureSession()
-{
- setState(QMediaRecorder::StoppedState);
-}
-
-QAudioFormat AudioCaptureSession::format() const
-{
- return m_format;
-}
-
-void AudioCaptureSession::setFormat(const QAudioFormat &format)
-{
- m_format = format;
-}
-
-void AudioCaptureSession::setContainerFormat(const QString &formatMimeType)
-{
- m_wavFile = (formatMimeType.isEmpty()
- || QString::compare(formatMimeType, QLatin1String("audio/x-wav")) == 0);
-}
-
-QString AudioCaptureSession::containerFormat() const
-{
- if (m_wavFile)
- return QStringLiteral("audio/x-wav");
-
- return QStringLiteral("audio/x-raw");
-}
-
-QUrl AudioCaptureSession::outputLocation() const
-{
- return m_actualOutputLocation;
-}
-
-bool AudioCaptureSession::setOutputLocation(const QUrl& location)
-{
- if (m_requestedOutputLocation == location)
- return false;
-
- m_actualOutputLocation = QUrl();
- m_requestedOutputLocation = location;
-
- if (m_requestedOutputLocation.isEmpty())
- return true;
-
- if (m_requestedOutputLocation.isValid() && (m_requestedOutputLocation.isLocalFile()
- || m_requestedOutputLocation.isRelative())) {
- emit actualLocationChanged(m_requestedOutputLocation);
- return true;
- }
-
- m_requestedOutputLocation = QUrl();
- return false;
-}
-
-qint64 AudioCaptureSession::position() const
-{
- if (m_audioInput)
- return m_audioInput->processedUSecs() / 1000;
- return 0;
-}
-
-void AudioCaptureSession::setState(QMediaRecorder::State state)
-{
- if (m_state == state)
- return;
-
- m_state = state;
- emit stateChanged(m_state);
-
- switch (m_state) {
- case QMediaRecorder::StoppedState:
- stop();
- break;
- case QMediaRecorder::PausedState:
- pause();
- break;
- case QMediaRecorder::RecordingState:
- record();
- break;
- }
-}
-
-QMediaRecorder::State AudioCaptureSession::state() const
-{
- return m_state;
-}
-
-void AudioCaptureSession::setStatus(QMediaRecorder::Status status)
-{
- if (m_status == status)
- return;
-
- m_status = status;
- emit statusChanged(m_status);
-}
-
-QMediaRecorder::Status AudioCaptureSession::status() const
-{
- return m_status;
-}
-
-QDir AudioCaptureSession::defaultDir() const
-{
- QStringList dirCandidates;
-
- dirCandidates << QDir::home().filePath("Documents");
- dirCandidates << QDir::home().filePath("My Documents");
- dirCandidates << QDir::homePath();
- dirCandidates << QDir::currentPath();
- dirCandidates << QDir::tempPath();
-
- for (const QString &path : qAsConst(dirCandidates)) {
- QDir dir(path);
- if (dir.exists() && QFileInfo(path).isWritable())
- return dir;
- }
-
- return QDir();
-}
-
-QString AudioCaptureSession::generateFileName(const QString &requestedName,
- const QString &extension) const
-{
- if (requestedName.isEmpty())
- return generateFileName(defaultDir(), extension);
-
- QString path = requestedName;
-
- if (QFileInfo(path).isRelative())
- path = defaultDir().absoluteFilePath(path);
-
- if (QFileInfo(path).isDir())
- return generateFileName(QDir(path), extension);
-
- if (!path.endsWith(extension))
- path.append(QString(".%1").arg(extension));
-
- return path;
-}
-
-QString AudioCaptureSession::generateFileName(const QDir &dir,
- const QString &ext) const
-{
- int lastClip = 0;
- const auto list = dir.entryList(QStringList() << QString("clip_*.%1").arg(ext));
- for (const QString &fileName : list) {
- int imgNumber = QStringView{fileName}.mid(5, fileName.size()-6-ext.length()).toInt();
- lastClip = qMax(lastClip, imgNumber);
- }
-
- QString name = QString("clip_%1.%2").arg(lastClip+1,
- 4, //fieldWidth
- 10,
- QLatin1Char('0')).arg(ext);
-
- return dir.absoluteFilePath(name);
-}
-
-void AudioCaptureSession::record()
-{
- if (m_status == QMediaRecorder::PausedStatus) {
- m_audioInput->resume();
- } else {
- if (m_deviceInfo.isNull()) {
- emit error(QMediaRecorder::ResourceError,
- QStringLiteral("No input device available."));
- m_state = QMediaRecorder::StoppedState;
- emit stateChanged(m_state);
- setStatus(QMediaRecorder::UnavailableStatus);
- return;
- }
-
- setStatus(QMediaRecorder::LoadingStatus);
-
- m_format = m_deviceInfo.nearestFormat(m_format);
- m_audioInput = new QAudioInput(m_deviceInfo, m_format);
- connect(m_audioInput, SIGNAL(stateChanged(QAudio::State)),
- this, SLOT(audioInputStateChanged(QAudio::State)));
- connect(m_audioInput, SIGNAL(notify()),
- this, SLOT(notify()));
-
-
- QString filePath = generateFileName(
- m_requestedOutputLocation.isLocalFile() ? m_requestedOutputLocation.toLocalFile()
- : m_requestedOutputLocation.toString(),
- m_wavFile ? QLatin1String("wav")
- : QLatin1String("raw"));
-
- m_actualOutputLocation = QUrl::fromLocalFile(filePath);
- if (m_actualOutputLocation != m_requestedOutputLocation)
- emit actualLocationChanged(m_actualOutputLocation);
-
- file.setFileName(filePath);
-
- setStatus(QMediaRecorder::LoadedStatus);
- setStatus(QMediaRecorder::StartingStatus);
-
- if (file.open(QIODevice::WriteOnly)) {
- if (m_wavFile) {
- memset(&header,0,sizeof(CombinedHeader));
- memcpy(header.riff.descriptor.id,"RIFF",4);
- header.riff.descriptor.size = 0xFFFFFFFF; // This should be updated on stop(), filesize-8
- memcpy(header.riff.type,"WAVE",4);
- memcpy(header.wave.descriptor.id,"fmt ",4);
- header.wave.descriptor.size = 16;
- header.wave.audioFormat = 1; // for PCM data
- header.wave.numChannels = m_format.channelCount();
- header.wave.sampleRate = m_format.sampleRate();
- header.wave.byteRate = m_format.sampleRate()*m_format.channelCount()*m_format.sampleSize()/8;
- header.wave.blockAlign = m_format.channelCount()*m_format.sampleSize()/8;
- header.wave.bitsPerSample = m_format.sampleSize();
- memcpy(header.data.descriptor.id,"data",4);
- header.data.descriptor.size = 0xFFFFFFFF; // This should be updated on stop(),samples*channels*sampleSize/8
- file.write((char*)&header,sizeof(CombinedHeader));
- }
-
- setVolumeHelper(m_muted ? 0 : m_volume);
-
- file.startProbes(m_format);
- m_audioInput->start(qobject_cast<QIODevice*>(&file));
- } else {
- delete m_audioInput;
- m_audioInput = 0;
- emit error(QMediaRecorder::ResourceError,
- QStringLiteral("Can't open output location"));
- m_state = QMediaRecorder::StoppedState;
- emit stateChanged(m_state);
- setStatus(QMediaRecorder::UnloadedStatus);
- }
- }
-}
-
-void AudioCaptureSession::pause()
-{
- m_audioInput->suspend();
-}
-
-void AudioCaptureSession::stop()
-{
- if(m_audioInput) {
- m_audioInput->stop();
- file.stopProbes();
- file.close();
- if (m_wavFile) {
- qint32 fileSize = file.size();
- file.open(QIODevice::ReadWrite | QIODevice::Unbuffered);
- file.read((char*)&header,sizeof(CombinedHeader));
- header.riff.descriptor.size = fileSize - 8; // The RIFF chunk size is the file size minus
- // the first two RIFF fields (8 bytes)
- header.data.descriptor.size = fileSize - 44; // dataSize = fileSize - headerSize (44 bytes)
- file.seek(0);
- file.write((char*)&header,sizeof(CombinedHeader));
- file.close();
- }
- delete m_audioInput;
- m_audioInput = 0;
- setStatus(QMediaRecorder::UnloadedStatus);
- }
-}
-
-void AudioCaptureSession::addProbe(AudioCaptureProbeControl *probe)
-{
- file.addProbe(probe);
-}
-
-void AudioCaptureSession::removeProbe(AudioCaptureProbeControl *probe)
-{
- file.removeProbe(probe);
-}
-
-void AudioCaptureSession::audioInputStateChanged(QAudio::State state)
-{
- switch(state) {
- case QAudio::ActiveState:
- setStatus(QMediaRecorder::RecordingStatus);
- break;
- case QAudio::SuspendedState:
- setStatus(QMediaRecorder::PausedStatus);
- break;
- case QAudio::StoppedState:
- setStatus(QMediaRecorder::FinalizingStatus);
- break;
- default:
- break;
- }
-}
-
-void AudioCaptureSession::notify()
-{
- emit positionChanged(position());
-}
-
-void AudioCaptureSession::setCaptureDevice(const QString &deviceName)
-{
- m_captureDevice = deviceName;
-
- QList<QAudioDeviceInfo> devices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
- for (int i = 0; i < devices.size(); ++i) {
- QAudioDeviceInfo info = devices.at(i);
- if (m_captureDevice == info.deviceName()){
- m_deviceInfo = info;
- return;
- }
- }
- m_deviceInfo = QAudioDeviceInfo::defaultInputDevice();
-}
-
-qreal AudioCaptureSession::volume() const
-{
- return m_volume;
-}
-
-bool AudioCaptureSession::isMuted() const
-{
- return m_muted;
-}
-
-void AudioCaptureSession::setVolume(qreal v)
-{
- qreal boundedVolume = qBound(qreal(0), v, qreal(1));
-
- if (m_volume == boundedVolume)
- return;
-
- m_volume = boundedVolume;
-
- if (!m_muted)
- setVolumeHelper(m_volume);
-
- emit volumeChanged(m_volume);
-}
-
-void AudioCaptureSession::setMuted(bool muted)
-{
- if (m_muted == muted)
- return;
-
- m_muted = muted;
-
- setVolumeHelper(m_muted ? 0 : m_volume);
-
- emit mutedChanged(m_muted);
-}
-
-void AudioCaptureSession::setVolumeHelper(qreal volume)
-{
- if (!m_audioInput)
- return;
-
- m_audioInput->setVolume(volume);
-}
-
-
-
-QT_END_NAMESPACE
diff --git a/src/plugins/audiocapture/audiocapturesession.h b/src/plugins/audiocapture/audiocapturesession.h
deleted file mode 100644
index 3db7a7595..000000000
--- a/src/plugins/audiocapture/audiocapturesession.h
+++ /dev/null
@@ -1,194 +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 AUDIOCAPTURESESSION_H
-#define AUDIOCAPTURESESSION_H
-
-#include <QFile>
-#include <QUrl>
-#include <QDir>
-#include <QMutex>
-
-#include "audioencodercontrol.h"
-#include "audioinputselector.h"
-#include "audiomediarecordercontrol.h"
-
-#include <qaudioformat.h>
-#include <qaudioinput.h>
-#include <qaudiodeviceinfo.h>
-
-QT_BEGIN_NAMESPACE
-
-class AudioCaptureProbeControl;
-
-class FileProbeProxy: public QFile {
-public:
- void startProbes(const QAudioFormat& format);
- void stopProbes();
- void addProbe(AudioCaptureProbeControl *probe);
- void removeProbe(AudioCaptureProbeControl *probe);
-
-protected:
- virtual qint64 writeData(const char *data, qint64 len);
-
-private:
- QAudioFormat m_format;
- QList<AudioCaptureProbeControl*> m_probes;
- QMutex m_probeMutex;
-};
-
-
-class AudioCaptureSession : public QObject
-{
- Q_OBJECT
-
-public:
- AudioCaptureSession(QObject *parent = 0);
- ~AudioCaptureSession();
-
- QAudioFormat format() const;
- void setFormat(const QAudioFormat &format);
-
- QString containerFormat() const;
- void setContainerFormat(const QString &formatMimeType);
-
- QUrl outputLocation() const;
- bool setOutputLocation(const QUrl& location);
-
- qint64 position() const;
-
- void setState(QMediaRecorder::State state);
- QMediaRecorder::State state() const;
- QMediaRecorder::Status status() const;
-
- void addProbe(AudioCaptureProbeControl *probe);
- void removeProbe(AudioCaptureProbeControl *probe);
-
- void setCaptureDevice(const QString &deviceName);
-
- void setVolume(qreal v);
- qreal volume() const;
-
- void setMuted(bool muted);
- bool isMuted() const;
-
-signals:
- void stateChanged(QMediaRecorder::State state);
- void statusChanged(QMediaRecorder::Status status);
- void positionChanged(qint64 position);
- void actualLocationChanged(const QUrl &location);
- void volumeChanged(qreal volume);
- void mutedChanged(bool muted);
- void error(int error, const QString &errorString);
-
-private slots:
- void audioInputStateChanged(QAudio::State state);
- void notify();
-
-private:
- void record();
- void pause();
- void stop();
-
- void setStatus(QMediaRecorder::Status status);
-
- void setVolumeHelper(qreal volume);
-
- QDir defaultDir() const;
- QString generateFileName(const QString &requestedName,
- const QString &extension) const;
- QString generateFileName(const QDir &dir, const QString &extension) const;
-
- FileProbeProxy file;
- QString m_captureDevice;
- QUrl m_requestedOutputLocation;
- QUrl m_actualOutputLocation;
- QMediaRecorder::State m_state;
- QMediaRecorder::Status m_status;
- QAudioInput *m_audioInput;
- QAudioDeviceInfo m_deviceInfo;
- QAudioFormat m_format;
- bool m_wavFile;
- qreal m_volume;
- bool m_muted;
-
- // WAV header stuff
-
- struct chunk
- {
- char id[4];
- quint32 size;
- };
-
- struct RIFFHeader
- {
- chunk descriptor;
- char type[4];
- };
-
- struct WAVEHeader
- {
- chunk descriptor;
- quint16 audioFormat; // PCM = 1
- quint16 numChannels;
- quint32 sampleRate;
- quint32 byteRate;
- quint16 blockAlign;
- quint16 bitsPerSample;
- };
-
- struct DATAHeader
- {
- chunk descriptor;
-// quint8 data[];
- };
-
- struct CombinedHeader
- {
- RIFFHeader riff;
- WAVEHeader wave;
- DATAHeader data;
- };
-
- CombinedHeader header;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/audiocapture/audiocontainercontrol.cpp b/src/plugins/audiocapture/audiocontainercontrol.cpp
deleted file mode 100644
index fd7d12e65..000000000
--- a/src/plugins/audiocapture/audiocontainercontrol.cpp
+++ /dev/null
@@ -1,82 +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 "audiocontainercontrol.h"
-#include "audiocapturesession.h"
-
-QT_BEGIN_NAMESPACE
-
-AudioContainerControl::AudioContainerControl(QObject *parent)
- :QMediaContainerControl(parent)
-{
- m_session = qobject_cast<AudioCaptureSession*>(parent);
-}
-
-AudioContainerControl::~AudioContainerControl()
-{
-}
-
-QStringList AudioContainerControl::supportedContainers() const
-{
- return QStringList() << QStringLiteral("audio/x-wav")
- << QStringLiteral("audio/x-raw");
-}
-
-QString AudioContainerControl::containerFormat() const
-{
- return m_session->containerFormat();
-}
-
-void AudioContainerControl::setContainerFormat(const QString &formatMimeType)
-{
- if (formatMimeType.isEmpty() || supportedContainers().contains(formatMimeType))
- m_session->setContainerFormat(formatMimeType);
-}
-
-QString AudioContainerControl::containerDescription(const QString &formatMimeType) const
-{
- if (QString::compare(formatMimeType, QLatin1String("audio/x-raw")) == 0)
- return tr("RAW (headerless) file format");
- if (QString::compare(formatMimeType, QLatin1String("audio/x-wav")) == 0)
- return tr("WAV file format");
-
- return QString();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/audiocapture/audiocontainercontrol.h b/src/plugins/audiocapture/audiocontainercontrol.h
deleted file mode 100644
index 169b3c189..000000000
--- a/src/plugins/audiocapture/audiocontainercontrol.h
+++ /dev/null
@@ -1,70 +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 AUDIOCONTAINERCONTROL_H
-#define AUDIOCONTAINERCONTROL_H
-
-#include "qmediacontainercontrol.h"
-
-#include <QtCore/qstringlist.h>
-#include <QtCore/qmap.h>
-
-QT_BEGIN_NAMESPACE
-
-class AudioCaptureSession;
-
-class AudioContainerControl : public QMediaContainerControl
-{
- Q_OBJECT
-public:
- AudioContainerControl(QObject *parent);
- virtual ~AudioContainerControl();
-
- QStringList supportedContainers() const;
- QString containerFormat() const;
- void setContainerFormat(const QString &formatMimeType);
- QString containerDescription(const QString &formatMimeType) const;
-
-private:
- AudioCaptureSession* m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/audiocapture/audioencodercontrol.cpp b/src/plugins/audiocapture/audioencodercontrol.cpp
deleted file mode 100644
index 6fc519cef..000000000
--- a/src/plugins/audiocapture/audioencodercontrol.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 "audioencodercontrol.h"
-#include "audiocapturesession.h"
-
-#include <qaudioformat.h>
-
-#include <QtCore/qdebug.h>
-
-#include <algorithm>
-
-QT_BEGIN_NAMESPACE
-
-static QAudioFormat audioSettingsToAudioFormat(const QAudioEncoderSettings &settings)
-{
- QAudioFormat fmt;
- fmt.setCodec(settings.codec());
- fmt.setChannelCount(settings.channelCount());
- fmt.setSampleRate(settings.sampleRate());
- int sampleSize = 16;
- if (settings.bitRate() && settings.channelCount() && settings.sampleRate())
- sampleSize = settings.bitRate() / settings.channelCount() / settings.sampleRate();
- fmt.setSampleSize(sampleSize);
- fmt.setSampleType(sampleSize == 8 ? QAudioFormat::UnSignedInt : QAudioFormat::SignedInt);
-
- fmt.setByteOrder(QAudioDeviceInfo::defaultInputDevice().preferredFormat().byteOrder());
- return fmt;
-}
-
-static QAudioEncoderSettings audioFormatToAudioSettings(const QAudioFormat &format)
-{
- QAudioEncoderSettings settings;
- settings.setCodec(format.codec());
- settings.setChannelCount(format.channelCount());
- settings.setSampleRate(format.sampleRate());
- settings.setEncodingMode(QMultimedia::ConstantBitRateEncoding);
- settings.setBitRate(format.channelCount()
- * format.sampleSize()
- * format.sampleRate());
- return settings;
-}
-
-AudioEncoderControl::AudioEncoderControl(QObject *parent)
- :QAudioEncoderSettingsControl(parent)
-{
- m_session = qobject_cast<AudioCaptureSession*>(parent);
- update();
-}
-
-AudioEncoderControl::~AudioEncoderControl()
-{
-}
-
-QStringList AudioEncoderControl::supportedAudioCodecs() const
-{
- return QStringList() << QStringLiteral("audio/pcm");
-}
-
-QString AudioEncoderControl::codecDescription(const QString &codecName) const
-{
- if (QString::compare(codecName, QLatin1String("audio/pcm")) == 0)
- return tr("Linear PCM audio data");
-
- return QString();
-}
-
-QList<int> AudioEncoderControl::supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous) const
-{
- if (continuous)
- *continuous = false;
-
- if (settings.codec().isEmpty() || settings.codec() == QLatin1String("audio/pcm"))
- return m_sampleRates;
-
- return QList<int>();
-}
-
-QAudioEncoderSettings AudioEncoderControl::audioSettings() const
-{
- return audioFormatToAudioSettings(m_session->format());
-}
-
-void AudioEncoderControl::setAudioSettings(const QAudioEncoderSettings &settings)
-{
- QAudioFormat fmt = audioSettingsToAudioFormat(settings);
-
- if (settings.encodingMode() == QMultimedia::ConstantQualityEncoding) {
- fmt.setCodec("audio/pcm");
- switch (settings.quality()) {
- case QMultimedia::VeryLowQuality:
- fmt.setSampleSize(8);
- fmt.setSampleRate(8000);
- fmt.setSampleType(QAudioFormat::UnSignedInt);
- break;
- case QMultimedia::LowQuality:
- fmt.setSampleSize(8);
- fmt.setSampleRate(22050);
- fmt.setSampleType(QAudioFormat::UnSignedInt);
- break;
- case QMultimedia::HighQuality:
- fmt.setSampleSize(16);
- fmt.setSampleRate(48000);
- fmt.setSampleType(QAudioFormat::SignedInt);
- break;
- case QMultimedia::VeryHighQuality:
- fmt.setSampleSize(16);
- fmt.setSampleRate(96000);
- fmt.setSampleType(QAudioFormat::SignedInt);
- break;
- case QMultimedia::NormalQuality:
- default:
- fmt.setSampleSize(16);
- fmt.setSampleRate(44100);
- fmt.setSampleType(QAudioFormat::SignedInt);
- break;
- }
- }
-
- m_session->setFormat(fmt);
-}
-
-void AudioEncoderControl::update()
-{
- m_sampleRates.clear();
- QList<QAudioDeviceInfo> devices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
- for (int i = 0; i < devices.size(); ++i) {
- QList<int> rates = devices.at(i).supportedSampleRates();
- for (int j = 0; j < rates.size(); ++j) {
- int rate = rates.at(j);
- if (!m_sampleRates.contains(rate))
- m_sampleRates.append(rate);
- }
- }
- std::sort(m_sampleRates.begin(), m_sampleRates.end());
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/audiocapture/audioencodercontrol.h b/src/plugins/audiocapture/audioencodercontrol.h
deleted file mode 100644
index 30de92e96..000000000
--- a/src/plugins/audiocapture/audioencodercontrol.h
+++ /dev/null
@@ -1,77 +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 AUDIOENCODERCONTROL_H
-#define AUDIOENCODERCONTROL_H
-
-#include "qaudioencodersettingscontrol.h"
-
-#include <QtCore/qstringlist.h>
-#include <QtCore/qmap.h>
-
-#include <qaudioformat.h>
-
-QT_BEGIN_NAMESPACE
-
-class AudioCaptureSession;
-
-class AudioEncoderControl : public QAudioEncoderSettingsControl
-{
- Q_OBJECT
-public:
- AudioEncoderControl(QObject *parent);
- virtual ~AudioEncoderControl();
-
- QStringList supportedAudioCodecs() const;
- QString codecDescription(const QString &codecName) const;
- QList<int> supportedSampleRates(const QAudioEncoderSettings &, bool *continuous = 0) const;
-
- QAudioEncoderSettings audioSettings() const;
- void setAudioSettings(const QAudioEncoderSettings&);
-
-private:
- void update();
-
- AudioCaptureSession* m_session;
- QList<int> m_sampleRates;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/audiocapture/audioinputselector.cpp b/src/plugins/audiocapture/audioinputselector.cpp
deleted file mode 100644
index 3e7e854c6..000000000
--- a/src/plugins/audiocapture/audioinputselector.cpp
+++ /dev/null
@@ -1,111 +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 "audiocapturesession.h"
-#include "audioinputselector.h"
-
-#include <qaudiodeviceinfo.h>
-
-QT_BEGIN_NAMESPACE
-
-AudioInputSelector::AudioInputSelector(QObject *parent)
- :QAudioInputSelectorControl(parent)
-{
- m_session = qobject_cast<AudioCaptureSession*>(parent);
-
- update();
-
- m_audioInput = defaultInput();
-}
-
-AudioInputSelector::~AudioInputSelector()
-{
-}
-
-QList<QString> AudioInputSelector::availableInputs() const
-{
- return m_names;
-}
-
-QString AudioInputSelector::inputDescription(const QString& name) const
-{
- QString desc;
-
- for(int i = 0; i < m_names.count(); i++) {
- if (m_names.at(i).compare(name) == 0) {
- desc = m_names.at(i);
- break;
- }
- }
- return desc;
-}
-
-QString AudioInputSelector::defaultInput() const
-{
- return QAudioDeviceInfo::defaultInputDevice().deviceName();
-}
-
-QString AudioInputSelector::activeInput() const
-{
- return m_audioInput;
-}
-
-void AudioInputSelector::setActiveInput(const QString& name)
-{
- if (m_audioInput.compare(name) != 0) {
- m_audioInput = name;
- m_session->setCaptureDevice(name);
- emit activeInputChanged(name);
- }
-}
-
-void AudioInputSelector::update()
-{
- m_names.clear();
- m_descriptions.clear();
-
- QList<QAudioDeviceInfo> devices;
- devices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
- for(int i = 0; i < devices.size(); ++i) {
- m_names.append(devices.at(i).deviceName());
- m_descriptions.append(devices.at(i).deviceName());
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/audiocapture/audioinputselector.h b/src/plugins/audiocapture/audioinputselector.h
deleted file mode 100644
index 7a0829223..000000000
--- a/src/plugins/audiocapture/audioinputselector.h
+++ /dev/null
@@ -1,77 +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 AUDIOINPUTSELECTOR_H
-#define AUDIOINPUTSELECTOR_H
-
-#include <QStringList>
-
-#include "qaudioinputselectorcontrol.h"
-
-QT_BEGIN_NAMESPACE
-
-class AudioCaptureSession;
-
-class AudioInputSelector : public QAudioInputSelectorControl
-{
-Q_OBJECT
-public:
- AudioInputSelector(QObject *parent);
- virtual ~AudioInputSelector();
-
- QList<QString> availableInputs() const;
- QString inputDescription(const QString& name) const;
- QString defaultInput() const;
- QString activeInput() const;
-
-public Q_SLOTS:
- void setActiveInput(const QString& name);
-
-private:
- void update();
-
- QString m_audioInput;
- QList<QString> m_names;
- QList<QString> m_descriptions;
- AudioCaptureSession* m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif // AUDIOINPUTSELECTOR_H
diff --git a/src/plugins/audiocapture/audiomediarecordercontrol.cpp b/src/plugins/audiocapture/audiomediarecordercontrol.cpp
deleted file mode 100644
index 767026180..000000000
--- a/src/plugins/audiocapture/audiomediarecordercontrol.cpp
+++ /dev/null
@@ -1,121 +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 "audiocapturesession.h"
-#include "audiomediarecordercontrol.h"
-
-#include <QtCore/qdebug.h>
-
-QT_BEGIN_NAMESPACE
-
-AudioMediaRecorderControl::AudioMediaRecorderControl(QObject *parent)
- : QMediaRecorderControl(parent)
-{
- m_session = qobject_cast<AudioCaptureSession*>(parent);
- connect(m_session, SIGNAL(positionChanged(qint64)),
- this, SIGNAL(durationChanged(qint64)));
- connect(m_session, SIGNAL(stateChanged(QMediaRecorder::State)),
- this, SIGNAL(stateChanged(QMediaRecorder::State)));
- connect(m_session, SIGNAL(statusChanged(QMediaRecorder::Status)),
- this, SIGNAL(statusChanged(QMediaRecorder::Status)));
- connect(m_session, SIGNAL(actualLocationChanged(QUrl)),
- this, SIGNAL(actualLocationChanged(QUrl)));
- connect(m_session, &AudioCaptureSession::volumeChanged,
- this, &AudioMediaRecorderControl::volumeChanged);
- connect(m_session, &AudioCaptureSession::mutedChanged,
- this, &AudioMediaRecorderControl::mutedChanged);
- connect(m_session, SIGNAL(error(int,QString)),
- this, SIGNAL(error(int,QString)));
-}
-
-AudioMediaRecorderControl::~AudioMediaRecorderControl()
-{
-}
-
-QUrl AudioMediaRecorderControl::outputLocation() const
-{
- return m_session->outputLocation();
-}
-
-bool AudioMediaRecorderControl::setOutputLocation(const QUrl& sink)
-{
- return m_session->setOutputLocation(sink);
-}
-
-QMediaRecorder::State AudioMediaRecorderControl::state() const
-{
- return m_session->state();
-}
-
-QMediaRecorder::Status AudioMediaRecorderControl::status() const
-{
- return m_session->status();
-}
-
-qint64 AudioMediaRecorderControl::duration() const
-{
- return m_session->position();
-}
-
-bool AudioMediaRecorderControl::isMuted() const
-{
- return m_session->isMuted();
-}
-
-qreal AudioMediaRecorderControl::volume() const
-{
- return m_session->volume();
-}
-
-void AudioMediaRecorderControl::setState(QMediaRecorder::State state)
-{
- m_session->setState(state);
-}
-
-void AudioMediaRecorderControl::setMuted(bool muted)
-{
- m_session->setMuted(muted);
-}
-
-void AudioMediaRecorderControl::setVolume(qreal volume)
-{
- m_session->setVolume(volume);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/audiocapture/audiomediarecordercontrol.h b/src/plugins/audiocapture/audiomediarecordercontrol.h
deleted file mode 100644
index cb6e79cb4..000000000
--- a/src/plugins/audiocapture/audiomediarecordercontrol.h
+++ /dev/null
@@ -1,82 +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 AUDIOMEDIARECORDERCONTROL_H
-#define AUDIOMEDIARECORDERCONTROL_H
-
-#include <QtCore/qobject.h>
-
-#include "qmediarecorder.h"
-#include "qmediarecordercontrol.h"
-
-QT_BEGIN_NAMESPACE
-
-class AudioCaptureSession;
-
-class AudioMediaRecorderControl : public QMediaRecorderControl
-{
- Q_OBJECT
-public:
- AudioMediaRecorderControl(QObject *parent = 0);
- ~AudioMediaRecorderControl();
-
- QUrl outputLocation() const;
- bool setOutputLocation(const QUrl &location);
-
- QMediaRecorder::State state() const;
- QMediaRecorder::Status status() const;
-
- qint64 duration() const;
-
- bool isMuted() const;
- qreal volume() const;
-
- void applySettings() {}
-
- void setState(QMediaRecorder::State state);
- void setMuted(bool);
- void setVolume(qreal volume);
-
-private:
- AudioCaptureSession* m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/avfoundation/avfoundation.pro b/src/plugins/avfoundation/avfoundation.pro
deleted file mode 100644
index 1110e196b..000000000
--- a/src/plugins/avfoundation/avfoundation.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-TEMPLATE = subdirs
-
-SUBDIRS += mediaplayer
-!tvos: SUBDIRS += camera
diff --git a/src/plugins/avfoundation/camera/avfaudioencodersettingscontrol.h b/src/plugins/avfoundation/camera/avfaudioencodersettingscontrol.h
deleted file mode 100644
index 94aa6a8f0..000000000
--- a/src/plugins/avfoundation/camera/avfaudioencodersettingscontrol.h
+++ /dev/null
@@ -1,75 +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 AVFAUDIOENCODERSETTINGSCONTROL_H
-#define AVFAUDIOENCODERSETTINGSCONTROL_H
-
-#include <qaudioencodersettingscontrol.h>
-
-@class NSDictionary;
-@class AVCaptureAudioDataOutput;
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraService;
-
-class AVFAudioEncoderSettingsControl : public QAudioEncoderSettingsControl
-{
-public:
- explicit AVFAudioEncoderSettingsControl(AVFCameraService *service);
-
- QStringList supportedAudioCodecs() const override;
- QString codecDescription(const QString &codecName) const override;
- QList<int> supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous = nullptr) const override;
- QAudioEncoderSettings audioSettings() const override;
- void setAudioSettings(const QAudioEncoderSettings &settings) override;
-
- NSDictionary *applySettings();
- void unapplySettings();
-
-private:
- AVFCameraService *m_service;
-
- QAudioEncoderSettings m_requestedSettings;
- QAudioEncoderSettings m_actualSettings;
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFAUDIOENCODERSETTINGSCONTROL_H
diff --git a/src/plugins/avfoundation/camera/avfaudioencodersettingscontrol.mm b/src/plugins/avfoundation/camera/avfaudioencodersettingscontrol.mm
deleted file mode 100644
index 1aeb44ff6..000000000
--- a/src/plugins/avfoundation/camera/avfaudioencodersettingscontrol.mm
+++ /dev/null
@@ -1,226 +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 "avfaudioencodersettingscontrol.h"
-
-#include "avfcameraservice.h"
-#include "avfcamerasession.h"
-
-#include <AVFoundation/AVFoundation.h>
-#include <CoreAudio/CoreAudioTypes.h>
-
-QT_BEGIN_NAMESPACE
-
-struct AudioCodecInfo
-{
- QString description;
- int id;
-
- AudioCodecInfo() : id(0) { }
- AudioCodecInfo(const QString &desc, int i)
- : description(desc), id(i)
- { }
-};
-
-typedef QMap<QString, AudioCodecInfo> SupportedAudioCodecs;
-Q_GLOBAL_STATIC_WITH_ARGS(QString , defaultCodec, (QLatin1String("aac")))
-Q_GLOBAL_STATIC(SupportedAudioCodecs, supportedCodecs)
-
-AVFAudioEncoderSettingsControl::AVFAudioEncoderSettingsControl(AVFCameraService *service)
- : QAudioEncoderSettingsControl()
- , m_service(service)
-{
- if (supportedCodecs->isEmpty()) {
- supportedCodecs->insert(QStringLiteral("lpcm"),
- AudioCodecInfo(QStringLiteral("Linear PCM"),
- kAudioFormatLinearPCM));
- supportedCodecs->insert(QStringLiteral("ulaw"),
- AudioCodecInfo(QStringLiteral("PCM Mu-Law 2:1"),
- kAudioFormatULaw));
- supportedCodecs->insert(QStringLiteral("alaw"),
- AudioCodecInfo(QStringLiteral("PCM A-Law 2:1"),
- kAudioFormatALaw));
- supportedCodecs->insert(QStringLiteral("ima4"),
- AudioCodecInfo(QStringLiteral("IMA 4:1 ADPCM"),
- kAudioFormatAppleIMA4));
- supportedCodecs->insert(QStringLiteral("alac"),
- AudioCodecInfo(QStringLiteral("Apple Lossless Audio Codec"),
- kAudioFormatAppleLossless));
- supportedCodecs->insert(QStringLiteral("aac"),
- AudioCodecInfo(QStringLiteral("MPEG-4 Low Complexity AAC"),
- kAudioFormatMPEG4AAC));
- supportedCodecs->insert(QStringLiteral("aach"),
- AudioCodecInfo(QStringLiteral("MPEG-4 High Efficiency AAC"),
- kAudioFormatMPEG4AAC_HE));
- supportedCodecs->insert(QStringLiteral("aacl"),
- AudioCodecInfo(QStringLiteral("MPEG-4 AAC Low Delay"),
- kAudioFormatMPEG4AAC_LD));
- supportedCodecs->insert(QStringLiteral("aace"),
- AudioCodecInfo(QStringLiteral("MPEG-4 AAC Enhanced Low Delay"),
- kAudioFormatMPEG4AAC_ELD));
- supportedCodecs->insert(QStringLiteral("aacf"),
- AudioCodecInfo(QStringLiteral("MPEG-4 AAC Enhanced Low Delay with SBR"),
- kAudioFormatMPEG4AAC_ELD_SBR));
- supportedCodecs->insert(QStringLiteral("aacp"),
- AudioCodecInfo(QStringLiteral("MPEG-4 HE AAC V2"),
- kAudioFormatMPEG4AAC_HE_V2));
- supportedCodecs->insert(QStringLiteral("ilbc"),
- AudioCodecInfo(QStringLiteral("iLBC"),
- kAudioFormatiLBC));
- }
-}
-
-QStringList AVFAudioEncoderSettingsControl::supportedAudioCodecs() const
-{
- return supportedCodecs->keys();
-}
-
-QString AVFAudioEncoderSettingsControl::codecDescription(const QString &codecName) const
-{
- return supportedCodecs->value(codecName).description;
-}
-
-QList<int> AVFAudioEncoderSettingsControl::supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous) const
-{
- Q_UNUSED(settings);
-
- if (continuous)
- *continuous = true;
-
- return QList<int>() << 8000 << 96000;
-}
-
-QAudioEncoderSettings AVFAudioEncoderSettingsControl::audioSettings() const
-{
- return m_actualSettings;
-}
-
-void AVFAudioEncoderSettingsControl::setAudioSettings(const QAudioEncoderSettings &settings)
-{
- if (m_requestedSettings == settings)
- return;
-
- m_requestedSettings = m_actualSettings = settings;
-}
-
-NSDictionary *AVFAudioEncoderSettingsControl::applySettings()
-{
- if (m_service->session()->state() != QCamera::LoadedState &&
- m_service->session()->state() != QCamera::ActiveState) {
- return nil;
- }
-
- NSMutableDictionary *settings = [NSMutableDictionary dictionary];
-
- QString codec = m_requestedSettings.codec().isEmpty() ? *defaultCodec : m_requestedSettings.codec();
- if (!supportedCodecs->contains(codec)) {
- qWarning("Unsupported codec: '%s'", codec.toLocal8Bit().constData());
- codec = *defaultCodec;
- }
- [settings setObject:[NSNumber numberWithInt:supportedCodecs->value(codec).id] forKey:AVFormatIDKey];
- m_actualSettings.setCodec(codec);
-
-#ifdef Q_OS_OSX
- if (m_requestedSettings.encodingMode() == QMultimedia::ConstantQualityEncoding) {
- int quality;
- switch (m_requestedSettings.quality()) {
- case QMultimedia::VeryLowQuality:
- quality = AVAudioQualityMin;
- break;
- case QMultimedia::LowQuality:
- quality = AVAudioQualityLow;
- break;
- case QMultimedia::HighQuality:
- quality = AVAudioQualityHigh;
- break;
- case QMultimedia::VeryHighQuality:
- quality = AVAudioQualityMax;
- break;
- case QMultimedia::NormalQuality:
- default:
- quality = AVAudioQualityMedium;
- break;
- }
- [settings setObject:[NSNumber numberWithInt:quality] forKey:AVEncoderAudioQualityKey];
-
- } else
-#endif
- if (m_requestedSettings.bitRate() > 0){
- [settings setObject:[NSNumber numberWithInt:m_requestedSettings.bitRate()] forKey:AVEncoderBitRateKey];
- }
-
- int sampleRate = m_requestedSettings.sampleRate();
- int channelCount = m_requestedSettings.channelCount();
-
-#ifdef Q_OS_IOS
- // Some keys are mandatory only on iOS
- if (codec == QLatin1String("lpcm")) {
- [settings setObject:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
- [settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsBigEndianKey];
- [settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsFloatKey];
- [settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsNonInterleaved];
- }
-
- if (codec == QLatin1String("alac"))
- [settings setObject:[NSNumber numberWithInt:24] forKey:AVEncoderBitDepthHintKey];
-
- if (sampleRate <= 0)
- sampleRate = codec == QLatin1String("ilbc") ? 8000 : 44100;
- if (channelCount <= 0)
- channelCount = codec == QLatin1String("ilbc") ? 1 : 2;
-#endif
-
- if (sampleRate > 0) {
- [settings setObject:[NSNumber numberWithInt:sampleRate] forKey:AVSampleRateKey];
- m_actualSettings.setSampleRate(sampleRate);
- }
- if (channelCount > 0) {
- [settings setObject:[NSNumber numberWithInt:channelCount] forKey:AVNumberOfChannelsKey];
- m_actualSettings.setChannelCount(channelCount);
- }
-
- return settings;
-}
-
-void AVFAudioEncoderSettingsControl::unapplySettings()
-{
- m_actualSettings = m_requestedSettings;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.h b/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.h
deleted file mode 100644
index a902a71f9..000000000
--- a/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 AVFAUDIOINPUTSELECTORCONTROL_H
-#define AVFAUDIOINPUTSELECTORCONTROL_H
-
-#include <QtMultimedia/qaudioinputselectorcontrol.h>
-#include <QtCore/qstringlist.h>
-
-#import <AVFoundation/AVFoundation.h>
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraSession;
-class AVFCameraService;
-
-class AVFAudioInputSelectorControl : public QAudioInputSelectorControl
-{
-Q_OBJECT
-public:
- AVFAudioInputSelectorControl(AVFCameraService *service, QObject *parent = nullptr);
- ~AVFAudioInputSelectorControl();
-
- QList<QString> availableInputs() const override;
- QString inputDescription(const QString &name) const override;
- QString defaultInput() const override;
- QString activeInput() const override;
-
-public Q_SLOTS:
- void setActiveInput(const QString &name) override;
-
-public:
- //device changed since the last createCaptureDevice()
- bool isDirty() const { return m_dirty; }
- AVCaptureDevice *createCaptureDevice();
-
-private:
- QString m_activeInput;
- bool m_dirty;
- QString m_defaultDevice;
- QStringList m_devices;
- QMap<QString, QString> m_deviceDescriptions;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.mm b/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.mm
deleted file mode 100644
index de29fd970..000000000
--- a/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.mm
+++ /dev/null
@@ -1,119 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfcameradebug.h"
-#include "avfaudioinputselectorcontrol.h"
-#include "avfcameraservice.h"
-
-#import <AVFoundation/AVFoundation.h>
-
-QT_USE_NAMESPACE
-
-AVFAudioInputSelectorControl::AVFAudioInputSelectorControl(AVFCameraService *service, QObject *parent)
- : QAudioInputSelectorControl(parent)
- , m_dirty(true)
-{
- Q_UNUSED(service);
- NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio];
- for (AVCaptureDevice *device in videoDevices) {
- QString deviceId = QString::fromUtf8([[device uniqueID] UTF8String]);
- m_devices << deviceId;
- m_deviceDescriptions.insert(deviceId,
- QString::fromUtf8([[device localizedName] UTF8String]));
- }
-
- AVCaptureDevice *defaultDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
- if (defaultDevice) {
- m_defaultDevice = QString::fromUtf8([defaultDevice.uniqueID UTF8String]);
- m_activeInput = m_defaultDevice;
- }
-}
-
-AVFAudioInputSelectorControl::~AVFAudioInputSelectorControl()
-{
-}
-
-QList<QString> AVFAudioInputSelectorControl::availableInputs() const
-{
- return m_devices;
-}
-
-QString AVFAudioInputSelectorControl::inputDescription(const QString &name) const
-{
- return m_deviceDescriptions.value(name);
-}
-
-QString AVFAudioInputSelectorControl::defaultInput() const
-{
- return m_defaultDevice;
-}
-
-QString AVFAudioInputSelectorControl::activeInput() const
-{
- return m_activeInput;
-}
-
-void AVFAudioInputSelectorControl::setActiveInput(const QString &name)
-{
- if (name != m_activeInput) {
- m_activeInput = name;
- m_dirty = true;
-
- Q_EMIT activeInputChanged(m_activeInput);
- }
-}
-
-AVCaptureDevice *AVFAudioInputSelectorControl::createCaptureDevice()
-{
- m_dirty = false;
- AVCaptureDevice *device = nullptr;
-
- if (!m_activeInput.isEmpty()) {
- device = [AVCaptureDevice deviceWithUniqueID:
- [NSString stringWithUTF8String:
- m_activeInput.toUtf8().constData()]];
- }
-
- if (!device)
- device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
-
- return device;
-}
-
-#include "moc_avfaudioinputselectorcontrol.cpp"
diff --git a/src/plugins/avfoundation/camera/avfcamera.json b/src/plugins/avfoundation/camera/avfcamera.json
deleted file mode 100644
index e4310b62c..000000000
--- a/src/plugins/avfoundation/camera/avfcamera.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Keys": ["avfoundationcamera"],
- "Services": ["org.qt-project.qt.camera"]
-}
diff --git a/src/plugins/avfoundation/camera/avfcameracontrol.h b/src/plugins/avfoundation/camera/avfcameracontrol.h
deleted file mode 100644
index df371e864..000000000
--- a/src/plugins/avfoundation/camera/avfcameracontrol.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 AVFCAMERACONTROL_H
-#define AVFCAMERACONTROL_H
-
-#include <QtCore/qobject.h>
-
-#include <QtMultimedia/qcameracontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraSession;
-class AVFCameraService;
-
-class AVFCameraControl : public QCameraControl
-{
-Q_OBJECT
-public:
- AVFCameraControl(AVFCameraService *service, QObject *parent = nullptr);
- ~AVFCameraControl();
-
- QCamera::State state() const override;
- void setState(QCamera::State state) override;
-
- QCamera::Status status() const override;
-
- QCamera::CaptureModes captureMode() const override;
- void setCaptureMode(QCamera::CaptureModes) override;
- bool isCaptureModeSupported(QCamera::CaptureModes mode) const override;
-
- bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const override;
-
-private Q_SLOTS:
- void updateStatus();
-
-private:
- AVFCameraSession *m_session;
-
- QCamera::State m_state;
- QCamera::Status m_lastStatus;
- QCamera::CaptureModes m_captureMode;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/avfoundation/camera/avfcameracontrol.mm b/src/plugins/avfoundation/camera/avfcameracontrol.mm
deleted file mode 100644
index c47eecfdf..000000000
--- a/src/plugins/avfoundation/camera/avfcameracontrol.mm
+++ /dev/null
@@ -1,134 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfcameradebug.h"
-#include "avfcameracontrol.h"
-#include "avfcamerasession.h"
-#include "avfcameraservice.h"
-
-QT_USE_NAMESPACE
-
-AVFCameraControl::AVFCameraControl(AVFCameraService *service, QObject *parent)
- : QCameraControl(parent)
- , m_session(service->session())
- , m_state(QCamera::UnloadedState)
- , m_lastStatus(QCamera::UnloadedStatus)
- , m_captureMode(QCamera::CaptureStillImage)
-{
- Q_UNUSED(service);
- connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(updateStatus()));
- connect(this, &AVFCameraControl::captureModeChanged, m_session, &AVFCameraSession::onCaptureModeChanged);
-}
-
-AVFCameraControl::~AVFCameraControl()
-{
-}
-
-QCamera::State AVFCameraControl::state() const
-{
- return m_state;
-}
-
-void AVFCameraControl::setState(QCamera::State state)
-{
- if (m_state == state)
- return;
- m_state = state;
- m_session->setState(state);
-
- Q_EMIT stateChanged(m_state);
- updateStatus();
-}
-
-QCamera::Status AVFCameraControl::status() const
-{
- static QCamera::Status statusTable[3][3] = {
- { QCamera::UnloadedStatus, QCamera::UnloadingStatus, QCamera::StoppingStatus }, //Unloaded state
- { QCamera::LoadingStatus, QCamera::LoadedStatus, QCamera::StoppingStatus }, //Loaded state
- { QCamera::LoadingStatus, QCamera::StartingStatus, QCamera::ActiveStatus } //ActiveState
- };
-
- return statusTable[m_state][m_session->state()];
-}
-
-void AVFCameraControl::updateStatus()
-{
- QCamera::Status newStatus = status();
-
- if (m_lastStatus != newStatus) {
- qDebugCamera() << "Camera status changed: " << m_lastStatus << " -> " << newStatus;
- m_lastStatus = newStatus;
- Q_EMIT statusChanged(m_lastStatus);
- }
-}
-
-QCamera::CaptureModes AVFCameraControl::captureMode() const
-{
- return m_captureMode;
-}
-
-void AVFCameraControl::setCaptureMode(QCamera::CaptureModes mode)
-{
- if (m_captureMode == mode)
- return;
-
- if (!isCaptureModeSupported(mode)) {
- Q_EMIT error(QCamera::NotSupportedFeatureError, tr("Requested capture mode is not supported"));
- return;
- }
-
- m_captureMode = mode;
- Q_EMIT captureModeChanged(mode);
-}
-
-bool AVFCameraControl::isCaptureModeSupported(QCamera::CaptureModes mode) const
-{
- //all the capture modes are supported, including QCamera::CaptureStillImage | QCamera::CaptureVideo
- return (mode & (QCamera::CaptureStillImage | QCamera::CaptureVideo)) == mode;
-}
-
-bool AVFCameraControl::canChangeProperty(QCameraControl::PropertyChangeType changeType, QCamera::Status status) const
-{
- Q_UNUSED(changeType);
- Q_UNUSED(status);
-
- return true;
-}
-
-#include "moc_avfcameracontrol.cpp"
diff --git a/src/plugins/avfoundation/camera/avfcameradebug.h b/src/plugins/avfoundation/camera/avfcameradebug.h
deleted file mode 100644
index 8838122e0..000000000
--- a/src/plugins/avfoundation/camera/avfcameradebug.h
+++ /dev/null
@@ -1,57 +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 AVFDEBUG_H
-#define AVFDEBUG_H
-
-#include "qtmultimediaglobal.h"
-
-#include <QtCore/qdebug.h>
-
-QT_USE_NAMESPACE
-
-//#define AVF_DEBUG_CAMERA
-
-#ifdef AVF_DEBUG_CAMERA
-#define qDebugCamera qDebug
-#else
-#define qDebugCamera QT_NO_QDEBUG_MACRO
-#endif
-
-#endif
diff --git a/src/plugins/avfoundation/camera/avfcameradevicecontrol.h b/src/plugins/avfoundation/camera/avfcameradevicecontrol.h
deleted file mode 100644
index 0a16c0408..000000000
--- a/src/plugins/avfoundation/camera/avfcameradevicecontrol.h
+++ /dev/null
@@ -1,85 +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 AVFCAMERADEVICECONTROL_H
-#define AVFCAMERADEVICECONTROL_H
-
-#include <QtMultimedia/qvideodeviceselectorcontrol.h>
-#include <QtCore/qstringlist.h>
-
-#import <AVFoundation/AVFoundation.h>
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraSession;
-class AVFCameraService;
-
-class AVFCameraDeviceControl : public QVideoDeviceSelectorControl
-{
-Q_OBJECT
-public:
- AVFCameraDeviceControl(AVFCameraService *service, QObject *parent = nullptr);
- ~AVFCameraDeviceControl();
-
- int deviceCount() const override;
-
- QString deviceName(int index) const override;
- QString deviceDescription(int index) const override;
-
- int defaultDevice() const override;
- int selectedDevice() const override;
-
-public Q_SLOTS:
- void setSelectedDevice(int index) override;
-
-public:
- //device changed since the last createCaptureDevice()
- bool isDirty() const { return m_dirty; }
- AVCaptureDevice *createCaptureDevice();
-
-private:
- AVFCameraService *m_service;
-
- int m_selectedDevice;
- bool m_dirty;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/avfoundation/camera/avfcameradevicecontrol.mm b/src/plugins/avfoundation/camera/avfcameradevicecontrol.mm
deleted file mode 100644
index 907373b61..000000000
--- a/src/plugins/avfoundation/camera/avfcameradevicecontrol.mm
+++ /dev/null
@@ -1,123 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfcameradebug.h"
-#include "avfcameradevicecontrol.h"
-#include "avfcameraservice.h"
-#include "avfcamerasession.h"
-
-QT_USE_NAMESPACE
-
-AVFCameraDeviceControl::AVFCameraDeviceControl(AVFCameraService *service, QObject *parent)
- : QVideoDeviceSelectorControl(parent)
- , m_service(service)
- , m_selectedDevice(0)
- , m_dirty(true)
-{
- Q_UNUSED(m_service);
-}
-
-AVFCameraDeviceControl::~AVFCameraDeviceControl()
-{
-}
-
-int AVFCameraDeviceControl::deviceCount() const
-{
- return AVFCameraSession::availableCameraDevices().count();
-}
-
-QString AVFCameraDeviceControl::deviceName(int index) const
-{
- const QList<AVFCameraInfo> &devices = AVFCameraSession::availableCameraDevices();
- if (index < 0 || index >= devices.count())
- return QString();
-
- return QString::fromUtf8(devices.at(index).deviceId);
-}
-
-QString AVFCameraDeviceControl::deviceDescription(int index) const
-{
- const QList<AVFCameraInfo> &devices = AVFCameraSession::availableCameraDevices();
- if (index < 0 || index >= devices.count())
- return QString();
-
- return devices.at(index).description;
-}
-
-int AVFCameraDeviceControl::defaultDevice() const
-{
- return AVFCameraSession::defaultCameraIndex();
-}
-
-int AVFCameraDeviceControl::selectedDevice() const
-{
- return m_selectedDevice;
-}
-
-void AVFCameraDeviceControl::setSelectedDevice(int index)
-{
- if (index >= 0 &&
- index < deviceCount() &&
- index != m_selectedDevice) {
- m_dirty = true;
- m_selectedDevice = index;
- Q_EMIT selectedDeviceChanged(index);
- Q_EMIT selectedDeviceChanged(deviceName(index));
- }
-}
-
-AVCaptureDevice *AVFCameraDeviceControl::createCaptureDevice()
-{
- m_dirty = false;
- AVCaptureDevice *device = nullptr;
-
- QString deviceId = deviceName(m_selectedDevice);
- if (!deviceId.isEmpty()) {
- device = [AVCaptureDevice deviceWithUniqueID:
- [NSString stringWithUTF8String:
- deviceId.toUtf8().constData()]];
- }
-
- if (!device)
- device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
-
- return device;
-}
-
-#include "moc_avfcameradevicecontrol.cpp"
diff --git a/src/plugins/avfoundation/camera/avfcameraexposurecontrol.h b/src/plugins/avfoundation/camera/avfcameraexposurecontrol.h
deleted file mode 100644
index 0e2846889..000000000
--- a/src/plugins/avfoundation/camera/avfcameraexposurecontrol.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 AVFCAMERAEXPOSURECONTROL_H
-#define AVFCAMERAEXPOSURECONTROL_H
-
-#include <QtMultimedia/qcameraexposurecontrol.h>
-#include <QtMultimedia/qcameraexposure.h>
-
-#include <QtCore/qglobal.h>
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraSession;
-class AVFCameraService;
-
-class AVFCameraExposureControl : public QCameraExposureControl
-{
- Q_OBJECT
-
-public:
- AVFCameraExposureControl(AVFCameraService *service);
-
- bool isParameterSupported(ExposureParameter parameter) const override;
- QVariantList supportedParameterRange(ExposureParameter parameter,
- bool *continuous) const override;
-
- QVariant requestedValue(ExposureParameter parameter) const override;
- QVariant actualValue(ExposureParameter parameter) const override;
- bool setValue(ExposureParameter parameter, const QVariant &value) override;
-
-private Q_SLOTS:
- void cameraStateChanged();
-
-private:
- AVFCameraService *m_service;
- AVFCameraSession *m_session;
-
- QVariant m_requestedMode;
- QVariant m_requestedCompensation;
- QVariant m_requestedShutterSpeed;
- QVariant m_requestedISO;
-
- // Aux. setters:
- bool setExposureMode(const QVariant &value);
- bool setExposureCompensation(const QVariant &value);
- bool setShutterSpeed(const QVariant &value);
- bool setISO(const QVariant &value);
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm b/src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm
deleted file mode 100644
index 62387604f..000000000
--- a/src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm
+++ /dev/null
@@ -1,661 +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 "avfcameraexposurecontrol.h"
-#include "avfcamerautility.h"
-#include "avfcamerasession.h"
-#include "avfcameraservice.h"
-#include "avfcameradebug.h"
-
-#include <QtCore/qvariant.h>
-#include <QtCore/qpointer.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qpair.h>
-
-#include <AVFoundation/AVFoundation.h>
-
-#include <limits>
-
-QT_BEGIN_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_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);
-
- AVCaptureDeviceFormat *activeFormat = captureDevice.activeFormat;
- if (!activeFormat) {
- qDebugCamera() << 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) {
- qDebugCamera() << 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, QCameraExposure::ExposureMode mode,
- AVCaptureExposureMode &avMode)
-{
- // Test if mode supported and convert.
- Q_ASSERT(captureDevice);
-
- if (mode == QCameraExposure::ExposureAuto) {
- if ([captureDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
- avMode = AVCaptureExposureModeContinuousAutoExposure;
- return true;
- }
- }
-
- if (mode == QCameraExposure::ExposureManual) {
- if ([captureDevice isExposureModeSupported:AVCaptureExposureModeCustom]) {
- avMode = AVCaptureExposureModeCustom;
- return true;
- }
- }
-
- return false;
-}
-
-// We set ISO/exposure duration with completion handlers, completion handlers try
-// to avoid dangling pointers (thus QPointer for QObjects) and not to create
-// a reference loop (in case we have ARC).
-
-void qt_set_exposure_bias(QPointer<AVFCameraService> service, QPointer<AVFCameraExposureControl> control,
- AVCaptureDevice *captureDevice, float bias)
-{
- Q_ASSERT(captureDevice);
-
- __block AVCaptureDevice *device = captureDevice; //For ARC.
-
- void (^completionHandler)(CMTime syncTime) = ^(CMTime) {
- // Test that service control is still alive and that
- // capture device is our device, if yes - emit actual value changed.
- if (service) {
- if (control) {
- if (service->session() && service->session()->videoCaptureDevice() == device)
- Q_EMIT control->actualValueChanged(int(QCameraExposureControl::ExposureCompensation));
- }
- }
- device = nil;
- };
-
- [captureDevice setExposureTargetBias:bias completionHandler:completionHandler];
-}
-
-void qt_set_duration_iso(QPointer<AVFCameraService> service, QPointer<AVFCameraExposureControl> control,
- AVCaptureDevice *captureDevice, CMTime duration, float iso)
-{
- Q_ASSERT(captureDevice);
-
- __block AVCaptureDevice *device = captureDevice; //For ARC.
- const bool setDuration = CMTimeCompare(duration, AVCaptureExposureDurationCurrent);
- const bool setISO = !qFuzzyCompare(iso, AVCaptureISOCurrent);
-
- void (^completionHandler)(CMTime syncTime) = ^(CMTime) {
- // Test that service control is still alive and that
- // capture device is our device, if yes - emit actual value changed.
- if (service) {
- if (control) {
- if (service->session() && service->session()->videoCaptureDevice() == device) {
- if (setDuration)
- Q_EMIT control->actualValueChanged(int(QCameraExposureControl::ShutterSpeed));
- if (setISO)
- Q_EMIT control->actualValueChanged(int(QCameraExposureControl::ISO));
- }
- }
- }
- device = nil;
- };
-
- [captureDevice setExposureModeCustomWithDuration:duration
- ISO:iso
- completionHandler:completionHandler];
-}
-
-#endif // defined(Q_OS_IOS)
-
-} // Unnamed namespace.
-
-AVFCameraExposureControl::AVFCameraExposureControl(AVFCameraService *service)
- : m_service(service),
- m_session(nullptr)
-{
- Q_ASSERT(service);
- m_session = m_service->session();
- Q_ASSERT(m_session);
-
- connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(cameraStateChanged()));
-}
-
-bool AVFCameraExposureControl::isParameterSupported(ExposureParameter parameter) const
-{
-#ifdef Q_OS_IOS
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice)
- return false;
-
- // These are the parameters we have an API to support:
- return parameter == QCameraExposureControl::ISO
- || parameter == QCameraExposureControl::ShutterSpeed
- || parameter == QCameraExposureControl::ExposureCompensation
- || parameter == QCameraExposureControl::ExposureMode;
-#else
- Q_UNUSED(parameter);
- return false;
-#endif
-}
-
-QVariantList AVFCameraExposureControl::supportedParameterRange(ExposureParameter parameter,
- bool *continuous) const
-{
- QVariantList parameterRange;
-#ifdef Q_OS_IOS
-
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice || !isParameterSupported(parameter)) {
- qDebugCamera() << Q_FUNC_INFO << "parameter not supported";
- return parameterRange;
- }
-
- if (continuous)
- *continuous = false;
-
- AVCaptureDeviceFormat *activeFormat = captureDevice.activeFormat;
-
- if (parameter == QCameraExposureControl::ISO) {
- if (!activeFormat) {
- qDebugCamera() << Q_FUNC_INFO << "failed to obtain capture device format";
- return parameterRange;
- }
-
- if (!qt_check_ISO_range(activeFormat)) {
- qDebugCamera() << Q_FUNC_INFO << "ISO range can not be represented as int";
- return parameterRange;
- }
-
- parameterRange << QVariant(int(activeFormat.minISO));
- parameterRange << QVariant(int(activeFormat.maxISO));
- if (continuous)
- *continuous = true;
- } else if (parameter == QCameraExposureControl::ExposureCompensation) {
- parameterRange << captureDevice.minExposureTargetBias;
- parameterRange << captureDevice.maxExposureTargetBias;
- if (continuous)
- *continuous = true;
- } else if (parameter == QCameraExposureControl::ShutterSpeed) {
- if (!activeFormat) {
- qDebugCamera() << Q_FUNC_INFO << "failed to obtain capture device format";
- return parameterRange;
- }
-
- // CMTimeGetSeconds returns Float64, test the conversion below, if it's valid?
- parameterRange << qreal(CMTimeGetSeconds(activeFormat.minExposureDuration));
- parameterRange << qreal(CMTimeGetSeconds(activeFormat.maxExposureDuration));
-
- if (continuous)
- *continuous = true;
- } else if (parameter == QCameraExposureControl::ExposureMode) {
- if ([captureDevice isExposureModeSupported:AVCaptureExposureModeCustom])
- parameterRange << QVariant::fromValue(QCameraExposure::ExposureManual);
-
- if ([captureDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure])
- parameterRange << QVariant::fromValue(QCameraExposure::ExposureAuto);
- }
-#else
- Q_UNUSED(parameter);
- Q_UNUSED(continuous);
-#endif
- return parameterRange;
-}
-
-QVariant AVFCameraExposureControl::requestedValue(ExposureParameter parameter) const
-{
- if (!isParameterSupported(parameter)) {
- qDebugCamera() << Q_FUNC_INFO << "parameter not supported";
- return QVariant();
- }
-
- if (parameter == QCameraExposureControl::ExposureMode)
- return m_requestedMode;
-
- if (parameter == QCameraExposureControl::ExposureCompensation)
- return m_requestedCompensation;
-
- if (parameter == QCameraExposureControl::ShutterSpeed)
- return m_requestedShutterSpeed;
-
- if (parameter == QCameraExposureControl::ISO)
- return m_requestedISO;
-
- return QVariant();
-}
-
-QVariant AVFCameraExposureControl::actualValue(ExposureParameter parameter) const
-{
-#ifdef Q_OS_IOS
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice || !isParameterSupported(parameter)) {
- // Actually, at the moment !captiredevice => !isParameterSupported.
- qDebugCamera() << Q_FUNC_INFO << "parameter not supported";
- return QVariant();
- }
-
- if (parameter == QCameraExposureControl::ExposureMode) {
- // This code expects exposureMode to be continuous by default ...
- if (captureDevice.exposureMode == AVCaptureExposureModeContinuousAutoExposure)
- return QVariant::fromValue(QCameraExposure::ExposureAuto);
- return QVariant::fromValue(QCameraExposure::ExposureManual);
- }
-
- if (parameter == QCameraExposureControl::ExposureCompensation)
- return captureDevice.exposureTargetBias;
-
- if (parameter == QCameraExposureControl::ShutterSpeed)
- return qreal(CMTimeGetSeconds(captureDevice.exposureDuration));
-
- if (parameter == QCameraExposureControl::ISO) {
- if (captureDevice.activeFormat && qt_check_ISO_range(captureDevice.activeFormat)
- && qt_check_ISO_conversion(captureDevice.ISO)) {
- // Can be represented as int ...
- return int(captureDevice.ISO);
- } else {
- qDebugCamera() << Q_FUNC_INFO << "ISO can not be represented as int";
- return QVariant();
- }
- }
-#else
- Q_UNUSED(parameter);
-#endif
- return QVariant();
-}
-
-bool AVFCameraExposureControl::setValue(ExposureParameter parameter, const QVariant &value)
-{
- if (parameter == QCameraExposureControl::ExposureMode)
- return setExposureMode(value);
- else if (parameter == QCameraExposureControl::ExposureCompensation)
- return setExposureCompensation(value);
- else if (parameter == QCameraExposureControl::ShutterSpeed)
- return setShutterSpeed(value);
- else if (parameter == QCameraExposureControl::ISO)
- return setISO(value);
-
- return false;
-}
-
-bool AVFCameraExposureControl::setExposureMode(const QVariant &value)
-{
-#ifdef Q_OS_IOS
- if (!value.canConvert<QCameraExposure::ExposureMode>()) {
- qDebugCamera() << Q_FUNC_INFO << "invalid exposure mode value,"
- << "QCameraExposure::ExposureMode expected";
- return false;
- }
-
- const QCameraExposure::ExposureMode qtMode = value.value<QCameraExposure::ExposureMode>();
- if (qtMode != QCameraExposure::ExposureAuto && qtMode != QCameraExposure::ExposureManual) {
- qDebugCamera() << Q_FUNC_INFO << "exposure mode not supported";
- return false;
- }
-
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice) {
- m_requestedMode = value;
- Q_EMIT requestedValueChanged(int(QCameraExposureControl::ExposureMode));
- return true;
- }
-
- AVCaptureExposureMode avMode = AVCaptureExposureModeAutoExpose;
- if (!qt_convert_exposure_mode(captureDevice, qtMode, avMode)) {
- qDebugCamera() << Q_FUNC_INFO << "exposure mode not supported";
- return false;
- }
-
- const AVFConfigurationLock lock(captureDevice);
- if (!lock) {
- qDebugCamera() << Q_FUNC_INFO << "failed to lock a capture device"
- << "for configuration";
- return false;
- }
-
- m_requestedMode = value;
- [captureDevice setExposureMode:avMode];
- Q_EMIT requestedValueChanged(int(QCameraExposureControl::ExposureMode));
- Q_EMIT actualValueChanged(int(QCameraExposureControl::ExposureMode));
-
- return true;
-#else
- Q_UNUSED(value);
- return false;
-#endif
-}
-
-bool AVFCameraExposureControl::setExposureCompensation(const QVariant &value)
-{
-#ifdef Q_OS_IOS
- if (!value.canConvert<qreal>()) {
- qDebugCamera() << Q_FUNC_INFO << "invalid exposure compensation"
- <<"value, floating point number expected";
- return false;
- }
-
- const qreal bias = value.toReal();
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice) {
- m_requestedCompensation = value;
- Q_EMIT requestedValueChanged(int(QCameraExposureControl::ExposureCompensation));
- return true;
- }
-
- if (bias < captureDevice.minExposureTargetBias || bias > captureDevice.maxExposureTargetBias) {
- // TODO: mixed fp types!
- qDebugCamera() << Q_FUNC_INFO << "exposure compenstation value is"
- << "out of range";
- return false;
- }
-
- const AVFConfigurationLock lock(captureDevice);
- if (!lock) {
- qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
- return false;
- }
-
- qt_set_exposure_bias(m_service, this, captureDevice, bias);
- m_requestedCompensation = value;
- Q_EMIT requestedValueChanged(int(QCameraExposureControl::ExposureCompensation));
-
- return true;
-#else
- Q_UNUSED(value);
- return false;
-#endif
-}
-
-bool AVFCameraExposureControl::setShutterSpeed(const QVariant &value)
-{
-#ifdef Q_OS_IOS
- if (value.isNull())
- return setExposureMode(QVariant::fromValue(QCameraExposure::ExposureAuto));
-
- if (!value.canConvert<qreal>()) {
- qDebugCamera() << Q_FUNC_INFO << "invalid shutter speed"
- << "value, floating point number expected";
- return false;
- }
-
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice) {
- m_requestedShutterSpeed = value;
- Q_EMIT requestedValueChanged(int(QCameraExposureControl::ShutterSpeed));
- return true;
- }
-
- const CMTime newDuration = CMTimeMakeWithSeconds(value.toReal(),
- captureDevice.exposureDuration.timescale);
- if (!qt_check_exposure_duration(captureDevice, newDuration)) {
- qDebugCamera() << Q_FUNC_INFO << "shutter speed value is out of range";
- return false;
- }
-
- const AVFConfigurationLock lock(captureDevice);
- if (!lock) {
- qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
- return false;
- }
-
- // Setting the shutter speed (exposure duration in Apple's terms,
- // since there is no shutter actually) will also reset
- // exposure mode into custom mode.
- qt_set_duration_iso(m_service, this, captureDevice, newDuration, AVCaptureISOCurrent);
-
- m_requestedShutterSpeed = value;
- Q_EMIT requestedValueChanged(int(QCameraExposureControl::ShutterSpeed));
-
- return true;
-#else
- Q_UNUSED(value);
- return false;
-#endif
-}
-
-bool AVFCameraExposureControl::setISO(const QVariant &value)
-{
-#ifdef Q_OS_IOS
- if (value.isNull())
- return setExposureMode(QVariant::fromValue(QCameraExposure::ExposureAuto));
-
- if (!value.canConvert<int>()) {
- qDebugCamera() << Q_FUNC_INFO << "invalid ISO value, int expected";
- return false;
- }
-
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice) {
- m_requestedISO = value;
- Q_EMIT requestedValueChanged(int(QCameraExposureControl::ISO));
- return true;
- }
-
- if (!qt_check_ISO_value(captureDevice, value.toInt())) {
- qDebugCamera() << Q_FUNC_INFO << "ISO value is out of range";
- return false;
- }
-
- const AVFConfigurationLock lock(captureDevice);
- if (!lock) {
- qDebugCamera() << Q_FUNC_INFO << "failed to lock a capture device"
- << "for configuration";
- return false;
- }
-
- // Setting the ISO will also reset
- // exposure mode to the custom mode.
- qt_set_duration_iso(m_service, this, captureDevice, AVCaptureExposureDurationCurrent, value.toInt());
-
- m_requestedISO = value;
- Q_EMIT requestedValueChanged(int(QCameraExposureControl::ISO));
-
- return true;
-#else
- Q_UNUSED(value);
- return false;
-#endif
-}
-
-void AVFCameraExposureControl::cameraStateChanged()
-{
-#ifdef Q_OS_IOS
- if (m_session->state() != QCamera::ActiveState)
- return;
-
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice) {
- qDebugCamera() << Q_FUNC_INFO << "capture device is nil, but the session"
- << "state is 'active'";
- return;
- }
-
- Q_EMIT parameterRangeChanged(int(QCameraExposureControl::ExposureCompensation));
- Q_EMIT parameterRangeChanged(int(QCameraExposureControl::ExposureMode));
- Q_EMIT parameterRangeChanged(int(QCameraExposureControl::ShutterSpeed));
- Q_EMIT parameterRangeChanged(int(QCameraExposureControl::ISO));
-
- const AVFConfigurationLock lock(captureDevice);
-
- CMTime newDuration = AVCaptureExposureDurationCurrent;
- bool setCustomMode = false;
-
- if (!m_requestedShutterSpeed.isNull()
- && !qt_exposure_duration_equal(captureDevice, m_requestedShutterSpeed.toReal())) {
- newDuration = CMTimeMakeWithSeconds(m_requestedShutterSpeed.toReal(),
- captureDevice.exposureDuration.timescale);
- if (!qt_check_exposure_duration(captureDevice, newDuration)) {
- qDebugCamera() << Q_FUNC_INFO << "requested exposure duration is out of range";
- return;
- }
- setCustomMode = true;
- }
-
- float newISO = AVCaptureISOCurrent;
- if (!m_requestedISO.isNull() && !qt_iso_equal(captureDevice, m_requestedISO.toInt())) {
- newISO = m_requestedISO.toInt();
- if (!qt_check_ISO_value(captureDevice, newISO)) {
- qDebugCamera() << Q_FUNC_INFO << "requested ISO value is out of range";
- return;
- }
- setCustomMode = true;
- }
-
- if (!m_requestedCompensation.isNull()
- && !qt_exposure_bias_equal(captureDevice, m_requestedCompensation.toReal())) {
- // TODO: mixed fpns.
- const qreal bias = m_requestedCompensation.toReal();
- if (bias < captureDevice.minExposureTargetBias || bias > captureDevice.maxExposureTargetBias) {
- qDebugCamera() << Q_FUNC_INFO << "exposure compenstation value is"
- << "out of range";
- return;
- }
- if (!lock) {
- qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
- return;
- }
- qt_set_exposure_bias(m_service, this, captureDevice, bias);
- }
-
- // Setting shutter speed (exposure duration) or ISO values
- // also reset exposure mode into Custom. With this settings
- // we ignore any attempts to set exposure mode.
-
- if (setCustomMode) {
- if (!lock)
- qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
- else
- qt_set_duration_iso(m_service, this, captureDevice, newDuration, newISO);
- return;
- }
-
- if (!m_requestedMode.isNull()) {
- QCameraExposure::ExposureMode qtMode = m_requestedMode.value<QCameraExposure::ExposureMode>();
- AVCaptureExposureMode avMode = AVCaptureExposureModeContinuousAutoExposure;
- if (!qt_convert_exposure_mode(captureDevice, qtMode, avMode)) {
- qDebugCamera() << Q_FUNC_INFO << "requested exposure mode is not supported";
- return;
- }
-
- if (avMode == captureDevice.exposureMode)
- return;
-
- if (!lock) {
- qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
- return;
- }
-
- [captureDevice setExposureMode:avMode];
- Q_EMIT actualValueChanged(int(QCameraExposureControl::ExposureMode));
- }
-#endif
-}
-
-QT_END_NAMESPACE
-
-#include "moc_avfcameraexposurecontrol.cpp"
diff --git a/src/plugins/avfoundation/camera/avfcameraflashcontrol.h b/src/plugins/avfoundation/camera/avfcameraflashcontrol.h
deleted file mode 100644
index 58403e829..000000000
--- a/src/plugins/avfoundation/camera/avfcameraflashcontrol.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 AVFCAMERAFLASHCONTROL_H
-#define AVFCAMERAFLASHCONTROL_H
-
-#include <QtMultimedia/qcameraflashcontrol.h>
-#include <QtMultimedia/qcamera.h>
-
-#include <QtCore/qlist.h>
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraService;
-class AVFCameraSession;
-
-class AVFCameraFlashControl : public QCameraFlashControl
-{
- Q_OBJECT
-public:
- AVFCameraFlashControl(AVFCameraService *service);
-
- QCameraExposure::FlashModes flashMode() const override;
- void setFlashMode(QCameraExposure::FlashModes mode) override;
- bool isFlashModeSupported(QCameraExposure::FlashModes mode) const override;
- bool isFlashReady() const override;
-
-private Q_SLOTS:
- void cameraStateChanged(QCamera::State newState);
-
-private:
- bool applyFlashSettings();
-
- AVFCameraService *m_service;
- AVFCameraSession *m_session;
-
- // Set of bits:
- QCameraExposure::FlashModes m_supportedModes;
- // Only one bit set actually:
- QCameraExposure::FlashModes m_flashMode;
-};
-
-QT_END_NAMESPACE
-
-
-#endif // AVFCAMERAFLASHCONTROL_H
-
diff --git a/src/plugins/avfoundation/camera/avfcameraflashcontrol.mm b/src/plugins/avfoundation/camera/avfcameraflashcontrol.mm
deleted file mode 100644
index 42303ce17..000000000
--- a/src/plugins/avfoundation/camera/avfcameraflashcontrol.mm
+++ /dev/null
@@ -1,233 +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 "avfcameraflashcontrol.h"
-#include "avfcamerautility.h"
-#include "avfcamerasession.h"
-#include "avfcameraservice.h"
-#include "avfcameradebug.h"
-
-#include <QtCore/qdebug.h>
-
-#include <AVFoundation/AVFoundation.h>
-
-
-AVFCameraFlashControl::AVFCameraFlashControl(AVFCameraService *service)
- : m_service(service)
- , m_session(nullptr)
- , m_supportedModes(QCameraExposure::FlashOff)
- , m_flashMode(QCameraExposure::FlashOff)
-{
- Q_ASSERT(service);
- m_session = m_service->session();
- Q_ASSERT(m_session);
-
- connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(cameraStateChanged(QCamera::State)));
-}
-
-QCameraExposure::FlashModes AVFCameraFlashControl::flashMode() const
-{
- return m_flashMode;
-}
-
-void AVFCameraFlashControl::setFlashMode(QCameraExposure::FlashModes mode)
-{
- if (m_flashMode == mode)
- return;
-
- if (m_session->state() == QCamera::ActiveState && !isFlashModeSupported(mode)) {
- qDebugCamera() << Q_FUNC_INFO << "unsupported mode" << mode;
- return;
- }
-
- m_flashMode = mode;
-
- if (m_session->state() != QCamera::ActiveState)
- return;
-
- applyFlashSettings();
-}
-
-bool AVFCameraFlashControl::isFlashModeSupported(QCameraExposure::FlashModes mode) const
-{
- // From what QCameraExposure has, we can support only these:
- // FlashAuto = 0x1,
- // FlashOff = 0x2,
- // FlashOn = 0x4,
- // AVCaptureDevice has these flash modes:
- // AVCaptureFlashModeAuto
- // AVCaptureFlashModeOff
- // AVCaptureFlashModeOn
- // QCameraExposure also has:
- // FlashTorch = 0x20, --> "Constant light source."
- // FlashVideoLight = 0x40. --> "Constant light source."
- // AVCaptureDevice:
- // AVCaptureTorchModeOff (no mapping)
- // AVCaptureTorchModeOn --> FlashVideoLight
- // AVCaptureTorchModeAuto (no mapping)
-
- return m_supportedModes & mode;
-}
-
-bool AVFCameraFlashControl::isFlashReady() const
-{
- if (m_session->state() != QCamera::ActiveState)
- return false;
-
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice)
- return false;
-
- if (!captureDevice.hasFlash && !captureDevice.hasTorch)
- return false;
-
- if (!isFlashModeSupported(m_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."
- if (m_flashMode != QCameraExposure::FlashVideoLight)
- return [captureDevice isFlashAvailable];
-
- return [captureDevice isTorchAvailable];
-#endif
-
- return true;
-}
-
-void AVFCameraFlashControl::cameraStateChanged(QCamera::State newState)
-{
- if (newState == QCamera::UnloadedState) {
- m_supportedModes = QCameraExposure::FlashOff;
- Q_EMIT flashReady(false);
- } else if (newState == QCamera::ActiveState) {
- m_supportedModes = QCameraExposure::FlashOff;
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice) {
- qDebugCamera() << Q_FUNC_INFO << "no capture device in 'Active' state";
- Q_EMIT flashReady(false);
- return;
- }
-
- if (captureDevice.hasFlash) {
- if ([captureDevice isFlashModeSupported:AVCaptureFlashModeOn])
- m_supportedModes |= QCameraExposure::FlashOn;
- if ([captureDevice isFlashModeSupported:AVCaptureFlashModeAuto])
- m_supportedModes |= QCameraExposure::FlashAuto;
- }
-
- if (captureDevice.hasTorch && [captureDevice isTorchModeSupported:AVCaptureTorchModeOn])
- m_supportedModes |= QCameraExposure::FlashVideoLight;
-
- Q_EMIT flashReady(applyFlashSettings());
- }
-}
-
-bool AVFCameraFlashControl::applyFlashSettings()
-{
- Q_ASSERT(m_session->requestedState() == QCamera::ActiveState);
-
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice) {
- qDebugCamera() << Q_FUNC_INFO << "no capture device found";
- return false;
- }
-
- if (!isFlashModeSupported(m_flashMode)) {
- qDebugCamera() << Q_FUNC_INFO << "unsupported mode" << m_flashMode;
- return false;
- }
-
- if (!captureDevice.hasFlash && !captureDevice.hasTorch) {
- // FlashOff is the only mode we support.
- // Return false - flash is not ready.
- return false;
- }
-
- const AVFConfigurationLock lock(captureDevice);
-
- if (m_flashMode != QCameraExposure::FlashVideoLight) {
- if (captureDevice.torchMode != AVCaptureTorchModeOff) {
-#ifdef Q_OS_IOS
- if (![captureDevice isTorchAvailable]) {
- qDebugCamera() << Q_FUNC_INFO << "torch is not available at the moment";
- return false;
- }
-#endif
- captureDevice.torchMode = AVCaptureTorchModeOff;
- }
-#ifdef Q_OS_IOS
- if (![captureDevice isFlashAvailable]) {
- // We'd like to switch flash (into some mode), but it's not available:
- qDebugCamera() << Q_FUNC_INFO << "flash is not available at the moment";
- return false;
- }
-#endif
- } else {
- if (captureDevice.flashMode != AVCaptureFlashModeOff) {
-#ifdef Q_OS_IOS
- if (![captureDevice isFlashAvailable]) {
- qDebugCamera() << Q_FUNC_INFO << "flash is not available at the moment";
- return false;
- }
-#endif
- captureDevice.flashMode = AVCaptureFlashModeOff;
- }
-
-#ifdef Q_OS_IOS
- if (![captureDevice isTorchAvailable]) {
- qDebugCamera() << Q_FUNC_INFO << "torch is not available at the moment";
- return false;
- }
-#endif
- }
-
- if (m_flashMode == QCameraExposure::FlashOff)
- captureDevice.flashMode = AVCaptureFlashModeOff;
- else if (m_flashMode == QCameraExposure::FlashOn)
- captureDevice.flashMode = AVCaptureFlashModeOn;
- else if (m_flashMode == QCameraExposure::FlashAuto)
- captureDevice.flashMode = AVCaptureFlashModeAuto;
- else if (m_flashMode == QCameraExposure::FlashVideoLight)
- captureDevice.torchMode = AVCaptureTorchModeOn;
-
- return true;
-}
diff --git a/src/plugins/avfoundation/camera/avfcamerafocuscontrol.h b/src/plugins/avfoundation/camera/avfcamerafocuscontrol.h
deleted file mode 100644
index 26135e534..000000000
--- a/src/plugins/avfoundation/camera/avfcamerafocuscontrol.h
+++ /dev/null
@@ -1,87 +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 AVFCAMERAFOCUSCONTROL_H
-#define AVFCAMERAFOCUSCONTROL_H
-
-#include <QtCore/qscopedpointer.h>
-#include <QtCore/qglobal.h>
-
-#include <qcamerafocuscontrol.h>
-
-@class AVCaptureDevice;
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraService;
-class AVFCameraSession;
-
-class AVFCameraFocusControl : public QCameraFocusControl
-{
- Q_OBJECT
-public:
- explicit AVFCameraFocusControl(AVFCameraService *service);
-
- QCameraFocus::FocusModes focusMode() const override;
- void setFocusMode(QCameraFocus::FocusModes mode) override;
- bool isFocusModeSupported(QCameraFocus::FocusModes mode) const override;
-
- QCameraFocus::FocusPointMode focusPointMode() const override;
- void setFocusPointMode(QCameraFocus::FocusPointMode mode) override;
- bool isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const override;
- QPointF customFocusPoint() const override;
- void setCustomFocusPoint(const QPointF &point) override;
-
- QCameraFocusZoneList focusZones() const override;
-
-private Q_SLOTS:
- void cameraStateChanged();
-
-private:
-
- AVFCameraSession *m_session;
- QCameraFocus::FocusModes m_focusMode;
- QCameraFocus::FocusPointMode m_focusPointMode;
- QPointF m_customFocusPoint;
- QPointF m_actualFocusPoint;
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFCAMERAFOCUSCONTROL_H
diff --git a/src/plugins/avfoundation/camera/avfcamerafocuscontrol.mm b/src/plugins/avfoundation/camera/avfcamerafocuscontrol.mm
deleted file mode 100644
index 265ccf3fd..000000000
--- a/src/plugins/avfoundation/camera/avfcamerafocuscontrol.mm
+++ /dev/null
@@ -1,306 +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 "avfcamerafocuscontrol.h"
-#include "avfcamerautility.h"
-#include "avfcameraservice.h"
-#include "avfcamerasession.h"
-#include "avfcameradebug.h"
-
-#include <QtCore/qdebug.h>
-
-#include <AVFoundation/AVFoundation.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace {
-
-bool qt_focus_mode_supported(QCameraFocus::FocusModes mode)
-{
- // Check if QCameraFocus::FocusMode has counterpart in AVFoundation.
-
- // AVFoundation has 'Manual', 'Auto' and 'Continuous',
- // where 'Manual' is actually 'Locked' + writable property 'lensPosition'.
- // Since Qt does not provide an API to manipulate a lens position, 'Maual' mode
- // (at the moment) is not supported.
- return mode == QCameraFocus::AutoFocus
- || mode == QCameraFocus::ContinuousFocus;
-}
-
-bool qt_focus_point_mode_supported(QCameraFocus::FocusPointMode mode)
-{
- return mode == QCameraFocus::FocusPointAuto
- || mode == QCameraFocus::FocusPointCustom
- || mode == QCameraFocus::FocusPointCenter;
-}
-
-AVCaptureFocusMode avf_focus_mode(QCameraFocus::FocusModes requestedMode)
-{
- if (requestedMode == QCameraFocus::AutoFocus)
- return AVCaptureFocusModeAutoFocus;
-
- return AVCaptureFocusModeContinuousAutoFocus;
-}
-
-}
-
-AVFCameraFocusControl::AVFCameraFocusControl(AVFCameraService *service)
- : m_session(service->session()),
- m_focusMode(QCameraFocus::ContinuousFocus),
- m_focusPointMode(QCameraFocus::FocusPointAuto),
- m_customFocusPoint(0.5f, 0.5f),
- m_actualFocusPoint(m_customFocusPoint)
-{
- Q_ASSERT(m_session);
- connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(cameraStateChanged()));
-}
-
-QCameraFocus::FocusModes AVFCameraFocusControl::focusMode() const
-{
- return m_focusMode;
-}
-
-void AVFCameraFocusControl::setFocusMode(QCameraFocus::FocusModes mode)
-{
- if (m_focusMode == mode)
- return;
-
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice) {
- if (qt_focus_mode_supported(mode)) {
- m_focusMode = mode;
- Q_EMIT focusModeChanged(m_focusMode);
- } else {
- qDebugCamera() << Q_FUNC_INFO
- << "focus mode not supported";
- }
- return;
- }
-
- if (isFocusModeSupported(mode)) {
- const AVFConfigurationLock lock(captureDevice);
- if (!lock) {
- qDebugCamera() << Q_FUNC_INFO
- << "failed to lock for configuration";
- return;
- }
-
- captureDevice.focusMode = avf_focus_mode(mode);
- m_focusMode = mode;
- } else {
- qDebugCamera() << Q_FUNC_INFO << "focus mode not supported";
- return;
- }
-
- Q_EMIT focusModeChanged(m_focusMode);
-}
-
-bool AVFCameraFocusControl::isFocusModeSupported(QCameraFocus::FocusModes mode) const
-{
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice)
- return false;
-
- if (!qt_focus_mode_supported(mode))
- return false;
-
- return [captureDevice isFocusModeSupported:avf_focus_mode(mode)];
-}
-
-QCameraFocus::FocusPointMode AVFCameraFocusControl::focusPointMode() const
-{
- return m_focusPointMode;
-}
-
-void AVFCameraFocusControl::setFocusPointMode(QCameraFocus::FocusPointMode mode)
-{
- if (m_focusPointMode == mode)
- return;
-
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice) {
- if (qt_focus_point_mode_supported(mode)) {
- m_focusPointMode = mode;
- Q_EMIT focusPointModeChanged(mode);
- }
- return;
- }
-
- if (isFocusPointModeSupported(mode)) {
- const AVFConfigurationLock lock(captureDevice);
- if (!lock) {
- qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
- return;
- }
-
- bool resetPOI = false;
- if (mode == QCameraFocus::FocusPointCenter || mode == QCameraFocus::FocusPointAuto) {
- if (m_actualFocusPoint != QPointF(0.5, 0.5)) {
- m_actualFocusPoint = QPointF(0.5, 0.5);
- resetPOI = true;
- }
- } else if (mode == QCameraFocus::FocusPointCustom) {
- if (m_actualFocusPoint != m_customFocusPoint) {
- m_actualFocusPoint = m_customFocusPoint;
- resetPOI = true;
- }
- } // else for any other mode in future.
-
- if (resetPOI) {
- const CGPoint focusPOI = CGPointMake(m_actualFocusPoint.x(), m_actualFocusPoint.y());
- [captureDevice setFocusPointOfInterest:focusPOI];
- }
- m_focusPointMode = mode;
- } else {
- qDebugCamera() << Q_FUNC_INFO << "focus point mode is not supported";
- return;
- }
-
- Q_EMIT focusPointModeChanged(mode);
-}
-
-bool AVFCameraFocusControl::isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const
-{
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice)
- return false;
-
- if (!qt_focus_point_mode_supported(mode))
- return false;
-
- return [captureDevice isFocusPointOfInterestSupported];
-}
-
-QPointF AVFCameraFocusControl::customFocusPoint() const
-{
- return m_customFocusPoint;
-}
-
-void AVFCameraFocusControl::setCustomFocusPoint(const QPointF &point)
-{
- if (m_customFocusPoint == point)
- return;
-
- if (!QRectF(0.f, 0.f, 1.f, 1.f).contains(point)) {
- qDebugCamera() << Q_FUNC_INFO << "invalid focus point (out of range)";
- return;
- }
-
- m_customFocusPoint = point;
- Q_EMIT customFocusPointChanged(m_customFocusPoint);
-
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice || m_focusPointMode != QCameraFocus::FocusPointCustom)
- return;
-
- if ([captureDevice isFocusPointOfInterestSupported]) {
- const AVFConfigurationLock lock(captureDevice);
- if (!lock) {
- qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
- return;
- }
-
- m_actualFocusPoint = m_customFocusPoint;
- const CGPoint focusPOI = CGPointMake(point.x(), point.y());
- [captureDevice setFocusPointOfInterest:focusPOI];
- if (m_focusMode != QCameraFocus::ContinuousFocus)
- [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
- } else {
- qDebugCamera() << Q_FUNC_INFO << "focus point of interest not supported";
- return;
- }
-}
-
-QCameraFocusZoneList AVFCameraFocusControl::focusZones() const
-{
- // Unsupported.
- return QCameraFocusZoneList();
-}
-
-void AVFCameraFocusControl::cameraStateChanged()
-{
- if (m_session->state() != QCamera::ActiveState)
- return;
-
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice) {
- qDebugCamera() << Q_FUNC_INFO << "capture device is nil in 'active' state";
- return;
- }
-
- const AVFConfigurationLock lock(captureDevice);
- if (m_customFocusPoint != m_actualFocusPoint
- && m_focusPointMode == QCameraFocus::FocusPointCustom) {
- if (![captureDevice isFocusPointOfInterestSupported]) {
- qDebugCamera() << Q_FUNC_INFO
- << "focus point of interest not supported";
- return;
- }
-
- if (!lock) {
- qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
- return;
- }
-
- m_actualFocusPoint = m_customFocusPoint;
- const CGPoint focusPOI = CGPointMake(m_customFocusPoint.x(), m_customFocusPoint.y());
- [captureDevice setFocusPointOfInterest:focusPOI];
- }
-
- if (m_focusMode != QCameraFocus::ContinuousFocus) {
- const AVCaptureFocusMode avMode = avf_focus_mode(m_focusMode);
- if (captureDevice.focusMode != avMode) {
- if (![captureDevice isFocusModeSupported:avMode]) {
- qDebugCamera() << Q_FUNC_INFO << "focus mode not supported";
- return;
- }
-
- if (!lock) {
- qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
- return;
- }
-
- [captureDevice setFocusMode:avMode];
- }
- }
-}
-
-QT_END_NAMESPACE
-
-#include "moc_avfcamerafocuscontrol.cpp"
diff --git a/src/plugins/avfoundation/camera/avfcamerainfocontrol.h b/src/plugins/avfoundation/camera/avfcamerainfocontrol.h
deleted file mode 100644
index c3aa11918..000000000
--- a/src/plugins/avfoundation/camera/avfcamerainfocontrol.h
+++ /dev/null
@@ -1,59 +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 AVFCAMERAINFOCONTROL_H
-#define AVFCAMERAINFOCONTROL_H
-
-#include <qcamerainfocontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraInfoControl : public QCameraInfoControl
-{
- Q_OBJECT
-public:
- explicit AVFCameraInfoControl(QObject *parent = nullptr);
-
- QCamera::Position cameraPosition(const QString &deviceName) const override;
- int cameraOrientation(const QString &deviceName) const override;
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFCAMERAINFOCONTROL_H
diff --git a/src/plugins/avfoundation/camera/avfcamerainfocontrol.mm b/src/plugins/avfoundation/camera/avfcamerainfocontrol.mm
deleted file mode 100644
index 26905b0c2..000000000
--- a/src/plugins/avfoundation/camera/avfcamerainfocontrol.mm
+++ /dev/null
@@ -1,60 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfcamerainfocontrol.h"
-#include "avfcamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-AVFCameraInfoControl::AVFCameraInfoControl(QObject *parent)
- : QCameraInfoControl(parent)
-{
-}
-
-QCamera::Position AVFCameraInfoControl::cameraPosition(const QString &deviceName) const
-{
- return AVFCameraSession::cameraDeviceInfo(deviceName.toUtf8()).position;
-}
-
-int AVFCameraInfoControl::cameraOrientation(const QString &deviceName) const
-{
- return AVFCameraSession::cameraDeviceInfo(deviceName.toUtf8()).orientation;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/avfoundation/camera/avfcamerametadatacontrol.h b/src/plugins/avfoundation/camera/avfcamerametadatacontrol.h
deleted file mode 100644
index cba29c394..000000000
--- a/src/plugins/avfoundation/camera/avfcamerametadatacontrol.h
+++ /dev/null
@@ -1,69 +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 AVFCAMERAMETADATACONTROL_H
-#define AVFCAMERAMETADATACONTROL_H
-
-#include <qmetadatawritercontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraService;
-
-class AVFCameraMetaDataControl : public QMetaDataWriterControl
-{
- Q_OBJECT
-public:
- AVFCameraMetaDataControl(AVFCameraService *service, QObject *parent = nullptr);
- virtual ~AVFCameraMetaDataControl();
-
- bool isMetaDataAvailable() const override;
- bool isWritable() const override;
-
- QVariant metaData(const QString &key) const override;
- void setMetaData(const QString &key, const QVariant &value) override;
- QStringList availableMetaData() const override;
-
-private:
- QMap<QString, QVariant> m_tags;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/avfoundation/camera/avfcamerametadatacontrol.mm b/src/plugins/avfoundation/camera/avfcamerametadatacontrol.mm
deleted file mode 100644
index 95a8a0d79..000000000
--- a/src/plugins/avfoundation/camera/avfcamerametadatacontrol.mm
+++ /dev/null
@@ -1,83 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfcamerametadatacontrol.h"
-#include "avfcamerasession.h"
-#include "avfcameraservice.h"
-
-QT_USE_NAMESPACE
-
-//metadata support is not implemented yet
-
-AVFCameraMetaDataControl::AVFCameraMetaDataControl(AVFCameraService *service, QObject *parent)
- :QMetaDataWriterControl(parent)
-{
- Q_UNUSED(service);
-}
-
-AVFCameraMetaDataControl::~AVFCameraMetaDataControl()
-{
-}
-
-bool AVFCameraMetaDataControl::isMetaDataAvailable() const
-{
- return !m_tags.isEmpty();
-}
-
-bool AVFCameraMetaDataControl::isWritable() const
-{
- return false;
-}
-
-QVariant AVFCameraMetaDataControl::metaData(const QString &key) const
-{
- return m_tags.value(key);
-}
-
-void AVFCameraMetaDataControl::setMetaData(const QString &key, const QVariant &value)
-{
- m_tags.insert(key, value);
-}
-
-QStringList AVFCameraMetaDataControl::availableMetaData() const
-{
- return m_tags.keys();
-}
-
-#include "moc_avfcamerametadatacontrol.cpp"
diff --git a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.h b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.h
deleted file mode 100644
index 3ef3d07d4..000000000
--- a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.h
+++ /dev/null
@@ -1,108 +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 AVFCAMERARENDERERCONTROL_H
-#define AVFCAMERARENDERERCONTROL_H
-
-#include <QtMultimedia/qvideorenderercontrol.h>
-#include <QtMultimedia/qvideoframe.h>
-#include <QtCore/qmutex.h>
-
-#import <AVFoundation/AVFoundation.h>
-
-@class AVFCaptureFramesDelegate;
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraSession;
-class AVFCameraService;
-class AVFCameraRendererControl;
-
-class AVFCameraRendererControl : public QVideoRendererControl
-{
-Q_OBJECT
-public:
- AVFCameraRendererControl(QObject *parent = nullptr);
- ~AVFCameraRendererControl();
-
- QAbstractVideoSurface *surface() const override;
- void setSurface(QAbstractVideoSurface *surface) override;
-
- void configureAVCaptureSession(AVFCameraSession *cameraSession);
- void syncHandleViewfinderFrame(const QVideoFrame &frame);
-
- AVCaptureVideoDataOutput *videoDataOutput() const;
-
- bool supportsTextures() const { return m_supportsTextures; }
-
-#ifdef Q_OS_IOS
- AVFCaptureFramesDelegate *captureDelegate() const;
- void resetCaptureDelegate() const;
-#endif
-
-Q_SIGNALS:
- void surfaceChanged(QAbstractVideoSurface *surface);
-
-private Q_SLOTS:
- void handleViewfinderFrame();
- void updateCaptureConnection();
-
-private:
- QAbstractVideoSurface *m_surface;
- AVFCaptureFramesDelegate *m_viewfinderFramesDelegate;
- AVFCameraSession *m_cameraSession;
- AVCaptureVideoDataOutput *m_videoDataOutput;
-
- bool m_supportsTextures;
- bool m_needsHorizontalMirroring;
-
-#ifdef Q_OS_IOS
- CVOpenGLESTextureCacheRef m_textureCache;
-#endif
-
- QVideoFrame m_lastViewfinderFrame;
- QMutex m_vfMutex;
- dispatch_queue_t m_delegateQueue;
-
- friend class CVImageVideoBuffer;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm
deleted file mode 100644
index a03d38b8c..000000000
--- a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm
+++ /dev/null
@@ -1,411 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfcameraviewfindersettingscontrol.h"
-#include "private/qabstractvideobuffer_p.h"
-#include "avfcamerarenderercontrol.h"
-#include "avfcamerasession.h"
-#include "avfcameraservice.h"
-#include "avfcameradebug.h"
-
-#ifdef Q_OS_IOS
-#include <QtGui/qopengl.h>
-#endif
-
-#include <QtMultimedia/qabstractvideosurface.h>
-#include <QtMultimedia/qabstractvideobuffer.h>
-
-#include <QtMultimedia/qvideosurfaceformat.h>
-
-QT_USE_NAMESPACE
-
-class CVImageVideoBuffer : public QAbstractPlanarVideoBuffer
-{
-public:
- CVImageVideoBuffer(CVImageBufferRef buffer, AVFCameraRendererControl *renderer)
-#ifndef Q_OS_IOS
- : QAbstractPlanarVideoBuffer(NoHandle)
-#else
- : QAbstractPlanarVideoBuffer(renderer->supportsTextures()
- && CVPixelBufferGetPixelFormatType(buffer) == kCVPixelFormatType_32BGRA
- ? GLTextureHandle : NoHandle)
- , m_texture(nullptr)
- , m_renderer(renderer)
-#endif
- , m_buffer(buffer)
- , m_mode(NotMapped)
- {
-#ifndef Q_OS_IOS
- Q_UNUSED(renderer);
-#endif // Q_OS_IOS
- CVPixelBufferRetain(m_buffer);
- }
-
- ~CVImageVideoBuffer()
- {
- CVImageVideoBuffer::unmap();
-#ifdef Q_OS_IOS
- if (m_texture)
- CFRelease(m_texture);
-#endif
- CVPixelBufferRelease(m_buffer);
- }
-
- MapMode mapMode() const { return m_mode; }
-
- int map(QAbstractVideoBuffer::MapMode mode, int *numBytes, int bytesPerLine[4], uchar *data[4])
- {
- // We only support RGBA or NV12 (or Apple's version of NV12),
- // they are either 0 planes or 2.
- const size_t nPlanes = CVPixelBufferGetPlaneCount(m_buffer);
- Q_ASSERT(nPlanes <= 2);
-
- if (!nPlanes) {
- data[0] = map(mode, numBytes, bytesPerLine);
- return data[0] ? 1 : 0;
- }
-
- // For a bi-planar format we have to set the parameters correctly:
- if (mode != QAbstractVideoBuffer::NotMapped && m_mode == QAbstractVideoBuffer::NotMapped) {
- CVPixelBufferLockBaseAddress(m_buffer, mode == QAbstractVideoBuffer::ReadOnly
- ? kCVPixelBufferLock_ReadOnly
- : 0);
-
- if (numBytes)
- *numBytes = CVPixelBufferGetDataSize(m_buffer);
-
- if (bytesPerLine) {
- // At the moment we handle only bi-planar format.
- bytesPerLine[0] = CVPixelBufferGetBytesPerRowOfPlane(m_buffer, 0);
- bytesPerLine[1] = CVPixelBufferGetBytesPerRowOfPlane(m_buffer, 1);
- }
-
- if (data) {
- data[0] = static_cast<uchar*>(CVPixelBufferGetBaseAddressOfPlane(m_buffer, 0));
- data[1] = static_cast<uchar*>(CVPixelBufferGetBaseAddressOfPlane(m_buffer, 1));
- }
-
- m_mode = mode;
- }
-
- return nPlanes;
- }
-
- uchar *map(MapMode mode, int *numBytes, int *bytesPerLine)
- {
- if (mode != NotMapped && m_mode == NotMapped) {
- CVPixelBufferLockBaseAddress(m_buffer, mode == QAbstractVideoBuffer::ReadOnly
- ? kCVPixelBufferLock_ReadOnly
- : 0);
- if (numBytes)
- *numBytes = CVPixelBufferGetDataSize(m_buffer);
-
- if (bytesPerLine)
- *bytesPerLine = CVPixelBufferGetBytesPerRow(m_buffer);
-
- m_mode = mode;
- return static_cast<uchar*>(CVPixelBufferGetBaseAddress(m_buffer));
- } else {
- return nullptr;
- }
- }
-
- void unmap()
- {
- if (m_mode != NotMapped) {
- CVPixelBufferUnlockBaseAddress(m_buffer, m_mode == QAbstractVideoBuffer::ReadOnly
- ? kCVPixelBufferLock_ReadOnly
- : 0);
- m_mode = NotMapped;
- }
- }
-
- QVariant handle() const
- {
-#ifdef Q_OS_IOS
- // Called from the render thread, so there is a current OpenGL context
-
- if (!m_renderer->m_textureCache) {
- CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault,
- nullptr,
- [EAGLContext currentContext],
- nullptr,
- &m_renderer->m_textureCache);
-
- if (err != kCVReturnSuccess)
- qWarning("Error creating texture cache");
- }
-
- if (m_renderer->m_textureCache && !m_texture) {
- CVOpenGLESTextureCacheFlush(m_renderer->m_textureCache, 0);
-
- CVReturn err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
- m_renderer->m_textureCache,
- m_buffer,
- nullptr,
- GL_TEXTURE_2D,
- GL_RGBA,
- CVPixelBufferGetWidth(m_buffer),
- CVPixelBufferGetHeight(m_buffer),
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- 0,
- &m_texture);
- if (err != kCVReturnSuccess)
- qWarning("Error creating texture from buffer");
- }
-
- if (m_texture)
- return CVOpenGLESTextureGetName(m_texture);
- else
- return 0;
-#else
- return QVariant();
-#endif
- }
-
-private:
-#ifdef Q_OS_IOS
- mutable CVOpenGLESTextureRef m_texture;
- AVFCameraRendererControl *m_renderer;
-#endif
- CVImageBufferRef m_buffer;
- MapMode m_mode;
-};
-
-
-@interface AVFCaptureFramesDelegate : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
-
-- (AVFCaptureFramesDelegate *) initWithRenderer:(AVFCameraRendererControl*)renderer;
-
-- (void) captureOutput:(AVCaptureOutput *)captureOutput
- didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
- fromConnection:(AVCaptureConnection *)connection;
-
-@end
-
-@implementation AVFCaptureFramesDelegate
-{
-@private
- AVFCameraRendererControl *m_renderer;
-}
-
-- (AVFCaptureFramesDelegate *) initWithRenderer:(AVFCameraRendererControl*)renderer
-{
- if (!(self = [super init]))
- return nil;
-
- self->m_renderer = renderer;
- 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);
-
- int width = CVPixelBufferGetWidth(imageBuffer);
- int height = CVPixelBufferGetHeight(imageBuffer);
- QVideoFrame::PixelFormat format =
- AVFCameraViewfinderSettingsControl2::QtPixelFormatFromCVFormat(CVPixelBufferGetPixelFormatType(imageBuffer));
- if (format == QVideoFrame::Format_Invalid)
- return;
-
- QVideoFrame frame(new CVImageVideoBuffer(imageBuffer, m_renderer),
- QSize(width, height),
- format);
-
- m_renderer->syncHandleViewfinderFrame(frame);
-}
-
-@end
-
-
-AVFCameraRendererControl::AVFCameraRendererControl(QObject *parent)
- : QVideoRendererControl(parent)
- , m_surface(nullptr)
- , m_supportsTextures(false)
- , m_needsHorizontalMirroring(false)
-#ifdef Q_OS_IOS
- , m_textureCache(nullptr)
-#endif
-{
- m_viewfinderFramesDelegate = [[AVFCaptureFramesDelegate alloc] initWithRenderer:this];
-}
-
-AVFCameraRendererControl::~AVFCameraRendererControl()
-{
- [m_cameraSession->captureSession() removeOutput:m_videoDataOutput];
- [m_viewfinderFramesDelegate release];
- if (m_delegateQueue)
- dispatch_release(m_delegateQueue);
-#ifdef Q_OS_IOS
- if (m_textureCache)
- CFRelease(m_textureCache);
-#endif
-}
-
-QAbstractVideoSurface *AVFCameraRendererControl::surface() const
-{
- return m_surface;
-}
-
-void AVFCameraRendererControl::setSurface(QAbstractVideoSurface *surface)
-{
- if (m_surface != surface) {
- m_surface = surface;
- m_supportsTextures = m_surface
- ? !m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty()
- : false;
- Q_EMIT surfaceChanged(surface);
- }
-}
-
-void AVFCameraRendererControl::configureAVCaptureSession(AVFCameraSession *cameraSession)
-{
- m_cameraSession = cameraSession;
- connect(m_cameraSession, SIGNAL(readyToConfigureConnections()),
- this, SLOT(updateCaptureConnection()));
-
- m_needsHorizontalMirroring = false;
-
- m_videoDataOutput = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
-
- // Configure video output
- m_delegateQueue = dispatch_queue_create("vf_queue", nullptr);
- [m_videoDataOutput
- setSampleBufferDelegate:m_viewfinderFramesDelegate
- queue:m_delegateQueue];
-
- [m_cameraSession->captureSession() addOutput:m_videoDataOutput];
-}
-
-void AVFCameraRendererControl::updateCaptureConnection()
-{
- AVCaptureConnection *connection = [m_videoDataOutput connectionWithMediaType:AVMediaTypeVideo];
- if (connection == nil || !m_cameraSession->videoCaptureDevice())
- return;
-
- // Frames of front-facing cameras should be mirrored horizontally (it's the default when using
- // AVCaptureVideoPreviewLayer but not with AVCaptureVideoDataOutput)
- if (connection.isVideoMirroringSupported)
- connection.videoMirrored = m_cameraSession->videoCaptureDevice().position == AVCaptureDevicePositionFront;
-
- // If the connection does't support mirroring, we'll have to do it ourselves
- m_needsHorizontalMirroring = !connection.isVideoMirrored
- && m_cameraSession->videoCaptureDevice().position == AVCaptureDevicePositionFront;
-}
-
-//can be called from non main thread
-void AVFCameraRendererControl::syncHandleViewfinderFrame(const QVideoFrame &frame)
-{
- QMutexLocker lock(&m_vfMutex);
- if (!m_lastViewfinderFrame.isValid()) {
- static QMetaMethod handleViewfinderFrameSlot = metaObject()->method(
- metaObject()->indexOfMethod("handleViewfinderFrame()"));
-
- handleViewfinderFrameSlot.invoke(this, Qt::QueuedConnection);
- }
-
- m_lastViewfinderFrame = frame;
-
- if (m_cameraSession && m_lastViewfinderFrame.isValid())
- m_cameraSession->onCameraFrameFetched(m_lastViewfinderFrame);
-}
-
-AVCaptureVideoDataOutput *AVFCameraRendererControl::videoDataOutput() const
-{
- return m_videoDataOutput;
-}
-
-#ifdef Q_OS_IOS
-
-AVFCaptureFramesDelegate *AVFCameraRendererControl::captureDelegate() const
-{
- return m_viewfinderFramesDelegate;
-}
-
-void AVFCameraRendererControl::resetCaptureDelegate() const
-{
- [m_videoDataOutput setSampleBufferDelegate:m_viewfinderFramesDelegate queue:m_delegateQueue];
-}
-
-#endif
-
-void AVFCameraRendererControl::handleViewfinderFrame()
-{
- QVideoFrame frame;
- {
- QMutexLocker lock(&m_vfMutex);
- frame = m_lastViewfinderFrame;
- m_lastViewfinderFrame = QVideoFrame();
- }
-
- if (m_surface && frame.isValid()) {
- if (m_surface->isActive() && (m_surface->surfaceFormat().pixelFormat() != frame.pixelFormat()
- || m_surface->surfaceFormat().frameSize() != frame.size())) {
- m_surface->stop();
- }
-
- if (!m_surface->isActive()) {
- QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), frame.handleType());
- if (m_needsHorizontalMirroring)
- format.setProperty("mirrored", true);
-
- if (!m_surface->start(format)) {
- qWarning() << "Failed to start viewfinder m_surface, format:" << format;
- } else {
- qDebugCamera() << "Viewfinder started: " << format;
- }
- }
-
- if (m_surface->isActive())
- m_surface->present(frame);
- }
-}
-
-
-#include "moc_avfcamerarenderercontrol.cpp"
diff --git a/src/plugins/avfoundation/camera/avfcameraservice.h b/src/plugins/avfoundation/camera/avfcameraservice.h
deleted file mode 100644
index 1397a7dee..000000000
--- a/src/plugins/avfoundation/camera/avfcameraservice.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 AVFCAMERASERVICE_H
-#define AVFCAMERASERVICE_H
-
-#include <QtCore/qobject.h>
-#include <QtCore/qset.h>
-#include <qmediaservice.h>
-
-
-QT_BEGIN_NAMESPACE
-class QCameraControl;
-class QMediaRecorderControl;
-class AVFCameraControl;
-class AVFCameraInfoControl;
-class AVFCameraMetaDataControl;
-class AVFVideoWindowControl;
-class AVFVideoWidgetControl;
-class AVFCameraRendererControl;
-class AVFImageCaptureControl;
-class AVFCameraSession;
-class AVFCameraDeviceControl;
-class AVFAudioInputSelectorControl;
-class AVFCameraFocusControl;
-class AVFCameraExposureControl;
-class AVFCameraZoomControl;
-class AVFCameraViewfinderSettingsControl2;
-class AVFCameraViewfinderSettingsControl;
-class AVFImageEncoderControl;
-class AVFCameraFlashControl;
-class AVFMediaRecorderControl;
-class AVFMediaRecorderControlIOS;
-class AVFAudioEncoderSettingsControl;
-class AVFVideoEncoderSettingsControl;
-class AVFMediaContainerControl;
-class AVFCameraWindowControl;
-class AVFCaptureDestinationControl;
-
-class AVFCameraService : public QMediaService
-{
-Q_OBJECT
-public:
- AVFCameraService(QObject *parent = nullptr);
- ~AVFCameraService();
-
- QMediaControl* requestControl(const char *name);
- void releaseControl(QMediaControl *control);
-
- AVFCameraSession *session() const { return m_session; }
- AVFCameraControl *cameraControl() const { return m_cameraControl; }
- AVFCameraDeviceControl *videoDeviceControl() const { return m_videoDeviceControl; }
- AVFAudioInputSelectorControl *audioInputSelectorControl() const { return m_audioInputSelectorControl; }
- AVFCameraMetaDataControl *metaDataControl() const { return m_metaDataControl; }
- QMediaRecorderControl *recorderControl() const { return m_recorderControl; }
- AVFImageCaptureControl *imageCaptureControl() const { return m_imageCaptureControl; }
- AVFCameraFocusControl *cameraFocusControl() const { return m_cameraFocusControl; }
- AVFCameraExposureControl *cameraExposureControl() const {return m_cameraExposureControl; }
- AVFCameraZoomControl *cameraZoomControl() const {return m_cameraZoomControl; }
- AVFCameraRendererControl *videoOutput() const {return m_videoOutput; }
- AVFCameraViewfinderSettingsControl2 *viewfinderSettingsControl2() const {return m_viewfinderSettingsControl2; }
- AVFCameraViewfinderSettingsControl *viewfinderSettingsControl() const {return m_viewfinderSettingsControl; }
- AVFImageEncoderControl *imageEncoderControl() const {return m_imageEncoderControl; }
- AVFCameraFlashControl *flashControl() const {return m_flashControl; }
- AVFAudioEncoderSettingsControl *audioEncoderSettingsControl() const { return m_audioEncoderSettingsControl; }
- AVFVideoEncoderSettingsControl *videoEncoderSettingsControl() const {return m_videoEncoderSettingsControl; }
- AVFMediaContainerControl *mediaContainerControl() const { return m_mediaContainerControl; }
- AVFCaptureDestinationControl *captureDestinationControl() const { return m_captureDestinationControl; }
-
-private:
- AVFCameraSession *m_session;
- AVFCameraControl *m_cameraControl;
- AVFCameraInfoControl *m_cameraInfoControl;
- AVFCameraDeviceControl *m_videoDeviceControl;
- AVFAudioInputSelectorControl *m_audioInputSelectorControl;
- AVFCameraRendererControl *m_videoOutput;
- AVFCameraMetaDataControl *m_metaDataControl;
- QMediaRecorderControl *m_recorderControl;
- AVFImageCaptureControl *m_imageCaptureControl;
- AVFCameraFocusControl *m_cameraFocusControl;
- AVFCameraExposureControl *m_cameraExposureControl;
- AVFCameraZoomControl *m_cameraZoomControl;
- AVFCameraViewfinderSettingsControl2 *m_viewfinderSettingsControl2;
- AVFCameraViewfinderSettingsControl *m_viewfinderSettingsControl;
- AVFImageEncoderControl *m_imageEncoderControl;
- AVFCameraFlashControl *m_flashControl;
- AVFAudioEncoderSettingsControl *m_audioEncoderSettingsControl;
- AVFVideoEncoderSettingsControl *m_videoEncoderSettingsControl;
- AVFMediaContainerControl *m_mediaContainerControl;
- AVFCameraWindowControl *m_captureWindowControl;
- AVFCaptureDestinationControl *m_captureDestinationControl;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/avfoundation/camera/avfcameraservice.mm b/src/plugins/avfoundation/camera/avfcameraservice.mm
deleted file mode 100644
index 79bf73910..000000000
--- a/src/plugins/avfoundation/camera/avfcameraservice.mm
+++ /dev/null
@@ -1,267 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <QtCore/qvariant.h>
-#include <QtCore/qdebug.h>
-
-#include "avfcameraservice.h"
-#include "avfcameracontrol.h"
-#include "avfcamerainfocontrol.h"
-#include "avfcamerasession.h"
-#include "avfcameradevicecontrol.h"
-#include "avfaudioinputselectorcontrol.h"
-#include "avfcamerametadatacontrol.h"
-#include "avfmediarecordercontrol.h"
-#include "avfimagecapturecontrol.h"
-#include "avfcamerarenderercontrol.h"
-#include "avfmediarecordercontrol.h"
-#include "avfimagecapturecontrol.h"
-#include "avfmediavideoprobecontrol.h"
-#include "avfcamerafocuscontrol.h"
-#include "avfcameraexposurecontrol.h"
-#include "avfcameraviewfindersettingscontrol.h"
-#include "avfimageencodercontrol.h"
-#include "avfcameraflashcontrol.h"
-#include "avfaudioencodersettingscontrol.h"
-#include "avfvideoencodersettingscontrol.h"
-#include "avfmediacontainercontrol.h"
-#include "avfcapturedestinationcontrol.h"
-#include "avfcamerawindowcontrol.h"
-
-#ifdef Q_OS_IOS
-#include "avfcamerazoomcontrol.h"
-#include "avfmediarecordercontrol_ios.h"
-#endif
-
-#include <private/qmediaplaylistnavigator_p.h>
-#include <qmediaplaylist.h>
-
-QT_USE_NAMESPACE
-
-AVFCameraService::AVFCameraService(QObject *parent):
- QMediaService(parent),
- m_videoOutput(nullptr),
- m_captureWindowControl(nullptr)
-{
- m_session = new AVFCameraSession(this);
- m_cameraControl = new AVFCameraControl(this);
- m_cameraInfoControl = new AVFCameraInfoControl(this);
- m_videoDeviceControl = new AVFCameraDeviceControl(this);
- m_audioInputSelectorControl = new AVFAudioInputSelectorControl(this);
-
- m_metaDataControl = new AVFCameraMetaDataControl(this);
-#ifndef Q_OS_IOS
- // This will connect a slot to 'captureModeChanged'
- // and will break viewfinder by attaching AVCaptureMovieFileOutput
- // in this slot.
- m_recorderControl = new AVFMediaRecorderControl(this);
-#else
- m_recorderControl = new AVFMediaRecorderControlIOS(this);
-#endif
- m_imageCaptureControl = new AVFImageCaptureControl(this);
- m_cameraFocusControl = new AVFCameraFocusControl(this);
- m_cameraExposureControl = nullptr;
-#ifdef Q_OS_IOS
- m_cameraExposureControl = new AVFCameraExposureControl(this);
-#endif
-
- m_cameraZoomControl = nullptr;
-#ifdef Q_OS_IOS
- m_cameraZoomControl = new AVFCameraZoomControl(this);
-#endif
- m_viewfinderSettingsControl2 = new AVFCameraViewfinderSettingsControl2(this);
- m_viewfinderSettingsControl = new AVFCameraViewfinderSettingsControl(this);
- m_imageEncoderControl = new AVFImageEncoderControl(this);
- m_flashControl = new AVFCameraFlashControl(this);
- m_audioEncoderSettingsControl = new AVFAudioEncoderSettingsControl(this);
- m_videoEncoderSettingsControl = new AVFVideoEncoderSettingsControl(this);
- m_mediaContainerControl = new AVFMediaContainerControl(this);
- m_captureDestinationControl = new AVFCaptureDestinationControl;
-}
-
-AVFCameraService::~AVFCameraService()
-{
- m_cameraControl->setState(QCamera::UnloadedState);
-
-#ifdef Q_OS_IOS
- delete m_recorderControl;
-#endif
-
- if (m_captureWindowControl) {
- m_session->setCapturePreviewOutput(nullptr);
- delete m_captureWindowControl;
- m_captureWindowControl = nullptr;
- }
-
- if (m_videoOutput) {
- m_session->setVideoOutput(nullptr);
- delete m_videoOutput;
- m_videoOutput = nullptr;
- }
-
- //delete controls before session,
- //so they have a chance to do deinitialization
- delete m_imageCaptureControl;
- //delete m_recorderControl;
- delete m_metaDataControl;
- delete m_cameraControl;
- delete m_cameraFocusControl;
- delete m_cameraExposureControl;
-#ifdef Q_OS_IOS
- delete m_cameraZoomControl;
-#endif
- delete m_viewfinderSettingsControl2;
- delete m_viewfinderSettingsControl;
- delete m_imageEncoderControl;
- delete m_flashControl;
- delete m_audioEncoderSettingsControl;
- delete m_videoEncoderSettingsControl;
- delete m_mediaContainerControl;
- delete m_captureDestinationControl;
-
- delete m_session;
-}
-
-QMediaControl *AVFCameraService::requestControl(const char *name)
-{
- if (qstrcmp(name, QCameraControl_iid) == 0)
- return m_cameraControl;
-
- if (qstrcmp(name, QCameraInfoControl_iid) == 0)
- return m_cameraInfoControl;
-
- if (qstrcmp(name, QVideoDeviceSelectorControl_iid) == 0)
- return m_videoDeviceControl;
-
- if (qstrcmp(name, QAudioInputSelectorControl_iid) == 0)
- return m_audioInputSelectorControl;
-
- //metadata support is not implemented yet
- //if (qstrcmp(name, QMetaDataWriterControl_iid) == 0)
- // return m_metaDataControl;
-
- if (qstrcmp(name, QMediaRecorderControl_iid) == 0)
- return m_recorderControl;
-
- if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0)
- return m_imageCaptureControl;
-
- if (qstrcmp(name, QCameraExposureControl_iid) == 0)
- return m_cameraExposureControl;
-
- if (qstrcmp(name, QCameraFocusControl_iid) == 0)
- return m_cameraFocusControl;
-
- if (qstrcmp(name, QCameraViewfinderSettingsControl2_iid) == 0)
- return m_viewfinderSettingsControl2;
-
- if (qstrcmp(name, QCameraViewfinderSettingsControl_iid) == 0)
- return m_viewfinderSettingsControl;
-
- if (qstrcmp(name, QImageEncoderControl_iid) == 0)
- return m_imageEncoderControl;
-
- if (qstrcmp(name, QCameraFlashControl_iid) == 0)
- return m_flashControl;
-
- if (qstrcmp(name, QAudioEncoderSettingsControl_iid) == 0)
- return m_audioEncoderSettingsControl;
-
- if (qstrcmp(name, QVideoEncoderSettingsControl_iid) == 0)
- return m_videoEncoderSettingsControl;
-
- if (qstrcmp(name, QMediaContainerControl_iid) == 0)
- return m_mediaContainerControl;
-
- if (qstrcmp(name,QMediaVideoProbeControl_iid) == 0) {
- AVFMediaVideoProbeControl *videoProbe = nullptr;
- videoProbe = new AVFMediaVideoProbeControl(this);
- m_session->addProbe(videoProbe);
- return videoProbe;
- }
-
-#ifdef Q_OS_IOS
- if (qstrcmp(name, QCameraZoomControl_iid) == 0)
- return m_cameraZoomControl;
-#endif
-
- if (qstrcmp(name, QCameraCaptureDestinationControl_iid) == 0)
- return m_captureDestinationControl;
-
- if (!m_captureWindowControl) {
- if (qstrcmp(name, QVideoWindowControl_iid) == 0) {
- m_captureWindowControl = new AVFCameraWindowControl(this);
- m_session->setCapturePreviewOutput(m_captureWindowControl);
- return m_captureWindowControl;
- }
- }
-
- if (!m_videoOutput) {
- if (qstrcmp(name, QVideoRendererControl_iid) == 0)
- m_videoOutput = new AVFCameraRendererControl(this);
-
- if (m_videoOutput) {
- m_session->setVideoOutput(m_videoOutput);
- return m_videoOutput;
- }
- }
-
- return nullptr;
-}
-
-void AVFCameraService::releaseControl(QMediaControl *control)
-{
- AVFMediaVideoProbeControl *videoProbe = qobject_cast<AVFMediaVideoProbeControl *>(control);
- if (videoProbe) {
- m_session->removeProbe(videoProbe);
- delete videoProbe;
- } else if (m_videoOutput == control) {
- m_session->setVideoOutput(nullptr);
- delete m_videoOutput;
- m_videoOutput = nullptr;
- }
- else if (m_captureWindowControl == control) {
- m_session->setCapturePreviewOutput(nullptr);
- delete m_captureWindowControl;
- m_captureWindowControl = nullptr;
- }
-}
-
-
-#include "moc_avfcameraservice.cpp"
diff --git a/src/plugins/avfoundation/camera/avfcameraserviceplugin.h b/src/plugins/avfoundation/camera/avfcameraserviceplugin.h
deleted file mode 100644
index bd5f83249..000000000
--- a/src/plugins/avfoundation/camera/avfcameraserviceplugin.h
+++ /dev/null
@@ -1,76 +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 AVFSERVICEPLUGIN_H
-#define AVFSERVICEPLUGIN_H
-
-#include <qmediaserviceproviderplugin.h>
-#include <QtCore/qmap.h>
-
-QT_BEGIN_NAMESPACE
-
-class AVFServicePlugin : public QMediaServiceProviderPlugin,
- public QMediaServiceSupportedDevicesInterface,
- public QMediaServiceDefaultDeviceInterface,
- public QMediaServiceCameraInfoInterface
-{
- Q_OBJECT
- Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
- Q_INTERFACES(QMediaServiceDefaultDeviceInterface)
- Q_INTERFACES(QMediaServiceCameraInfoInterface)
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "avfcamera.json")
-
-public:
- AVFServicePlugin();
-
- QMediaService* create(QString const &key) override;
- void release(QMediaService *service) override;
-
- QByteArray defaultDevice(const QByteArray &service) const override;
- QList<QByteArray> devices(const QByteArray &service) const override;
- QString deviceDescription(const QByteArray &service, const QByteArray &device) override;
-
- QCamera::Position cameraPosition(const QByteArray &device) const override;
- int cameraOrientation(const QByteArray &device) const override;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/avfoundation/camera/avfcameraserviceplugin.mm b/src/plugins/avfoundation/camera/avfcameraserviceplugin.mm
deleted file mode 100644
index 08bd8c9bd..000000000
--- a/src/plugins/avfoundation/camera/avfcameraserviceplugin.mm
+++ /dev/null
@@ -1,113 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <QtCore/qstring.h>
-#include <QtCore/qdebug.h>
-
-#include "avfcameraserviceplugin.h"
-#include "avfcameraservice.h"
-#include "avfcamerasession.h"
-
-#include <qmediaserviceproviderplugin.h>
-
-QT_BEGIN_NAMESPACE
-
-AVFServicePlugin::AVFServicePlugin()
-{
-}
-
-QMediaService* AVFServicePlugin::create(QString const& key)
-{
- if (key == QLatin1String(Q_MEDIASERVICE_CAMERA))
- return new AVFCameraService;
- else
- qWarning() << "unsupported key:" << key;
-
- return nullptr;
-}
-
-void AVFServicePlugin::release(QMediaService *service)
-{
- delete service;
-}
-
-QByteArray AVFServicePlugin::defaultDevice(const QByteArray &service) const
-{
- if (service == Q_MEDIASERVICE_CAMERA) {
- int i = AVFCameraSession::defaultCameraIndex();
- if (i != -1)
- return AVFCameraSession::availableCameraDevices().at(i).deviceId;
- }
-
- return QByteArray();
-}
-
-QList<QByteArray> AVFServicePlugin::devices(const QByteArray &service) const
-{
- QList<QByteArray> devs;
-
- if (service == Q_MEDIASERVICE_CAMERA) {
- const QList<AVFCameraInfo> &cameras = AVFCameraSession::availableCameraDevices();
- devs.reserve(cameras.size());
- for (const AVFCameraInfo &info : cameras)
- devs.append(info.deviceId);
- }
-
- return devs;
-}
-
-QString AVFServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device)
-{
- if (service == Q_MEDIASERVICE_CAMERA)
- return AVFCameraSession::cameraDeviceInfo(device).description;
-
- return QString();
-}
-
-QCamera::Position AVFServicePlugin::cameraPosition(const QByteArray &device) const
-{
- return AVFCameraSession::cameraDeviceInfo(device).position;
-}
-
-int AVFServicePlugin::cameraOrientation(const QByteArray &device) const
-{
- return AVFCameraSession::cameraDeviceInfo(device).orientation;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/avfoundation/camera/avfcamerasession.h b/src/plugins/avfoundation/camera/avfcamerasession.h
deleted file mode 100644
index a449bb806..000000000
--- a/src/plugins/avfoundation/camera/avfcamerasession.h
+++ /dev/null
@@ -1,144 +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 AVFCAMERASESSION_H
-#define AVFCAMERASESSION_H
-
-#include <QtCore/qmutex.h>
-#include <QtMultimedia/qcamera.h>
-#include <QVideoFrame>
-
-#import <AVFoundation/AVFoundation.h>
-
-@class AVFCameraSessionObserver;
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraControl;
-class AVFCameraService;
-class AVFCameraRendererControl;
-class AVFMediaVideoProbeControl;
-class AVFCameraWindowControl;
-
-struct AVFCameraInfo
-{
- AVFCameraInfo() : position(QCamera::UnspecifiedPosition), orientation(0)
- { }
-
- QByteArray deviceId;
- QString description;
- QCamera::Position position;
- int orientation;
-};
-
-class AVFCameraSession : public QObject
-{
- Q_OBJECT
-public:
- AVFCameraSession(AVFCameraService *service, QObject *parent = nullptr);
- ~AVFCameraSession();
-
- static int defaultCameraIndex();
- static const QList<AVFCameraInfo> &availableCameraDevices();
- static AVFCameraInfo cameraDeviceInfo(const QByteArray &device);
- AVFCameraInfo activeCameraInfo() const { return m_activeCameraInfo; }
-
- void setVideoOutput(AVFCameraRendererControl *output);
- void setCapturePreviewOutput(AVFCameraWindowControl *output);
- AVCaptureSession *captureSession() const { return m_captureSession; }
- AVCaptureDevice *videoCaptureDevice() const;
-
- QCamera::State state() const;
- QCamera::State requestedState() const { return m_state; }
- bool isActive() const { return m_active; }
-
- void addProbe(AVFMediaVideoProbeControl *probe);
- void removeProbe(AVFMediaVideoProbeControl *probe);
- FourCharCode defaultCodec();
-
- AVCaptureDeviceInput *videoInput() const {return m_videoInput;}
-
-public Q_SLOTS:
- void setState(QCamera::State state);
-
- void processRuntimeError();
- void processSessionStarted();
- void processSessionStopped();
-
- void onCaptureModeChanged(QCamera::CaptureModes mode);
-
- void onCameraFrameFetched(const QVideoFrame &frame);
-
-Q_SIGNALS:
- void readyToConfigureConnections();
- void stateChanged(QCamera::State newState);
- void activeChanged(bool);
- void newViewfinderFrame(const QVideoFrame &frame);
- void error(int error, const QString &errorString);
-
-private:
- static void updateCameraDevices();
- void attachVideoInputDevice();
- bool applyImageEncoderSettings();
- bool applyViewfinderSettings();
-
- static int m_defaultCameraIndex;
- static QList<AVFCameraInfo> m_cameraDevices;
- AVFCameraInfo m_activeCameraInfo;
-
- AVFCameraService *m_service;
- AVFCameraRendererControl *m_videoOutput;
- AVFCameraWindowControl *m_capturePreviewWindowOutput;
-
- QCamera::State m_state;
- bool m_active;
-
- AVCaptureSession *m_captureSession;
- AVCaptureDeviceInput *m_videoInput;
- AVFCameraSessionObserver *m_observer;
-
- QSet<AVFMediaVideoProbeControl *> m_videoProbes;
- QMutex m_videoProbesMutex;
-
- FourCharCode m_defaultCodec;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/avfoundation/camera/avfcamerasession.mm b/src/plugins/avfoundation/camera/avfcamerasession.mm
deleted file mode 100644
index ebefa07e5..000000000
--- a/src/plugins/avfoundation/camera/avfcamerasession.mm
+++ /dev/null
@@ -1,492 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfcameradebug.h"
-#include "avfcamerasession.h"
-#include "avfcameraservice.h"
-#include "avfcameracontrol.h"
-#include "avfcamerarenderercontrol.h"
-#include "avfcameradevicecontrol.h"
-#include "avfaudioinputselectorcontrol.h"
-#include "avfmediavideoprobecontrol.h"
-#include "avfcameraviewfindersettingscontrol.h"
-#include "avfimageencodercontrol.h"
-#include "avfcamerautility.h"
-#include "avfcamerawindowcontrol.h"
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <Foundation/Foundation.h>
-
-#include <QtCore/qdatetime.h>
-#include <QtCore/qurl.h>
-#include <QtCore/qelapsedtimer.h>
-
-#include <QtCore/qdebug.h>
-
-QT_USE_NAMESPACE
-
-int AVFCameraSession::m_defaultCameraIndex;
-QList<AVFCameraInfo> AVFCameraSession::m_cameraDevices;
-
-@interface AVFCameraSessionObserver : NSObject
-
-- (AVFCameraSessionObserver *) initWithCameraSession:(AVFCameraSession*)session;
-- (void) processRuntimeError:(NSNotification *)notification;
-- (void) processSessionStarted:(NSNotification *)notification;
-- (void) processSessionStopped:(NSNotification *)notification;
-
-@end
-
-@implementation AVFCameraSessionObserver
-{
-@private
- AVFCameraSession *m_session;
- AVCaptureSession *m_captureSession;
-}
-
-- (AVFCameraSessionObserver *) initWithCameraSession:(AVFCameraSession*)session
-{
- if (!(self = [super init]))
- return nil;
-
- self->m_session = session;
- self->m_captureSession = session->captureSession();
-
- [m_captureSession retain];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(processRuntimeError:)
- name:AVCaptureSessionRuntimeErrorNotification
- object:m_captureSession];
-
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(processSessionStarted:)
- name:AVCaptureSessionDidStartRunningNotification
- object:m_captureSession];
-
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(processSessionStopped:)
- name:AVCaptureSessionDidStopRunningNotification
- object:m_captureSession];
-
- return self;
-}
-
-- (void) dealloc
-{
- [[NSNotificationCenter defaultCenter] removeObserver:self
- name:AVCaptureSessionRuntimeErrorNotification
- object:m_captureSession];
-
- [[NSNotificationCenter defaultCenter] removeObserver:self
- name:AVCaptureSessionDidStartRunningNotification
- object:m_captureSession];
-
- [[NSNotificationCenter defaultCenter] removeObserver:self
- name:AVCaptureSessionDidStopRunningNotification
- object:m_captureSession];
- [m_captureSession release];
- [super dealloc];
-}
-
-- (void) processRuntimeError:(NSNotification *)notification
-{
- Q_UNUSED(notification);
- QMetaObject::invokeMethod(m_session, "processRuntimeError", Qt::AutoConnection);
-}
-
-- (void) processSessionStarted:(NSNotification *)notification
-{
- Q_UNUSED(notification);
- QMetaObject::invokeMethod(m_session, "processSessionStarted", Qt::AutoConnection);
-}
-
-- (void) processSessionStopped:(NSNotification *)notification
-{
- Q_UNUSED(notification);
- QMetaObject::invokeMethod(m_session, "processSessionStopped", Qt::AutoConnection);
-}
-
-@end
-
-AVFCameraSession::AVFCameraSession(AVFCameraService *service, QObject *parent)
- : QObject(parent)
- , m_service(service)
- , m_capturePreviewWindowOutput(nullptr)
- , m_state(QCamera::UnloadedState)
- , m_active(false)
- , m_videoInput(nil)
- , m_defaultCodec(0)
-{
- m_captureSession = [[AVCaptureSession alloc] init];
- m_observer = [[AVFCameraSessionObserver alloc] initWithCameraSession:this];
-
- //configuration is commited during transition to Active state
- [m_captureSession beginConfiguration];
-}
-
-AVFCameraSession::~AVFCameraSession()
-{
- if (m_capturePreviewWindowOutput) {
- m_capturePreviewWindowOutput->setLayer(nil);
- }
-
- if (m_videoInput) {
- [m_captureSession removeInput:m_videoInput];
- [m_videoInput release];
- }
-
- [m_observer release];
- [m_captureSession release];
-}
-
-int AVFCameraSession::defaultCameraIndex()
-{
- updateCameraDevices();
- return m_defaultCameraIndex;
-}
-
-const QList<AVFCameraInfo> &AVFCameraSession::availableCameraDevices()
-{
- updateCameraDevices();
- return m_cameraDevices;
-}
-
-AVFCameraInfo AVFCameraSession::cameraDeviceInfo(const QByteArray &device)
-{
- updateCameraDevices();
-
- for (const AVFCameraInfo &info : qAsConst(m_cameraDevices)) {
- if (info.deviceId == device)
- return info;
- }
-
- return AVFCameraInfo();
-}
-
-void AVFCameraSession::updateCameraDevices()
-{
-#ifdef Q_OS_IOS
- // Cameras can't change dynamically on iOS. Update only once.
- if (!m_cameraDevices.isEmpty())
- return;
-#else
- // On OS X, cameras can be added or removed. Update the list every time, but not more than
- // once every 500 ms
- static QElapsedTimer timer;
- if (timer.isValid() && timer.elapsed() < 500) // ms
- return;
-#endif
-
- m_defaultCameraIndex = -1;
- m_cameraDevices.clear();
-
- AVCaptureDevice *defaultDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
- NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
- for (AVCaptureDevice *device in videoDevices) {
- if (defaultDevice && [defaultDevice.uniqueID isEqualToString:device.uniqueID])
- m_defaultCameraIndex = m_cameraDevices.count();
-
- AVFCameraInfo info;
- info.deviceId = QByteArray([[device uniqueID] UTF8String]);
- info.description = QString::fromNSString([device localizedName]);
-
- // There is no API to get the camera sensor orientation, however, cameras are always
- // mounted in landscape on iDevices.
- // - Back-facing cameras have the top side of the sensor aligned with the right side of
- // the screen when held in portrait ==> 270 degrees clockwise angle
- // - Front-facing cameras have the top side of the sensor aligned with the left side of
- // the screen when held in portrait ==> 270 degrees clockwise angle
- // On OS X, the position will always be unspecified and the sensor orientation unknown.
- switch (device.position) {
- case AVCaptureDevicePositionBack:
- info.position = QCamera::BackFace;
- info.orientation = 270;
- break;
- case AVCaptureDevicePositionFront:
- info.position = QCamera::FrontFace;
- info.orientation = 270;
- break;
- default:
- info.position = QCamera::UnspecifiedPosition;
- info.orientation = 0;
- break;
- }
-
- m_cameraDevices.append(info);
- }
-
-#ifndef Q_OS_IOS
- timer.restart();
-#endif
-}
-
-void AVFCameraSession::setVideoOutput(AVFCameraRendererControl *output)
-{
- m_videoOutput = output;
- if (output)
- output->configureAVCaptureSession(this);
-}
-
-void AVFCameraSession::setCapturePreviewOutput(AVFCameraWindowControl *output)
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO << output;
-#endif
-
- if (m_capturePreviewWindowOutput == output)
- return;
-
- if (m_capturePreviewWindowOutput)
- m_capturePreviewWindowOutput->setLayer(nil);
-
- m_capturePreviewWindowOutput = output;
-
- if (m_capturePreviewWindowOutput) {
- AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:m_captureSession];
- m_capturePreviewWindowOutput->setLayer(previewLayer);
- if (AVFCameraViewfinderSettingsControl2 *vfControl = m_service->viewfinderSettingsControl2()) {
- m_capturePreviewWindowOutput->setNativeSize(vfControl->viewfinderSettings().resolution());
- }
- }
-}
-
-AVCaptureDevice *AVFCameraSession::videoCaptureDevice() const
-{
- if (m_videoInput)
- return m_videoInput.device;
-
- return nullptr;
-}
-
-QCamera::State AVFCameraSession::state() const
-{
- if (m_active)
- return QCamera::ActiveState;
-
- return m_state == QCamera::ActiveState ? QCamera::LoadedState : m_state;
-}
-
-void AVFCameraSession::setState(QCamera::State newState)
-{
- if (m_state == newState)
- return;
-
- qDebugCamera() << Q_FUNC_INFO << m_state << " -> " << newState;
-
- QCamera::State oldState = m_state;
- m_state = newState;
-
- //attach video input during Unloaded->Loaded transition
- if (oldState == QCamera::UnloadedState)
- attachVideoInputDevice();
-
- if (m_state == QCamera::ActiveState) {
- Q_EMIT readyToConfigureConnections();
- m_defaultCodec = 0;
- defaultCodec();
-
- bool activeFormatSet = applyImageEncoderSettings()
- | applyViewfinderSettings();
-
- [m_captureSession commitConfiguration];
-
- if (activeFormatSet) {
- // According to the doc, the capture device must be locked before
- // startRunning to prevent the format we set to be overriden by the
- // session preset.
- [videoCaptureDevice() lockForConfiguration:nil];
- }
-
- [m_captureSession startRunning];
-
- if (activeFormatSet)
- [videoCaptureDevice() unlockForConfiguration];
- }
-
- if (oldState == QCamera::ActiveState) {
- [m_captureSession stopRunning];
- [m_captureSession beginConfiguration];
- }
-
- Q_EMIT stateChanged(m_state);
-}
-
-void AVFCameraSession::processRuntimeError()
-{
- qWarning() << tr("Runtime camera error");
- Q_EMIT error(QCamera::CameraError, tr("Runtime camera error"));
-}
-
-void AVFCameraSession::processSessionStarted()
-{
- qDebugCamera() << Q_FUNC_INFO;
- if (!m_active) {
- m_active = true;
- Q_EMIT activeChanged(m_active);
- Q_EMIT stateChanged(state());
- }
-}
-
-void AVFCameraSession::processSessionStopped()
-{
- qDebugCamera() << Q_FUNC_INFO;
- if (m_active) {
- m_active = false;
- Q_EMIT activeChanged(m_active);
- Q_EMIT stateChanged(state());
- }
-}
-
-void AVFCameraSession::onCaptureModeChanged(QCamera::CaptureModes mode)
-{
- Q_UNUSED(mode);
-
- const QCamera::State s = state();
- if (s == QCamera::LoadedState || s == QCamera::ActiveState) {
- applyImageEncoderSettings();
- applyViewfinderSettings();
- }
-}
-
-void AVFCameraSession::attachVideoInputDevice()
-{
- //Attach video input device:
- if (m_service->videoDeviceControl()->isDirty()) {
- if (m_videoInput) {
- [m_captureSession removeInput:m_videoInput];
- [m_videoInput release];
- m_videoInput = nullptr;
- m_activeCameraInfo = AVFCameraInfo();
- }
-
- AVCaptureDevice *videoDevice = m_service->videoDeviceControl()->createCaptureDevice();
-
- NSError *error = nil;
- m_videoInput = [AVCaptureDeviceInput
- deviceInputWithDevice:videoDevice
- error:&error];
-
- if (!m_videoInput) {
- qWarning() << "Failed to create video device input";
- } else {
- if ([m_captureSession canAddInput:m_videoInput]) {
- m_activeCameraInfo = m_cameraDevices.at(m_service->videoDeviceControl()->selectedDevice());
- [m_videoInput retain];
- [m_captureSession addInput:m_videoInput];
- } else {
- qWarning() << "Failed to connect video device input";
- }
- }
- }
-}
-
-bool AVFCameraSession::applyImageEncoderSettings()
-{
- if (AVFImageEncoderControl *control = m_service->imageEncoderControl())
- return control->applySettings();
-
- return false;
-}
-
-bool AVFCameraSession::applyViewfinderSettings()
-{
- if (AVFCameraViewfinderSettingsControl2 *vfControl = m_service->viewfinderSettingsControl2()) {
- QCamera::CaptureModes currentMode = m_service->cameraControl()->captureMode();
- QCameraViewfinderSettings vfSettings(vfControl->requestedSettings());
- // Viewfinder and image capture solutions must be the same, if an image capture
- // resolution is set, it takes precedence over the viewfinder resolution.
- if (currentMode.testFlag(QCamera::CaptureStillImage)) {
- const QSize imageResolution(m_service->imageEncoderControl()->requestedSettings().resolution());
- if (!imageResolution.isNull() && imageResolution.isValid())
- vfSettings.setResolution(imageResolution);
- }
-
- vfControl->applySettings(vfSettings);
-
- if (m_capturePreviewWindowOutput)
- m_capturePreviewWindowOutput->setNativeSize(vfControl->viewfinderSettings().resolution());
-
- return !vfSettings.isNull();
- }
-
- return false;
-}
-
-void AVFCameraSession::addProbe(AVFMediaVideoProbeControl *probe)
-{
- m_videoProbesMutex.lock();
- if (probe)
- m_videoProbes << probe;
- m_videoProbesMutex.unlock();
-}
-
-void AVFCameraSession::removeProbe(AVFMediaVideoProbeControl *probe)
-{
- m_videoProbesMutex.lock();
- m_videoProbes.remove(probe);
- m_videoProbesMutex.unlock();
-}
-
-FourCharCode AVFCameraSession::defaultCodec()
-{
- if (!m_defaultCodec) {
- if (AVCaptureDevice *device = videoCaptureDevice()) {
- AVCaptureDeviceFormat *format = device.activeFormat;
- if (!format || !format.formatDescription)
- return m_defaultCodec;
- m_defaultCodec = CMVideoFormatDescriptionGetCodecType(format.formatDescription);
- }
- }
- return m_defaultCodec;
-}
-
-void AVFCameraSession::onCameraFrameFetched(const QVideoFrame &frame)
-{
- Q_EMIT newViewfinderFrame(frame);
-
- m_videoProbesMutex.lock();
- QSet<AVFMediaVideoProbeControl *>::const_iterator i = m_videoProbes.constBegin();
- while (i != m_videoProbes.constEnd()) {
- (*i)->newFrameProbed(frame);
- ++i;
- }
- m_videoProbesMutex.unlock();
-}
-
-#include "moc_avfcamerasession.cpp"
diff --git a/src/plugins/avfoundation/camera/avfcamerautility.h b/src/plugins/avfoundation/camera/avfcamerautility.h
deleted file mode 100644
index 2e8bf39b4..000000000
--- a/src/plugins/avfoundation/camera/avfcamerautility.h
+++ /dev/null
@@ -1,179 +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 AVFCAMERAUTILITY_H
-#define AVFCAMERAUTILITY_H
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qlist.h>
-#include <QtCore/qpair.h>
-#include <QtCore/qsize.h>
-
-#include <AVFoundation/AVFoundation.h>
-
-// In case we have SDK below 10.7/7.0:
-@class AVCaptureDeviceFormat;
-
-QT_BEGIN_NAMESPACE
-
-class AVFConfigurationLock
-{
-public:
- explicit AVFConfigurationLock(AVCaptureDevice *captureDevice)
- : m_captureDevice(captureDevice),
- m_locked(false)
- {
- Q_ASSERT(m_captureDevice);
- NSError *error = nil;
- m_locked = [m_captureDevice lockForConfiguration:&error];
- }
-
- ~AVFConfigurationLock()
- {
- if (m_locked)
- [m_captureDevice unlockForConfiguration];
- }
-
- operator bool() const
- {
- return m_locked;
- }
-
-private:
- Q_DISABLE_COPY(AVFConfigurationLock)
-
- AVCaptureDevice *m_captureDevice;
- bool m_locked;
-};
-
-struct AVFObjectDeleter {
- static void cleanup(NSObject *obj)
- {
- if (obj)
- [obj release];
- }
-};
-
-template<class T>
-class AVFScopedPointer : public QScopedPointer<NSObject, AVFObjectDeleter>
-{
-public:
- AVFScopedPointer() {}
- explicit AVFScopedPointer(T *ptr) : QScopedPointer(ptr) {}
- operator T*() const
- {
- // Quite handy operator to enable Obj-C messages: [ptr someMethod];
- return data();
- }
-
- T *data() const
- {
- return static_cast<T *>(QScopedPointer::data());
- }
-
- T *take()
- {
- return static_cast<T *>(QScopedPointer::take());
- }
-};
-
-template<>
-class AVFScopedPointer<dispatch_queue_t>
-{
-public:
- AVFScopedPointer() : m_queue(nullptr) {}
- explicit AVFScopedPointer(dispatch_queue_t q) : m_queue(q) {}
-
- ~AVFScopedPointer()
- {
- if (m_queue)
- dispatch_release(m_queue);
- }
-
- operator dispatch_queue_t() const
- {
- // Quite handy operator to enable Obj-C messages: [ptr someMethod];
- return m_queue;
- }
-
- dispatch_queue_t data() const
- {
- return m_queue;
- }
-
- void reset(dispatch_queue_t q = nullptr)
- {
- if (m_queue)
- dispatch_release(m_queue);
- m_queue = q;
- }
-
-private:
- dispatch_queue_t m_queue;
-
- Q_DISABLE_COPY(AVFScopedPointer)
-};
-
-typedef QPair<qreal, qreal> AVFPSRange;
-AVFPSRange qt_connection_framerates(AVCaptureConnection *videoConnection);
-
-QList<AVCaptureDeviceFormat *> qt_unique_device_formats(AVCaptureDevice *captureDevice,
- FourCharCode preferredFormat);
-QSize qt_device_format_resolution(AVCaptureDeviceFormat *format);
-QSize qt_device_format_high_resolution(AVCaptureDeviceFormat *format);
-QSize qt_device_format_pixel_aspect_ratio(AVCaptureDeviceFormat *format);
-QList<AVFPSRange> qt_device_format_framerates(AVCaptureDeviceFormat *format);
-AVCaptureDeviceFormat *qt_find_best_resolution_match(AVCaptureDevice *captureDevice, const QSize &res,
- FourCharCode preferredFormat, bool stillImage = true);
-AVCaptureDeviceFormat *qt_find_best_framerate_match(AVCaptureDevice *captureDevice,
- FourCharCode preferredFormat,
- Float64 fps);
-AVFrameRateRange *qt_find_supported_framerate_range(AVCaptureDeviceFormat *format, Float64 fps);
-
-bool qt_formats_are_equal(AVCaptureDeviceFormat *f1, AVCaptureDeviceFormat *f2);
-bool qt_set_active_format(AVCaptureDevice *captureDevice, AVCaptureDeviceFormat *format, bool preserveFps);
-
-AVFPSRange qt_current_framerates(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection);
-void qt_set_framerate_limits(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection,
- qreal minFPS, qreal maxFPS);
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/avfoundation/camera/avfcamerautility.mm b/src/plugins/avfoundation/camera/avfcamerautility.mm
deleted file mode 100644
index 25ccc4b01..000000000
--- a/src/plugins/avfoundation/camera/avfcamerautility.mm
+++ /dev/null
@@ -1,575 +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 "avfcamerautility.h"
-#include "avfcameradebug.h"
-
-#include <QtCore/qvector.h>
-#include <QtCore/qpair.h>
-#include <private/qmultimediautils_p.h>
-
-#include <functional>
-#include <algorithm>
-#include <limits>
-#include <tuple>
-
-QT_BEGIN_NAMESPACE
-
-AVFPSRange qt_connection_framerates(AVCaptureConnection *videoConnection)
-{
- Q_ASSERT(videoConnection);
-
- AVFPSRange newRange;
- // "The value in the videoMinFrameDuration is equivalent to the reciprocal
- // of the maximum framerate, the value in the videoMaxFrameDuration is equivalent
- // to the reciprocal of the minimum framerate."
- if (videoConnection.supportsVideoMinFrameDuration) {
- const CMTime cmMin = videoConnection.videoMinFrameDuration;
- if (CMTimeCompare(cmMin, kCMTimeInvalid)) { // Has some non-default value:
- if (const Float64 minSeconds = CMTimeGetSeconds(cmMin))
- newRange.second = 1. / minSeconds;
- }
- }
-
- if (videoConnection.supportsVideoMaxFrameDuration) {
- const CMTime cmMax = videoConnection.videoMaxFrameDuration;
- if (CMTimeCompare(cmMax, kCMTimeInvalid)) {
- if (const Float64 maxSeconds = CMTimeGetSeconds(cmMax))
- newRange.first = 1. / maxSeconds;
- }
- }
-
- return newRange;
-}
-
-namespace {
-
-inline bool qt_area_sane(const QSize &size)
-{
- return !size.isNull() && size.isValid()
- && std::numeric_limits<int>::max() / size.width() >= size.height();
-}
-
-template <template <typename...> class Comp> // std::less or std::greater (or std::equal_to)
-struct ByResolution
-{
- bool operator() (AVCaptureDeviceFormat *f1, AVCaptureDeviceFormat *f2)const
- {
- Q_ASSERT(f1 && f2);
- const QSize r1(qt_device_format_resolution(f1));
- const QSize r2(qt_device_format_resolution(f2));
- // use std::tuple for lexicograpical sorting:
- const Comp<std::tuple<int, int>> op = {};
- return op(std::make_tuple(r1.width(), r1.height()),
- std::make_tuple(r2.width(), r2.height()));
- }
-};
-
-struct FormatHasNoFPSRange : std::unary_function<AVCaptureDeviceFormat *, bool>
-{
- bool operator() (AVCaptureDeviceFormat *format)
- {
- Q_ASSERT(format);
- return !format.videoSupportedFrameRateRanges || !format.videoSupportedFrameRateRanges.count;
- }
-};
-
-Float64 qt_find_min_framerate_distance(AVCaptureDeviceFormat *format, Float64 fps)
-{
- Q_ASSERT(format && format.videoSupportedFrameRateRanges
- && format.videoSupportedFrameRateRanges.count);
-
- AVFrameRateRange *range = [format.videoSupportedFrameRateRanges objectAtIndex:0];
- Float64 distance = qAbs(range.maxFrameRate - fps);
- for (NSUInteger i = 1, e = format.videoSupportedFrameRateRanges.count; i < e; ++i) {
- range = [format.videoSupportedFrameRateRanges objectAtIndex:i];
- distance = qMin(distance, qAbs(range.maxFrameRate - fps));
- }
-
- return distance;
-}
-
-} // Unnamed namespace.
-
-QVector<AVCaptureDeviceFormat *> qt_unique_device_formats(AVCaptureDevice *captureDevice, FourCharCode filter)
-{
- // 'filter' is the format we prefer if we have duplicates.
- Q_ASSERT(captureDevice);
-
- QVector<AVCaptureDeviceFormat *> formats;
-
- if (!captureDevice.formats || !captureDevice.formats.count)
- return formats;
-
- formats.reserve(captureDevice.formats.count);
- for (AVCaptureDeviceFormat *format in captureDevice.formats) {
- const QSize resolution(qt_device_format_resolution(format));
- if (resolution.isNull() || !resolution.isValid())
- continue;
- formats << format;
- }
-
- if (!formats.size())
- return formats;
-
- std::sort(formats.begin(), formats.end(), ByResolution<std::less>());
-
- QSize size(qt_device_format_resolution(formats[0]));
- FourCharCode codec = CMVideoFormatDescriptionGetCodecType(formats[0].formatDescription);
- int last = 0;
- for (int i = 1; i < formats.size(); ++i) {
- const QSize nextSize(qt_device_format_resolution(formats[i]));
- if (nextSize == size) {
- if (codec == filter)
- continue;
- formats[last] = formats[i];
- } else {
- ++last;
- formats[last] = formats[i];
- size = nextSize;
- }
- codec = CMVideoFormatDescriptionGetCodecType(formats[i].formatDescription);
- }
- formats.resize(last + 1);
-
- return formats;
-}
-
-QSize qt_device_format_resolution(AVCaptureDeviceFormat *format)
-{
- if (!format || !format.formatDescription)
- return QSize();
-
- const CMVideoDimensions res = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
- return QSize(res.width, res.height);
-}
-
-QSize qt_device_format_high_resolution(AVCaptureDeviceFormat *format)
-{
- Q_ASSERT(format);
- QSize res;
-#if defined(Q_OS_IOS)
- const CMVideoDimensions hrDim(format.highResolutionStillImageDimensions);
- res.setWidth(hrDim.width);
- res.setHeight(hrDim.height);
-#endif
- return res;
-}
-
-QVector<AVFPSRange> qt_device_format_framerates(AVCaptureDeviceFormat *format)
-{
- Q_ASSERT(format);
-
- QVector<AVFPSRange> qtRanges;
-
- if (!format.videoSupportedFrameRateRanges || !format.videoSupportedFrameRateRanges.count)
- return qtRanges;
-
- qtRanges.reserve(format.videoSupportedFrameRateRanges.count);
- for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges)
- qtRanges << AVFPSRange(range.minFrameRate, range.maxFrameRate);
-
- return qtRanges;
-}
-
-QSize qt_device_format_pixel_aspect_ratio(AVCaptureDeviceFormat *format)
-{
- Q_ASSERT(format);
-
- if (!format.formatDescription) {
- qDebugCamera() << Q_FUNC_INFO << "no format description found";
- return QSize();
- }
-
- const CMVideoDimensions res = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
- const CGSize resPAR = CMVideoFormatDescriptionGetPresentationDimensions(format.formatDescription, true, false);
-
- if (qAbs(resPAR.width - res.width) < 1.) {
- // "Pixel aspect ratio is used to adjust the width, leaving the height alone."
- return QSize(1, 1);
- }
-
- 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);
-
- return QSize(n, d);
-}
-
-AVCaptureDeviceFormat *qt_find_best_resolution_match(AVCaptureDevice *captureDevice,
- const QSize &request,
- FourCharCode filter,
- bool stillImage)
-{
- Q_ASSERT(captureDevice);
- Q_ASSERT(!request.isNull() && request.isValid());
-
- if (!captureDevice.formats || !captureDevice.formats.count)
- return nullptr;
-
- QVector<AVCaptureDeviceFormat *> formats(qt_unique_device_formats(captureDevice, filter));
-
- for (int i = 0; i < formats.size(); ++i) {
- AVCaptureDeviceFormat *format = formats[i];
- if (qt_device_format_resolution(format) == request)
- return format;
- // iOS only (still images).
- if (stillImage && qt_device_format_high_resolution(format) == request)
- return format;
- }
-
- if (!qt_area_sane(request))
- return nullptr;
-
- typedef QPair<QSize, AVCaptureDeviceFormat *> FormatPair;
-
- QVector<FormatPair> pairs; // default|HR sizes
- pairs.reserve(formats.size());
-
- for (int i = 0; i < formats.size(); ++i) {
- AVCaptureDeviceFormat *format = formats[i];
- const QSize res(qt_device_format_resolution(format));
- if (!res.isNull() && res.isValid() && qt_area_sane(res))
- pairs << FormatPair(res, format);
- const QSize highRes(qt_device_format_high_resolution(format));
- if (stillImage && !highRes.isNull() && highRes.isValid() && qt_area_sane(highRes))
- pairs << FormatPair(highRes, format);
- }
-
- if (!pairs.size())
- return nullptr;
-
- AVCaptureDeviceFormat *best = pairs[0].second;
- QSize next(pairs[0].first);
- int wDiff = qAbs(request.width() - next.width());
- int hDiff = qAbs(request.height() - next.height());
- const int area = request.width() * request.height();
- int areaDiff = qAbs(area - next.width() * next.height());
- for (int i = 1; i < pairs.size(); ++i) {
- next = pairs[i].first;
- const int newWDiff = qAbs(next.width() - request.width());
- const int newHDiff = qAbs(next.height() - request.height());
- const int newAreaDiff = qAbs(area - next.width() * next.height());
-
- if ((newWDiff < wDiff && newHDiff < hDiff)
- || ((newWDiff <= wDiff || newHDiff <= hDiff) && newAreaDiff <= areaDiff)) {
- wDiff = newWDiff;
- hDiff = newHDiff;
- best = pairs[i].second;
- areaDiff = newAreaDiff;
- }
- }
-
- return best;
-}
-
-AVCaptureDeviceFormat *qt_find_best_framerate_match(AVCaptureDevice *captureDevice,
- FourCharCode filter,
- Float64 fps)
-{
- Q_ASSERT(captureDevice);
- Q_ASSERT(fps > 0.);
-
- const qreal epsilon = 0.1;
-
- QVector<AVCaptureDeviceFormat *>sorted(qt_unique_device_formats(captureDevice, filter));
- // Sort formats by their resolution in decreasing order:
- std::sort(sorted.begin(), sorted.end(), ByResolution<std::greater>());
- // We can use only formats with framerate ranges:
- sorted.erase(std::remove_if(sorted.begin(), sorted.end(), FormatHasNoFPSRange()), sorted.end());
-
- if (!sorted.size())
- return nil;
-
- for (int i = 0; i < sorted.size(); ++i) {
- AVCaptureDeviceFormat *format = sorted[i];
- for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) {
- if (range.maxFrameRate - range.minFrameRate < epsilon) {
- // On OS X ranges are points (built-in camera).
- if (qAbs(fps - range.maxFrameRate) < epsilon)
- return format;
- }
-
- if (fps >= range.minFrameRate && fps <= range.maxFrameRate)
- return format;
- }
- }
-
- Float64 distance = qt_find_min_framerate_distance(sorted[0], fps);
- AVCaptureDeviceFormat *match = sorted[0];
- for (int i = 1; i < sorted.size(); ++i) {
- const Float64 newDistance = qt_find_min_framerate_distance(sorted[i], fps);
- if (newDistance < distance) {
- distance = newDistance;
- match = sorted[i];
- }
- }
-
- return match;
-}
-
-AVFrameRateRange *qt_find_supported_framerate_range(AVCaptureDeviceFormat *format, Float64 fps)
-{
- Q_ASSERT(format && format.videoSupportedFrameRateRanges
- && format.videoSupportedFrameRateRanges.count);
-
- const qreal epsilon = 0.1;
-
- for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) {
- if (range.maxFrameRate - range.minFrameRate < epsilon) {
- // On OS X ranges are points (built-in camera).
- if (qAbs(fps - range.maxFrameRate) < epsilon)
- return range;
- }
-
- if (fps >= range.minFrameRate && fps <= range.maxFrameRate)
- return range;
- }
-
- AVFrameRateRange *match = [format.videoSupportedFrameRateRanges objectAtIndex:0];
- Float64 distance = qAbs(match.maxFrameRate - fps);
- for (NSUInteger i = 1, e = format.videoSupportedFrameRateRanges.count; i < e; ++i) {
- AVFrameRateRange *range = [format.videoSupportedFrameRateRanges objectAtIndex:i];
- const Float64 newDistance = qAbs(range.maxFrameRate - fps);
- if (newDistance < distance) {
- distance = newDistance;
- match = range;
- }
- }
-
- return match;
-}
-
-bool qt_formats_are_equal(AVCaptureDeviceFormat *f1, AVCaptureDeviceFormat *f2)
-{
- if (f1 == f2)
- return true;
-
- if (![f1.mediaType isEqualToString:f2.mediaType])
- return false;
-
- return CMFormatDescriptionEqual(f1.formatDescription, f2.formatDescription);
-}
-
-bool qt_set_active_format(AVCaptureDevice *captureDevice, AVCaptureDeviceFormat *format, bool preserveFps)
-{
- static bool firstSet = true;
-
- if (!captureDevice || !format)
- return false;
-
- if (qt_formats_are_equal(captureDevice.activeFormat, format)) {
- if (firstSet) {
- // The capture device format is persistent. The first time we set a format, report that
- // it changed even if the formats are the same.
- // This prevents the session from resetting the format to the default value.
- firstSet = false;
- return true;
- }
- return false;
- }
-
- firstSet = false;
-
- const AVFConfigurationLock lock(captureDevice);
- if (!lock) {
- qWarning("Failed to set active format (lock failed)");
- return false;
- }
-
- // Changing the activeFormat resets the frame rate.
- AVFPSRange fps;
- if (preserveFps)
- fps = qt_current_framerates(captureDevice, nil);
-
- captureDevice.activeFormat = format;
-
- if (preserveFps)
- qt_set_framerate_limits(captureDevice, nil, fps.first, fps.second);
-
- return true;
-}
-
-void qt_set_framerate_limits(AVCaptureConnection *videoConnection, qreal minFPS, qreal maxFPS)
-{
- Q_ASSERT(videoConnection);
-
- if (minFPS < 0. || maxFPS < 0. || (maxFPS && maxFPS < minFPS)) {
- qDebugCamera() << Q_FUNC_INFO << "invalid framerates (min, max):"
- << minFPS << maxFPS;
- return;
- }
-
- CMTime minDuration = kCMTimeInvalid;
- if (maxFPS > 0.) {
- if (!videoConnection.supportsVideoMinFrameDuration)
- qDebugCamera() << Q_FUNC_INFO << "maximum framerate is not supported";
- else
- minDuration = CMTimeMake(1, maxFPS);
- }
- if (videoConnection.supportsVideoMinFrameDuration)
- videoConnection.videoMinFrameDuration = minDuration;
-
- CMTime maxDuration = kCMTimeInvalid;
- if (minFPS > 0.) {
- if (!videoConnection.supportsVideoMaxFrameDuration)
- qDebugCamera() << Q_FUNC_INFO << "minimum framerate is not supported";
- else
- maxDuration = CMTimeMake(1, minFPS);
- }
- if (videoConnection.supportsVideoMaxFrameDuration)
- videoConnection.videoMaxFrameDuration = maxDuration;
-}
-
-CMTime qt_adjusted_frame_duration(AVFrameRateRange *range, qreal fps)
-{
- Q_ASSERT(range);
- Q_ASSERT(fps > 0.);
-
- if (range.maxFrameRate - range.minFrameRate < 0.1) {
- // Can happen on OS X.
- return range.minFrameDuration;
- }
-
- if (fps <= range.minFrameRate)
- return range.maxFrameDuration;
- if (fps >= range.maxFrameRate)
- return range.minFrameDuration;
-
- int n, d;
- qt_real_to_fraction(1. / fps, &n, &d);
- return CMTimeMake(n, d);
-}
-
-void qt_set_framerate_limits(AVCaptureDevice *captureDevice, qreal minFPS, qreal maxFPS)
-{
- Q_ASSERT(captureDevice);
- if (!captureDevice.activeFormat) {
- qDebugCamera() << Q_FUNC_INFO << "no active capture device format";
- return;
- }
-
- if (minFPS < 0. || maxFPS < 0. || (maxFPS && maxFPS < minFPS)) {
- qDebugCamera() << Q_FUNC_INFO << "invalid framerates (min, max):"
- << minFPS << maxFPS;
- return;
- }
-
- CMTime minFrameDuration = kCMTimeInvalid;
- CMTime maxFrameDuration = kCMTimeInvalid;
- if (maxFPS || minFPS) {
- AVFrameRateRange *range = qt_find_supported_framerate_range(captureDevice.activeFormat,
- maxFPS ? maxFPS : minFPS);
- if (!range) {
- qDebugCamera() << Q_FUNC_INFO << "no framerate range found, (min, max):"
- << minFPS << maxFPS;
- return;
- }
-
- if (maxFPS)
- minFrameDuration = qt_adjusted_frame_duration(range, maxFPS);
- if (minFPS)
- maxFrameDuration = qt_adjusted_frame_duration(range, minFPS);
- }
-
- const AVFConfigurationLock lock(captureDevice);
- if (!lock) {
- qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
- return;
- }
-
- // While Apple's docs say kCMTimeInvalid will end in default
- // settings for this format, kCMTimeInvalid on OS X ends with a runtime
- // exception:
- // "The activeVideoMinFrameDuration passed is not supported by the device."
- // Instead, use the first item in the supported frame rates.
-#ifdef Q_OS_IOS
- [captureDevice setActiveVideoMinFrameDuration:minFrameDuration];
- [captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration];
-#elif defined(Q_OS_MACOS)
- if (CMTimeCompare(minFrameDuration, kCMTimeInvalid) == 0
- && CMTimeCompare(maxFrameDuration, kCMTimeInvalid) == 0) {
- AVFrameRateRange *range = captureDevice.activeFormat.videoSupportedFrameRateRanges.firstObject;
- minFrameDuration = range.minFrameDuration;
- maxFrameDuration = range.maxFrameDuration;
- }
-
- if (CMTimeCompare(minFrameDuration, kCMTimeInvalid))
- [captureDevice setActiveVideoMinFrameDuration:minFrameDuration];
-
- if (CMTimeCompare(maxFrameDuration, kCMTimeInvalid))
- [captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration];
-#endif // Q_OS_MACOS
-}
-
-void qt_set_framerate_limits(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection,
- qreal minFPS, qreal maxFPS)
-{
- Q_UNUSED(videoConnection);
- Q_ASSERT(captureDevice);
- qt_set_framerate_limits(captureDevice, minFPS, maxFPS);
-}
-
-AVFPSRange qt_current_framerates(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection)
-{
- Q_UNUSED(videoConnection);
- Q_ASSERT(captureDevice);
-
- AVFPSRange fps;
- const CMTime minDuration = captureDevice.activeVideoMinFrameDuration;
- if (CMTimeCompare(minDuration, kCMTimeInvalid)) {
- if (const Float64 minSeconds = CMTimeGetSeconds(minDuration))
- fps.second = 1. / minSeconds; // Max FPS = 1 / MinDuration.
- }
-
- const CMTime maxDuration = captureDevice.activeVideoMaxFrameDuration;
- if (CMTimeCompare(maxDuration, kCMTimeInvalid)) {
- if (const Float64 maxSeconds = CMTimeGetSeconds(maxDuration))
- fps.first = 1. / maxSeconds; // Min FPS = 1 / MaxDuration.
- }
-
- return fps;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h
deleted file mode 100644
index 7cd59117f..000000000
--- a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 AVFCAMERAVIEWFINDERSETTINGSCONTROL_H
-#define AVFCAMERAVIEWFINDERSETTINGSCONTROL_H
-
-#include <QtMultimedia/qcameraviewfindersettingscontrol.h>
-#include <QtMultimedia/qcameraviewfindersettings.h>
-#include <QtMultimedia/qvideoframe.h>
-
-#include <QtCore/qpointer.h>
-#include <QtCore/qglobal.h>
-#include <QtCore/qsize.h>
-
-@class AVCaptureDevice;
-@class AVCaptureVideoDataOutput;
-@class AVCaptureConnection;
-@class AVCaptureDeviceFormat;
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraSession;
-class AVFCameraService;
-
-class AVFCameraViewfinderSettingsControl2 : public QCameraViewfinderSettingsControl2
-{
- Q_OBJECT
-
- friend class AVFCameraSession;
- friend class AVFCameraViewfinderSettingsControl;
-public:
- AVFCameraViewfinderSettingsControl2(AVFCameraService *service);
-
- QList<QCameraViewfinderSettings> supportedViewfinderSettings() const override;
- QCameraViewfinderSettings viewfinderSettings() const override;
- void setViewfinderSettings(const QCameraViewfinderSettings &settings) override;
-
- // "Converters":
- static QVideoFrame::PixelFormat QtPixelFormatFromCVFormat(unsigned avPixelFormat);
- static bool CVPixelFormatFromQtFormat(QVideoFrame::PixelFormat qtFormat, unsigned &conv);
-
-private:
- void setResolution(const QSize &resolution);
- void setFramerate(qreal minFPS, qreal maxFPS, bool useActive);
- void setPixelFormat(QVideoFrame::PixelFormat newFormat);
- AVCaptureDeviceFormat *findBestFormatMatch(const QCameraViewfinderSettings &settings) const;
- QList<QVideoFrame::PixelFormat> viewfinderPixelFormats() const;
- bool convertPixelFormatIfSupported(QVideoFrame::PixelFormat format, unsigned &avfFormat) const;
- bool applySettings(const QCameraViewfinderSettings &settings);
- QCameraViewfinderSettings requestedSettings() const;
-
- AVCaptureConnection *videoConnection() const;
-
- AVFCameraService *m_service;
- QCameraViewfinderSettings m_settings;
-};
-
-class AVFCameraViewfinderSettingsControl : public QCameraViewfinderSettingsControl
-{
- Q_OBJECT
-public:
- AVFCameraViewfinderSettingsControl(AVFCameraService *service);
-
- bool isViewfinderParameterSupported(ViewfinderParameter parameter) const override;
- QVariant viewfinderParameter(ViewfinderParameter parameter) const override;
- void setViewfinderParameter(ViewfinderParameter parameter, const QVariant &value) override;
-
-private:
- void setResolution(const QVariant &resolution);
- void setAspectRatio(const QVariant &aspectRatio);
- void setFrameRate(const QVariant &fps, bool max);
- void setPixelFormat(const QVariant &pf);
- bool initSettingsControl() const;
-
- AVFCameraService *m_service;
- mutable QPointer<AVFCameraViewfinderSettingsControl2> m_settingsControl;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm
deleted file mode 100644
index dd0393f96..000000000
--- a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm
+++ /dev/null
@@ -1,599 +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 "avfcameraviewfindersettingscontrol.h"
-#include "avfcamerarenderercontrol.h"
-#include "avfcamerautility.h"
-#include "avfcamerasession.h"
-#include "avfcameraservice.h"
-#include "avfcameradebug.h"
-
-#include <QtMultimedia/qabstractvideosurface.h>
-
-#include <QtCore/qvariant.h>
-#include <QtCore/qvector.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qlist.h>
-#include <private/qmultimediautils_p.h>
-
-#include <algorithm>
-
-#include <AVFoundation/AVFoundation.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace {
-
-bool qt_framerates_sane(const QCameraViewfinderSettings &settings)
-{
- const qreal minFPS = settings.minimumFrameRate();
- const qreal maxFPS = settings.maximumFrameRate();
-
- if (minFPS < 0. || maxFPS < 0.)
- return false;
-
- return !maxFPS || maxFPS >= minFPS;
-}
-
-} // Unnamed namespace.
-
-AVFCameraViewfinderSettingsControl2::AVFCameraViewfinderSettingsControl2(AVFCameraService *service)
- : m_service(service)
-{
- Q_ASSERT(service);
-}
-
-QList<QCameraViewfinderSettings> AVFCameraViewfinderSettingsControl2::supportedViewfinderSettings() const
-{
- QList<QCameraViewfinderSettings> supportedSettings;
-
- AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
- if (!captureDevice) {
- qDebugCamera() << Q_FUNC_INFO << "no capture device found";
- return supportedSettings;
- }
-
- QVector<AVFPSRange> framerates;
-
- QVector<QVideoFrame::PixelFormat> pixelFormats(viewfinderPixelFormats());
-
- if (!pixelFormats.size())
- pixelFormats << QVideoFrame::Format_Invalid; // The default value.
-
- if (!captureDevice.formats || !captureDevice.formats.count) {
- qDebugCamera() << Q_FUNC_INFO << "no capture device formats found";
- return supportedSettings;
- }
-
- const QVector<AVCaptureDeviceFormat *> formats(qt_unique_device_formats(captureDevice,
- m_service->session()->defaultCodec()));
- for (int i = 0; i < formats.size(); ++i) {
- AVCaptureDeviceFormat *format = formats[i];
-
- const QSize res(qt_device_format_resolution(format));
- if (res.isNull() || !res.isValid())
- continue;
- const QSize par(qt_device_format_pixel_aspect_ratio(format));
- if (par.isNull() || !par.isValid())
- continue;
-
- framerates = qt_device_format_framerates(format);
- if (!framerates.size())
- framerates << AVFPSRange(); // The default value.
-
- for (int i = 0; i < pixelFormats.size(); ++i) {
- for (int j = 0; j < framerates.size(); ++j) {
- QCameraViewfinderSettings newSet;
- newSet.setResolution(res);
- newSet.setPixelAspectRatio(par);
- newSet.setPixelFormat(pixelFormats[i]);
- newSet.setMinimumFrameRate(framerates[j].first);
- newSet.setMaximumFrameRate(framerates[j].second);
- supportedSettings << newSet;
- }
- }
- }
-
- return supportedSettings;
-}
-
-QCameraViewfinderSettings AVFCameraViewfinderSettingsControl2::viewfinderSettings() const
-{
- QCameraViewfinderSettings settings = m_settings;
-
- AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
- if (!captureDevice) {
- qDebugCamera() << Q_FUNC_INFO << "no capture device found";
- return settings;
- }
-
- if (m_service->session()->state() != QCamera::LoadedState &&
- m_service->session()->state() != QCamera::ActiveState) {
- return settings;
- }
-
- if (!captureDevice.activeFormat) {
- qDebugCamera() << Q_FUNC_INFO << "no active capture device format";
- return settings;
- }
-
- const QSize res(qt_device_format_resolution(captureDevice.activeFormat));
- const QSize par(qt_device_format_pixel_aspect_ratio(captureDevice.activeFormat));
- if (res.isNull() || !res.isValid() || par.isNull() || !par.isValid()) {
- qDebugCamera() << Q_FUNC_INFO << "failed to obtain resolution/pixel aspect ratio";
- return settings;
- }
-
- settings.setResolution(res);
- settings.setPixelAspectRatio(par);
-
- const AVFPSRange fps = qt_current_framerates(captureDevice, videoConnection());
- settings.setMinimumFrameRate(fps.first);
- settings.setMaximumFrameRate(fps.second);
-
- AVCaptureVideoDataOutput *videoOutput = m_service->videoOutput() ? m_service->videoOutput()->videoDataOutput() : nullptr;
- if (videoOutput) {
- NSObject *obj = [videoOutput.videoSettings objectForKey:(id)kCVPixelBufferPixelFormatTypeKey];
- if (obj && [obj isKindOfClass:[NSNumber class]]) {
- NSNumber *nsNum = static_cast<NSNumber *>(obj);
- settings.setPixelFormat(QtPixelFormatFromCVFormat([nsNum unsignedIntValue]));
- }
- }
-
- return settings;
-}
-
-void AVFCameraViewfinderSettingsControl2::setViewfinderSettings(const QCameraViewfinderSettings &settings)
-{
- if (m_settings == settings)
- return;
-
- m_settings = settings;
-#if defined(Q_OS_IOS)
- bool active = m_service->session()->state() == QCamera::ActiveState;
- if (active)
- [m_service->session()->captureSession() beginConfiguration];
- applySettings(m_settings);
- if (active)
- [m_service->session()->captureSession() commitConfiguration];
-#else
- applySettings(m_settings);
-#endif
-}
-
-QVideoFrame::PixelFormat AVFCameraViewfinderSettingsControl2::QtPixelFormatFromCVFormat(unsigned avPixelFormat)
-{
- // BGRA <-> ARGB "swap" is intentional:
- // to work correctly with GL_RGBA, color swap shaders
- // (in QSG node renderer etc.).
- switch (avPixelFormat) {
- case kCVPixelFormatType_32ARGB:
- return QVideoFrame::Format_BGRA32;
- case kCVPixelFormatType_32BGRA:
- return QVideoFrame::Format_ARGB32;
- case kCVPixelFormatType_24RGB:
- return QVideoFrame::Format_RGB24;
- case kCVPixelFormatType_24BGR:
- return QVideoFrame::Format_BGR24;
- case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
- case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
- return QVideoFrame::Format_NV12;
- case kCVPixelFormatType_422YpCbCr8:
- return QVideoFrame::Format_UYVY;
- case kCVPixelFormatType_422YpCbCr8_yuvs:
- return QVideoFrame::Format_YUYV;
- default:
- return QVideoFrame::Format_Invalid;
- }
-}
-
-bool AVFCameraViewfinderSettingsControl2::CVPixelFormatFromQtFormat(QVideoFrame::PixelFormat qtFormat, unsigned &conv)
-{
- // BGRA <-> ARGB "swap" is intentional:
- // to work correctly with GL_RGBA, color swap shaders
- // (in QSG node renderer etc.).
- switch (qtFormat) {
- case QVideoFrame::Format_ARGB32:
- conv = kCVPixelFormatType_32BGRA;
- break;
- case QVideoFrame::Format_BGRA32:
- conv = kCVPixelFormatType_32ARGB;
- break;
- case QVideoFrame::Format_NV12:
- conv = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;
- break;
- case QVideoFrame::Format_UYVY:
- conv = kCVPixelFormatType_422YpCbCr8;
- break;
- case QVideoFrame::Format_YUYV:
- conv = kCVPixelFormatType_422YpCbCr8_yuvs;
- break;
- // These two formats below are not supported
- // by QSGVideoNodeFactory_RGB, so for now I have to
- // disable them.
- /*
- case QVideoFrame::Format_RGB24:
- conv = kCVPixelFormatType_24RGB;
- break;
- case QVideoFrame::Format_BGR24:
- conv = kCVPixelFormatType_24BGR;
- break;
- */
- default:
- return false;
- }
-
- return true;
-}
-
-AVCaptureDeviceFormat *AVFCameraViewfinderSettingsControl2::findBestFormatMatch(const QCameraViewfinderSettings &settings) const
-{
- AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
- if (!captureDevice || settings.isNull())
- return nil;
-
- const QSize &resolution = settings.resolution();
- if (!resolution.isNull() && resolution.isValid()) {
- // Either the exact match (including high resolution for images on iOS)
- // or a format with a resolution close to the requested one.
- return qt_find_best_resolution_match(captureDevice, resolution,
- m_service->session()->defaultCodec(), false);
- }
-
- // No resolution requested, what about framerates?
- if (!qt_framerates_sane(settings)) {
- qDebugCamera() << Q_FUNC_INFO << "invalid framerate requested (min/max):"
- << settings.minimumFrameRate() << settings.maximumFrameRate();
- return nil;
- }
-
- const qreal minFPS(settings.minimumFrameRate());
- const qreal maxFPS(settings.maximumFrameRate());
- if (minFPS || maxFPS)
- return qt_find_best_framerate_match(captureDevice,
- m_service->session()->defaultCodec(),
- maxFPS ? maxFPS : minFPS);
- // Ignore PAR for the moment (PAR without resolution can
- // pick a format with really bad resolution).
- // No need to test pixel format, just return settings.
-
- return nil;
-}
-
-QVector<QVideoFrame::PixelFormat> AVFCameraViewfinderSettingsControl2::viewfinderPixelFormats() const
-{
- QVector<QVideoFrame::PixelFormat> qtFormats;
-
- AVCaptureVideoDataOutput *videoOutput = m_service->videoOutput() ? m_service->videoOutput()->videoDataOutput() : nullptr;
- if (!videoOutput) {
- qDebugCamera() << Q_FUNC_INFO << "no video output found";
- return qtFormats;
- }
-
- NSArray *pixelFormats = [videoOutput availableVideoCVPixelFormatTypes];
-
- for (NSObject *obj in pixelFormats) {
- if (![obj isKindOfClass:[NSNumber class]])
- continue;
-
- NSNumber *formatAsNSNumber = static_cast<NSNumber *>(obj);
- // It's actually FourCharCode (== UInt32):
- const QVideoFrame::PixelFormat qtFormat(QtPixelFormatFromCVFormat([formatAsNSNumber unsignedIntValue]));
- if (qtFormat != QVideoFrame::Format_Invalid
- && !qtFormats.contains(qtFormat)) { // Can happen, for example, with 8BiPlanar existing in video/full range.
- qtFormats << qtFormat;
- }
- }
-
- return qtFormats;
-}
-
-bool AVFCameraViewfinderSettingsControl2::convertPixelFormatIfSupported(QVideoFrame::PixelFormat qtFormat,
- unsigned &avfFormat)const
-{
- AVCaptureVideoDataOutput *videoOutput = m_service->videoOutput() ? m_service->videoOutput()->videoDataOutput() : nullptr;
- if (!videoOutput)
- return false;
-
- unsigned conv = 0;
- if (!CVPixelFormatFromQtFormat(qtFormat, conv))
- return false;
-
- NSArray *formats = [videoOutput availableVideoCVPixelFormatTypes];
- if (!formats || !formats.count)
- return false;
-
- if (m_service->videoOutput()->surface()) {
- const QAbstractVideoSurface *surface = m_service->videoOutput()->surface();
- QAbstractVideoBuffer::HandleType h = m_service->videoOutput()->supportsTextures()
- ? QAbstractVideoBuffer::GLTextureHandle
- : QAbstractVideoBuffer::NoHandle;
- if (!surface->supportedPixelFormats(h).contains(qtFormat))
- return false;
- }
-
- bool found = false;
- for (NSObject *obj in formats) {
- if (![obj isKindOfClass:[NSNumber class]])
- continue;
-
- NSNumber *nsNum = static_cast<NSNumber *>(obj);
- if ([nsNum unsignedIntValue] == conv) {
- avfFormat = conv;
- found = true;
- }
- }
-
- return found;
-}
-
-bool AVFCameraViewfinderSettingsControl2::applySettings(const QCameraViewfinderSettings &settings)
-{
- if (m_service->session()->state() != QCamera::LoadedState &&
- m_service->session()->state() != QCamera::ActiveState) {
- return false;
- }
-
- AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
- if (!captureDevice)
- return false;
-
- bool activeFormatChanged = false;
-
- AVCaptureDeviceFormat *match = findBestFormatMatch(settings);
- if (match) {
- activeFormatChanged = qt_set_active_format(captureDevice, match, false);
- } else {
- qDebugCamera() << Q_FUNC_INFO << "matching device format not found";
- // We still can update the pixel format at least.
- }
-
- AVCaptureVideoDataOutput *videoOutput = m_service->videoOutput() ? m_service->videoOutput()->videoDataOutput() : nullptr;
- if (videoOutput) {
- unsigned avfPixelFormat = 0;
- if (!convertPixelFormatIfSupported(settings.pixelFormat(), avfPixelFormat)) {
- // If the the pixel format is not specified or invalid, pick the preferred video surface
- // format, or if no surface is set, the preferred capture device format
-
- const QVector<QVideoFrame::PixelFormat> deviceFormats = viewfinderPixelFormats();
- QAbstractVideoSurface *surface = m_service->videoOutput()->surface();
- QVideoFrame::PixelFormat pickedFormat = deviceFormats.first();
- if (surface) {
- pickedFormat = QVideoFrame::Format_Invalid;
- QAbstractVideoBuffer::HandleType h = m_service->videoOutput()->supportsTextures()
- ? QAbstractVideoBuffer::GLTextureHandle
- : QAbstractVideoBuffer::NoHandle;
- QList<QVideoFrame::PixelFormat> surfaceFormats = surface->supportedPixelFormats(h);
- for (int i = 0; i < surfaceFormats.count(); ++i) {
- const QVideoFrame::PixelFormat surfaceFormat = surfaceFormats.at(i);
- if (deviceFormats.contains(surfaceFormat)) {
- pickedFormat = surfaceFormat;
- break;
- }
- }
- }
-
- CVPixelFormatFromQtFormat(pickedFormat, avfPixelFormat);
- }
-
- NSMutableDictionary *videoSettings = [NSMutableDictionary dictionaryWithCapacity:1];
- [videoSettings setObject:[NSNumber numberWithUnsignedInt:avfPixelFormat]
- forKey:(id)kCVPixelBufferPixelFormatTypeKey];
-
- const AVFConfigurationLock lock(captureDevice);
- if (!lock)
- qWarning("Failed to set active format (lock failed)");
-
- videoOutput.videoSettings = videoSettings;
- }
-
- qt_set_framerate_limits(captureDevice, videoConnection(), settings.minimumFrameRate(), settings.maximumFrameRate());
-
- return activeFormatChanged;
-}
-
-QCameraViewfinderSettings AVFCameraViewfinderSettingsControl2::requestedSettings() const
-{
- return m_settings;
-}
-
-AVCaptureConnection *AVFCameraViewfinderSettingsControl2::videoConnection() const
-{
- if (!m_service->videoOutput() || !m_service->videoOutput()->videoDataOutput())
- return nil;
-
- return [m_service->videoOutput()->videoDataOutput() connectionWithMediaType:AVMediaTypeVideo];
-}
-
-AVFCameraViewfinderSettingsControl::AVFCameraViewfinderSettingsControl(AVFCameraService *service)
- : m_service(service)
-{
- // Legacy viewfinder settings control.
- Q_ASSERT(service);
- initSettingsControl();
-}
-
-bool AVFCameraViewfinderSettingsControl::isViewfinderParameterSupported(ViewfinderParameter parameter) const
-{
- return parameter == Resolution
- || parameter == PixelAspectRatio
- || parameter == MinimumFrameRate
- || parameter == MaximumFrameRate
- || parameter == PixelFormat;
-}
-
-QVariant AVFCameraViewfinderSettingsControl::viewfinderParameter(ViewfinderParameter parameter) const
-{
- if (!isViewfinderParameterSupported(parameter)) {
- qDebugCamera() << Q_FUNC_INFO << "parameter is not supported";
- return QVariant();
- }
-
- if (!initSettingsControl()) {
- qDebugCamera() << Q_FUNC_INFO << "initialization failed";
- return QVariant();
- }
-
- const QCameraViewfinderSettings settings(m_settingsControl->viewfinderSettings());
- if (parameter == Resolution)
- return settings.resolution();
- if (parameter == PixelAspectRatio)
- return settings.pixelAspectRatio();
- if (parameter == MinimumFrameRate)
- return settings.minimumFrameRate();
- if (parameter == MaximumFrameRate)
- return settings.maximumFrameRate();
- if (parameter == PixelFormat)
- return QVariant::fromValue(settings.pixelFormat());
-
- return QVariant();
-}
-
-void AVFCameraViewfinderSettingsControl::setViewfinderParameter(ViewfinderParameter parameter, const QVariant &value)
-{
- if (!isViewfinderParameterSupported(parameter)) {
- qDebugCamera() << Q_FUNC_INFO << "parameter is not supported";
- return;
- }
-
- if (parameter == Resolution)
- setResolution(value);
- if (parameter == PixelAspectRatio)
- setAspectRatio(value);
- if (parameter == MinimumFrameRate)
- setFrameRate(value, false);
- if (parameter == MaximumFrameRate)
- setFrameRate(value, true);
- if (parameter == PixelFormat)
- setPixelFormat(value);
-}
-
-void AVFCameraViewfinderSettingsControl::setResolution(const QVariant &newValue)
-{
- if (!newValue.canConvert<QSize>()) {
- qDebugCamera() << Q_FUNC_INFO << "QSize type expected";
- return;
- }
-
- if (!initSettingsControl()) {
- qDebugCamera() << Q_FUNC_INFO << "initialization failed";
- return;
- }
-
- const QSize res(newValue.toSize());
- if (res.isNull() || !res.isValid()) {
- qDebugCamera() << Q_FUNC_INFO << "invalid resolution:" << res;
- return;
- }
-
- QCameraViewfinderSettings settings(m_settingsControl->viewfinderSettings());
- settings.setResolution(res);
- m_settingsControl->setViewfinderSettings(settings);
-}
-
-void AVFCameraViewfinderSettingsControl::setAspectRatio(const QVariant &newValue)
-{
- if (!newValue.canConvert<QSize>()) {
- qDebugCamera() << Q_FUNC_INFO << "QSize type expected";
- return;
- }
-
- if (!initSettingsControl()) {
- qDebugCamera() << Q_FUNC_INFO << "initialization failed";
- return;
- }
-
- const QSize par(newValue.value<QSize>());
- if (par.isNull() || !par.isValid()) {
- qDebugCamera() << Q_FUNC_INFO << "invalid pixel aspect ratio:" << par;
- return;
- }
-
- QCameraViewfinderSettings settings(m_settingsControl->viewfinderSettings());
- settings.setPixelAspectRatio(par);
- m_settingsControl->setViewfinderSettings(settings);
-}
-
-void AVFCameraViewfinderSettingsControl::setFrameRate(const QVariant &newValue, bool max)
-{
- if (!newValue.canConvert<qreal>()) {
- qDebugCamera() << Q_FUNC_INFO << "qreal type expected";
- return;
- }
-
- if (!initSettingsControl()) {
- qDebugCamera() << Q_FUNC_INFO << "initialization failed";
- return;
- }
-
- const qreal fps(newValue.toReal());
- QCameraViewfinderSettings settings(m_settingsControl->viewfinderSettings());
- max ? settings.setMaximumFrameRate(fps) : settings.setMinimumFrameRate(fps);
- m_settingsControl->setViewfinderSettings(settings);
-}
-
-void AVFCameraViewfinderSettingsControl::setPixelFormat(const QVariant &newValue)
-{
- if (!newValue.canConvert<QVideoFrame::PixelFormat>()) {
- qDebugCamera() << Q_FUNC_INFO
- << "QVideoFrame::PixelFormat type expected";
- return;
- }
-
- if (!initSettingsControl()) {
- qDebugCamera() << Q_FUNC_INFO << "initialization failed";
- return;
- }
-
- QCameraViewfinderSettings settings(m_settingsControl->viewfinderSettings());
- settings.setPixelFormat(newValue.value<QVideoFrame::PixelFormat>());
- m_settingsControl->setViewfinderSettings(settings);
-}
-
-bool AVFCameraViewfinderSettingsControl::initSettingsControl()const
-{
- if (!m_settingsControl)
- m_settingsControl = m_service->viewfinderSettingsControl2();
-
- return !m_settingsControl.isNull();
-}
-
-QT_END_NAMESPACE
-
-#include "moc_avfcameraviewfindersettingscontrol.cpp"
diff --git a/src/plugins/avfoundation/camera/avfcamerawindowcontrol.h b/src/plugins/avfoundation/camera/avfcamerawindowcontrol.h
deleted file mode 100644
index d1a950e38..000000000
--- a/src/plugins/avfoundation/camera/avfcamerawindowcontrol.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef AVFCAMERAWINDOWCONTROL_H
-#define AVFCAMERAWINDOWCONTROL_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 <QVideoWindowControl>
-
-@class AVCaptureVideoPreviewLayer;
-#if defined(Q_OS_MACOS)
-@class NSView;
-typedef NSView NativeView;
-#else
-@class UIView;
-typedef UIView NativeView;
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraWindowControl : public QVideoWindowControl
-{
- Q_OBJECT
-public:
- AVFCameraWindowControl(QObject *parent = nullptr);
- virtual ~AVFCameraWindowControl() override;
-
- // QVideoWindowControl interface
-public:
- WId winId() const override;
- void setWinId(WId id) override;
-
- QRect displayRect() const override;
- void setDisplayRect(const QRect &rect) override;
-
- bool isFullScreen() const override;
- void setFullScreen(bool fullScreen) override;
-
- void repaint() override;
-
- QSize nativeSize() const override;
-
- Qt::AspectRatioMode aspectRatioMode() const override;
- void setAspectRatioMode(Qt::AspectRatioMode mode) override;
-
- int brightness() const override;
- void setBrightness(int brightness) override;
-
- int contrast() const override;
- void setContrast(int contrast) override;
-
- int hue() const override;
- void setHue(int hue) override;
-
- int saturation() const override;
- void setSaturation(int saturation) override;
-
- // AVF Camera implementation details
- void setNativeSize(QSize size);
- void setLayer(AVCaptureVideoPreviewLayer *capturePreviewLayer);
-
-private:
- void updateAspectRatio();
- void updateCaptureLayerBounds();
-
- void retainNativeLayer();
- void releaseNativeLayer();
-
- void attachNativeLayer();
- void detachNativeLayer();
-
- WId m_winId{0};
- QRect m_displayRect;
- bool m_fullscreen{false};
- Qt::AspectRatioMode m_aspectRatioMode{Qt::IgnoreAspectRatio};
- QSize m_nativeSize;
- AVCaptureVideoPreviewLayer *m_captureLayer{nullptr};
- NativeView *m_nativeView{nullptr};
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFCAMERAWINDOWCONTROL_H
diff --git a/src/plugins/avfoundation/camera/avfcamerawindowcontrol.mm b/src/plugins/avfoundation/camera/avfcamerawindowcontrol.mm
deleted file mode 100644
index 5154d0646..000000000
--- a/src/plugins/avfoundation/camera/avfcamerawindowcontrol.mm
+++ /dev/null
@@ -1,262 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 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$
-**
-****************************************************************************/
-
-#include "avfcamerawindowcontrol.h"
-
-#import <AVFoundation/AVFoundation.h>
-#import <QuartzCore/CATransaction.h>
-
-#if QT_HAS_INCLUDE(<AppKit/AppKit.h>)
-#import <AppKit/AppKit.h>
-#endif
-
-#if QT_HAS_INCLUDE(<UIKit/UIKit.h>)
-#import <UIKit/UIKit.h>
-#endif
-
-QT_USE_NAMESPACE
-
-AVFCameraWindowControl::AVFCameraWindowControl(QObject *parent)
- : QVideoWindowControl(parent)
-{
- setObjectName(QStringLiteral("AVFCameraWindowControl"));
-}
-
-AVFCameraWindowControl::~AVFCameraWindowControl()
-{
- releaseNativeLayer();
-}
-
-WId AVFCameraWindowControl::winId() const
-{
- return m_winId;
-}
-
-void AVFCameraWindowControl::setWinId(WId id)
-{
- if (m_winId == id)
- return;
-
- m_winId = id;
-
- detachNativeLayer();
- m_nativeView = (NativeView*)m_winId;
- attachNativeLayer();
-}
-
-QRect AVFCameraWindowControl::displayRect() const
-{
- return m_displayRect;
-}
-
-void AVFCameraWindowControl::setDisplayRect(const QRect &rect)
-{
- if (m_displayRect != rect) {
- m_displayRect = rect;
- updateCaptureLayerBounds();
- }
-}
-
-bool AVFCameraWindowControl::isFullScreen() const
-{
- return m_fullscreen;
-}
-
-void AVFCameraWindowControl::setFullScreen(bool fullscreen)
-{
- if (m_fullscreen != fullscreen) {
- m_fullscreen = fullscreen;
- Q_EMIT fullScreenChanged(fullscreen);
- }
-}
-
-void AVFCameraWindowControl::repaint()
-{
- if (m_captureLayer)
- [m_captureLayer setNeedsDisplay];
-}
-
-QSize AVFCameraWindowControl::nativeSize() const
-{
- return m_nativeSize;
-}
-
-void AVFCameraWindowControl::setNativeSize(QSize size)
-{
- if (m_nativeSize != size) {
- m_nativeSize = size;
- Q_EMIT nativeSizeChanged();
- }
-}
-
-Qt::AspectRatioMode AVFCameraWindowControl::aspectRatioMode() const
-{
- return m_aspectRatioMode;
-}
-
-void AVFCameraWindowControl::setAspectRatioMode(Qt::AspectRatioMode mode)
-{
- if (m_aspectRatioMode != mode) {
- m_aspectRatioMode = mode;
- updateAspectRatio();
- }
-}
-
-int AVFCameraWindowControl::brightness() const
-{
- return 0;
-}
-
-void AVFCameraWindowControl::setBrightness(int brightness)
-{
- if (0 != brightness)
- qWarning("AVFCameraWindowControl doesn't support changing Brightness");
-}
-
-int AVFCameraWindowControl::contrast() const
-{
- return 0;
-}
-
-void AVFCameraWindowControl::setContrast(int contrast)
-{
- if (0 != contrast)
- qWarning("AVFCameraWindowControl doesn't support changing Contrast");
-}
-
-int AVFCameraWindowControl::hue() const
-{
- return 0;
-}
-
-void AVFCameraWindowControl::setHue(int hue)
-{
- if (0 != hue)
- qWarning("AVFCameraWindowControl doesn't support changing Hue");
-}
-
-int AVFCameraWindowControl::saturation() const
-{
- return 0;
-}
-
-void AVFCameraWindowControl::setSaturation(int saturation)
-{
- if (0 != saturation)
- qWarning("AVFCameraWindowControl doesn't support changing Saturation");
-}
-
-void AVFCameraWindowControl::setLayer(AVCaptureVideoPreviewLayer *capturePreviewLayer)
-{
- if (m_captureLayer == capturePreviewLayer)
- return;
-
- releaseNativeLayer();
-
- m_captureLayer = capturePreviewLayer;
-
- if (m_captureLayer)
- retainNativeLayer();
-}
-
-void AVFCameraWindowControl::updateAspectRatio()
-{
- if (m_captureLayer) {
- switch (m_aspectRatioMode) {
- case Qt::IgnoreAspectRatio:
- [m_captureLayer setVideoGravity:AVLayerVideoGravityResize];
- break;
- case Qt::KeepAspectRatio:
- [m_captureLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
- break;
- case Qt::KeepAspectRatioByExpanding:
- [m_captureLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
- break;
- default:
- break;
- }
- }
-}
-
-void AVFCameraWindowControl::updateCaptureLayerBounds()
-{
- if (m_captureLayer && m_nativeView) {
- [CATransaction begin];
- [CATransaction setDisableActions: YES]; // disable animation/flicks
- m_captureLayer.frame = m_displayRect.toCGRect();
- [CATransaction commit];
- }
-}
-
-void AVFCameraWindowControl::retainNativeLayer()
-{
- [m_captureLayer retain];
-
- updateAspectRatio();
- attachNativeLayer();
-}
-
-void AVFCameraWindowControl::releaseNativeLayer()
-{
- if (m_captureLayer) {
- detachNativeLayer();
- [m_captureLayer release];
- m_captureLayer = nullptr;
- }
-}
-
-void AVFCameraWindowControl::attachNativeLayer()
-{
- if (m_captureLayer && m_nativeView) {
-#if defined(Q_OS_MACOS)
- m_nativeView.wantsLayer = YES;
-#endif
- CALayer *nativeLayer = m_nativeView.layer;
- [nativeLayer addSublayer:m_captureLayer];
- updateCaptureLayerBounds();
- }
-}
-
-void AVFCameraWindowControl::detachNativeLayer()
-{
- if (m_captureLayer && m_nativeView)
- [m_captureLayer removeFromSuperlayer];
-}
-
-#include "moc_avfcamerawindowcontrol.cpp"
diff --git a/src/plugins/avfoundation/camera/avfcamerazoomcontrol.h b/src/plugins/avfoundation/camera/avfcamerazoomcontrol.h
deleted file mode 100644
index 2307dfc28..000000000
--- a/src/plugins/avfoundation/camera/avfcamerazoomcontrol.h
+++ /dev/null
@@ -1,85 +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 AVFCAMERAZOOMCONTROL_H
-#define AVFCAMERAZOOMCONTROL_H
-
-#include <QtMultimedia/qcamerazoomcontrol.h>
-#include <QtMultimedia/qcamera.h>
-
-#include <AVFoundation/AVFoundation.h>
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraService;
-class AVFCameraSession;
-class AVFCameraControl;
-
-class AVFCameraZoomControl : public QCameraZoomControl
-{
- Q_OBJECT
-public:
- AVFCameraZoomControl(AVFCameraService *service);
-
- qreal maximumOpticalZoom() const override;
- qreal maximumDigitalZoom() const override;
-
- qreal requestedOpticalZoom() const override;
- qreal requestedDigitalZoom() const override;
- qreal currentOpticalZoom() const override;
- qreal currentDigitalZoom() const override;
-
- void zoomTo(qreal optical, qreal digital) override;
-
-private Q_SLOTS:
- void cameraStateChanged();
-
-private:
- void zoomToRequestedDigital();
-
- AVFCameraSession *m_session;
-
- CGFloat m_maxZoomFactor;
- CGFloat m_zoomFactor;
- CGFloat m_requestedZoomFactor;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/avfoundation/camera/avfcamerazoomcontrol.mm b/src/plugins/avfoundation/camera/avfcamerazoomcontrol.mm
deleted file mode 100644
index 119a1fc0a..000000000
--- a/src/plugins/avfoundation/camera/avfcamerazoomcontrol.mm
+++ /dev/null
@@ -1,179 +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 "avfcamerazoomcontrol.h"
-#include "avfcameraservice.h"
-#include "avfcamerautility.h"
-#include "avfcamerasession.h"
-#include "avfcameracontrol.h"
-#include "avfcameradebug.h"
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qdebug.h>
-
-QT_BEGIN_NAMESPACE
-
-AVFCameraZoomControl::AVFCameraZoomControl(AVFCameraService *service)
- : m_session(service->session()),
- m_maxZoomFactor(1.),
- m_zoomFactor(1.),
- m_requestedZoomFactor(1.)
-{
- Q_ASSERT(m_session);
- connect(m_session, SIGNAL(stateChanged(QCamera::State)),
- SLOT(cameraStateChanged()));
-}
-
-qreal AVFCameraZoomControl::maximumOpticalZoom() const
-{
- // Not supported.
- return 1.;
-}
-
-qreal AVFCameraZoomControl::maximumDigitalZoom() const
-{
- return m_maxZoomFactor;
-}
-
-qreal AVFCameraZoomControl::requestedOpticalZoom() const
-{
- // Not supported.
- return 1;
-}
-
-qreal AVFCameraZoomControl::requestedDigitalZoom() const
-{
- return m_requestedZoomFactor;
-}
-
-qreal AVFCameraZoomControl::currentOpticalZoom() const
-{
- // Not supported.
- return 1.;
-}
-
-qreal AVFCameraZoomControl::currentDigitalZoom() const
-{
- return m_zoomFactor;
-}
-
-void AVFCameraZoomControl::zoomTo(qreal optical, qreal digital)
-{
- Q_UNUSED(optical);
- Q_UNUSED(digital);
-
- if (qFuzzyCompare(CGFloat(digital), m_requestedZoomFactor))
- return;
-
- m_requestedZoomFactor = digital;
- Q_EMIT requestedDigitalZoomChanged(digital);
-
- zoomToRequestedDigital();
-}
-
-void AVFCameraZoomControl::cameraStateChanged()
-{
- const QCamera::State state = m_session->state();
- if (state != QCamera::ActiveState) {
- if (state == QCamera::UnloadedState && m_maxZoomFactor > 1.) {
- m_maxZoomFactor = 1.;
- Q_EMIT maximumDigitalZoomChanged(1.);
- }
- return;
- }
-
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice || !captureDevice.activeFormat) {
- qDebugCamera() << Q_FUNC_INFO << "camera state is active, but"
- << "video capture device and/or active format is nil";
- return;
- }
-
- if (captureDevice.activeFormat.videoMaxZoomFactor > 1.) {
- if (!qFuzzyCompare(m_maxZoomFactor, captureDevice.activeFormat.videoMaxZoomFactor)) {
- m_maxZoomFactor = captureDevice.activeFormat.videoMaxZoomFactor;
- Q_EMIT maximumDigitalZoomChanged(m_maxZoomFactor);
- }
- } else if (!qFuzzyCompare(m_maxZoomFactor, CGFloat(1.))) {
- m_maxZoomFactor = 1.;
-
- Q_EMIT maximumDigitalZoomChanged(1.);
- }
-
- zoomToRequestedDigital();
-}
-
-void AVFCameraZoomControl::zoomToRequestedDigital()
-{
- AVCaptureDevice *captureDevice = m_session->videoCaptureDevice();
- if (!captureDevice || !captureDevice.activeFormat)
- return;
-
- if (qFuzzyCompare(captureDevice.activeFormat.videoMaxZoomFactor, CGFloat(1.)))
- return;
-
- const CGFloat clampedZoom = qBound(CGFloat(1.), m_requestedZoomFactor,
- captureDevice.activeFormat.videoMaxZoomFactor);
- const CGFloat deviceZoom = captureDevice.videoZoomFactor;
- if (qFuzzyCompare(clampedZoom, deviceZoom)) {
- // Nothing to set, but check if a signal must be emitted:
- if (!qFuzzyCompare(m_zoomFactor, deviceZoom)) {
- m_zoomFactor = deviceZoom;
- Q_EMIT currentDigitalZoomChanged(deviceZoom);
- }
- return;
- }
-
- const AVFConfigurationLock lock(captureDevice);
- if (!lock) {
- qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
- return;
- }
-
- captureDevice.videoZoomFactor = clampedZoom;
-
- if (!qFuzzyCompare(clampedZoom, m_zoomFactor)) {
- m_zoomFactor = clampedZoom;
- Q_EMIT currentDigitalZoomChanged(clampedZoom);
- }
-}
-
-QT_END_NAMESPACE
-
-#include "moc_avfcamerazoomcontrol.cpp"
diff --git a/src/plugins/avfoundation/camera/avfcapturedestinationcontrol.h b/src/plugins/avfoundation/camera/avfcapturedestinationcontrol.h
deleted file mode 100644
index 04493437e..000000000
--- a/src/plugins/avfoundation/camera/avfcapturedestinationcontrol.h
+++ /dev/null
@@ -1,63 +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 AVFCAPTUREDESTINATIONCONTROL_H
-#define AVFCAPTUREDESTINATIONCONTROL_H
-
-#include <qcameracapturedestinationcontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class AVFCaptureDestinationControl : public QCameraCaptureDestinationControl
-{
-public:
- AVFCaptureDestinationControl() = default;
- ~AVFCaptureDestinationControl() = default;
-
- bool isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const override;
- QCameraImageCapture::CaptureDestinations captureDestination() const override;
- void setCaptureDestination(QCameraImageCapture::CaptureDestinations destination) override;
-
-private:
- QCameraImageCapture::CaptureDestinations m_destination = QCameraImageCapture::CaptureToFile;
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFCAPTUREDESTINATIONCONTROL_H
diff --git a/src/plugins/avfoundation/camera/avfcapturedestinationcontrol.mm b/src/plugins/avfoundation/camera/avfcapturedestinationcontrol.mm
deleted file mode 100644
index d0700d69d..000000000
--- a/src/plugins/avfoundation/camera/avfcapturedestinationcontrol.mm
+++ /dev/null
@@ -1,62 +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 "avfcapturedestinationcontrol.h"
-
-QT_BEGIN_NAMESPACE
-
-bool AVFCaptureDestinationControl::isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const
-{
- return destination & (QCameraImageCapture::CaptureToFile | QCameraImageCapture::CaptureToBuffer);
-}
-
-QCameraImageCapture::CaptureDestinations AVFCaptureDestinationControl::captureDestination() const
-{
- return m_destination;
-}
-
-void AVFCaptureDestinationControl::setCaptureDestination(QCameraImageCapture::CaptureDestinations destination)
-{
- if (m_destination != destination) {
- m_destination = destination;
- Q_EMIT captureDestinationChanged(m_destination);
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/avfoundation/camera/avfimagecapturecontrol.h b/src/plugins/avfoundation/camera/avfimagecapturecontrol.h
deleted file mode 100644
index 314e867e7..000000000
--- a/src/plugins/avfoundation/camera/avfimagecapturecontrol.h
+++ /dev/null
@@ -1,100 +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 AVFIMAGECAPTURECONTROL_H
-#define AVFIMAGECAPTURECONTROL_H
-
-#import <AVFoundation/AVFoundation.h>
-
-#include <QtCore/qqueue.h>
-#include <QtCore/qsemaphore.h>
-#include <QtCore/qsharedpointer.h>
-#include <QtMultimedia/qcameraimagecapturecontrol.h>
-#include "avfcamerasession.h"
-#include "avfstoragelocation.h"
-
-QT_BEGIN_NAMESPACE
-
-class AVFImageCaptureControl : public QCameraImageCaptureControl
-{
-Q_OBJECT
-public:
- struct CaptureRequest {
- int captureId;
- QSharedPointer<QSemaphore> previewReady;
- };
-
- AVFImageCaptureControl(AVFCameraService *service, QObject *parent = nullptr);
- ~AVFImageCaptureControl();
-
- bool isReadyForCapture() const override;
-
- QCameraImageCapture::DriveMode driveMode() const override { return QCameraImageCapture::SingleImageCapture; }
- void setDriveMode(QCameraImageCapture::DriveMode ) override {}
- AVCaptureStillImageOutput *stillImageOutput() const {return m_stillImageOutput;}
-
- int capture(const QString &fileName) override;
- void cancelCapture() override;
-
-private Q_SLOTS:
- void updateCaptureConnection();
- void updateReadyStatus();
- void onNewViewfinderFrame(const QVideoFrame &frame);
-
-private:
- void makeCapturePreview(CaptureRequest request, const QVideoFrame &frame, int rotation);
-
- AVFCameraService *m_service;
- AVFCameraSession *m_session;
- AVFCameraControl *m_cameraControl;
- bool m_ready;
- int m_lastCaptureId;
- AVCaptureStillImageOutput *m_stillImageOutput;
- AVCaptureConnection *m_videoConnection;
- AVFStorageLocation m_storageLocation;
-
- QMutex m_requestsMutex;
- QQueue<CaptureRequest> m_captureRequests;
-};
-
-Q_DECLARE_TYPEINFO(AVFImageCaptureControl::CaptureRequest, Q_PRIMITIVE_TYPE);
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/avfoundation/camera/avfimagecapturecontrol.mm b/src/plugins/avfoundation/camera/avfimagecapturecontrol.mm
deleted file mode 100644
index f2c019fdc..000000000
--- a/src/plugins/avfoundation/camera/avfimagecapturecontrol.mm
+++ /dev/null
@@ -1,266 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfcameradebug.h"
-#include "avfimagecapturecontrol.h"
-#include "avfcameraservice.h"
-#include "avfcamerautility.h"
-#include "avfcameracontrol.h"
-#include "avfcapturedestinationcontrol.h"
-#include <private/qmemoryvideobuffer_p.h>
-
-#include <QtCore/qurl.h>
-#include <QtCore/qfile.h>
-#include <QtCore/qbuffer.h>
-#include <QtConcurrent/qtconcurrentrun.h>
-#include <QtGui/qimagereader.h>
-
-QT_USE_NAMESPACE
-
-AVFImageCaptureControl::AVFImageCaptureControl(AVFCameraService *service, QObject *parent)
- : QCameraImageCaptureControl(parent)
- , m_service(service)
- , m_session(service->session())
- , m_cameraControl(service->cameraControl())
- , m_ready(false)
- , m_lastCaptureId(0)
- , m_videoConnection(nil)
-{
- Q_UNUSED(service);
- m_stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
-
- NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:
- AVVideoCodecJPEG, AVVideoCodecKey, nil];
-
- [m_stillImageOutput setOutputSettings:outputSettings];
- [outputSettings release];
- connect(m_cameraControl, SIGNAL(captureModeChanged(QCamera::CaptureModes)), SLOT(updateReadyStatus()));
- connect(m_cameraControl, SIGNAL(statusChanged(QCamera::Status)), SLOT(updateReadyStatus()));
-
- connect(m_session, SIGNAL(readyToConfigureConnections()), SLOT(updateCaptureConnection()));
- connect(m_cameraControl, SIGNAL(captureModeChanged(QCamera::CaptureModes)), SLOT(updateCaptureConnection()));
-
- connect(m_session, &AVFCameraSession::newViewfinderFrame,
- this, &AVFImageCaptureControl::onNewViewfinderFrame,
- Qt::DirectConnection);
-}
-
-AVFImageCaptureControl::~AVFImageCaptureControl()
-{
-}
-
-bool AVFImageCaptureControl::isReadyForCapture() const
-{
- return m_videoConnection &&
- m_cameraControl->captureMode().testFlag(QCamera::CaptureStillImage) &&
- m_cameraControl->status() == QCamera::ActiveStatus;
-}
-
-void AVFImageCaptureControl::updateReadyStatus()
-{
- if (m_ready != isReadyForCapture()) {
- m_ready = !m_ready;
- qDebugCamera() << "ReadyToCapture status changed:" << m_ready;
- Q_EMIT readyForCaptureChanged(m_ready);
- }
-}
-
-int AVFImageCaptureControl::capture(const QString &fileName)
-{
- m_lastCaptureId++;
-
- if (!isReadyForCapture()) {
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
- Q_ARG(int, m_lastCaptureId),
- Q_ARG(int, QCameraImageCapture::NotReadyError),
- Q_ARG(QString, tr("Camera not ready")));
- return m_lastCaptureId;
- }
-
- auto destination = m_service->captureDestinationControl()->captureDestination();
- QString actualFileName;
- if (destination & QCameraImageCapture::CaptureToFile) {
- actualFileName = m_storageLocation.generateFileName(fileName,
- QCamera::CaptureStillImage,
- QLatin1String("img_"),
- QLatin1String("jpg"));
-
- qDebugCamera() << "Capture image to" << actualFileName;
- }
-
- CaptureRequest request = { m_lastCaptureId, QSharedPointer<QSemaphore>::create()};
- m_requestsMutex.lock();
- m_captureRequests.enqueue(request);
- m_requestsMutex.unlock();
-
- [m_stillImageOutput captureStillImageAsynchronouslyFromConnection:m_videoConnection
- completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
-
- if (error) {
- QStringList messageParts;
- messageParts << QString::fromUtf8([[error localizedDescription] UTF8String]);
- messageParts << QString::fromUtf8([[error localizedFailureReason] UTF8String]);
- messageParts << QString::fromUtf8([[error localizedRecoverySuggestion] UTF8String]);
-
- QString errorMessage = messageParts.join(" ");
- qDebugCamera() << "Image capture failed:" << errorMessage;
-
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
- Q_ARG(int, request.captureId),
- Q_ARG(int, QCameraImageCapture::ResourceError),
- Q_ARG(QString, errorMessage));
- return;
- }
-
- // Wait for the preview to be generated before saving the JPEG (but only
- // if we have AVFCameraRendererControl attached).
- // It is possible to stop camera immediately after trying to capture an
- // image; this can result in a blocked callback's thread, waiting for a
- // new viewfinder's frame to arrive/semaphore to be released. It is also
- // unspecified on which thread this callback gets executed, (probably it's
- // not the same thread that initiated a capture and stopped the camera),
- // so we cannot reliably check the camera's status. Instead, we wait
- // with a timeout and treat a failure to acquire a semaphore as an error.
- if (!m_service->videoOutput() || request.previewReady->tryAcquire(1, 1000)) {
- qDebugCamera() << "Image capture completed";
-
- NSData *nsJpgData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
- QByteArray jpgData = QByteArray::fromRawData((const char *)[nsJpgData bytes], [nsJpgData length]);
-
- if (destination & QCameraImageCapture::CaptureToBuffer) {
- QBuffer data(&jpgData);
- QImageReader reader(&data, "JPEG");
- QSize size = reader.size();
- QVideoFrame frame(new QMemoryVideoBuffer(QByteArray(jpgData.constData(), jpgData.size()), -1), size, QVideoFrame::Format_Jpeg);
- QMetaObject::invokeMethod(this, "imageAvailable", Qt::QueuedConnection,
- Q_ARG(int, request.captureId),
- Q_ARG(QVideoFrame, frame));
- }
-
- if (!(destination & QCameraImageCapture::CaptureToFile))
- return;
-
- QFile f(actualFileName);
- if (f.open(QFile::WriteOnly)) {
- if (f.write(jpgData) != -1) {
- QMetaObject::invokeMethod(this, "imageSaved", Qt::QueuedConnection,
- Q_ARG(int, request.captureId),
- Q_ARG(QString, actualFileName));
- } else {
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
- Q_ARG(int, request.captureId),
- Q_ARG(int, QCameraImageCapture::OutOfSpaceError),
- Q_ARG(QString, f.errorString()));
- }
- } else {
- QString errorMessage = tr("Could not open destination file:\n%1").arg(actualFileName);
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
- Q_ARG(int, request.captureId),
- Q_ARG(int, QCameraImageCapture::ResourceError),
- Q_ARG(QString, errorMessage));
- }
- } else {
- const QLatin1String errorMessage("Image capture failed: timed out waiting"
- " for a preview frame.");
- qDebugCamera() << errorMessage;
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
- Q_ARG(int, request.captureId),
- Q_ARG(int, QCameraImageCapture::ResourceError),
- Q_ARG(QString, errorMessage));
- }
- }];
-
- return request.captureId;
-}
-
-void AVFImageCaptureControl::onNewViewfinderFrame(const QVideoFrame &frame)
-{
- QMutexLocker locker(&m_requestsMutex);
-
- if (m_captureRequests.isEmpty())
- return;
-
- CaptureRequest request = m_captureRequests.dequeue();
- Q_EMIT imageExposed(request.captureId);
-
- QtConcurrent::run(&AVFImageCaptureControl::makeCapturePreview, this,
- request,
- frame,
- 0 /* rotation */);
-}
-
-void AVFImageCaptureControl::makeCapturePreview(CaptureRequest request,
- const QVideoFrame &frame,
- int rotation)
-{
- QTransform transform;
- transform.rotate(rotation);
-
- Q_EMIT imageCaptured(request.captureId, frame.image().transformed(transform));
-
- request.previewReady->release();
-}
-
-void AVFImageCaptureControl::cancelCapture()
-{
- //not supported
-}
-
-void AVFImageCaptureControl::updateCaptureConnection()
-{
- if (m_session->videoCaptureDevice()
- && m_cameraControl->captureMode().testFlag(QCamera::CaptureStillImage)) {
- qDebugCamera() << Q_FUNC_INFO;
- AVCaptureSession *captureSession = m_session->captureSession();
-
- if (![captureSession.outputs containsObject:m_stillImageOutput]) {
- if ([captureSession canAddOutput:m_stillImageOutput]) {
- // Lock the video capture device to make sure the active format is not reset
- const AVFConfigurationLock lock(m_session->videoCaptureDevice());
- [captureSession addOutput:m_stillImageOutput];
- m_videoConnection = [m_stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
- updateReadyStatus();
- }
- } else {
- m_videoConnection = [m_stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
- }
- }
-}
-
-#include "moc_avfimagecapturecontrol.cpp"
diff --git a/src/plugins/avfoundation/camera/avfimageencodercontrol.h b/src/plugins/avfoundation/camera/avfimageencodercontrol.h
deleted file mode 100644
index 7e2e34294..000000000
--- a/src/plugins/avfoundation/camera/avfimageencodercontrol.h
+++ /dev/null
@@ -1,85 +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 AVFIMAGEENCODERCONTROL_H
-#define AVFIMAGEENCODERCONTROL_H
-
-#include <QtMultimedia/qmediaencodersettings.h>
-#include <QtMultimedia/qimageencodercontrol.h>
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qlist.h>
-
-@class AVCaptureDeviceFormat;
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraService;
-
-class AVFImageEncoderControl : public QImageEncoderControl
-{
- Q_OBJECT
-
- friend class AVFCameraSession;
-public:
- AVFImageEncoderControl(AVFCameraService *service);
-
- QStringList supportedImageCodecs() const override;
- QString imageCodecDescription(const QString &codecName) const override;
- QList<QSize> supportedResolutions(const QImageEncoderSettings &settings,
- bool *continuous) const override;
- QImageEncoderSettings imageSettings() const override;
- void setImageSettings(const QImageEncoderSettings &settings) override;
-
- QImageEncoderSettings requestedSettings() const;
-
-private:
- AVFCameraService *m_service;
- QImageEncoderSettings m_settings;
-
- bool applySettings();
- bool videoCaptureDeviceIsValid() const;
-};
-
-QSize qt_image_high_resolution(AVCaptureDeviceFormat *fomat);
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/avfoundation/camera/avfimageencodercontrol.mm b/src/plugins/avfoundation/camera/avfimageencodercontrol.mm
deleted file mode 100644
index 93de3e63c..000000000
--- a/src/plugins/avfoundation/camera/avfimageencodercontrol.mm
+++ /dev/null
@@ -1,240 +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 "avfcameraviewfindersettingscontrol.h"
-#include "avfimageencodercontrol.h"
-#include "avfimagecapturecontrol.h"
-#include "avfcamerautility.h"
-#include "avfcamerasession.h"
-#include "avfcameraservice.h"
-#include "avfcameradebug.h"
-#include "avfcameracontrol.h"
-
-#include <QtMultimedia/qmediaencodersettings.h>
-
-#include <QtCore/qdebug.h>
-
-#include <AVFoundation/AVFoundation.h>
-
-QT_BEGIN_NAMESPACE
-
-AVFImageEncoderControl::AVFImageEncoderControl(AVFCameraService *service)
- : m_service(service)
-{
- Q_ASSERT(service);
-}
-
-QStringList AVFImageEncoderControl::supportedImageCodecs() const
-{
- return QStringList() << QLatin1String("jpeg");
-}
-
-QString AVFImageEncoderControl::imageCodecDescription(const QString &codecName) const
-{
- if (codecName == QLatin1String("jpeg"))
- return tr("JPEG image");
-
- return QString();
-}
-
-QList<QSize> AVFImageEncoderControl::supportedResolutions(const QImageEncoderSettings &settings,
- bool *continuous) const
-{
- Q_UNUSED(settings);
-
- QList<QSize> resolutions;
-
- if (!videoCaptureDeviceIsValid())
- return resolutions;
-
- AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
- const QVector<AVCaptureDeviceFormat *> formats(qt_unique_device_formats(captureDevice,
- m_service->session()->defaultCodec()));
-
- for (int i = 0; i < formats.size(); ++i) {
- AVCaptureDeviceFormat *format = formats[i];
-
- const QSize res(qt_device_format_resolution(format));
- if (!res.isNull() && res.isValid())
- resolutions << res;
-#ifdef Q_OS_IOS
- // From Apple's docs (iOS):
- // By default, AVCaptureStillImageOutput emits images with the same dimensions as
- // its source AVCaptureDevice instance’s activeFormat.formatDescription. However,
- // if you set this property to YES, the receiver emits still images at the capture
- // device’s highResolutionStillImageDimensions value.
- const QSize hrRes(qt_device_format_high_resolution(format));
- if (!hrRes.isNull() && hrRes.isValid())
- resolutions << res;
-#endif
- }
-
- if (continuous)
- *continuous = false;
-
- return resolutions;
-}
-
-QImageEncoderSettings AVFImageEncoderControl::requestedSettings() const
-{
- return m_settings;
-}
-
-QImageEncoderSettings AVFImageEncoderControl::imageSettings() const
-{
- QImageEncoderSettings settings;
-
- if (!videoCaptureDeviceIsValid())
- return settings;
-
- AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
- if (!captureDevice.activeFormat) {
- qDebugCamera() << Q_FUNC_INFO << "no active format";
- return settings;
- }
-
- QSize res(qt_device_format_resolution(captureDevice.activeFormat));
-#ifdef Q_OS_IOS
- if (!m_service->imageCaptureControl() || !m_service->imageCaptureControl()->stillImageOutput()) {
- qDebugCamera() << Q_FUNC_INFO << "no still image output";
- return settings;
- }
-
- AVCaptureStillImageOutput *stillImageOutput = m_service->imageCaptureControl()->stillImageOutput();
- if (stillImageOutput.highResolutionStillImageOutputEnabled)
- res = qt_device_format_high_resolution(captureDevice.activeFormat);
-#endif
- if (res.isNull() || !res.isValid()) {
- qDebugCamera() << Q_FUNC_INFO << "failed to exctract the image resolution";
- return settings;
- }
-
- settings.setResolution(res);
-
- settings.setCodec(QLatin1String("jpeg"));
-
- return settings;
-}
-
-void AVFImageEncoderControl::setImageSettings(const QImageEncoderSettings &settings)
-{
- if (m_settings == settings)
- return;
-
- m_settings = settings;
- applySettings();
-}
-
-bool AVFImageEncoderControl::applySettings()
-{
- if (!videoCaptureDeviceIsValid())
- return false;
-
- AVFCameraSession *session = m_service->session();
- if (!session || (session->state() != QCamera::ActiveState
- && session->state() != QCamera::LoadedState)
- || !m_service->cameraControl()->captureMode().testFlag(QCamera::CaptureStillImage)) {
- return false;
- }
-
- if (!m_service->imageCaptureControl()
- || !m_service->imageCaptureControl()->stillImageOutput()) {
- qDebugCamera() << Q_FUNC_INFO << "no still image output";
- return false;
- }
-
- if (m_settings.codec().size()
- && m_settings.codec() != QLatin1String("jpeg")) {
- qDebugCamera() << Q_FUNC_INFO << "unsupported codec:" << m_settings.codec();
- return false;
- }
-
- QSize res(m_settings.resolution());
- if (res.isNull()) {
- qDebugCamera() << Q_FUNC_INFO << "invalid resolution:" << res;
- return false;
- }
-
- if (!res.isValid()) {
- // Invalid == default value.
- // Here we could choose the best format available, but
- // activeFormat is already equal to 'preset high' by default,
- // which is good enough, otherwise we can end in some format with low framerates.
- return false;
- }
-
- bool activeFormatChanged = false;
-
- AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
- AVCaptureDeviceFormat *match = qt_find_best_resolution_match(captureDevice, res,
- m_service->session()->defaultCodec());
-
- if (!match) {
- qDebugCamera() << Q_FUNC_INFO << "unsupported resolution:" << res;
- return false;
- }
-
- activeFormatChanged = qt_set_active_format(captureDevice, match, true);
-
-#ifdef Q_OS_IOS
- AVCaptureStillImageOutput *imageOutput = m_service->imageCaptureControl()->stillImageOutput();
- if (res == qt_device_format_high_resolution(captureDevice.activeFormat))
- imageOutput.highResolutionStillImageOutputEnabled = YES;
- else
- imageOutput.highResolutionStillImageOutputEnabled = NO;
-#endif
-
- return activeFormatChanged;
-}
-
-bool AVFImageEncoderControl::videoCaptureDeviceIsValid() const
-{
- if (!m_service->session() || !m_service->session()->videoCaptureDevice())
- return false;
-
- AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
- if (!captureDevice.formats || !captureDevice.formats.count)
- return false;
-
- return true;
-}
-
-QT_END_NAMESPACE
-
-#include "moc_avfimageencodercontrol.cpp"
diff --git a/src/plugins/avfoundation/camera/avfmediaassetwriter.h b/src/plugins/avfoundation/camera/avfmediaassetwriter.h
deleted file mode 100644
index f063dab4b..000000000
--- a/src/plugins/avfoundation/camera/avfmediaassetwriter.h
+++ /dev/null
@@ -1,75 +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 AVFMEDIAASSETWRITER_H
-#define AVFMEDIAASSETWRITER_H
-
-#include "avfcamerautility.h"
-
-#include <QtCore/qglobal.h>
-
-#include <AVFoundation/AVFoundation.h>
-
-QT_BEGIN_NAMESPACE
-
-class AVFMediaRecorderControlIOS;
-class AVFCameraService;
-
-QT_END_NAMESPACE
-
-@interface QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) : NSObject<AVCaptureVideoDataOutputSampleBufferDelegate,
- AVCaptureAudioDataOutputSampleBufferDelegate>
-- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(AVFMediaRecorderControlIOS) *)delegate;
-
-- (bool)setupWithFileURL:(NSURL *)fileURL
- cameraService:(QT_PREPEND_NAMESPACE(AVFCameraService) *)service
- audioSettings:(NSDictionary *)audioSettings
- videoSettings:(NSDictionary *)videoSettings
- transform:(CGAffineTransform)transform;
-
-// This to be called from the recorder control's thread:
-- (void)start;
-- (void)stop;
-// This to be called from the recorder control's dtor:
-- (void)abort;
-- (qint64)durationInMs;
-
-@end
-
-#endif // AVFMEDIAASSETWRITER_H
diff --git a/src/plugins/avfoundation/camera/avfmediaassetwriter.mm b/src/plugins/avfoundation/camera/avfmediaassetwriter.mm
deleted file mode 100644
index 57c5cb8c5..000000000
--- a/src/plugins/avfoundation/camera/avfmediaassetwriter.mm
+++ /dev/null
@@ -1,514 +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 "avfaudioinputselectorcontrol.h"
-#include "avfmediarecordercontrol_ios.h"
-#include "avfcamerarenderercontrol.h"
-#include "avfmediaassetwriter.h"
-#include "avfcameraservice.h"
-#include "avfcamerasession.h"
-#include "avfcameradebug.h"
-#include "avfmediacontainercontrol.h"
-
-#include <QtCore/qmetaobject.h>
-#include <QtCore/qatomic.h>
-
-QT_USE_NAMESPACE
-
-namespace {
-
-bool qt_camera_service_isValid(AVFCameraService *service)
-{
- if (!service || !service->session())
- return false;
-
- AVFCameraSession *session = service->session();
- if (!session->captureSession())
- return false;
-
- if (!session->videoInput())
- return false;
-
- if (!service->videoOutput()
- || !service->videoOutput()->videoDataOutput()) {
- return false;
- }
-
- return true;
-}
-
-enum WriterState
-{
- WriterStateIdle,
- WriterStateActive,
- WriterStateAborted
-};
-
-using AVFAtomicInt64 = QAtomicInteger<qint64>;
-
-} // unnamed namespace
-
-@interface QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) (PrivateAPI)
-- (bool)addAudioCapture;
-- (bool)addWriterInputs;
-- (void)setQueues;
-- (void)updateDuration:(CMTime)newTimeStamp;
-@end
-
-@implementation QT_MANGLE_NAMESPACE(AVFMediaAssetWriter)
-{
-@private
- AVFCameraService *m_service;
-
- AVFScopedPointer<AVAssetWriterInput> m_cameraWriterInput;
- AVFScopedPointer<AVCaptureDeviceInput> m_audioInput;
- AVFScopedPointer<AVCaptureAudioDataOutput> m_audioOutput;
- AVFScopedPointer<AVAssetWriterInput> m_audioWriterInput;
-
- AVCaptureDevice *m_audioCaptureDevice;
-
- // Queue to write sample buffers:
- AVFScopedPointer<dispatch_queue_t> m_writerQueue;
- // High priority serial queue for video output:
- AVFScopedPointer<dispatch_queue_t> m_videoQueue;
- // Serial queue for audio output:
- AVFScopedPointer<dispatch_queue_t> m_audioQueue;
-
- AVFScopedPointer<AVAssetWriter> m_assetWriter;
-
- AVFMediaRecorderControlIOS *m_delegate;
-
- bool m_setStartTime;
-
- QAtomicInt m_state;
-
- CMTime m_startTime;
- CMTime m_lastTimeStamp;
-
- NSDictionary *m_audioSettings;
- NSDictionary *m_videoSettings;
-
- AVFAtomicInt64 m_durationInMs;
-}
-
-- (id)initWithDelegate:(AVFMediaRecorderControlIOS *)delegate
-{
- Q_ASSERT(delegate);
-
- if (self = [super init]) {
- m_delegate = delegate;
- m_setStartTime = true;
- m_state.storeRelaxed(WriterStateIdle);
- m_startTime = kCMTimeInvalid;
- m_lastTimeStamp = kCMTimeInvalid;
- m_durationInMs.storeRelaxed(0);
- m_audioSettings = nil;
- m_videoSettings = nil;
- }
-
- return self;
-}
-
-- (bool)setupWithFileURL:(NSURL *)fileURL
- cameraService:(AVFCameraService *)service
- audioSettings:(NSDictionary *)audioSettings
- videoSettings:(NSDictionary *)videoSettings
- transform:(CGAffineTransform)transform
-{
- Q_ASSERT(fileURL);
-
- if (!qt_camera_service_isValid(service)) {
- qDebugCamera() << Q_FUNC_INFO << "invalid camera service";
- return false;
- }
-
- m_service = service;
- m_audioSettings = audioSettings;
- m_videoSettings = videoSettings;
-
- m_writerQueue.reset(dispatch_queue_create("asset-writer-queue", DISPATCH_QUEUE_SERIAL));
- if (!m_writerQueue) {
- qDebugCamera() << Q_FUNC_INFO << "failed to create an asset writer's queue";
- return false;
- }
-
- m_videoQueue.reset(dispatch_queue_create("video-output-queue", DISPATCH_QUEUE_SERIAL));
- if (!m_videoQueue) {
- qDebugCamera() << Q_FUNC_INFO << "failed to create video queue";
- return false;
- }
- dispatch_set_target_queue(m_videoQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
- m_audioQueue.reset(dispatch_queue_create("audio-output-queue", DISPATCH_QUEUE_SERIAL));
- if (!m_audioQueue) {
- qDebugCamera() << Q_FUNC_INFO << "failed to create audio queue";
- // But we still can write video!
- }
-
- m_assetWriter.reset([[AVAssetWriter alloc] initWithURL:fileURL
- fileType:m_service->mediaContainerControl()->fileType()
- error:nil]);
- if (!m_assetWriter) {
- qDebugCamera() << Q_FUNC_INFO << "failed to create asset writer";
- return false;
- }
-
- bool audioCaptureOn = false;
-
- if (m_audioQueue)
- audioCaptureOn = [self addAudioCapture];
-
- if (![self addWriterInputs]) {
- if (audioCaptureOn) {
- AVCaptureSession *session = m_service->session()->captureSession();
- [session removeOutput:m_audioOutput];
- [session removeInput:m_audioInput];
- m_audioOutput.reset();
- m_audioInput.reset();
- m_audioCaptureDevice = 0;
- }
- m_assetWriter.reset();
- return false;
- }
-
- m_cameraWriterInput.data().transform = transform;
-
- // Ready to start ...
- return true;
-}
-
-- (void)start
-{
- [self setQueues];
-
- m_setStartTime = true;
-
- m_state.storeRelease(WriterStateActive);
-
- [m_assetWriter startWriting];
- AVCaptureSession *session = m_service->session()->captureSession();
- if (!session.running)
- [session startRunning];
-}
-
-- (void)stop
-{
- if (m_state.loadAcquire() != WriterStateActive)
- return;
-
- if ([m_assetWriter status] != AVAssetWriterStatusWriting)
- return;
-
- // Do this here so that -
- // 1. '-abort' should not try calling finishWriting again and
- // 2. async block (see below) will know if recorder control was deleted
- // before the block's execution:
- m_state.storeRelease(WriterStateIdle);
- // Now, since we have to ensure no sample buffers are
- // appended after a call to finishWriting, we must
- // ensure writer's queue sees this change in m_state
- // _before_ we call finishWriting:
- dispatch_sync(m_writerQueue, ^{});
- // Done, but now we also want to prevent video queue
- // from updating our viewfinder:
- dispatch_sync(m_videoQueue, ^{});
-
- // Now we're safe to stop:
- [m_assetWriter finishWritingWithCompletionHandler:^{
- // This block is async, so by the time it's executed,
- // it's possible that render control was deleted already ...
- if (m_state.loadAcquire() == WriterStateAborted)
- return;
-
- AVCaptureSession *session = m_service->session()->captureSession();
- if (session.running)
- [session stopRunning];
- [session removeOutput:m_audioOutput];
- [session removeInput:m_audioInput];
- QMetaObject::invokeMethod(m_delegate, "assetWriterFinished", Qt::QueuedConnection);
- }];
-}
-
-- (void)abort
-{
- // -abort is to be called from recorder control's dtor.
-
- if (m_state.fetchAndStoreRelease(WriterStateAborted) != WriterStateActive) {
- // Not recording, nothing to stop.
- return;
- }
-
- // From Apple's docs:
- // "To guarantee that all sample buffers are successfully written,
- // you must ensure that all calls to appendSampleBuffer: and
- // appendPixelBuffer:withPresentationTime: have returned before
- // invoking this method."
- //
- // The only way we can ensure this is:
- dispatch_sync(m_writerQueue, ^{});
- // At this point next block (if any) on the writer's queue
- // will see m_state preventing it from any further processing.
- dispatch_sync(m_videoQueue, ^{});
- // After this point video queue will not try to modify our
- // viewfider, so we're safe to delete now.
-
- [m_assetWriter finishWritingWithCompletionHandler:^{
- }];
-}
-
-- (void)setStartTimeFrom:(CMSampleBufferRef)sampleBuffer
-{
- // Writer's queue only.
- Q_ASSERT(m_setStartTime);
- Q_ASSERT(sampleBuffer);
-
- if (m_state.loadAcquire() != WriterStateActive)
- return;
-
- QMetaObject::invokeMethod(m_delegate, "assetWriterStarted", Qt::QueuedConnection);
-
- m_durationInMs.storeRelease(0);
- m_startTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
- m_lastTimeStamp = m_startTime;
- [m_assetWriter startSessionAtSourceTime:m_startTime];
- m_setStartTime = false;
-}
-
-- (void)writeVideoSampleBuffer:(CMSampleBufferRef)sampleBuffer
-{
- // This code is executed only on a writer's queue.
- Q_ASSERT(sampleBuffer);
-
- if (m_state.loadAcquire() == WriterStateActive) {
- if (m_setStartTime)
- [self setStartTimeFrom:sampleBuffer];
-
- if (m_cameraWriterInput.data().readyForMoreMediaData) {
- [self updateDuration:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)];
- [m_cameraWriterInput appendSampleBuffer:sampleBuffer];
- }
- }
-
- CFRelease(sampleBuffer);
-}
-
-- (void)writeAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer
-{
- Q_ASSERT(sampleBuffer);
-
- // This code is executed only on a writer's queue.
- if (m_state.loadAcquire() == WriterStateActive) {
- if (m_setStartTime)
- [self setStartTimeFrom:sampleBuffer];
-
- if (m_audioWriterInput.data().readyForMoreMediaData) {
- [self updateDuration:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)];
- [m_audioWriterInput appendSampleBuffer:sampleBuffer];
- }
- }
-
- CFRelease(sampleBuffer);
-}
-
-- (void)captureOutput:(AVCaptureOutput *)captureOutput
- didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
- fromConnection:(AVCaptureConnection *)connection
-{
- Q_UNUSED(connection);
-
- if (m_state.loadAcquire() != WriterStateActive)
- return;
-
- if (!CMSampleBufferDataIsReady(sampleBuffer)) {
- qDebugCamera() << Q_FUNC_INFO << "sample buffer is not ready, skipping.";
- return;
- }
-
- CFRetain(sampleBuffer);
-
- if (captureOutput != m_audioOutput.data()) {
- if (m_state.loadRelaxed() != WriterStateActive) {
- CFRelease(sampleBuffer);
- return;
- }
- // Find renderercontrol's delegate and invoke its method to
- // show updated viewfinder's frame.
- if (m_service && m_service->videoOutput()) {
- NSObject<AVCaptureVideoDataOutputSampleBufferDelegate> *vfDelegate =
- (NSObject<AVCaptureVideoDataOutputSampleBufferDelegate> *)m_service->videoOutput()->captureDelegate();
- if (vfDelegate)
- [vfDelegate captureOutput:nil didOutputSampleBuffer:sampleBuffer fromConnection:nil];
- }
-
- dispatch_async(m_writerQueue, ^{
- [self writeVideoSampleBuffer:sampleBuffer];
- });
- } else {
- dispatch_async(m_writerQueue, ^{
- [self writeAudioSampleBuffer:sampleBuffer];
- });
- }
-}
-
-- (bool)addAudioCapture
-{
- Q_ASSERT(m_service && m_service->session() && m_service->session()->captureSession());
-
- if (!m_service->audioInputSelectorControl())
- return false;
-
- AVCaptureSession *captureSession = m_service->session()->captureSession();
-
- m_audioCaptureDevice = m_service->audioInputSelectorControl()->createCaptureDevice();
- if (!m_audioCaptureDevice) {
- qWarning() << Q_FUNC_INFO << "no audio input device available";
- return false;
- } else {
- NSError *error = nil;
- m_audioInput.reset([[AVCaptureDeviceInput deviceInputWithDevice:m_audioCaptureDevice error:&error] retain]);
-
- if (!m_audioInput || error) {
- qWarning() << Q_FUNC_INFO << "failed to create audio device input";
- m_audioCaptureDevice = 0;
- m_audioInput.reset();
- return false;
- } else if (![captureSession canAddInput:m_audioInput]) {
- qWarning() << Q_FUNC_INFO << "could not connect the audio input";
- m_audioCaptureDevice = 0;
- m_audioInput.reset();
- return false;
- } else {
- [captureSession addInput:m_audioInput];
- }
- }
-
-
- m_audioOutput.reset([[AVCaptureAudioDataOutput alloc] init]);
- if (m_audioOutput.data() && [captureSession canAddOutput:m_audioOutput]) {
- [captureSession addOutput:m_audioOutput];
- } else {
- qDebugCamera() << Q_FUNC_INFO << "failed to add audio output";
- [captureSession removeInput:m_audioInput];
- m_audioCaptureDevice = 0;
- m_audioInput.reset();
- m_audioOutput.reset();
- return false;
- }
-
- return true;
-}
-
-- (bool)addWriterInputs
-{
- Q_ASSERT(m_service && m_service->videoOutput()
- && m_service->videoOutput()->videoDataOutput());
- Q_ASSERT(m_assetWriter.data());
-
- m_cameraWriterInput.reset([[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo
- outputSettings:m_videoSettings
- sourceFormatHint:m_service->session()->videoCaptureDevice().activeFormat.formatDescription]);
- if (!m_cameraWriterInput) {
- qDebugCamera() << Q_FUNC_INFO << "failed to create camera writer input";
- return false;
- }
-
- if ([m_assetWriter canAddInput:m_cameraWriterInput]) {
- [m_assetWriter addInput:m_cameraWriterInput];
- } else {
- qDebugCamera() << Q_FUNC_INFO << "failed to add camera writer input";
- m_cameraWriterInput.reset();
- return false;
- }
-
- m_cameraWriterInput.data().expectsMediaDataInRealTime = YES;
-
- if (m_audioOutput.data()) {
- CMFormatDescriptionRef sourceFormat = m_audioCaptureDevice ? m_audioCaptureDevice.activeFormat.formatDescription : 0;
- m_audioWriterInput.reset([[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio
- outputSettings:m_audioSettings
- sourceFormatHint:sourceFormat]);
- if (!m_audioWriterInput) {
- qDebugCamera() << Q_FUNC_INFO << "failed to create audio writer input";
- // But we still can record video.
- } else if ([m_assetWriter canAddInput:m_audioWriterInput]) {
- [m_assetWriter addInput:m_audioWriterInput];
- m_audioWriterInput.data().expectsMediaDataInRealTime = YES;
- } else {
- qDebugCamera() << Q_FUNC_INFO << "failed to add audio writer input";
- m_audioWriterInput.reset();
- // We can (still) write video though ...
- }
- }
-
- return true;
-}
-
-- (void)setQueues
-{
- Q_ASSERT(m_service && m_service->videoOutput() && m_service->videoOutput()->videoDataOutput());
- Q_ASSERT(m_videoQueue);
-
- [m_service->videoOutput()->videoDataOutput() setSampleBufferDelegate:self queue:m_videoQueue];
-
- if (m_audioOutput.data()) {
- Q_ASSERT(m_audioQueue);
- [m_audioOutput setSampleBufferDelegate:self queue:m_audioQueue];
- }
-}
-
-- (void)updateDuration:(CMTime)newTimeStamp
-{
- Q_ASSERT(CMTimeCompare(m_startTime, kCMTimeInvalid));
- Q_ASSERT(CMTimeCompare(m_lastTimeStamp, kCMTimeInvalid));
- if (CMTimeCompare(newTimeStamp, m_lastTimeStamp) > 0) {
-
- const CMTime duration = CMTimeSubtract(newTimeStamp, m_startTime);
- if (!CMTimeCompare(duration, kCMTimeInvalid))
- return;
-
- m_durationInMs.storeRelease(CMTimeGetSeconds(duration) * 1000);
- m_lastTimeStamp = newTimeStamp;
- }
-}
-
-- (qint64)durationInMs
-{
- return m_durationInMs.loadAcquire();
-}
-
-@end
diff --git a/src/plugins/avfoundation/camera/avfmediacontainercontrol.h b/src/plugins/avfoundation/camera/avfmediacontainercontrol.h
deleted file mode 100644
index e43e70baf..000000000
--- a/src/plugins/avfoundation/camera/avfmediacontainercontrol.h
+++ /dev/null
@@ -1,69 +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 AVFMEDIACONTAINERCONTROL_H
-#define AVFMEDIACONTAINERCONTROL_H
-
-#include <qmediacontainercontrol.h>
-
-@class NSString;
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraService;
-
-class AVFMediaContainerControl : public QMediaContainerControl
-{
-public:
- explicit AVFMediaContainerControl(AVFCameraService *service);
-
- QStringList supportedContainers() const override;
- QString containerFormat() const override;
- void setContainerFormat(const QString &format) override;
- QString containerDescription(const QString &formatMimeType) const override;
-
- NSString *fileType() const;
-
-private:
- QString m_format;
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFMEDIACONTAINERCONTROL_H
diff --git a/src/plugins/avfoundation/camera/avfmediacontainercontrol.mm b/src/plugins/avfoundation/camera/avfmediacontainercontrol.mm
deleted file mode 100644
index 89fb50a19..000000000
--- a/src/plugins/avfoundation/camera/avfmediacontainercontrol.mm
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfmediacontainercontrol.h"
-
-#include <AVFoundation/AVMediaFormat.h>
-
-QT_BEGIN_NAMESPACE
-
-struct ContainerInfo
-{
- QString description;
- NSString *fileType;
-
- ContainerInfo() : fileType(nil) { }
- ContainerInfo(const QString &desc, NSString *type)
- : description(desc), fileType(type)
- { }
-};
-
-typedef QMap<QString, ContainerInfo> SupportedContainers;
-Q_GLOBAL_STATIC(SupportedContainers, containers);
-
-AVFMediaContainerControl::AVFMediaContainerControl(AVFCameraService *)
- : QMediaContainerControl()
- , m_format(QStringLiteral("mov")) // .mov is the default container format on Apple platforms
-{
- if (containers->isEmpty()) {
- containers->insert(QStringLiteral("mov"),
- ContainerInfo(QStringLiteral("QuickTime movie file format"),
- AVFileTypeQuickTimeMovie));
- containers->insert(QStringLiteral("mp4"),
- ContainerInfo(QStringLiteral("MPEG-4 file format"),
- AVFileTypeMPEG4));
- containers->insert(QStringLiteral("m4v"),
- ContainerInfo(QStringLiteral("iTunes video file format"),
- AVFileTypeAppleM4V));
-#ifdef Q_OS_IOS
- containers->insert(QStringLiteral("3gp"),
- ContainerInfo(QStringLiteral("3GPP file format"),
- AVFileType3GPP));
-#endif
- }
-}
-
-QStringList AVFMediaContainerControl::supportedContainers() const
-{
- return containers->keys();
-}
-
-QString AVFMediaContainerControl::containerFormat() const
-{
- return m_format;
-}
-
-void AVFMediaContainerControl::setContainerFormat(const QString &format)
-{
- if (!containers->contains(format)) {
- qWarning("Unsupported container format: '%s'", format.toLocal8Bit().constData());
- return;
- }
-
- m_format = format;
-}
-
-QString AVFMediaContainerControl::containerDescription(const QString &formatMimeType) const
-{
- return containers->value(formatMimeType).description;
-}
-
-NSString *AVFMediaContainerControl::fileType() const
-{
- return containers->value(m_format).fileType;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol.h b/src/plugins/avfoundation/camera/avfmediarecordercontrol.h
deleted file mode 100644
index fbba5eca4..000000000
--- a/src/plugins/avfoundation/camera/avfmediarecordercontrol.h
+++ /dev/null
@@ -1,120 +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 AVFMEDIARECORDERCONTROL_H
-#define AVFMEDIARECORDERCONTROL_H
-
-#include <QtCore/qurl.h>
-#include <QtMultimedia/qmediarecordercontrol.h>
-
-#import <AVFoundation/AVFoundation.h>
-#include "avfstoragelocation.h"
-#include "avfcamerautility.h"
-
-@class AVFMediaRecorderDelegate;
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraSession;
-class AVFCameraControl;
-class AVFAudioInputSelectorControl;
-class AVFCameraService;
-
-class AVFMediaRecorderControl : public QMediaRecorderControl
-{
-Q_OBJECT
-public:
- AVFMediaRecorderControl(AVFCameraService *service, QObject *parent = nullptr);
- ~AVFMediaRecorderControl();
-
- QUrl outputLocation() const override;
- bool setOutputLocation(const QUrl &location) override;
-
- QMediaRecorder::State state() const override;
- QMediaRecorder::Status status() const override;
-
- qint64 duration() const override;
-
- bool isMuted() const override;
- qreal volume() const override;
-
- void applySettings() override;
- void unapplySettings();
-
-public Q_SLOTS:
- void setState(QMediaRecorder::State state) override;
- void setMuted(bool muted) override;
- void setVolume(qreal volume) override;
-
- void handleRecordingStarted();
- void handleRecordingFinished();
- void handleRecordingFailed(const QString &message);
-
-private Q_SLOTS:
- void setupSessionForCapture();
- void updateStatus();
-
-private:
- AVFCameraService *m_service;
- AVFCameraControl *m_cameraControl;
- AVFAudioInputSelectorControl *m_audioInputControl;
- AVFCameraSession *m_session;
-
- bool m_connected;
- QUrl m_outputLocation;
- QMediaRecorder::State m_state;
- QMediaRecorder::Status m_lastStatus;
-
- bool m_recordingStarted;
- bool m_recordingFinished;
-
- bool m_muted;
- qreal m_volume;
-
- AVCaptureDeviceInput *m_audioInput;
- AVCaptureMovieFileOutput *m_movieOutput;
- AVFMediaRecorderDelegate *m_recorderDelagate;
- AVFStorageLocation m_storageLocation;
-
- AVFPSRange m_restoreFPS;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm b/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm
deleted file mode 100644
index 7296b7dc1..000000000
--- a/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm
+++ /dev/null
@@ -1,430 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfcameradebug.h"
-#include "avfmediarecordercontrol.h"
-#include "avfcamerasession.h"
-#include "avfcameraservice.h"
-#include "avfcameracontrol.h"
-#include "avfaudioinputselectorcontrol.h"
-#include "avfaudioencodersettingscontrol.h"
-#include "avfvideoencodersettingscontrol.h"
-#include "avfmediacontainercontrol.h"
-
-#include <QtCore/qurl.h>
-#include <QtCore/qfileinfo.h>
-#include <QtMultimedia/qcameracontrol.h>
-
-
-QT_USE_NAMESPACE
-
-@interface AVFMediaRecorderDelegate : NSObject <AVCaptureFileOutputRecordingDelegate>
-{
-@private
- AVFMediaRecorderControl *m_recorder;
-}
-
-- (AVFMediaRecorderDelegate *) initWithRecorder:(AVFMediaRecorderControl*)recorder;
-
-- (void) captureOutput:(AVCaptureFileOutput *)captureOutput
- didStartRecordingToOutputFileAtURL:(NSURL *)fileURL
- fromConnections:(NSArray *)connections;
-
-- (void) captureOutput:(AVCaptureFileOutput *)captureOutput
- didFinishRecordingToOutputFileAtURL:(NSURL *)fileURL
- fromConnections:(NSArray *)connections
- error:(NSError *)error;
-@end
-
-@implementation AVFMediaRecorderDelegate
-
-- (AVFMediaRecorderDelegate *) initWithRecorder:(AVFMediaRecorderControl*)recorder
-{
- if (!(self = [super init]))
- return nil;
-
- self->m_recorder = recorder;
- return self;
-}
-
-- (void) captureOutput:(AVCaptureFileOutput *)captureOutput
- didStartRecordingToOutputFileAtURL:(NSURL *)fileURL
- fromConnections:(NSArray *)connections
-{
- Q_UNUSED(captureOutput);
- Q_UNUSED(fileURL);
- Q_UNUSED(connections);
-
- QMetaObject::invokeMethod(m_recorder, "handleRecordingStarted", Qt::QueuedConnection);
-}
-
-- (void) captureOutput:(AVCaptureFileOutput *)captureOutput
- didFinishRecordingToOutputFileAtURL:(NSURL *)fileURL
- fromConnections:(NSArray *)connections
- error:(NSError *)error
-{
- Q_UNUSED(captureOutput);
- Q_UNUSED(fileURL);
- Q_UNUSED(connections);
-
- if (error) {
- QStringList messageParts;
- messageParts << QString::fromUtf8([[error localizedDescription] UTF8String]);
- messageParts << QString::fromUtf8([[error localizedFailureReason] UTF8String]);
- messageParts << QString::fromUtf8([[error localizedRecoverySuggestion] UTF8String]);
-
- QString errorMessage = messageParts.join(" ");
-
- QMetaObject::invokeMethod(m_recorder, "handleRecordingFailed", Qt::QueuedConnection,
- Q_ARG(QString, errorMessage));
- } else {
- QMetaObject::invokeMethod(m_recorder, "handleRecordingFinished", Qt::QueuedConnection);
- }
-}
-
-@end
-
-
-AVFMediaRecorderControl::AVFMediaRecorderControl(AVFCameraService *service, QObject *parent)
- : QMediaRecorderControl(parent)
- , m_service(service)
- , m_cameraControl(service->cameraControl())
- , m_audioInputControl(service->audioInputSelectorControl())
- , m_session(service->session())
- , m_connected(false)
- , m_state(QMediaRecorder::StoppedState)
- , m_lastStatus(QMediaRecorder::UnloadedStatus)
- , m_recordingStarted(false)
- , m_recordingFinished(false)
- , m_muted(false)
- , m_volume(1.0)
- , m_audioInput(nil)
- , m_restoreFPS(-1, -1)
-{
- m_movieOutput = [[AVCaptureMovieFileOutput alloc] init];
- m_recorderDelagate = [[AVFMediaRecorderDelegate alloc] initWithRecorder:this];
-
- connect(m_cameraControl, SIGNAL(stateChanged(QCamera::State)), SLOT(updateStatus()));
- connect(m_cameraControl, SIGNAL(statusChanged(QCamera::Status)), SLOT(updateStatus()));
- connect(m_cameraControl, SIGNAL(captureModeChanged(QCamera::CaptureModes)), SLOT(setupSessionForCapture()));
- connect(m_session, SIGNAL(readyToConfigureConnections()), SLOT(setupSessionForCapture()));
- connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(setupSessionForCapture()));
-}
-
-AVFMediaRecorderControl::~AVFMediaRecorderControl()
-{
- if (m_movieOutput) {
- [m_session->captureSession() removeOutput:m_movieOutput];
- [m_movieOutput release];
- }
-
- if (m_audioInput) {
- [m_session->captureSession() removeInput:m_audioInput];
- [m_audioInput release];
- }
-
- [m_recorderDelagate release];
-}
-
-QUrl AVFMediaRecorderControl::outputLocation() const
-{
- return m_outputLocation;
-}
-
-bool AVFMediaRecorderControl::setOutputLocation(const QUrl &location)
-{
- m_outputLocation = location;
- return location.scheme() == QLatin1String("file") || location.scheme().isEmpty();
-}
-
-QMediaRecorder::State AVFMediaRecorderControl::state() const
-{
- return m_state;
-}
-
-QMediaRecorder::Status AVFMediaRecorderControl::status() const
-{
- QMediaRecorder::Status status = m_lastStatus;
- //bool videoEnabled = m_cameraControl->captureMode().testFlag(QCamera::CaptureVideo);
-
- if (m_cameraControl->status() == QCamera::ActiveStatus && m_connected) {
- if (m_state == QMediaRecorder::StoppedState) {
- if (m_recordingStarted && !m_recordingFinished)
- status = QMediaRecorder::FinalizingStatus;
- else
- status = QMediaRecorder::LoadedStatus;
- } else {
- status = m_recordingStarted ? QMediaRecorder::RecordingStatus :
- QMediaRecorder::StartingStatus;
- }
- } else {
- //camera not started yet
- status = m_cameraControl->state() == QCamera::ActiveState && m_connected ?
- QMediaRecorder::LoadingStatus:
- QMediaRecorder::UnloadedStatus;
- }
-
- return status;
-}
-
-void AVFMediaRecorderControl::updateStatus()
-{
- QMediaRecorder::Status newStatus = status();
-
- if (m_lastStatus != newStatus) {
- qDebugCamera() << "Camera recorder status changed: " << m_lastStatus << " -> " << newStatus;
- m_lastStatus = newStatus;
- Q_EMIT statusChanged(m_lastStatus);
- }
-}
-
-
-qint64 AVFMediaRecorderControl::duration() const
-{
- if (!m_movieOutput)
- return 0;
-
- return qint64(CMTimeGetSeconds(m_movieOutput.recordedDuration) * 1000);
-}
-
-bool AVFMediaRecorderControl::isMuted() const
-{
- return m_muted;
-}
-
-qreal AVFMediaRecorderControl::volume() const
-{
- return m_volume;
-}
-
-void AVFMediaRecorderControl::applySettings()
-{
- if (m_state != QMediaRecorder::StoppedState
- || (m_session->state() != QCamera::ActiveState && m_session->state() != QCamera::LoadedState)
- || !m_service->cameraControl()->captureMode().testFlag(QCamera::CaptureVideo)) {
- return;
- }
-
- // Configure audio settings
- [m_movieOutput setOutputSettings:m_service->audioEncoderSettingsControl()->applySettings()
- forConnection:[m_movieOutput connectionWithMediaType:AVMediaTypeAudio]];
-
- // Configure video settings
- AVCaptureConnection *videoConnection = [m_movieOutput connectionWithMediaType:AVMediaTypeVideo];
- NSDictionary *videoSettings = m_service->videoEncoderSettingsControl()->applySettings(videoConnection);
-
- const AVFConfigurationLock lock(m_session->videoCaptureDevice()); // prevents activeFormat from being overridden
-
- [m_movieOutput setOutputSettings:videoSettings forConnection:videoConnection];
-}
-
-void AVFMediaRecorderControl::unapplySettings()
-{
- m_service->audioEncoderSettingsControl()->unapplySettings();
- m_service->videoEncoderSettingsControl()->unapplySettings([m_movieOutput connectionWithMediaType:AVMediaTypeVideo]);
-}
-
-void AVFMediaRecorderControl::setState(QMediaRecorder::State state)
-{
- if (m_state == state)
- return;
-
- qDebugCamera() << Q_FUNC_INFO << m_state << " -> " << state;
-
- switch (state) {
- case QMediaRecorder::RecordingState:
- {
- if (m_connected) {
- QString outputLocationPath = m_outputLocation.scheme() == QLatin1String("file") ?
- m_outputLocation.path() : m_outputLocation.toString();
-
- QString extension = m_service->mediaContainerControl()->containerFormat();
-
- QUrl actualLocation = QUrl::fromLocalFile(
- m_storageLocation.generateFileName(outputLocationPath,
- QCamera::CaptureVideo,
- QLatin1String("clip_"),
- extension));
-
- qDebugCamera() << "Video capture location:" << actualLocation.toString();
-
- applySettings();
-
- [m_movieOutput startRecordingToOutputFileURL:actualLocation.toNSURL()
- recordingDelegate:m_recorderDelagate];
-
- m_state = QMediaRecorder::RecordingState;
- m_recordingStarted = false;
- m_recordingFinished = false;
-
- Q_EMIT actualLocationChanged(actualLocation);
- updateStatus();
- Q_EMIT stateChanged(m_state);
- } else {
- Q_EMIT error(QMediaRecorder::FormatError, tr("Recorder not configured"));
- }
-
- } break;
- case QMediaRecorder::PausedState:
- {
- Q_EMIT error(QMediaRecorder::FormatError, tr("Recording pause not supported"));
- return;
- } break;
- case QMediaRecorder::StoppedState:
- {
- m_lastStatus = QMediaRecorder::FinalizingStatus;
- Q_EMIT statusChanged(m_lastStatus);
- [m_movieOutput stopRecording];
- unapplySettings();
- }
- }
-}
-
-void AVFMediaRecorderControl::setMuted(bool muted)
-{
- if (m_muted != muted) {
- m_muted = muted;
- Q_EMIT mutedChanged(muted);
- }
-}
-
-void AVFMediaRecorderControl::setVolume(qreal volume)
-{
- if (m_volume != volume) {
- m_volume = volume;
- Q_EMIT volumeChanged(volume);
- }
-}
-
-void AVFMediaRecorderControl::handleRecordingStarted()
-{
- m_recordingStarted = true;
- updateStatus();
-}
-
-void AVFMediaRecorderControl::handleRecordingFinished()
-{
- m_recordingFinished = true;
- if (m_state != QMediaRecorder::StoppedState) {
- m_state = QMediaRecorder::StoppedState;
- Q_EMIT stateChanged(m_state);
- }
- updateStatus();
-}
-
-void AVFMediaRecorderControl::handleRecordingFailed(const QString &message)
-{
- m_recordingFinished = true;
- if (m_state != QMediaRecorder::StoppedState) {
- m_state = QMediaRecorder::StoppedState;
- Q_EMIT stateChanged(m_state);
- }
- updateStatus();
-
- Q_EMIT error(QMediaRecorder::ResourceError, message);
-}
-
-void AVFMediaRecorderControl::setupSessionForCapture()
-{
- if (!m_session->videoCaptureDevice())
- return;
-
- //adding movie output causes high CPU usage even when while recording is not active,
- //connect it only while video capture mode is enabled.
- // Similarly, connect the Audio input only in that mode, since it's only necessary
- // when recording anyway. Adding an Audio input will trigger the microphone permission
- // request on iOS, but it shoudn't do so until we actually try to record.
- AVCaptureSession *captureSession = m_session->captureSession();
-
- if (!m_connected
- && m_cameraControl->captureMode().testFlag(QCamera::CaptureVideo)
- && m_session->state() != QCamera::UnloadedState) {
-
- // Lock the video capture device to make sure the active format is not reset
- const AVFConfigurationLock lock(m_session->videoCaptureDevice());
-
- // Add audio input
- // Allow recording even if something wrong happens with the audio input initialization
- AVCaptureDevice *audioDevice = m_audioInputControl->createCaptureDevice();
- if (!audioDevice) {
- qWarning("No audio input device available");
- } else {
- NSError *error = nil;
- m_audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error];
-
- if (!m_audioInput) {
- qWarning() << "Failed to create audio device input";
- } else if (![captureSession canAddInput:m_audioInput]) {
- qWarning() << "Could not connect the audio input";
- m_audioInput = nullptr;
- } else {
- [m_audioInput retain];
- [captureSession addInput:m_audioInput];
- }
- }
-
- if ([captureSession canAddOutput:m_movieOutput]) {
- [captureSession addOutput:m_movieOutput];
- m_connected = true;
- } else {
- Q_EMIT error(QMediaRecorder::ResourceError, tr("Could not connect the video recorder"));
- qWarning() << "Could not connect the video recorder";
- }
- } else if (m_connected
- && (!m_cameraControl->captureMode().testFlag(QCamera::CaptureVideo)
- || m_session->state() == QCamera::UnloadedState)) {
-
- // Lock the video capture device to make sure the active format is not reset
- const AVFConfigurationLock lock(m_session->videoCaptureDevice());
-
- [captureSession removeOutput:m_movieOutput];
-
- if (m_audioInput) {
- [captureSession removeInput:m_audioInput];
- [m_audioInput release];
- m_audioInput = nil;
- }
-
- m_connected = false;
- }
-
- updateStatus();
-}
-
-#include "moc_avfmediarecordercontrol.cpp"
diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h b/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h
deleted file mode 100644
index 9afb1068d..000000000
--- a/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 AVFMEDIARECORDERCONTROL_IOS_H
-#define AVFMEDIARECORDERCONTROL_IOS_H
-
-#include "avfmediaassetwriter.h"
-#include "avfstoragelocation.h"
-#include "avfcamerautility.h"
-
-#include <QtMultimedia/qmediarecordercontrol.h>
-#include <private/qvideooutputorientationhandler_p.h>
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qurl.h>
-
-#include <AVFoundation/AVFoundation.h>
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraService;
-class QString;
-class QUrl;
-
-class AVFMediaRecorderControlIOS : public QMediaRecorderControl
-{
- Q_OBJECT
-public:
- AVFMediaRecorderControlIOS(AVFCameraService *service, QObject *parent = nullptr);
- ~AVFMediaRecorderControlIOS() override;
-
- QUrl outputLocation() const override;
- bool setOutputLocation(const QUrl &location) override;
-
- QMediaRecorder::State state() const override;
- QMediaRecorder::Status status() const override;
-
- qint64 duration() const override;
-
- bool isMuted() const override;
- qreal volume() const override;
-
- void applySettings() override;
- void unapplySettings();
-
-public Q_SLOTS:
- void setState(QMediaRecorder::State state) override;
- void setMuted(bool muted) override;
- void setVolume(qreal volume) override;
-
-private:
-
- Q_INVOKABLE void assetWriterStarted();
- Q_INVOKABLE void assetWriterFinished();
-
-private Q_SLOTS:
- void captureModeChanged(QCamera::CaptureModes);
- void cameraStatusChanged(QCamera::Status newStatus);
-
-private:
- void stopWriter();
-
- AVFCameraService *m_service;
- AVFScopedPointer<QT_MANGLE_NAMESPACE(AVFMediaAssetWriter)> m_writer;
-
- QUrl m_outputLocation;
- AVFStorageLocation m_storageLocation;
-
- QMediaRecorder::State m_state;
- QMediaRecorder::Status m_lastStatus;
-
- NSDictionary *m_audioSettings;
- NSDictionary *m_videoSettings;
- QVideoOutputOrientationHandler m_orientationHandler;
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFMEDIARECORDERCONTROL_IOS_H
diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm b/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm
deleted file mode 100644
index 33064827d..000000000
--- a/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm
+++ /dev/null
@@ -1,414 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfmediarecordercontrol_ios.h"
-#include "avfcamerarenderercontrol.h"
-#include "avfcamerasession.h"
-#include "avfcameracontrol.h"
-#include "avfcameraservice.h"
-#include "avfcameradebug.h"
-#include "avfaudioencodersettingscontrol.h"
-#include "avfvideoencodersettingscontrol.h"
-#include "avfmediacontainercontrol.h"
-#include "avfcamerautility.h"
-
-#include <QtCore/qmath.h>
-#include <QtCore/qdebug.h>
-
-QT_USE_NAMESPACE
-
-namespace {
-
-bool qt_is_writable_file_URL(NSURL *fileURL)
-{
- Q_ASSERT(fileURL);
-
- if (![fileURL isFileURL])
- return false;
-
- if (NSString *path = [[fileURL path] stringByExpandingTildeInPath]) {
- return [[NSFileManager defaultManager]
- isWritableFileAtPath:[path stringByDeletingLastPathComponent]];
- }
-
- return false;
-}
-
-bool qt_file_exists(NSURL *fileURL)
-{
- Q_ASSERT(fileURL);
-
- if (NSString *path = [[fileURL path] stringByExpandingTildeInPath])
- return [[NSFileManager defaultManager] fileExistsAtPath:path];
-
- return false;
-}
-
-}
-
-AVFMediaRecorderControlIOS::AVFMediaRecorderControlIOS(AVFCameraService *service, QObject *parent)
- : QMediaRecorderControl(parent)
- , m_service(service)
- , m_state(QMediaRecorder::StoppedState)
- , m_lastStatus(QMediaRecorder::UnloadedStatus)
- , m_audioSettings(nil)
- , m_videoSettings(nil)
-{
- Q_ASSERT(service);
-
- m_writer.reset([[QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) alloc] initWithDelegate:this]);
- if (!m_writer) {
- qDebugCamera() << Q_FUNC_INFO << "failed to create an asset writer";
- return;
- }
-
- AVFCameraControl *cameraControl = m_service->cameraControl();
- if (!cameraControl) {
- qDebugCamera() << Q_FUNC_INFO << "camera control is nil";
- return;
- }
-
- connect(cameraControl, SIGNAL(captureModeChanged(QCamera::CaptureModes)),
- SLOT(captureModeChanged(QCamera::CaptureModes)));
- connect(cameraControl, SIGNAL(statusChanged(QCamera::Status)),
- SLOT(cameraStatusChanged(QCamera::Status)));
-}
-
-AVFMediaRecorderControlIOS::~AVFMediaRecorderControlIOS()
-{
- [m_writer abort];
-
- if (m_audioSettings)
- [m_audioSettings release];
- if (m_videoSettings)
- [m_videoSettings release];
-}
-
-QUrl AVFMediaRecorderControlIOS::outputLocation() const
-{
- return m_outputLocation;
-}
-
-bool AVFMediaRecorderControlIOS::setOutputLocation(const QUrl &location)
-{
- m_outputLocation = location;
- return location.scheme() == QLatin1String("file") || location.scheme().isEmpty();
-}
-
-QMediaRecorder::State AVFMediaRecorderControlIOS::state() const
-{
- return m_state;
-}
-
-QMediaRecorder::Status AVFMediaRecorderControlIOS::status() const
-{
- return m_lastStatus;
-}
-
-qint64 AVFMediaRecorderControlIOS::duration() const
-{
- return m_writer.data().durationInMs;
-}
-
-bool AVFMediaRecorderControlIOS::isMuted() const
-{
- return false;
-}
-
-qreal AVFMediaRecorderControlIOS::volume() const
-{
- return 1.;
-}
-
-void AVFMediaRecorderControlIOS::applySettings()
-{
- AVFCameraSession *session = m_service->session();
- if (!session)
- return;
-
- if (m_state != QMediaRecorder::StoppedState
- || (session->state() != QCamera::ActiveState && session->state() != QCamera::LoadedState)
- || !m_service->cameraControl()->captureMode().testFlag(QCamera::CaptureVideo)) {
- return;
- }
-
- // audio settings
- m_audioSettings = m_service->audioEncoderSettingsControl()->applySettings();
- if (m_audioSettings)
- [m_audioSettings retain];
-
- // video settings
- AVCaptureConnection *conn = [m_service->videoOutput()->videoDataOutput() connectionWithMediaType:AVMediaTypeVideo];
- m_videoSettings = m_service->videoEncoderSettingsControl()->applySettings(conn);
- if (m_videoSettings)
- [m_videoSettings retain];
-}
-
-void AVFMediaRecorderControlIOS::unapplySettings()
-{
- m_service->audioEncoderSettingsControl()->unapplySettings();
-
- AVCaptureConnection *conn = [m_service->videoOutput()->videoDataOutput() connectionWithMediaType:AVMediaTypeVideo];
- m_service->videoEncoderSettingsControl()->unapplySettings(conn);
-
- if (m_audioSettings) {
- [m_audioSettings release];
- m_audioSettings = nil;
- }
- if (m_videoSettings) {
- [m_videoSettings release];
- m_videoSettings = nil;
- }
-}
-
-void AVFMediaRecorderControlIOS::setState(QMediaRecorder::State state)
-{
- Q_ASSERT(m_service->session()
- && m_service->session()->captureSession());
-
- if (!m_writer) {
- qDebugCamera() << Q_FUNC_INFO << "Invalid recorder";
- return;
- }
-
- if (state == m_state)
- return;
-
- switch (state) {
- case QMediaRecorder::RecordingState:
- {
- AVFCameraControl *cameraControl = m_service->cameraControl();
- Q_ASSERT(cameraControl);
-
- if (!(cameraControl->captureMode() & QCamera::CaptureVideo)) {
- qDebugCamera() << Q_FUNC_INFO << "wrong capture mode, CaptureVideo expected";
- Q_EMIT error(QMediaRecorder::ResourceError, tr("Failed to start recording"));
- return;
- }
-
- if (cameraControl->status() != QCamera::ActiveStatus) {
- qDebugCamera() << Q_FUNC_INFO << "can not start record while camera is not active";
- Q_EMIT error(QMediaRecorder::ResourceError, tr("Failed to start recording"));
- return;
- }
-
- const QString path(m_outputLocation.scheme() == QLatin1String("file") ?
- m_outputLocation.path() : m_outputLocation.toString());
- const QUrl fileURL(QUrl::fromLocalFile(m_storageLocation.generateFileName(path, QCamera::CaptureVideo,
- QLatin1String("clip_"),
- m_service->mediaContainerControl()->containerFormat())));
-
- NSURL *nsFileURL = fileURL.toNSURL();
- if (!nsFileURL) {
- qWarning() << Q_FUNC_INFO << "invalid output URL:" << fileURL;
- Q_EMIT error(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"));
- return;
- }
- if (qt_file_exists(nsFileURL)) {
- // We test for/handle this error here since AWAssetWriter will raise an
- // 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"));
- return;
- }
-
- AVCaptureSession *session = m_service->session()->captureSession();
- // We stop session now so that no more frames for renderer's queue
- // generated, will restart in assetWriterStarted.
- [session stopRunning];
-
- applySettings();
-
- // Make sure the video is recorded in device orientation.
- // The top of the video will match the side of the device which is on top
- // when recording starts (regardless of the UI orientation).
- AVFCameraInfo cameraInfo = m_service->session()->activeCameraInfo();
- int screenOrientation = 360 - m_orientationHandler.currentOrientation();
- float rotation = 0;
- if (cameraInfo.position == QCamera::FrontFace)
- rotation = (screenOrientation + cameraInfo.orientation) % 360;
- else
- rotation = (screenOrientation + (360 - cameraInfo.orientation)) % 360;
-
- if ([m_writer setupWithFileURL:nsFileURL
- cameraService:m_service
- audioSettings:m_audioSettings
- videoSettings:m_videoSettings
- transform:CGAffineTransformMakeRotation(qDegreesToRadians(rotation))]) {
-
- m_state = QMediaRecorder::RecordingState;
- m_lastStatus = QMediaRecorder::StartingStatus;
-
- Q_EMIT actualLocationChanged(fileURL);
- Q_EMIT stateChanged(m_state);
- Q_EMIT statusChanged(m_lastStatus);
-
- // Apple recommends to call startRunning and do all
- // setup on a special queue, and that's what we had
- // initially (dispatch_async to writerQueue). Unfortunately,
- // writer's queue is not the only queue/thread that can
- // access/modify the session, and as a result we have
- // all possible data/race-conditions with Obj-C exceptions
- // at best and something worse in general.
- // Now we try to only modify session on the same thread.
- [m_writer start];
- } else {
- [session startRunning];
- Q_EMIT error(QMediaRecorder::FormatError, tr("Failed to start recording"));
- }
- } break;
- case QMediaRecorder::PausedState:
- {
- Q_EMIT error(QMediaRecorder::FormatError, tr("Recording pause not supported"));
- return;
- } break;
- case QMediaRecorder::StoppedState:
- {
- // Do not check the camera status, we can stop if we started.
- stopWriter();
- }
- }
-}
-
-void AVFMediaRecorderControlIOS::setMuted(bool muted)
-{
- Q_UNUSED(muted);
- qDebugCamera() << Q_FUNC_INFO << "not implemented";
-}
-
-void AVFMediaRecorderControlIOS::setVolume(qreal volume)
-{
- Q_UNUSED(volume);
- qDebugCamera() << Q_FUNC_INFO << "not implemented";
-}
-
-void AVFMediaRecorderControlIOS::assetWriterStarted()
-{
- m_lastStatus = QMediaRecorder::RecordingStatus;
- Q_EMIT statusChanged(QMediaRecorder::RecordingStatus);
-}
-
-void AVFMediaRecorderControlIOS::assetWriterFinished()
-{
- AVFCameraControl *cameraControl = m_service->cameraControl();
- Q_ASSERT(cameraControl);
-
- const QMediaRecorder::Status lastStatus = m_lastStatus;
- const QMediaRecorder::State lastState = m_state;
- if (cameraControl->captureMode() & QCamera::CaptureVideo)
- m_lastStatus = QMediaRecorder::LoadedStatus;
- else
- m_lastStatus = QMediaRecorder::UnloadedStatus;
-
- unapplySettings();
-
- m_service->videoOutput()->resetCaptureDelegate();
- [m_service->session()->captureSession() startRunning];
- m_state = QMediaRecorder::StoppedState;
- if (m_lastStatus != lastStatus)
- Q_EMIT statusChanged(m_lastStatus);
- if (m_state != lastState)
- Q_EMIT stateChanged(m_state);
-}
-
-void AVFMediaRecorderControlIOS::captureModeChanged(QCamera::CaptureModes newMode)
-{
- AVFCameraControl *cameraControl = m_service->cameraControl();
- Q_ASSERT(cameraControl);
-
- const QMediaRecorder::Status lastStatus = m_lastStatus;
-
- if (newMode & QCamera::CaptureVideo) {
- if (cameraControl->status() == QCamera::ActiveStatus)
- m_lastStatus = QMediaRecorder::LoadedStatus;
- } else {
- if (m_lastStatus == QMediaRecorder::RecordingStatus)
- return stopWriter();
- else
- m_lastStatus = QMediaRecorder::UnloadedStatus;
- }
-
- if (m_lastStatus != lastStatus)
- Q_EMIT statusChanged(m_lastStatus);
-}
-
-void AVFMediaRecorderControlIOS::cameraStatusChanged(QCamera::Status newStatus)
-{
- AVFCameraControl *cameraControl = m_service->cameraControl();
- Q_ASSERT(cameraControl);
-
- const QMediaRecorder::Status lastStatus = m_lastStatus;
- const bool isCapture = cameraControl->captureMode() & QCamera::CaptureVideo;
- if (newStatus == QCamera::StartingStatus) {
- if (isCapture && m_lastStatus == QMediaRecorder::UnloadedStatus)
- m_lastStatus = QMediaRecorder::LoadingStatus;
- } else if (newStatus == QCamera::ActiveStatus) {
- if (isCapture && m_lastStatus == QMediaRecorder::LoadingStatus)
- m_lastStatus = QMediaRecorder::LoadedStatus;
- } else {
- if (m_lastStatus == QMediaRecorder::RecordingStatus)
- return stopWriter();
- if (newStatus == QCamera::UnloadedStatus)
- m_lastStatus = QMediaRecorder::UnloadedStatus;
- }
-
- if (lastStatus != m_lastStatus)
- Q_EMIT statusChanged(m_lastStatus);
-}
-
-void AVFMediaRecorderControlIOS::stopWriter()
-{
- if (m_lastStatus == QMediaRecorder::RecordingStatus) {
- m_lastStatus = QMediaRecorder::FinalizingStatus;
-
- Q_EMIT statusChanged(m_lastStatus);
-
- [m_writer stop];
- }
-}
-
-#include "moc_avfmediarecordercontrol_ios.cpp"
diff --git a/src/plugins/avfoundation/camera/avfmediavideoprobecontrol.h b/src/plugins/avfoundation/camera/avfmediavideoprobecontrol.h
deleted file mode 100644
index 69a452a97..000000000
--- a/src/plugins/avfoundation/camera/avfmediavideoprobecontrol.h
+++ /dev/null
@@ -1,60 +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 AVFMEDIAVIDEOPROBECONTROL_H
-#define AVFMEDIAVIDEOPROBECONTROL_H
-
-#include <qmediavideoprobecontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class AVFMediaVideoProbeControl : public QMediaVideoProbeControl
-{
- Q_OBJECT
-public:
- explicit AVFMediaVideoProbeControl(QObject *parent = nullptr);
- ~AVFMediaVideoProbeControl();
-
- void newFrameProbed(const QVideoFrame& frame);
-
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFMEDIAVIDEOPROBECONTROL_H
diff --git a/src/plugins/avfoundation/camera/avfmediavideoprobecontrol.mm b/src/plugins/avfoundation/camera/avfmediavideoprobecontrol.mm
deleted file mode 100644
index 7621661c3..000000000
--- a/src/plugins/avfoundation/camera/avfmediavideoprobecontrol.mm
+++ /dev/null
@@ -1,61 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Copyright (C) 2016 Integrated Computer Solutions, Inc
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfmediavideoprobecontrol.h"
-#include <qvideoframe.h>
-
-QT_BEGIN_NAMESPACE
-
-AVFMediaVideoProbeControl::AVFMediaVideoProbeControl(QObject *parent) :
- QMediaVideoProbeControl(parent)
-{
-}
-
-AVFMediaVideoProbeControl::~AVFMediaVideoProbeControl()
-{
-
-}
-
-void AVFMediaVideoProbeControl::newFrameProbed(const QVideoFrame &frame)
-{
- Q_EMIT videoFrameProbed(frame);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/avfoundation/camera/avfstoragelocation.h b/src/plugins/avfoundation/camera/avfstoragelocation.h
deleted file mode 100644
index 76621983d..000000000
--- a/src/plugins/avfoundation/camera/avfstoragelocation.h
+++ /dev/null
@@ -1,71 +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 AVFSTORAGE_H
-#define AVFSTORAGE_H
-
-#include "qtmultimediaglobal.h"
-
-#include <QtCore/qdir.h>
-#include <QtMultimedia/qcamera.h>
-
-QT_BEGIN_NAMESPACE
-
-class AVFStorageLocation
-{
-public:
- AVFStorageLocation();
- ~AVFStorageLocation();
-
- QString generateFileName(const QString &requestedName,
- QCamera::CaptureMode mode,
- const QString &prefix,
- const QString &ext) const;
-
-
- QDir defaultDir(QCamera::CaptureMode mode) const;
- QString generateFileName(const QString &prefix, const QDir &dir, const QString &ext) const;
-
-private:
- mutable QMap<QString, int> m_lastUsedIndex;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/avfoundation/camera/avfstoragelocation.mm b/src/plugins/avfoundation/camera/avfstoragelocation.mm
deleted file mode 100644
index 6a1cdca17..000000000
--- a/src/plugins/avfoundation/camera/avfstoragelocation.mm
+++ /dev/null
@@ -1,136 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfstoragelocation.h"
-#include "QtCore/qstandardpaths.h"
-
-
-QT_BEGIN_NAMESPACE
-
-
-AVFStorageLocation::AVFStorageLocation()
-{
-}
-
-AVFStorageLocation::~AVFStorageLocation()
-{
-}
-
-/*!
- * Generate the actual file name from user requested one.
- * requestedName may be either empty (the default dir and naming theme is used),
- * points to existing dir (the default name used)
- * or specify the full actual path.
- */
-QString AVFStorageLocation::generateFileName(const QString &requestedName,
- QCamera::CaptureMode mode,
- const QString &prefix,
- const QString &ext) const
-{
- if (requestedName.isEmpty())
- return generateFileName(prefix, defaultDir(mode), ext);
-
- if (QFileInfo(requestedName).isDir())
- return generateFileName(prefix, QDir(requestedName), ext);
-
- return requestedName;
-}
-
-QDir AVFStorageLocation::defaultDir(QCamera::CaptureMode mode) const
-{
- QStringList dirCandidates;
-
- if (mode == QCamera::CaptureVideo) {
- dirCandidates << QStandardPaths::writableLocation(QStandardPaths::MoviesLocation);
- } else {
- dirCandidates << QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
- }
-
- dirCandidates << QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
- dirCandidates << QDir::homePath();
- dirCandidates << QDir::currentPath();
- dirCandidates << QDir::tempPath();
-
- for (const QString &path : qAsConst(dirCandidates)) {
- if (QFileInfo(path).isWritable())
- return QDir(path);
- }
-
- return QDir();
-}
-
-QString AVFStorageLocation::generateFileName(const QString &prefix, const QDir &dir, const QString &ext) const
-{
- QString lastClipKey = dir.absolutePath()+QLatin1Char(' ')+prefix+QLatin1Char(' ')+ext;
- int lastClip = m_lastUsedIndex.value(lastClipKey, 0);
-
- if (lastClip == 0) {
- //first run, find the maximum clip number during the fist capture
- const auto list = dir.entryList(QStringList() << QString("%1*.%2").arg(prefix).arg(ext));
- for (const QString &fileName : list) {
- int imgNumber = QStringView{fileName}.mid(prefix.length(), fileName.size()-prefix.length()-ext.length()-1).toInt();
- lastClip = qMax(lastClip, imgNumber);
- }
- }
-
-
- //don't just rely on cached lastClip value,
- //someone else may create a file after camera started
- while (true) {
- QString name = QString("%1%2.%3").arg(prefix)
- .arg(lastClip+1,
- 4, //fieldWidth
- 10,
- QLatin1Char('0'))
- .arg(ext);
-
- QString path = dir.absoluteFilePath(name);
- if (!QFileInfo(path).exists()) {
- m_lastUsedIndex[lastClipKey] = lastClip+1;
- return path;
- }
-
- lastClip++;
- }
-
- return QString();
-}
-
-
-QT_END_NAMESPACE
diff --git a/src/plugins/avfoundation/camera/avfvideoencodersettingscontrol.h b/src/plugins/avfoundation/camera/avfvideoencodersettingscontrol.h
deleted file mode 100644
index 7c9574f3d..000000000
--- a/src/plugins/avfoundation/camera/avfvideoencodersettingscontrol.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 AVFVIDEOENCODERSETTINGSCONTROL_H
-#define AVFVIDEOENCODERSETTINGSCONTROL_H
-
-#include <qvideoencodersettingscontrol.h>
-
-#include "avfcamerautility.h"
-#import <AVFoundation/AVFoundation.h>
-
-@class NSDictionary;
-
-QT_BEGIN_NAMESPACE
-
-class AVFCameraService;
-
-class AVFVideoEncoderSettingsControl : public QVideoEncoderSettingsControl
-{
- Q_OBJECT
-
-public:
- explicit AVFVideoEncoderSettingsControl(AVFCameraService *service);
-
- QList<QSize> supportedResolutions(const QVideoEncoderSettings &requestedVideoSettings,
- bool *continuous = nullptr) const override;
-
- QList<qreal> supportedFrameRates(const QVideoEncoderSettings &requestedVideoSettings,
- bool *continuous = nullptr) const override;
-
- QStringList supportedVideoCodecs() const override;
- QString videoCodecDescription(const QString &codecName) const override;
-
- QVideoEncoderSettings videoSettings() const override;
- void setVideoSettings(const QVideoEncoderSettings &requestedVideoSettings) override;
-
- NSDictionary *applySettings(AVCaptureConnection *connection);
- void unapplySettings(AVCaptureConnection *connection);
-
-private:
- AVFCameraService *m_service;
-
- QVideoEncoderSettings m_requestedSettings;
- QVideoEncoderSettings m_actualSettings;
-
- AVCaptureDeviceFormat *m_restoreFormat;
- AVFPSRange m_restoreFps;
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFVIDEOENCODERSETTINGSCONTROL_H
diff --git a/src/plugins/avfoundation/camera/avfvideoencodersettingscontrol.mm b/src/plugins/avfoundation/camera/avfvideoencodersettingscontrol.mm
deleted file mode 100644
index 70ec38988..000000000
--- a/src/plugins/avfoundation/camera/avfvideoencodersettingscontrol.mm
+++ /dev/null
@@ -1,385 +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 "avfvideoencodersettingscontrol.h"
-
-#include "avfcameraservice.h"
-#include "avfcamerautility.h"
-#include "avfcamerasession.h"
-#include "avfcamerarenderercontrol.h"
-
-#include <AVFoundation/AVFoundation.h>
-
-QT_BEGIN_NAMESPACE
-
-Q_GLOBAL_STATIC_WITH_ARGS(QStringList, supportedCodecs, (QStringList() << QLatin1String("avc1")
- << QLatin1String("jpeg")
- #ifdef Q_OS_OSX
- << QLatin1String("ap4h")
- << QLatin1String("apcn")
- #endif
- ))
-
-static bool format_supports_framerate(AVCaptureDeviceFormat *format, qreal fps)
-{
- if (format && fps > qreal(0)) {
- const qreal epsilon = 0.1;
- for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) {
- if (range.maxFrameRate - range.minFrameRate < epsilon) {
- if (qAbs(fps - range.maxFrameRate) < epsilon)
- return true;
- }
-
- if (fps >= range.minFrameRate && fps <= range.maxFrameRate)
- return true;
- }
- }
-
- return false;
-}
-
-static bool real_list_contains(const QList<qreal> &list, qreal value)
-{
- for (qreal r : list) {
- if (qFuzzyCompare(r, value))
- return true;
- }
- return false;
-}
-
-AVFVideoEncoderSettingsControl::AVFVideoEncoderSettingsControl(AVFCameraService *service)
- : QVideoEncoderSettingsControl()
- , m_service(service)
- , m_restoreFormat(nil)
-{
-}
-
-QList<QSize> AVFVideoEncoderSettingsControl::supportedResolutions(const QVideoEncoderSettings &settings,
- bool *continuous) const
-{
- Q_UNUSED(settings);
-
- if (continuous)
- *continuous = true;
-
- // AVFoundation seems to support any resolution for recording, with the following limitations:
- // - The recording resolution can't be higher than the camera's active resolution
- // - On OS X, the recording resolution is automatically adjusted to have the same aspect ratio as
- // the camera's active resolution
- QList<QSize> resolutions;
- resolutions.append(QSize(32, 32));
-
- AVCaptureDevice *device = m_service->session()->videoCaptureDevice();
- if (device) {
- int maximumWidth = 0;
- const QVector<AVCaptureDeviceFormat *> formats(qt_unique_device_formats(device,
- m_service->session()->defaultCodec()));
- for (int i = 0; i < formats.size(); ++i) {
- const QSize res(qt_device_format_resolution(formats[i]));
- if (res.width() > maximumWidth)
- maximumWidth = res.width();
- }
-
- if (maximumWidth > 0)
- resolutions.append(QSize(maximumWidth, maximumWidth));
- }
-
- if (resolutions.count() == 1)
- resolutions.append(QSize(3840, 3840));
-
- return resolutions;
-}
-
-QList<qreal> AVFVideoEncoderSettingsControl::supportedFrameRates(const QVideoEncoderSettings &settings,
- bool *continuous) const
-{
- QList<qreal> uniqueFrameRates;
-
- AVCaptureDevice *device = m_service->session()->videoCaptureDevice();
- if (!device)
- return uniqueFrameRates;
-
- if (continuous)
- *continuous = false;
-
- QVector<AVFPSRange> allRates;
-
- if (!settings.resolution().isValid()) {
- const QVector<AVCaptureDeviceFormat *> formats(qt_unique_device_formats(device, 0));
- for (int i = 0; i < formats.size(); ++i) {
- AVCaptureDeviceFormat *format = formats.at(i);
- allRates += qt_device_format_framerates(format);
- }
- } else {
- AVCaptureDeviceFormat *format = qt_find_best_resolution_match(device,
- settings.resolution(),
- m_service->session()->defaultCodec());
- if (format)
- allRates = qt_device_format_framerates(format);
- }
-
- for (int j = 0; j < allRates.size(); ++j) {
- if (!real_list_contains(uniqueFrameRates, allRates[j].first))
- uniqueFrameRates.append(allRates[j].first);
- if (!real_list_contains(uniqueFrameRates, allRates[j].second))
- uniqueFrameRates.append(allRates[j].second);
- }
-
- return uniqueFrameRates;
-}
-
-QStringList AVFVideoEncoderSettingsControl::supportedVideoCodecs() const
-{
- return *supportedCodecs;
-}
-
-QString AVFVideoEncoderSettingsControl::videoCodecDescription(const QString &codecName) const
-{
- if (codecName == QLatin1String("avc1"))
- return QStringLiteral("H.264");
- else if (codecName == QLatin1String("jpeg"))
- return QStringLiteral("M-JPEG");
-#ifdef Q_OS_OSX
- else if (codecName == QLatin1String("ap4h"))
- return QStringLiteral("Apple ProRes 4444");
- else if (codecName == QLatin1String("apcn"))
- return QStringLiteral("Apple ProRes 422 Standard Definition");
-#endif
-
- return QString();
-}
-
-QVideoEncoderSettings AVFVideoEncoderSettingsControl::videoSettings() const
-{
- return m_actualSettings;
-}
-
-void AVFVideoEncoderSettingsControl::setVideoSettings(const QVideoEncoderSettings &settings)
-{
- if (m_requestedSettings == settings)
- return;
-
- m_requestedSettings = m_actualSettings = settings;
-}
-
-NSDictionary *AVFVideoEncoderSettingsControl::applySettings(AVCaptureConnection *connection)
-{
- if (m_service->session()->state() != QCamera::LoadedState &&
- m_service->session()->state() != QCamera::ActiveState) {
- return nil;
- }
-
- AVCaptureDevice *device = m_service->session()->videoCaptureDevice();
- if (!device)
- return nil;
-
- AVFPSRange currentFps = qt_current_framerates(device, connection);
- const bool needFpsChange = m_requestedSettings.frameRate() > 0
- && m_requestedSettings.frameRate() != currentFps.second;
-
- NSMutableDictionary *videoSettings = [NSMutableDictionary dictionary];
-
- // -- Codec
-
- // AVVideoCodecKey is the only mandatory key
- QString codec = m_requestedSettings.codec().isEmpty() ? supportedCodecs->first() : m_requestedSettings.codec();
- if (!supportedCodecs->contains(codec)) {
- qWarning("Unsupported codec: '%s'", codec.toLocal8Bit().constData());
- codec = supportedCodecs->first();
- }
- [videoSettings setObject:codec.toNSString() forKey:AVVideoCodecKey];
- m_actualSettings.setCodec(codec);
-
- // -- Resolution
-
- int w = m_requestedSettings.resolution().width();
- int h = m_requestedSettings.resolution().height();
-
- if (AVCaptureDeviceFormat *currentFormat = device.activeFormat) {
- CMFormatDescriptionRef formatDesc = currentFormat.formatDescription;
- CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(formatDesc);
-
- // We have to change the device's activeFormat in 3 cases:
- // - the requested recording resolution is higher than the current device resolution
- // - the requested recording resolution has a different aspect ratio than the current device aspect ratio
- // - the requested frame rate is not available for the current device format
- AVCaptureDeviceFormat *newFormat = nil;
- if ((w <= 0 || h <= 0)
- && m_requestedSettings.frameRate() > 0
- && !format_supports_framerate(currentFormat, m_requestedSettings.frameRate())) {
-
- newFormat = qt_find_best_framerate_match(device,
- m_service->session()->defaultCodec(),
- m_requestedSettings.frameRate());
-
- } else if (w > 0 && h > 0) {
- AVCaptureDeviceFormat *f = qt_find_best_resolution_match(device,
- m_requestedSettings.resolution(),
- m_service->session()->defaultCodec());
-
- if (f) {
- CMVideoDimensions d = CMVideoFormatDescriptionGetDimensions(f.formatDescription);
- qreal fAspectRatio = qreal(d.width) / d.height;
-
- if (w > dim.width || h > dim.height
- || qAbs((qreal(dim.width) / dim.height) - fAspectRatio) > 0.01) {
- newFormat = f;
- }
- }
- }
-
- if (qt_set_active_format(device, newFormat, !needFpsChange)) {
- m_restoreFormat = [currentFormat retain];
- formatDesc = newFormat.formatDescription;
- dim = CMVideoFormatDescriptionGetDimensions(formatDesc);
- }
-
- if (w > 0 && h > 0) {
- // Make sure the recording resolution has the same aspect ratio as the device's
- // current resolution
- qreal deviceAspectRatio = qreal(dim.width) / dim.height;
- qreal recAspectRatio = qreal(w) / h;
- if (qAbs(deviceAspectRatio - recAspectRatio) > 0.01) {
- if (recAspectRatio > deviceAspectRatio)
- w = qRound(h * deviceAspectRatio);
- else
- h = qRound(w / deviceAspectRatio);
- }
-
- // recording resolution can't be higher than the device's active resolution
- w = qMin(w, dim.width);
- h = qMin(h, dim.height);
- }
- }
-
- if (w > 0 && h > 0) {
- // Width and height must be divisible by 2
- w += w & 1;
- h += h & 1;
-
- [videoSettings setObject:[NSNumber numberWithInt:w] forKey:AVVideoWidthKey];
- [videoSettings setObject:[NSNumber numberWithInt:h] forKey:AVVideoHeightKey];
- m_actualSettings.setResolution(w, h);
- } else {
- m_actualSettings.setResolution(qt_device_format_resolution(device.activeFormat));
- }
-
- // -- FPS
-
- if (needFpsChange) {
- m_restoreFps = currentFps;
- const qreal fps = m_requestedSettings.frameRate();
- qt_set_framerate_limits(device, connection, fps, fps);
- }
- m_actualSettings.setFrameRate(qt_current_framerates(device, connection).second);
-
- // -- Codec Settings
-
- NSMutableDictionary *codecProperties = [NSMutableDictionary dictionary];
- int bitrate = -1;
- float quality = -1.f;
-
- if (m_requestedSettings.encodingMode() == QMultimedia::ConstantQualityEncoding) {
- if (m_requestedSettings.quality() != QMultimedia::NormalQuality) {
- if (codec != QLatin1String("jpeg")) {
- qWarning("ConstantQualityEncoding is not supported for codec: '%s'", codec.toLocal8Bit().constData());
- } else {
- switch (m_requestedSettings.quality()) {
- case QMultimedia::VeryLowQuality:
- quality = 0.f;
- break;
- case QMultimedia::LowQuality:
- quality = 0.25f;
- break;
- case QMultimedia::HighQuality:
- quality = 0.75f;
- break;
- case QMultimedia::VeryHighQuality:
- quality = 1.f;
- break;
- default:
- quality = -1.f; // NormalQuality, let the system decide
- break;
- }
- }
- }
- } else if (m_requestedSettings.encodingMode() == QMultimedia::AverageBitRateEncoding){
- if (codec != QLatin1String("avc1"))
- qWarning("AverageBitRateEncoding is not supported for codec: '%s'", codec.toLocal8Bit().constData());
- else
- bitrate = m_requestedSettings.bitRate();
- } else {
- qWarning("Encoding mode is not supported");
- }
-
- if (bitrate != -1)
- [codecProperties setObject:[NSNumber numberWithInt:bitrate] forKey:AVVideoAverageBitRateKey];
- if (quality != -1.f)
- [codecProperties setObject:[NSNumber numberWithFloat:quality] forKey:AVVideoQualityKey];
-
- [videoSettings setObject:codecProperties forKey:AVVideoCompressionPropertiesKey];
-
- return videoSettings;
-}
-
-void AVFVideoEncoderSettingsControl::unapplySettings(AVCaptureConnection *connection)
-{
- m_actualSettings = m_requestedSettings;
-
- AVCaptureDevice *device = m_service->session()->videoCaptureDevice();
- if (!device)
- return;
-
- const bool needFpsChanged = m_restoreFps.first || m_restoreFps.second;
-
- if (m_restoreFormat) {
- qt_set_active_format(device, m_restoreFormat, !needFpsChanged);
- [m_restoreFormat release];
- m_restoreFormat = nil;
- }
-
- if (needFpsChanged) {
- qt_set_framerate_limits(device, connection, m_restoreFps.first, m_restoreFps.second);
- m_restoreFps = AVFPSRange();
- }
-}
-
-QT_END_NAMESPACE
-
-#include "moc_avfvideoencodersettingscontrol.cpp"
diff --git a/src/plugins/avfoundation/camera/camera.pro b/src/plugins/avfoundation/camera/camera.pro
deleted file mode 100644
index 76aa8af85..000000000
--- a/src/plugins/avfoundation/camera/camera.pro
+++ /dev/null
@@ -1,94 +0,0 @@
-TARGET = qavfcamera
-
-# Avoid clash with a variable named `slots' in a Quartz header
-CONFIG += no_keywords
-
-QT += multimedia-private network
-
-LIBS += -framework CoreFoundation \
- -framework Foundation \
- -framework AudioToolbox \
- -framework CoreAudio \
- -framework QuartzCore \
- -framework CoreMedia
-osx:LIBS += -framework AppKit \
- -framework AudioUnit
-ios:LIBS += -framework CoreGraphics \
- -framework CoreVideo
-
-QMAKE_USE += avfoundation
-
-OTHER_FILES += avfcamera.json
-
-HEADERS += \
- avfcameradebug.h \
- avfcameraserviceplugin.h \
- avfcameracontrol.h \
- avfcamerametadatacontrol.h \
- avfimagecapturecontrol.h \
- avfcameraservice.h \
- avfcamerasession.h \
- avfstoragelocation.h \
- avfaudioinputselectorcontrol.h \
- avfcamerainfocontrol.h \
- avfmediavideoprobecontrol.h \
- avfcamerarenderercontrol.h \
- avfcameradevicecontrol.h \
- avfcamerafocuscontrol.h \
- avfcameraexposurecontrol.h \
- avfcamerautility.h \
- avfcameraviewfindersettingscontrol.h \
- avfimageencodercontrol.h \
- avfcameraflashcontrol.h \
- avfvideoencodersettingscontrol.h \
- avfmediacontainercontrol.h \
- avfaudioencodersettingscontrol.h \
- avfcamerawindowcontrol.h \
- avfcapturedestinationcontrol.h
-
-OBJECTIVE_SOURCES += \
- avfcameraserviceplugin.mm \
- avfcameracontrol.mm \
- avfcamerametadatacontrol.mm \
- avfimagecapturecontrol.mm \
- avfcameraservice.mm \
- avfcamerasession.mm \
- avfstoragelocation.mm \
- avfaudioinputselectorcontrol.mm \
- avfcamerainfocontrol.mm \
- avfmediavideoprobecontrol.mm \
- avfcameradevicecontrol.mm \
- avfcamerarenderercontrol.mm \
- avfcamerafocuscontrol.mm \
- avfcameraexposurecontrol.mm \
- avfcamerautility.mm \
- avfcameraviewfindersettingscontrol.mm \
- avfimageencodercontrol.mm \
- avfcameraflashcontrol.mm \
- avfvideoencodersettingscontrol.mm \
- avfmediacontainercontrol.mm \
- avfaudioencodersettingscontrol.mm \
- avfcamerawindowcontrol.mm \
- avfcapturedestinationcontrol.mm
-
-osx {
-
-HEADERS += avfmediarecordercontrol.h
-OBJECTIVE_SOURCES += avfmediarecordercontrol.mm
-
-}
-
-ios {
-
-HEADERS += avfcamerazoomcontrol.h \
- avfmediaassetwriter.h \
- avfmediarecordercontrol_ios.h
-OBJECTIVE_SOURCES += avfcamerazoomcontrol.mm \
- avfmediaassetwriter.mm \
- avfmediarecordercontrol_ios.mm
-
-}
-
-PLUGIN_TYPE = mediaservice
-PLUGIN_CLASS_NAME = AVFServicePlugin
-load(qt_plugin)
diff --git a/src/plugins/avfoundation/mediaplayer/avfdisplaylink.h b/src/plugins/avfoundation/mediaplayer/avfdisplaylink.h
deleted file mode 100644
index ba0803807..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfdisplaylink.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 AVFDISPLAYLINK_H
-#define AVFDISPLAYLINK_H
-
-#include <QtCore/qobject.h>
-#include <QtCore/qmutex.h>
-
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
-#include <CoreVideo/CVBase.h>
-#else
-#include <QuartzCore/CVDisplayLink.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class AVFDisplayLink : public QObject
-{
- Q_OBJECT
-public:
- explicit AVFDisplayLink(QObject *parent = nullptr);
- virtual ~AVFDisplayLink();
- bool isValid() const;
- bool isActive() const;
-
-public Q_SLOTS:
- void start();
- void stop();
-
-Q_SIGNALS:
- void tick(const CVTimeStamp &ts);
-
-public:
- void displayLinkEvent(const CVTimeStamp *);
-
-protected:
- virtual bool event(QEvent *) override;
-
-private:
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
- void *m_displayLink;
-#else
- CVDisplayLinkRef m_displayLink;
-#endif
- QMutex m_displayLinkMutex;
- bool m_pendingDisplayLinkEvent;
- bool m_isActive;
- CVTimeStamp m_frameTimeStamp;
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFDISPLAYLINK_H
diff --git a/src/plugins/avfoundation/mediaplayer/avfdisplaylink.mm b/src/plugins/avfoundation/mediaplayer/avfdisplaylink.mm
deleted file mode 100644
index bae18bc0a..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfdisplaylink.mm
+++ /dev/null
@@ -1,241 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfdisplaylink.h"
-#include <QtCore/qcoreapplication.h>
-
-#ifdef QT_DEBUG_AVF
-#include <QtCore/qdebug.h>
-#endif
-
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
-#import <QuartzCore/CADisplayLink.h>
-#import <Foundation/NSRunLoop.h>
-#define _m_displayLink static_cast<DisplayLinkObserver*>(m_displayLink)
-#else
-#endif
-
-QT_USE_NAMESPACE
-
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
-@interface DisplayLinkObserver : NSObject
-
-- (void)start;
-- (void)stop;
-- (void)displayLinkNotification:(CADisplayLink *)sender;
-
-@end
-
-@implementation DisplayLinkObserver
-{
- AVFDisplayLink *m_avfDisplayLink;
- CADisplayLink *m_displayLink;
-}
-
-- (id)initWithAVFDisplayLink:(AVFDisplayLink *)link
-{
- self = [super init];
-
- if (self) {
- m_avfDisplayLink = link;
- m_displayLink = [[CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkNotification:)] retain];
- }
-
- return self;
-}
-
-- (void) dealloc
-{
- if (m_displayLink) {
- [m_displayLink release];
- m_displayLink = nullptr;
- }
-
- [super dealloc];
-}
-
-- (void)start
-{
- [m_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
-}
-
-- (void)stop
-{
- [m_displayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
-}
-
-- (void)displayLinkNotification:(CADisplayLink *)sender
-{
- Q_UNUSED(sender);
- m_avfDisplayLink->displayLinkEvent(nullptr);
-}
-
-@end
-#else
-static CVReturn CVDisplayLinkCallback(CVDisplayLinkRef displayLink,
- const CVTimeStamp *inNow,
- const CVTimeStamp *inOutputTime,
- CVOptionFlags flagsIn,
- CVOptionFlags *flagsOut,
- void *displayLinkContext)
-{
- Q_UNUSED(displayLink);
- Q_UNUSED(inNow);
- Q_UNUSED(flagsIn);
- Q_UNUSED(flagsOut);
-
- AVFDisplayLink *link = (AVFDisplayLink *)displayLinkContext;
-
- link->displayLinkEvent(inOutputTime);
- return kCVReturnSuccess;
-}
-#endif
-
-AVFDisplayLink::AVFDisplayLink(QObject *parent)
- : QObject(parent)
- , m_displayLink(nullptr)
- , m_pendingDisplayLinkEvent(false)
- , m_isActive(false)
-{
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
- m_displayLink = [[DisplayLinkObserver alloc] initWithAVFDisplayLink:this];
-#else
- // create display link for the main display
- CVDisplayLinkCreateWithCGDisplay(kCGDirectMainDisplay, &m_displayLink);
- if (m_displayLink) {
- // set the current display of a display link.
- CVDisplayLinkSetCurrentCGDisplay(m_displayLink, kCGDirectMainDisplay);
-
- // set the renderer output callback function
- CVDisplayLinkSetOutputCallback(m_displayLink, &CVDisplayLinkCallback, this);
- }
-#endif
-}
-
-AVFDisplayLink::~AVFDisplayLink()
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO;
-#endif
-
- if (m_displayLink) {
- stop();
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
- [_m_displayLink release];
-#else
- CVDisplayLinkRelease(m_displayLink);
-#endif
- m_displayLink = nullptr;
- }
-}
-
-bool AVFDisplayLink::isValid() const
-{
- return m_displayLink != nullptr;
-}
-
-bool AVFDisplayLink::isActive() const
-{
- return m_isActive;
-}
-
-void AVFDisplayLink::start()
-{
- if (m_displayLink && !m_isActive) {
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
- [_m_displayLink start];
-#else
- CVDisplayLinkStart(m_displayLink);
-#endif
- m_isActive = true;
- }
-}
-
-void AVFDisplayLink::stop()
-{
- if (m_displayLink && m_isActive) {
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
- [_m_displayLink stop];
-#else
- CVDisplayLinkStop(m_displayLink);
-#endif
- m_isActive = false;
- }
-}
-
-void AVFDisplayLink::displayLinkEvent(const CVTimeStamp *ts)
-{
- // This function is called from a
- // thread != gui thread. So we post the event.
- // But we need to make sure that we don't post faster
- // than the event loop can eat:
- m_displayLinkMutex.lock();
- bool pending = m_pendingDisplayLinkEvent;
- m_pendingDisplayLinkEvent = true;
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
- Q_UNUSED(ts);
- memset(&m_frameTimeStamp, 0, sizeof(CVTimeStamp));
-#else
- m_frameTimeStamp = *ts;
-#endif
- m_displayLinkMutex.unlock();
-
- if (!pending)
- qApp->postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority);
-}
-
-bool AVFDisplayLink::event(QEvent *event)
-{
- switch (event->type()){
- case QEvent::User: {
- m_displayLinkMutex.lock();
- m_pendingDisplayLinkEvent = false;
- CVTimeStamp ts = m_frameTimeStamp;
- m_displayLinkMutex.unlock();
-
- Q_EMIT tick(ts);
-
- return false;
- }
- break;
- default:
- break;
- }
- return QObject::event(event);
-}
diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayer.json b/src/plugins/avfoundation/mediaplayer/avfmediaplayer.json
deleted file mode 100644
index 5626edec0..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfmediaplayer.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Keys": ["avfoundationmediaplayer"],
- "Services": ["org.qt-project.qt.mediaplayer"]
-}
diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.h b/src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.h
deleted file mode 100644
index ac0ba0ab2..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.h
+++ /dev/null
@@ -1,99 +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 AVFMEDIAPLAYERCONTROL_H
-#define AVFMEDIAPLAYERCONTROL_H
-
-#include <QtMultimedia/QMediaPlayerControl>
-#include <QtCore/QObject>
-
-QT_BEGIN_NAMESPACE
-
-class AVFMediaPlayerSession;
-
-class AVFMediaPlayerControl : public QMediaPlayerControl
-{
- Q_OBJECT
-public:
- explicit AVFMediaPlayerControl(QObject *parent = nullptr);
- ~AVFMediaPlayerControl();
-
- void setSession(AVFMediaPlayerSession *session);
-
- QMediaPlayer::State state() const override;
- QMediaPlayer::MediaStatus mediaStatus() const override;
-
- QMediaContent media() const override;
- const QIODevice *mediaStream() const override;
- void setMedia(const QMediaContent &content, QIODevice *stream) override;
-
- qint64 position() const override;
- qint64 duration() const override;
-
- int bufferStatus() const override;
-
- int volume() const override;
- bool isMuted() const override;
-
- bool isAudioAvailable() const override;
- bool isVideoAvailable() const override;
-
- bool isSeekable() const override;
- QMediaTimeRange availablePlaybackRanges() const override;
-
- qreal playbackRate() const override;
- void setPlaybackRate(qreal rate) override;
-
-public Q_SLOTS:
- void setPosition(qint64 pos) override;
-
- void play() override;
- void pause() override;
- void stop() override;
-
- void setVolume(int volume) override;
- void setMuted(bool muted) override;
-
-private:
- AVFMediaPlayerSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFMEDIAPLAYERCONTROL_H
diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.mm
deleted file mode 100644
index bf7ebb4a0..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.mm
+++ /dev/null
@@ -1,192 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfmediaplayercontrol.h"
-#include "avfmediaplayersession.h"
-
-QT_USE_NAMESPACE
-
-AVFMediaPlayerControl::AVFMediaPlayerControl(QObject *parent) :
- QMediaPlayerControl(parent)
-{
-}
-
-AVFMediaPlayerControl::~AVFMediaPlayerControl()
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO;
-#endif
-}
-
-void AVFMediaPlayerControl::setSession(AVFMediaPlayerSession *session)
-{
- m_session = session;
-
- connect(m_session, SIGNAL(positionChanged(qint64)), this, SIGNAL(positionChanged(qint64)));
- connect(m_session, SIGNAL(durationChanged(qint64)), this, SIGNAL(durationChanged(qint64)));
- connect(m_session, SIGNAL(bufferStatusChanged(int)), this, SIGNAL(bufferStatusChanged(int)));
- connect(m_session, SIGNAL(stateChanged(QMediaPlayer::State)),
- this, SIGNAL(stateChanged(QMediaPlayer::State)));
- connect(m_session, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)),
- this, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)));
- connect(m_session, SIGNAL(volumeChanged(int)), this, SIGNAL(volumeChanged(int)));
- connect(m_session, SIGNAL(mutedChanged(bool)), this, SIGNAL(mutedChanged(bool)));
- connect(m_session, SIGNAL(audioAvailableChanged(bool)), this, SIGNAL(audioAvailableChanged(bool)));
- connect(m_session, SIGNAL(videoAvailableChanged(bool)), this, SIGNAL(videoAvailableChanged(bool)));
- connect(m_session, SIGNAL(error(int,QString)), this, SIGNAL(error(int,QString)));
- connect(m_session, &AVFMediaPlayerSession::playbackRateChanged,
- this, &AVFMediaPlayerControl::playbackRateChanged);
- connect(m_session, &AVFMediaPlayerSession::seekableChanged,
- this, &AVFMediaPlayerControl::seekableChanged);
-}
-
-QMediaPlayer::State AVFMediaPlayerControl::state() const
-{
- return m_session->state();
-}
-
-QMediaPlayer::MediaStatus AVFMediaPlayerControl::mediaStatus() const
-{
- return m_session->mediaStatus();
-}
-
-QMediaContent AVFMediaPlayerControl::media() const
-{
- return m_session->media();
-}
-
-const QIODevice *AVFMediaPlayerControl::mediaStream() const
-{
- return m_session->mediaStream();
-}
-
-void AVFMediaPlayerControl::setMedia(const QMediaContent &content, QIODevice *stream)
-{
- const QMediaContent oldContent = m_session->media();
-
- m_session->setMedia(content, stream);
-
- if (content != oldContent)
- Q_EMIT mediaChanged(content);
-}
-
-qint64 AVFMediaPlayerControl::position() const
-{
- return m_session->position();
-}
-
-qint64 AVFMediaPlayerControl::duration() const
-{
- return m_session->duration();
-}
-
-int AVFMediaPlayerControl::bufferStatus() const
-{
- return m_session->bufferStatus();
-}
-
-int AVFMediaPlayerControl::volume() const
-{
- return m_session->volume();
-}
-
-bool AVFMediaPlayerControl::isMuted() const
-{
- return m_session->isMuted();
-}
-
-bool AVFMediaPlayerControl::isAudioAvailable() const
-{
- return m_session->isAudioAvailable();
-}
-
-bool AVFMediaPlayerControl::isVideoAvailable() const
-{
- return m_session->isVideoAvailable();
-}
-
-bool AVFMediaPlayerControl::isSeekable() const
-{
- return m_session->isSeekable();
-}
-
-QMediaTimeRange AVFMediaPlayerControl::availablePlaybackRanges() const
-{
- return m_session->availablePlaybackRanges();
-}
-
-qreal AVFMediaPlayerControl::playbackRate() const
-{
- return m_session->playbackRate();
-}
-
-void AVFMediaPlayerControl::setPlaybackRate(qreal rate)
-{
- m_session->setPlaybackRate(rate);
-}
-
-void AVFMediaPlayerControl::setPosition(qint64 pos)
-{
- m_session->setPosition(pos);
-}
-
-void AVFMediaPlayerControl::play()
-{
- m_session->play();
-}
-
-void AVFMediaPlayerControl::pause()
-{
- m_session->pause();
-}
-
-void AVFMediaPlayerControl::stop()
-{
- m_session->stop();
-}
-
-void AVFMediaPlayerControl::setVolume(int volume)
-{
- m_session->setVolume(volume);
-}
-
-void AVFMediaPlayerControl::setMuted(bool muted)
-{
- m_session->setMuted(muted);
-}
diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.h b/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.h
deleted file mode 100644
index ae99c7eda..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.h
+++ /dev/null
@@ -1,74 +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 AVFMEDIAPLAYERMETADATACONTROL_H
-#define AVFMEDIAPLAYERMETADATACONTROL_H
-
-#include <QtMultimedia/QMetaDataReaderControl>
-
-QT_BEGIN_NAMESPACE
-
-class AVFMediaPlayerSession;
-
-class AVFMediaPlayerMetaDataControl : public QMetaDataReaderControl
-{
- Q_OBJECT
-public:
- explicit AVFMediaPlayerMetaDataControl(AVFMediaPlayerSession *session, QObject *parent = nullptr);
- virtual ~AVFMediaPlayerMetaDataControl();
-
- bool isMetaDataAvailable() const override;
- bool isWritable() const;
-
- QVariant metaData(const QString &key) const override;
- QStringList availableMetaData() const override;
-
-private Q_SLOTS:
- void updateTags();
-
-private:
- AVFMediaPlayerSession *m_session;
- QVariantMap m_tags;
- void *m_asset;
-
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFMEDIAPLAYERMETADATACONTROL_H
diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.mm
deleted file mode 100644
index b9ec38c0f..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.mm
+++ /dev/null
@@ -1,161 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfmediaplayermetadatacontrol.h"
-#include "avfmediaplayersession.h"
-
-#include <QtMultimedia/qmediametadata.h>
-
-#import <AVFoundation/AVFoundation.h>
-
-QT_USE_NAMESPACE
-
-AVFMediaPlayerMetaDataControl::AVFMediaPlayerMetaDataControl(AVFMediaPlayerSession *session, QObject *parent)
- : QMetaDataReaderControl(parent)
- , m_session(session)
- , m_asset(nullptr)
-{
- QObject::connect(m_session, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), this, SLOT(updateTags()));
-}
-
-AVFMediaPlayerMetaDataControl::~AVFMediaPlayerMetaDataControl()
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO;
-#endif
-}
-
-bool AVFMediaPlayerMetaDataControl::isMetaDataAvailable() const
-{
- return !m_tags.isEmpty();
-}
-
-bool AVFMediaPlayerMetaDataControl::isWritable() const
-{
- return false;
-}
-
-QVariant AVFMediaPlayerMetaDataControl::metaData(const QString &key) const
-{
- return m_tags.value(key);
-}
-
-QStringList AVFMediaPlayerMetaDataControl::availableMetaData() const
-{
- return m_tags.keys();
-}
-
-static QString itemKey(AVMetadataItem *item)
-{
- NSString *keyString = [item commonKey];
-
- if (keyString.length != 0) {
- if ([keyString isEqualToString:AVMetadataCommonKeyTitle]) {
- return QMediaMetaData::Title;
- } else if ([keyString isEqualToString: AVMetadataCommonKeySubject]) {
- return QMediaMetaData::SubTitle;
- } else if ([keyString isEqualToString: AVMetadataCommonKeyDescription]) {
- return QMediaMetaData::Description;
- } else if ([keyString isEqualToString: AVMetadataCommonKeyPublisher]) {
- return QMediaMetaData::Publisher;
- } else if ([keyString isEqualToString: AVMetadataCommonKeyCreationDate]) {
- return QMediaMetaData::Date;
- } else if ([keyString isEqualToString: AVMetadataCommonKeyType]) {
- return QMediaMetaData::MediaType;
- } else if ([keyString isEqualToString: AVMetadataCommonKeyLanguage]) {
- return QMediaMetaData::Language;
- } else if ([keyString isEqualToString: AVMetadataCommonKeyCopyrights]) {
- return QMediaMetaData::Copyright;
- } else if ([keyString isEqualToString: AVMetadataCommonKeyAlbumName]) {
- return QMediaMetaData::AlbumTitle;
- } else if ([keyString isEqualToString: AVMetadataCommonKeyAuthor]) {
- return QMediaMetaData::Author;
- } else if ([keyString isEqualToString: AVMetadataCommonKeyArtist]) {
- return QMediaMetaData::ContributingArtist;
- } else if ([keyString isEqualToString: AVMetadataCommonKeyArtwork]) {
- return QMediaMetaData::PosterUrl;
- }
- }
-
- return QString();
-}
-
-void AVFMediaPlayerMetaDataControl::updateTags()
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO;
-#endif
- AVAsset *currentAsset = static_cast<AVAsset*>(m_session->currentAssetHandle());
-
- //Don't read the tags from the same asset more than once
- if (currentAsset == m_asset)
- return;
-
- m_asset = currentAsset;
-
- QVariantMap oldTags = m_tags;
- //Since we've changed assets, clear old tags
- m_tags.clear();
- bool changed = false;
-
- // TODO: also process ID3, iTunes and QuickTime metadata
-
- NSArray *metadataItems = [currentAsset commonMetadata];
- for (AVMetadataItem* item in metadataItems) {
- const QString key = itemKey(item);
- if (!key.isEmpty()) {
- const QString value = QString::fromNSString([item stringValue]);
- if (!value.isNull()) {
- m_tags.insert(key, value);
- if (value != oldTags.value(key)) {
- changed = true;
- Q_EMIT metaDataChanged(key, value);
- }
- }
- }
- }
-
- if (oldTags.isEmpty() != m_tags.isEmpty()) {
- Q_EMIT metaDataAvailableChanged(!m_tags.isEmpty());
- changed = true;
- }
-
- if (changed)
- Q_EMIT metaDataChanged();
-}
diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.h b/src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.h
deleted file mode 100644
index 6a4b915cf..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.h
+++ /dev/null
@@ -1,70 +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 AVFMEDIAPLAYERSERVICE_H
-#define AVFMEDIAPLAYERSERVICE_H
-
-#include <QtMultimedia/QMediaService>
-
-QT_BEGIN_NAMESPACE
-
-class AVFMediaPlayerSession;
-class AVFMediaPlayerControl;
-class AVFMediaPlayerMetaDataControl;
-class AVFVideoOutput;
-
-class AVFMediaPlayerService : public QMediaService
-{
-public:
- explicit AVFMediaPlayerService(QObject *parent = nullptr);
- ~AVFMediaPlayerService();
-
- QMediaControl* requestControl(const char *name) override;
- void releaseControl(QMediaControl *control) override;
-
-private:
- AVFMediaPlayerSession *m_session;
- AVFMediaPlayerControl *m_control;
- QMediaControl *m_videoOutput;
- AVFMediaPlayerMetaDataControl *m_playerMetaDataControl;
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFMEDIAPLAYERSERVICE_H
diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.mm
deleted file mode 100644
index bc59fa921..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfmediaplayerservice.mm
+++ /dev/null
@@ -1,134 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfmediaplayerservice.h"
-#include "avfmediaplayersession.h"
-#include "avfmediaplayercontrol.h"
-#include "avfmediaplayermetadatacontrol.h"
-#include "avfvideooutput.h"
-#if QT_CONFIG(opengl)
-#include "avfvideorenderercontrol.h"
-#endif
-#ifndef QT_NO_WIDGETS
-# include "avfvideowidgetcontrol.h"
-#endif
-#include "avfvideowindowcontrol.h"
-
-#import <AVFoundation/AVFoundation.h>
-
-QT_USE_NAMESPACE
-
-AVFMediaPlayerService::AVFMediaPlayerService(QObject *parent)
- : QMediaService(parent)
- , m_videoOutput(nullptr)
-{
- m_session = new AVFMediaPlayerSession(this);
- m_control = new AVFMediaPlayerControl(this);
- m_control->setSession(m_session);
- m_playerMetaDataControl = new AVFMediaPlayerMetaDataControl(m_session, this);
-
- connect(m_control, SIGNAL(mediaChanged(QMediaContent)), m_playerMetaDataControl, SLOT(updateTags()));
-}
-
-AVFMediaPlayerService::~AVFMediaPlayerService()
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO;
-#endif
- delete m_session;
-}
-
-QMediaControl *AVFMediaPlayerService::requestControl(const char *name)
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO << name;
-#endif
-
- if (qstrcmp(name, QMediaPlayerControl_iid) == 0)
- return m_control;
-
- if (qstrcmp(name, QMetaDataReaderControl_iid) == 0)
- return m_playerMetaDataControl;
-
-#if QT_CONFIG(opengl)
- if (qstrcmp(name, QVideoRendererControl_iid) == 0) {
- if (!m_videoOutput)
- m_videoOutput = new AVFVideoRendererControl(this);
-
- m_session->setVideoOutput(qobject_cast<AVFVideoOutput*>(m_videoOutput));
- return m_videoOutput;
- }
-#endif
-#ifndef QT_NO_WIDGETS
- if (qstrcmp(name, QVideoWidgetControl_iid) == 0) {
- if (!m_videoOutput)
- m_videoOutput = new AVFVideoWidgetControl(this);
-
- m_session->setVideoOutput(qobject_cast<AVFVideoOutput*>(m_videoOutput));
- return m_videoOutput;
- }
-#endif
- if (qstrcmp(name, QVideoWindowControl_iid) == 0) {
- if (!m_videoOutput)
- m_videoOutput = new AVFVideoWindowControl(this);
-
- m_session->setVideoOutput(qobject_cast<AVFVideoOutput*>(m_videoOutput));
- return m_videoOutput;
- }
- return nullptr;
-}
-
-void AVFMediaPlayerService::releaseControl(QMediaControl *control)
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO << control;
-#endif
- if (m_videoOutput == control) {
-#if QT_CONFIG(opengl)
- AVFVideoRendererControl *renderControl = qobject_cast<AVFVideoRendererControl*>(m_videoOutput);
-
- if (renderControl)
- renderControl->setSurface(nullptr);
-#endif
- m_videoOutput = nullptr;
- m_session->setVideoOutput(nullptr);
-
- delete control;
- }
-}
diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayerserviceplugin.h b/src/plugins/avfoundation/mediaplayer/avfmediaplayerserviceplugin.h
deleted file mode 100644
index e08f0f46e..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfmediaplayerserviceplugin.h
+++ /dev/null
@@ -1,77 +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 AVFMEDIAPLAYERSERVICEPLUGIN_H
-#define AVFMEDIAPLAYERSERVICEPLUGIN_H
-
-#include <QtCore/qglobal.h>
-#include <QtMultimedia/qmediaserviceproviderplugin.h>
-
-QT_BEGIN_NAMESPACE
-
-class AVFMediaPlayerServicePlugin
- : public QMediaServiceProviderPlugin
- , public QMediaServiceSupportedFormatsInterface
- , public QMediaServiceFeaturesInterface
-{
- Q_OBJECT
- Q_INTERFACES(QMediaServiceSupportedFormatsInterface)
- Q_INTERFACES(QMediaServiceFeaturesInterface)
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "avfmediaplayer.json")
-
-public:
- explicit AVFMediaPlayerServicePlugin();
-
- QMediaService* create(QString const& key) override;
- void release(QMediaService *service) override;
-
- QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const override;
- QMultimedia::SupportEstimate hasSupport(const QString &mimeType, const QStringList& codecs) const override;
- QStringList supportedMimeTypes() const override;
-
-private:
- void buildSupportedTypes();
-
- QStringList m_supportedMimeTypes;
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFMEDIAPLAYERSERVICEPLUGIN_H
diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayerserviceplugin.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayerserviceplugin.mm
deleted file mode 100644
index 59b29a3d2..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfmediaplayerserviceplugin.mm
+++ /dev/null
@@ -1,107 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfmediaplayerserviceplugin.h"
-#include <QtCore/QDebug>
-
-#include "avfmediaplayerservice.h"
-
-#import <AVFoundation/AVFoundation.h>
-
-QT_USE_NAMESPACE
-
-AVFMediaPlayerServicePlugin::AVFMediaPlayerServicePlugin()
-{
- buildSupportedTypes();
-}
-
-QMediaService *AVFMediaPlayerServicePlugin::create(const QString &key)
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << "AVFMediaPlayerServicePlugin::create" << key;
-#endif
- if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER))
- return new AVFMediaPlayerService;
-
- qWarning() << "unsupported key: " << key;
- return nullptr;
-}
-
-void AVFMediaPlayerServicePlugin::release(QMediaService *service)
-{
- delete service;
-}
-
-QMediaServiceProviderHint::Features AVFMediaPlayerServicePlugin::supportedFeatures(const QByteArray &service) const
-{
- if (service == Q_MEDIASERVICE_MEDIAPLAYER)
- return QMediaServiceProviderHint::VideoSurface;
- else
- return QMediaServiceProviderHint::Features();
-}
-
-QMultimedia::SupportEstimate AVFMediaPlayerServicePlugin::hasSupport(const QString &mimeType, const QStringList &codecs) const
-{
- Q_UNUSED(codecs);
-
- if (m_supportedMimeTypes.contains(mimeType))
- return QMultimedia::ProbablySupported;
-
- return QMultimedia::MaybeSupported;
-}
-
-QStringList AVFMediaPlayerServicePlugin::supportedMimeTypes() const
-{
- return m_supportedMimeTypes;
-}
-
-void AVFMediaPlayerServicePlugin::buildSupportedTypes()
-{
- //Populate m_supportedMimeTypes with mimetypes AVAsset supports
- NSArray *mimeTypes = [AVURLAsset audiovisualMIMETypes];
- for (NSString *mimeType in mimeTypes)
- {
- m_supportedMimeTypes.append(QString::fromUtf8([mimeType UTF8String]));
- }
-#ifdef QT_DEBUG_AVF
- qDebug() << "AVFMediaPlayerServicePlugin::buildSupportedTypes";
- qDebug() << "Supported Types: " << m_supportedMimeTypes;
-#endif
-
-}
diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h
deleted file mode 100644
index db29e88aa..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h
+++ /dev/null
@@ -1,161 +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 AVFMEDIAPLAYERSESSION_H
-#define AVFMEDIAPLAYERSESSION_H
-
-#include <QtCore/QObject>
-#include <QtCore/QByteArray>
-#include <QtCore/QSet>
-#include <QtCore/QResource>
-
-#include <QtMultimedia/QMediaPlayerControl>
-#include <QtMultimedia/QMediaPlayer>
-
-QT_BEGIN_NAMESPACE
-
-class AVFMediaPlayerService;
-class AVFVideoOutput;
-
-class AVFMediaPlayerSession : public QObject
-{
- Q_OBJECT
-public:
- AVFMediaPlayerSession(AVFMediaPlayerService *service, QObject *parent = nullptr);
- virtual ~AVFMediaPlayerSession();
-
- void setVideoOutput(AVFVideoOutput *output);
- void *currentAssetHandle();
-
- QMediaPlayer::State state() const;
- QMediaPlayer::MediaStatus mediaStatus() const;
-
- QMediaContent media() const;
- QIODevice *mediaStream() const;
- void setMedia(const QMediaContent &content, QIODevice *stream);
-
- qint64 position() const;
- qint64 duration() const;
-
- int bufferStatus() const;
-
- int volume() const;
- bool isMuted() const;
-
- bool isAudioAvailable() const;
- bool isVideoAvailable() const;
-
- bool isSeekable() const;
- QMediaTimeRange availablePlaybackRanges() const;
-
- qreal playbackRate() const;
-
-public Q_SLOTS:
- void setPlaybackRate(qreal rate);
-
- void setPosition(qint64 pos);
-
- void play();
- void pause();
- void stop();
-
- void setVolume(int volume);
- void setMuted(bool muted);
-
- void processEOS();
- void processLoadStateChange(QMediaPlayer::State newState);
- void processPositionChange();
- void processMediaLoadError();
-
- void processLoadStateChange();
- void processLoadStateFailure();
-
- void processBufferStateChange(int bufferStatus);
-
- void processDurationChange(qint64 duration);
-
- void streamReady();
- void streamDestroyed();
-
-Q_SIGNALS:
- void positionChanged(qint64 position);
- void durationChanged(qint64 duration);
- void stateChanged(QMediaPlayer::State newState);
- void bufferStatusChanged(int bufferStatus);
- void mediaStatusChanged(QMediaPlayer::MediaStatus status);
- void volumeChanged(int volume);
- void mutedChanged(bool muted);
- void audioAvailableChanged(bool audioAvailable);
- void videoAvailableChanged(bool videoAvailable);
- void playbackRateChanged(qreal rate);
- void seekableChanged(bool seekable);
- void error(int error, const QString &errorString);
-
-private:
- void setAudioAvailable(bool available);
- void setVideoAvailable(bool available);
- void setSeekable(bool seekable);
- void resetStream(QIODevice *stream = nullptr);
-
- AVFMediaPlayerService *m_service;
- AVFVideoOutput *m_videoOutput;
-
- QMediaPlayer::State m_state;
- QMediaPlayer::MediaStatus m_mediaStatus;
- QIODevice *m_mediaStream;
- QMediaContent m_resources;
-
- bool m_muted;
- bool m_tryingAsync;
- int m_volume;
- qreal m_rate;
- qint64 m_requestedPosition;
-
- qint64 m_duration;
- int m_bufferStatus;
- bool m_videoAvailable;
- bool m_audioAvailable;
- bool m_seekable;
-
- void *m_observer;
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFMEDIAPLAYERSESSION_H
diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm
deleted file mode 100644
index 95121f58a..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm
+++ /dev/null
@@ -1,1067 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfmediaplayersession.h"
-#include "avfmediaplayerservice.h"
-#include "avfvideooutput.h"
-
-#include <qpointer.h>
-#include <QFileInfo>
-
-#import <AVFoundation/AVFoundation.h>
-
-QT_USE_NAMESPACE
-
-//AVAsset Keys
-static NSString* const AVF_TRACKS_KEY = @"tracks";
-static NSString* const AVF_PLAYABLE_KEY = @"playable";
-
-//AVPlayerItem keys
-static NSString* const AVF_STATUS_KEY = @"status";
-static NSString* const AVF_BUFFER_LIKELY_KEEP_UP_KEY = @"playbackLikelyToKeepUp";
-
-//AVPlayer keys
-static NSString* const AVF_RATE_KEY = @"rate";
-static NSString* const AVF_CURRENT_ITEM_KEY = @"currentItem";
-static NSString* const AVF_CURRENT_ITEM_DURATION_KEY = @"currentItem.duration";
-
-static void *AVFMediaPlayerSessionObserverRateObservationContext = &AVFMediaPlayerSessionObserverRateObservationContext;
-static void *AVFMediaPlayerSessionObserverStatusObservationContext = &AVFMediaPlayerSessionObserverStatusObservationContext;
-static void *AVFMediaPlayerSessionObserverBufferLikelyToKeepUpContext = &AVFMediaPlayerSessionObserverBufferLikelyToKeepUpContext;
-static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMediaPlayerSessionObserverCurrentItemObservationContext;
-static void *AVFMediaPlayerSessionObserverCurrentItemDurationObservationContext = &AVFMediaPlayerSessionObserverCurrentItemDurationObservationContext;
-
-@interface AVFMediaPlayerSessionObserver : NSObject<AVAssetResourceLoaderDelegate>
-
-@property (readonly, getter=player) AVPlayer* m_player;
-@property (readonly, getter=playerItem) AVPlayerItem* m_playerItem;
-@property (readonly, getter=playerLayer) AVPlayerLayer* m_playerLayer;
-@property (readonly, getter=session) AVFMediaPlayerSession* m_session;
-
-- (AVFMediaPlayerSessionObserver *) initWithMediaPlayerSession:(AVFMediaPlayerSession *)session;
-- (void) setURL:(NSURL *)url mimeType:(NSString *)mimeType;
-- (void) unloadMedia;
-- (void) prepareToPlayAsset:(AVURLAsset *)asset withKeys:(NSArray *)requestedKeys;
-- (void) assetFailedToPrepareForPlayback:(NSError *)error;
-- (void) playerItemDidReachEnd:(NSNotification *)notification;
-- (void) playerItemTimeJumped:(NSNotification *)notification;
-- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
- change:(NSDictionary *)change context:(void *)context;
-- (void) detatchSession;
-- (void) dealloc;
-- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest;
-@end
-
-@implementation AVFMediaPlayerSessionObserver
-{
-@private
- AVFMediaPlayerSession *m_session;
- AVPlayer *m_player;
- AVPlayerItem *m_playerItem;
- AVPlayerLayer *m_playerLayer;
- NSURL *m_URL;
- BOOL m_bufferIsLikelyToKeepUp;
- NSData *m_data;
- NSString *m_mimeType;
-}
-
-@synthesize m_player, m_playerItem, m_playerLayer, m_session;
-
-- (AVFMediaPlayerSessionObserver *) initWithMediaPlayerSession:(AVFMediaPlayerSession *)session
-{
- if (!(self = [super init]))
- return nil;
-
- self->m_session = session;
- self->m_bufferIsLikelyToKeepUp = FALSE;
- return self;
-}
-
-- (void) setURL:(NSURL *)url mimeType:(NSString *)mimeType
-{
- [m_mimeType release];
- m_mimeType = [mimeType retain];
-
- if (m_URL != url)
- {
- [m_URL release];
- m_URL = [url copy];
-
- //Create an asset for inspection of a resource referenced by a given URL.
- //Load the values for the asset keys "tracks", "playable".
-
- // use __block to avoid maintaining strong references on variables captured by the
- // following block callback
- __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 AVFMediaPlayerSessionObserver *blockSelf = self;
- QPointer<AVFMediaPlayerSession> session(m_session);
-
- // 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];
- [asset release];
- [requestedKeys release];
- });
- }];
- }
-}
-
-- (void) unloadMedia
-{
- if (m_playerItem) {
- [m_playerItem removeObserver:self forKeyPath:AVF_STATUS_KEY];
- [m_playerItem removeObserver:self forKeyPath:AVF_BUFFER_LIKELY_KEEP_UP_KEY];
-
- [[NSNotificationCenter defaultCenter] removeObserver:self
- name:AVPlayerItemDidPlayToEndTimeNotification
- object:m_playerItem];
- [[NSNotificationCenter defaultCenter] removeObserver:self
- name:AVPlayerItemTimeJumpedNotification
- object:m_playerItem];
- m_playerItem = 0;
- }
- if (m_player) {
- [m_player setRate:0.0];
- [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_DURATION_KEY];
- [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_KEY];
- [m_player removeObserver:self forKeyPath:AVF_RATE_KEY];
- [m_player release];
- m_player = 0;
- }
- if (m_playerLayer) {
- [m_playerLayer release];
- m_playerLayer = 0;
- }
-#if defined(Q_OS_IOS)
- [[AVAudioSession sharedInstance] setActive:NO error:nil];
-#endif
-}
-
-- (void) prepareToPlayAsset:(AVURLAsset *)asset
- withKeys:(NSArray *)requestedKeys
-{
- //Make sure that the value of each key has loaded successfully.
- for (NSString *thisKey in requestedKeys)
- {
- NSError *error = nil;
- AVKeyValueStatus keyStatus = [asset statusOfValueForKey:thisKey error:&error];
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO << [thisKey UTF8String] << " status: " << keyStatus;
-#endif
- if (keyStatus == AVKeyValueStatusFailed)
- {
- [self assetFailedToPrepareForPlayback:error];
- return;
- }
- }
-
- //Use the AVAsset playable property to detect whether the asset can be played.
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO << "isPlayable: " << [asset isPlayable];
-#endif
- if (!asset.playable)
- {
- //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");
- NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys:
- localizedDescription, NSLocalizedDescriptionKey,
- localizedFailureReason, NSLocalizedFailureReasonErrorKey,
- nil];
- 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
- options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
- context:AVFMediaPlayerSessionObserverStatusObservationContext];
-
- [m_playerItem addObserver:self
- forKeyPath:AVF_BUFFER_LIKELY_KEEP_UP_KEY
- options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
- context:AVFMediaPlayerSessionObserverBufferLikelyToKeepUpContext];
-
- //When the player item has played to its end time we'll toggle
- //the movie controller Pause button to be the Play button
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(playerItemDidReachEnd:)
- name:AVPlayerItemDidPlayToEndTimeNotification
- object:m_playerItem];
-
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(playerItemTimeJumped:)
- name:AVPlayerItemTimeJumpedNotification
- object:m_playerItem];
-
- //Get a new AVPlayer initialized to play the specified player item.
- m_player = [AVPlayer playerWithPlayerItem:m_playerItem];
- [m_player retain];
-
- //Set the initial volume on new player object
- if (self.session) {
- [m_player setVolume:m_session->volume() / 100.0f];
- [m_player setMuted:m_session->isMuted()];
- }
-
- //Create a new player layer if we don't have one already
- if (!m_playerLayer)
- {
- m_playerLayer = [AVPlayerLayer playerLayerWithPlayer:m_player];
- [m_playerLayer retain];
- m_playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
- m_playerLayer.anchorPoint = CGPointMake(0.0f, 0.0f);
- }
-
- //Observe the AVPlayer "currentItem" property to find out when any
- //AVPlayer replaceCurrentItemWithPlayerItem: replacement will/did
- //occur.
- [m_player addObserver:self
- forKeyPath:AVF_CURRENT_ITEM_KEY
- options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
- context:AVFMediaPlayerSessionObserverCurrentItemObservationContext];
-
- //Observe the AVPlayer "rate" property to update the scrubber control.
- [m_player addObserver:self
- forKeyPath:AVF_RATE_KEY
- options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
- context:AVFMediaPlayerSessionObserverRateObservationContext];
-
- //Observe the duration for getting the buffer state
- [m_player addObserver:self
- forKeyPath:AVF_CURRENT_ITEM_DURATION_KEY
- options:0
- context:AVFMediaPlayerSessionObserverCurrentItemDurationObservationContext];
-#if defined(Q_OS_IOS)
- [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
- [[AVAudioSession sharedInstance] setActive:YES error:nil];
-#endif
-}
-
--(void) assetFailedToPrepareForPlayback:(NSError *)error
-{
- Q_UNUSED(error);
- QMetaObject::invokeMethod(m_session, "processMediaLoadError", Qt::AutoConnection);
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO;
- qDebug() << [[error localizedDescription] UTF8String];
- qDebug() << [[error localizedFailureReason] UTF8String];
- qDebug() << [[error localizedRecoverySuggestion] UTF8String];
-#endif
-}
-
-- (void) playerItemDidReachEnd:(NSNotification *)notification
-{
- Q_UNUSED(notification);
- if (self.session)
- QMetaObject::invokeMethod(m_session, "processEOS", Qt::AutoConnection);
-}
-
-- (void) playerItemTimeJumped:(NSNotification *)notification
-{
- Q_UNUSED(notification);
- if (self.session)
- QMetaObject::invokeMethod(m_session, "processPositionChange", Qt::AutoConnection);
-}
-
-- (void) observeValueForKeyPath:(NSString*) path
- ofObject:(id)object
- change:(NSDictionary*)change
- context:(void*)context
-{
- //AVPlayerItem "status" property value observer.
- if (context == AVFMediaPlayerSessionObserverStatusObservationContext)
- {
- AVPlayerStatus status = (AVPlayerStatus)[[change objectForKey:NSKeyValueChangeNewKey] integerValue];
- switch (status)
- {
- //Indicates that the status of the player is not yet known because
- //it has not tried to load new media resources for playback
- case AVPlayerStatusUnknown:
- {
- //QMetaObject::invokeMethod(m_session, "processLoadStateChange", Qt::AutoConnection);
- }
- break;
-
- case AVPlayerStatusReadyToPlay:
- {
- //Once the AVPlayerItem becomes ready to play, i.e.
- //[playerItem status] == AVPlayerItemStatusReadyToPlay,
- //its duration can be fetched from the item.
- if (self.session)
- QMetaObject::invokeMethod(m_session, "processLoadStateChange", Qt::AutoConnection);
- }
- break;
-
- case AVPlayerStatusFailed:
- {
- AVPlayerItem *playerItem = static_cast<AVPlayerItem*>(object);
- [self assetFailedToPrepareForPlayback:playerItem.error];
-
- if (self.session)
- QMetaObject::invokeMethod(m_session, "processLoadStateFailure", Qt::AutoConnection);
- }
- break;
- }
- }
- else if (context == AVFMediaPlayerSessionObserverBufferLikelyToKeepUpContext)
- {
- const bool isPlaybackLikelyToKeepUp = [m_playerItem isPlaybackLikelyToKeepUp];
- if (isPlaybackLikelyToKeepUp != m_bufferIsLikelyToKeepUp) {
- m_bufferIsLikelyToKeepUp = isPlaybackLikelyToKeepUp;
- QMetaObject::invokeMethod(m_session, "processBufferStateChange", Qt::AutoConnection,
- Q_ARG(int, isPlaybackLikelyToKeepUp ? 100 : 0));
- }
- }
- //AVPlayer "rate" property value observer.
- else if (context == AVFMediaPlayerSessionObserverRateObservationContext)
- {
- //QMetaObject::invokeMethod(m_session, "setPlaybackRate", Qt::AutoConnection, Q_ARG(qreal, [m_player rate]));
- }
- //AVPlayer "currentItem" property observer.
- //Called when the AVPlayer replaceCurrentItemWithPlayerItem:
- //replacement will/did occur.
- else if (context == AVFMediaPlayerSessionObserverCurrentItemObservationContext)
- {
- AVPlayerItem *newPlayerItem = [change objectForKey:NSKeyValueChangeNewKey];
- if (m_playerItem != newPlayerItem)
- m_playerItem = newPlayerItem;
- }
- else if (context == AVFMediaPlayerSessionObserverCurrentItemDurationObservationContext)
- {
- const CMTime time = [m_playerItem duration];
- const qint64 duration = static_cast<qint64>(float(time.value) / float(time.timescale) * 1000.0f);
- if (self.session)
- QMetaObject::invokeMethod(m_session, "processDurationChange", Qt::AutoConnection, Q_ARG(qint64, duration));
- }
- else
- {
- [super observeValueForKeyPath:path ofObject:object change:change context:context];
- }
-}
-
-- (void) detatchSession
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO;
-#endif
- m_session = 0;
-}
-
-- (void) dealloc
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO;
-#endif
- [self unloadMedia];
-
- if (m_URL) {
- [m_URL release];
- }
-
- [m_mimeType release];
- [super dealloc];
-}
-
-- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
-{
- Q_UNUSED(resourceLoader);
-
- if (![loadingRequest.request.URL.scheme isEqualToString:@"iodevice"])
- return NO;
-
- QIODevice *device = m_session->mediaStream();
- if (!device)
- return NO;
-
- device->seek(loadingRequest.dataRequest.requestedOffset);
- if (loadingRequest.contentInformationRequest) {
- loadingRequest.contentInformationRequest.contentType = m_mimeType;
- loadingRequest.contentInformationRequest.contentLength = device->size();
- loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
- }
-
- if (loadingRequest.dataRequest) {
- NSInteger requestedLength = loadingRequest.dataRequest.requestedLength;
- int maxBytes = qMin(32 * 1064, int(requestedLength));
- char buffer[maxBytes];
- NSInteger submitted = 0;
- while (submitted < requestedLength) {
- qint64 len = device->read(buffer, maxBytes);
- if (len < 1)
- break;
-
- [loadingRequest.dataRequest respondWithData:[NSData dataWithBytes:buffer length:len]];
- submitted += len;
- }
-
- // Finish loading even if not all bytes submitted.
- [loadingRequest finishLoading];
- }
-
- return YES;
-}
-@end
-
-AVFMediaPlayerSession::AVFMediaPlayerSession(AVFMediaPlayerService *service, QObject *parent)
- : QObject(parent)
- , m_service(service)
- , m_videoOutput(nullptr)
- , m_state(QMediaPlayer::StoppedState)
- , m_mediaStatus(QMediaPlayer::NoMedia)
- , m_mediaStream(nullptr)
- , m_muted(false)
- , m_tryingAsync(false)
- , m_volume(100)
- , m_rate(1.0)
- , m_requestedPosition(-1)
- , m_duration(0)
- , m_bufferStatus(0)
- , m_videoAvailable(false)
- , m_audioAvailable(false)
- , m_seekable(false)
-{
- m_observer = [[AVFMediaPlayerSessionObserver alloc] initWithMediaPlayerSession:this];
-}
-
-AVFMediaPlayerSession::~AVFMediaPlayerSession()
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO;
-#endif
- //Detatch the session from the sessionObserver (which could still be alive trying to communicate with this session).
- [static_cast<AVFMediaPlayerSessionObserver*>(m_observer) detatchSession];
- [static_cast<AVFMediaPlayerSessionObserver*>(m_observer) release];
-}
-
-void AVFMediaPlayerSession::setVideoOutput(AVFVideoOutput *output)
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO << output;
-#endif
-
- if (m_videoOutput == output)
- return;
-
- //Set the current output layer to null to stop rendering
- if (m_videoOutput) {
- m_videoOutput->setLayer(nullptr);
- }
-
- m_videoOutput = output;
-
- if (m_videoOutput && m_state != QMediaPlayer::StoppedState)
- m_videoOutput->setLayer([static_cast<AVFMediaPlayerSessionObserver*>(m_observer) playerLayer]);
-}
-
-void *AVFMediaPlayerSession::currentAssetHandle()
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO;
-#endif
- AVAsset *currentAsset = [[static_cast<AVFMediaPlayerSessionObserver*>(m_observer) playerItem] asset];
- return currentAsset;
-}
-
-QMediaPlayer::State AVFMediaPlayerSession::state() const
-{
- return m_state;
-}
-
-QMediaPlayer::MediaStatus AVFMediaPlayerSession::mediaStatus() const
-{
- return m_mediaStatus;
-}
-
-QMediaContent AVFMediaPlayerSession::media() const
-{
- return m_resources;
-}
-
-QIODevice *AVFMediaPlayerSession::mediaStream() const
-{
- return m_mediaStream;
-}
-
-static void setURL(void *observer, const QByteArray &url, const QString &mimeType = QString())
-{
- NSString *urlString = [NSString stringWithUTF8String:url.constData()];
- NSURL *nsurl = [NSURL URLWithString:urlString];
- [static_cast<AVFMediaPlayerSessionObserver*>(observer) setURL:nsurl mimeType:[NSString stringWithUTF8String:mimeType.toLatin1().constData()]];
-}
-
-static void setStreamURL(void *observer, const QByteArray &url)
-{
- setURL(observer, QByteArrayLiteral("iodevice://") + url, QFileInfo(url).suffix());
-}
-
-void AVFMediaPlayerSession::setMedia(const QMediaContent &content, QIODevice *stream)
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO << content.request().url();
-#endif
-
- [static_cast<AVFMediaPlayerSessionObserver*>(m_observer) unloadMedia];
-
- m_resources = content;
- resetStream(stream);
-
- setAudioAvailable(false);
- setVideoAvailable(false);
- setSeekable(false);
- m_requestedPosition = -1;
- Q_EMIT positionChanged(position());
-
- const QMediaPlayer::MediaStatus oldMediaStatus = m_mediaStatus;
- const QMediaPlayer::State oldState = m_state;
-
- if (!m_mediaStream && (content.isNull() || content.request().url().isEmpty())) {
- m_mediaStatus = QMediaPlayer::NoMedia;
- if (m_mediaStatus != oldMediaStatus)
- Q_EMIT mediaStatusChanged(m_mediaStatus);
-
- m_state = QMediaPlayer::StoppedState;
- if (m_state != oldState)
- Q_EMIT stateChanged(m_state);
-
- return;
- }
-
- m_mediaStatus = QMediaPlayer::LoadingMedia;
- if (m_mediaStatus != oldMediaStatus)
- Q_EMIT mediaStatusChanged(m_mediaStatus);
-
- if (m_mediaStream) {
- // If there is a data, try to load it,
- // otherwise wait for readyRead.
- if (m_mediaStream->size())
- setStreamURL(m_observer, m_resources.request().url().toEncoded());
- } else {
- //Load AVURLAsset
- //initialize asset using content's URL
- setURL(m_observer, m_resources.request().url().toEncoded());
- }
-
- m_state = QMediaPlayer::StoppedState;
- if (m_state != oldState)
- Q_EMIT stateChanged(m_state);
-}
-
-qint64 AVFMediaPlayerSession::position() const
-{
- AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerSessionObserver*>(m_observer) playerItem];
-
- if (!playerItem)
- return m_requestedPosition != -1 ? m_requestedPosition : 0;
-
- CMTime time = [playerItem currentTime];
- return static_cast<quint64>(float(time.value) / float(time.timescale) * 1000.0f);
-}
-
-qint64 AVFMediaPlayerSession::duration() const
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO;
-#endif
- return m_duration;
-}
-
-int AVFMediaPlayerSession::bufferStatus() const
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO;
-#endif
- return m_bufferStatus;
-}
-
-int AVFMediaPlayerSession::volume() const
-{
- return m_volume;
-}
-
-bool AVFMediaPlayerSession::isMuted() const
-{
- return m_muted;
-}
-
-void AVFMediaPlayerSession::setAudioAvailable(bool available)
-{
- if (m_audioAvailable == available)
- return;
-
- m_audioAvailable = available;
- Q_EMIT audioAvailableChanged(available);
-}
-
-bool AVFMediaPlayerSession::isAudioAvailable() const
-{
- return m_audioAvailable;
-}
-
-void AVFMediaPlayerSession::setVideoAvailable(bool available)
-{
- if (m_videoAvailable == available)
- return;
-
- m_videoAvailable = available;
- Q_EMIT videoAvailableChanged(available);
-}
-
-bool AVFMediaPlayerSession::isVideoAvailable() const
-{
- return m_videoAvailable;
-}
-
-bool AVFMediaPlayerSession::isSeekable() const
-{
- return m_seekable;
-}
-
-void AVFMediaPlayerSession::setSeekable(bool seekable)
-{
- if (m_seekable == seekable)
- return;
-
- m_seekable = seekable;
- Q_EMIT seekableChanged(seekable);
-}
-
-QMediaTimeRange AVFMediaPlayerSession::availablePlaybackRanges() const
-{
- AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerSessionObserver*>(m_observer) playerItem];
-
- if (playerItem) {
- QMediaTimeRange timeRanges;
-
- NSArray *ranges = [playerItem loadedTimeRanges];
- for (NSValue *timeRange in ranges) {
- CMTimeRange currentTimeRange = [timeRange CMTimeRangeValue];
- qint64 startTime = qint64(float(currentTimeRange.start.value) / currentTimeRange.start.timescale * 1000.0);
- timeRanges.addInterval(startTime, startTime + qint64(float(currentTimeRange.duration.value) / currentTimeRange.duration.timescale * 1000.0));
- }
- if (!timeRanges.isEmpty())
- return timeRanges;
- }
- return QMediaTimeRange(0, duration());
-}
-
-qreal AVFMediaPlayerSession::playbackRate() const
-{
- return m_rate;
-}
-
-void AVFMediaPlayerSession::setPlaybackRate(qreal rate)
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO << rate;
-#endif
-
- if (qFuzzyCompare(m_rate, rate))
- return;
-
- m_rate = rate;
-
- AVPlayer *player = [static_cast<AVFMediaPlayerSessionObserver*>(m_observer) player];
- if (player && m_state == QMediaPlayer::PlayingState)
- [player setRate:m_rate];
-
- Q_EMIT playbackRateChanged(m_rate);
-}
-
-void AVFMediaPlayerSession::setPosition(qint64 pos)
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO << pos;
-#endif
-
- if (pos == position())
- return;
-
- AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerSessionObserver*>(m_observer) playerItem];
- if (!playerItem) {
- m_requestedPosition = pos;
- Q_EMIT positionChanged(m_requestedPosition);
- return;
- }
-
- if (!isSeekable()) {
- if (m_requestedPosition != -1) {
- m_requestedPosition = -1;
- Q_EMIT positionChanged(position());
- }
- return;
- }
-
- pos = qMax(qint64(0), pos);
- if (duration() > 0)
- pos = qMin(pos, duration());
-
- CMTime newTime = [playerItem currentTime];
- newTime.value = (pos / 1000.0f) * newTime.timescale;
- [playerItem seekToTime:newTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:nil];
-
- Q_EMIT positionChanged(pos);
-
- // Reset media status if the current status is EndOfMedia
- if (m_mediaStatus == QMediaPlayer::EndOfMedia) {
- QMediaPlayer::MediaStatus newMediaStatus = (m_state == QMediaPlayer::PausedState) ? QMediaPlayer::BufferedMedia
- : QMediaPlayer::LoadedMedia;
- Q_EMIT mediaStatusChanged((m_mediaStatus = newMediaStatus));
- }
-}
-
-void AVFMediaPlayerSession::play()
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO << "currently: " << m_state;
-#endif
-
- if (m_mediaStatus == QMediaPlayer::NoMedia || m_mediaStatus == QMediaPlayer::InvalidMedia)
- return;
-
- if (m_state == QMediaPlayer::PlayingState)
- return;
-
- if (m_videoOutput) {
- m_videoOutput->setLayer([static_cast<AVFMediaPlayerSessionObserver*>(m_observer) playerLayer]);
- }
-
- // Reset media status if the current status is EndOfMedia
- if (m_mediaStatus == QMediaPlayer::EndOfMedia)
- setPosition(0);
-
- if (m_mediaStatus == QMediaPlayer::LoadedMedia || m_mediaStatus == QMediaPlayer::BufferedMedia) {
- // Setting the rate starts playback
- [[static_cast<AVFMediaPlayerSessionObserver*>(m_observer) player] setRate:m_rate];
- }
-
- m_state = QMediaPlayer::PlayingState;
- processLoadStateChange();
-
- Q_EMIT stateChanged(m_state);
-}
-
-void AVFMediaPlayerSession::pause()
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO << "currently: " << m_state;
-#endif
-
- if (m_mediaStatus == QMediaPlayer::NoMedia)
- return;
-
- if (m_state == QMediaPlayer::PausedState)
- return;
-
- m_state = QMediaPlayer::PausedState;
-
- if (m_videoOutput) {
- m_videoOutput->setLayer([static_cast<AVFMediaPlayerSessionObserver*>(m_observer) playerLayer]);
- }
-
- [[static_cast<AVFMediaPlayerSessionObserver*>(m_observer) player] pause];
-
- // Reset media status if the current status is EndOfMedia
- if (m_mediaStatus == QMediaPlayer::EndOfMedia)
- setPosition(0);
-
-
- Q_EMIT stateChanged(m_state);
-}
-
-void AVFMediaPlayerSession::stop()
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO << "currently: " << m_state;
-#endif
-
- if (m_state == QMediaPlayer::StoppedState)
- return;
-
- // AVPlayer doesn't have stop(), only pause() and play().
- [[static_cast<AVFMediaPlayerSessionObserver*>(m_observer) player] pause];
- setPosition(0);
-
- if (m_videoOutput) {
- m_videoOutput->setLayer(nullptr);
- }
-
- if (m_mediaStatus == QMediaPlayer::BufferedMedia)
- Q_EMIT mediaStatusChanged((m_mediaStatus = QMediaPlayer::LoadedMedia));
-
- Q_EMIT positionChanged(position());
- Q_EMIT stateChanged((m_state = QMediaPlayer::StoppedState));
-}
-
-void AVFMediaPlayerSession::setVolume(int volume)
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO << volume;
-#endif
-
- if (m_volume == volume)
- return;
-
- m_volume = volume;
-
- AVPlayer *player = [static_cast<AVFMediaPlayerSessionObserver*>(m_observer) player];
- if (player)
- [player setVolume:volume / 100.0f];
-
- Q_EMIT volumeChanged(m_volume);
-}
-
-void AVFMediaPlayerSession::setMuted(bool muted)
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO << muted;
-#endif
-
- if (m_muted == muted)
- return;
-
- m_muted = muted;
-
- AVPlayer *player = [static_cast<AVFMediaPlayerSessionObserver*>(m_observer) player];
- if (player)
- [player setMuted:muted];
-
- Q_EMIT mutedChanged(muted);
-}
-
-void AVFMediaPlayerSession::processEOS()
-{
- //AVPlayerItem has reached end of track/stream
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO;
-#endif
- Q_EMIT positionChanged(position());
- m_mediaStatus = QMediaPlayer::EndOfMedia;
- m_state = QMediaPlayer::StoppedState;
-
- // At this point, frames should not be rendered anymore.
- // Clear the output layer to make sure of that.
- if (m_videoOutput)
- m_videoOutput->setLayer(nullptr);
-
- Q_EMIT mediaStatusChanged(m_mediaStatus);
- Q_EMIT stateChanged(m_state);
-}
-
-void AVFMediaPlayerSession::processLoadStateChange(QMediaPlayer::State newState)
-{
- AVPlayerStatus currentStatus = [[static_cast<AVFMediaPlayerSessionObserver*>(m_observer) player] status];
-
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO << currentStatus << ", " << m_mediaStatus << ", " << newState;
-#endif
-
- if (m_mediaStatus == QMediaPlayer::NoMedia)
- return;
-
- if (currentStatus == AVPlayerStatusReadyToPlay) {
-
- QMediaPlayer::MediaStatus newStatus = m_mediaStatus;
-
- AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerSessionObserver*>(m_observer) playerItem];
-
- if (playerItem) {
- // Check each track for audio and video content
- AVAssetTrack *videoTrack = nil;
- NSArray *tracks = playerItem.tracks;
- for (AVPlayerItemTrack *track in tracks) {
- AVAssetTrack *assetTrack = track.assetTrack;
- if (assetTrack) {
- if ([assetTrack.mediaType isEqualToString:AVMediaTypeAudio])
- setAudioAvailable(true);
- if ([assetTrack.mediaType isEqualToString:AVMediaTypeVideo]) {
- setVideoAvailable(true);
- if (!videoTrack)
- videoTrack = assetTrack;
- }
- }
- }
-
- setSeekable([[playerItem seekableTimeRanges] count] > 0);
-
- // Get the native size of the video, and reset the bounds of the player layer
- AVPlayerLayer *playerLayer = [static_cast<AVFMediaPlayerSessionObserver*>(m_observer) playerLayer];
- if (videoTrack && playerLayer) {
- if (!playerLayer.bounds.size.width || !playerLayer.bounds.size.height) {
- playerLayer.bounds = CGRectMake(0.0f, 0.0f,
- videoTrack.naturalSize.width,
- videoTrack.naturalSize.height);
- }
-
- if (m_videoOutput && newState != QMediaPlayer::StoppedState) {
- m_videoOutput->setLayer(playerLayer);
- }
- }
-
- if (m_requestedPosition != -1) {
- setPosition(m_requestedPosition);
- m_requestedPosition = -1;
- }
- }
-
- newStatus = (newState != QMediaPlayer::StoppedState) ? QMediaPlayer::BufferedMedia
- : QMediaPlayer::LoadedMedia;
-
- if (newStatus != m_mediaStatus)
- Q_EMIT mediaStatusChanged((m_mediaStatus = newStatus));
-
- }
-
- if (newState == QMediaPlayer::PlayingState && [static_cast<AVFMediaPlayerSessionObserver*>(m_observer) player]) {
- // Setting the rate is enough to start playback, no need to call play()
- [[static_cast<AVFMediaPlayerSessionObserver*>(m_observer) player] setRate:m_rate];
- }
-}
-
-
-void AVFMediaPlayerSession::processLoadStateChange()
-{
- processLoadStateChange(m_state);
-}
-
-
-void AVFMediaPlayerSession::processLoadStateFailure()
-{
- Q_EMIT stateChanged((m_state = QMediaPlayer::StoppedState));
-}
-
-void AVFMediaPlayerSession::processBufferStateChange(int bufferStatus)
-{
- if (bufferStatus == m_bufferStatus)
- return;
-
- auto status = m_mediaStatus;
- // Buffered -> unbuffered.
- if (!bufferStatus) {
- status = QMediaPlayer::StalledMedia;
- } else if (status == QMediaPlayer::StalledMedia) {
- status = QMediaPlayer::BufferedMedia;
- // Resume playback.
- if (m_state == QMediaPlayer::PlayingState)
- [[static_cast<AVFMediaPlayerSessionObserver*>(m_observer) player] setRate:m_rate];
- }
-
- if (m_mediaStatus != status)
- Q_EMIT mediaStatusChanged(m_mediaStatus = status);
-
- m_bufferStatus = bufferStatus;
- Q_EMIT bufferStatusChanged(bufferStatus);
-}
-
-void AVFMediaPlayerSession::processDurationChange(qint64 duration)
-{
- if (duration == m_duration)
- return;
-
- m_duration = duration;
- Q_EMIT durationChanged(duration);
-}
-
-void AVFMediaPlayerSession::processPositionChange()
-{
- if (m_state == QMediaPlayer::StoppedState)
- return;
-
- Q_EMIT positionChanged(position());
-}
-
-void AVFMediaPlayerSession::processMediaLoadError()
-{
- if (m_requestedPosition != -1) {
- m_requestedPosition = -1;
- Q_EMIT positionChanged(position());
- }
-
- Q_EMIT mediaStatusChanged((m_mediaStatus = QMediaPlayer::InvalidMedia));
-
- Q_EMIT error(QMediaPlayer::FormatError, tr("Failed to load media"));
-}
-
-void AVFMediaPlayerSession::streamReady()
-{
- setStreamURL(m_observer, m_resources.request().url().toEncoded());
-}
-
-void AVFMediaPlayerSession::streamDestroyed()
-{
- resetStream(nullptr);
-}
-
-void AVFMediaPlayerSession::resetStream(QIODevice *stream)
-{
- if (m_mediaStream) {
- disconnect(m_mediaStream, &QIODevice::readyRead, this, &AVFMediaPlayerSession::streamReady);
- disconnect(m_mediaStream, &QIODevice::destroyed, this, &AVFMediaPlayerSession::streamDestroyed);
- }
-
- m_mediaStream = stream;
-
- if (m_mediaStream) {
- connect(m_mediaStream, &QIODevice::readyRead, this, &AVFMediaPlayerSession::streamReady);
- connect(m_mediaStream, &QIODevice::destroyed, this, &AVFMediaPlayerSession::streamDestroyed);
- }
-}
diff --git a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.h b/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.h
deleted file mode 100644
index d4f74964a..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.h
+++ /dev/null
@@ -1,92 +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 AVFVIDEOFRAMERENDERER_H
-#define AVFVIDEOFRAMERENDERER_H
-
-#include <QtCore/QObject>
-#include <QtGui/QImage>
-#include <QtGui/QOpenGLContext>
-#include <QtCore/QSize>
-
-#import "Metal/Metal.h"
-#import "MetalKit/MetalKit.h"
-
-@class CARenderer;
-@class AVPlayerLayer;
-
-QT_BEGIN_NAMESPACE
-
-class QOpenGLFramebufferObject;
-class QWindow;
-class QOpenGLContext;
-class QAbstractVideoSurface;
-
-class AVFVideoFrameRenderer : public QObject
-{
-public:
- AVFVideoFrameRenderer(QAbstractVideoSurface *surface, QObject *parent = nullptr);
-
- virtual ~AVFVideoFrameRenderer();
-
- quint64 renderLayerToTexture(AVPlayerLayer *layer);
- quint64 renderLayerToMTLTexture(AVPlayerLayer *layer);
- QImage renderLayerToImage(AVPlayerLayer *layer);
-
-private:
- QOpenGLFramebufferObject* initRenderer(AVPlayerLayer *layer);
- void renderLayerToFBO(AVPlayerLayer *layer, QOpenGLFramebufferObject *fbo);
-
- CARenderer *m_videoLayerRenderer;
- QAbstractVideoSurface *m_surface;
- QOpenGLFramebufferObject *m_fbo[2];
- QWindow *m_offscreenSurface;
- QOpenGLContext *m_glContext;
- QSize m_targetSize;
-
- uint m_currentBuffer;
- bool m_isContextShared;
-
- id<MTLDevice> m_metalDevice = nil;
- id<MTLTexture> m_metalTexture = nil;
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFVIDEOFRAMERENDERER_H
diff --git a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm b/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm
deleted file mode 100644
index f81412d65..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm
+++ /dev/null
@@ -1,276 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfvideoframerenderer.h"
-
-#include <QtMultimedia/qabstractvideosurface.h>
-#include <QtOpenGL/QOpenGLFramebufferObject>
-#include <QtGui/QWindow>
-
-#ifdef QT_DEBUG_AVF
-#include <QtCore/qdebug.h>
-#endif
-
-#import <CoreVideo/CVBase.h>
-#import <AVFoundation/AVFoundation.h>
-
-QT_USE_NAMESPACE
-
-AVFVideoFrameRenderer::AVFVideoFrameRenderer(QAbstractVideoSurface *surface, QObject *parent)
- : QObject(parent)
- , m_videoLayerRenderer(nullptr)
- , m_surface(surface)
- , m_offscreenSurface(nullptr)
- , m_glContext(nullptr)
- , m_currentBuffer(1)
- , m_isContextShared(true)
-{
- m_fbo[0] = nullptr;
- m_fbo[1] = nullptr;
-}
-
-AVFVideoFrameRenderer::~AVFVideoFrameRenderer()
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO;
-#endif
-
- [m_videoLayerRenderer release];
- [m_metalDevice release];
- delete m_fbo[0];
- delete m_fbo[1];
- delete m_offscreenSurface;
- delete m_glContext;
-}
-
-quint64 AVFVideoFrameRenderer::renderLayerToTexture(AVPlayerLayer *layer)
-{
- //Is layer valid
- if (!layer)
- return 0;
-
- //If the glContext isn't shared, it doesn't make sense to return a texture for us
- if (m_offscreenSurface && !m_isContextShared)
- return 0;
-
- QOpenGLFramebufferObject *fbo = initRenderer(layer);
-
- if (!fbo)
- return 0;
-
- renderLayerToFBO(layer, fbo);
- if (m_glContext)
- m_glContext->doneCurrent();
-
- return fbo->texture();
-}
-
-quint64 AVFVideoFrameRenderer::renderLayerToMTLTexture(AVPlayerLayer *layer)
-{
- m_targetSize = QSize(layer.bounds.size.width, layer.bounds.size.height);
-
- if (!m_metalDevice)
- m_metalDevice = MTLCreateSystemDefaultDevice();
-
- if (!m_metalTexture) {
- auto desc = [[MTLTextureDescriptor alloc] init];
- desc.textureType = MTLTextureType2D;
- desc.width = NSUInteger(m_targetSize.width());
- desc.height = NSUInteger(m_targetSize.height());
- desc.resourceOptions = MTLResourceStorageModePrivate;
- desc.usage = MTLTextureUsageRenderTarget;
- desc.pixelFormat = MTLPixelFormatRGBA8Unorm;
-
- m_metalTexture = [m_metalDevice newTextureWithDescriptor: desc];
- [desc release];
- }
-
- if (!m_videoLayerRenderer) {
- m_videoLayerRenderer = [CARenderer rendererWithMTLTexture:m_metalTexture options:nil];
- [m_videoLayerRenderer retain];
- }
-
- if (m_videoLayerRenderer.layer != layer) {
- m_videoLayerRenderer.layer = layer;
- m_videoLayerRenderer.bounds = layer.bounds;
- }
-
- [m_videoLayerRenderer beginFrameAtTime:CACurrentMediaTime() timeStamp:NULL];
- [m_videoLayerRenderer addUpdateRect:layer.bounds];
- [m_videoLayerRenderer render];
- [m_videoLayerRenderer endFrame];
-
- return quint64(m_metalTexture);
-}
-
-QImage AVFVideoFrameRenderer::renderLayerToImage(AVPlayerLayer *layer)
-{
- //Is layer valid
- if (!layer) {
- return QImage();
- }
-
- QOpenGLFramebufferObject *fbo = initRenderer(layer);
-
- if (!fbo)
- return QImage();
-
- renderLayerToFBO(layer, fbo);
- QImage fboImage = fbo->toImage();
- if (m_glContext)
- m_glContext->doneCurrent();
-
- return fboImage;
-}
-
-QOpenGLFramebufferObject *AVFVideoFrameRenderer::initRenderer(AVPlayerLayer *layer)
-{
-
- //Get size from AVPlayerLayer
- m_targetSize = QSize(layer.bounds.size.width, layer.bounds.size.height);
-
- QOpenGLContext *shareContext = !m_glContext && m_surface
- ? qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>())
- : nullptr;
-
- //Make sure we have an OpenGL context to make current
- if (shareContext || (!QOpenGLContext::currentContext() && !m_glContext)) {
-
- //Create Hidden QWindow surface to create context in this thread
- delete m_offscreenSurface;
- m_offscreenSurface = new QWindow();
- m_offscreenSurface->setSurfaceType(QWindow::OpenGLSurface);
- //Needs geometry to be a valid surface, but size is not important
- m_offscreenSurface->setGeometry(0, 0, 1, 1);
- m_offscreenSurface->create();
-
- delete m_glContext;
- m_glContext = new QOpenGLContext();
- m_glContext->setFormat(m_offscreenSurface->requestedFormat());
-
- if (shareContext) {
- m_glContext->setShareContext(shareContext);
- m_isContextShared = true;
- } else {
-#ifdef QT_DEBUG_AVF
- qWarning("failed to get Render Thread context");
-#endif
- m_isContextShared = false;
- }
- if (!m_glContext->create()) {
- qWarning("failed to create QOpenGLContext");
- return nullptr;
- }
-
- // CARenderer must be re-created with different current context, so release it now.
- // See lines below where m_videoLayerRenderer is constructed.
- if (m_videoLayerRenderer) {
- [m_videoLayerRenderer release];
- m_videoLayerRenderer = nullptr;
- }
- }
-
- //Need current context
- if (m_glContext)
- m_glContext->makeCurrent(m_offscreenSurface);
-
- //Create the CARenderer if needed
- if (!m_videoLayerRenderer) {
- m_videoLayerRenderer = [CARenderer rendererWithCGLContext: CGLGetCurrentContext() options: nil];
- [m_videoLayerRenderer retain];
- }
-
- //Set/Change render source if needed
- if (m_videoLayerRenderer.layer != layer) {
- m_videoLayerRenderer.layer = layer;
- m_videoLayerRenderer.bounds = layer.bounds;
- }
-
- //Do we have FBO's already?
- if ((!m_fbo[0] && !m_fbo[0]) || (m_fbo[0]->size() != m_targetSize)) {
- delete m_fbo[0];
- delete m_fbo[1];
- m_fbo[0] = new QOpenGLFramebufferObject(m_targetSize);
- m_fbo[1] = new QOpenGLFramebufferObject(m_targetSize);
- }
-
- //Switch buffer target
- m_currentBuffer = !m_currentBuffer;
- return m_fbo[m_currentBuffer];
-}
-
-void AVFVideoFrameRenderer::renderLayerToFBO(AVPlayerLayer *layer, QOpenGLFramebufferObject *fbo)
-{
- //Start Rendering
- //NOTE: This rendering method will NOT work on iOS as there is no CARenderer in iOS
- if (!fbo->bind()) {
- qWarning("AVFVideoRender FBO failed to bind");
- return;
- }
-
- glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glViewport(0, 0, m_targetSize.width(), m_targetSize.height());
-
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
-
- //Render to FBO with inverted Y
- glOrtho(0.0, m_targetSize.width(), 0.0, m_targetSize.height(), 0.0, 1.0);
-
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- glLoadIdentity();
-
- [m_videoLayerRenderer beginFrameAtTime:CACurrentMediaTime() timeStamp:NULL];
- [m_videoLayerRenderer addUpdateRect:layer.bounds];
- [m_videoLayerRenderer render];
- [m_videoLayerRenderer endFrame];
-
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix();
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
-
- glFinish(); //Rendering needs to be done before passing texture to video frame
-
- fbo->release();
-}
diff --git a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer_ios.h b/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer_ios.h
deleted file mode 100644
index d1de1f511..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer_ios.h
+++ /dev/null
@@ -1,120 +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 AVFVIDEOFRAMERENDERER_H
-#define AVFVIDEOFRAMERENDERER_H
-
-#include <QtCore/QObject>
-#include <QtGui/QImage>
-#include <QtGui/QOpenGLContext>
-#include <QtCore/QSize>
-
-#import "Metal/Metal.h"
-#import "MetalKit/MetalKit.h"
-
-@class AVPlayerLayer;
-@class AVPlayerItemVideoOutput;
-
-QT_BEGIN_NAMESPACE
-
-class QOpenGLContext;
-class QOpenGLFramebufferObject;
-class QOpenGLShaderProgram;
-class QOffscreenSurface;
-class QAbstractVideoSurface;
-
-typedef struct __CVBuffer *CVBufferRef;
-typedef CVBufferRef CVImageBufferRef;
-typedef CVImageBufferRef CVPixelBufferRef;
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
-typedef struct __CVOpenGLESTextureCache *CVOpenGLESTextureCacheRef;
-typedef CVImageBufferRef CVOpenGLESTextureRef;
-// helpers to avoid boring if def
-typedef CVOpenGLESTextureCacheRef CVOGLTextureCacheRef;
-typedef CVOpenGLESTextureRef CVOGLTextureRef;
-#define CVOGLTextureGetTarget CVOpenGLESTextureGetTarget
-#define CVOGLTextureGetName CVOpenGLESTextureGetName
-#define CVOGLTextureCacheCreate CVOpenGLESTextureCacheCreate
-#define CVOGLTextureCacheCreateTextureFromImage CVOpenGLESTextureCacheCreateTextureFromImage
-#define CVOGLTextureCacheFlush CVOpenGLESTextureCacheFlush
-#else
-typedef struct __CVOpenGLTextureCache *CVOpenGLTextureCacheRef;
-typedef CVImageBufferRef CVOpenGLTextureRef;
-// helpers to avoid boring if def
-typedef CVOpenGLTextureCacheRef CVOGLTextureCacheRef;
-typedef CVOpenGLTextureRef CVOGLTextureRef;
-#define CVOGLTextureGetTarget CVOpenGLTextureGetTarget
-#define CVOGLTextureGetName CVOpenGLTextureGetName
-#define CVOGLTextureCacheCreate CVOpenGLTextureCacheCreate
-#define CVOGLTextureCacheCreateTextureFromImage CVOpenGLTextureCacheCreateTextureFromImage
-#define CVOGLTextureCacheFlush CVOpenGLTextureCacheFlush
-#endif
-
-class AVFVideoFrameRenderer : public QObject
-{
-public:
- AVFVideoFrameRenderer(QAbstractVideoSurface *surface, QObject *parent = nullptr);
-
- virtual ~AVFVideoFrameRenderer();
-
- void setPlayerLayer(AVPlayerLayer *layer);
-
- quint64 renderLayerToTexture(AVPlayerLayer *layer);
- quint64 renderLayerToMTLTexture(AVPlayerLayer *layer);
- QImage renderLayerToImage(AVPlayerLayer *layer);
-
-private:
- void initRenderer();
- CVPixelBufferRef copyPixelBufferFromLayer(AVPlayerLayer *layer, size_t& width, size_t& height);
- CVOGLTextureRef createCacheTextureFromLayer(AVPlayerLayer *layer, size_t& width, size_t& height);
-
- QOpenGLContext *m_glContext;
- QOffscreenSurface *m_offscreenSurface;
- QAbstractVideoSurface *m_surface;
- CVOGLTextureCacheRef m_textureCache;
- AVPlayerItemVideoOutput* m_videoOutput;
- bool m_isContextShared;
-
- id<MTLDevice> m_metalDevice = nil;
- CVMetalTextureCacheRef m_metalTextureCache = nil;
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFVIDEOFRAMERENDERER_H
diff --git a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer_ios.mm b/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer_ios.mm
deleted file mode 100644
index cbaa1aa11..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer_ios.mm
+++ /dev/null
@@ -1,307 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfvideoframerenderer_ios.h"
-
-#include <QtMultimedia/qabstractvideosurface.h>
-#include <QtOpenGL/QOpenGLFramebufferObject>
-#include <QtOpenGL/QOpenGLShaderProgram>
-#include <QtGui/QOffscreenSurface>
-
-#ifdef QT_DEBUG_AVF
-#include <QtCore/qdebug.h>
-#endif
-
-#import <CoreVideo/CVBase.h>
-#import <AVFoundation/AVFoundation.h>
-QT_USE_NAMESPACE
-
-AVFVideoFrameRenderer::AVFVideoFrameRenderer(QAbstractVideoSurface *surface, QObject *parent)
- : QObject(parent)
- , m_glContext(nullptr)
- , m_offscreenSurface(nullptr)
- , m_surface(surface)
- , m_textureCache(nullptr)
- , m_videoOutput(nullptr)
- , m_isContextShared(true)
-{
-}
-
-AVFVideoFrameRenderer::~AVFVideoFrameRenderer()
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO;
-#endif
-
- [m_videoOutput release]; // sending to nil is fine
- if (m_textureCache)
- CFRelease(m_textureCache);
- if (m_metalTextureCache)
- CFRelease(m_metalTextureCache);
- [m_metalDevice release];
- delete m_offscreenSurface;
- delete m_glContext;
-}
-
-void AVFVideoFrameRenderer::setPlayerLayer(AVPlayerLayer *layer)
-{
- Q_UNUSED(layer);
- if (m_videoOutput) {
- [m_videoOutput release];
- m_videoOutput = nullptr;
- // will be re-created in first call to copyPixelBufferFromLayer
- }
-}
-
-quint64 AVFVideoFrameRenderer::renderLayerToMTLTexture(AVPlayerLayer *layer)
-{
- if (!m_metalDevice)
- m_metalDevice = MTLCreateSystemDefaultDevice();
-
- if (!m_metalTextureCache) {
- CVReturn err = CVMetalTextureCacheCreate(kCFAllocatorDefault, nullptr,
- m_metalDevice, nullptr, &m_metalTextureCache);
- if (err) {
- qWarning() << "Error at CVMetalTextureCacheCreate" << err;
- return 0;
- }
- }
-
- size_t width = 0, height = 0;
- CVPixelBufferRef pixelBuffer = copyPixelBufferFromLayer(layer, width, height);
-
- if (!pixelBuffer)
- return 0;
-
- CVMetalTextureCacheFlush(m_metalTextureCache, 0);
-
- CVMetalTextureRef texture = nil;
- CVReturn err = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, m_metalTextureCache, pixelBuffer, NULL,
- MTLPixelFormatBGRA8Unorm_sRGB, width, height, 0, &texture);
-
- if (!texture || err)
- qWarning("CVMetalTextureCacheCreateTextureFromImage failed (error: %d)", err);
-
- CVPixelBufferRelease(pixelBuffer);
- quint64 tex = 0;
- if (texture) {
- tex = quint64(CVMetalTextureGetTexture(texture));
- CFRelease(texture);
- }
-
- return tex;
-}
-
-quint64 AVFVideoFrameRenderer::renderLayerToTexture(AVPlayerLayer *layer)
-{
- initRenderer();
-
- // If the glContext isn't shared, it doesn't make sense to return a texture for us
- if (!m_isContextShared)
- return 0;
-
- size_t dummyWidth = 0, dummyHeight = 0;
- auto texture = createCacheTextureFromLayer(layer, dummyWidth, dummyHeight);
- auto tex = quint64(CVOGLTextureGetName(texture));
- CFRelease(texture);
-
- return tex;
-}
-
-static NSString* const AVF_PIXEL_FORMAT_KEY = (NSString*)kCVPixelBufferPixelFormatTypeKey;
-static NSNumber* const AVF_PIXEL_FORMAT_VALUE = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA];
-static NSDictionary* const AVF_OUTPUT_SETTINGS = [NSDictionary dictionaryWithObject:AVF_PIXEL_FORMAT_VALUE forKey:AVF_PIXEL_FORMAT_KEY];
-
-
-CVPixelBufferRef AVFVideoFrameRenderer::copyPixelBufferFromLayer(AVPlayerLayer *layer,
- size_t& width, size_t& height)
-{
- //Is layer valid
- if (!layer) {
-#ifdef QT_DEBUG_AVF
- qWarning("copyPixelBufferFromLayer: invalid layer");
-#endif
- return nullptr;
- }
-
- if (!m_videoOutput) {
- m_videoOutput = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:AVF_OUTPUT_SETTINGS];
- [m_videoOutput setDelegate:nil queue:nil];
- AVPlayerItem * item = [[layer player] currentItem];
- [item addOutput:m_videoOutput];
- }
-
- CFTimeInterval currentCAFrameTime = CACurrentMediaTime();
- CMTime currentCMFrameTime = [m_videoOutput itemTimeForHostTime:currentCAFrameTime];
- // happens when buffering / loading
- if (CMTimeCompare(currentCMFrameTime, kCMTimeZero) < 0) {
- return nullptr;
- }
-
- CVPixelBufferRef pixelBuffer = [m_videoOutput copyPixelBufferForItemTime:currentCMFrameTime
- itemTimeForDisplay:nil];
- if (!pixelBuffer) {
-#ifdef QT_DEBUG_AVF
- qWarning("copyPixelBufferForItemTime returned nil");
- CMTimeShow(currentCMFrameTime);
-#endif
- return nullptr;
- }
-
- width = CVPixelBufferGetWidth(pixelBuffer);
- height = CVPixelBufferGetHeight(pixelBuffer);
- return pixelBuffer;
-}
-
-CVOGLTextureRef AVFVideoFrameRenderer::createCacheTextureFromLayer(AVPlayerLayer *layer,
- size_t& width, size_t& height)
-{
- CVPixelBufferRef pixelBuffer = copyPixelBufferFromLayer(layer, width, height);
-
- if (!pixelBuffer)
- return nullptr;
-
- CVOGLTextureCacheFlush(m_textureCache, 0);
-
- CVOGLTextureRef texture = nullptr;
- CVReturn err = CVOGLTextureCacheCreateTextureFromImage(kCFAllocatorDefault, m_textureCache, pixelBuffer, nullptr,
- GL_TEXTURE_2D, GL_RGBA,
- (GLsizei) width, (GLsizei) height,
- GL_BGRA, GL_UNSIGNED_BYTE, 0,
- &texture);
-
- if (!texture || err) {
-#ifdef QT_DEBUG_AVF
- qWarning("CVOGLTextureCacheCreateTextureFromImage failed (error: %d)", err);
-#endif
- }
-
- CVPixelBufferRelease(pixelBuffer);
-
- return texture;
-}
-
-QImage AVFVideoFrameRenderer::renderLayerToImage(AVPlayerLayer *layer)
-{
- size_t width = 0;
- size_t height = 0;
- CVPixelBufferRef pixelBuffer = copyPixelBufferFromLayer(layer, width, height);
-
- if (!pixelBuffer)
- return QImage();
-
- OSType pixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer);
- if (pixelFormat != kCVPixelFormatType_32BGRA) {
-#ifdef QT_DEBUG_AVF
- qWarning("CVPixelBuffer format is not BGRA32 (got: %d)", static_cast<quint32>(pixelFormat));
-#endif
- return QImage();
- }
-
- CVPixelBufferLockBaseAddress(pixelBuffer, 0);
- char *data = (char *)CVPixelBufferGetBaseAddress(pixelBuffer);
- size_t stride = CVPixelBufferGetBytesPerRow(pixelBuffer);
-
- // format here is not relevant, only using for storage
- QImage img = QImage(width, height, QImage::Format_ARGB32);
- for (size_t j = 0; j < height; j++) {
- memcpy(img.scanLine(j), data, width * 4);
- data += stride;
- }
-
- CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
- CVPixelBufferRelease(pixelBuffer);
- return img;
-}
-
-void AVFVideoFrameRenderer::initRenderer()
-{
- // even for using a texture directly, we need to be able to make a context current,
- // so we need an offscreen, and we shouldn't assume we can make the surface context
- // current on that offscreen, so use our own (sharing with it). Slightly
- // excessive but no performance penalty and makes the QImage path easier to maintain
-
- //Make sure we have an OpenGL context to make current
- if (!m_glContext) {
- //Create OpenGL context and set share context from surface
- QOpenGLContext *shareContext = nullptr;
- if (m_surface) {
- shareContext = qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>());
- }
-
- m_glContext = new QOpenGLContext();
- if (shareContext) {
- m_glContext->setShareContext(shareContext);
- m_isContextShared = true;
- } else {
-#ifdef QT_DEBUG_AVF
- qWarning("failed to get Render Thread context");
-#endif
- m_isContextShared = false;
- }
- if (!m_glContext->create()) {
-#ifdef QT_DEBUG_AVF
- qWarning("failed to create QOpenGLContext");
-#endif
- return;
- }
- }
-
- if (!m_offscreenSurface) {
- m_offscreenSurface = new QOffscreenSurface();
- m_offscreenSurface->setFormat(m_glContext->format());
- m_offscreenSurface->create();
- }
-
- //Need current context
- m_glContext->makeCurrent(m_offscreenSurface);
-
- if (!m_textureCache) {
- // Create a new open gl texture cache
- CVReturn err = CVOGLTextureCacheCreate(kCFAllocatorDefault, nullptr,
- [EAGLContext currentContext],
- nullptr, &m_textureCache);
- if (err) {
- #ifdef QT_DEBUG_AVF
- qWarning("Error at CVOGLTextureCacheCreate %d", err);
- #endif
- }
- }
-
-}
diff --git a/src/plugins/avfoundation/mediaplayer/avfvideooutput.h b/src/plugins/avfoundation/mediaplayer/avfvideooutput.h
deleted file mode 100644
index d3bcfecf7..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfvideooutput.h
+++ /dev/null
@@ -1,60 +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 AVFVIDEOOUTPUT_H
-#define AVFVIDEOOUTPUT_H
-
-#include <QtCore/qobject.h>
-
-QT_BEGIN_NAMESPACE
-
-class AVFVideoOutput
-{
-public:
- virtual ~AVFVideoOutput() {}
- virtual void setLayer(void *playerLayer) = 0;
-};
-
-#define AVFVideoOutput_iid \
- "org.qt-project.qt.AVFVideoOutput/5.0"
-Q_DECLARE_INTERFACE(AVFVideoOutput, AVFVideoOutput_iid)
-
-QT_END_NAMESPACE
-
-#endif // AVFVIDEOOUTPUT_H
diff --git a/src/plugins/avfoundation/mediaplayer/avfvideooutput.mm b/src/plugins/avfoundation/mediaplayer/avfvideooutput.mm
deleted file mode 100644
index 327681f50..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfvideooutput.mm
+++ /dev/null
@@ -1,42 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfvideooutput.h"
-
-QT_USE_NAMESPACE
diff --git a/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.h b/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.h
deleted file mode 100644
index c1a629944..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.h
+++ /dev/null
@@ -1,92 +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 AVFVIDEORENDERERCONTROL_H
-#define AVFVIDEORENDERERCONTROL_H
-
-#include <QtMultimedia/QVideoRendererControl>
-#include <QtCore/QMutex>
-#include <QtCore/QSize>
-
-#include "avfvideooutput.h"
-
-#import <CoreVideo/CVBase.h>
-
-QT_BEGIN_NAMESPACE
-
-class AVFDisplayLink;
-class AVFVideoFrameRenderer;
-
-class AVFVideoRendererControl : public QVideoRendererControl, public AVFVideoOutput
-{
- Q_OBJECT
- Q_INTERFACES(AVFVideoOutput)
-public:
- explicit AVFVideoRendererControl(QObject *parent = nullptr);
- virtual ~AVFVideoRendererControl();
-
- QAbstractVideoSurface *surface() const override;
- void setSurface(QAbstractVideoSurface *surface) override;
-
- void setLayer(void *playerLayer) override;
-
-private Q_SLOTS:
- void updateVideoFrame(const CVTimeStamp &ts);
-
-Q_SIGNALS:
- void surfaceChanged(QAbstractVideoSurface *surface);
-
-private:
- void setupVideoOutput();
-
- QMutex m_mutex;
- QAbstractVideoSurface *m_surface;
-
- void *m_playerLayer;
-
- AVFVideoFrameRenderer *m_frameRenderer;
- AVFDisplayLink *m_displayLink;
- QSize m_nativeSize;
- bool m_enableOpenGL;
- bool m_enableMetal;
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFVIDEORENDERERCONTROL_H
diff --git a/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.mm b/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.mm
deleted file mode 100644
index 726dc5193..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.mm
+++ /dev/null
@@ -1,301 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfvideorenderercontrol.h"
-#include "avfdisplaylink.h"
-
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
-#include "avfvideoframerenderer_ios.h"
-#else
-#include "avfvideoframerenderer.h"
-#endif
-
-#include <QtMultimedia/qabstractvideobuffer.h>
-#include <QtMultimedia/qabstractvideosurface.h>
-#include <QtMultimedia/qvideosurfaceformat.h>
-
-#include <private/qimagevideobuffer_p.h>
-
-#include <QtCore/qdebug.h>
-
-#import <AVFoundation/AVFoundation.h>
-
-QT_USE_NAMESPACE
-
-class TextureVideoBuffer : public QAbstractVideoBuffer
-{
-public:
- TextureVideoBuffer(HandleType type, quint64 tex)
- : QAbstractVideoBuffer(type)
- , m_texture(tex)
- {}
-
- virtual ~TextureVideoBuffer()
- {
- }
-
- MapMode mapMode() const { return NotMapped; }
- uchar *map(MapMode, int*, int*) { return 0; }
- void unmap() {}
-
- QVariant handle() const
- {
- return QVariant::fromValue<unsigned long long>(m_texture);
- }
-
-private:
- quint64 m_texture;
-};
-
-AVFVideoRendererControl::AVFVideoRendererControl(QObject *parent)
- : QVideoRendererControl(parent)
- , m_surface(nullptr)
- , m_playerLayer(nullptr)
- , m_frameRenderer(nullptr)
- , m_enableOpenGL(false)
- , m_enableMetal(false)
-
-{
- m_displayLink = new AVFDisplayLink(this);
- connect(m_displayLink, SIGNAL(tick(CVTimeStamp)), SLOT(updateVideoFrame(CVTimeStamp)));
-}
-
-AVFVideoRendererControl::~AVFVideoRendererControl()
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO;
-#endif
- m_displayLink->stop();
- [static_cast<AVPlayerLayer*>(m_playerLayer) release];
-}
-
-QAbstractVideoSurface *AVFVideoRendererControl::surface() const
-{
- return m_surface;
-}
-
-void AVFVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << "Set video surface" << surface;
-#endif
-
- //When we have a valid surface, we can setup a frame renderer
- //and schedule surface updates with the display link.
- if (surface == m_surface)
- return;
-
- QMutexLocker locker(&m_mutex);
-
- if (m_surface && m_surface->isActive())
- m_surface->stop();
-
- m_surface = surface;
-
- //If the surface changed, then the current frame renderer is no longer valid
- delete m_frameRenderer;
- m_frameRenderer = nullptr;
-
- //If there is now no surface to render too
- if (m_surface == nullptr) {
- m_displayLink->stop();
- return;
- }
-
- //Surface changed, so we need a new frame renderer
- m_frameRenderer = new AVFVideoFrameRenderer(m_surface, this);
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
- if (m_playerLayer) {
- m_frameRenderer->setPlayerLayer(static_cast<AVPlayerLayer*>(m_playerLayer));
- }
-#endif
-
- auto checkHandleType = [this] {
- m_enableOpenGL = m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).contains(QVideoFrame::Format_BGR32);
- m_enableMetal = m_surface->supportedPixelFormats(QAbstractVideoBuffer::MTLTextureHandle).contains(QVideoFrame::Format_BGR32);
- };
- checkHandleType();
- connect(m_surface, &QAbstractVideoSurface::supportedFormatsChanged, this, checkHandleType);
-
- //If we already have a layer, but changed surfaces start rendering again
- if (m_playerLayer && !m_displayLink->isActive()) {
- m_displayLink->start();
- }
-
-}
-
-void AVFVideoRendererControl::setLayer(void *playerLayer)
-{
- if (m_playerLayer == playerLayer)
- return;
-
- [static_cast<AVPlayerLayer*>(m_playerLayer) release];
-
- m_playerLayer = [static_cast<AVPlayerLayer*>(playerLayer) retain];
-
- //If there is an active surface, make sure it has been stopped so that
- //we can update it's state with the new content.
- if (m_surface && m_surface->isActive())
- m_surface->stop();
-
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
- if (m_frameRenderer) {
- m_frameRenderer->setPlayerLayer(static_cast<AVPlayerLayer*>(playerLayer));
- }
-#endif
-
- //If there is no layer to render, stop scheduling updates
- if (m_playerLayer == nullptr) {
- m_displayLink->stop();
- return;
- }
-
- setupVideoOutput();
-
- //If we now have both a valid surface and layer, start scheduling updates
- if (m_surface && !m_displayLink->isActive()) {
- m_displayLink->start();
- }
-}
-
-void AVFVideoRendererControl::updateVideoFrame(const CVTimeStamp &ts)
-{
- Q_UNUSED(ts);
-
- AVPlayerLayer *playerLayer = static_cast<AVPlayerLayer*>(m_playerLayer);
-
- if (!playerLayer) {
- qWarning("updateVideoFrame called without AVPlayerLayer (which shouldn't happen");
- return;
- }
-
- if (!playerLayer.readyForDisplay || !m_surface)
- return;
-
- if (m_enableMetal) {
- quint64 tex = m_frameRenderer->renderLayerToMTLTexture(playerLayer);
- if (tex == 0)
- return;
-
- auto buffer = new TextureVideoBuffer(QAbstractVideoBuffer::MTLTextureHandle, tex);
- QVideoFrame frame(buffer, m_nativeSize, QVideoFrame::Format_BGR32);
- if (m_surface->isActive() && m_surface->surfaceFormat().pixelFormat() != frame.pixelFormat())
- m_surface->stop();
-
- if (!m_surface->isActive()) {
- QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), QAbstractVideoBuffer::MTLTextureHandle);
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
- format.setScanLineDirection(QVideoSurfaceFormat::TopToBottom);
-#else
- format.setScanLineDirection(QVideoSurfaceFormat::BottomToTop);
-#endif
- if (!m_surface->start(format))
- qWarning("Failed to activate video surface");
- }
-
- if (m_surface->isActive())
- m_surface->present(frame);
-
- return;
- }
-
- if (m_enableOpenGL) {
- quint64 tex = m_frameRenderer->renderLayerToTexture(playerLayer);
- //Make sure we got a valid texture
- if (tex == 0)
- return;
-
- QAbstractVideoBuffer *buffer = new TextureVideoBuffer(QAbstractVideoBuffer::GLTextureHandle, tex);
- QVideoFrame frame = QVideoFrame(buffer, m_nativeSize, QVideoFrame::Format_BGR32);
-
- if (m_surface && frame.isValid()) {
- if (m_surface->isActive() && m_surface->surfaceFormat().pixelFormat() != frame.pixelFormat())
- m_surface->stop();
-
- if (!m_surface->isActive()) {
- QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), QAbstractVideoBuffer::GLTextureHandle);
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
- format.setScanLineDirection(QVideoSurfaceFormat::TopToBottom);
-#else
- format.setScanLineDirection(QVideoSurfaceFormat::BottomToTop);
-#endif
- if (!m_surface->start(format)) {
- //Surface doesn't support GLTextureHandle
- qWarning("Failed to activate video surface");
- }
- }
-
- if (m_surface->isActive())
- m_surface->present(frame);
- }
- } else {
- //fallback to rendering frames to QImages
- QImage frameData = m_frameRenderer->renderLayerToImage(playerLayer);
-
- if (frameData.isNull()) {
- return;
- }
-
- QAbstractVideoBuffer *buffer = new QImageVideoBuffer(frameData);
- QVideoFrame frame = QVideoFrame(buffer, m_nativeSize, QVideoFrame::Format_ARGB32);
- if (m_surface && frame.isValid()) {
- if (m_surface->isActive() && m_surface->surfaceFormat().pixelFormat() != frame.pixelFormat())
- m_surface->stop();
-
- if (!m_surface->isActive()) {
- QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), QAbstractVideoBuffer::NoHandle);
-
- if (!m_surface->start(format)) {
- qWarning("Failed to activate video surface");
- }
- }
-
- if (m_surface->isActive())
- m_surface->present(frame);
- }
-
- }
-}
-
-void AVFVideoRendererControl::setupVideoOutput()
-{
- AVPlayerLayer *playerLayer = static_cast<AVPlayerLayer*>(m_playerLayer);
- if (playerLayer)
- m_nativeSize = QSize(playerLayer.bounds.size.width, playerLayer.bounds.size.height);
-}
diff --git a/src/plugins/avfoundation/mediaplayer/avfvideowidget.h b/src/plugins/avfoundation/mediaplayer/avfvideowidget.h
deleted file mode 100644
index faf71f1a4..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfvideowidget.h
+++ /dev/null
@@ -1,85 +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 AVFVIDEOWIDGET_H
-#define AVFVIDEOWIDGET_H
-
-#include <QtWidgets/QWidget>
-
-@class AVPlayerLayer;
-#if defined(Q_OS_OSX)
-@class NSView;
-#else
-@class UIView;
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class AVFVideoWidget : public QWidget
-{
-public:
- AVFVideoWidget(QWidget *parent);
- virtual ~AVFVideoWidget();
-
- QSize sizeHint() const override;
- Qt::AspectRatioMode aspectRatioMode() const;
- void setAspectRatioMode(Qt::AspectRatioMode mode);
- void setPlayerLayer(AVPlayerLayer *layer);
-
-protected:
- void resizeEvent(QResizeEvent *) override;
- void paintEvent(QPaintEvent *) override;
-
-private:
- void updateAspectRatio();
- void updatePlayerLayerBounds(const QSize &size);
-
- QSize m_nativeSize;
- Qt::AspectRatioMode m_aspectRatioMode;
- AVPlayerLayer *m_playerLayer;
-#if defined(Q_OS_OSX)
- NSView *m_nativeView;
-#else
- UIView *m_nativeView;
-#endif
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFVIDEOWIDGET_H
diff --git a/src/plugins/avfoundation/mediaplayer/avfvideowidget.mm b/src/plugins/avfoundation/mediaplayer/avfvideowidget.mm
deleted file mode 100644
index 0987342b4..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfvideowidget.mm
+++ /dev/null
@@ -1,187 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfvideowidget.h"
-
-#import <AVFoundation/AVFoundation.h>
-#import <QuartzCore/CATransaction.h>
-
-#if defined(Q_OS_MACOS)
-#import <AppKit/AppKit.h>
-#else
-#import <UIKit/UIKit.h>
-#endif
-
-#include <QtCore/QDebug>
-#include <QtGui/QResizeEvent>
-#include <QtGui/QPaintEvent>
-#include <QtGui/QPainter>
-
-QT_USE_NAMESPACE
-
-AVFVideoWidget::AVFVideoWidget(QWidget *parent)
- : QWidget(parent)
- , m_aspectRatioMode(Qt::KeepAspectRatio)
- , m_playerLayer(nullptr)
- , m_nativeView(nullptr)
-{
- setAutoFillBackground(false);
-}
-
-AVFVideoWidget::~AVFVideoWidget()
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO;
-#endif
-
- if (m_playerLayer) {
- [m_playerLayer removeFromSuperlayer];
- [m_playerLayer release];
- }
-}
-
-QSize AVFVideoWidget::sizeHint() const
-{
- return m_nativeSize;
-}
-
-Qt::AspectRatioMode AVFVideoWidget::aspectRatioMode() const
-{
- return m_aspectRatioMode;
-}
-
-void AVFVideoWidget::setAspectRatioMode(Qt::AspectRatioMode mode)
-{
- if (m_aspectRatioMode != mode) {
- m_aspectRatioMode = mode;
-
- updateAspectRatio();
- }
-}
-
-void AVFVideoWidget::setPlayerLayer(AVPlayerLayer *layer)
-{
- if (m_playerLayer == layer)
- return;
-
- if (!m_nativeView) {
- //make video widget a native window
-#if defined(Q_OS_OSX)
- m_nativeView = (NSView*)this->winId();
- [m_nativeView setWantsLayer:YES];
-#else
- m_nativeView = (UIView*)this->winId();
-#endif
- }
-
- if (m_playerLayer) {
- [m_playerLayer removeFromSuperlayer];
- [m_playerLayer release];
- }
-
- m_playerLayer = layer;
-
- CALayer *nativeLayer = [m_nativeView layer];
-
- if (layer) {
- [layer retain];
-
- m_nativeSize = QSize(m_playerLayer.bounds.size.width,
- m_playerLayer.bounds.size.height);
-
- updateAspectRatio();
- [nativeLayer addSublayer:m_playerLayer];
- updatePlayerLayerBounds(this->size());
- }
-#ifdef QT_DEBUG_AVF
- NSArray *sublayers = [nativeLayer sublayers];
- qDebug() << "playerlayer: " << "at z:" << [m_playerLayer zPosition]
- << " frame: " << m_playerLayer.frame.size.width << "x" << m_playerLayer.frame.size.height;
- qDebug() << "superlayer: " << "at z:" << [nativeLayer zPosition]
- << " frame: " << nativeLayer.frame.size.width << "x" << nativeLayer.frame.size.height;
- int i = 0;
- for (CALayer *layer in sublayers) {
- qDebug() << "layer " << i << ": at z:" << [layer zPosition]
- << " frame: " << layer.frame.size.width << "x" << layer.frame.size.height;
- i++;
- }
-#endif
-
-}
-
-void AVFVideoWidget::resizeEvent(QResizeEvent *event)
-{
- updatePlayerLayerBounds(event->size());
- QWidget::resizeEvent(event);
-}
-
-void AVFVideoWidget::paintEvent(QPaintEvent *event)
-{
- QPainter painter(this);
- painter.fillRect(rect(), Qt::black);
-
- QWidget::paintEvent(event);
-}
-
-void AVFVideoWidget::updateAspectRatio()
-{
- if (m_playerLayer) {
- switch (m_aspectRatioMode) {
- case Qt::IgnoreAspectRatio:
- [m_playerLayer setVideoGravity:AVLayerVideoGravityResize];
- break;
- case Qt::KeepAspectRatio:
- [m_playerLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
- break;
- case Qt::KeepAspectRatioByExpanding:
- [m_playerLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
- break;
- default:
- break;
- }
- }
-}
-
-void AVFVideoWidget::updatePlayerLayerBounds(const QSize &size)
-{
- [CATransaction begin];
- [CATransaction setDisableActions: YES]; // disable animation/flicks
- m_playerLayer.bounds = QRect(QPoint(0, 0), size).toCGRect();
- [CATransaction commit];
-}
diff --git a/src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.h b/src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.h
deleted file mode 100644
index 22379d273..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 AVFVIDEOWIDGETCONTROL_H
-#define AVFVIDEOWIDGETCONTROL_H
-
-#include <qvideowidgetcontrol.h>
-#include "avfvideooutput.h"
-
-@class AVPlayerLayer;
-
-QT_BEGIN_NAMESPACE
-
-class AVFVideoWidget;
-
-class AVFVideoWidgetControl : public QVideoWidgetControl, public AVFVideoOutput
-{
- Q_OBJECT
- Q_INTERFACES(AVFVideoOutput)
-public:
- AVFVideoWidgetControl(QObject *parent = nullptr);
- virtual ~AVFVideoWidgetControl();
-
- void setLayer(void *playerLayer) override;
-
- QWidget *videoWidget() override;
-
- bool isFullScreen() const override;
- void setFullScreen(bool fullScreen) override;
-
- Qt::AspectRatioMode aspectRatioMode() const override;
- void setAspectRatioMode(Qt::AspectRatioMode mode) override;
-
- int brightness() const override;
- void setBrightness(int brightness) override;
-
- int contrast() const override;
- void setContrast(int contrast) override;
-
- int hue() const override;
- void setHue(int hue) override;
-
- int saturation() const override;
- void setSaturation(int saturation) override;
-
-private:
- AVFVideoWidget *m_videoWidget;
-
- bool m_fullscreen;
- int m_brightness;
- int m_contrast;
- int m_hue;
- int m_saturation;
-
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFVIDEOWIDGETCONTROL_H
diff --git a/src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.mm b/src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.mm
deleted file mode 100644
index 91ece817e..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfvideowidgetcontrol.mm
+++ /dev/null
@@ -1,145 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfvideowidgetcontrol.h"
-#include "avfvideowidget.h"
-
-#ifdef QT_DEBUG_AVF
-#include <QtCore/QDebug>
-#endif
-
-#import <AVFoundation/AVFoundation.h>
-
-QT_USE_NAMESPACE
-
-AVFVideoWidgetControl::AVFVideoWidgetControl(QObject *parent)
- : QVideoWidgetControl(parent)
- , m_fullscreen(false)
- , m_brightness(0)
- , m_contrast(0)
- , m_hue(0)
- , m_saturation(0)
-{
- m_videoWidget = new AVFVideoWidget(nullptr);
-}
-
-AVFVideoWidgetControl::~AVFVideoWidgetControl()
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO;
-#endif
- delete m_videoWidget;
-}
-
-void AVFVideoWidgetControl::setLayer(void *playerLayer)
-{
-#ifdef QT_DEBUG_AVF
- qDebug() << Q_FUNC_INFO << playerLayer;
-#endif
-
- m_videoWidget->setPlayerLayer(static_cast<AVPlayerLayer*>(playerLayer));
-
-}
-
-QWidget *AVFVideoWidgetControl::videoWidget()
-{
- return m_videoWidget;
-}
-
-bool AVFVideoWidgetControl::isFullScreen() const
-{
- return m_fullscreen;
-}
-
-void AVFVideoWidgetControl::setFullScreen(bool fullScreen)
-{
- m_fullscreen = fullScreen;
-}
-
-Qt::AspectRatioMode AVFVideoWidgetControl::aspectRatioMode() const
-{
- return m_videoWidget->aspectRatioMode();
-}
-
-void AVFVideoWidgetControl::setAspectRatioMode(Qt::AspectRatioMode mode)
-{
- m_videoWidget->setAspectRatioMode(mode);
-}
-
-int AVFVideoWidgetControl::brightness() const
-{
- return m_brightness;
-}
-
-void AVFVideoWidgetControl::setBrightness(int brightness)
-{
- m_brightness = brightness;
-}
-
-int AVFVideoWidgetControl::contrast() const
-{
- return m_contrast;
-}
-
-void AVFVideoWidgetControl::setContrast(int contrast)
-{
- m_contrast = contrast;
-}
-
-int AVFVideoWidgetControl::hue() const
-{
- return m_hue;
-}
-
-void AVFVideoWidgetControl::setHue(int hue)
-{
- m_hue = hue;
-}
-
-int AVFVideoWidgetControl::saturation() const
-{
- return m_saturation;
-}
-
-void AVFVideoWidgetControl::setSaturation(int saturation)
-{
- m_saturation = saturation;
-}
-
-#include "moc_avfvideowidgetcontrol.cpp"
diff --git a/src/plugins/avfoundation/mediaplayer/avfvideowindowcontrol.h b/src/plugins/avfoundation/mediaplayer/avfvideowindowcontrol.h
deleted file mode 100644
index 763656c6c..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfvideowindowcontrol.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 AVFVIDEOWINDOWCONTROL_H
-#define AVFVIDEOWINDOWCONTROL_H
-
-#include <QVideoWindowControl>
-
-@class AVPlayerLayer;
-#if defined(Q_OS_OSX)
-@class NSView;
-typedef NSView NativeView;
-#else
-@class UIView;
-typedef UIView NativeView;
-#endif
-
-#include "avfvideooutput.h"
-
-QT_BEGIN_NAMESPACE
-
-class AVFVideoWindowControl : public QVideoWindowControl, public AVFVideoOutput
-{
- Q_OBJECT
- Q_INTERFACES(AVFVideoOutput)
-
-public:
- AVFVideoWindowControl(QObject *parent = nullptr);
- virtual ~AVFVideoWindowControl();
-
- // QVideoWindowControl interface
-public:
- WId winId() const override;
- void setWinId(WId id) override;
-
- QRect displayRect() const override;
- void setDisplayRect(const QRect &rect) override;
-
- bool isFullScreen() const override;
- void setFullScreen(bool fullScreen) override;
-
- void repaint() override;
- QSize nativeSize() const override;
-
- Qt::AspectRatioMode aspectRatioMode() const override;
- void setAspectRatioMode(Qt::AspectRatioMode mode) override;
-
- int brightness() const override;
- void setBrightness(int brightness) override;
-
- int contrast() const override;
- void setContrast(int contrast) override;
-
- int hue() const override;
- void setHue(int hue) override;
-
- int saturation() const override;
- void setSaturation(int saturation) override;
-
- // AVFVideoOutput interface
- void setLayer(void *playerLayer) override;
-
-private:
- void updateAspectRatio();
- void updatePlayerLayerBounds();
-
- WId m_winId;
- QRect m_displayRect;
- bool m_fullscreen;
- int m_brightness;
- int m_contrast;
- int m_hue;
- int m_saturation;
- Qt::AspectRatioMode m_aspectRatioMode;
- QSize m_nativeSize;
- AVPlayerLayer *m_playerLayer;
- NativeView *m_nativeView;
-};
-
-QT_END_NAMESPACE
-
-#endif // AVFVIDEOWINDOWCONTROL_H
diff --git a/src/plugins/avfoundation/mediaplayer/avfvideowindowcontrol.mm b/src/plugins/avfoundation/mediaplayer/avfvideowindowcontrol.mm
deleted file mode 100644
index d61129ec9..000000000
--- a/src/plugins/avfoundation/mediaplayer/avfvideowindowcontrol.mm
+++ /dev/null
@@ -1,255 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "avfvideowindowcontrol.h"
-
-#include <AVFoundation/AVFoundation.h>
-#import <QuartzCore/CATransaction.h>
-
-#if QT_HAS_INCLUDE(<AppKit/AppKit.h>)
-#include <AppKit/AppKit.h>
-#endif
-
-#if QT_HAS_INCLUDE(<UIKit/UIKit.h>)
-#include <UIKit/UIKit.h>
-#endif
-
-QT_USE_NAMESPACE
-
-AVFVideoWindowControl::AVFVideoWindowControl(QObject *parent)
- : QVideoWindowControl(parent)
- , m_winId(0)
- , m_fullscreen(false)
- , m_brightness(0)
- , m_contrast(0)
- , m_hue(0)
- , m_saturation(0)
- , m_aspectRatioMode(Qt::IgnoreAspectRatio)
- , m_playerLayer(nullptr)
- , m_nativeView(nullptr)
-{
-}
-
-AVFVideoWindowControl::~AVFVideoWindowControl()
-{
- if (m_playerLayer) {
- [m_playerLayer removeFromSuperlayer];
- [m_playerLayer release];
- }
-}
-
-WId AVFVideoWindowControl::winId() const
-{
- return m_winId;
-}
-
-void AVFVideoWindowControl::setWinId(WId id)
-{
- m_winId = id;
- m_nativeView = (NativeView*)m_winId;
-}
-
-QRect AVFVideoWindowControl::displayRect() const
-{
- return m_displayRect;
-}
-
-void AVFVideoWindowControl::setDisplayRect(const QRect &rect)
-{
- if (m_displayRect != rect) {
- m_displayRect = rect;
- updatePlayerLayerBounds();
- }
-}
-
-bool AVFVideoWindowControl::isFullScreen() const
-{
- return m_fullscreen;
-}
-
-void AVFVideoWindowControl::setFullScreen(bool fullScreen)
-{
- if (m_fullscreen != fullScreen) {
- m_fullscreen = fullScreen;
- Q_EMIT QVideoWindowControl::fullScreenChanged(fullScreen);
- }
-}
-
-void AVFVideoWindowControl::repaint()
-{
- if (m_playerLayer)
- [m_playerLayer setNeedsDisplay];
-}
-
-QSize AVFVideoWindowControl::nativeSize() const
-{
- return m_nativeSize;
-}
-
-Qt::AspectRatioMode AVFVideoWindowControl::aspectRatioMode() const
-{
- return m_aspectRatioMode;
-}
-
-void AVFVideoWindowControl::setAspectRatioMode(Qt::AspectRatioMode mode)
-{
- if (m_aspectRatioMode != mode) {
- m_aspectRatioMode = mode;
- updateAspectRatio();
- }
-}
-
-int AVFVideoWindowControl::brightness() const
-{
- return m_brightness;
-}
-
-void AVFVideoWindowControl::setBrightness(int brightness)
-{
- if (m_brightness != brightness) {
- m_brightness = brightness;
- Q_EMIT QVideoWindowControl::brightnessChanged(brightness);
- }
-}
-
-int AVFVideoWindowControl::contrast() const
-{
- return m_contrast;
-}
-
-void AVFVideoWindowControl::setContrast(int contrast)
-{
- if (m_contrast != contrast) {
- m_contrast = contrast;
- Q_EMIT QVideoWindowControl::contrastChanged(contrast);
- }
-}
-
-int AVFVideoWindowControl::hue() const
-{
- return m_hue;
-}
-
-void AVFVideoWindowControl::setHue(int hue)
-{
- if (m_hue != hue) {
- m_hue = hue;
- Q_EMIT QVideoWindowControl::hueChanged(hue);
- }
-}
-
-int AVFVideoWindowControl::saturation() const
-{
- return m_saturation;
-}
-
-void AVFVideoWindowControl::setSaturation(int saturation)
-{
- if (m_saturation != saturation) {
- m_saturation = saturation;
- Q_EMIT QVideoWindowControl::saturationChanged(saturation);
- }
-}
-
-void AVFVideoWindowControl::setLayer(void *playerLayer)
-{
- AVPlayerLayer *layer = static_cast<AVPlayerLayer*>(playerLayer);
- if (m_playerLayer == layer)
- return;
-
- if (!m_winId) {
- qDebug("AVFVideoWindowControl: No video window");
- return;
- }
-
-#if defined(Q_OS_OSX)
- [m_nativeView setWantsLayer:YES];
-#endif
-
- if (m_playerLayer) {
- [m_playerLayer removeFromSuperlayer];
- [m_playerLayer release];
- }
-
- m_playerLayer = layer;
-
- CALayer *nativeLayer = [m_nativeView layer];
-
- if (layer) {
- [layer retain];
-
- m_nativeSize = QSize(m_playerLayer.bounds.size.width,
- m_playerLayer.bounds.size.height);
-
- updateAspectRatio();
- [nativeLayer addSublayer:m_playerLayer];
- updatePlayerLayerBounds();
- }
-}
-
-void AVFVideoWindowControl::updateAspectRatio()
-{
- if (m_playerLayer) {
- switch (m_aspectRatioMode) {
- case Qt::IgnoreAspectRatio:
- [m_playerLayer setVideoGravity:AVLayerVideoGravityResize];
- break;
- case Qt::KeepAspectRatio:
- [m_playerLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
- break;
- case Qt::KeepAspectRatioByExpanding:
- [m_playerLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
- break;
- default:
- break;
- }
- }
-}
-
-void AVFVideoWindowControl::updatePlayerLayerBounds()
-{
- if (m_playerLayer) {
- [CATransaction begin];
- [CATransaction setDisableActions: YES]; // disable animation/flicks
- m_playerLayer.frame = m_displayRect.toCGRect();
- [CATransaction commit];
- }
-}
-
-#include "moc_avfvideowindowcontrol.cpp"
diff --git a/src/plugins/avfoundation/mediaplayer/mediaplayer.pro b/src/plugins/avfoundation/mediaplayer/mediaplayer.pro
deleted file mode 100644
index 604866058..000000000
--- a/src/plugins/avfoundation/mediaplayer/mediaplayer.pro
+++ /dev/null
@@ -1,76 +0,0 @@
-TARGET = qavfmediaplayer
-
-#DEFINES += QT_DEBUG_AVF
-# Avoid clash with a variable named `slots' in a Quartz header
-CONFIG += no_keywords
-
-QT += opengl multimedia-private network
-
-LIBS += -framework CoreMedia -framework CoreVideo -framework QuartzCore -framework Metal
-
-QMAKE_USE += avfoundation
-
-HEADERS += \
- avfmediaplayercontrol.h \
- avfmediaplayermetadatacontrol.h \
- avfmediaplayerservice.h \
- avfmediaplayersession.h \
- avfmediaplayerserviceplugin.h \
- avfvideooutput.h \
- avfvideowindowcontrol.h
-
-OBJECTIVE_SOURCES += \
- avfmediaplayercontrol.mm \
- avfmediaplayermetadatacontrol.mm \
- avfmediaplayerservice.mm \
- avfmediaplayerserviceplugin.mm \
- avfmediaplayersession.mm \
- avfvideooutput.mm \
- avfvideowindowcontrol.mm
-
- qtHaveModule(widgets) {
- QT += multimediawidgets-private
- HEADERS += \
- avfvideowidgetcontrol.h \
- avfvideowidget.h
-
- OBJECTIVE_SOURCES += \
- avfvideowidgetcontrol.mm \
- avfvideowidget.mm
- }
-
-ios|tvos {
- qtConfig(opengl) {
- HEADERS += \
- avfvideoframerenderer_ios.h \
- avfvideorenderercontrol.h \
- avfdisplaylink.h
-
- OBJECTIVE_SOURCES += \
- avfvideoframerenderer_ios.mm \
- avfvideorenderercontrol.mm \
- avfdisplaylink.mm
- }
- LIBS += -framework Foundation
-} else {
- LIBS += -framework AppKit
-
- qtConfig(opengl) {
- HEADERS += \
- avfvideoframerenderer.h \
- avfvideorenderercontrol.h \
- avfdisplaylink.h
-
- OBJECTIVE_SOURCES += \
- avfvideoframerenderer.mm \
- avfvideorenderercontrol.mm \
- avfdisplaylink.mm
- }
-}
-
-OTHER_FILES += \
- avfmediaplayer.json
-
-PLUGIN_TYPE = mediaservice
-PLUGIN_CLASS_NAME = AVFMediaPlayerServicePlugin
-load(qt_plugin)
diff --git a/src/plugins/common/evr.pri b/src/plugins/common/evr.pri
deleted file mode 100644
index 2a1b383df..000000000
--- a/src/plugins/common/evr.pri
+++ /dev/null
@@ -1,20 +0,0 @@
-INCLUDEPATH += $$PWD/evr
-
-qtHaveModule(widgets): QT += widgets
-QT += gui-private
-
-LIBS += -lmf -lmfplat -lmfuuid -ld3d9 -ldxva2 -lwinmm -levr
-
-HEADERS += \
- $$PWD/evr/evrvideowindowcontrol.h \
- $$PWD/evr/evrcustompresenter.h \
- $$PWD/evr/evrd3dpresentengine.h \
- $$PWD/evr/evrhelpers.h \
- $$PWD/evr/evrdefs.h
-
-SOURCES += \
- $$PWD/evr/evrvideowindowcontrol.cpp \
- $$PWD/evr/evrcustompresenter.cpp \
- $$PWD/evr/evrd3dpresentengine.cpp \
- $$PWD/evr/evrhelpers.cpp \
- $$PWD/evr/evrdefs.cpp
diff --git a/src/plugins/common/evr/evrcustompresenter.cpp b/src/plugins/common/evr/evrcustompresenter.cpp
deleted file mode 100644
index b2dd0426c..000000000
--- a/src/plugins/common/evr/evrcustompresenter.cpp
+++ /dev/null
@@ -1,2062 +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 "evrcustompresenter.h"
-
-#include "evrd3dpresentengine.h"
-#include "evrhelpers.h"
-
-#include <QtCore/qmutex.h>
-#include <QtCore/qvarlengtharray.h>
-#include <QtCore/qrect.h>
-#include <qabstractvideosurface.h>
-#include <qthread.h>
-#include <qcoreapplication.h>
-#include <qmath.h>
-#include <QtCore/qdebug.h>
-
-#include <mutex>
-
-#include <float.h>
-#include <evcode.h>
-
-QT_BEGIN_NAMESPACE
-
-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 QVideoFrame::PixelFormat pixelFormatFromMediaType(IMFMediaType *type);
-
-static inline LONG MFTimeToMsec(const LONGLONG& time)
-{
- return (LONG)(time / (ONE_SECOND / ONE_MSEC));
-}
-
-bool qt_evr_setCustomPresenter(IUnknown *evr, EVRCustomPresenter *presenter)
-{
- if (!evr || !presenter)
- return false;
-
- HRESULT result = E_FAIL;
-
- IMFVideoRenderer *renderer = NULL;
- if (SUCCEEDED(evr->QueryInterface(IID_PPV_ARGS(&renderer)))) {
- result = renderer->InitializeRenderer(NULL, presenter);
- renderer->Release();
- }
-
- return result == S_OK;
-}
-
-class PresentSampleEvent : public QEvent
-{
-public:
- PresentSampleEvent(IMFSample *sample)
- : QEvent(QEvent::Type(EVRCustomPresenter::PresentSample))
- , m_sample(sample)
- {
- if (m_sample)
- m_sample->AddRef();
- }
-
- ~PresentSampleEvent() override
- {
- if (m_sample)
- m_sample->Release();
- }
-
- IMFSample *sample() const { return m_sample; }
-
-private:
- 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();
-}
-
-void Scheduler::setFrameRate(const MFRatio& fps)
-{
- UINT64 AvgTimePerFrame = 0;
-
- // 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;
-}
-
-HRESULT Scheduler::startScheduler(IMFClock *clock)
-{
- if (m_schedulerThread)
- return E_UNEXPECTED;
-
- HRESULT hr = S_OK;
- DWORD dwID = 0;
- 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);
- 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);
- if (!m_flushEvent) {
- hr = HRESULT_FROM_WIN32(GetLastError());
- goto done;
- }
-
- // Create the scheduler thread.
- m_schedulerThread = 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;
- 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;
-
- hr = E_UNEXPECTED;
- goto done;
- }
-
- m_threadID = dwID;
-
-done:
- // Regardless success/failure, we are done using the "thread ready" event.
- if (m_threadReadyEvent) {
- CloseHandle(m_threadReadyEvent);
- m_threadReadyEvent = NULL;
- }
- return hr;
-}
-
-HRESULT Scheduler::stopScheduler()
-{
- if (!m_schedulerThread)
- return S_OK;
-
- // Ask the scheduler thread to exit.
- PostThreadMessage(m_threadID, Terminate, 0, 0);
-
- // Wait for the thread to exit.
- WaitForSingleObject(m_schedulerThread, INFINITE);
-
- // Close handles.
- CloseHandle(m_schedulerThread);
- m_schedulerThread = NULL;
-
- CloseHandle(m_flushEvent);
- m_flushEvent = NULL;
-
- // Discard samples.
- m_mutex.lock();
- for (int i = 0; i < m_scheduledSamples.size(); ++i)
- m_scheduledSamples[i]->Release();
- m_scheduledSamples.clear();
- m_mutex.unlock();
-
- // Restore the timer resolution.
- timeEndPeriod(1);
-
- return S_OK;
-}
-
-HRESULT Scheduler::flush()
-{
- if (m_schedulerThread) {
- // Ask the scheduler thread to flush.
- PostThreadMessage(m_threadID, Flush, 0 , 0);
-
- // Wait for the scheduler thread to signal the flush event,
- // OR for the thread to terminate.
- HANDLE objects[] = { m_flushEvent, m_schedulerThread };
-
- WaitForMultipleObjects(ARRAYSIZE(objects), objects, FALSE, SCHEDULER_TIMEOUT);
- }
-
- return S_OK;
-}
-
-bool Scheduler::areSamplesScheduled()
-{
- QMutexLocker locker(&m_mutex);
- return m_scheduledSamples.count() > 0;
-}
-
-HRESULT Scheduler::scheduleSample(IMFSample *sample, bool presentNow)
-{
- if (!m_schedulerThread)
- return MF_E_NOT_INITIALIZED;
-
- HRESULT hr = S_OK;
- DWORD dwExitCode = 0;
-
- GetExitCodeThread(m_schedulerThread, &dwExitCode);
- if (dwExitCode != STILL_ACTIVE)
- return E_FAIL;
-
- if (presentNow || !m_clock) {
- m_presenter->presentSample(sample);
- } else {
- // Queue the sample and ask the scheduler thread to wake up.
- m_mutex.lock();
- sample->AddRef();
- m_scheduledSamples.enqueue(sample);
- m_mutex.unlock();
-
- if (SUCCEEDED(hr))
- PostThreadMessage(m_threadID, Schedule, 0, 0);
- }
-
- return hr;
-}
-
-HRESULT Scheduler::processSamplesInQueue(LONG *nextSleep)
-{
- HRESULT hr = S_OK;
- LONG wait = 0;
- IMFSample *sample = NULL;
-
- // 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();
-
- // 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.
-
- hr = processSample(sample, &wait);
- qt_evr_safe_release(&sample);
-
- if (FAILED(hr) || wait > 0)
- break;
- }
-
- // 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.
- if (wait == 0)
- wait = INFINITE;
-
- *nextSleep = wait;
- return hr;
-}
-
-HRESULT Scheduler::processSample(IMFSample *sample, LONG *pNextSleep)
-{
- 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;
- }
-
- 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));
-
- // 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));
-
- // Don't present yet.
- presentNow = false;
- }
- }
-
- if (presentNow) {
- m_presenter->presentSample(sample);
- } else {
- // The sample is not ready yet. Return it to the queue.
- m_mutex.lock();
- sample->AddRef();
- m_scheduledSamples.prepend(sample);
- m_mutex.unlock();
- }
-
- *pNextSleep = nextSleep;
-
- return hr;
-}
-
-DWORD WINAPI Scheduler::schedulerThreadProc(LPVOID parameter)
-{
- Scheduler* scheduler = reinterpret_cast<Scheduler*>(parameter);
- if (!scheduler)
- return -1;
- return scheduler->schedulerThreadProcPrivate();
-}
-
-DWORD Scheduler::schedulerThreadProcPrivate()
-{
- HRESULT hr = S_OK;
- MSG msg;
- LONG wait = INFINITE;
- bool exitThread = false;
-
- // Force the system to create a message queue for this thread.
- // (See MSDN documentation for PostThreadMessage.)
- PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
-
- // Signal to the scheduler that the thread is ready.
- SetEvent(m_threadReadyEvent);
-
- while (!exitThread) {
- // Wait for a thread message OR until the wait time expires.
- DWORD result = MsgWaitForMultipleObjects(0, NULL, FALSE, wait, QS_POSTMESSAGE);
-
- if (result == WAIT_TIMEOUT) {
- // If we timed out, then process the samples in the queue
- hr = processSamplesInQueue(&wait);
- if (FAILED(hr))
- exitThread = true;
- }
-
- while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
- bool processSamples = true;
-
- switch (msg.message) {
- case Terminate:
- exitThread = true;
- break;
- 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);
- break;
- case Schedule:
- // Process as many samples as we can.
- if (processSamples) {
- hr = processSamplesInQueue(&wait);
- if (FAILED(hr))
- exitThread = true;
- processSamples = (wait != (LONG)INFINITE);
- }
- break;
- }
- }
-
- }
-
- return (SUCCEEDED(hr) ? 0 : 1);
-}
-
-
-SamplePool::SamplePool()
- : m_initialized(false)
-{
-}
-
-SamplePool::~SamplePool()
-{
- clear();
-}
-
-HRESULT SamplePool::getSample(IMFSample **sample)
-{
- QMutexLocker locker(&m_mutex);
-
- if (!m_initialized)
- return MF_E_NOT_INITIALIZED;
-
- if (m_videoSampleQueue.isEmpty())
- return MF_E_SAMPLEALLOCATOR_EMPTY;
-
- // 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();
-
- // Give the sample to the caller.
- *sample = taken;
- (*sample)->AddRef();
-
- taken->Release();
-
- return S_OK;
-}
-
-HRESULT SamplePool::returnSample(IMFSample *sample)
-{
- QMutexLocker locker(&m_mutex);
-
- if (!m_initialized)
- return MF_E_NOT_INITIALIZED;
-
- m_videoSampleQueue.append(sample);
- sample->AddRef();
-
- return S_OK;
-}
-
-HRESULT SamplePool::initialize(QList<IMFSample*> &samples)
-{
- QMutexLocker locker(&m_mutex);
-
- if (m_initialized)
- return MF_E_INVALIDREQUEST;
-
- // Move these samples into our allocated queue.
- for (auto sample : qAsConst(samples)) {
- sample->AddRef();
- m_videoSampleQueue.append(sample);
- }
-
- m_initialized = true;
-
- for (auto sample : qAsConst(samples))
- sample->Release();
- samples.clear();
- return S_OK;
-}
-
-HRESULT SamplePool::clear()
-{
- QMutexLocker locker(&m_mutex);
-
- for (auto sample : qAsConst(m_videoSampleQueue))
- sample->Release();
- m_videoSampleQueue.clear();
- m_initialized = false;
-
- return S_OK;
-}
-
-
-EVRCustomPresenter::EVRCustomPresenter(QAbstractVideoSurface *surface)
- : QObject()
- , m_sampleFreeCB(this, &EVRCustomPresenter::onSampleFree)
- , m_refCount(1)
- , m_renderState(RenderShutdown)
- , 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)
- , m_clock(0)
- , m_mixer(0)
- , m_mediaEventSink(0)
- , m_mediaType(0)
- , m_surface(0)
- , m_canRenderToSurface(false)
- , m_positionOffset(0)
-{
- // Initial source rectangle = (0,0,1,1)
- m_sourceRect.top = 0;
- m_sourceRect.left = 0;
- m_sourceRect.bottom = 1;
- m_sourceRect.right = 1;
-
- setSurface(surface);
-}
-
-EVRCustomPresenter::~EVRCustomPresenter()
-{
- m_scheduler.flush();
- 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;
-}
-
-HRESULT EVRCustomPresenter::QueryInterface(REFIID riid, void ** ppvObject)
-{
- if (!ppvObject)
- return E_POINTER;
- if (riid == IID_IMFGetService) {
- *ppvObject = static_cast<IMFGetService*>(this);
- } else if (riid == IID_IMFTopologyServiceLookupClient) {
- *ppvObject = static_cast<IMFTopologyServiceLookupClient*>(this);
- } else if (riid == IID_IMFVideoDeviceID) {
- *ppvObject = static_cast<IMFVideoDeviceID*>(this);
- } else if (riid == IID_IMFVideoPresenter) {
- *ppvObject = static_cast<IMFVideoPresenter*>(this);
- } else if (riid == IID_IMFRateSupport) {
- *ppvObject = static_cast<IMFRateSupport*>(this);
- } else if (riid == IID_IUnknown) {
- *ppvObject = static_cast<IUnknown*>(static_cast<IMFGetService*>(this));
- } else if (riid == IID_IMFClockStateSink) {
- *ppvObject = static_cast<IMFClockStateSink*>(this);
- } else {
- *ppvObject = NULL;
- return E_NOINTERFACE;
- }
- AddRef();
- return S_OK;
-}
-
-ULONG EVRCustomPresenter::AddRef()
-{
- return InterlockedIncrement(&m_refCount);
-}
-
-ULONG EVRCustomPresenter::Release()
-{
- ULONG uCount = InterlockedDecrement(&m_refCount);
- if (uCount == 0)
- delete this;
- return uCount;
-}
-
-HRESULT EVRCustomPresenter::GetService(REFGUID guidService, REFIID riid, LPVOID *ppvObject)
-{
- HRESULT hr = S_OK;
-
- if (!ppvObject)
- return E_POINTER;
-
- // The only service GUID that we support is MR_VIDEO_RENDER_SERVICE.
- if (guidService != mr_VIDEO_RENDER_SERVICE)
- return MF_E_UNSUPPORTED_SERVICE;
-
- // First try to get the service interface from the D3DPresentEngine object.
- hr = m_presentEngine->getService(guidService, riid, ppvObject);
- if (FAILED(hr))
- // Next, check if this object supports the interface.
- hr = QueryInterface(riid, ppvObject);
-
- return hr;
-}
-
-HRESULT EVRCustomPresenter::GetDeviceID(IID* deviceID)
-{
- if (!deviceID)
- return E_POINTER;
-
- *deviceID = iid_IDirect3DDevice9;
-
- return S_OK;
-}
-
-HRESULT EVRCustomPresenter::InitServicePointers(IMFTopologyServiceLookup *lookup)
-{
- if (!lookup)
- return E_POINTER;
-
- HRESULT hr = S_OK;
- DWORD objectCount = 0;
-
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- // Do not allow initializing when playing or paused.
- if (isActive())
- return MF_E_INVALIDREQUEST;
-
- qt_evr_safe_release(&m_clock);
- qt_evr_safe_release(&m_mixer);
- qt_evr_safe_release(&m_mediaEventSink);
-
- // Ask for the clock. Optional, because the EVR might not have a clock.
- objectCount = 1;
-
- lookup->LookupService(MF_SERVICE_LOOKUP_GLOBAL, 0,
- mr_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_clock),
- &objectCount
- );
-
- // Ask for the mixer. (Required.)
- objectCount = 1;
-
- hr = lookup->LookupService(MF_SERVICE_LOOKUP_GLOBAL, 0,
- mr_VIDEO_MIXER_SERVICE, IID_PPV_ARGS(&m_mixer),
- &objectCount
- );
-
- if (FAILED(hr))
- return hr;
-
- // Make sure that we can work with this mixer.
- hr = configureMixer(m_mixer);
- if (FAILED(hr))
- return hr;
-
- // Ask for the EVR's event-sink interface. (Required.)
- objectCount = 1;
-
- hr = lookup->LookupService(MF_SERVICE_LOOKUP_GLOBAL, 0,
- mr_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_mediaEventSink),
- &objectCount
- );
-
- if (SUCCEEDED(hr))
- m_renderState = RenderStopped;
-
- return hr;
-}
-
-HRESULT EVRCustomPresenter::ReleaseServicePointers()
-{
- // Enter the shut-down state.
- m_mutex.lock();
-
- m_renderState = RenderShutdown;
-
- m_mutex.unlock();
-
- // Flush any samples that were scheduled.
- flush();
-
- // Clear the media type and release related resources.
- 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);
-
- return S_OK;
-}
-
-bool EVRCustomPresenter::isValid() const
-{
- return m_presentEngine->isValid() && m_canRenderToSurface;
-}
-
-HRESULT EVRCustomPresenter::ProcessMessage(MFVP_MESSAGE_TYPE message, ULONG_PTR param)
-{
- HRESULT hr = S_OK;
-
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- hr = checkShutdown();
- if (FAILED(hr))
- return hr;
-
- switch (message) {
- // Flush all pending samples.
- case MFVP_MESSAGE_FLUSH:
- hr = flush();
- break;
-
- // Renegotiate the media type with the mixer.
- case MFVP_MESSAGE_INVALIDATEMEDIATYPE:
- hr = renegotiateMediaType();
- break;
-
- // The mixer received a new input sample.
- case MFVP_MESSAGE_PROCESSINPUTNOTIFY:
- hr = processInputNotify();
- break;
-
- // Streaming is about to start.
- case MFVP_MESSAGE_BEGINSTREAMING:
- hr = beginStreaming();
- break;
-
- // Streaming has ended. (The EVR has stopped.)
- case MFVP_MESSAGE_ENDSTREAMING:
- hr = endStreaming();
- break;
-
- // All input streams have ended.
- case MFVP_MESSAGE_ENDOFSTREAM:
- // Set the EOS flag.
- m_endStreaming = true;
- // Check if it's time to send the EC_COMPLETE event to the EVR.
- hr = checkEndOfStream();
- break;
-
- // Frame-stepping is starting.
- case MFVP_MESSAGE_STEP:
- hr = prepareFrameStep(DWORD(param));
- break;
-
- // Cancels frame-stepping.
- case MFVP_MESSAGE_CANCELSTEP:
- hr = cancelFrameStep();
- break;
-
- default:
- hr = E_INVALIDARG; // Unknown message. This case should never occur.
- break;
- }
-
- return hr;
-}
-
-HRESULT EVRCustomPresenter::GetCurrentMediaType(IMFVideoMediaType **mediaType)
-{
- HRESULT hr = S_OK;
-
- if (!mediaType)
- return E_POINTER;
-
- *mediaType = NULL;
-
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- hr = checkShutdown();
- if (FAILED(hr))
- return hr;
-
- if (!m_mediaType)
- return MF_E_NOT_INITIALIZED;
-
- return m_mediaType->QueryInterface(IID_PPV_ARGS(mediaType));
-}
-
-HRESULT EVRCustomPresenter::OnClockStart(MFTIME, LONGLONG clockStartOffset)
-{
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- // We cannot start after shutdown.
- HRESULT hr = checkShutdown();
- if (FAILED(hr))
- return hr;
-
- // Check if the clock is already active (not stopped).
- if (isActive()) {
- m_renderState = RenderStarted;
-
- // If the clock position changes while the clock is active, it
- // is a seek request. We need to flush all pending samples.
- if (clockStartOffset != PRESENTATION_CURRENT_POSITION)
- flush();
- } else {
- m_renderState = RenderStarted;
-
- // The clock has started from the stopped state.
-
- // Possibly we are in the middle of frame-stepping OR have samples waiting
- // in the frame-step queue. Deal with these two cases first:
- hr = startFrameStep();
- if (FAILED(hr))
- return hr;
- }
-
- // Now try to get new output samples from the mixer.
- processOutputLoop();
-
- return hr;
-}
-
-HRESULT EVRCustomPresenter::OnClockRestart(MFTIME)
-{
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- HRESULT hr = checkShutdown();
- if (FAILED(hr))
- return hr;
-
- // The EVR calls OnClockRestart only while paused.
-
- m_renderState = RenderStarted;
-
- // Possibly we are in the middle of frame-stepping OR we have samples waiting
- // in the frame-step queue. Deal with these two cases first:
- hr = startFrameStep();
- if (FAILED(hr))
- return hr;
-
- // Now resume the presentation loop.
- processOutputLoop();
-
- return hr;
-}
-
-HRESULT EVRCustomPresenter::OnClockStop(MFTIME)
-{
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- HRESULT hr = checkShutdown();
- if (FAILED(hr))
- return hr;
-
- if (m_renderState != RenderStopped) {
- m_renderState = RenderStopped;
- flush();
-
- // If we are in the middle of frame-stepping, cancel it now.
- if (m_frameStep.state != FrameStepNone)
- cancelFrameStep();
- }
-
- return S_OK;
-}
-
-HRESULT EVRCustomPresenter::OnClockPause(MFTIME)
-{
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- // We cannot pause the clock after shutdown.
- HRESULT hr = checkShutdown();
-
- if (SUCCEEDED(hr))
- m_renderState = RenderPaused;
-
- return hr;
-}
-
-HRESULT EVRCustomPresenter::OnClockSetRate(MFTIME, float rate)
-{
- // Note:
- // The presenter reports its maximum rate through the IMFRateSupport interface.
- // Here, we assume that the EVR honors the maximum rate.
-
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- HRESULT hr = checkShutdown();
- if (FAILED(hr))
- return hr;
-
- // If the rate is changing from zero (scrubbing) to non-zero, cancel the
- // 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();
- }
-
- m_playbackRate = rate;
-
- // Tell the scheduler about the new rate.
- m_scheduler.setClockRate(rate);
-
- return S_OK;
-}
-
-HRESULT EVRCustomPresenter::GetSlowestRate(MFRATE_DIRECTION, BOOL, float *rate)
-{
- if (!rate)
- return E_POINTER;
-
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- HRESULT hr = checkShutdown();
-
- if (SUCCEEDED(hr)) {
- // There is no minimum playback rate, so the minimum is zero.
- *rate = 0;
- }
-
- return S_OK;
-}
-
-HRESULT EVRCustomPresenter::GetFastestRate(MFRATE_DIRECTION direction, BOOL thin, float *rate)
-{
- if (!rate)
- return E_POINTER;
-
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- float maxRate = 0.0f;
-
- HRESULT hr = checkShutdown();
- if (FAILED(hr))
- return hr;
-
- // Get the maximum *forward* rate.
- maxRate = getMaxRate(thin);
-
- // For reverse playback, it's the negative of maxRate.
- if (direction == MFRATE_REVERSE)
- maxRate = -maxRate;
-
- *rate = maxRate;
-
- return S_OK;
-}
-
-HRESULT EVRCustomPresenter::IsRateSupported(BOOL thin, float rate, float *nearestSupportedRate)
-{
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- float maxRate = 0.0f;
- float nearestRate = rate; // If we support rate, that is the nearest.
-
- HRESULT hr = checkShutdown();
- if (FAILED(hr))
- return hr;
-
- // Find the maximum forward rate.
- // Note: We have no minimum rate (that is, we support anything down to 0).
- maxRate = getMaxRate(thin);
-
- if (qFabs(rate) > maxRate) {
- // The (absolute) requested rate exceeds the maximum rate.
- hr = MF_E_UNSUPPORTED_RATE;
-
- // The nearest supported rate is maxRate.
- nearestRate = maxRate;
- if (rate < 0) {
- // Negative for reverse playback.
- nearestRate = -nearestRate;
- }
- }
-
- // Return the nearest supported rate.
- if (nearestSupportedRate)
- *nearestSupportedRate = nearestRate;
-
- return hr;
-}
-
-void EVRCustomPresenter::supportedFormatsChanged()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- m_canRenderToSurface = false;
- m_presentEngine->setHint(D3DPresentEngine::RenderToTexture, false);
-
- // check if we can render to the surface (compatible formats)
- if (m_surface) {
- QList<QVideoFrame::PixelFormat> formats = m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle);
- if (m_presentEngine->supportsTextureRendering() && formats.contains(QVideoFrame::Format_RGB32)) {
- m_presentEngine->setHint(D3DPresentEngine::RenderToTexture, true);
- m_canRenderToSurface = true;
- } else {
- formats = m_surface->supportedPixelFormats(QAbstractVideoBuffer::NoHandle);
- for (QVideoFrame::PixelFormat format : qAsConst(formats)) {
- if (SUCCEEDED(m_presentEngine->checkFormat(qt_evr_D3DFormatFromPixelFormat(format)))) {
- m_canRenderToSurface = true;
- break;
- }
- }
- }
- }
-
- // TODO: if media type already set, renegotiate?
-}
-
-void EVRCustomPresenter::setSurface(QAbstractVideoSurface *surface)
-{
- m_mutex.lock();
-
- if (m_surface) {
- disconnect(m_surface, &QAbstractVideoSurface::supportedFormatsChanged,
- this, &EVRCustomPresenter::supportedFormatsChanged);
- }
-
- m_surface = surface;
-
- if (m_surface) {
- connect(m_surface, &QAbstractVideoSurface::supportedFormatsChanged,
- this, &EVRCustomPresenter::supportedFormatsChanged);
- }
-
- m_mutex.unlock();
-
- supportedFormatsChanged();
-}
-
-HRESULT EVRCustomPresenter::configureMixer(IMFTransform *mixer)
-{
- // Set the zoom rectangle (ie, the source clipping rectangle).
- return setMixerSourceRect(mixer, m_sourceRect);
-}
-
-HRESULT EVRCustomPresenter::renegotiateMediaType()
-{
- HRESULT hr = S_OK;
- bool foundMediaType = false;
-
- IMFMediaType *mixerType = NULL;
- IMFMediaType *optimalType = NULL;
-
- if (!m_mixer)
- return MF_E_INVALIDREQUEST;
-
- // Loop through all of the mixer's proposed output types.
- DWORD typeIndex = 0;
- while (!foundMediaType && (hr != MF_E_NO_MORE_TYPES)) {
- qt_evr_safe_release(&mixerType);
- qt_evr_safe_release(&optimalType);
-
- // Step 1. Get the next media type supported by mixer.
- hr = m_mixer->GetOutputAvailableType(0, typeIndex++, &mixerType);
- if (FAILED(hr))
- break;
-
- // From now on, if anything in this loop fails, try the next type,
- // until we succeed or the mixer runs out of types.
-
- // Step 2. Check if we support this media type.
- if (SUCCEEDED(hr))
- hr = isMediaTypeSupported(mixerType);
-
- // Step 3. Adjust the mixer's type to match our requirements.
- if (SUCCEEDED(hr))
- hr = createOptimalVideoType(mixerType, &optimalType);
-
- // Step 4. Check if the mixer will accept this media type.
- if (SUCCEEDED(hr))
- hr = m_mixer->SetOutputType(0, optimalType, MFT_SET_TYPE_TEST_ONLY);
-
- // Step 5. Try to set the media type on ourselves.
- if (SUCCEEDED(hr))
- hr = setMediaType(optimalType);
-
- // Step 6. Set output media type on mixer.
- if (SUCCEEDED(hr)) {
- hr = m_mixer->SetOutputType(0, optimalType, 0);
-
- // If something went wrong, clear the media type.
- if (FAILED(hr))
- setMediaType(NULL);
- }
-
- if (SUCCEEDED(hr))
- foundMediaType = true;
- }
-
- qt_evr_safe_release(&mixerType);
- qt_evr_safe_release(&optimalType);
-
- return hr;
-}
-
-HRESULT EVRCustomPresenter::flush()
-{
- m_prerolled = false;
-
- // The scheduler might have samples that are waiting for
- // their presentation time. Tell the scheduler to flush.
-
- // This call blocks until the scheduler threads discards all scheduled samples.
- 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_surface && m_surface->isActive()) {
- // Repaint with black.
- presentSample(NULL);
- }
-
- return S_OK;
-}
-
-HRESULT EVRCustomPresenter::processInputNotify()
-{
- HRESULT hr = S_OK;
-
- // Set the flag that says the mixer has a new sample.
- m_sampleNotify = true;
-
- if (!m_mediaType) {
- // We don't have a valid media type yet.
- hr = MF_E_TRANSFORM_TYPE_NOT_SET;
- } else {
- // Try to process an output sample.
- processOutputLoop();
- }
- return hr;
-}
-
-HRESULT EVRCustomPresenter::beginStreaming()
-{
- HRESULT hr = S_OK;
-
- // Start the scheduler thread.
- hr = m_scheduler.startScheduler(m_clock);
-
- return hr;
-}
-
-HRESULT EVRCustomPresenter::endStreaming()
-{
- HRESULT hr = S_OK;
-
- // Stop the scheduler thread.
- hr = m_scheduler.stopScheduler();
-
- return hr;
-}
-
-HRESULT EVRCustomPresenter::checkEndOfStream()
-{
- if (!m_endStreaming) {
- // The EVR did not send the MFVP_MESSAGE_ENDOFSTREAM message.
- return S_OK;
- }
-
- if (m_sampleNotify) {
- // The mixer still has input.
- return S_OK;
- }
-
- if (m_scheduler.areSamplesScheduled()) {
- // Samples are still scheduled for rendering.
- return S_OK;
- }
-
- // Everything is complete. Now we can tell the EVR that we are done.
- notifyEvent(EC_COMPLETE, (LONG_PTR)S_OK, 0);
- m_endStreaming = false;
-
- stopSurface();
- return S_OK;
-}
-
-HRESULT EVRCustomPresenter::prepareFrameStep(DWORD steps)
-{
- HRESULT hr = S_OK;
-
- // Cache the step count.
- m_frameStep.steps += steps;
-
- // Set the frame-step state.
- m_frameStep.state = FrameStepWaitingStart;
-
- // If the clock is are already running, we can start frame-stepping now.
- // Otherwise, we will start when the clock starts.
- if (m_renderState == RenderStarted)
- hr = startFrameStep();
-
- return hr;
-}
-
-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.
- m_frameStep.state = FrameStepPending;
-
- // 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();
-
- hr = deliverFrameStepSample(sample);
- if (FAILED(hr))
- goto done;
-
- qt_evr_safe_release(&sample);
-
- // We break from this loop when:
- // (a) the frame-step queue is empty, or
- // (b) the frame-step operation is complete.
- }
- } else if (m_frameStep.state == FrameStepNone) {
- // 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();
-
- hr = deliverSample(sample, false);
- if (FAILED(hr))
- goto done;
-
- qt_evr_safe_release(&sample);
- }
- }
-
-done:
- qt_evr_safe_release(&sample);
- return hr;
-}
-
-HRESULT EVRCustomPresenter::completeFrameStep(IMFSample *sample)
-{
- HRESULT hr = S_OK;
- MFTIME sampleTime = 0;
- MFTIME systemTime = 0;
-
- // Update our state.
- m_frameStep.state = FrameStepComplete;
- m_frameStep.sampleNoRef = 0;
-
- // Notify the EVR that the frame-step is complete.
- notifyEvent(EC_STEP_COMPLETE, FALSE, 0); // FALSE = completed (not cancelled)
-
- // If we are scrubbing (rate == 0), also send the "scrub time" event.
- if (isScrubbing()) {
- // Get the time stamp from the sample.
- hr = sample->GetSampleTime(&sampleTime);
- if (FAILED(hr)) {
- // No time stamp. Use the current presentation time.
- if (m_clock)
- m_clock->GetCorrelatedTime(0, &sampleTime, &systemTime);
-
- hr = S_OK; // (Not an error condition.)
- }
-
- notifyEvent(EC_SCRUB_TIME, DWORD(sampleTime), DWORD(((sampleTime) >> 32) & 0xffffffff));
- }
- return hr;
-}
-
-HRESULT EVRCustomPresenter::cancelFrameStep()
-{
- FrameStepState oldState = m_frameStep.state;
-
- m_frameStep.state = FrameStepNone;
- m_frameStep.steps = 0;
- m_frameStep.sampleNoRef = 0;
- // Don't clear the frame-step queue yet, because we might frame step again.
-
- if (oldState > FrameStepNone && oldState < FrameStepComplete) {
- // We were in the middle of frame-stepping when it was cancelled.
- // Notify the EVR.
- notifyEvent(EC_STEP_COMPLETE, TRUE, 0); // TRUE = cancelled
- }
- return S_OK;
-}
-
-HRESULT EVRCustomPresenter::createOptimalVideoType(IMFMediaType *proposedType, IMFMediaType **optimalType)
-{
- HRESULT hr = S_OK;
-
- RECT rcOutput;
- ZeroMemory(&rcOutput, sizeof(rcOutput));
-
- MFVideoArea displayArea;
- ZeroMemory(&displayArea, sizeof(displayArea));
-
- IMFMediaType *mtOptimal = NULL;
-
- UINT64 size;
- int width;
- int height;
-
- // Clone the proposed type.
-
- hr = MFCreateMediaType(&mtOptimal);
- if (FAILED(hr))
- goto done;
-
- hr = proposedType->CopyAllItems(mtOptimal);
- if (FAILED(hr))
- goto done;
-
- // Modify the new type.
-
- 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;
-
- // Set the geometric aperture, and disable pan/scan.
- displayArea = qt_evr_makeMFArea(0, 0, rcOutput.right, rcOutput.bottom);
-
- hr = mtOptimal->SetUINT32(MF_MT_PAN_SCAN_ENABLED, FALSE);
- if (FAILED(hr))
- goto done;
-
- hr = mtOptimal->SetBlob(MF_MT_GEOMETRIC_APERTURE, reinterpret_cast<UINT8*>(&displayArea),
- sizeof(displayArea));
- if (FAILED(hr))
- goto done;
-
- // Set the pan/scan aperture and the minimum display aperture. We don't care
- // about them per se, but the mixer will reject the type if these exceed the
- // frame dimentions.
- hr = mtOptimal->SetBlob(MF_MT_PAN_SCAN_APERTURE, reinterpret_cast<UINT8*>(&displayArea),
- sizeof(displayArea));
- if (FAILED(hr))
- goto done;
-
- hr = mtOptimal->SetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, reinterpret_cast<UINT8*>(&displayArea),
- sizeof(displayArea));
- if (FAILED(hr))
- goto done;
-
- // Return the pointer to the caller.
- *optimalType = mtOptimal;
- (*optimalType)->AddRef();
-
-done:
- qt_evr_safe_release(&mtOptimal);
- return hr;
-
-}
-
-HRESULT EVRCustomPresenter::setMediaType(IMFMediaType *mediaType)
-{
- // Note: mediaType can be NULL (to clear the type)
-
- // Clearing the media type is allowed in any state (including shutdown).
- if (!mediaType) {
- stopSurface();
- qt_evr_safe_release(&m_mediaType);
- releaseResources();
- return S_OK;
- }
-
- MFRatio fps = { 0, 0 };
- QList<IMFSample*> sampleQueue;
-
- // Cannot set the media type after shutdown.
- HRESULT hr = checkShutdown();
- if (FAILED(hr))
- goto done;
-
- // Check if the new type is actually different.
- // Note: This function safely handles NULL input parameters.
- if (qt_evr_areMediaTypesEqual(m_mediaType, 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);
- releaseResources();
-
- // Initialize the presenter engine with the new media type.
- // The presenter engine allocates the samples.
-
- hr = m_presentEngine->createVideoSamples(mediaType, sampleQueue);
- 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)) {
- hr = sample->SetUINT32(MFSamplePresenter_SampleCounter, m_tokenCounter);
- if (FAILED(hr))
- goto done;
- }
-
- // Add the samples to the sample pool.
- hr = m_samplePool.initialize(sampleQueue);
- if (FAILED(hr))
- goto done;
-
- // Set the frame rate on the scheduler.
- if (SUCCEEDED(qt_evr_getFrameRate(mediaType, &fps)) && (fps.Numerator != 0) && (fps.Denominator != 0)) {
- m_scheduler.setFrameRate(fps);
- } else {
- // NOTE: The mixer's proposed type might not have a frame rate, in which case
- // we'll use an arbitrary default. (Although it's unlikely the video source
- // does not have a frame rate.)
- m_scheduler.setFrameRate(g_DefaultFrameRate);
- }
-
- // Store the media type.
- m_mediaType = mediaType;
- m_mediaType->AddRef();
-
- startSurface();
-
-done:
- if (FAILED(hr))
- releaseResources();
- return hr;
-}
-
-HRESULT EVRCustomPresenter::isMediaTypeSupported(IMFMediaType *proposed)
-{
- D3DFORMAT d3dFormat = D3DFMT_UNKNOWN;
- BOOL compressed = FALSE;
- MFVideoInterlaceMode interlaceMode = MFVideoInterlace_Unknown;
- MFVideoArea videoCropArea;
- UINT32 width = 0, height = 0;
-
- // Validate the format.
- HRESULT hr = qt_evr_getFourCC(proposed, reinterpret_cast<DWORD*>(&d3dFormat));
- if (FAILED(hr))
- return hr;
-
- QVideoFrame::PixelFormat pixelFormat = pixelFormatFromMediaType(proposed);
- if (pixelFormat == QVideoFrame::Format_Invalid)
- return MF_E_INVALIDMEDIATYPE;
-
- // When not rendering to texture, only accept pixel formats supported by the video surface
- if (!m_presentEngine->isTextureRenderingEnabled()
- && m_surface
- && !m_surface->supportedPixelFormats().contains(pixelFormat)) {
- return MF_E_INVALIDMEDIATYPE;
- }
-
- // Reject compressed media types.
- hr = proposed->IsCompressedFormat(&compressed);
- if (FAILED(hr))
- return hr;
-
- if (compressed)
- return MF_E_INVALIDMEDIATYPE;
-
- // The D3DPresentEngine checks whether surfaces can be created using this format
- hr = m_presentEngine->checkFormat(d3dFormat);
- if (FAILED(hr))
- return hr;
-
- // Reject interlaced formats.
- hr = proposed->GetUINT32(MF_MT_INTERLACE_MODE, reinterpret_cast<UINT32*>(&interlaceMode));
- if (FAILED(hr))
- return hr;
-
- if (interlaceMode != MFVideoInterlace_Progressive)
- return MF_E_INVALIDMEDIATYPE;
-
- hr = MFGetAttributeSize(proposed, MF_MT_FRAME_SIZE, &width, &height);
- if (FAILED(hr))
- return hr;
-
- // Validate the various apertures (cropping regions) against the frame size.
- // Any of these apertures may be unspecified in the media type, in which case
- // we ignore it. We just want to reject invalid apertures.
-
- if (SUCCEEDED(proposed->GetBlob(MF_MT_PAN_SCAN_APERTURE,
- reinterpret_cast<UINT8*>(&videoCropArea),
- sizeof(videoCropArea), nullptr))) {
- hr = qt_evr_validateVideoArea(videoCropArea, width, height);
- }
- if (SUCCEEDED(proposed->GetBlob(MF_MT_GEOMETRIC_APERTURE,
- reinterpret_cast<UINT8*>(&videoCropArea),
- sizeof(videoCropArea), nullptr))) {
- hr = qt_evr_validateVideoArea(videoCropArea, width, height);
- }
- if (SUCCEEDED(proposed->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE,
- reinterpret_cast<UINT8*>(&videoCropArea),
- sizeof(videoCropArea), nullptr))) {
- hr = qt_evr_validateVideoArea(videoCropArea, width, height);
- }
- return hr;
-}
-
-void EVRCustomPresenter::processOutputLoop()
-{
- HRESULT hr = S_OK;
-
- // Process as many samples as possible.
- while (hr == S_OK) {
- // If the mixer doesn't have a new input sample, break from the loop.
- if (!m_sampleNotify) {
- hr = MF_E_TRANSFORM_NEED_MORE_INPUT;
- break;
- }
-
- // Try to process a sample.
- hr = processOutput();
-
- // NOTE: ProcessOutput can return S_FALSE to indicate it did not
- // process a sample. If so, break out of the loop.
- }
-
- if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
- // The mixer has run out of input data. Check for end-of-stream.
- checkEndOfStream();
- }
-}
-
-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)
- return S_FALSE;
-
- // Make sure we have a pointer to the mixer.
- if (!m_mixer)
- 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;
-
- // 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);
-
- 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);
-
- if (FAILED(hr)) {
- // Return the sample to the pool.
- HRESULT hr2 = m_samplePool.returnSample(sample);
- if (FAILED(hr2)) {
- hr = hr2;
- goto done;
- }
- // 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.
- hr = renegotiateMediaType();
- } else if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
- // There was a dynamic media type change. Clear our media type.
- setMediaType(NULL);
- } else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
- // The mixer needs more input.
- // 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.
-
- m_clock->GetCorrelatedTime(0, &mixerEndTime, &systemTime);
-
- LONGLONG latencyTime = mixerEndTime - mixerStartTime;
- notifyEvent(EC_PROCESSING_LATENCY, reinterpret_cast<LONG_PTR>(&latencyTime), 0);
- }
-
- // Set up notification for when the sample is released.
- hr = trackSample(sample);
- if (FAILED(hr))
- goto done;
-
- // 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;
- }
-
- m_prerolled = true; // We have presented at least one sample now.
- }
-
-done:
- qt_evr_safe_release(&sample);
-
- // Important: Release any events returned from the ProcessOutput method.
- qt_evr_safe_release(&dataBuffer.pEvents);
- return hr;
-}
-
-HRESULT EVRCustomPresenter::deliverSample(IMFSample *sample, bool repaint)
-{
- // 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,
- // schedule it normally.
-
- bool presentNow = ((m_renderState != RenderStarted) || isScrubbing() || repaint);
-
- HRESULT hr = m_scheduler.scheduleSample(sample, presentNow);
-
- if (FAILED(hr)) {
- // Notify the EVR that we have failed during streaming. The EVR will notify the
- // pipeline.
-
- notifyEvent(EC_ERRORABORT, hr, 0);
- }
-
- return hr;
-}
-
-HRESULT EVRCustomPresenter::deliverFrameStepSample(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)) {
- // 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.
-
- // Decrement the number of steps.
- if (m_frameStep.steps > 0)
- m_frameStep.steps--;
-
- if (m_frameStep.steps > 0) {
- // This is not the last step. Discard this sample.
- } else if (m_frameStep.state == FrameStepWaitingStart) {
- // 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);
- if (FAILED(hr))
- goto done;
-
- // Query for IUnknown so that we can identify the sample later.
- // Per COM rules, an object always returns the same pointer when QI'ed for IUnknown.
- hr = sample->QueryInterface(IID_PPV_ARGS(&unk));
- if (FAILED(hr))
- goto done;
-
- m_frameStep.sampleNoRef = reinterpret_cast<DWORD_PTR>(unk); // No add-ref.
-
- // NOTE: We do not AddRef the IUnknown pointer, because that would prevent the
- // sample from invoking the OnSampleFree callback after the sample is presented.
- // We use this IUnknown pointer purely to identify the sample later; we never
- // attempt to dereference the pointer.
-
- m_frameStep.state = FrameStepScheduled;
- }
- }
-done:
- qt_evr_safe_release(&unk);
- return hr;
-}
-
-HRESULT EVRCustomPresenter::trackSample(IMFSample *sample)
-{
- IMFTrackedSample *tracked = NULL;
-
- HRESULT hr = sample->QueryInterface(IID_PPV_ARGS(&tracked));
-
- if (SUCCEEDED(hr))
- hr = tracked->SetAllocator(&m_sampleFreeCB, NULL);
-
- qt_evr_safe_release(&tracked);
- return hr;
-}
-
-void EVRCustomPresenter::releaseResources()
-{
- // Increment the token counter to indicate that all existing video samples
- // are "stale." As these samples get released, we'll dispose of them.
- //
- // Note: The token counter is required because the samples are shared
- // between more than one thread, and they are returned to the presenter
- // through an asynchronous callback (onSampleFree). Without the token, we
- // might accidentally re-use a stale sample after the ReleaseResources
- // method returns.
-
- m_tokenCounter++;
-
- flush();
-
- m_samplePool.clear();
-
- m_presentEngine->releaseResources();
-}
-
-HRESULT EVRCustomPresenter::onSampleFree(IMFAsyncResult *result)
-{
- IUnknown *object = NULL;
- IMFSample *sample = NULL;
- IUnknown *unk = NULL;
- UINT32 token;
-
- // Get the sample from the async result object.
- HRESULT hr = result->GetObject(&object);
- if (FAILED(hr))
- goto done;
-
- hr = object->QueryInterface(IID_PPV_ARGS(&sample));
- if (FAILED(hr))
- goto done;
-
- // If this sample was submitted for a frame-step, the frame step operation
- // is complete.
-
- if (m_frameStep.state == FrameStepScheduled) {
- // Query the sample for IUnknown and compare it to our cached value.
- hr = sample->QueryInterface(IID_PPV_ARGS(&unk));
- if (FAILED(hr))
- goto done;
-
- if (m_frameStep.sampleNoRef == reinterpret_cast<DWORD_PTR>(unk)) {
- // Notify the EVR.
- hr = completeFrameStep(sample);
- if (FAILED(hr))
- goto done;
- }
-
- // Note: Although object is also an IUnknown pointer, it is not
- // guaranteed to be the exact pointer value returned through
- // QueryInterface. Therefore, the second QueryInterface call is
- // required.
- }
-
- m_mutex.lock();
-
- token = MFGetAttributeUINT32(sample, MFSamplePresenter_SampleCounter, (UINT32)-1);
-
- 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_mutex.unlock();
-
-done:
- if (FAILED(hr))
- notifyEvent(EC_ERRORABORT, hr, 0);
- qt_evr_safe_release(&object);
- qt_evr_safe_release(&sample);
- qt_evr_safe_release(&unk);
- return hr;
-}
-
-float EVRCustomPresenter::getMaxRate(bool thin)
-{
- // Non-thinned:
- // If we have a valid frame rate and a monitor refresh rate, the maximum
- // playback rate is equal to the refresh rate. Otherwise, the maximum rate
- // is unbounded (FLT_MAX).
-
- // Thinned: The maximum rate is unbounded.
-
- float maxRate = FLT_MAX;
- MFRatio fps = { 0, 0 };
- UINT monitorRateHz = 0;
-
- if (!thin && m_mediaType) {
- qt_evr_getFrameRate(m_mediaType, &fps);
- monitorRateHz = m_presentEngine->refreshRate();
-
- if (fps.Denominator && fps.Numerator && monitorRateHz) {
- // Max Rate = Refresh Rate / Frame Rate
- maxRate = (float)MulDiv(monitorRateHz, fps.Denominator, fps.Numerator);
- }
- }
-
- return maxRate;
-}
-
-bool EVRCustomPresenter::event(QEvent *e)
-{
- switch (int(e->type())) {
- case StartSurface:
- startSurface();
- return true;
- case StopSurface:
- stopSurface();
- return true;
- case PresentSample:
- presentSample(static_cast<PresentSampleEvent *>(e)->sample());
- return true;
- default:
- break;
- }
- return QObject::event(e);
-}
-
-void EVRCustomPresenter::startSurface()
-{
- if (thread() != QThread::currentThread()) {
- QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StartSurface)));
- return;
- }
-
- if (!m_surface || m_surface->isActive())
- return;
-
- QVideoSurfaceFormat format = m_presentEngine->videoSurfaceFormat();
- if (!format.isValid())
- return;
-
- m_surface->start(format);
-}
-
-void EVRCustomPresenter::stopSurface()
-{
- if (thread() != QThread::currentThread()) {
- QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StopSurface)));
- return;
- }
-
- if (!m_surface || !m_surface->isActive())
- return;
-
- m_surface->stop();
-}
-
-void EVRCustomPresenter::presentSample(IMFSample *sample)
-{
- if (thread() != QThread::currentThread()) {
- QCoreApplication::postEvent(this, new PresentSampleEvent(sample));
- return;
- }
-
- if (!m_surface || !m_presentEngine->videoSurfaceFormat().isValid())
- return;
-
- QVideoFrame frame = m_presentEngine->makeVideoFrame(sample);
-
- // Since start/end times are related to a position when the clock is started,
- // to have times from the beginning, need to adjust it by adding seeked position.
- if (m_positionOffset) {
- if (frame.startTime())
- frame.setStartTime(frame.startTime() + m_positionOffset);
- if (frame.endTime())
- frame.setEndTime(frame.endTime() + m_positionOffset);
- }
-
- if (!m_surface->isActive() || m_surface->surfaceFormat() != m_presentEngine->videoSurfaceFormat()) {
- m_surface->stop();
- if (!m_surface->start(m_presentEngine->videoSurfaceFormat()))
- return;
- }
-
- m_surface->present(frame);
-}
-
-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)
- return E_POINTER;
-
- IMFAttributes *attributes = NULL;
-
- HRESULT hr = mixer->GetAttributes(&attributes);
- if (SUCCEEDED(hr)) {
- hr = attributes->SetBlob(video_ZOOM_RECT, reinterpret_cast<const UINT8*>(&sourceRect),
- sizeof(sourceRect));
- attributes->Release();
- }
- return hr;
-}
-
-static QVideoFrame::PixelFormat pixelFormatFromMediaType(IMFMediaType *type)
-{
- GUID majorType;
- if (FAILED(type->GetMajorType(&majorType)))
- return QVideoFrame::Format_Invalid;
- if (majorType != MFMediaType_Video)
- return QVideoFrame::Format_Invalid;
-
- GUID subtype;
- if (FAILED(type->GetGUID(MF_MT_SUBTYPE, &subtype)))
- return QVideoFrame::Format_Invalid;
-
- if (subtype == MFVideoFormat_RGB32)
- return QVideoFrame::Format_RGB32;
- if (subtype == MFVideoFormat_ARGB32)
- return QVideoFrame::Format_ARGB32;
- if (subtype == MFVideoFormat_RGB24)
- return QVideoFrame::Format_RGB24;
- if (subtype == MFVideoFormat_RGB565)
- return QVideoFrame::Format_RGB565;
- if (subtype == MFVideoFormat_RGB555)
- return QVideoFrame::Format_RGB555;
- if (subtype == MFVideoFormat_AYUV)
- return QVideoFrame::Format_AYUV444;
- if (subtype == MFVideoFormat_I420)
- return QVideoFrame::Format_YUV420P;
- if (subtype == MFVideoFormat_UYVY)
- return QVideoFrame::Format_UYVY;
- if (subtype == MFVideoFormat_YV12)
- return QVideoFrame::Format_YV12;
- if (subtype == MFVideoFormat_NV12)
- return QVideoFrame::Format_NV12;
-
- return QVideoFrame::Format_Invalid;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/common/evr/evrcustompresenter.h b/src/plugins/common/evr/evrcustompresenter.h
deleted file mode 100644
index c1c21580e..000000000
--- a/src/plugins/common/evr/evrcustompresenter.h
+++ /dev/null
@@ -1,377 +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 EVRCUSTOMPRESENTER_H
-#define EVRCUSTOMPRESENTER_H
-
-#include <QObject>
-#include <qmutex.h>
-#include <qqueue.h>
-#include <qevent.h>
-#include <qvideosurfaceformat.h>
-
-#include "evrdefs.h"
-
-QT_BEGIN_NAMESPACE
-
-class EVRCustomPresenter;
-class D3DPresentEngine;
-
-class QAbstractVideoSurface;
-
-template<class T>
-class AsyncCallback : public IMFAsyncCallback
-{
- Q_DISABLE_COPY(AsyncCallback)
-public:
- typedef HRESULT (T::*InvokeFn)(IMFAsyncResult *asyncResult);
-
- AsyncCallback(T *parent, InvokeFn fn) : m_parent(parent), m_invokeFn(fn)
- {
- }
-
- // IUnknown
- STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override
- {
- if (!ppv)
- return E_POINTER;
-
- if (iid == __uuidof(IUnknown)) {
- *ppv = static_cast<IUnknown*>(static_cast<IMFAsyncCallback*>(this));
- } else if (iid == __uuidof(IMFAsyncCallback)) {
- *ppv = static_cast<IMFAsyncCallback*>(this);
- } else {
- *ppv = NULL;
- return E_NOINTERFACE;
- }
- AddRef();
- return S_OK;
- }
-
- STDMETHODIMP_(ULONG) AddRef() override {
- // Delegate to parent class.
- return m_parent->AddRef();
- }
- STDMETHODIMP_(ULONG) Release() override {
- // Delegate to parent class.
- return m_parent->Release();
- }
-
- // IMFAsyncCallback methods
- STDMETHODIMP GetParameters(DWORD*, DWORD*) override
- {
- // Implementation of this method is optional.
- return E_NOTIMPL;
- }
-
- STDMETHODIMP Invoke(IMFAsyncResult* asyncResult) override
- {
- return (m_parent->*m_invokeFn)(asyncResult);
- }
-
- T *m_parent;
- InvokeFn m_invokeFn;
-};
-
-class Scheduler
-{
- Q_DISABLE_COPY(Scheduler)
-public:
- enum ScheduleEvent
- {
- Terminate = WM_USER,
- Schedule = WM_USER + 1,
- Flush = WM_USER + 2
- };
-
- Scheduler(EVRCustomPresenter *presenter);
- ~Scheduler();
-
- 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 stopScheduler();
-
- HRESULT scheduleSample(IMFSample *sample, bool presentNow);
- HRESULT processSamplesInQueue(LONG *nextSleep);
- HRESULT processSample(IMFSample *sample, LONG *nextSleep);
- HRESULT flush();
-
- bool areSamplesScheduled();
-
- // ThreadProc for the scheduler thread.
- static DWORD WINAPI schedulerThreadProc(LPVOID parameter);
-
-private:
- DWORD schedulerThreadProcPrivate();
-
- EVRCustomPresenter *m_presenter;
-
- QQueue<IMFSample*> m_scheduledSamples; // Samples waiting to be presented.
-
- IMFClock *m_clock; // Presentation clock. Can be NULL.
-
- DWORD m_threadID;
- HANDLE m_schedulerThread;
- HANDLE m_threadReadyEvent;
- HANDLE 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.
-
- QMutex m_mutex;
-};
-
-class SamplePool
-{
- Q_DISABLE_COPY(SamplePool)
-public:
- SamplePool();
- ~SamplePool();
-
- HRESULT initialize(QList<IMFSample*> &samples);
- HRESULT clear();
-
- HRESULT getSample(IMFSample **sample);
- HRESULT returnSample(IMFSample *sample);
-
-private:
- QMutex m_mutex;
- QList<IMFSample*> m_videoSampleQueue;
- bool m_initialized;
-};
-
-class EVRCustomPresenter
- : public QObject
- , public IMFVideoDeviceID
- , public IMFVideoPresenter // Inherits IMFClockStateSink
- , public IMFRateSupport
- , public IMFGetService
- , public IMFTopologyServiceLookupClient
-{
- Q_DISABLE_COPY(EVRCustomPresenter)
-public:
- // Defines the state of the presenter.
- enum RenderState
- {
- RenderStarted = 1,
- RenderStopped,
- RenderPaused,
- RenderShutdown // Initial state.
- };
-
- // Defines the presenter's state with respect to frame-stepping.
- enum FrameStepState
- {
- FrameStepNone, // Not frame stepping.
- FrameStepWaitingStart, // Frame stepping, but the clock is not started.
- FrameStepPending, // Clock is started. Waiting for samples.
- FrameStepScheduled, // Submitted a sample for rendering.
- FrameStepComplete // Sample was rendered.
- };
-
- enum PresenterEvents
- {
- StartSurface = QEvent::User,
- StopSurface = QEvent::User + 1,
- PresentSample = QEvent::User + 2
- };
-
- EVRCustomPresenter(QAbstractVideoSurface *surface = 0);
- ~EVRCustomPresenter() override;
-
- bool isValid() const;
-
- // IUnknown methods
- STDMETHODIMP QueryInterface(REFIID riid, void ** ppv) override;
- STDMETHODIMP_(ULONG) AddRef() override;
- STDMETHODIMP_(ULONG) Release() override;
-
- // IMFGetService methods
- STDMETHODIMP GetService(REFGUID guidService, REFIID riid, LPVOID *ppvObject) override;
-
- // IMFVideoPresenter methods
- STDMETHODIMP ProcessMessage(MFVP_MESSAGE_TYPE message, ULONG_PTR param) override;
- STDMETHODIMP GetCurrentMediaType(IMFVideoMediaType** mediaType) override;
-
- // IMFClockStateSink methods
- STDMETHODIMP OnClockStart(MFTIME systemTime, LONGLONG clockStartOffset) override;
- STDMETHODIMP OnClockStop(MFTIME systemTime) override;
- STDMETHODIMP OnClockPause(MFTIME systemTime) override;
- STDMETHODIMP OnClockRestart(MFTIME systemTime) override;
- STDMETHODIMP OnClockSetRate(MFTIME systemTime, float rate) override;
-
- // IMFRateSupport methods
- STDMETHODIMP GetSlowestRate(MFRATE_DIRECTION direction, BOOL thin, float *rate) override;
- STDMETHODIMP GetFastestRate(MFRATE_DIRECTION direction, BOOL thin, float *rate) override;
- STDMETHODIMP IsRateSupported(BOOL thin, float rate, float *nearestSupportedRate) override;
-
- // IMFVideoDeviceID methods
- STDMETHODIMP GetDeviceID(IID* deviceID) override;
-
- // IMFTopologyServiceLookupClient methods
- STDMETHODIMP InitServicePointers(IMFTopologyServiceLookup *lookup) override;
- STDMETHODIMP ReleaseServicePointers() override;
-
- void supportedFormatsChanged();
- void setSurface(QAbstractVideoSurface *surface);
-
- void startSurface();
- void stopSurface();
- void presentSample(IMFSample *sample);
-
- bool event(QEvent *) override;
-
-public Q_SLOTS:
- void positionChanged(qint64 position);
-
-private:
- HRESULT checkShutdown() const
- {
- if (m_renderState == RenderShutdown)
- return MF_E_SHUTDOWN;
- else
- return S_OK;
- }
-
- // The "active" state is started or paused.
- inline bool isActive() const
- {
- return ((m_renderState == RenderStarted) || (m_renderState == RenderPaused));
- }
-
- // Scrubbing occurs when the frame rate is 0.
- inline bool isScrubbing() const { return m_playbackRate == 0.0f; }
-
- // Send an event to the EVR through its IMediaEventSink interface.
- void notifyEvent(long eventCode, LONG_PTR param1, LONG_PTR param2)
- {
- if (m_mediaEventSink)
- m_mediaEventSink->Notify(eventCode, param1, param2);
- }
-
- float getMaxRate(bool thin);
-
- // Mixer operations
- HRESULT configureMixer(IMFTransform *mixer);
-
- // Formats
- HRESULT createOptimalVideoType(IMFMediaType* proposed, IMFMediaType **optimal);
- HRESULT setMediaType(IMFMediaType *mediaType);
- HRESULT isMediaTypeSupported(IMFMediaType *mediaType);
-
- // Message handlers
- HRESULT flush();
- HRESULT renegotiateMediaType();
- HRESULT processInputNotify();
- HRESULT beginStreaming();
- HRESULT endStreaming();
- HRESULT checkEndOfStream();
-
- // Managing samples
- void processOutputLoop();
- HRESULT processOutput();
- HRESULT deliverSample(IMFSample *sample, bool repaint);
- HRESULT trackSample(IMFSample *sample);
- void releaseResources();
-
- // Frame-stepping
- HRESULT prepareFrameStep(DWORD steps);
- HRESULT startFrameStep();
- HRESULT deliverFrameStepSample(IMFSample *sample);
- HRESULT completeFrameStep(IMFSample *sample);
- HRESULT cancelFrameStep();
-
- // Callback when a video sample is released.
- HRESULT onSampleFree(IMFAsyncResult *result);
- AsyncCallback<EVRCustomPresenter> m_sampleFreeCB;
-
- // Holds information related to frame-stepping.
- struct FrameStep
- {
- FrameStepState state = FrameStepNone;
- QList<IMFSample*> samples;
- DWORD steps = 0;
- DWORD_PTR sampleNoRef = 0;
- };
-
- long m_refCount;
-
- RenderState m_renderState;
- FrameStep m_frameStep;
-
- QRecursiveMutex m_mutex;
-
- // Samples and scheduling
- Scheduler m_scheduler; // Manages scheduling of samples.
- SamplePool m_samplePool; // Pool of allocated samples.
- DWORD m_tokenCounter; // Counter. Incremented whenever we create new samples.
-
- // 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)?
-
- MFVideoNormalizedRect m_sourceRect;
- float m_playbackRate;
-
- 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
-
- QAbstractVideoSurface *m_surface;
- bool m_canRenderToSurface;
- qint64 m_positionOffset; // Seek position in microseconds.
-};
-
-bool qt_evr_setCustomPresenter(IUnknown *evr, EVRCustomPresenter *presenter);
-
-QT_END_NAMESPACE
-
-#endif // EVRCUSTOMPRESENTER_H
diff --git a/src/plugins/common/evr/evrd3dpresentengine.cpp b/src/plugins/common/evr/evrd3dpresentengine.cpp
deleted file mode 100644
index 964504e48..000000000
--- a/src/plugins/common/evr/evrd3dpresentengine.cpp
+++ /dev/null
@@ -1,413 +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 "evrd3dpresentengine.h"
-
-#include "evrhelpers.h"
-
-#include <qabstractvideobuffer.h>
-#include <QAbstractVideoSurface>
-#include <qvideoframe.h>
-#include <QDebug>
-#include <qthread.h>
-#include <QOffscreenSurface>
-
-static const int PRESENTER_BUFFER_COUNT = 3;
-
-QT_BEGIN_NAMESPACE
-
-class IMFSampleVideoBuffer: public QAbstractVideoBuffer
-{
-public:
- IMFSampleVideoBuffer(D3DPresentEngine *engine, IMFSample *sample, QAbstractVideoBuffer::HandleType handleType)
- : QAbstractVideoBuffer(handleType)
- , m_engine(engine)
- , m_sample(sample)
- , m_surface(0)
- , m_mapMode(NotMapped)
- {
- if (m_sample) {
- m_sample->AddRef();
-
- IMFMediaBuffer *buffer;
- if (SUCCEEDED(m_sample->GetBufferByIndex(0, &buffer))) {
- MFGetService(buffer,
- mr_BUFFER_SERVICE,
- iid_IDirect3DSurface9,
- reinterpret_cast<void **>(&m_surface));
- buffer->Release();
- }
- }
- }
-
- ~IMFSampleVideoBuffer() override
- {
- if (m_surface) {
- if (m_mapMode != NotMapped)
- m_surface->UnlockRect();
- m_surface->Release();
- }
- if (m_sample)
- m_sample->Release();
- }
-
- QVariant handle() const override;
-
- MapMode mapMode() const override { return m_mapMode; }
- uchar *map(MapMode, int*, int*) override;
- void unmap() override;
-
-private:
- mutable D3DPresentEngine *m_engine;
- IMFSample *m_sample;
- IDirect3DSurface9 *m_surface;
- MapMode m_mapMode;
- mutable unsigned int m_textureId = 0;
-};
-
-uchar *IMFSampleVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine)
-{
- if (!m_surface || m_mapMode != NotMapped)
- return 0;
-
- D3DSURFACE_DESC desc;
- if (FAILED(m_surface->GetDesc(&desc)))
- return 0;
-
- D3DLOCKED_RECT rect;
- if (FAILED(m_surface->LockRect(&rect, NULL, mode == ReadOnly ? D3DLOCK_READONLY : 0)))
- return 0;
-
- m_mapMode = mode;
-
- if (numBytes)
- *numBytes = (int)(rect.Pitch * desc.Height);
-
- if (bytesPerLine)
- *bytesPerLine = (int)rect.Pitch;
-
- return reinterpret_cast<uchar *>(rect.pBits);
-}
-
-void IMFSampleVideoBuffer::unmap()
-{
- if (m_mapMode == NotMapped)
- return;
-
- m_mapMode = NotMapped;
- m_surface->UnlockRect();
-}
-
-QVariant IMFSampleVideoBuffer::handle() const
-{
- return m_textureId;
-}
-
-
-D3DPresentEngine::D3DPresentEngine()
- : m_deviceResetToken(0)
- , m_D3D9(0)
- , m_device(0)
- , m_deviceManager(0)
- , m_useTextureRendering(false)
-{
- ZeroMemory(&m_displayMode, sizeof(m_displayMode));
-
- HRESULT hr = initializeD3D();
-
- if (SUCCEEDED(hr)) {
- hr = createD3DDevice();
- if (FAILED(hr))
- qWarning("Failed to create D3D device");
- } else {
- qWarning("Failed to initialize D3D");
- }
-}
-
-D3DPresentEngine::~D3DPresentEngine()
-{
- releaseResources();
-
- qt_evr_safe_release(&m_device);
- qt_evr_safe_release(&m_deviceManager);
- qt_evr_safe_release(&m_D3D9);
-}
-
-HRESULT D3DPresentEngine::initializeD3D()
-{
- HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, &m_D3D9);
-
- if (SUCCEEDED(hr))
- hr = DXVA2CreateDirect3DDeviceManager9(&m_deviceResetToken, &m_deviceManager);
-
- return hr;
-}
-
-HRESULT D3DPresentEngine::createD3DDevice()
-{
- HRESULT hr = S_OK;
- HWND hwnd = NULL;
- UINT uAdapterID = D3DADAPTER_DEFAULT;
- DWORD vp = 0;
-
- D3DCAPS9 ddCaps;
- ZeroMemory(&ddCaps, sizeof(ddCaps));
-
- IDirect3DDevice9Ex* device = NULL;
-
- if (!m_D3D9 || !m_deviceManager)
- return MF_E_NOT_INITIALIZED;
-
- hwnd = ::GetShellWindow();
-
- D3DPRESENT_PARAMETERS pp;
- ZeroMemory(&pp, sizeof(pp));
-
- pp.BackBufferWidth = 1;
- pp.BackBufferHeight = 1;
- pp.BackBufferFormat = D3DFMT_UNKNOWN;
- pp.BackBufferCount = 1;
- pp.Windowed = TRUE;
- pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
- pp.BackBufferFormat = D3DFMT_UNKNOWN;
- pp.hDeviceWindow = hwnd;
- pp.Flags = D3DPRESENTFLAG_VIDEO;
- pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
-
- hr = m_D3D9->GetDeviceCaps(uAdapterID, D3DDEVTYPE_HAL, &ddCaps);
- if (FAILED(hr))
- goto done;
-
- if (ddCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
- vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
- else
- vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
-
- hr = m_D3D9->CreateDeviceEx(
- uAdapterID,
- D3DDEVTYPE_HAL,
- pp.hDeviceWindow,
- vp | D3DCREATE_NOWINDOWCHANGES | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
- &pp,
- NULL,
- &device
- );
- if (FAILED(hr))
- goto done;
-
- hr = m_D3D9->GetAdapterDisplayMode(uAdapterID, &m_displayMode);
- if (FAILED(hr))
- goto done;
-
- hr = m_deviceManager->ResetDevice(device, m_deviceResetToken);
- if (FAILED(hr))
- goto done;
-
- qt_evr_safe_release(&m_device);
-
- m_device = device;
- m_device->AddRef();
-
-done:
- qt_evr_safe_release(&device);
- return hr;
-}
-
-bool D3DPresentEngine::isValid() const
-{
- return m_device != NULL;
-}
-
-void D3DPresentEngine::releaseResources()
-{
- m_surfaceFormat = QVideoSurfaceFormat();
-}
-
-HRESULT D3DPresentEngine::getService(REFGUID, REFIID riid, void** ppv)
-{
- HRESULT hr = S_OK;
-
- if (riid == __uuidof(IDirect3DDeviceManager9)) {
- if (m_deviceManager == NULL) {
- hr = MF_E_UNSUPPORTED_SERVICE;
- } else {
- *ppv = m_deviceManager;
- m_deviceManager->AddRef();
- }
- } else {
- hr = MF_E_UNSUPPORTED_SERVICE;
- }
-
- return hr;
-}
-
-HRESULT D3DPresentEngine::checkFormat(D3DFORMAT format)
-{
- if (!m_D3D9 || !m_device)
- return E_FAIL;
-
- HRESULT hr = S_OK;
-
- D3DDISPLAYMODE mode;
- D3DDEVICE_CREATION_PARAMETERS params;
-
- hr = m_device->GetCreationParameters(&params);
- if (FAILED(hr))
- return hr;
-
- UINT uAdapter = params.AdapterOrdinal;
- D3DDEVTYPE type = params.DeviceType;
-
- hr = m_D3D9->GetAdapterDisplayMode(uAdapter, &mode);
- if (FAILED(hr))
- return hr;
-
- hr = m_D3D9->CheckDeviceFormat(uAdapter, type, mode.Format,
- D3DUSAGE_RENDERTARGET,
- D3DRTYPE_SURFACE,
- format);
-
- if (m_useTextureRendering && format != D3DFMT_X8R8G8B8 && format != D3DFMT_A8R8G8B8) {
- // The texture is always in RGB32 so the d3d driver must support conversion from the
- // requested format to RGB32.
- hr = m_D3D9->CheckDeviceFormatConversion(uAdapter, type, format, D3DFMT_X8R8G8B8);
- }
-
- return hr;
-}
-
-bool D3DPresentEngine::supportsTextureRendering() const
-{
- return false;
-}
-
-void D3DPresentEngine::setHint(Hint hint, bool enable)
-{
- if (hint == RenderToTexture)
- m_useTextureRendering = enable && supportsTextureRendering();
-}
-
-HRESULT D3DPresentEngine::createVideoSamples(IMFMediaType *format, QList<IMFSample*> &videoSampleQueue)
-{
- if (!format)
- return MF_E_UNEXPECTED;
-
- HRESULT hr = S_OK;
-
- IDirect3DSurface9 *surface = NULL;
- IMFSample *videoSample = NULL;
-
- releaseResources();
-
- UINT32 width = 0, height = 0;
- hr = MFGetAttributeSize(format, MF_MT_FRAME_SIZE, &width, &height);
- if (FAILED(hr))
- return hr;
-
- DWORD d3dFormat = 0;
- hr = qt_evr_getFourCC(format, &d3dFormat);
- if (FAILED(hr))
- return hr;
-
- // Create the video samples.
- for (int i = 0; i < PRESENTER_BUFFER_COUNT; i++) {
- hr = m_device->CreateRenderTarget(width, height,
- (D3DFORMAT)d3dFormat,
- D3DMULTISAMPLE_NONE,
- 0,
- TRUE,
- &surface, NULL);
- if (FAILED(hr))
- goto done;
-
- hr = MFCreateVideoSampleFromSurface(surface, &videoSample);
- if (FAILED(hr))
- goto done;
-
- videoSample->AddRef();
- videoSampleQueue.append(videoSample);
-
- qt_evr_safe_release(&videoSample);
- qt_evr_safe_release(&surface);
- }
-
-done:
- if (SUCCEEDED(hr)) {
- m_surfaceFormat = QVideoSurfaceFormat(QSize(width, height),
- m_useTextureRendering ? QVideoFrame::Format_RGB32
- : qt_evr_pixelFormatFromD3DFormat(d3dFormat),
- m_useTextureRendering ? QAbstractVideoBuffer::GLTextureHandle
- : QAbstractVideoBuffer::NoHandle);
- UINT32 horizontal = 1, vertical = 1;
- hr = MFGetAttributeRatio(format, MF_MT_PIXEL_ASPECT_RATIO, &horizontal, &vertical);
- if (SUCCEEDED(hr))
- m_surfaceFormat.setPixelAspectRatio(horizontal, vertical);
- } else {
- releaseResources();
- }
-
- qt_evr_safe_release(&videoSample);
- qt_evr_safe_release(&surface);
- return hr;
-}
-
-QVideoFrame D3DPresentEngine::makeVideoFrame(IMFSample *sample)
-{
- if (!sample)
- return QVideoFrame();
-
- QVideoFrame frame(new IMFSampleVideoBuffer(this, sample, m_surfaceFormat.handleType()),
- m_surfaceFormat.frameSize(),
- m_surfaceFormat.pixelFormat());
-
- // WMF uses 100-nanosecond units, Qt uses microseconds
- LONGLONG startTime = 0;
- auto hr = sample->GetSampleTime(&startTime);
- if (SUCCEEDED(hr)) {
- frame.setStartTime(startTime * 0.1);
-
- LONGLONG duration = -1;
- if (SUCCEEDED(sample->GetSampleDuration(&duration)))
- frame.setEndTime((startTime + duration) * 0.1);
- }
-
- return frame;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/common/evr/evrd3dpresentengine.h b/src/plugins/common/evr/evrd3dpresentengine.h
deleted file mode 100644
index eb2def7b2..000000000
--- a/src/plugins/common/evr/evrd3dpresentengine.h
+++ /dev/null
@@ -1,152 +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 EVRD3DPRESENTENGINE_H
-#define EVRD3DPRESENTENGINE_H
-
-#include <QMutex>
-#include <QVideoSurfaceFormat>
-
-#include <d3d9.h>
-
-struct IDirect3D9Ex;
-struct IDirect3DDevice9Ex;
-struct IDirect3DDeviceManager9;
-struct IDirect3DSurface9;
-struct IDirect3DTexture9;
-struct IMFSample;
-struct IMFMediaType;
-
-// Randomly generated GUIDs
-static const GUID MFSamplePresenter_SampleCounter =
-{ 0xb0bb83cc, 0xf10f, 0x4e2e, { 0xaa, 0x2b, 0x29, 0xea, 0x5e, 0x92, 0xef, 0x85 } };
-
-QT_BEGIN_NAMESPACE
-
-class QAbstractVideoSurface;
-
-#ifdef MAYBE_ANGLE
-
-class OpenGLResources;
-
-class EGLWrapper
-{
- Q_DISABLE_COPY(EGLWrapper)
-public:
- EGLWrapper();
-
- __eglMustCastToProperFunctionPointerType getProcAddress(const char *procname);
- EGLSurface createPbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list);
- EGLBoolean destroySurface(EGLDisplay dpy, EGLSurface surface);
- EGLBoolean bindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer);
- EGLBoolean releaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer);
-
-private:
- typedef __eglMustCastToProperFunctionPointerType (EGLAPIENTRYP EglGetProcAddress)(const char *procname);
- typedef EGLSurface (EGLAPIENTRYP EglCreatePbufferSurface)(EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list);
- typedef EGLBoolean (EGLAPIENTRYP EglDestroySurface)(EGLDisplay dpy, EGLSurface surface);
- typedef EGLBoolean (EGLAPIENTRYP EglBindTexImage)(EGLDisplay dpy, EGLSurface surface, EGLint buffer);
- typedef EGLBoolean (EGLAPIENTRYP EglReleaseTexImage)(EGLDisplay dpy, EGLSurface surface, EGLint buffer);
-
- EglGetProcAddress m_eglGetProcAddress;
- EglCreatePbufferSurface m_eglCreatePbufferSurface;
- EglDestroySurface m_eglDestroySurface;
- EglBindTexImage m_eglBindTexImage;
- EglReleaseTexImage m_eglReleaseTexImage;
-};
-
-#endif // MAYBE_ANGLE
-
-class D3DPresentEngine
-{
- Q_DISABLE_COPY(D3DPresentEngine)
-public:
- enum Hint
- {
- RenderToTexture
- };
-
- D3DPresentEngine();
- virtual ~D3DPresentEngine();
-
- bool isValid() const;
- void setHint(Hint hint, bool enable = true);
-
- HRESULT getService(REFGUID guidService, REFIID riid, void** ppv);
- HRESULT checkFormat(D3DFORMAT format);
- UINT refreshRate() const { return m_displayMode.RefreshRate; }
-
- bool supportsTextureRendering() const;
- bool isTextureRenderingEnabled() const { return m_useTextureRendering; }
-
- HRESULT createVideoSamples(IMFMediaType *format, QList<IMFSample*>& videoSampleQueue);
- QVideoSurfaceFormat videoSurfaceFormat() const { return m_surfaceFormat; }
- QVideoFrame makeVideoFrame(IMFSample* sample);
-
- void releaseResources();
-
-private:
- HRESULT initializeD3D();
- HRESULT createD3DDevice();
-
-
- UINT m_deviceResetToken;
- D3DDISPLAYMODE m_displayMode;
-
- IDirect3D9Ex *m_D3D9;
- IDirect3DDevice9Ex *m_device;
- IDirect3DDeviceManager9 *m_deviceManager;
-
- QVideoSurfaceFormat m_surfaceFormat;
-
- bool m_useTextureRendering;
-
-#ifdef MAYBE_ANGLE
- unsigned int updateTexture(IDirect3DSurface9 *src);
-
- OpenGLResources *m_glResources;
- IDirect3DTexture9 *m_texture;
-#endif
-
- friend class IMFSampleVideoBuffer;
-};
-
-QT_END_NAMESPACE
-
-#endif // EVRD3DPRESENTENGINE_H
diff --git a/src/plugins/common/evr/evrdefs.cpp b/src/plugins/common/evr/evrdefs.cpp
deleted file mode 100644
index e143ada0b..000000000
--- a/src/plugins/common/evr/evrdefs.cpp
+++ /dev/null
@@ -1,48 +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 "evrdefs.h"
-
-const CLSID clsid_EnhancedVideoRenderer = { 0xfa10746c, 0x9b63, 0x4b6c, {0xbc, 0x49, 0xfc, 0x30, 0xe, 0xa5, 0xf2, 0x56} };
-const GUID mr_VIDEO_RENDER_SERVICE = { 0x1092a86c, 0xab1a, 0x459a, {0xa3, 0x36, 0x83, 0x1f, 0xbc, 0x4d, 0x11, 0xff} };
-const GUID mr_VIDEO_MIXER_SERVICE = { 0x73cd2fc, 0x6cf4, 0x40b7, {0x88, 0x59, 0xe8, 0x95, 0x52, 0xc8, 0x41, 0xf8} };
-const GUID mr_BUFFER_SERVICE = { 0xa562248c, 0x9ac6, 0x4ffc, {0x9f, 0xba, 0x3a, 0xf8, 0xf8, 0xad, 0x1a, 0x4d} };
-const GUID video_ZOOM_RECT = { 0x7aaa1638, 0x1b7f, 0x4c93, {0xbd, 0x89, 0x5b, 0x9c, 0x9f, 0xb6, 0xfc, 0xf0} };
-const GUID iid_IDirect3DDevice9 = { 0xd0223b96, 0xbf7a, 0x43fd, {0x92, 0xbd, 0xa4, 0x3b, 0xd, 0x82, 0xb9, 0xeb} };
-const GUID iid_IDirect3DSurface9 = { 0xcfbaf3a, 0x9ff6, 0x429a, {0x99, 0xb3, 0xa2, 0x79, 0x6a, 0xf8, 0xb8, 0x9b} };
diff --git a/src/plugins/common/evr/evrdefs.h b/src/plugins/common/evr/evrdefs.h
deleted file mode 100644
index 4f3dd832a..000000000
--- a/src/plugins/common/evr/evrdefs.h
+++ /dev/null
@@ -1,353 +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 EVRDEFS_H
-#define EVRDEFS_H
-
-#include <d3d9.h>
-#include <evr9.h>
-#include <evr.h>
-#include <dxva2api.h>
-#include <mfapi.h>
-#include <mfidl.h>
-#include <mferror.h>
-
-extern const CLSID clsid_EnhancedVideoRenderer;
-extern const GUID mr_VIDEO_RENDER_SERVICE;
-extern const GUID mr_VIDEO_MIXER_SERVICE;
-extern const GUID mr_BUFFER_SERVICE;
-extern const GUID video_ZOOM_RECT;
-extern const GUID iid_IDirect3DDevice9;
-extern const GUID iid_IDirect3DSurface9;
-
-// The following is required to compile with MinGW
-
-extern "C" {
-HRESULT WINAPI MFCreateVideoSampleFromSurface(IUnknown *pUnkSurface, IMFSample **ppSample);
-HRESULT WINAPI Direct3DCreate9Ex(UINT SDKVersion, IDirect3D9Ex**);
-}
-
-#ifndef PRESENTATION_CURRENT_POSITION
-#define PRESENTATION_CURRENT_POSITION 0x7fffffffffffffff
-#endif
-
-#ifndef MF_E_SHUTDOWN
-#define MF_E_SHUTDOWN ((HRESULT)0xC00D3E85L)
-#endif
-
-#ifndef MF_E_SAMPLEALLOCATOR_EMPTY
-#define MF_E_SAMPLEALLOCATOR_EMPTY ((HRESULT)0xC00D4A3EL)
-#endif
-
-#ifndef MF_E_TRANSFORM_STREAM_CHANGE
-#define MF_E_TRANSFORM_STREAM_CHANGE ((HRESULT)0xC00D6D61L)
-#endif
-
-#ifndef MF_E_TRANSFORM_NEED_MORE_INPUT
-#define MF_E_TRANSFORM_NEED_MORE_INPUT ((HRESULT)0xC00D6D72L)
-#endif
-
-#if defined(__GNUC__) && !defined(_MFVideoNormalizedRect_)
-#define _MFVideoNormalizedRect_
-typedef struct MFVideoNormalizedRect {
- float left;
- float top;
- float right;
- float bottom;
-} MFVideoNormalizedRect;
-#endif
-
-#include <initguid.h>
-
-#ifndef __IMFGetService_INTERFACE_DEFINED__
-#define __IMFGetService_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IMFGetService, 0xfa993888, 0x4383, 0x415a, 0xa9,0x30, 0xdd,0x47,0x2a,0x8c,0xf6,0xf7);
-MIDL_INTERFACE("fa993888-4383-415a-a930-dd472a8cf6f7")
-IMFGetService : public IUnknown
-{
- virtual HRESULT STDMETHODCALLTYPE GetService(REFGUID, REFIID, LPVOID *) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IMFGetService, 0xfa993888, 0x4383, 0x415a, 0xa9,0x30, 0xdd,0x47,0x2a,0x8c,0xf6,0xf7)
-#endif
-#endif // __IMFGetService_INTERFACE_DEFINED__
-
-#ifndef __IMFVideoDisplayControl_INTERFACE_DEFINED__
-#define __IMFVideoDisplayControl_INTERFACE_DEFINED__
-typedef enum MFVideoAspectRatioMode
-{
- MFVideoARMode_None = 0,
- MFVideoARMode_PreservePicture = 0x1,
- MFVideoARMode_PreservePixel = 0x2,
- MFVideoARMode_NonLinearStretch = 0x4,
- MFVideoARMode_Mask = 0x7
-} MFVideoAspectRatioMode;
-
-DEFINE_GUID(IID_IMFVideoDisplayControl, 0xa490b1e4, 0xab84, 0x4d31, 0xa1,0xb2, 0x18,0x1e,0x03,0xb1,0x07,0x7a);
-MIDL_INTERFACE("a490b1e4-ab84-4d31-a1b2-181e03b1077a")
-IMFVideoDisplayControl : public IUnknown
-{
- virtual HRESULT STDMETHODCALLTYPE GetNativeVideoSize(SIZE *, SIZE *) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetIdealVideoSize(SIZE *, SIZE *) = 0;
- virtual HRESULT STDMETHODCALLTYPE SetVideoPosition(const MFVideoNormalizedRect *, const LPRECT) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetVideoPosition(MFVideoNormalizedRect *, LPRECT) = 0;
- virtual HRESULT STDMETHODCALLTYPE SetAspectRatioMode(DWORD) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetAspectRatioMode(DWORD *) = 0;
- virtual HRESULT STDMETHODCALLTYPE SetVideoWindow(HWND) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetVideoWindow(HWND *) = 0;
- virtual HRESULT STDMETHODCALLTYPE RepaintVideo(void) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetCurrentImage(BITMAPINFOHEADER *, BYTE **, DWORD *, LONGLONG *) = 0;
- virtual HRESULT STDMETHODCALLTYPE SetBorderColor(COLORREF) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetBorderColor(COLORREF *) = 0;
- virtual HRESULT STDMETHODCALLTYPE SetRenderingPrefs(DWORD) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetRenderingPrefs(DWORD *) = 0;
- virtual HRESULT STDMETHODCALLTYPE SetFullscreen(BOOL) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetFullscreen(BOOL *) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IMFVideoDisplayControl, 0xa490b1e4, 0xab84, 0x4d31, 0xa1,0xb2, 0x18,0x1e,0x03,0xb1,0x07,0x7a)
-#endif
-#endif // __IMFVideoDisplayControl_INTERFACE_DEFINED__
-
-#ifndef __IMFVideoProcessor_INTERFACE_DEFINED__
-#define __IMFVideoProcessor_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IMFVideoProcessor, 0x6AB0000C, 0xFECE, 0x4d1f, 0xA2,0xAC, 0xA9,0x57,0x35,0x30,0x65,0x6E);
-MIDL_INTERFACE("6AB0000C-FECE-4d1f-A2AC-A9573530656E")
-IMFVideoProcessor : public IUnknown
-{
- virtual HRESULT STDMETHODCALLTYPE GetAvailableVideoProcessorModes(UINT *, GUID **) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetVideoProcessorCaps(LPGUID, DXVA2_VideoProcessorCaps *) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetVideoProcessorMode(LPGUID) = 0;
- virtual HRESULT STDMETHODCALLTYPE SetVideoProcessorMode(LPGUID) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetProcAmpRange(DWORD, DXVA2_ValueRange *) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetProcAmpValues(DWORD, DXVA2_ProcAmpValues *) = 0;
- virtual HRESULT STDMETHODCALLTYPE SetProcAmpValues(DWORD, DXVA2_ProcAmpValues *) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetFilteringRange(DWORD, DXVA2_ValueRange *) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetFilteringValue(DWORD, DXVA2_Fixed32 *) = 0;
- virtual HRESULT STDMETHODCALLTYPE SetFilteringValue(DWORD, DXVA2_Fixed32 *) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetBackgroundColor(COLORREF *) = 0;
- virtual HRESULT STDMETHODCALLTYPE SetBackgroundColor(COLORREF) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IMFVideoProcessor, 0x6AB0000C, 0xFECE, 0x4d1f, 0xA2,0xAC, 0xA9,0x57,0x35,0x30,0x65,0x6E)
-#endif
-#endif // __IMFVideoProcessor_INTERFACE_DEFINED__
-
-#ifndef __IMFVideoDeviceID_INTERFACE_DEFINED__
-#define __IMFVideoDeviceID_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IMFVideoDeviceID, 0xA38D9567, 0x5A9C, 0x4f3c, 0xB2,0x93, 0x8E,0xB4,0x15,0xB2,0x79,0xBA);
-MIDL_INTERFACE("A38D9567-5A9C-4f3c-B293-8EB415B279BA")
-IMFVideoDeviceID : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE GetDeviceID(IID *pDeviceID) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IMFVideoDeviceID, 0xA38D9567, 0x5A9C, 0x4f3c, 0xB2,0x93, 0x8E,0xB4,0x15,0xB2,0x79,0xBA)
-#endif
-#endif // __IMFVideoDeviceID_INTERFACE_DEFINED__
-
-#ifndef __IMFClockStateSink_INTERFACE_DEFINED__
-#define __IMFClockStateSink_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IMFClockStateSink, 0xF6696E82, 0x74F7, 0x4f3d, 0xA1,0x78, 0x8A,0x5E,0x09,0xC3,0x65,0x9F);
-MIDL_INTERFACE("F6696E82-74F7-4f3d-A178-8A5E09C3659F")
-IMFClockStateSink : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset) = 0;
- virtual HRESULT STDMETHODCALLTYPE OnClockStop(MFTIME hnsSystemTime) = 0;
- virtual HRESULT STDMETHODCALLTYPE OnClockPause(MFTIME hnsSystemTime) = 0;
- virtual HRESULT STDMETHODCALLTYPE OnClockRestart(MFTIME hnsSystemTime) = 0;
- virtual HRESULT STDMETHODCALLTYPE OnClockSetRate(MFTIME hnsSystemTime, float flRate) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IMFClockStateSink, 0xF6696E82, 0x74F7, 0x4f3d, 0xA1,0x78, 0x8A,0x5E,0x09,0xC3,0x65,0x9F)
-#endif
-#endif // __IMFClockStateSink_INTERFACE_DEFINED__
-
-#ifndef __IMFVideoPresenter_INTERFACE_DEFINED__
-#define __IMFVideoPresenter_INTERFACE_DEFINED__
-typedef enum MFVP_MESSAGE_TYPE
-{
- MFVP_MESSAGE_FLUSH = 0,
- MFVP_MESSAGE_INVALIDATEMEDIATYPE = 0x1,
- MFVP_MESSAGE_PROCESSINPUTNOTIFY = 0x2,
- MFVP_MESSAGE_BEGINSTREAMING = 0x3,
- MFVP_MESSAGE_ENDSTREAMING = 0x4,
- MFVP_MESSAGE_ENDOFSTREAM = 0x5,
- MFVP_MESSAGE_STEP = 0x6,
- MFVP_MESSAGE_CANCELSTEP = 0x7
-} MFVP_MESSAGE_TYPE;
-
-DEFINE_GUID(IID_IMFVideoPresenter, 0x29AFF080, 0x182A, 0x4a5d, 0xAF,0x3B, 0x44,0x8F,0x3A,0x63,0x46,0xCB);
-MIDL_INTERFACE("29AFF080-182A-4a5d-AF3B-448F3A6346CB")
-IMFVideoPresenter : public IMFClockStateSink
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE ProcessMessage(MFVP_MESSAGE_TYPE eMessage, ULONG_PTR ulParam) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetCurrentMediaType(IMFVideoMediaType **ppMediaType) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IMFVideoPresenter, 0x29AFF080, 0x182A, 0x4a5d, 0xAF,0x3B, 0x44,0x8F,0x3A,0x63,0x46,0xCB)
-#endif
-#endif // __IMFVideoPresenter_INTERFACE_DEFINED__
-
-#ifndef __IMFRateSupport_INTERFACE_DEFINED__
-#define __IMFRateSupport_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IMFRateSupport, 0x0a9ccdbc, 0xd797, 0x4563, 0x96,0x67, 0x94,0xec,0x5d,0x79,0x29,0x2d);
-MIDL_INTERFACE("0a9ccdbc-d797-4563-9667-94ec5d79292d")
-IMFRateSupport : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE GetSlowestRate(MFRATE_DIRECTION eDirection, BOOL fThin, float *pflRate) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetFastestRate(MFRATE_DIRECTION eDirection, BOOL fThin, float *pflRate) = 0;
- virtual HRESULT STDMETHODCALLTYPE IsRateSupported(BOOL fThin, float flRate, float *pflNearestSupportedRate) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IMFRateSupport, 0x0a9ccdbc, 0xd797, 0x4563, 0x96,0x67, 0x94,0xec,0x5d,0x79,0x29,0x2d)
-#endif
-#endif // __IMFRateSupport_INTERFACE_DEFINED__
-
-#ifndef __IMFTopologyServiceLookup_INTERFACE_DEFINED__
-#define __IMFTopologyServiceLookup_INTERFACE_DEFINED__
-typedef enum _MF_SERVICE_LOOKUP_TYPE
-{
- MF_SERVICE_LOOKUP_UPSTREAM = 0,
- MF_SERVICE_LOOKUP_UPSTREAM_DIRECT = (MF_SERVICE_LOOKUP_UPSTREAM + 1),
- MF_SERVICE_LOOKUP_DOWNSTREAM = (MF_SERVICE_LOOKUP_UPSTREAM_DIRECT + 1),
- MF_SERVICE_LOOKUP_DOWNSTREAM_DIRECT = (MF_SERVICE_LOOKUP_DOWNSTREAM + 1),
- MF_SERVICE_LOOKUP_ALL = (MF_SERVICE_LOOKUP_DOWNSTREAM_DIRECT + 1),
- MF_SERVICE_LOOKUP_GLOBAL = (MF_SERVICE_LOOKUP_ALL + 1)
-} MF_SERVICE_LOOKUP_TYPE;
-
-DEFINE_GUID(IID_IMFTopologyServiceLookup, 0xfa993889, 0x4383, 0x415a, 0xa9,0x30, 0xdd,0x47,0x2a,0x8c,0xf6,0xf7);
-MIDL_INTERFACE("fa993889-4383-415a-a930-dd472a8cf6f7")
-IMFTopologyServiceLookup : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE LookupService(MF_SERVICE_LOOKUP_TYPE Type,
- DWORD dwIndex,
- REFGUID guidService,
- REFIID riid,
- LPVOID *ppvObjects,
- DWORD *pnObjects) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IMFTopologyServiceLookup, 0xfa993889, 0x4383, 0x415a, 0xa9,0x30, 0xdd,0x47,0x2a,0x8c,0xf6,0xf7)
-#endif
-#endif // __IMFTopologyServiceLookup_INTERFACE_DEFINED__
-
-#ifndef __IMFTopologyServiceLookupClient_INTERFACE_DEFINED__
-#define __IMFTopologyServiceLookupClient_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IMFTopologyServiceLookupClient, 0xfa99388a, 0x4383, 0x415a, 0xa9,0x30, 0xdd,0x47,0x2a,0x8c,0xf6,0xf7);
-MIDL_INTERFACE("fa99388a-4383-415a-a930-dd472a8cf6f7")
-IMFTopologyServiceLookupClient : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE InitServicePointers(IMFTopologyServiceLookup *pLookup) = 0;
- virtual HRESULT STDMETHODCALLTYPE ReleaseServicePointers(void) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IMFTopologyServiceLookupClient, 0xfa99388a, 0x4383, 0x415a, 0xa9,0x30, 0xdd,0x47,0x2a,0x8c,0xf6,0xf7)
-#endif
-#endif // __IMFTopologyServiceLookupClient_INTERFACE_DEFINED__
-
-#ifndef __IMediaEventSink_INTERFACE_DEFINED__
-#define __IMediaEventSink_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IMediaEventSink, 0x56a868a2, 0x0ad4, 0x11ce, 0xb0,0x3a, 0x00,0x20,0xaf,0x0b,0xa7,0x70);
-MIDL_INTERFACE("56a868a2-0ad4-11ce-b03a-0020af0ba770")
-IMediaEventSink : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE Notify(long EventCode, LONG_PTR EventParam1, LONG_PTR EventParam2) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IMediaEventSink, 0x56a868a2, 0x0ad4, 0x11ce, 0xb0,0x3a, 0x00,0x20,0xaf,0x0b,0xa7,0x70)
-#endif
-#endif // __IMediaEventSink_INTERFACE_DEFINED__
-
-#ifndef __IMFVideoRenderer_INTERFACE_DEFINED__
-#define __IMFVideoRenderer_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IMFVideoRenderer, 0xDFDFD197, 0xA9CA, 0x43d8, 0xB3,0x41, 0x6A,0xF3,0x50,0x37,0x92,0xCD);
-MIDL_INTERFACE("DFDFD197-A9CA-43d8-B341-6AF3503792CD")
-IMFVideoRenderer : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE InitializeRenderer(IMFTransform *pVideoMixer,
- IMFVideoPresenter *pVideoPresenter) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IMFVideoRenderer, 0xDFDFD197, 0xA9CA, 0x43d8, 0xB3,0x41, 0x6A,0xF3,0x50,0x37,0x92,0xCD)
-#endif
-#endif // __IMFVideoRenderer_INTERFACE_DEFINED__
-
-#ifndef __IMFTrackedSample_INTERFACE_DEFINED__
-#define __IMFTrackedSample_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IMFTrackedSample, 0x245BF8E9, 0x0755, 0x40f7, 0x88,0xA5, 0xAE,0x0F,0x18,0xD5,0x5E,0x17);
-MIDL_INTERFACE("245BF8E9-0755-40f7-88A5-AE0F18D55E17")
-IMFTrackedSample : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE SetAllocator(IMFAsyncCallback *pSampleAllocator, IUnknown *pUnkState) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IMFTrackedSample, 0x245BF8E9, 0x0755, 0x40f7, 0x88,0xA5, 0xAE,0x0F,0x18,0xD5,0x5E,0x17)
-#endif
-#endif // __IMFTrackedSample_INTERFACE_DEFINED__
-
-#ifndef __IMFDesiredSample_INTERFACE_DEFINED__
-#define __IMFDesiredSample_INTERFACE_DEFINED__
-DEFINE_GUID(IID_IMFDesiredSample, 0x56C294D0, 0x753E, 0x4260, 0x8D,0x61, 0xA3,0xD8,0x82,0x0B,0x1D,0x54);
-MIDL_INTERFACE("56C294D0-753E-4260-8D61-A3D8820B1D54")
-IMFDesiredSample : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE GetDesiredSampleTimeAndDuration(LONGLONG *phnsSampleTime,
- LONGLONG *phnsSampleDuration) = 0;
- virtual void STDMETHODCALLTYPE SetDesiredSampleTimeAndDuration(LONGLONG hnsSampleTime,
- LONGLONG hnsSampleDuration) = 0;
- virtual void STDMETHODCALLTYPE Clear( void) = 0;
-};
-#ifdef __CRT_UUID_DECL
-__CRT_UUID_DECL(IMFDesiredSample, 0x56C294D0, 0x753E, 0x4260, 0x8D,0x61, 0xA3,0xD8,0x82,0x0B,0x1D,0x54)
-#endif
-#endif
-
-#endif // EVRDEFS_H
-
diff --git a/src/plugins/common/evr/evrhelpers.cpp b/src/plugins/common/evr/evrhelpers.cpp
deleted file mode 100644
index a315f1a73..000000000
--- a/src/plugins/common/evr/evrhelpers.cpp
+++ /dev/null
@@ -1,186 +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 "evrhelpers.h"
-
-#ifndef D3DFMT_YV12
-#define D3DFMT_YV12 (D3DFORMAT)MAKEFOURCC ('Y', 'V', '1', '2')
-#endif
-#ifndef D3DFMT_NV12
-#define D3DFMT_NV12 (D3DFORMAT)MAKEFOURCC ('N', 'V', '1', '2')
-#endif
-
-QT_BEGIN_NAMESPACE
-
-HRESULT qt_evr_getFourCC(IMFMediaType *type, DWORD *fourCC)
-{
- if (!fourCC)
- return E_POINTER;
-
- HRESULT hr = S_OK;
- GUID guidSubType = GUID_NULL;
-
- if (SUCCEEDED(hr))
- hr = type->GetGUID(MF_MT_SUBTYPE, &guidSubType);
-
- if (SUCCEEDED(hr))
- *fourCC = guidSubType.Data1;
-
- return hr;
-}
-
-bool qt_evr_areMediaTypesEqual(IMFMediaType *type1, IMFMediaType *type2)
-{
- if (!type1 && !type2)
- return true;
- if (!type1 || !type2)
- return false;
-
- DWORD dwFlags = 0;
- HRESULT hr = type1->IsEqual(type2, &dwFlags);
-
- return (hr == S_OK);
-}
-
-HRESULT qt_evr_validateVideoArea(const MFVideoArea& area, UINT32 width, UINT32 height)
-{
- float fOffsetX = qt_evr_MFOffsetToFloat(area.OffsetX);
- float fOffsetY = qt_evr_MFOffsetToFloat(area.OffsetY);
-
- if ( ((LONG)fOffsetX + area.Area.cx > (LONG)width) ||
- ((LONG)fOffsetY + area.Area.cy > (LONG)height) ) {
- return MF_E_INVALIDMEDIATYPE;
- }
- return S_OK;
-}
-
-bool qt_evr_isSampleTimePassed(IMFClock *clock, IMFSample *sample)
-{
- if (!sample || !clock)
- return false;
-
- HRESULT hr = S_OK;
- MFTIME hnsTimeNow = 0;
- MFTIME hnsSystemTime = 0;
- MFTIME hnsSampleStart = 0;
- MFTIME hnsSampleDuration = 0;
-
- hr = clock->GetCorrelatedTime(0, &hnsTimeNow, &hnsSystemTime);
-
- if (SUCCEEDED(hr))
- hr = sample->GetSampleTime(&hnsSampleStart);
-
- if (SUCCEEDED(hr))
- hr = sample->GetSampleDuration(&hnsSampleDuration);
-
- if (SUCCEEDED(hr)) {
- if (hnsSampleStart + hnsSampleDuration < hnsTimeNow)
- return true;
- }
-
- return false;
-}
-
-QVideoFrame::PixelFormat qt_evr_pixelFormatFromD3DFormat(DWORD format)
-{
- switch (format) {
- case D3DFMT_R8G8B8:
- return QVideoFrame::Format_RGB24;
- case D3DFMT_A8R8G8B8:
- return QVideoFrame::Format_ARGB32;
- case D3DFMT_X8R8G8B8:
- return QVideoFrame::Format_RGB32;
- case D3DFMT_R5G6B5:
- return QVideoFrame::Format_RGB565;
- case D3DFMT_X1R5G5B5:
- return QVideoFrame::Format_RGB555;
- case D3DFMT_A8:
- return QVideoFrame::Format_Y8;
- case D3DFMT_A8B8G8R8:
- return QVideoFrame::Format_BGRA32;
- case D3DFMT_X8B8G8R8:
- return QVideoFrame::Format_BGR32;
- case D3DFMT_UYVY:
- return QVideoFrame::Format_UYVY;
- case D3DFMT_YUY2:
- return QVideoFrame::Format_YUYV;
- case D3DFMT_NV12:
- return QVideoFrame::Format_NV12;
- case D3DFMT_YV12:
- return QVideoFrame::Format_YV12;
- case D3DFMT_UNKNOWN:
- default:
- return QVideoFrame::Format_Invalid;
- }
-}
-
-D3DFORMAT qt_evr_D3DFormatFromPixelFormat(QVideoFrame::PixelFormat format)
-{
- switch (format) {
- case QVideoFrame::Format_RGB24:
- return D3DFMT_R8G8B8;
- case QVideoFrame::Format_ARGB32:
- return D3DFMT_A8R8G8B8;
- case QVideoFrame::Format_RGB32:
- return D3DFMT_X8R8G8B8;
- case QVideoFrame::Format_RGB565:
- return D3DFMT_R5G6B5;
- case QVideoFrame::Format_RGB555:
- return D3DFMT_X1R5G5B5;
- case QVideoFrame::Format_Y8:
- return D3DFMT_A8;
- case QVideoFrame::Format_BGRA32:
- return D3DFMT_A8B8G8R8;
- case QVideoFrame::Format_BGR32:
- return D3DFMT_X8B8G8R8;
- case QVideoFrame::Format_UYVY:
- return D3DFMT_UYVY;
- case QVideoFrame::Format_YUYV:
- return D3DFMT_YUY2;
- case QVideoFrame::Format_NV12:
- return D3DFMT_NV12;
- case QVideoFrame::Format_YV12:
- return D3DFMT_YV12;
- case QVideoFrame::Format_Invalid:
- default:
- return D3DFMT_UNKNOWN;
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/common/evr/evrhelpers.h b/src/plugins/common/evr/evrhelpers.h
deleted file mode 100644
index b5bdf5ead..000000000
--- a/src/plugins/common/evr/evrhelpers.h
+++ /dev/null
@@ -1,101 +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 EVRHELPERS_H
-#define EVRHELPERS_H
-
-#include "evrdefs.h"
-#include <qvideoframe.h>
-
-QT_BEGIN_NAMESPACE
-
-template<class T>
-static inline void qt_evr_safe_release(T **unk)
-{
- if (*unk) {
- (*unk)->Release();
- *unk = NULL;
- }
-}
-
-HRESULT qt_evr_getFourCC(IMFMediaType *type, DWORD *fourCC);
-
-bool qt_evr_areMediaTypesEqual(IMFMediaType *type1, IMFMediaType *type2);
-
-HRESULT qt_evr_validateVideoArea(const MFVideoArea& area, UINT32 width, UINT32 height);
-
-bool qt_evr_isSampleTimePassed(IMFClock *clock, IMFSample *sample);
-
-inline float qt_evr_MFOffsetToFloat(const MFOffset& offset)
-{
- return offset.value + (float(offset.fract) / 65536);
-}
-
-inline MFOffset qt_evr_makeMFOffset(float v)
-{
- MFOffset offset;
- offset.value = short(v);
- offset.fract = WORD(65536 * (v-offset.value));
- return offset;
-}
-
-inline MFVideoArea qt_evr_makeMFArea(float x, float y, DWORD width, DWORD height)
-{
- MFVideoArea area;
- area.OffsetX = qt_evr_makeMFOffset(x);
- area.OffsetY = qt_evr_makeMFOffset(y);
- area.Area.cx = width;
- area.Area.cy = height;
- return area;
-}
-
-inline HRESULT qt_evr_getFrameRate(IMFMediaType *pType, MFRatio *pRatio)
-{
- return MFGetAttributeRatio(pType, MF_MT_FRAME_RATE,
- reinterpret_cast<UINT32*>(&pRatio->Numerator),
- reinterpret_cast<UINT32*>(&pRatio->Denominator));
-}
-
-QVideoFrame::PixelFormat qt_evr_pixelFormatFromD3DFormat(DWORD format);
-D3DFORMAT qt_evr_D3DFormatFromPixelFormat(QVideoFrame::PixelFormat format);
-
-QT_END_NAMESPACE
-
-#endif // EVRHELPERS_H
-
diff --git a/src/plugins/common/evr/evrvideowindowcontrol.cpp b/src/plugins/common/evr/evrvideowindowcontrol.cpp
deleted file mode 100644
index 95f63c2e7..000000000
--- a/src/plugins/common/evr/evrvideowindowcontrol.cpp
+++ /dev/null
@@ -1,362 +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 "evrvideowindowcontrol.h"
-
-#ifndef QT_NO_WIDGETS
-#include <qwidget.h>
-#endif
-
-EvrVideoWindowControl::EvrVideoWindowControl(QObject *parent)
- : QVideoWindowControl(parent)
- , m_windowId(0)
- , m_windowColor(RGB(0, 0, 0))
- , m_dirtyValues(0)
- , m_aspectRatioMode(Qt::KeepAspectRatio)
- , m_brightness(0)
- , m_contrast(0)
- , m_hue(0)
- , m_saturation(0)
- , m_fullScreen(false)
- , m_displayControl(0)
- , m_processor(0)
-{
-}
-
-EvrVideoWindowControl::~EvrVideoWindowControl()
-{
- clear();
-}
-
-bool EvrVideoWindowControl::setEvr(IUnknown *evr)
-{
- clear();
-
- if (!evr)
- return true;
-
- IMFGetService *service = NULL;
-
- if (SUCCEEDED(evr->QueryInterface(IID_PPV_ARGS(&service)))
- && SUCCEEDED(service->GetService(mr_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_displayControl)))) {
-
- service->GetService(mr_VIDEO_MIXER_SERVICE, IID_PPV_ARGS(&m_processor));
-
- setWinId(m_windowId);
- setDisplayRect(m_displayRect);
- setAspectRatioMode(m_aspectRatioMode);
- m_dirtyValues = DXVA2_ProcAmp_Brightness | DXVA2_ProcAmp_Contrast | DXVA2_ProcAmp_Hue | DXVA2_ProcAmp_Saturation;
- applyImageControls();
- }
-
- if (service)
- service->Release();
-
- return m_displayControl != NULL;
-}
-
-void EvrVideoWindowControl::clear()
-{
- if (m_displayControl)
- m_displayControl->Release();
- m_displayControl = NULL;
-
- if (m_processor)
- m_processor->Release();
- m_processor = NULL;
-}
-
-WId EvrVideoWindowControl::winId() const
-{
- return m_windowId;
-}
-
-void EvrVideoWindowControl::setWinId(WId id)
-{
- m_windowId = id;
-
-#ifndef QT_NO_WIDGETS
- if (QWidget *widget = QWidget::find(m_windowId)) {
- const QColor color = widget->palette().color(QPalette::Window);
-
- m_windowColor = RGB(color.red(), color.green(), color.blue());
- }
-#endif
-
- if (m_displayControl)
- m_displayControl->SetVideoWindow(HWND(m_windowId));
-}
-
-QRect EvrVideoWindowControl::displayRect() const
-{
- return m_displayRect;
-}
-
-void EvrVideoWindowControl::setDisplayRect(const QRect &rect)
-{
- m_displayRect = rect;
-
- if (m_displayControl) {
- RECT displayRect = { rect.left(), rect.top(), rect.right() + 1, rect.bottom() + 1 };
- QSize sourceSize = nativeSize();
-
- RECT sourceRect = { 0, 0, sourceSize.width(), sourceSize.height() };
-
- if (m_aspectRatioMode == Qt::KeepAspectRatioByExpanding) {
- QSize clippedSize = rect.size();
- clippedSize.scale(sourceRect.right, sourceRect.bottom, Qt::KeepAspectRatio);
-
- sourceRect.left = (sourceRect.right - clippedSize.width()) / 2;
- sourceRect.top = (sourceRect.bottom - clippedSize.height()) / 2;
- sourceRect.right = sourceRect.left + clippedSize.width();
- sourceRect.bottom = sourceRect.top + clippedSize.height();
- }
-
- if (sourceSize.width() > 0 && sourceSize.height() > 0) {
- MFVideoNormalizedRect sourceNormRect;
- sourceNormRect.left = float(sourceRect.left) / float(sourceRect.right);
- sourceNormRect.top = float(sourceRect.top) / float(sourceRect.bottom);
- sourceNormRect.right = float(sourceRect.right) / float(sourceRect.right);
- sourceNormRect.bottom = float(sourceRect.bottom) / float(sourceRect.bottom);
- m_displayControl->SetVideoPosition(&sourceNormRect, &displayRect);
- } else {
- m_displayControl->SetVideoPosition(NULL, &displayRect);
- }
-
- // To refresh content immediately.
- repaint();
- }
-}
-
-bool EvrVideoWindowControl::isFullScreen() const
-{
- return m_fullScreen;
-}
-
-void EvrVideoWindowControl::setFullScreen(bool fullScreen)
-{
- if (m_fullScreen == fullScreen)
- return;
- emit fullScreenChanged(m_fullScreen = fullScreen);
-}
-
-void EvrVideoWindowControl::repaint()
-{
- QSize size = nativeSize();
- if (size.width() > 0 && size.height() > 0
- && m_displayControl
- && SUCCEEDED(m_displayControl->RepaintVideo())) {
- return;
- }
-
- PAINTSTRUCT paint;
- if (HDC dc = ::BeginPaint(HWND(m_windowId), &paint)) {
- HPEN pen = ::CreatePen(PS_SOLID, 1, m_windowColor);
- HBRUSH brush = ::CreateSolidBrush(m_windowColor);
- ::SelectObject(dc, pen);
- ::SelectObject(dc, brush);
-
- ::Rectangle(
- dc,
- m_displayRect.left(),
- m_displayRect.top(),
- m_displayRect.right() + 1,
- m_displayRect.bottom() + 1);
-
- ::DeleteObject(pen);
- ::DeleteObject(brush);
- ::EndPaint(HWND(m_windowId), &paint);
- }
-}
-
-QSize EvrVideoWindowControl::nativeSize() const
-{
- QSize size;
- if (m_displayControl) {
- SIZE sourceSize;
- if (SUCCEEDED(m_displayControl->GetNativeVideoSize(&sourceSize, 0)))
- size = QSize(sourceSize.cx, sourceSize.cy);
- }
- return size;
-}
-
-Qt::AspectRatioMode EvrVideoWindowControl::aspectRatioMode() const
-{
- return m_aspectRatioMode;
-}
-
-void EvrVideoWindowControl::setAspectRatioMode(Qt::AspectRatioMode mode)
-{
- m_aspectRatioMode = mode;
-
- if (m_displayControl) {
- switch (mode) {
- case Qt::IgnoreAspectRatio:
- //comment from MSDN: Do not maintain the aspect ratio of the video. Stretch the video to fit the output rectangle.
- m_displayControl->SetAspectRatioMode(MFVideoARMode_None);
- break;
- case Qt::KeepAspectRatio:
- //comment from MSDN: Preserve the aspect ratio of the video by letterboxing or within the output rectangle.
- m_displayControl->SetAspectRatioMode(MFVideoARMode_PreservePicture);
- break;
- case Qt::KeepAspectRatioByExpanding:
- //for this mode, more adjustment will be done in setDisplayRect
- m_displayControl->SetAspectRatioMode(MFVideoARMode_PreservePicture);
- break;
- default:
- break;
- }
- setDisplayRect(m_displayRect);
- }
-}
-
-int EvrVideoWindowControl::brightness() const
-{
- return m_brightness;
-}
-
-void EvrVideoWindowControl::setBrightness(int brightness)
-{
- if (m_brightness == brightness)
- return;
-
- m_brightness = brightness;
-
- m_dirtyValues |= DXVA2_ProcAmp_Brightness;
-
- applyImageControls();
-
- emit brightnessChanged(brightness);
-}
-
-int EvrVideoWindowControl::contrast() const
-{
- return m_contrast;
-}
-
-void EvrVideoWindowControl::setContrast(int contrast)
-{
- if (m_contrast == contrast)
- return;
-
- m_contrast = contrast;
-
- m_dirtyValues |= DXVA2_ProcAmp_Contrast;
-
- applyImageControls();
-
- emit contrastChanged(contrast);
-}
-
-int EvrVideoWindowControl::hue() const
-{
- return m_hue;
-}
-
-void EvrVideoWindowControl::setHue(int hue)
-{
- if (m_hue == hue)
- return;
-
- m_hue = hue;
-
- m_dirtyValues |= DXVA2_ProcAmp_Hue;
-
- applyImageControls();
-
- emit hueChanged(hue);
-}
-
-int EvrVideoWindowControl::saturation() const
-{
- return m_saturation;
-}
-
-void EvrVideoWindowControl::setSaturation(int saturation)
-{
- if (m_saturation == saturation)
- return;
-
- m_saturation = saturation;
-
- m_dirtyValues |= DXVA2_ProcAmp_Saturation;
-
- applyImageControls();
-
- emit saturationChanged(saturation);
-}
-
-void EvrVideoWindowControl::applyImageControls()
-{
- if (m_processor) {
- DXVA2_ProcAmpValues values;
- if (m_dirtyValues & DXVA2_ProcAmp_Brightness) {
- values.Brightness = scaleProcAmpValue(DXVA2_ProcAmp_Brightness, m_brightness);
- }
- if (m_dirtyValues & DXVA2_ProcAmp_Contrast) {
- values.Contrast = scaleProcAmpValue(DXVA2_ProcAmp_Contrast, m_contrast);
- }
- if (m_dirtyValues & DXVA2_ProcAmp_Hue) {
- values.Hue = scaleProcAmpValue(DXVA2_ProcAmp_Hue, m_hue);
- }
- if (m_dirtyValues & DXVA2_ProcAmp_Saturation) {
- values.Saturation = scaleProcAmpValue(DXVA2_ProcAmp_Saturation, m_saturation);
- }
-
- if (SUCCEEDED(m_processor->SetProcAmpValues(m_dirtyValues, &values))) {
- m_dirtyValues = 0;
- }
- }
-}
-
-DXVA2_Fixed32 EvrVideoWindowControl::scaleProcAmpValue(DWORD prop, int value) const
-{
- float scaledValue = 0.0;
-
- DXVA2_ValueRange range;
- if (SUCCEEDED(m_processor->GetProcAmpRange(prop, &range))) {
- scaledValue = DXVA2FixedToFloat(range.DefaultValue);
- if (value > 0)
- scaledValue += float(value) * (DXVA2FixedToFloat(range.MaxValue) - DXVA2FixedToFloat(range.DefaultValue)) / 100;
- else if (value < 0)
- scaledValue -= float(value) * (DXVA2FixedToFloat(range.MinValue) - DXVA2FixedToFloat(range.DefaultValue)) / 100;
- }
-
- return DXVA2FloatToFixed(scaledValue);
-}
diff --git a/src/plugins/common/evr/evrvideowindowcontrol.h b/src/plugins/common/evr/evrvideowindowcontrol.h
deleted file mode 100644
index ce3b7746f..000000000
--- a/src/plugins/common/evr/evrvideowindowcontrol.h
+++ /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:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 EVRVIDEOWINDOWCONTROL_H
-#define EVRVIDEOWINDOWCONTROL_H
-
-#include "qvideowindowcontrol.h"
-
-#include "evrdefs.h"
-
-QT_BEGIN_NAMESPACE
-
-class EvrVideoWindowControl : public QVideoWindowControl
-{
- Q_OBJECT
-public:
- EvrVideoWindowControl(QObject *parent = 0);
- ~EvrVideoWindowControl() override;
-
- bool setEvr(IUnknown *evr);
-
- WId winId() const override;
- void setWinId(WId id) override;
-
- QRect displayRect() const override;
- void setDisplayRect(const QRect &rect) override;
-
- bool isFullScreen() const override;
- void setFullScreen(bool fullScreen) override;
-
- void repaint() override;
-
- QSize nativeSize() const override;
-
- Qt::AspectRatioMode aspectRatioMode() const override;
- void setAspectRatioMode(Qt::AspectRatioMode mode) override;
-
- int brightness() const override;
- void setBrightness(int brightness) override;
-
- int contrast() const override;
- void setContrast(int contrast) override;
-
- int hue() const override;
- void setHue(int hue) override;
-
- int saturation() const override;
- void setSaturation(int saturation) override;
-
- void applyImageControls();
-
-private:
- void clear();
- DXVA2_Fixed32 scaleProcAmpValue(DWORD prop, int value) const;
-
- WId m_windowId;
- COLORREF m_windowColor;
- DWORD m_dirtyValues;
- Qt::AspectRatioMode m_aspectRatioMode;
- QRect m_displayRect;
- int m_brightness;
- int m_contrast;
- int m_hue;
- int m_saturation;
- bool m_fullScreen;
-
- IMFVideoDisplayControl *m_displayControl;
- IMFVideoProcessor *m_processor;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/coreaudio/coreaudio.json b/src/plugins/coreaudio/coreaudio.json
deleted file mode 100644
index a31d52107..000000000
--- a/src/plugins/coreaudio/coreaudio.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "Keys": ["default"]
-}
diff --git a/src/plugins/coreaudio/coreaudio.pro b/src/plugins/coreaudio/coreaudio.pro
deleted file mode 100644
index 50159311a..000000000
--- a/src/plugins/coreaudio/coreaudio.pro
+++ /dev/null
@@ -1,39 +0,0 @@
-TARGET = qtaudio_coreaudio
-QT += multimedia-private
-
-OTHER_FILES += \
- coreaudio.json
-
-#DEFINES += QT_DEBUG_COREAUDIO
-
-HEADERS += \
- coreaudiodeviceinfo.h \
- coreaudioinput.h \
- coreaudiooutput.h \
- coreaudioplugin.h \
- coreaudioutils.h
-
-OBJECTIVE_SOURCES += \
- coreaudiodeviceinfo.mm \
- coreaudioinput.mm \
- coreaudiooutput.mm \
- coreaudioplugin.mm \
- coreaudioutils.mm
-
-ios|tvos {
- HEADERS += coreaudiosessionmanager.h
- OBJECTIVE_SOURCES += coreaudiosessionmanager.mm
- LIBS += -framework Foundation -framework AVFoundation
-} else {
- LIBS += \
- -framework ApplicationServices \
- -framework AudioUnit
-}
-
-LIBS += \
- -framework CoreAudio \
- -framework AudioToolbox
-
-PLUGIN_TYPE = audio
-PLUGIN_CLASS_NAME = CoreAudioPlugin
-load(qt_plugin)
diff --git a/src/plugins/coreaudio/coreaudiodeviceinfo.h b/src/plugins/coreaudio/coreaudiodeviceinfo.h
deleted file mode 100644
index 08c3961e6..000000000
--- a/src/plugins/coreaudio/coreaudiodeviceinfo.h
+++ /dev/null
@@ -1,82 +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 IOSAUDIODEVICEINFO_H
-#define IOSAUDIODEVICEINFO_H
-
-#include <qaudiosystem.h>
-
-#if defined(Q_OS_OSX)
-# include <CoreAudio/CoreAudio.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class CoreAudioDeviceInfo : public QAbstractAudioDeviceInfo
-{
- Q_OBJECT
-
-public:
- CoreAudioDeviceInfo(const QByteArray &device, QAudio::Mode mode);
- ~CoreAudioDeviceInfo() {}
-
- QAudioFormat preferredFormat() const;
- bool isFormatSupported(const QAudioFormat &format) const;
- QString deviceName() const;
- QStringList supportedCodecs();
- QList<int> supportedSampleRates();
- QList<int> supportedChannelCounts();
- QList<int> supportedSampleSizes();
- QList<QAudioFormat::Endian> supportedByteOrders();
- QList<QAudioFormat::SampleType> supportedSampleTypes();
-
- static QByteArray defaultDevice(QAudio::Mode mode);
- static QList<QByteArray> availableDevices(QAudio::Mode mode);
-
-private:
-#if defined(Q_OS_OSX)
- AudioDeviceID m_deviceId;
-#endif
-
- QString m_device;
- QAudio::Mode m_mode;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/coreaudio/coreaudiodeviceinfo.mm b/src/plugins/coreaudio/coreaudiodeviceinfo.mm
deleted file mode 100644
index 94ed6dc24..000000000
--- a/src/plugins/coreaudio/coreaudiodeviceinfo.mm
+++ /dev/null
@@ -1,346 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "coreaudiodeviceinfo.h"
-#include "coreaudioutils.h"
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
-# include "coreaudiosessionmanager.h"
-#endif
-
-#include <QtCore/QDataStream>
-#include <QtCore/QDebug>
-#include <QtCore/QSet>
-#include <QIODevice>
-
-QT_BEGIN_NAMESPACE
-
-CoreAudioDeviceInfo::CoreAudioDeviceInfo(const QByteArray &device, QAudio::Mode mode)
- : m_mode(mode)
-{
-#if defined(Q_OS_OSX)
- quint32 deviceID;
-
- QDataStream dataStream(device);
- dataStream >> deviceID >> m_device;
- m_deviceId = AudioDeviceID(deviceID);
-#else //iOS
- m_device = device;
-#endif
-}
-
-
-QAudioFormat CoreAudioDeviceInfo::preferredFormat() const
-{
- QAudioFormat format;
-
-#if defined(Q_OS_OSX)
- UInt32 propSize = 0;
- AudioObjectPropertyScope audioDevicePropertyScope = m_mode == QAudio::AudioInput ? 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() << "QAudioDeviceInfo: Unable to find perferedFormat for stream";
- }
- } else {
- qWarning() << "QAudioDeviceInfo: Unable to find size of perferedFormat for stream";
- }
- }
- }
-
- delete[] streams;
- }
- }
-#else //iOS
- format.setSampleSize(16);
- if (m_mode == QAudio::AudioInput) {
- format.setChannelCount(1);
- format.setSampleRate(8000);
- } else {
- format.setChannelCount(2);
- format.setSampleRate(44100);
- }
- format.setCodec(QString::fromLatin1("audio/pcm"));
- format.setByteOrder(QAudioFormat::LittleEndian);
- format.setSampleType(QAudioFormat::SignedInt);
-#endif
-
- return format;
-}
-
-
-bool CoreAudioDeviceInfo::isFormatSupported(const QAudioFormat &format) const
-{
- CoreAudioDeviceInfo *self = const_cast<CoreAudioDeviceInfo*>(this);
-
- //Sample rates are more of a suggestion with CoreAudio so as long as we get a
- //sane value then we can likely use it.
- return format.isValid()
- && format.codec() == QString::fromLatin1("audio/pcm")
- && format.sampleRate() > 0
- && self->supportedChannelCounts().contains(format.channelCount())
- && self->supportedSampleSizes().contains(format.sampleSize());
-}
-
-
-QString CoreAudioDeviceInfo::deviceName() const
-{
- return m_device;
-}
-
-
-QStringList CoreAudioDeviceInfo::supportedCodecs()
-{
- return QStringList() << QString::fromLatin1("audio/pcm");
-}
-
-
-QList<int> CoreAudioDeviceInfo::supportedSampleRates()
-{
- QSet<int> sampleRates;
-
-#if defined(Q_OS_OSX)
- UInt32 propSize = 0;
- AudioObjectPropertyScope scope = m_mode == QAudio::AudioInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
- AudioObjectPropertyAddress availableNominalSampleRatesAddress = { kAudioDevicePropertyAvailableNominalSampleRates,
- scope,
- kAudioObjectPropertyElementMaster };
-
- if (AudioObjectGetPropertyDataSize(m_deviceId, &availableNominalSampleRatesAddress, 0, NULL, &propSize) == noErr) {
- const int pc = propSize / sizeof(AudioValueRange);
-
- if (pc > 0) {
- AudioValueRange* vr = new AudioValueRange[pc];
-
- if (AudioObjectGetPropertyData(m_deviceId, &availableNominalSampleRatesAddress, 0, NULL, &propSize, vr) == noErr) {
- for (int i = 0; i < pc; ++i) {
- sampleRates << vr[i].mMinimum << vr[i].mMaximum;
- }
- }
-
- delete[] vr;
- }
- }
-#else //iOS
- //iOS doesn't have a way to query available sample rates
- //instead we provide reasonable targets
- //It may be necessary have CoreAudioSessionManger test combinations
- //with available hardware
- sampleRates << 8000 << 11025 << 22050 << 44100 << 48000;
-#endif
- return sampleRates.values();
-}
-
-
-QList<int> CoreAudioDeviceInfo::supportedChannelCounts()
-{
- static QList<int> supportedChannels;
-
- if (supportedChannels.isEmpty()) {
- // If the number of channels is not supported by an audio device, Core Audio will
- // automatically convert the audio data.
- for (int i = 1; i <= 16; ++i)
- supportedChannels.append(i);
- }
-
- return supportedChannels;
-}
-
-
-QList<int> CoreAudioDeviceInfo::supportedSampleSizes()
-{
- return QList<int>() << 8 << 16 << 24 << 32 << 64;
-}
-
-
-QList<QAudioFormat::Endian> CoreAudioDeviceInfo::supportedByteOrders()
-{
- return QList<QAudioFormat::Endian>() << QAudioFormat::LittleEndian << QAudioFormat::BigEndian;
-}
-
-
-QList<QAudioFormat::SampleType> CoreAudioDeviceInfo::supportedSampleTypes()
-{
- return QList<QAudioFormat::SampleType>() << QAudioFormat::SignedInt << QAudioFormat::UnSignedInt << QAudioFormat::Float;
-}
-
-#if defined(Q_OS_OSX)
-// XXX: remove at some future date
-static inline QString cfStringToQString(CFStringRef str)
-{
- CFIndex length = CFStringGetLength(str);
- const UniChar *chars = CFStringGetCharactersPtr(str);
- if (chars)
- return QString(reinterpret_cast<const QChar *>(chars), length);
-
- UniChar buffer[length];
- CFStringGetCharacters(str, CFRangeMake(0, length), buffer);
- return QString(reinterpret_cast<const QChar *>(buffer), length);
-}
-
-static QByteArray get_device_info(AudioDeviceID audioDevice, QAudio::Mode mode)
-{
- UInt32 size;
- QByteArray device;
- QDataStream ds(&device, QIODevice::WriteOnly);
- AudioStreamBasicDescription sf;
- CFStringRef name;
- Boolean isInput = mode == QAudio::AudioInput;
- AudioObjectPropertyScope audioPropertyScope = isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
-
- // Id
- ds << quint32(audioDevice);
-
- // Mode //TODO: Why don't we use the Stream Format we ask for?
- size = sizeof(AudioStreamBasicDescription);
- AudioObjectPropertyAddress audioDeviceStreamFormatPropertyAddress = { kAudioDevicePropertyStreamFormat,
- audioPropertyScope,
- kAudioObjectPropertyElementMaster };
-
- if (AudioObjectGetPropertyData(audioDevice, &audioDeviceStreamFormatPropertyAddress, 0, NULL, &size, &sf) != noErr) {
- return QByteArray();
- }
-
- // Name
- size = sizeof(CFStringRef);
- AudioObjectPropertyAddress audioDeviceNamePropertyAddress = { kAudioObjectPropertyName,
- audioPropertyScope,
- kAudioObjectPropertyElementMaster };
-
- if (AudioObjectGetPropertyData(audioDevice, &audioDeviceNamePropertyAddress, 0, NULL, &size, &name) != noErr) {
- qWarning() << "QAudioDeviceInfo: Unable to find device name";
- return QByteArray();
- }
- ds << cfStringToQString(name);
-
- CFRelease(name);
-
- return device;
-}
-#endif
-
-QByteArray CoreAudioDeviceInfo::defaultDevice(QAudio::Mode mode)
-{
-#if defined(Q_OS_OSX)
- AudioDeviceID audioDevice;
- UInt32 size = sizeof(audioDevice);
- const AudioObjectPropertySelector selector = (mode == QAudio::AudioOutput) ? kAudioHardwarePropertyDefaultOutputDevice
- : kAudioHardwarePropertyDefaultInputDevice;
- AudioObjectPropertyAddress defaultDevicePropertyAddress = { selector,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
-
- if (AudioObjectGetPropertyData(kAudioObjectSystemObject,
- &defaultDevicePropertyAddress,
- 0, NULL, &size, &audioDevice) != noErr) {
- qWarning("QAudioDeviceInfo: Unable to find default %s device", (mode == QAudio::AudioOutput) ? "output" : "input");
- return QByteArray();
- }
-
- return get_device_info(audioDevice, mode);
-#else //iOS
- const auto &devices = (mode == QAudio::AudioOutput) ? CoreAudioSessionManager::instance().outputDevices()
- : CoreAudioSessionManager::instance().inputDevices();
- return !devices.isEmpty() ? devices.first() : QByteArray();
-#endif
-}
-
-QList<QByteArray> CoreAudioDeviceInfo::availableDevices(QAudio::Mode mode)
-{
- QList<QByteArray> devices;
-#if defined(Q_OS_OSX)
- UInt32 propSize = 0;
- AudioObjectPropertyAddress audioDevicesPropertyAddress = { kAudioHardwarePropertyDevices,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
-
- if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
- &audioDevicesPropertyAddress,
- 0, NULL, &propSize) == noErr) {
-
- const int dc = propSize / sizeof(AudioDeviceID);
-
- if (dc > 0) {
- AudioDeviceID* audioDevices = new AudioDeviceID[dc];
-
- if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &audioDevicesPropertyAddress, 0, NULL, &propSize, audioDevices) == noErr) {
- for (int i = 0; i < dc; ++i) {
- const QByteArray &info = get_device_info(audioDevices[i], mode);
- if (!info.isNull())
- devices << info;
- }
- }
-
- delete[] audioDevices;
- }
- }
-#else //iOS
- if (mode == QAudio::AudioOutput)
- return CoreAudioSessionManager::instance().outputDevices();
- if (mode == QAudio::AudioInput)
- return CoreAudioSessionManager::instance().inputDevices();
-#endif
-
- return devices;
-}
-
-QT_END_NAMESPACE
-
-#include "moc_coreaudiodeviceinfo.cpp"
diff --git a/src/plugins/coreaudio/coreaudioinput.h b/src/plugins/coreaudio/coreaudioinput.h
deleted file mode 100644
index 40455b3db..000000000
--- a/src/plugins/coreaudio/coreaudioinput.h
+++ /dev/null
@@ -1,266 +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 IOSAUDIOINPUT_H
-#define IOSAUDIOINPUT_H
-
-#include <qaudiosystem.h>
-#include <AudioUnit/AudioUnit.h>
-#include <CoreAudio/CoreAudioTypes.h>
-#include <AudioToolbox/AudioToolbox.h>
-
-#include <QtCore/QIODevice>
-#include <QtCore/QWaitCondition>
-#include <QtCore/QMutex>
-#include <QtCore/QTimer>
-
-QT_BEGIN_NAMESPACE
-
-class CoreAudioRingBuffer;
-class CoreAudioPacketFeeder;
-class CoreAudioInputBuffer;
-class CoreAudioInputDevice;
-
-class CoreAudioBufferList
-{
-public:
- CoreAudioBufferList(AudioStreamBasicDescription const& streamFormat);
- CoreAudioBufferList(AudioStreamBasicDescription const& streamFormat, char *buffer, int bufferSize);
- CoreAudioBufferList(AudioStreamBasicDescription const& streamFormat, int framesToBuffer);
-
- ~CoreAudioBufferList();
-
- AudioBufferList* audioBufferList() const { return m_bufferList; }
- char *data(int buffer = 0) const;
- qint64 bufferSize(int buffer = 0) const;
- int frameCount(int buffer = 0) const;
- int packetCount(int buffer = 0) const;
- int packetSize() const;
- void reset();
-
-private:
- bool m_owner;
- int m_dataSize;
- AudioStreamBasicDescription m_streamDescription;
- AudioBufferList *m_bufferList;
-};
-
-class CoreAudioPacketFeeder
-{
-public:
- CoreAudioPacketFeeder(CoreAudioBufferList *abl);
-
- bool feed(AudioBufferList& dst, UInt32& packetCount);
- bool empty() const;
-
-private:
- UInt32 m_totalPackets;
- UInt32 m_position;
- CoreAudioBufferList *m_audioBufferList;
-};
-
-class CoreAudioInputBuffer : public QObject
-{
- Q_OBJECT
-
-public:
- CoreAudioInputBuffer(int bufferSize,
- int maxPeriodSize,
- AudioStreamBasicDescription const& inputFormat,
- AudioStreamBasicDescription const& outputFormat,
- QObject *parent);
-
- ~CoreAudioInputBuffer();
-
- qreal volume() const;
- void setVolume(qreal v);
-
- qint64 renderFromDevice(AudioUnit audioUnit,
- AudioUnitRenderActionFlags *ioActionFlags,
- const AudioTimeStamp *inTimeStamp,
- UInt32 inBusNumber,
- UInt32 inNumberFrames);
-
- qint64 readBytes(char *data, qint64 len);
-
- void setFlushDevice(QIODevice *device);
-
- void startFlushTimer();
- void stopFlushTimer();
-
- void flush(bool all = false);
- void reset();
- int available() const;
- int used() const;
-
-signals:
- void readyRead();
-
-private slots:
- void flushBuffer();
-
-private:
- bool m_deviceError;
- int m_maxPeriodSize;
- int m_periodTime;
- QIODevice *m_device;
- QTimer *m_flushTimer;
- CoreAudioRingBuffer *m_buffer;
- CoreAudioBufferList *m_inputBufferList;
- AudioConverterRef m_audioConverter;
- AudioStreamBasicDescription m_inputFormat;
- AudioStreamBasicDescription m_outputFormat;
- QAudioFormat m_qFormat;
- qreal m_volume;
-
- const static OSStatus as_empty = 'qtem';
-
- // Converter callback
- static OSStatus converterCallback(AudioConverterRef inAudioConverter,
- UInt32 *ioNumberDataPackets,
- AudioBufferList *ioData,
- AudioStreamPacketDescription **outDataPacketDescription,
- void *inUserData);
-};
-
-class CoreAudioInputDevice : public QIODevice
-{
- Q_OBJECT
-
-public:
- CoreAudioInputDevice(CoreAudioInputBuffer *audioBuffer, QObject *parent);
-
- qint64 readData(char *data, qint64 len);
- qint64 writeData(const char *data, qint64 len);
-
- bool isSequential() const { return true; }
-
-private:
- CoreAudioInputBuffer *m_audioBuffer;
-};
-
-class CoreAudioInput : public QAbstractAudioInput
-{
- Q_OBJECT
-
-public:
- CoreAudioInput(const QByteArray &device);
- ~CoreAudioInput();
-
- void start(QIODevice *device);
- QIODevice *start();
- void stop();
- void reset();
- void suspend();
- void resume();
- int bytesReady() const;
- int periodSize() const;
- void setBufferSize(int value);
- int bufferSize() const;
- void setNotifyInterval(int milliSeconds);
- int notifyInterval() const;
- qint64 processedUSecs() const;
- qint64 elapsedUSecs() const;
- QAudio::Error error() const;
- QAudio::State state() const;
- void setFormat(const QAudioFormat &format);
- QAudioFormat format() const;
-
- void setVolume(qreal volume);
- qreal volume() const;
-
-private slots:
- void deviceStoppped();
-
-private:
- enum {
- Running,
- Stopped
- };
-
- bool open();
- void close();
-
- void audioThreadStart();
- void audioThreadStop();
-
- void audioDeviceStop();
- void audioDeviceActive();
- void audioDeviceFull();
- void audioDeviceError();
-
- void startTimers();
- void stopTimers();
-
- // Input callback
- static OSStatus inputCallback(void *inRefCon,
- AudioUnitRenderActionFlags *ioActionFlags,
- const AudioTimeStamp *inTimeStamp,
- UInt32 inBusNumber,
- UInt32 inNumberFrames,
- AudioBufferList *ioData);
-
- QByteArray m_device;
- bool m_isOpen;
- int m_periodSizeBytes;
- int m_internalBufferSize;
- qint64 m_totalFrames;
- QAudioFormat m_audioFormat;
- QIODevice *m_audioIO;
- AudioUnit m_audioUnit;
-#if defined(Q_OS_OSX)
- AudioDeviceID m_audioDeviceId;
-#endif
- Float64 m_clockFrequency;
- UInt64 m_startTime;
- QAudio::Error m_errorCode;
- QAudio::State m_stateCode;
- CoreAudioInputBuffer *m_audioBuffer;
- QMutex m_mutex;
- QWaitCondition m_threadFinished;
- QAtomicInt m_audioThreadState;
- QTimer *m_intervalTimer;
- AudioStreamBasicDescription m_streamFormat;
- AudioStreamBasicDescription m_deviceFormat;
- QAbstractAudioDeviceInfo *m_audioDeviceInfo;
- qreal m_volume;
-};
-
-QT_END_NAMESPACE
-
-#endif // IOSAUDIOINPUT_H
diff --git a/src/plugins/coreaudio/coreaudioinput.mm b/src/plugins/coreaudio/coreaudioinput.mm
deleted file mode 100644
index 9f6a1f7bc..000000000
--- a/src/plugins/coreaudio/coreaudioinput.mm
+++ /dev/null
@@ -1,1005 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "coreaudioinput.h"
-#include "coreaudiosessionmanager.h"
-#include "coreaudiodeviceinfo.h"
-#include "coreaudioutils.h"
-
-#if defined(Q_OS_OSX)
-# include <AudioUnit/AudioComponent.h>
-#endif
-
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
-# include "coreaudiosessionmanager.h"
-#endif
-
-#include <QtMultimedia/private/qaudiohelpers_p.h>
-#include <QtCore/QDataStream>
-#include <QtCore/QDebug>
-
-QT_BEGIN_NAMESPACE
-
-static const int DEFAULT_BUFFER_SIZE = 4 * 1024;
-
-CoreAudioBufferList::CoreAudioBufferList(const AudioStreamBasicDescription &streamFormat)
- : m_owner(false)
- , m_streamDescription(streamFormat)
-{
- const bool isInterleaved = (m_streamDescription.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
- const int numberOfBuffers = isInterleaved ? 1 : m_streamDescription.mChannelsPerFrame;
-
- m_dataSize = 0;
-
- m_bufferList = reinterpret_cast<AudioBufferList*>(malloc(sizeof(AudioBufferList) +
- (sizeof(AudioBuffer) * numberOfBuffers)));
-
- m_bufferList->mNumberBuffers = numberOfBuffers;
- for (int i = 0; i < numberOfBuffers; ++i) {
- m_bufferList->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1;
- m_bufferList->mBuffers[i].mDataByteSize = 0;
- m_bufferList->mBuffers[i].mData = 0;
- }
-}
-
-CoreAudioBufferList::CoreAudioBufferList(const AudioStreamBasicDescription &streamFormat, char *buffer, int bufferSize)
- : m_owner(false)
- , m_streamDescription(streamFormat)
- , m_bufferList(0)
-{
- m_dataSize = bufferSize;
-
- m_bufferList = reinterpret_cast<AudioBufferList*>(malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer)));
-
- m_bufferList->mNumberBuffers = 1;
- m_bufferList->mBuffers[0].mNumberChannels = 1;
- m_bufferList->mBuffers[0].mDataByteSize = m_dataSize;
- m_bufferList->mBuffers[0].mData = buffer;
-}
-
-CoreAudioBufferList::CoreAudioBufferList(const AudioStreamBasicDescription &streamFormat, int framesToBuffer)
- : m_owner(true)
- , m_streamDescription(streamFormat)
- , m_bufferList(0)
-{
- const bool isInterleaved = (m_streamDescription.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
- const int numberOfBuffers = isInterleaved ? 1 : m_streamDescription.mChannelsPerFrame;
-
- m_dataSize = framesToBuffer * m_streamDescription.mBytesPerFrame;
-
- m_bufferList = reinterpret_cast<AudioBufferList*>(malloc(sizeof(AudioBufferList) +
- (sizeof(AudioBuffer) * numberOfBuffers)));
- m_bufferList->mNumberBuffers = numberOfBuffers;
- for (int i = 0; i < numberOfBuffers; ++i) {
- m_bufferList->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1;
- m_bufferList->mBuffers[i].mDataByteSize = m_dataSize;
- m_bufferList->mBuffers[i].mData = malloc(m_dataSize);
- }
-}
-
-CoreAudioBufferList::~CoreAudioBufferList()
-{
- if (m_owner) {
- for (UInt32 i = 0; i < m_bufferList->mNumberBuffers; ++i)
- free(m_bufferList->mBuffers[i].mData);
- }
-
- free(m_bufferList);
-}
-
-char *CoreAudioBufferList::data(int buffer) const
-{
- return static_cast<char*>(m_bufferList->mBuffers[buffer].mData);
-}
-
-qint64 CoreAudioBufferList::bufferSize(int buffer) const
-{
- return m_bufferList->mBuffers[buffer].mDataByteSize;
-}
-
-int CoreAudioBufferList::frameCount(int buffer) const
-{
- return m_bufferList->mBuffers[buffer].mDataByteSize / m_streamDescription.mBytesPerFrame;
-}
-
-int CoreAudioBufferList::packetCount(int buffer) const
-{
- return m_bufferList->mBuffers[buffer].mDataByteSize / m_streamDescription.mBytesPerPacket;
-}
-
-int CoreAudioBufferList::packetSize() const
-{
- return m_streamDescription.mBytesPerPacket;
-}
-
-void CoreAudioBufferList::reset()
-{
- for (UInt32 i = 0; i < m_bufferList->mNumberBuffers; ++i) {
- m_bufferList->mBuffers[i].mDataByteSize = m_dataSize;
- m_bufferList->mBuffers[i].mData = 0;
- }
-}
-
-CoreAudioPacketFeeder::CoreAudioPacketFeeder(CoreAudioBufferList *abl)
- : m_audioBufferList(abl)
-{
- m_totalPackets = m_audioBufferList->packetCount();
- m_position = 0;
-}
-
-bool CoreAudioPacketFeeder::feed(AudioBufferList &dst, UInt32 &packetCount)
-{
- if (m_position == m_totalPackets) {
- dst.mBuffers[0].mDataByteSize = 0;
- packetCount = 0;
- return false;
- }
-
- if (m_totalPackets - m_position < packetCount)
- packetCount = m_totalPackets - m_position;
-
- dst.mBuffers[0].mDataByteSize = packetCount * m_audioBufferList->packetSize();
- dst.mBuffers[0].mData = m_audioBufferList->data() + (m_position * m_audioBufferList->packetSize());
-
- m_position += packetCount;
-
- return true;
-}
-
-bool CoreAudioPacketFeeder::empty() const
-{
- return m_position == m_totalPackets;
-}
-
-CoreAudioInputBuffer::CoreAudioInputBuffer(int bufferSize, int maxPeriodSize, const AudioStreamBasicDescription &inputFormat, const AudioStreamBasicDescription &outputFormat, QObject *parent)
- : QObject(parent)
- , m_deviceError(false)
- , m_device(0)
- , m_audioConverter(0)
- , m_inputFormat(inputFormat)
- , m_outputFormat(outputFormat)
- , m_volume(qreal(1.0f))
-{
- m_maxPeriodSize = maxPeriodSize;
- m_periodTime = m_maxPeriodSize / m_outputFormat.mBytesPerFrame * 1000 / m_outputFormat.mSampleRate;
-
- m_buffer = new CoreAudioRingBuffer(bufferSize);
-
- m_inputBufferList = new CoreAudioBufferList(m_inputFormat);
-
- m_flushTimer = new QTimer(this);
- connect(m_flushTimer, SIGNAL(timeout()), SLOT(flushBuffer()));
-
- if (CoreAudioUtils::toQAudioFormat(inputFormat) != CoreAudioUtils::toQAudioFormat(outputFormat)) {
- if (AudioConverterNew(&m_inputFormat, &m_outputFormat, &m_audioConverter) != noErr) {
- qWarning() << "QAudioInput: Unable to create an Audio Converter";
- m_audioConverter = 0;
- }
- }
-
- m_qFormat = CoreAudioUtils::toQAudioFormat(inputFormat); // we adjust volume before conversion
-}
-
-CoreAudioInputBuffer::~CoreAudioInputBuffer()
-{
- delete m_buffer;
-}
-
-qreal CoreAudioInputBuffer::volume() const
-{
- return m_volume;
-}
-
-void CoreAudioInputBuffer::setVolume(qreal v)
-{
- m_volume = v;
-}
-
-qint64 CoreAudioInputBuffer::renderFromDevice(AudioUnit audioUnit, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames)
-{
- const bool pullMode = m_device == 0;
-
- OSStatus err;
- qint64 framesRendered = 0;
-
- m_inputBufferList->reset();
- err = AudioUnitRender(audioUnit,
- ioActionFlags,
- inTimeStamp,
- inBusNumber,
- inNumberFrames,
- m_inputBufferList->audioBufferList());
-
- // adjust volume, if necessary
- if (!qFuzzyCompare(m_volume, qreal(1.0f))) {
- QAudioHelperInternal::qMultiplySamples(m_volume,
- m_qFormat,
- m_inputBufferList->data(), /* input */
- m_inputBufferList->data(), /* output */
- m_inputBufferList->bufferSize());
- }
-
- if (m_audioConverter != 0) {
- CoreAudioPacketFeeder feeder(m_inputBufferList);
-
- int copied = 0;
- const int available = m_buffer->free();
-
- while (err == noErr && !feeder.empty()) {
- CoreAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available - copied);
-
- if (region.second == 0)
- break;
-
- AudioBufferList output;
- output.mNumberBuffers = 1;
- output.mBuffers[0].mNumberChannels = 1;
- output.mBuffers[0].mDataByteSize = region.second;
- output.mBuffers[0].mData = region.first;
-
- UInt32 packetSize = region.second / m_outputFormat.mBytesPerPacket;
- err = AudioConverterFillComplexBuffer(m_audioConverter,
- converterCallback,
- &feeder,
- &packetSize,
- &output,
- 0);
- region.second = output.mBuffers[0].mDataByteSize;
- copied += region.second;
-
- m_buffer->releaseWriteRegion(region);
- }
-
- framesRendered += copied / m_outputFormat.mBytesPerFrame;
- }
- else {
- const int available = m_inputBufferList->bufferSize();
- bool wecan = true;
- int copied = 0;
-
- while (wecan && copied < available) {
- CoreAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available - copied);
-
- if (region.second > 0) {
- memcpy(region.first, m_inputBufferList->data() + copied, region.second);
- copied += region.second;
- }
- else
- wecan = false;
-
- m_buffer->releaseWriteRegion(region);
- }
-
- framesRendered = copied / m_outputFormat.mBytesPerFrame;
- }
-
- if (pullMode && framesRendered > 0)
- emit readyRead();
-
- return framesRendered;
-}
-
-qint64 CoreAudioInputBuffer::readBytes(char *data, qint64 len)
-{
- bool wecan = true;
- qint64 bytesCopied = 0;
-
- len -= len % m_maxPeriodSize;
- while (wecan && bytesCopied < len) {
- CoreAudioRingBuffer::Region region = m_buffer->acquireReadRegion(len - bytesCopied);
-
- if (region.second > 0) {
- memcpy(data + bytesCopied, region.first, region.second);
- bytesCopied += region.second;
- }
- else
- wecan = false;
-
- m_buffer->releaseReadRegion(region);
- }
-
- return bytesCopied;
-}
-
-void CoreAudioInputBuffer::setFlushDevice(QIODevice *device)
-{
- if (m_device != device)
- m_device = device;
-}
-
-void CoreAudioInputBuffer::startFlushTimer()
-{
- if (m_device != 0) {
- // We use the period time for the timer, since that's
- // around the buffer size (pre conversion >.>)
- m_flushTimer->start(qMax(1, m_periodTime));
- }
-}
-
-void CoreAudioInputBuffer::stopFlushTimer()
-{
- m_flushTimer->stop();
-}
-
-void CoreAudioInputBuffer::flush(bool all)
-{
- if (m_device == 0)
- return;
-
- const int used = m_buffer->used();
- const int readSize = all ? used : used - (used % m_maxPeriodSize);
-
- if (readSize > 0) {
- bool wecan = true;
- int flushed = 0;
-
- while (!m_deviceError && wecan && flushed < readSize) {
- CoreAudioRingBuffer::Region region = m_buffer->acquireReadRegion(readSize - flushed);
-
- if (region.second > 0) {
- int bytesWritten = m_device->write(region.first, region.second);
- if (bytesWritten < 0) {
- stopFlushTimer();
- m_deviceError = true;
- }
- else {
- region.second = bytesWritten;
- flushed += bytesWritten;
- wecan = bytesWritten != 0;
- }
- }
- else
- wecan = false;
-
- m_buffer->releaseReadRegion(region);
- }
- }
-}
-
-void CoreAudioInputBuffer::reset()
-{
- m_buffer->reset();
- m_deviceError = false;
-}
-
-int CoreAudioInputBuffer::available() const
-{
- return m_buffer->free();
-}
-
-int CoreAudioInputBuffer::used() const
-{
- return m_buffer->used();
-}
-
-void CoreAudioInputBuffer::flushBuffer()
-{
- flush();
-}
-
-OSStatus CoreAudioInputBuffer::converterCallback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData)
-{
- Q_UNUSED(inAudioConverter);
- Q_UNUSED(outDataPacketDescription);
-
- CoreAudioPacketFeeder* feeder = static_cast<CoreAudioPacketFeeder*>(inUserData);
-
- if (!feeder->feed(*ioData, *ioNumberDataPackets))
- return as_empty;
-
- return noErr;
-}
-
-CoreAudioInputDevice::CoreAudioInputDevice(CoreAudioInputBuffer *audioBuffer, QObject *parent)
- : QIODevice(parent)
- , m_audioBuffer(audioBuffer)
-{
- open(QIODevice::ReadOnly | QIODevice::Unbuffered);
- connect(m_audioBuffer, SIGNAL(readyRead()), SIGNAL(readyRead()));
-}
-
-qint64 CoreAudioInputDevice::readData(char *data, qint64 len)
-{
- return m_audioBuffer->readBytes(data, len);
-}
-
-qint64 CoreAudioInputDevice::writeData(const char *data, qint64 len)
-{
- Q_UNUSED(data);
- Q_UNUSED(len);
-
- return 0;
-}
-
-CoreAudioInput::CoreAudioInput(const QByteArray &device)
- : m_isOpen(false)
- , m_internalBufferSize(DEFAULT_BUFFER_SIZE)
- , m_totalFrames(0)
- , m_audioUnit(0)
- , m_clockFrequency(CoreAudioUtils::frequency() / 1000)
- , m_startTime(0)
- , m_errorCode(QAudio::NoError)
- , m_stateCode(QAudio::StoppedState)
- , m_audioBuffer(0)
- , m_volume(1.0)
-{
-#if defined(Q_OS_OSX)
- quint32 deviceId;
- QDataStream dataStream(device);
- dataStream >> deviceId >> m_device;
- m_audioDeviceId = AudioDeviceID(deviceId);
-#else //iOS
- m_device = device;
-#endif
-
- m_audioDeviceInfo = new CoreAudioDeviceInfo(device, QAudio::AudioInput);
-
- m_intervalTimer = new QTimer(this);
- m_intervalTimer->setInterval(1000);
- connect(m_intervalTimer, SIGNAL(timeout()), this, SIGNAL(notify()));
-}
-
-
-CoreAudioInput::~CoreAudioInput()
-{
- close();
- delete m_audioDeviceInfo;
-}
-
-bool CoreAudioInput::open()
-{
-#if defined(Q_OS_IOS)
- CoreAudioSessionManager::instance().setCategory(CoreAudioSessionManager::PlayAndRecord, CoreAudioSessionManager::MixWithOthers);
- CoreAudioSessionManager::instance().setActive(true);
-#endif
-
- if (m_isOpen)
- return true;
-
- UInt32 size = 0;
-
- AudioComponentDescription componentDescription;
- componentDescription.componentType = kAudioUnitType_Output;
-#if defined(Q_OS_OSX)
- componentDescription.componentSubType = kAudioUnitSubType_HALOutput;
-#else
- componentDescription.componentSubType = kAudioUnitSubType_RemoteIO;
-#endif
- componentDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
- componentDescription.componentFlags = 0;
- componentDescription.componentFlagsMask = 0;
-
- AudioComponent component = AudioComponentFindNext(0, &componentDescription);
- if (component == 0) {
- qWarning() << "QAudioInput: Failed to find Output component";
- return false;
- }
-
- if (AudioComponentInstanceNew(component, &m_audioUnit) != noErr) {
- qWarning() << "QAudioInput: Unable to Open Output Component";
- return false;
- }
-
- // Set mode
- // switch to input mode
- UInt32 enable = 1;
- if (AudioUnitSetProperty(m_audioUnit,
- kAudioOutputUnitProperty_EnableIO,
- kAudioUnitScope_Input,
- 1,
- &enable,
- sizeof(enable)) != noErr) {
- qWarning() << "QAudioInput: Unable to switch to input mode (Enable Input)";
- return false;
- }
-
- enable = 0;
- if (AudioUnitSetProperty(m_audioUnit,
- kAudioOutputUnitProperty_EnableIO,
- kAudioUnitScope_Output,
- 0,
- &enable,
- sizeof(enable)) != noErr) {
- qWarning() << "QAudioInput: Unable to switch to input mode (Disable output)";
- return false;
- }
-
- // register callback
- AURenderCallbackStruct callback;
- callback.inputProc = inputCallback;
- callback.inputProcRefCon = this;
-
- if (AudioUnitSetProperty(m_audioUnit,
- kAudioOutputUnitProperty_SetInputCallback,
- kAudioUnitScope_Global,
- 0,
- &callback,
- sizeof(callback)) != noErr) {
- qWarning() << "QAudioInput: Failed to set AudioUnit callback";
- return false;
- }
-
-#if defined(Q_OS_OSX)
- //Set Audio Device
- if (AudioUnitSetProperty(m_audioUnit,
- kAudioOutputUnitProperty_CurrentDevice,
- kAudioUnitScope_Global,
- 0,
- &m_audioDeviceId,
- sizeof(m_audioDeviceId)) != noErr) {
- qWarning() << "QAudioInput: Unable to use configured device";
- return false;
- }
-#endif
-
- //set format
- m_streamFormat = CoreAudioUtils::toAudioStreamBasicDescription(m_audioFormat);
-
-#if defined(Q_OS_OSX)
- if (m_audioFormat == m_audioDeviceInfo->preferredFormat()) {
-#endif
-
- m_deviceFormat = m_streamFormat;
- AudioUnitSetProperty(m_audioUnit,
- kAudioUnitProperty_StreamFormat,
- kAudioUnitScope_Output,
- 1,
- &m_deviceFormat,
- sizeof(m_deviceFormat));
-#if defined(Q_OS_OSX)
- } else {
- size = sizeof(m_deviceFormat);
- if (AudioUnitGetProperty(m_audioUnit,
- kAudioUnitProperty_StreamFormat,
- kAudioUnitScope_Input,
- 1,
- &m_deviceFormat,
- &size) != noErr) {
- qWarning() << "QAudioInput: Unable to retrieve device format";
- return false;
- }
-
- if (AudioUnitSetProperty(m_audioUnit,
- kAudioUnitProperty_StreamFormat,
- kAudioUnitScope_Output,
- 1,
- &m_deviceFormat,
- sizeof(m_deviceFormat)) != noErr) {
- qWarning() << "QAudioInput: Unable to set device format";
- return false;
- }
- }
-#endif
-
- //setup buffers
- UInt32 numberOfFrames;
-#if defined(Q_OS_OSX)
- size = sizeof(UInt32);
- if (AudioUnitGetProperty(m_audioUnit,
- kAudioDevicePropertyBufferFrameSize,
- kAudioUnitScope_Global,
- 0,
- &numberOfFrames,
- &size) != noErr) {
- qWarning() << "QAudioInput: Failed to get audio period size";
- return false;
- }
- //BUG: numberOfFrames gets ignored after this point
-
- AudioValueRange bufferRange;
- size = sizeof(AudioValueRange);
-
- if (AudioUnitGetProperty(m_audioUnit,
- kAudioDevicePropertyBufferFrameSizeRange,
- kAudioUnitScope_Global,
- 0,
- &bufferRange,
- &size) != noErr) {
- qWarning() << "QAudioInput: Failed to get audio period size range";
- return false;
- }
-
- // See if the requested buffer size is permissible
- numberOfFrames = qBound((UInt32)bufferRange.mMinimum, m_internalBufferSize / m_streamFormat.mBytesPerFrame, (UInt32)bufferRange.mMaximum);
-
- // Set it back
- if (AudioUnitSetProperty(m_audioUnit,
- kAudioDevicePropertyBufferFrameSize,
- kAudioUnitScope_Global,
- 0,
- &numberOfFrames,
- sizeof(UInt32)) != noErr) {
- qWarning() << "QAudioInput: Failed to set audio buffer size";
- return false;
- }
-#else //iOS
- Float32 bufferSize = CoreAudioSessionManager::instance().currentIOBufferDuration();
- bufferSize *= m_streamFormat.mSampleRate;
- numberOfFrames = bufferSize;
-#endif
-
- // Now allocate a few buffers to be safe.
- m_periodSizeBytes = m_internalBufferSize = numberOfFrames * m_streamFormat.mBytesPerFrame;
-
- m_audioBuffer = new CoreAudioInputBuffer(m_internalBufferSize * 4,
- m_periodSizeBytes,
- m_deviceFormat,
- m_streamFormat,
- this);
-
- m_audioBuffer->setVolume(m_volume);
- m_audioIO = new CoreAudioInputDevice(m_audioBuffer, this);
-
- // Init
- if (AudioUnitInitialize(m_audioUnit) != noErr) {
- qWarning() << "QAudioInput: Failed to initialize AudioUnit";
- return false;
- }
-
- m_isOpen = true;
-
- return m_isOpen;
-
-}
-
-void CoreAudioInput::close()
-{
- if (m_audioUnit != 0) {
- AudioOutputUnitStop(m_audioUnit);
- AudioUnitUninitialize(m_audioUnit);
- AudioComponentInstanceDispose(m_audioUnit);
- }
-
- delete m_audioBuffer;
-}
-
-void CoreAudioInput::start(QIODevice *device)
-{
- QIODevice* op = device;
-
- if (!m_audioDeviceInfo->isFormatSupported(m_audioFormat) || !open()) {
- m_stateCode = QAudio::StoppedState;
- m_errorCode = QAudio::OpenError;
- return;
- }
-
- reset();
- m_audioBuffer->reset();
- m_audioBuffer->setFlushDevice(op);
-
- if (op == 0)
- op = m_audioIO;
-
- // Start
- m_startTime = CoreAudioUtils::currentTime();
- m_totalFrames = 0;
-
- m_stateCode = QAudio::IdleState;
- m_errorCode = QAudio::NoError;
- emit stateChanged(m_stateCode);
-
- audioThreadStart();
-}
-
-
-QIODevice *CoreAudioInput::start()
-{
- QIODevice* op = 0;
-
- if (!m_audioDeviceInfo->isFormatSupported(m_audioFormat) || !open()) {
- m_stateCode = QAudio::StoppedState;
- m_errorCode = QAudio::OpenError;
- return m_audioIO;
- }
-
- reset();
- m_audioBuffer->reset();
- m_audioBuffer->setFlushDevice(op);
-
- if (op == 0)
- op = m_audioIO;
-
- // Start
- m_startTime = CoreAudioUtils::currentTime();
- m_totalFrames = 0;
-
- m_stateCode = QAudio::IdleState;
- m_errorCode = QAudio::NoError;
- emit stateChanged(m_stateCode);
-
- audioThreadStart();
-
- return op;
-}
-
-
-void CoreAudioInput::stop()
-{
- QMutexLocker lock(&m_mutex);
- if (m_stateCode != QAudio::StoppedState) {
- audioThreadStop();
- m_audioBuffer->flush(true);
-
- m_errorCode = QAudio::NoError;
- m_stateCode = QAudio::StoppedState;
- QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, m_stateCode));
- }
-}
-
-
-void CoreAudioInput::reset()
-{
- QMutexLocker lock(&m_mutex);
- if (m_stateCode != QAudio::StoppedState) {
- audioThreadStop();
-
- m_errorCode = QAudio::NoError;
- m_stateCode = QAudio::StoppedState;
- m_audioBuffer->reset();
- QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, m_stateCode));
- }
-}
-
-
-void CoreAudioInput::suspend()
-{
- QMutexLocker lock(&m_mutex);
- if (m_stateCode == QAudio::ActiveState || m_stateCode == QAudio::IdleState) {
- audioThreadStop();
-
- m_errorCode = QAudio::NoError;
- m_stateCode = QAudio::SuspendedState;
- QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, m_stateCode));
- }
-}
-
-
-void CoreAudioInput::resume()
-{
- QMutexLocker lock(&m_mutex);
- if (m_stateCode == QAudio::SuspendedState) {
- audioThreadStart();
-
- m_errorCode = QAudio::NoError;
- m_stateCode = QAudio::ActiveState;
- QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, m_stateCode));
- }
-}
-
-
-int CoreAudioInput::bytesReady() const
-{
- if (!m_audioBuffer)
- return 0;
- return m_audioBuffer->used();
-}
-
-
-int CoreAudioInput::periodSize() const
-{
- return m_periodSizeBytes;
-}
-
-
-void CoreAudioInput::setBufferSize(int value)
-{
- m_internalBufferSize = value;
-}
-
-
-int CoreAudioInput::bufferSize() const
-{
- return m_internalBufferSize;
-}
-
-
-void CoreAudioInput::setNotifyInterval(int milliSeconds)
-{
- if (m_intervalTimer->interval() == milliSeconds)
- return;
-
- if (milliSeconds <= 0)
- milliSeconds = 0;
-
- m_intervalTimer->setInterval(milliSeconds);
-}
-
-
-int CoreAudioInput::notifyInterval() const
-{
- return m_intervalTimer->interval();
-}
-
-
-qint64 CoreAudioInput::processedUSecs() const
-{
- return m_totalFrames * 1000000 / m_audioFormat.sampleRate();
-}
-
-
-qint64 CoreAudioInput::elapsedUSecs() const
-{
- if (m_stateCode == QAudio::StoppedState)
- return 0;
-
- return (CoreAudioUtils::currentTime() - m_startTime) / (m_clockFrequency / 1000);
-}
-
-
-QAudio::Error CoreAudioInput::error() const
-{
- return m_errorCode;
-}
-
-
-QAudio::State CoreAudioInput::state() const
-{
- return m_stateCode;
-}
-
-
-void CoreAudioInput::setFormat(const QAudioFormat &format)
-{
- if (m_stateCode == QAudio::StoppedState)
- m_audioFormat = format;
-}
-
-
-QAudioFormat CoreAudioInput::format() const
-{
- return m_audioFormat;
-}
-
-
-void CoreAudioInput::setVolume(qreal volume)
-{
- m_volume = volume;
- if (m_audioBuffer)
- m_audioBuffer->setVolume(m_volume);
-}
-
-
-qreal CoreAudioInput::volume() const
-{
- return m_volume;
-}
-
-void CoreAudioInput::deviceStoppped()
-{
- stopTimers();
- emit stateChanged(m_stateCode);
-}
-
-void CoreAudioInput::audioThreadStart()
-{
- startTimers();
- m_audioThreadState.storeRelaxed(Running);
- AudioOutputUnitStart(m_audioUnit);
-}
-
-void CoreAudioInput::audioThreadStop()
-{
- stopTimers();
- if (m_audioThreadState.testAndSetAcquire(Running, Stopped))
- m_threadFinished.wait(&m_mutex);
-}
-
-void CoreAudioInput::audioDeviceStop()
-{
- AudioOutputUnitStop(m_audioUnit);
- m_audioThreadState.storeRelaxed(Stopped);
- m_threadFinished.wakeOne();
-}
-
-void CoreAudioInput::audioDeviceActive()
-{
- if (m_stateCode == QAudio::IdleState) {
- QMutexLocker lock(&m_mutex);
- m_stateCode = QAudio::ActiveState;
- emit stateChanged(m_stateCode);
- }
-}
-
-void CoreAudioInput::audioDeviceFull()
-{
- if (m_stateCode == QAudio::ActiveState) {
- QMutexLocker lock(&m_mutex);
- m_errorCode = QAudio::UnderrunError;
- m_stateCode = QAudio::IdleState;
- emit stateChanged(m_stateCode);
- }
-}
-
-void CoreAudioInput::audioDeviceError()
-{
- if (m_stateCode == QAudio::ActiveState) {
- QMutexLocker lock(&m_mutex);
- audioDeviceStop();
-
- m_errorCode = QAudio::IOError;
- m_stateCode = QAudio::StoppedState;
- QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
- }
-}
-
-void CoreAudioInput::startTimers()
-{
- m_audioBuffer->startFlushTimer();
- if (m_intervalTimer->interval() > 0)
- m_intervalTimer->start();
-}
-
-void CoreAudioInput::stopTimers()
-{
- m_audioBuffer->stopFlushTimer();
- m_intervalTimer->stop();
-}
-
-OSStatus CoreAudioInput::inputCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
-{
- Q_UNUSED(ioData);
-
- CoreAudioInput* d = static_cast<CoreAudioInput*>(inRefCon);
-
- const int threadState = d->m_audioThreadState.loadAcquire();
- if (threadState == Stopped)
- d->audioDeviceStop();
- else {
- qint64 framesWritten;
-
- framesWritten = d->m_audioBuffer->renderFromDevice(d->m_audioUnit,
- ioActionFlags,
- inTimeStamp,
- inBusNumber,
- inNumberFrames);
-
- if (framesWritten > 0) {
- d->m_totalFrames += framesWritten;
- d->audioDeviceActive();
- } else if (framesWritten == 0)
- d->audioDeviceFull();
- else if (framesWritten < 0)
- d->audioDeviceError();
- }
-
- return noErr;
-}
-
-QT_END_NAMESPACE
-
-#include "moc_coreaudioinput.cpp"
diff --git a/src/plugins/coreaudio/coreaudiooutput.h b/src/plugins/coreaudio/coreaudiooutput.h
deleted file mode 100644
index 97b1e0438..000000000
--- a/src/plugins/coreaudio/coreaudiooutput.h
+++ /dev/null
@@ -1,205 +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 IOSAUDIOOUTPUT_H
-#define IOSAUDIOOUTPUT_H
-
-#include <qaudiosystem.h>
-
-#if defined(Q_OS_OSX)
-# include <CoreAudio/CoreAudio.h>
-#endif
-#include <AudioUnit/AudioUnit.h>
-#include <CoreAudio/CoreAudioTypes.h>
-
-#include <QtCore/QIODevice>
-#include <QtCore/QWaitCondition>
-#include <QtCore/QMutex>
-
-QT_BEGIN_NAMESPACE
-
-class CoreAudioOutputBuffer;
-class QTimer;
-class CoreAudioDeviceInfo;
-class CoreAudioRingBuffer;
-
-class CoreAudioOutputBuffer : public QObject
-{
- Q_OBJECT
-
-public:
- CoreAudioOutputBuffer(int bufferSize, int maxPeriodSize, QAudioFormat const& audioFormat);
- ~CoreAudioOutputBuffer();
-
- qint64 readFrames(char *data, qint64 maxFrames);
- qint64 writeBytes(const char *data, qint64 maxSize);
-
- int available() const;
- void reset();
-
- void setPrefetchDevice(QIODevice *device);
-
- void startFillTimer();
- void stopFillTimer();
-
-signals:
- void readyRead();
-
-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;
-};
-
-class CoreAudioOutputDevice : public QIODevice
-{
-public:
- CoreAudioOutputDevice(CoreAudioOutputBuffer *audioBuffer, QObject *parent);
-
- qint64 readData(char *data, qint64 len);
- qint64 writeData(const char *data, qint64 len);
-
- bool isSequential() const { return true; }
-
-private:
- CoreAudioOutputBuffer *m_audioBuffer;
-};
-
-
-class CoreAudioOutput : public QAbstractAudioOutput
-{
- Q_OBJECT
-
-public:
- CoreAudioOutput(const QByteArray &device);
- ~CoreAudioOutput();
-
- void start(QIODevice *device);
- QIODevice *start();
- void stop();
- void reset();
- void suspend();
- void resume();
- int bytesFree() const;
- int periodSize() const;
- void setBufferSize(int value);
- int bufferSize() const;
- void setNotifyInterval(int milliSeconds);
- int notifyInterval() const;
- qint64 processedUSecs() const;
- qint64 elapsedUSecs() const;
- QAudio::Error error() const;
- QAudio::State state() const;
- void setFormat(const QAudioFormat &format);
- QAudioFormat format() const;
-
- void setVolume(qreal volume);
- qreal volume() const;
-
- void setCategory(const QString &category);
- QString category() const;
-
-private slots:
- void deviceStopped();
- void inputReady();
-
-private:
- enum {
- Running,
- Draining,
- Stopped
- };
-
- static OSStatus renderCallback(void *inRefCon,
- AudioUnitRenderActionFlags *ioActionFlags,
- const AudioTimeStamp *inTimeStamp,
- UInt32 inBusNumber,
- UInt32 inNumberFrames,
- AudioBufferList *ioData);
-
- bool open();
- void close();
- void audioThreadStart();
- void audioThreadStop();
- void audioThreadDrain();
- void audioDeviceStop();
- void audioDeviceIdle();
- void audioDeviceError();
-
- void startTimers();
- void stopTimers();
-
- QByteArray m_device;
-
- bool m_isOpen;
- int m_internalBufferSize;
- int m_periodSizeBytes;
- qint64 m_totalFrames;
- QAudioFormat m_audioFormat;
- QIODevice *m_audioIO;
-#if defined(Q_OS_OSX)
- AudioDeviceID m_audioDeviceId;
-#endif
- AudioUnit m_audioUnit;
- Float64 m_clockFrequency;
- UInt64 m_startTime;
- AudioStreamBasicDescription m_streamFormat;
- CoreAudioOutputBuffer *m_audioBuffer;
- QAtomicInt m_audioThreadState;
- QWaitCondition m_threadFinished;
- QMutex m_mutex;
- QTimer *m_intervalTimer;
- CoreAudioDeviceInfo *m_audioDeviceInfo;
- qreal m_cachedVolume;
- qreal m_volume;
- bool m_pullMode;
-
- QAudio::Error m_errorCode;
- QAudio::State m_stateCode;
-};
-
-QT_END_NAMESPACE
-
-#endif // IOSAUDIOOUTPUT_H
diff --git a/src/plugins/coreaudio/coreaudiooutput.mm b/src/plugins/coreaudio/coreaudiooutput.mm
deleted file mode 100644
index 1138de3e2..000000000
--- a/src/plugins/coreaudio/coreaudiooutput.mm
+++ /dev/null
@@ -1,752 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "coreaudiooutput.h"
-#include "coreaudiosessionmanager.h"
-#include "coreaudiodeviceinfo.h"
-#include "coreaudioutils.h"
-
-#include <QtCore/QDataStream>
-#include <QtCore/QTimer>
-#include <QtCore/QDebug>
-
-#include <AudioUnit/AudioUnit.h>
-#include <AudioToolbox/AudioToolbox.h>
-#if defined(Q_OS_OSX)
-# include <AudioUnit/AudioComponent.h>
-#endif
-
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
-# include <QtMultimedia/private/qaudiohelpers_p.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-static const int DEFAULT_BUFFER_SIZE = 8 * 1024;
-
-CoreAudioOutputBuffer::CoreAudioOutputBuffer(int bufferSize, int maxPeriodSize, const QAudioFormat &audioFormat)
- : m_deviceError(false)
- , m_maxPeriodSize(maxPeriodSize)
- , m_device(0)
-{
- m_buffer = new CoreAudioRingBuffer(bufferSize + (bufferSize % maxPeriodSize == 0 ? 0 : maxPeriodSize - (bufferSize % maxPeriodSize)));
- m_bytesPerFrame = (audioFormat.sampleSize() / 8) * audioFormat.channelCount();
- m_periodTime = m_maxPeriodSize / m_bytesPerFrame * 1000 / audioFormat.sampleRate();
-
- m_fillTimer = new QTimer(this);
- connect(m_fillTimer, SIGNAL(timeout()), SLOT(fillBuffer()));
-}
-
-CoreAudioOutputBuffer::~CoreAudioOutputBuffer()
-{
- delete m_buffer;
-}
-
-qint64 CoreAudioOutputBuffer::readFrames(char *data, qint64 maxFrames)
-{
- bool wecan = true;
- qint64 framesRead = 0;
-
- while (wecan && framesRead < maxFrames) {
- CoreAudioRingBuffer::Region region = m_buffer->acquireReadRegion((maxFrames - framesRead) * m_bytesPerFrame);
-
- if (region.second > 0) {
- // Ensure that we only read whole frames.
- region.second -= region.second % m_bytesPerFrame;
-
- if (region.second > 0) {
- memcpy(data + (framesRead * m_bytesPerFrame), region.first, region.second);
- framesRead += region.second / m_bytesPerFrame;
- } else
- wecan = false; // If there is only a partial frame left we should exit.
- }
- else
- wecan = false;
-
- m_buffer->releaseReadRegion(region);
- }
-
- if (framesRead == 0 && m_deviceError)
- framesRead = -1;
-
- return framesRead;
-}
-
-qint64 CoreAudioOutputBuffer::writeBytes(const char *data, qint64 maxSize)
-{
- bool wecan = true;
- qint64 bytesWritten = 0;
-
- maxSize -= maxSize % m_bytesPerFrame;
- while (wecan && bytesWritten < maxSize) {
- CoreAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(maxSize - bytesWritten);
-
- if (region.second > 0) {
- memcpy(region.first, data + bytesWritten, region.second);
- bytesWritten += region.second;
- }
- else
- wecan = false;
-
- m_buffer->releaseWriteRegion(region);
- }
-
- if (bytesWritten > 0)
- emit readyRead();
-
- return bytesWritten;
-}
-
-int CoreAudioOutputBuffer::available() const
-{
- return m_buffer->free();
-}
-
-void CoreAudioOutputBuffer::reset()
-{
- m_buffer->reset();
- m_device = 0;
- m_deviceError = false;
-}
-
-void CoreAudioOutputBuffer::setPrefetchDevice(QIODevice *device)
-{
- if (m_device != device) {
- m_device = device;
- if (m_device != 0)
- fillBuffer();
- }
-}
-
-void CoreAudioOutputBuffer::startFillTimer()
-{
- if (m_device != 0)
- m_fillTimer->start(m_buffer->size() / 2 / m_maxPeriodSize * m_periodTime);
-}
-
-void CoreAudioOutputBuffer::stopFillTimer()
-{
- m_fillTimer->stop();
-}
-
-void CoreAudioOutputBuffer::fillBuffer()
-{
- const int free = m_buffer->free();
- const int writeSize = free - (free % m_maxPeriodSize);
-
- if (writeSize > 0) {
- bool wecan = true;
- int filled = 0;
-
- while (!m_deviceError && wecan && filled < writeSize) {
- CoreAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(writeSize - filled);
-
- if (region.second > 0) {
- region.second = m_device->read(region.first, region.second);
- if (region.second > 0)
- filled += region.second;
- else if (region.second == 0)
- wecan = false;
- else if (region.second < 0) {
- m_fillTimer->stop();
- region.second = 0;
- m_deviceError = true;
- }
- }
- else
- wecan = false;
-
- m_buffer->releaseWriteRegion(region);
- }
-
- if (filled > 0)
- emit readyRead();
- }
-}
-
-CoreAudioOutputDevice::CoreAudioOutputDevice(CoreAudioOutputBuffer *audioBuffer, QObject *parent)
- : QIODevice(parent)
- , m_audioBuffer(audioBuffer)
-{
- open(QIODevice::WriteOnly | QIODevice::Unbuffered);
-}
-
-qint64 CoreAudioOutputDevice::readData(char *data, qint64 len)
-{
- Q_UNUSED(data);
- Q_UNUSED(len);
-
- return 0;
-}
-
-qint64 CoreAudioOutputDevice::writeData(const char *data, qint64 len)
-{
- return m_audioBuffer->writeBytes(data, len);
-}
-
-CoreAudioOutput::CoreAudioOutput(const QByteArray &device)
- : m_isOpen(false)
- , m_internalBufferSize(DEFAULT_BUFFER_SIZE)
- , m_totalFrames(0)
- , m_audioIO(0)
- , m_audioUnit(0)
- , m_startTime(0)
- , m_audioBuffer(0)
- , m_cachedVolume(1.0)
- , m_volume(1.0)
- , m_pullMode(false)
- , m_errorCode(QAudio::NoError)
- , m_stateCode(QAudio::StoppedState)
-{
-#if defined(Q_OS_OSX)
- quint32 deviceID;
- QDataStream dataStream(device);
- dataStream >> deviceID >> m_device;
- m_audioDeviceId = AudioDeviceID(deviceID);
-#else //iOS
- m_device = device;
-#endif
-
- m_clockFrequency = CoreAudioUtils::frequency() / 1000;
- m_audioDeviceInfo = new CoreAudioDeviceInfo(device, QAudio::AudioOutput);
- m_audioThreadState.storeRelaxed(Stopped);
-
- m_intervalTimer = new QTimer(this);
- m_intervalTimer->setInterval(1000);
- connect(m_intervalTimer, SIGNAL(timeout()), this, SIGNAL(notify()));
-}
-
-CoreAudioOutput::~CoreAudioOutput()
-{
- close();
- delete m_audioDeviceInfo;
-}
-
-void CoreAudioOutput::start(QIODevice *device)
-{
- QIODevice* op = device;
-
- if (!m_audioDeviceInfo->isFormatSupported(m_audioFormat) || !open()) {
- m_stateCode = QAudio::StoppedState;
- m_errorCode = QAudio::OpenError;
- return;
- }
-
- reset();
- m_audioBuffer->reset();
- m_audioBuffer->setPrefetchDevice(op);
-
- if (op == 0) {
- op = m_audioIO;
- m_stateCode = QAudio::IdleState;
- }
- else
- m_stateCode = QAudio::ActiveState;
-
- // Start
- m_pullMode = true;
- m_errorCode = QAudio::NoError;
- m_totalFrames = 0;
- m_startTime = CoreAudioUtils::currentTime();
-
- if (m_stateCode == QAudio::ActiveState)
- audioThreadStart();
-
- emit stateChanged(m_stateCode);
-}
-
-QIODevice *CoreAudioOutput::start()
-{
- if (!m_audioDeviceInfo->isFormatSupported(m_audioFormat) || !open()) {
- m_stateCode = QAudio::StoppedState;
- m_errorCode = 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;
- m_startTime = CoreAudioUtils::currentTime();
-
- emit stateChanged(m_stateCode);
-
- return m_audioIO;
-}
-
-void CoreAudioOutput::stop()
-{
- QMutexLocker lock(&m_mutex);
- if (m_stateCode != QAudio::StoppedState) {
- audioThreadDrain();
-
- m_stateCode = QAudio::StoppedState;
- m_errorCode = QAudio::NoError;
- emit stateChanged(m_stateCode);
- }
-}
-
-void CoreAudioOutput::reset()
-{
- QMutexLocker lock(&m_mutex);
- if (m_stateCode != QAudio::StoppedState) {
- audioThreadStop();
-
- m_stateCode = QAudio::StoppedState;
- m_errorCode = QAudio::NoError;
- emit stateChanged(m_stateCode);
- }
-}
-
-void CoreAudioOutput::suspend()
-{
- QMutexLocker lock(&m_mutex);
- if (m_stateCode == QAudio::ActiveState || m_stateCode == QAudio::IdleState) {
- audioThreadStop();
-
- m_stateCode = QAudio::SuspendedState;
- m_errorCode = QAudio::NoError;
- emit stateChanged(m_stateCode);
- }
-}
-
-void CoreAudioOutput::resume()
-{
- QMutexLocker lock(&m_mutex);
- if (m_stateCode == QAudio::SuspendedState) {
- audioThreadStart();
-
- m_stateCode = m_pullMode ? QAudio::ActiveState : QAudio::IdleState;
- m_errorCode = QAudio::NoError;
- emit stateChanged(m_stateCode);
- }
-}
-
-int CoreAudioOutput::bytesFree() const
-{
- return m_audioBuffer->available();
-}
-
-int CoreAudioOutput::periodSize() const
-{
- return m_periodSizeBytes;
-}
-
-void CoreAudioOutput::setBufferSize(int value)
-{
- if (m_stateCode == QAudio::StoppedState)
- m_internalBufferSize = value;
-}
-
-int CoreAudioOutput::bufferSize() const
-{
- return m_internalBufferSize;
-}
-
-void CoreAudioOutput::setNotifyInterval(int milliSeconds)
-{
- if (m_intervalTimer->interval() == milliSeconds)
- return;
-
- if (milliSeconds <= 0)
- milliSeconds = 0;
-
- m_intervalTimer->setInterval(milliSeconds);
-}
-
-int CoreAudioOutput::notifyInterval() const
-{
- return m_intervalTimer->interval();
-}
-
-qint64 CoreAudioOutput::processedUSecs() const
-{
- return m_totalFrames * 1000000 / m_audioFormat.sampleRate();
-}
-
-qint64 CoreAudioOutput::elapsedUSecs() const
-{
- if (m_stateCode == QAudio::StoppedState)
- return 0;
-
- return (CoreAudioUtils::currentTime() - m_startTime) / (m_clockFrequency / 1000);
-}
-
-QAudio::Error CoreAudioOutput::error() const
-{
- return m_errorCode;
-}
-
-QAudio::State CoreAudioOutput::state() const
-{
- return m_stateCode;
-}
-
-void CoreAudioOutput::setFormat(const QAudioFormat &format)
-{
- if (m_stateCode == QAudio::StoppedState)
- m_audioFormat = format;
-}
-
-QAudioFormat CoreAudioOutput::format() const
-{
- return m_audioFormat;
-}
-
-void CoreAudioOutput::setVolume(qreal volume)
-{
- m_cachedVolume = qBound(qreal(0.0), volume, qreal(1.0));
- if (!m_isOpen)
- return;
-
-#if defined(Q_OS_OSX)
- //on OS X the volume can be set directly on the AudioUnit
- if (AudioUnitSetParameter(m_audioUnit,
- kHALOutputParam_Volume,
- kAudioUnitScope_Global,
- 0 /* bus */,
- m_cachedVolume,
- 0) == noErr)
- m_volume = m_cachedVolume;
-#endif
-}
-
-qreal CoreAudioOutput::volume() const
-{
- return m_cachedVolume;
-}
-
-void CoreAudioOutput::setCategory(const QString &category)
-{
- Q_UNUSED(category);
-}
-
-QString CoreAudioOutput::category() const
-{
- return QString();
-}
-
-void CoreAudioOutput::deviceStopped()
-{
- m_intervalTimer->stop();
- emit stateChanged(m_stateCode);
-}
-
-void CoreAudioOutput::inputReady()
-{
- QMutexLocker lock(&m_mutex);
- if (m_stateCode == QAudio::IdleState) {
- audioThreadStart();
-
- m_stateCode = QAudio::ActiveState;
- m_errorCode = QAudio::NoError;
-
- emit stateChanged(m_stateCode);
- }
-}
-
-OSStatus CoreAudioOutput::renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
-{
- Q_UNUSED(ioActionFlags);
- Q_UNUSED(inTimeStamp);
- Q_UNUSED(inBusNumber);
- Q_UNUSED(inNumberFrames);
-
- CoreAudioOutput* d = static_cast<CoreAudioOutput*>(inRefCon);
-
- const int threadState = d->m_audioThreadState.fetchAndAddAcquire(0);
- if (threadState == Stopped) {
- ioData->mBuffers[0].mDataByteSize = 0;
- d->audioDeviceStop();
- }
- else {
- const UInt32 bytesPerFrame = d->m_streamFormat.mBytesPerFrame;
- qint64 framesRead;
-
- framesRead = d->m_audioBuffer->readFrames((char*)ioData->mBuffers[0].mData,
- ioData->mBuffers[0].mDataByteSize / bytesPerFrame);
-
- if (framesRead > 0) {
- ioData->mBuffers[0].mDataByteSize = framesRead * bytesPerFrame;
- d->m_totalFrames += framesRead;
-
-#if defined(Q_OS_MACOS)
- // If playback is already stopped.
- if (threadState != Running) {
- qreal oldVolume = d->m_cachedVolume;
- // Decrease volume smoothly.
- d->setVolume(d->m_volume / 2);
- d->m_cachedVolume = oldVolume;
- }
-#elif defined(Q_OS_IOS) || defined(Q_OS_TVOS)
- // on iOS we have to adjust the sound volume ourselves
- if (!qFuzzyCompare(d->m_cachedVolume, qreal(1.0f))) {
- QAudioHelperInternal::qMultiplySamples(d->m_cachedVolume,
- d->m_audioFormat,
- ioData->mBuffers[0].mData, /* input */
- ioData->mBuffers[0].mData, /* output */
- ioData->mBuffers[0].mDataByteSize);
- }
-#endif
-
- }
- else {
- ioData->mBuffers[0].mDataByteSize = 0;
- if (framesRead == 0) {
- if (threadState == Draining)
- d->audioDeviceStop();
- else
- d->audioDeviceIdle();
- }
- else
- d->audioDeviceError();
- }
- }
-
- return noErr;
-}
-
-bool CoreAudioOutput::open()
-{
-#if defined(Q_OS_IOS)
- // Set default category to Ambient (implies MixWithOthers). This makes sure audio stops playing
- // if the screen is locked or if the Silent switch is toggled.
- CoreAudioSessionManager::instance().setCategory(CoreAudioSessionManager::Ambient, CoreAudioSessionManager::None);
- CoreAudioSessionManager::instance().setActive(true);
-#endif
-
- if (m_errorCode != QAudio::NoError)
- return false;
-
- if (m_isOpen) {
- setVolume(m_cachedVolume);
- return true;
- }
-
- AudioComponentDescription componentDescription;
- componentDescription.componentType = kAudioUnitType_Output;
-#if defined(Q_OS_OSX)
- componentDescription.componentSubType = kAudioUnitSubType_HALOutput;
-#else
- componentDescription.componentSubType = kAudioUnitSubType_RemoteIO;
-#endif
- componentDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
- componentDescription.componentFlags = 0;
- componentDescription.componentFlagsMask = 0;
-
- AudioComponent component = AudioComponentFindNext(0, &componentDescription);
- if (component == 0) {
- qWarning() << "QAudioOutput: Failed to find Output component";
- return false;
- }
-
- if (AudioComponentInstanceNew(component, &m_audioUnit) != noErr) {
- qWarning() << "QAudioOutput: Unable to Open Output Component";
- return false;
- }
-
- // register callback
- AURenderCallbackStruct callback;
- callback.inputProc = renderCallback;
- callback.inputProcRefCon = this;
-
- if (AudioUnitSetProperty(m_audioUnit,
- kAudioUnitProperty_SetRenderCallback,
- kAudioUnitScope_Global,
- 0,
- &callback,
- sizeof(callback)) != noErr) {
- qWarning() << "QAudioOutput: Failed to set AudioUnit callback";
- return false;
- }
-
-#if defined(Q_OS_OSX)
- //Set Audio Device
- if (AudioUnitSetProperty(m_audioUnit,
- kAudioOutputUnitProperty_CurrentDevice,
- kAudioUnitScope_Global,
- 0,
- &m_audioDeviceId,
- sizeof(m_audioDeviceId)) != noErr) {
- qWarning() << "QAudioOutput: Unable to use configured device";
- return false;
- }
-#endif
-
- // Set stream format
- m_streamFormat = CoreAudioUtils::toAudioStreamBasicDescription(m_audioFormat);
-
- UInt32 size = sizeof(m_streamFormat);
- if (AudioUnitSetProperty(m_audioUnit,
- kAudioUnitProperty_StreamFormat,
- kAudioUnitScope_Input,
- 0,
- &m_streamFormat,
- size) != noErr) {
- qWarning() << "QAudioOutput: Unable to Set Stream information";
- return false;
- }
-
- // Allocate buffer
- UInt32 numberOfFrames = 0;
-#if defined(Q_OS_OSX)
- size = sizeof(UInt32);
- if (AudioUnitGetProperty(m_audioUnit,
- kAudioDevicePropertyBufferFrameSize,
- kAudioUnitScope_Global,
- 0,
- &numberOfFrames,
- &size) != noErr) {
- qWarning() << "QAudioInput: Failed to get audio period size";
- return false;
- }
-#else //iOS
- Float32 bufferSize = CoreAudioSessionManager::instance().currentIOBufferDuration();
- bufferSize *= m_streamFormat.mSampleRate;
- numberOfFrames = bufferSize;
-#endif
-
- m_periodSizeBytes = numberOfFrames * m_streamFormat.mBytesPerFrame;
- if (m_internalBufferSize < m_periodSizeBytes * 2)
- m_internalBufferSize = m_periodSizeBytes * 2;
- else
- m_internalBufferSize -= m_internalBufferSize % m_streamFormat.mBytesPerFrame;
-
- m_audioBuffer = new CoreAudioOutputBuffer(m_internalBufferSize, m_periodSizeBytes, m_audioFormat);
- connect(m_audioBuffer, SIGNAL(readyRead()), SLOT(inputReady())); //Pull
-
- m_audioIO = new CoreAudioOutputDevice(m_audioBuffer, this);
-
- //Init
- if (AudioUnitInitialize(m_audioUnit)) {
- qWarning() << "QAudioOutput: Failed to initialize AudioUnit";
- return false;
- }
-
- m_isOpen = true;
-
- setVolume(m_cachedVolume);
-
- return true;
-}
-
-void CoreAudioOutput::close()
-{
- if (m_audioUnit != 0) {
- AudioOutputUnitStop(m_audioUnit);
- AudioUnitUninitialize(m_audioUnit);
- AudioComponentInstanceDispose(m_audioUnit);
- }
-
- delete m_audioBuffer;
-}
-
-void CoreAudioOutput::audioThreadStart()
-{
- startTimers();
- m_audioThreadState.storeRelaxed(Running);
- AudioOutputUnitStart(m_audioUnit);
-}
-
-void CoreAudioOutput::audioThreadStop()
-{
- stopTimers();
- if (m_audioThreadState.testAndSetAcquire(Running, Stopped))
- m_threadFinished.wait(&m_mutex, 500);
-}
-
-void CoreAudioOutput::audioThreadDrain()
-{
- stopTimers();
- if (m_audioThreadState.testAndSetAcquire(Running, Draining))
- m_threadFinished.wait(&m_mutex, 500);
-}
-
-void CoreAudioOutput::audioDeviceStop()
-{
- AudioOutputUnitStop(m_audioUnit);
- m_audioThreadState.storeRelaxed(Stopped);
- m_threadFinished.wakeOne();
-}
-
-void CoreAudioOutput::audioDeviceIdle()
-{
- if (m_stateCode == QAudio::ActiveState) {
- QMutexLocker lock(&m_mutex);
- audioDeviceStop();
-
- m_errorCode = QAudio::UnderrunError;
- m_stateCode = QAudio::IdleState;
- QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
- }
-}
-
-void CoreAudioOutput::audioDeviceError()
-{
- if (m_stateCode == QAudio::ActiveState) {
- QMutexLocker lock(&m_mutex);
- audioDeviceStop();
-
- m_errorCode = QAudio::IOError;
- m_stateCode = QAudio::StoppedState;
- QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
- }
-}
-
-void CoreAudioOutput::startTimers()
-{
- m_audioBuffer->startFillTimer();
- if (m_intervalTimer->interval() > 0)
- m_intervalTimer->start();
-}
-
-void CoreAudioOutput::stopTimers()
-{
- m_audioBuffer->stopFillTimer();
- m_intervalTimer->stop();
-}
-
-QT_END_NAMESPACE
-
-#include "moc_coreaudiooutput.cpp"
diff --git a/src/plugins/coreaudio/coreaudioplugin.h b/src/plugins/coreaudio/coreaudioplugin.h
deleted file mode 100644
index 5407bdeb9..000000000
--- a/src/plugins/coreaudio/coreaudioplugin.h
+++ /dev/null
@@ -1,66 +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 IOSAUDIOPLUGIN_H
-#define IOSAUDIOPLUGIN_H
-
-#include <QtMultimedia/qaudiosystemplugin.h>
-#include <QtMultimedia/private/qaudiosystempluginext_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class CoreAudioPlugin : public QAudioSystemPlugin, public QAudioSystemPluginExtension
-{
- Q_OBJECT
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.audiosystemfactory/5.0" FILE "coreaudio.json")
- Q_INTERFACES(QAudioSystemPluginExtension)
-
-public:
- explicit CoreAudioPlugin(QObject *parent = 0);
- ~CoreAudioPlugin() {}
-
- QByteArray defaultDevice(QAudio::Mode mode) const override;
- QList<QByteArray> availableDevices(QAudio::Mode mode) const override;
- QAbstractAudioInput *createInput(const QByteArray &device) override;
- QAbstractAudioOutput *createOutput(const QByteArray &device) override;
- QAbstractAudioDeviceInfo *createDeviceInfo(const QByteArray &device, QAudio::Mode mode) override;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/coreaudio/coreaudioplugin.mm b/src/plugins/coreaudio/coreaudioplugin.mm
deleted file mode 100644
index ac51b9cd0..000000000
--- a/src/plugins/coreaudio/coreaudioplugin.mm
+++ /dev/null
@@ -1,82 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "coreaudioplugin.h"
-
-#include "coreaudiodeviceinfo.h"
-#include "coreaudioinput.h"
-#include "coreaudiooutput.h"
-
-QT_BEGIN_NAMESPACE
-
-CoreAudioPlugin::CoreAudioPlugin(QObject *parent)
- : QAudioSystemPlugin(parent)
-{
-}
-
-QByteArray CoreAudioPlugin::defaultDevice(QAudio::Mode mode) const
-{
- return CoreAudioDeviceInfo::defaultDevice(mode);
-}
-
-QList<QByteArray> CoreAudioPlugin::availableDevices(QAudio::Mode mode) const
-{
- return CoreAudioDeviceInfo::availableDevices(mode);
-}
-
-
-QAbstractAudioInput *CoreAudioPlugin::createInput(const QByteArray &device)
-{
- return new CoreAudioInput(device);
-}
-
-
-QAbstractAudioOutput *CoreAudioPlugin::createOutput(const QByteArray &device)
-{
- return new CoreAudioOutput(device);
-}
-
-
-QAbstractAudioDeviceInfo *CoreAudioPlugin::createDeviceInfo(const QByteArray &device, QAudio::Mode mode)
-{
- return new CoreAudioDeviceInfo(device, mode);
-}
-
-QT_END_NAMESPACE
-
-#include "moc_coreaudioplugin.cpp"
diff --git a/src/plugins/coreaudio/coreaudiosessionmanager.h b/src/plugins/coreaudio/coreaudiosessionmanager.h
deleted file mode 100644
index 73ef9f303..000000000
--- a/src/plugins/coreaudio/coreaudiosessionmanager.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef IOSAUDIOSESSIONMANAGER_H
-#define IOSAUDIOSESSIONMANAGER_H
-
-#include <QObject>
-#ifdef QT_DEBUG_COREAUDIO
-# include <QtCore/QDebug>
-#endif
-
-@class CoreAudioSessionObserver;
-
-QT_BEGIN_NAMESPACE
-
-class CoreAudioSessionManager : public QObject
-{
- Q_OBJECT
-public:
- enum AudioSessionCategorys {
- Ambient,
- SoloAmbient,
- Playback,
- Record,
- PlayAndRecord,
- AudioProcessing,
- MultiRoute
- };
- enum AudioSessionCategoryOptions {
- None = 0,
- MixWithOthers = 1,
- DuckOthers = 2,
- AllowBluetooth = 4,
- DefaultToSpeaker = 8
- };
- enum AudioSessionModes {
- Default,
- VoiceChat,
- GameChat,
- VideoRecording,
- Measurement,
- MoviePlayback
- };
-
- static CoreAudioSessionManager& instance();
-
- bool setActive(bool active);
- bool setCategory(AudioSessionCategorys category, AudioSessionCategoryOptions options = None);
- bool setMode(AudioSessionModes mode);
-
- AudioSessionCategorys category();
- AudioSessionModes mode();
-
- QList<QByteArray> inputDevices();
- QList<QByteArray> outputDevices();
-
- float currentIOBufferDuration();
- float preferredSampleRate();
-
-signals:
- void activeChanged();
- void categoryChanged();
- void modeChanged();
- void routeChanged();
- void inputDevicesAvailableChanged();
- void outputDevicesAvailableChanged();
-
-private:
- CoreAudioSessionManager();
- ~CoreAudioSessionManager();
- CoreAudioSessionManager(CoreAudioSessionManager const &copy);
- CoreAudioSessionManager& operator =(CoreAudioSessionManager const &copy);
-
- CoreAudioSessionObserver *m_sessionObserver;
-};
-
-#ifdef QT_DEBUG_COREAUDIO
-QDebug operator <<(QDebug dbg, CoreAudioSessionManager::AudioSessionCategorys category);
-QDebug operator <<(QDebug dbg, CoreAudioSessionManager::AudioSessionCategoryOptions option);
-QDebug operator <<(QDebug dbg, CoreAudioSessionManager::AudioSessionModes mode);
-#endif
-
-QT_END_NAMESPACE
-
-Q_DECLARE_METATYPE(CoreAudioSessionManager::AudioSessionCategorys)
-Q_DECLARE_METATYPE(CoreAudioSessionManager::AudioSessionCategoryOptions)
-Q_DECLARE_METATYPE(CoreAudioSessionManager::AudioSessionModes)
-
-#endif // IOSAUDIOSESSIONMANAGER_H
diff --git a/src/plugins/coreaudio/coreaudiosessionmanager.mm b/src/plugins/coreaudio/coreaudiosessionmanager.mm
deleted file mode 100644
index 372c49491..000000000
--- a/src/plugins/coreaudio/coreaudiosessionmanager.mm
+++ /dev/null
@@ -1,472 +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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "coreaudiosessionmanager.h"
-#import <AVFoundation/AVAudioSession.h>
-#import <Foundation/Foundation.h>
-
-QT_BEGIN_NAMESPACE
-
-@interface CoreAudioSessionObserver : NSObject
-{
- CoreAudioSessionManager *m_sessionManager;
- AVAudioSession *m_audioSession;
-}
-
-@property (readonly, getter=sessionManager) CoreAudioSessionManager *m_sessionManager;
-@property (readonly, getter=audioSession) AVAudioSession *m_audioSession;
-
--(CoreAudioSessionObserver *)initWithAudioSessionManager:(CoreAudioSessionManager *)sessionManager;
-
--(BOOL)activateAudio;
--(BOOL)deactivateAudio;
-
-//Notification handlers
--(void)audioSessionInterruption:(NSNotification *)notification;
--(void)audioSessionRouteChange:(NSNotification *)notification;
--(void)audioSessionMediaServicesWereReset:(NSNotification *)notification;
-
-@end //interface CoreAudioSessionObserver
-
-@implementation CoreAudioSessionObserver
-
-@synthesize m_sessionManager, m_audioSession;
-
--(CoreAudioSessionObserver *)initWithAudioSessionManager:(CoreAudioSessionManager *)sessionManager
-{
- if (!(self = [super init]))
- return nil;
-
- self->m_sessionManager = sessionManager;
- self->m_audioSession = [AVAudioSession sharedInstance];
-
- //Set up observers
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(audioSessionInterruption:)
- name:AVAudioSessionInterruptionNotification
- object:self->m_audioSession];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(audioSessionMediaServicesWereReset:)
- name:AVAudioSessionMediaServicesWereResetNotification
- object:self->m_audioSession];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(audioSessionRouteChange:)
- name:AVAudioSessionRouteChangeNotification
- object:self->m_audioSession];
-
- return self;
-}
-
--(void)dealloc
-{
-#ifdef QT_DEBUG_COREAUDIO
- qDebug() << Q_FUNC_INFO;
-#endif
-
- [[NSNotificationCenter defaultCenter] removeObserver:self
- name:AVAudioSessionInterruptionNotification
- object:self->m_audioSession];
- [[NSNotificationCenter defaultCenter] removeObserver:self
- name:AVAudioSessionMediaServicesWereResetNotification
- object:self->m_audioSession];
- [[NSNotificationCenter defaultCenter] removeObserver:self
- name:AVAudioSessionRouteChangeNotification
- object:self->m_audioSession];
-
- [super dealloc];
-}
-
--(BOOL)activateAudio
-{
- NSError *error = nil;
- BOOL success = [self->m_audioSession setActive:YES error:&error];
- if (![self->m_audioSession setActive:YES error:&error]) {
-#ifdef QT_DEBUG_COREAUDIO
- qDebug("audio session activation failed: %s", [[error localizedDescription] UTF8String]);
- } else {
- qDebug("audio session activated");
-#endif
- }
-
- return success;
-}
-
--(BOOL)deactivateAudio
-{
- NSError *error = nil;
- BOOL success = [m_audioSession setActive:NO error:&error];
-#ifdef QT_DEBUG_COREAUDIO
- if (!success) {
- qDebug("%s", [[error localizedDescription] UTF8String]);
- }
-#endif
- return success;
-}
-
--(void)audioSessionInterruption:(NSNotification *)notification
-{
- NSNumber *type = [[notification userInfo] valueForKey:AVAudioSessionInterruptionTypeKey];
- if ([type intValue] == AVAudioSessionInterruptionTypeBegan) {
-#ifdef QT_DEBUG_COREAUDIO
- qDebug("audioSession Interuption begain");
-#endif
- } else if ([type intValue] == AVAudioSessionInterruptionTypeEnded) {
-#ifdef QT_DEBUG_COREAUDIO
- qDebug("audioSession Interuption ended");
-#endif
- NSNumber *option = [[notification userInfo] valueForKey:AVAudioSessionInterruptionOptionKey];
- if ([option intValue] == AVAudioSessionInterruptionOptionShouldResume) {
-#ifdef QT_DEBUG_COREAUDIO
- qDebug("audioSession is active and immediately ready to be used.");
-#endif
- } else {
- [self activateAudio];
- }
- }
-}
-
--(void)audioSessionMediaServicesWereReset:(NSNotification *)notification
-{
- Q_UNUSED(notification);
-#ifdef QT_DEBUG_COREAUDIO
- qDebug("audioSession Media Services were reset");
-#endif
- //Reactivate audio when this occurs
- [self activateAudio];
-}
-
--(void)audioSessionRouteChange:(NSNotification *)notification
-{
- NSNumber *reason = [[notification userInfo] valueForKey:AVAudioSessionRouteChangeReasonKey];
- NSUInteger reasonEnum = [reason intValue];
-
- if (reasonEnum == AVAudioSessionRouteChangeReasonUnknown) {
-#ifdef QT_DEBUG_COREAUDIO
- qDebug("audioSession route changed. reason: unknown");
-#endif
- } else if (reasonEnum == AVAudioSessionRouteChangeReasonNewDeviceAvailable) {
-#ifdef QT_DEBUG_COREAUDIO
- qDebug("audioSession route changed. reason: new device available");
-#endif
- } else if (reasonEnum == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
-#ifdef QT_DEBUG_COREAUDIO
- qDebug("audioSession route changed. reason: old device unavailable");
-#endif
- } else if (reasonEnum == AVAudioSessionRouteChangeReasonCategoryChange) {
-#ifdef QT_DEBUG_COREAUDIO
- qDebug("audioSession route changed. reason: category changed");
-#endif
- } else if (reasonEnum == AVAudioSessionRouteChangeReasonOverride) {
-#ifdef QT_DEBUG_COREAUDIO
- qDebug("audioSession route changed. reason: override");
-#endif
- } else if (reasonEnum == AVAudioSessionRouteChangeReasonWakeFromSleep) {
-#ifdef QT_DEBUG_COREAUDIO
- qDebug("audioSession route changed. reason: woken from sleep");
-#endif
- } else if (reasonEnum == AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory) {
-#ifdef QT_DEBUG_COREAUDIO
- qDebug("audioSession route changed. reason: no suitable route for category");
-#endif
- }
-
-}
-
-@end //implementation CoreAudioSessionObserver
-
-CoreAudioSessionManager::CoreAudioSessionManager() :
- QObject(0)
-{
- m_sessionObserver = [[CoreAudioSessionObserver alloc] initWithAudioSessionManager:this];
-}
-
-CoreAudioSessionManager::~CoreAudioSessionManager()
-{
-#ifdef QT_DEBUG_COREAUDIO
- qDebug() << Q_FUNC_INFO;
-#endif
- [m_sessionObserver release];
-}
-
-
-CoreAudioSessionManager &CoreAudioSessionManager::instance()
-{
- static CoreAudioSessionManager instance;
- return instance;
-}
-
-bool CoreAudioSessionManager::setActive(bool active)
-{
- if (active) {
- return [m_sessionObserver activateAudio];
- } else {
- return [m_sessionObserver deactivateAudio];
- }
-}
-
-bool CoreAudioSessionManager::setCategory(CoreAudioSessionManager::AudioSessionCategorys category, CoreAudioSessionManager::AudioSessionCategoryOptions options)
-{
- NSString *targetCategory = nil;
-
- switch (category) {
- case CoreAudioSessionManager::Ambient:
- targetCategory = AVAudioSessionCategoryAmbient;
- break;
- case CoreAudioSessionManager::SoloAmbient:
- targetCategory = AVAudioSessionCategorySoloAmbient;
- break;
- case CoreAudioSessionManager::Playback:
- targetCategory = AVAudioSessionCategoryPlayback;
- break;
- case CoreAudioSessionManager::Record:
- targetCategory = AVAudioSessionCategoryRecord;
- break;
- case CoreAudioSessionManager::PlayAndRecord:
- targetCategory = AVAudioSessionCategoryPlayAndRecord;
- break;
- case CoreAudioSessionManager::AudioProcessing:
-#ifndef Q_OS_TVOS
- targetCategory = AVAudioSessionCategoryAudioProcessing;
-#endif
- break;
- case CoreAudioSessionManager::MultiRoute:
- targetCategory = AVAudioSessionCategoryMultiRoute;
- break;
- }
-
- if (targetCategory == nil)
- return false;
-
- return [[m_sessionObserver audioSession] setCategory:targetCategory
- withOptions:(AVAudioSessionCategoryOptions)options
- error:nil];
-}
-
-bool CoreAudioSessionManager::setMode(CoreAudioSessionManager::AudioSessionModes mode)
-{
- NSString *targetMode = nil;
- switch (mode) {
- case CoreAudioSessionManager::Default:
- targetMode = AVAudioSessionModeDefault;
- break;
- case CoreAudioSessionManager::VoiceChat:
- targetMode = AVAudioSessionModeVoiceChat;
- break;
- case CoreAudioSessionManager::GameChat:
- targetMode = AVAudioSessionModeGameChat;
- break;
- case CoreAudioSessionManager::VideoRecording:
- targetMode = AVAudioSessionModeVideoRecording;
- break;
- case CoreAudioSessionManager::Measurement:
- targetMode = AVAudioSessionModeMeasurement;
- break;
- case CoreAudioSessionManager::MoviePlayback:
- targetMode = AVAudioSessionModeMoviePlayback;
- break;
- }
-
- if (targetMode == nil)
- return false;
-
- return [[m_sessionObserver audioSession] setMode:targetMode error:nil];
-
-}
-
-CoreAudioSessionManager::AudioSessionCategorys CoreAudioSessionManager::category()
-{
- NSString *category = [[m_sessionObserver audioSession] category];
- AudioSessionCategorys localCategory = Ambient;
-
- if (category == AVAudioSessionCategoryAmbient) {
- localCategory = Ambient;
- } else if (category == AVAudioSessionCategorySoloAmbient) {
- localCategory = SoloAmbient;
- } else if (category == AVAudioSessionCategoryPlayback) {
- localCategory = Playback;
- } else if (category == AVAudioSessionCategoryRecord) {
- localCategory = Record;
- } else if (category == AVAudioSessionCategoryPlayAndRecord) {
- localCategory = PlayAndRecord;
-#ifndef Q_OS_TVOS
- } else if (category == AVAudioSessionCategoryAudioProcessing) {
- localCategory = AudioProcessing;
-#endif
- } else if (category == AVAudioSessionCategoryMultiRoute) {
- localCategory = MultiRoute;
- }
-
- return localCategory;
-}
-
-CoreAudioSessionManager::AudioSessionModes CoreAudioSessionManager::mode()
-{
- NSString *mode = [[m_sessionObserver audioSession] mode];
- AudioSessionModes localMode = Default;
-
- if (mode == AVAudioSessionModeDefault) {
- localMode = Default;
- } else if (mode == AVAudioSessionModeVoiceChat) {
- localMode = VoiceChat;
- } else if (mode == AVAudioSessionModeGameChat) {
- localMode = GameChat;
- } else if (mode == AVAudioSessionModeVideoRecording) {
- localMode = VideoRecording;
- } else if (mode == AVAudioSessionModeMeasurement) {
- localMode = Measurement;
- } else if (mode == AVAudioSessionModeMoviePlayback) {
- localMode = MoviePlayback;
- }
-
- return localMode;
-}
-
-QList<QByteArray> CoreAudioSessionManager::inputDevices()
-{
- //TODO: Add support for USB input devices
- //Right now the default behavior on iOS is to have only one input route
- //at a time.
- QList<QByteArray> inputDevices;
- inputDevices << "default";
- return inputDevices;
-}
-
-QList<QByteArray> CoreAudioSessionManager::outputDevices()
-{
- //TODO: Add support for USB output devices
- //Right now the default behavior on iOS is to have only one output route
- //at a time.
- QList<QByteArray> outputDevices;
- outputDevices << "default";
- return outputDevices;
-}
-
-float CoreAudioSessionManager::currentIOBufferDuration()
-{
- return [[m_sessionObserver audioSession] IOBufferDuration];
-}
-
-float CoreAudioSessionManager::preferredSampleRate()
-{
- return [[m_sessionObserver audioSession] preferredSampleRate];
-}
-
-#ifdef QT_DEBUG_COREAUDIO
-QDebug operator<<(QDebug dbg, CoreAudioSessionManager::AudioSessionCategorys category)
-{
- QDebug output = dbg.nospace();
- switch (category) {
- case CoreAudioSessionManager::Ambient:
- output << "AudioSessionCategoryAmbient";
- break;
- case CoreAudioSessionManager::SoloAmbient:
- output << "AudioSessionCategorySoloAmbient";
- break;
- case CoreAudioSessionManager::Playback:
- output << "AudioSessionCategoryPlayback";
- break;
- case CoreAudioSessionManager::Record:
- output << "AudioSessionCategoryRecord";
- break;
- case CoreAudioSessionManager::PlayAndRecord:
- output << "AudioSessionCategoryPlayAndRecord";
- break;
- case CoreAudioSessionManager::AudioProcessing:
- output << "AudioSessionCategoryAudioProcessing";
- break;
- case CoreAudioSessionManager::MultiRoute:
- output << "AudioSessionCategoryMultiRoute";
- break;
- }
- return output;
-}
-
-QDebug operator<<(QDebug dbg, CoreAudioSessionManager::AudioSessionCategoryOptions option)
-{
- QDebug output = dbg.nospace();
- switch (option) {
- case CoreAudioSessionManager::None:
- output << "AudioSessionCategoryOptionNone";
- break;
- case CoreAudioSessionManager::MixWithOthers:
- output << "AudioSessionCategoryOptionMixWithOthers";
- break;
- case CoreAudioSessionManager::DuckOthers:
- output << "AudioSessionCategoryOptionDuckOthers";
- break;
- case CoreAudioSessionManager::AllowBluetooth:
- output << "AudioSessionCategoryOptionAllowBluetooth";
- break;
- case CoreAudioSessionManager::DefaultToSpeaker:
- output << "AudioSessionCategoryOptionDefaultToSpeaker";
- break;
- }
- return output;
-}
-
-QDebug operator<<(QDebug dbg, CoreAudioSessionManager::AudioSessionModes mode)
-{
- QDebug output = dbg.nospace();
- switch (mode) {
- case CoreAudioSessionManager::Default:
- output << "AudioSessionModeDefault";
- break;
- case CoreAudioSessionManager::VoiceChat:
- output << "AudioSessionModeVoiceChat";
- break;
- case CoreAudioSessionManager::GameChat:
- output << "AudioSessionModeGameChat";
- break;
- case CoreAudioSessionManager::VideoRecording:
- output << "AudioSessionModeVideoRecording";
- break;
- case CoreAudioSessionManager::Measurement:
- output << "AudioSessionModeMeasurement";
- break;
- case CoreAudioSessionManager::MoviePlayback:
- output << "AudioSessionModeMoviePlayback";
- break;
- }
- return output;
-}
-#endif
-
-QT_END_NAMESPACE
-
-#include "moc_coreaudiosessionmanager.cpp"
diff --git a/src/plugins/coreaudio/coreaudioutils.h b/src/plugins/coreaudio/coreaudioutils.h
deleted file mode 100644
index 7b67f9e41..000000000
--- a/src/plugins/coreaudio/coreaudioutils.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef IOSAUDIOUTILS_H
-#define IOSAUDIOUTILS_H
-
-#include <CoreAudio/CoreAudioTypes.h>
-
-#include <QtMultimedia/QAudioFormat>
-#include <QtCore/qglobal.h>
-
-QT_BEGIN_NAMESPACE
-
-class CoreAudioUtils
-{
-public:
- static quint64 currentTime();
- static double frequency();
- static QAudioFormat toQAudioFormat(const AudioStreamBasicDescription& streamFormat);
- static AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const& audioFormat);
-
-private:
- static void initialize();
- static double sFrequency;
- static bool sIsInitialized;
-};
-
-class CoreAudioRingBuffer
-{
-public:
- typedef QPair<char*, int> Region;
-
- CoreAudioRingBuffer(int bufferSize);
- ~CoreAudioRingBuffer();
-
- Region acquireReadRegion(int size);
- void releaseReadRegion(Region const& region);
- Region acquireWriteRegion(int size);
- void releaseWriteRegion(Region const& region);
-
- int used() const;
- int free() const;
- int size() const;
-
- void reset();
-
-private:
- int m_bufferSize;
- int m_readPos;
- int m_writePos;
- char* m_buffer;
- QAtomicInt m_bufferUsed;
-};
-
-QT_END_NAMESPACE
-
-#endif // IOSAUDIOUTILS_H
diff --git a/src/plugins/coreaudio/coreaudioutils.mm b/src/plugins/coreaudio/coreaudioutils.mm
deleted file mode 100644
index 1f9b9866c..000000000
--- a/src/plugins/coreaudio/coreaudioutils.mm
+++ /dev/null
@@ -1,198 +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 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$
-**
-****************************************************************************/
-
-#include "coreaudioutils.h"
-#include <mach/mach_time.h>
-
-QT_BEGIN_NAMESPACE
-
-double CoreAudioUtils::sFrequency = 0.0;
-bool CoreAudioUtils::sIsInitialized = false;
-
-void CoreAudioUtils::initialize()
-{
- struct mach_timebase_info timeBaseInfo;
- mach_timebase_info(&timeBaseInfo);
- sFrequency = static_cast<double>(timeBaseInfo.denom) / static_cast<double>(timeBaseInfo.numer);
- sFrequency *= 1000000000.0;
-
- sIsInitialized = true;
-}
-
-
-quint64 CoreAudioUtils::currentTime()
-{
- return mach_absolute_time();
-}
-
-double CoreAudioUtils::frequency()
-{
- if (!sIsInitialized)
- initialize();
- return sFrequency;
-}
-
-QAudioFormat CoreAudioUtils::toQAudioFormat(AudioStreamBasicDescription const& sf)
-{
- QAudioFormat audioFormat;
-
- audioFormat.setSampleRate(sf.mSampleRate);
- audioFormat.setChannelCount(sf.mChannelsPerFrame);
- audioFormat.setSampleSize(sf.mBitsPerChannel);
- audioFormat.setCodec(QString::fromLatin1("audio/pcm"));
- audioFormat.setByteOrder((sf.mFormatFlags & kAudioFormatFlagIsBigEndian) != 0 ? QAudioFormat::BigEndian : QAudioFormat::LittleEndian);
- QAudioFormat::SampleType type = QAudioFormat::UnSignedInt;
- if ((sf.mFormatFlags & kAudioFormatFlagIsSignedInteger) != 0)
- type = QAudioFormat::SignedInt;
- else if ((sf.mFormatFlags & kAudioFormatFlagIsFloat) != 0)
- type = QAudioFormat::Float;
- audioFormat.setSampleType(type);
-
- return audioFormat;
-}
-
-AudioStreamBasicDescription CoreAudioUtils::toAudioStreamBasicDescription(QAudioFormat const& audioFormat)
-{
- AudioStreamBasicDescription sf;
-
- sf.mFormatFlags = kAudioFormatFlagIsPacked;
- sf.mSampleRate = audioFormat.sampleRate();
- sf.mFramesPerPacket = 1;
- sf.mChannelsPerFrame = audioFormat.channelCount();
- sf.mBitsPerChannel = audioFormat.sampleSize();
- sf.mBytesPerFrame = sf.mChannelsPerFrame * (sf.mBitsPerChannel / 8);
- sf.mBytesPerPacket = sf.mFramesPerPacket * sf.mBytesPerFrame;
- sf.mFormatID = kAudioFormatLinearPCM;
-
- switch (audioFormat.sampleType()) {
- case QAudioFormat::SignedInt: sf.mFormatFlags |= kAudioFormatFlagIsSignedInteger; break;
- case QAudioFormat::UnSignedInt: /* default */ break;
- case QAudioFormat::Float: sf.mFormatFlags |= kAudioFormatFlagIsFloat; break;
- case QAudioFormat::Unknown: default: break;
- }
-
- if (audioFormat.byteOrder() == QAudioFormat::BigEndian)
- sf.mFormatFlags |= kAudioFormatFlagIsBigEndian;
-
- return sf;
-}
-
-// QAudioRingBuffer
-CoreAudioRingBuffer::CoreAudioRingBuffer(int bufferSize):
- m_bufferSize(bufferSize)
-{
- m_buffer = new char[m_bufferSize];
- reset();
-}
-
-CoreAudioRingBuffer::~CoreAudioRingBuffer()
-{
- delete[] m_buffer;
-}
-
-CoreAudioRingBuffer::Region CoreAudioRingBuffer::acquireReadRegion(int size)
-{
- const int used = m_bufferUsed.fetchAndAddAcquire(0);
-
- if (used > 0) {
- const int readSize = qMin(size, qMin(m_bufferSize - m_readPos, used));
-
- return readSize > 0 ? Region(m_buffer + m_readPos, readSize) : Region(0, 0);
- }
-
- return Region(0, 0);
-}
-
-void CoreAudioRingBuffer::releaseReadRegion(const CoreAudioRingBuffer::Region &region)
-{
- m_readPos = (m_readPos + region.second) % m_bufferSize;
-
- m_bufferUsed.fetchAndAddRelease(-region.second);
-}
-
-CoreAudioRingBuffer::Region CoreAudioRingBuffer::acquireWriteRegion(int size)
-{
- const int free = m_bufferSize - m_bufferUsed.fetchAndAddAcquire(0);
-
- Region output;
-
- if (free > 0) {
- const int writeSize = qMin(size, qMin(m_bufferSize - m_writePos, free));
- output = writeSize > 0 ? Region(m_buffer + m_writePos, writeSize) : Region(0, 0);
- } else {
- output = Region(0, 0);
- }
-#ifdef QT_DEBUG_COREAUDIO
- qDebug("acquireWriteRegion(%d) free: %d returning Region(%p, %d)", size, free, output.first, output.second);
-#endif
- return output;
-}
-void CoreAudioRingBuffer::releaseWriteRegion(const CoreAudioRingBuffer::Region &region)
-{
- m_writePos = (m_writePos + region.second) % m_bufferSize;
-
- m_bufferUsed.fetchAndAddRelease(region.second);
-#ifdef QT_DEBUG_COREAUDIO
- qDebug("releaseWriteRegion(%p,%d): m_writePos:%d", region.first, region.second, m_writePos);
-#endif
-}
-
-int CoreAudioRingBuffer::used() const
-{
- return m_bufferUsed.loadRelaxed();
-}
-
-int CoreAudioRingBuffer::free() const
-{
- return m_bufferSize - m_bufferUsed.loadRelaxed();
-}
-
-int CoreAudioRingBuffer::size() const
-{
- return m_bufferSize;
-}
-
-void CoreAudioRingBuffer::reset()
-{
- m_readPos = 0;
- m_writePos = 0;
- m_bufferUsed.storeRelaxed(0);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/camera.pri b/src/plugins/directshow/camera/camera.pri
deleted file mode 100644
index 3be1acc49..000000000
--- a/src/plugins/directshow/camera/camera.pri
+++ /dev/null
@@ -1,37 +0,0 @@
-INCLUDEPATH += $$PWD
-
-win32: DEFINES += _CRT_SECURE_NO_WARNINGS
-
-HEADERS += \
- $$PWD/dscameraservice.h \
- $$PWD/dscameracontrol.h \
- $$PWD/dsvideorenderer.h \
- $$PWD/dsvideodevicecontrol.h \
- $$PWD/dsimagecapturecontrol.h \
- $$PWD/dscamerasession.h \
- $$PWD/directshowcameraglobal.h \
- $$PWD/dscameraviewfindersettingscontrol.h \
- $$PWD/dscameraimageprocessingcontrol.h \
- $$PWD/directshowcameraexposurecontrol.h \
- $$PWD/directshowcameracapturedestinationcontrol.h \
- $$PWD/directshowcameracapturebufferformatcontrol.h \
- $$PWD/directshowcamerazoomcontrol.h \
- $$PWD/directshowcameraimageencodercontrol.h
-
-SOURCES += \
- $$PWD/dscameraservice.cpp \
- $$PWD/dscameracontrol.cpp \
- $$PWD/dsvideorenderer.cpp \
- $$PWD/dsvideodevicecontrol.cpp \
- $$PWD/dsimagecapturecontrol.cpp \
- $$PWD/dscamerasession.cpp \
- $$PWD/dscameraviewfindersettingscontrol.cpp \
- $$PWD/dscameraimageprocessingcontrol.cpp \
- $$PWD/directshowcameraexposurecontrol.cpp \
- $$PWD/directshowcameracapturedestinationcontrol.cpp \
- $$PWD/directshowcameracapturebufferformatcontrol.cpp \
- $$PWD/directshowcamerazoomcontrol.cpp \
- $$PWD/directshowcameraimageencodercontrol.cpp
-
-*-msvc*:INCLUDEPATH += $$(DXSDK_DIR)/include
-QMAKE_USE += directshow
diff --git a/src/plugins/directshow/camera/directshowcameracapturebufferformatcontrol.cpp b/src/plugins/directshow/camera/directshowcameracapturebufferformatcontrol.cpp
deleted file mode 100644
index cc0a0ad17..000000000
--- a/src/plugins/directshow/camera/directshowcameracapturebufferformatcontrol.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "directshowcameracapturebufferformatcontrol.h"
-
-#include "dscamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-DirectShowCameraCaptureBufferFormatControl::DirectShowCameraCaptureBufferFormatControl()
-{
-}
-
-QList<QVideoFrame::PixelFormat> DirectShowCameraCaptureBufferFormatControl::supportedBufferFormats() const
-{
- return QList<QVideoFrame::PixelFormat>() << QVideoFrame::Format_RGB32;
-}
-
-QVideoFrame::PixelFormat DirectShowCameraCaptureBufferFormatControl::bufferFormat() const
-{
- return QVideoFrame::Format_RGB32;
-}
-
-void DirectShowCameraCaptureBufferFormatControl::setBufferFormat(QVideoFrame::PixelFormat format)
-{
- Q_UNUSED(format);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/directshowcameracapturebufferformatcontrol.h b/src/plugins/directshow/camera/directshowcameracapturebufferformatcontrol.h
deleted file mode 100644
index cacd3652b..000000000
--- a/src/plugins/directshow/camera/directshowcameracapturebufferformatcontrol.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 DIRECTSHOWCAMERACAPTUREBUFFERFORMATCONTROL_H
-#define DIRECTSHOWCAMERACAPTUREBUFFERFORMATCONTROL_H
-
-#include <QtMultimedia/qcameracapturebufferformatcontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowCameraCaptureBufferFormatControl : public QCameraCaptureBufferFormatControl
-{
- Q_OBJECT
-public:
- DirectShowCameraCaptureBufferFormatControl();
-
- QList<QVideoFrame::PixelFormat> supportedBufferFormats() const override;
- QVideoFrame::PixelFormat bufferFormat() const override;
- void setBufferFormat(QVideoFrame::PixelFormat format) override;
-};
-
-QT_END_NAMESPACE
-
-#endif // DIRECTSHOWCAMERACAPTUREBUFFERFORMATCONTROL_H
diff --git a/src/plugins/directshow/camera/directshowcameracapturedestinationcontrol.cpp b/src/plugins/directshow/camera/directshowcameracapturedestinationcontrol.cpp
deleted file mode 100644
index bfb10fc03..000000000
--- a/src/plugins/directshow/camera/directshowcameracapturedestinationcontrol.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "directshowcameracapturedestinationcontrol.h"
-
-#include "dscamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-DirectShowCameraCaptureDestinationControl::DirectShowCameraCaptureDestinationControl(DSCameraSession *session)
- : m_session(session)
-{
- connect(m_session, &DSCameraSession::captureDestinationChanged,
- this, &DirectShowCameraCaptureDestinationControl::captureDestinationChanged);
-}
-
-bool DirectShowCameraCaptureDestinationControl::isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const
-{
- return m_session->isCaptureDestinationSupported(destination);
-}
-
-QCameraImageCapture::CaptureDestinations DirectShowCameraCaptureDestinationControl::captureDestination() const
-{
- return m_session->captureDestination();
-}
-
-void DirectShowCameraCaptureDestinationControl::setCaptureDestination(QCameraImageCapture::CaptureDestinations destination)
-{
- m_session->setCaptureDestination(destination);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/directshowcameracapturedestinationcontrol.h b/src/plugins/directshow/camera/directshowcameracapturedestinationcontrol.h
deleted file mode 100644
index 224df9dbc..000000000
--- a/src/plugins/directshow/camera/directshowcameracapturedestinationcontrol.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 DIRECTSHOWCAMERACAPTUREDESTINATIONCONTROL_H
-#define DIRECTSHOWCAMERACAPTUREDESTINATIONCONTROL_H
-
-#include <QtMultimedia/qcameracapturedestinationcontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class DSCameraSession;
-
-class DirectShowCameraCaptureDestinationControl : public QCameraCaptureDestinationControl
-{
- Q_OBJECT
-public:
- DirectShowCameraCaptureDestinationControl(DSCameraSession *session);
-
- bool isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const override;
- QCameraImageCapture::CaptureDestinations captureDestination() const override;
- void setCaptureDestination(QCameraImageCapture::CaptureDestinations destination) override;
-
-private:
- DSCameraSession *m_session;
-
-};
-
-QT_END_NAMESPACE
-
-#endif // DIRECTSHOWCAMERACAPTUREDESTINATIONCONTROL_H
diff --git a/src/plugins/directshow/camera/directshowcameraexposurecontrol.cpp b/src/plugins/directshow/camera/directshowcameraexposurecontrol.cpp
deleted file mode 100644
index 6f138450c..000000000
--- a/src/plugins/directshow/camera/directshowcameraexposurecontrol.cpp
+++ /dev/null
@@ -1,413 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "directshowcameraexposurecontrol.h"
-#include "dscamerasession.h"
-#include "directshowglobal.h"
-#include "directshowutils.h"
-
-#include <functional>
-#include <cmath>
-
-QT_BEGIN_NAMESPACE
-
-static qreal convertToSec(long v) { return (v < 0) ? (1 / std::pow(2., qreal(v))) : std::pow(2., qreal(v)); }
-static Q_DECL_CONSTEXPR qreal convertToFvalue(long v) { return qreal(v) / 10.; }
-
-DirectShowCameraExposureControl::DirectShowCameraExposureControl(DSCameraSession *session)
- : m_session(session)
- , m_shutterSpeedValues({ 0, 0, 0, 0, 0 })
- , m_apertureValues({ 0, 0, 0, 0, 0 })
- , m_requestedShutterSpeed(qreal(0.0))
- , m_currentShutterSpeed(qreal(-1.0))
- , m_requestedAperture(qreal(0.0))
- , m_currentAperture(qreal(-1.0))
- , m_requestedExposureMode(QCameraExposure::ExposureAuto)
- , m_currentExposureMode(QCameraExposure::ExposureAuto)
-{
- Q_ASSERT(m_session);
- connect(m_session, &DSCameraSession::statusChanged,
- this, &DirectShowCameraExposureControl::onStatusChanged);
-}
-
-bool DirectShowCameraExposureControl::isParameterSupported(QCameraExposureControl::ExposureParameter parameter) const
-{
- switch (parameter) {
- case QCameraExposureControl::Aperture:
- return (m_apertureValues.caps & CameraControl_Flags_Manual);
- case QCameraExposureControl::ShutterSpeed:
- return (m_shutterSpeedValues.caps & CameraControl_Flags_Manual);
- case QCameraExposureControl::ExposureMode:
- return true;
- default:
- break;
- }
- return false;
-}
-
-QVariantList DirectShowCameraExposureControl::supportedParameterRange(QCameraExposureControl::ExposureParameter parameter,
- bool *continuous) const
-{
- if (continuous)
- *continuous = false;
-
- if (parameter == ShutterSpeed)
- return m_supportedShutterSpeeds;
-
- if (parameter == Aperture)
- return m_supportedApertureValues;
-
- if (parameter == ExposureMode)
- return m_supportedExposureModes;
-
- return QVariantList();
-}
-
-QVariant DirectShowCameraExposureControl::requestedValue(QCameraExposureControl::ExposureParameter parameter) const
-{
- if (parameter == ShutterSpeed)
- return QVariant::fromValue(m_requestedShutterSpeed);
-
- if (parameter == Aperture)
- return QVariant::fromValue(m_requestedAperture);
-
- if (parameter == ExposureMode)
- return QVariant::fromValue(m_requestedExposureMode);
-
- return QVariant();
-}
-
-QVariant DirectShowCameraExposureControl::actualValue(QCameraExposureControl::ExposureParameter parameter) const
-{
- if (parameter == ExposureMode)
- return QVariant::fromValue(m_currentExposureMode);
-
- if (parameter == ShutterSpeed) {
- return qFuzzyCompare(m_currentShutterSpeed, qreal(-1.0))
- ? QVariant()
- : QVariant::fromValue(m_currentShutterSpeed);
- }
-
- if (parameter == Aperture) {
- return qFuzzyCompare(m_currentAperture, qreal(-1.0))
- ? QVariant()
- : QVariant::fromValue(m_currentAperture);
- }
-
- return QVariant();
-}
-
-bool DirectShowCameraExposureControl::setValue(QCameraExposureControl::ExposureParameter parameter,
- const QVariant &value)
-{
- IAMCameraControl *cameraControl = nullptr;
- const DirectShowUtils::ScopedSafeRelease<IAMCameraControl> rControl { &cameraControl };
- if (!m_session->getCameraControlInterface(&cameraControl))
- return false;
-
- // Reset exposure mode if the value is invalid.
- if (!value.isValid()) {
- m_requestedExposureMode = QCameraExposure::ExposureAuto;
- return setExposureMode(cameraControl, m_requestedExposureMode);
- }
-
- if (parameter == ShutterSpeed || parameter == Aperture) {
- bool ok = false;
- const qreal newValue = value.toReal(&ok);
- if (!ok)
- return false;
-
- // Change the exposure mode first
- setExposureMode(cameraControl, QCameraExposure::ExposureManual);
-
- if (parameter == ShutterSpeed) {
- m_requestedShutterSpeed = newValue;
- return setShutterSpeed(cameraControl, m_requestedShutterSpeed);
- }
- m_requestedAperture = newValue;
- return setAperture(cameraControl, m_requestedAperture);
- }
-
- if (parameter == ExposureMode) {
- m_requestedExposureMode = value.value<QCameraExposure::ExposureMode>();
- return setExposureMode(cameraControl, m_requestedExposureMode);
- }
-
- return false;
-}
-
-void DirectShowCameraExposureControl::onStatusChanged(QCamera::Status status)
-{
- const bool shouldUpdate = (qFuzzyCompare(m_currentAperture, qreal(-1.0)) && qFuzzyCompare(m_currentShutterSpeed, qreal(-1.0)));
-
- if (status == QCamera::LoadedStatus && shouldUpdate)
- updateExposureSettings();
-
- if (status == QCamera::UnloadedStatus) {
- m_supportedApertureValues.clear();
- m_supportedExposureModes.clear();
- m_supportedShutterSpeeds.clear();
- m_currentAperture = qreal(-1.0);
- m_currentShutterSpeed = qreal(-1.0);
- m_currentExposureMode = QCameraExposure::ExposureAuto;
- }
-}
-
-void DirectShowCameraExposureControl::updateExposureSettings()
-{
- IAMCameraControl *cameraControl = nullptr;
- const DirectShowUtils::ScopedSafeRelease<IAMCameraControl> rControl { &cameraControl };
- if (!m_session->getCameraControlInterface(&cameraControl))
- return;
-
- const auto updateValues = [cameraControl](long property,
- ExposureValues &currentValues,
- QVariantList &parameterRange,
- const std::function<qreal(long)> &converter,
- bool *changed) -> bool {
- ExposureValues values { 0, 0, 0, 0, 0 };
- if (FAILED(cameraControl->GetRange(property,
- &values.minValue,
- &values.maxValue,
- &values.stepping,
- &values.defaultValue,
- &values.caps))) {
- return false;
- }
-
- const bool minValueChanged = values.minValue != currentValues.minValue;
- const bool maxValueChanged = values.maxValue != currentValues.maxValue;
- const bool steppingChanged = values.stepping != currentValues.stepping;
-
- if (minValueChanged || maxValueChanged || steppingChanged) {
- parameterRange.clear();
- long nextValue = values.minValue;
- while (nextValue != values.maxValue && values.stepping != 0) {
- parameterRange << converter(nextValue);
- nextValue += values.stepping;
- }
-
- if (changed)
- *changed = true;
- }
-
- currentValues = values;
- return true;
- };
-
- const auto getCurrentValue = [cameraControl](long property, const std::function<qreal(long)> &converter, qreal *value) -> bool {
- long currentValue;
- long currentFlags;
- if (FAILED(cameraControl->Get(property, &currentValue, &currentFlags)))
- return false;
-
- *value = converter(currentValue);
- return true;
- };
-
- // Shutter speed
- bool changed = false;
- if (!updateValues(CameraControl_Exposure, m_shutterSpeedValues, m_supportedShutterSpeeds, convertToSec, &changed))
- qCDebug(qtDirectShowPlugin, "Unable to update the shutter speed values");
-
- if (changed)
- Q_EMIT parameterRangeChanged(int(ShutterSpeed));
-
- if ((m_shutterSpeedValues.caps & CameraControl_Flags_Manual)) {
- if (getCurrentValue(CameraControl_Exposure, convertToSec, &m_currentShutterSpeed)) {
- if (m_currentExposureMode == QCameraExposure::ExposureManual)
- setShutterSpeed(cameraControl, m_requestedShutterSpeed);
- } else {
- m_currentShutterSpeed = qreal(-1.0);
- qCDebug(qtDirectShowPlugin, "Unable to get the current shutter speed!");
- }
- }
-
- // Aperture
- changed = false;
- if (!updateValues(CameraControl_Iris, m_apertureValues, m_supportedApertureValues, convertToFvalue, &changed))
- qCDebug(qtDirectShowPlugin, "Unable to update the aperture values");
-
- if (changed)
- Q_EMIT parameterRangeChanged(int(Aperture));
-
- if (getCurrentValue(CameraControl_Iris, convertToFvalue, &m_currentAperture)) {
- if (m_currentExposureMode == QCameraExposure::ExposureManual)
- setAperture(cameraControl, m_requestedAperture);
- } else {
- m_currentAperture = qreal(-1.0);
- qCDebug(qtDirectShowPlugin, "Unable to get the current aperture value!");
- }
-
- // Update exposure modes
- const bool hasAutoExposure = (m_apertureValues.caps & CameraControl_Flags_Auto)
- || (m_shutterSpeedValues.caps & CameraControl_Flags_Auto);
- const bool hasManualExposure = (m_apertureValues.caps & CameraControl_Flags_Manual)
- || (m_shutterSpeedValues.caps & CameraControl_Flags_Manual);
-
- QVariantList exposureModes;
- if (hasAutoExposure && !m_supportedExposureModes.contains(QVariant::fromValue(QCameraExposure::ExposureAuto)))
- exposureModes << QVariant::fromValue(QCameraExposure::ExposureAuto);
-
- if (hasManualExposure && !m_supportedExposureModes.contains(QVariant::fromValue(QCameraExposure::ExposureManual)))
- exposureModes << QVariant::fromValue(QCameraExposure::ExposureManual);
-
- if (!exposureModes.isEmpty() || !m_supportedExposureModes.isEmpty()) {
- m_supportedExposureModes = exposureModes;
- Q_EMIT parameterRangeChanged(int(ExposureMode));
- }
-}
-
-bool DirectShowCameraExposureControl::setShutterSpeed(IAMCameraControl *cameraControl, qreal shutterSpeed)
-{
- if (m_currentExposureMode != QCameraExposure::ExposureManual) {
- qCDebug(qtDirectShowPlugin, "Trying to set shutter speed value while in auto exposure mode!");
- return false;
- }
-
- if (qFuzzyCompare(m_currentShutterSpeed, shutterSpeed))
- return true;
-
- if ((m_shutterSpeedValues.caps & CameraControl_Flags_Manual) == 0)
- return false;
-
- if (!m_supportedShutterSpeeds.contains(QVariant::fromValue(shutterSpeed)))
- return false;
-
- if (qFuzzyIsNull(shutterSpeed) || (shutterSpeed < qreal(0.0)))
- return false;
-
- const long newValue = long(log2(shutterSpeed));
- if (FAILED(cameraControl->Set(CameraControl_Exposure, newValue, CameraControl_Flags_Manual))) {
- qCDebug(qtDirectShowPlugin, "Unable to set shutter speed value to: %d", int(shutterSpeed));
- return false;
- }
-
- m_currentShutterSpeed = shutterSpeed;
- Q_EMIT actualValueChanged(int(ShutterSpeed));
- return true;
-}
-
-bool DirectShowCameraExposureControl::setAperture(IAMCameraControl *cameraControl, qreal aperture)
-{
- if (m_currentExposureMode != QCameraExposure::ExposureManual) {
- qCDebug(qtDirectShowPlugin, "Trying to set aperture value while in auto exposure mode!");
- return false;
- }
-
- if (qFuzzyCompare(m_currentAperture, aperture))
- return true;
-
- if ((m_apertureValues.caps & CameraControl_Flags_Manual) == 0)
- return false;
-
- if (!m_supportedApertureValues.contains(QVariant::fromValue(aperture)))
- return false;
-
- if (aperture < qreal(0.0))
- return false;
-
- const long newValue = long(10 * aperture);
- if (FAILED(cameraControl->Set(CameraControl_Iris, newValue, CameraControl_Flags_Manual))) {
- qCDebug(qtDirectShowPlugin, "Unable to set aperture value to: %d", int(aperture));
- return false;
- }
-
- m_currentAperture = aperture;
- Q_EMIT actualValueChanged(int(Aperture));
-
- return true;
-}
-
-bool DirectShowCameraExposureControl::setExposureMode(IAMCameraControl *cameraControl, QCameraExposure::ExposureMode mode)
-{
- if (m_currentExposureMode == mode)
- return true;
-
- bool exposureModeChanged = true;
-
- // Set auto exposure mode
- if (mode == QCameraExposure::ExposureAuto) {
- if ((m_apertureValues.caps & CameraControl_Flags_Auto)
- && FAILED(cameraControl->Set(CameraControl_Iris, 0, CameraControl_Flags_Auto))) {
- qCDebug(qtDirectShowPlugin, "Setting auto exposure mode failed!");
- exposureModeChanged = false;
- }
-
- if ((m_shutterSpeedValues.caps & CameraControl_Flags_Auto)
- && FAILED(cameraControl->Set(CameraControl_Exposure, 0, CameraControl_Flags_Auto))) {
- qCDebug(qtDirectShowPlugin, "Setting auto exposure mode failed");
- exposureModeChanged = false;
- }
-
- if (exposureModeChanged) {
- m_currentExposureMode = mode;
- Q_EMIT actualValueChanged(int(ExposureMode));
- }
-
- return exposureModeChanged;
- }
-
- // Change the current exposure mode to manual first.
- m_currentExposureMode = QCameraExposure::ExposureManual;
-
- const qreal newShutterSpeed = qFuzzyCompare(m_requestedShutterSpeed, -1.0)
- ? convertToSec(m_shutterSpeedValues.defaultValue)
- : m_requestedShutterSpeed;
- if ((m_shutterSpeedValues.caps & CameraControl_Flags_Manual))
- setShutterSpeed(cameraControl, newShutterSpeed);
-
- const qreal newAperture = qFuzzyCompare(m_requestedAperture, -1.0)
- ? convertToFvalue(m_apertureValues.defaultValue)
- : m_requestedAperture;
- if ((m_apertureValues.caps & CameraControl_Flags_Manual))
- setAperture(cameraControl, newAperture);
-
-
- // Check if any of the values changed.
- exposureModeChanged = (qFuzzyCompare(m_currentShutterSpeed, newShutterSpeed)
- || qFuzzyCompare(m_currentAperture, newAperture));
-
- if (exposureModeChanged)
- Q_EMIT actualValueChanged(int(ExposureMode));
-
- return exposureModeChanged;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/directshowcameraexposurecontrol.h b/src/plugins/directshow/camera/directshowcameraexposurecontrol.h
deleted file mode 100644
index db3fc5984..000000000
--- a/src/plugins/directshow/camera/directshowcameraexposurecontrol.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 DIRECTSHOWCAMERAEXPOSURECONTROL_H
-#define DIRECTSHOWCAMERAEXPOSURECONTROL_H
-
-#include <QtMultimedia/qcameraexposurecontrol.h>
-
-struct IAMCameraControl;
-
-QT_BEGIN_NAMESPACE
-
-class DSCameraSession;
-
-class DirectShowCameraExposureControl : public QCameraExposureControl
-{
- Q_OBJECT
-public:
- DirectShowCameraExposureControl(DSCameraSession *session);
-
- bool isParameterSupported(ExposureParameter parameter) const override;
- QVariantList supportedParameterRange(ExposureParameter parameter, bool *continuous) const override;
- QVariant requestedValue(ExposureParameter parameter) const override;
- QVariant actualValue(ExposureParameter parameter) const override;
- bool setValue(ExposureParameter parameter, const QVariant &value) override;
-
-private Q_SLOTS:
- void onStatusChanged(QCamera::Status status);
-
-private:
- DSCameraSession *m_session;
-
- struct ExposureValues
- {
- long caps;
- long minValue;
- long maxValue;
- long stepping;
- long defaultValue;
- } m_shutterSpeedValues, m_apertureValues;
-
- qreal m_requestedShutterSpeed;
- qreal m_currentShutterSpeed;
-
- qreal m_requestedAperture;
- qreal m_currentAperture;
-
- QVariantList m_supportedShutterSpeeds;
- QVariantList m_supportedApertureValues;
- QVariantList m_supportedExposureModes;
-
- QCameraExposure::ExposureMode m_requestedExposureMode;
- QCameraExposure::ExposureMode m_currentExposureMode;
-
- void updateExposureSettings();
-
- bool setShutterSpeed(IAMCameraControl *cameraControl, qreal shutterSpeed);
- bool setAperture(IAMCameraControl *cameraControl, qreal aperture);
- bool setExposureMode(IAMCameraControl *cameraControl, QCameraExposure::ExposureMode mode);
-};
-
-QT_END_NAMESPACE
-
-#endif // DIRECTSHOWCAMERAEXPOSURECONTROL_H
diff --git a/src/plugins/directshow/camera/directshowcameraglobal.h b/src/plugins/directshow/camera/directshowcameraglobal.h
deleted file mode 100644
index 917ae2dc7..000000000
--- a/src/plugins/directshow/camera/directshowcameraglobal.h
+++ /dev/null
@@ -1,231 +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 DIRECTSHOWCAMERAGLOBAL_H
-#define DIRECTSHOWCAMERAGLOBAL_H
-
-#include <QtCore/qglobal.h>
-
-#include <dshow.h>
-
-extern const GUID MEDIASUBTYPE_RGB24;
-extern const GUID MEDIASUBTYPE_RGB32;
-extern const GUID MEDIASUBTYPE_YUY2;
-extern const GUID MEDIASUBTYPE_MJPG;
-extern const GUID MEDIASUBTYPE_RGB555;
-extern const GUID MEDIASUBTYPE_YVU9;
-extern const GUID MEDIASUBTYPE_UYVY;
-extern const GUID PIN_CATEGORY_CAPTURE;
-extern const GUID PIN_CATEGORY_PREVIEW;
-
-extern const IID IID_IPropertyBag;
-extern const IID IID_ISampleGrabber;
-extern const IID IID_ICaptureGraphBuilder2;
-extern const IID IID_IAMStreamConfig;
-
-
-extern const CLSID CLSID_CVidCapClassManager;
-extern const CLSID CLSID_VideoInputDeviceCategory;
-extern const CLSID CLSID_SampleGrabber;
-extern const CLSID CLSID_CaptureGraphBuilder2;
-
-#define SAFE_RELEASE(x) { if (x) x->Release(); x = nullptr; }
-
-typedef struct IFileSinkFilter *LPFILESINKFILTER;
-typedef struct IAMCopyCaptureFileProgress *LPAMCOPYCAPTUREFILEPROGRESS;
-
-#ifndef __ICaptureGraphBuilder2_INTERFACE_DEFINED__
-#define __ICaptureGraphBuilder2_INTERFACE_DEFINED__
-struct ICaptureGraphBuilder2 : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE SetFiltergraph(
- /* [in] */ IGraphBuilder *pfg) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetFiltergraph(
- /* [out] */ IGraphBuilder **ppfg) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE SetOutputFileName(
- /* [in] */ const GUID *pType,
- /* [in] */ LPCOLESTR lpstrFile,
- /* [out] */ IBaseFilter **ppf,
- /* [out] */ IFileSinkFilter **ppSink) = 0;
-
- virtual /* [local] */ HRESULT STDMETHODCALLTYPE FindInterface(
- /* [in] */ const GUID *pCategory,
- /* [in] */ const GUID *pType,
- /* [in] */ IBaseFilter *pf,
- /* [in] */ REFIID riid,
- /* [out] */ void **ppint) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE RenderStream(
- /* [in] */ const GUID *pCategory,
- /* [in] */ const GUID *pType,
- /* [in] */ IUnknown *pSource,
- /* [in] */ IBaseFilter *pfCompressor,
- /* [in] */ IBaseFilter *pfRenderer) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE ControlStream(
- /* [in] */ const GUID *pCategory,
- /* [in] */ const GUID *pType,
- /* [in] */ IBaseFilter *pFilter,
- /* [in] */ REFERENCE_TIME *pstart,
- /* [in] */ REFERENCE_TIME *pstop,
- /* [in] */ WORD wStartCookie,
- /* [in] */ WORD wStopCookie) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE AllocCapFile(
- /* [in] */ LPCOLESTR lpstr,
- /* [in] */ DWORDLONG dwlSize) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE CopyCaptureFile(
- /* [in] */ LPOLESTR lpwstrOld,
- /* [in] */ LPOLESTR lpwstrNew,
- /* [in] */ int fAllowEscAbort,
- /* [in] */ IAMCopyCaptureFileProgress *pCallback) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE FindPin(
- /* [in] */ IUnknown *pSource,
- /* [in] */ PIN_DIRECTION pindir,
- /* [in] */ const GUID *pCategory,
- /* [in] */ const GUID *pType,
- /* [in] */ BOOL fUnconnected,
- /* [in] */ int num,
- /* [out] */ IPin **ppPin) = 0;
-
-};
-#endif
-
-#ifndef __IAMStreamConfig_INTERFACE_DEFINED__
-#define __IAMStreamConfig_INTERFACE_DEFINED__
-struct IAMStreamConfig : public IUnknown
-{
-public:
- virtual HRESULT STDMETHODCALLTYPE SetFormat(
- /* [in] */ AM_MEDIA_TYPE *pmt) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetFormat(
- /* [out] */ AM_MEDIA_TYPE **ppmt) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetNumberOfCapabilities(
- /* [out] */ int *piCount,
- /* [out] */ int *piSize) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetStreamCaps(
- /* [in] */ int iIndex,
- /* [out] */ AM_MEDIA_TYPE **ppmt,
- /* [out] */ BYTE *pSCC) = 0;
-
-};
-#endif
-
-#ifndef __IErrorLog_INTERFACE_DEFINED__
-#define __IErrorLog_INTERFACE_DEFINED__
-struct IErrorLog : public IUnknown
- {
- public:
- virtual HRESULT STDMETHODCALLTYPE AddError(
- /* [in] */ LPCOLESTR pszPropName,
- /* [in] */ EXCEPINFO *pExcepInfo) = 0;
-
- };
-#endif
-
-#ifndef __IPropertyBag_INTERFACE_DEFINED__
-#define __IPropertyBag_INTERFACE_DEFINED__
-struct IPropertyBag : public IUnknown
-{
-public:
- virtual /* [local] */ HRESULT STDMETHODCALLTYPE Read(
- /* [in] */ LPCOLESTR pszPropName,
- /* [out][in] */ VARIANT *pVar,
- /* [in] */ IErrorLog *pErrorLog) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE Write(
- /* [in] */ LPCOLESTR pszPropName,
- /* [in] */ VARIANT *pVar) = 0;
-
-};
-#endif
-
-typedef struct IMediaSample *LPMEDIASAMPLE;
-
-EXTERN_C const IID IID_ISampleGrabberCB;
-
-#ifndef __ISampleGrabberCB_INTERFACE_DEFINED__
-#define __ISampleGrabberCB_INTERFACE_DEFINED__
-
-#undef INTERFACE
-#define INTERFACE ISampleGrabberCB
-DECLARE_INTERFACE_(ISampleGrabberCB, IUnknown)
-{
-// STDMETHOD(QueryInterface) (THIS_ const GUID *, void **) PURE;
- STDMETHOD(QueryInterface) (THIS_ REFIID riid, void **) PURE;
- STDMETHOD_(ULONG, AddRef) (THIS) PURE;
- STDMETHOD_(ULONG, Release) (THIS) PURE;
- STDMETHOD_(HRESULT, SampleCB) (THIS_ double, LPMEDIASAMPLE) PURE;
- STDMETHOD_(HRESULT, BufferCB) (THIS_ double, BYTE *, long) PURE;
-};
-#undef INTERFACE
-
-#endif
-
-
-#ifndef __ISampleGrabber_INTERFACE_DEFINED__
-#define __ISampleGrabber_INTERFACE_DEFINED__
-
-#define INTERFACE ISampleGrabber
-DECLARE_INTERFACE_(ISampleGrabber,IUnknown)
-{
- STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE;
- STDMETHOD_(ULONG,AddRef)(THIS) PURE;
- STDMETHOD_(ULONG,Release)(THIS) PURE;
- STDMETHOD(SetOneShot)(THIS_ BOOL) PURE;
- STDMETHOD(SetMediaType)(THIS_ const AM_MEDIA_TYPE*) PURE;
- STDMETHOD(GetConnectedMediaType)(THIS_ AM_MEDIA_TYPE*) PURE;
- STDMETHOD(SetBufferSamples)(THIS_ BOOL) PURE;
- STDMETHOD(GetCurrentBuffer)(THIS_ long*,long*) PURE;
- STDMETHOD(GetCurrentSample)(THIS_ IMediaSample**) PURE;
- STDMETHOD(SetCallback)(THIS_ ISampleGrabberCB *,long) PURE;
-};
-#undef INTERFACE
-#endif
-
-
-#endif
diff --git a/src/plugins/directshow/camera/directshowcameraimageencodercontrol.cpp b/src/plugins/directshow/camera/directshowcameraimageencodercontrol.cpp
deleted file mode 100644
index 912f67a2d..000000000
--- a/src/plugins/directshow/camera/directshowcameraimageencodercontrol.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "directshowcameraimageencodercontrol.h"
-#include "dscamerasession.h"
-#include <QImageWriter>
-
-QT_BEGIN_NAMESPACE
-
-DirectShowCameraImageEncoderControl::DirectShowCameraImageEncoderControl(DSCameraSession *session)
- : QImageEncoderControl(session)
- , m_session(session)
-{
-}
-
-QList<QSize> DirectShowCameraImageEncoderControl::supportedResolutions(const QImageEncoderSettings &settings, bool *continuous) const
-{
- QList<QSize> res;
- if (!settings.codec().isEmpty() && !supportedImageCodecs().contains(settings.codec(), Qt::CaseInsensitive))
- return res;
-
- QList<QSize> resolutions = m_session->supportedResolutions(continuous);
- QSize r = settings.resolution();
- if (!r.isValid())
- return resolutions;
-
- if (resolutions.contains(r))
- res << settings.resolution();
-
- return res;
-}
-
-QStringList DirectShowCameraImageEncoderControl::supportedImageCodecs() const
-{
- QStringList supportedCodecs;
- for (const QByteArray &type: QImageWriter::supportedImageFormats()) {
- supportedCodecs << type;
- }
-
- return supportedCodecs;
-}
-
-QString DirectShowCameraImageEncoderControl::imageCodecDescription(const QString &codecName) const
-{
- Q_UNUSED(codecName);
- return QString();
-}
-
-QImageEncoderSettings DirectShowCameraImageEncoderControl::imageSettings() const
-{
- return m_session->imageEncoderSettings();
-}
-
-void DirectShowCameraImageEncoderControl::setImageSettings(const QImageEncoderSettings &settings)
-{
- m_session->setImageEncoderSettings(settings);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/directshowcameraimageencodercontrol.h b/src/plugins/directshow/camera/directshowcameraimageencodercontrol.h
deleted file mode 100644
index 6891bea77..000000000
--- a/src/plugins/directshow/camera/directshowcameraimageencodercontrol.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 DIRECTSHOWCAMERAIMAGEENCODERCONTROL_H
-#define DIRECTSHOWCAMERAIMAGEENCODERCONTROL_H
-
-#include <qimageencodercontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class DSCameraSession;
-class DirectShowCameraImageEncoderControl : public QImageEncoderControl
-{
- Q_OBJECT
-public:
- DirectShowCameraImageEncoderControl(DSCameraSession *session);
-
- QList<QSize> supportedResolutions(
- const QImageEncoderSettings &settings = QImageEncoderSettings(),
- bool *continuous = nullptr) const override;
-
- QStringList supportedImageCodecs() const override;
- QString imageCodecDescription(const QString &formatName) const override;
-
- QImageEncoderSettings imageSettings() const override;
- void setImageSettings(const QImageEncoderSettings &settings) override;
-
-private:
- DSCameraSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif // DIRECTSHOWCAMERAIMAGEENCODERCONTROL_H
diff --git a/src/plugins/directshow/camera/directshowcamerazoomcontrol.cpp b/src/plugins/directshow/camera/directshowcamerazoomcontrol.cpp
deleted file mode 100644
index 079976e15..000000000
--- a/src/plugins/directshow/camera/directshowcamerazoomcontrol.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "directshowcamerazoomcontrol.h"
-#include "dscamerasession.h"
-#include "directshowutils.h"
-
-QT_BEGIN_NAMESPACE
-
-inline static qreal defaultZoomValue() { return qreal(1.0); }
-
-DirectShowCameraZoomControl::DirectShowCameraZoomControl(DSCameraSession *session)
- : m_session(session)
- , m_opticalZoom({0, 0, 0, 0, 0})
- , m_currentOpticalZoom(qreal(0.0))
- , m_requestedOpticalZoom(qreal(0.0))
- , m_maxOpticalZoom(qreal(1.0))
-{
- Q_ASSERT(m_session);
- connect(m_session, &DSCameraSession::statusChanged,
- this, &DirectShowCameraZoomControl::onStatusChanged);
-}
-
-qreal DirectShowCameraZoomControl::maximumOpticalZoom() const
-{
- return m_maxOpticalZoom;
-}
-
-qreal DirectShowCameraZoomControl::maximumDigitalZoom() const
-{
- return defaultZoomValue();
-}
-
-qreal DirectShowCameraZoomControl::requestedOpticalZoom() const
-{
- return qMax(defaultZoomValue(), m_requestedOpticalZoom);
-}
-
-qreal DirectShowCameraZoomControl::requestedDigitalZoom() const
-{
- return defaultZoomValue();
-}
-
-qreal DirectShowCameraZoomControl::currentOpticalZoom() const
-{
- return qMax(defaultZoomValue(), m_currentOpticalZoom);
-}
-
-qreal DirectShowCameraZoomControl::currentDigitalZoom() const
-{
- return defaultZoomValue();
-}
-
-void DirectShowCameraZoomControl::zoomTo(qreal optical, qreal digital)
-{
- Q_UNUSED(digital);
- if (!(m_opticalZoom.caps & CameraControl_Flags_Manual))
- return;
-
- if (qFuzzyCompare(optical, m_requestedOpticalZoom))
- return;
-
- m_requestedOpticalZoom = optical;
- Q_EMIT requestedOpticalZoomChanged(m_requestedOpticalZoom);
-
- if (qFuzzyCompare(m_requestedOpticalZoom, m_currentOpticalZoom))
- return;
-
- if (m_session->status() != QCamera::LoadedStatus && m_session->status() != QCamera::ActiveStatus)
- return; // We'll wait until the camera is loaded, see: statusChanged connection
-
- opticalZoomToPrivate(optical);
-}
-
-void DirectShowCameraZoomControl::onStatusChanged(QCamera::Status status)
-{
- if (status == QCamera::LoadedStatus) {
- updateZoomValues();
- } else if (status == QCamera::UnloadedStatus) {
- SecureZeroMemory(&m_opticalZoom, sizeof(ZoomValues));
- m_currentOpticalZoom = qreal(0.0);
- m_requestedOpticalZoom = qreal(0.0);
- }
-
-}
-
-void DirectShowCameraZoomControl::updateZoomValues()
-{
- IAMCameraControl *cameraControl = nullptr;
- const DirectShowUtils::ScopedSafeRelease<IAMCameraControl> rControl { &cameraControl };
- if (!m_session->getCameraControlInterface(&cameraControl))
- return;
-
- ZoomValues values { 0, 0, 0, 0, 0 };
- // Zoom levels in DS are in the range [10, 600]
- // The default zoom is device specific.
- HRESULT hr = cameraControl->GetRange(CameraControl_Zoom,
- &values.minZoom,
- &values.maxZoom,
- &values.stepping,
- &values.defaultZoom,
- &values.caps);
-
- if (FAILED(hr)) {
- qCDebug(qtDirectShowPlugin, "Getting the camera's zoom properties failed");
- SecureZeroMemory(&m_opticalZoom, sizeof(ZoomValues));
- return;
- }
-
- if (!(values.caps & CameraControl_Flags_Manual)) {
- qCDebug(qtDirectShowPlugin, "Camera does not support manual zoom");
- SecureZeroMemory(&m_opticalZoom, sizeof(ZoomValues));
- return;
- }
-
- if (values.maxZoom != m_opticalZoom.maxZoom) {
- const qreal newMaxZoomScale = (values.minZoom == 0) ? defaultZoomValue()
- : (qreal(values.maxZoom) / qreal(values.minZoom));
- if (!qFuzzyCompare(newMaxZoomScale, m_maxOpticalZoom)) {
- m_maxOpticalZoom = newMaxZoomScale;
- Q_EMIT maximumOpticalZoomChanged(m_maxOpticalZoom);
- }
- }
-
- m_opticalZoom = values;
-
- long currentZoom = 0;
- long flags = 0;
- if (FAILED(cameraControl->Get(CameraControl_Zoom, &currentZoom, &flags))) {
- qCDebug(qtDirectShowPlugin, "Getting the camera's current zoom value failed!");
- return;
- }
-
- qreal currentOpticalZoom = (m_opticalZoom.minZoom == 0) ? defaultZoomValue()
- : (qreal(currentZoom) / qreal(m_opticalZoom.minZoom));
- currentOpticalZoom = qMax(defaultZoomValue(), currentOpticalZoom);
- if (!qFuzzyCompare(m_currentOpticalZoom, currentOpticalZoom)) {
- m_currentOpticalZoom = currentOpticalZoom;
- Q_EMIT currentOpticalZoomChanged(m_currentOpticalZoom);
- }
-
- // Check if there is a pending zoom value.
- if (!qFuzzyCompare(m_currentOpticalZoom, m_requestedOpticalZoom) && !qFuzzyIsNull(m_requestedOpticalZoom))
- opticalZoomToPrivate(m_requestedOpticalZoom);
-}
-
-bool DirectShowCameraZoomControl::opticalZoomToPrivate(qreal scaleFactor)
-{
- IAMCameraControl *cameraControl = nullptr;
- const DirectShowUtils::ScopedSafeRelease<IAMCameraControl> rControl { &cameraControl };
- if (!m_session->getCameraControlInterface(&cameraControl))
- return false;
-
- // Convert to DS zoom value
- const int newDSOpticalZoom = qRound(m_opticalZoom.minZoom * scaleFactor);
- long newDSOpticalZoomAdjusted = newDSOpticalZoom - (newDSOpticalZoom % m_opticalZoom.stepping);
- newDSOpticalZoomAdjusted = qBound(m_opticalZoom.minZoom, newDSOpticalZoomAdjusted, m_opticalZoom.maxZoom);
-
- if (FAILED(cameraControl->Set(CameraControl_Zoom, newDSOpticalZoomAdjusted, CameraControl_Flags_Manual))) {
- qCDebug(qtDirectShowPlugin, "Setting the camera's zoom value failed");
- return false;
- }
-
- const qreal newScaleFactor = (m_opticalZoom.minZoom == 0) ? defaultZoomValue()
- : (qreal(newDSOpticalZoomAdjusted) / qreal(m_opticalZoom.minZoom));
- // convert back to Qt scale value
- m_currentOpticalZoom = qMax(defaultZoomValue(), newScaleFactor);
- Q_EMIT currentOpticalZoomChanged(m_currentOpticalZoom);
-
- return true;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/directshowcamerazoomcontrol.h b/src/plugins/directshow/camera/directshowcamerazoomcontrol.h
deleted file mode 100644
index f6fb05b0e..000000000
--- a/src/plugins/directshow/camera/directshowcamerazoomcontrol.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 DIRECTSHOWCAMERAZOOMCONTROL_H
-#define DIRECTSHOWCAMERAZOOMCONTROL_H
-
-#include <QtMultimedia/qcamerazoomcontrol.h>
-#include <QtMultimedia/qcamera.h>
-
-QT_BEGIN_NAMESPACE
-
-class DSCameraSession;
-
-class DirectShowCameraZoomControl : public QCameraZoomControl
-{
- Q_OBJECT
-public:
- DirectShowCameraZoomControl(DSCameraSession *session);
-
- qreal maximumOpticalZoom() const override;
- qreal maximumDigitalZoom() const override;
- qreal requestedOpticalZoom() const override;
- qreal requestedDigitalZoom() const override;
- qreal currentOpticalZoom() const override;
- qreal currentDigitalZoom() const override;
- void zoomTo(qreal optical, qreal digital) override;
-
-private Q_SLOTS:
- void onStatusChanged(QCamera::Status status);
-
-private:
- DSCameraSession *m_session;
- struct ZoomValues
- {
- long maxZoom;
- long minZoom;
- long stepping;
- long defaultZoom;
- long caps;
- } m_opticalZoom;
-
- qreal m_currentOpticalZoom;
- qreal m_requestedOpticalZoom;
- qreal m_maxOpticalZoom;
-
- void updateZoomValues();
- bool opticalZoomToPrivate(qreal value);
-};
-
-QT_END_NAMESPACE
-
-#endif // DIRECTSHOWCAMERAZOOMCONTROL_H
diff --git a/src/plugins/directshow/camera/dscameracontrol.cpp b/src/plugins/directshow/camera/dscameracontrol.cpp
deleted file mode 100644
index 3f60ec848..000000000
--- a/src/plugins/directshow/camera/dscameracontrol.cpp
+++ /dev/null
@@ -1,120 +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 <QtCore/qdebug.h>
-
-#include "dscameracontrol.h"
-#include "dscameraservice.h"
-#include "dscamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-DSCameraControl::DSCameraControl(QObject *parent)
- : QCameraControl(parent)
-{
- m_session = qobject_cast<DSCameraSession*>(parent);
- connect(m_session, &DSCameraSession::statusChanged, this,
- [&](QCamera::Status status) {
- if (status == QCamera::UnloadedStatus)
- m_state = QCamera::UnloadedState;
- emit statusChanged(status);
- });
- connect(m_session, &DSCameraSession::cameraError,
- this, &DSCameraControl::error);
-}
-
-DSCameraControl::~DSCameraControl() = default;
-
-void DSCameraControl::setState(QCamera::State state)
-{
- if (m_state == state)
- return;
-
- bool succeeded = false;
- switch (state) {
- case QCamera::UnloadedState:
- succeeded = m_session->unload();
- break;
- case QCamera::LoadedState:
- case QCamera::ActiveState:
- if (m_state == QCamera::UnloadedState && !m_session->load())
- return;
-
- if (state == QCamera::ActiveState)
- succeeded = m_session->startPreview();
- else
- succeeded = m_session->stopPreview();
-
- break;
- }
-
- if (succeeded) {
- m_state = state;
- emit stateChanged(m_state);
- }
-}
-
-bool DSCameraControl::isCaptureModeSupported(QCamera::CaptureModes mode) const
-{
- bool bCaptureSupported = false;
- switch (mode) {
- case QCamera::CaptureStillImage:
- bCaptureSupported = true;
- break;
- case QCamera::CaptureVideo:
- bCaptureSupported = false;
- break;
- }
- return bCaptureSupported;
-}
-
-void DSCameraControl::setCaptureMode(QCamera::CaptureModes mode)
-{
- if (m_captureMode != mode && isCaptureModeSupported(mode)) {
- m_captureMode = mode;
- emit captureModeChanged(mode);
- }
-}
-
-QCamera::Status DSCameraControl::status() const
-{
- return m_session->status();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/dscameracontrol.h b/src/plugins/directshow/camera/dscameracontrol.h
deleted file mode 100644
index 2087623d1..000000000
--- a/src/plugins/directshow/camera/dscameracontrol.h
+++ /dev/null
@@ -1,80 +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 DSCAMERACONTROL_H
-#define DSCAMERACONTROL_H
-
-#include <QtCore/qobject.h>
-#include <qcameracontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class DSCameraSession;
-
-
-class DSCameraControl : public QCameraControl
-{
- Q_OBJECT
-public:
- DSCameraControl(QObject *parent = nullptr);
- ~DSCameraControl() override;
-
- QCamera::State state() const override { return m_state; }
-
- QCamera::CaptureModes captureMode() const override { return m_captureMode; }
- void setCaptureMode(QCamera::CaptureModes mode) override;
-
- void setState(QCamera::State state) override;
-
- QCamera::Status status() const override;
- bool isCaptureModeSupported(QCamera::CaptureModes mode) const override;
- bool canChangeProperty(PropertyChangeType, QCamera::Status) const override
- { return false; }
-
-private:
- DSCameraSession *m_session;
- QCamera::State m_state = QCamera::UnloadedState;
- QCamera::CaptureModes m_captureMode = QCamera::CaptureStillImage;
-};
-
-QT_END_NAMESPACE
-
-#endif
-
-
diff --git a/src/plugins/directshow/camera/dscameraimageprocessingcontrol.cpp b/src/plugins/directshow/camera/dscameraimageprocessingcontrol.cpp
deleted file mode 100644
index 6d0f45ae9..000000000
--- a/src/plugins/directshow/camera/dscameraimageprocessingcontrol.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Denis Shienkov <denis.shienkov@gmail.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "dscameraimageprocessingcontrol.h"
-#include "dscamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-DSCameraImageProcessingControl::DSCameraImageProcessingControl(DSCameraSession *session)
- : QCameraImageProcessingControl(session)
- , m_session(session)
-{
-}
-
-DSCameraImageProcessingControl::~DSCameraImageProcessingControl() = default;
-
-bool DSCameraImageProcessingControl::isParameterSupported(
- QCameraImageProcessingControl::ProcessingParameter parameter) const
-{
- return m_session->isImageProcessingParameterSupported(parameter);
-}
-
-bool DSCameraImageProcessingControl::isParameterValueSupported(
- QCameraImageProcessingControl::ProcessingParameter parameter,
- const QVariant &value) const
-{
- return m_session->isImageProcessingParameterValueSupported(parameter, value);
-}
-
-QVariant DSCameraImageProcessingControl::parameter(
- QCameraImageProcessingControl::ProcessingParameter parameter) const
-{
- return m_session->imageProcessingParameter(parameter);
-}
-
-void DSCameraImageProcessingControl::setParameter(QCameraImageProcessingControl::ProcessingParameter parameter,
- const QVariant &value)
-{
- m_session->setImageProcessingParameter(parameter, value);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/dscameraimageprocessingcontrol.h b/src/plugins/directshow/camera/dscameraimageprocessingcontrol.h
deleted file mode 100644
index 48f1b6b2c..000000000
--- a/src/plugins/directshow/camera/dscameraimageprocessingcontrol.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Denis Shienkov <denis.shienkov@gmail.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 DSCAMERAIMAGEPROCESSINGCONTROL_H
-#define DSCAMERAIMAGEPROCESSINGCONTROL_H
-
-#include <qcamera.h>
-#include <qcameraimageprocessingcontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class DSCameraSession;
-
-class DSCameraImageProcessingControl : public QCameraImageProcessingControl
-{
- Q_OBJECT
-
-public:
- DSCameraImageProcessingControl(DSCameraSession *session);
- ~DSCameraImageProcessingControl() override;
-
- bool isParameterSupported(ProcessingParameter) const override;
- bool isParameterValueSupported(ProcessingParameter parameter,
- const QVariant &value) const override;
- QVariant parameter(ProcessingParameter parameter) const override;
- void setParameter(ProcessingParameter parameter, const QVariant &value) override;
-
-private:
- DSCameraSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif // DSCAMERAIMAGEPROCESSINGCONTROL_H
diff --git a/src/plugins/directshow/camera/dscameraservice.cpp b/src/plugins/directshow/camera/dscameraservice.cpp
deleted file mode 100644
index ff488cf09..000000000
--- a/src/plugins/directshow/camera/dscameraservice.cpp
+++ /dev/null
@@ -1,161 +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 <QtCore/qvariant.h>
-#include <QtCore/qdebug.h>
-
-#include "dscameraservice.h"
-#include "dscameracontrol.h"
-#include "dscamerasession.h"
-#include "dsvideorenderer.h"
-#include "dsvideodevicecontrol.h"
-#include "dsimagecapturecontrol.h"
-#include "dscameraviewfindersettingscontrol.h"
-#include "dscameraimageprocessingcontrol.h"
-#include "directshowcameraexposurecontrol.h"
-#include "directshowcameracapturedestinationcontrol.h"
-#include "directshowcameracapturebufferformatcontrol.h"
-#include "directshowvideoprobecontrol.h"
-#include "directshowcamerazoomcontrol.h"
-#include "directshowcameraimageencodercontrol.h"
-
-QT_BEGIN_NAMESPACE
-
-DSCameraService::DSCameraService(QObject *parent):
- QMediaService(parent)
- , m_session(new DSCameraSession(this))
- , m_control(new DSCameraControl(m_session))
- , m_videoDevice(new DSVideoDeviceControl(m_session))
- , m_imageCapture(new DSImageCaptureControl(m_session))
- , m_viewfinderSettings(new DSCameraViewfinderSettingsControl(m_session))
- , m_imageProcessingControl(new DSCameraImageProcessingControl(m_session))
- , m_exposureControl(new DirectShowCameraExposureControl(m_session))
- , m_captureDestinationControl(new DirectShowCameraCaptureDestinationControl(m_session))
- , m_captureBufferFormatControl(new DirectShowCameraCaptureBufferFormatControl)
- , m_zoomControl(new DirectShowCameraZoomControl(m_session))
- , m_imageEncoderControl(new DirectShowCameraImageEncoderControl(m_session))
-{
-}
-
-DSCameraService::~DSCameraService()
-{
- delete m_control;
- delete m_viewfinderSettings;
- delete m_imageProcessingControl;
- delete m_videoDevice;
- delete m_videoRenderer;
- delete m_imageCapture;
- delete m_imageEncoderControl;
- delete m_session;
- delete m_exposureControl;
- delete m_captureDestinationControl;
- delete m_captureBufferFormatControl;
- delete m_videoProbeControl;
- delete m_zoomControl;
-}
-
-QMediaControl* DSCameraService::requestControl(const char *name)
-{
- if(qstrcmp(name,QCameraControl_iid) == 0)
- return m_control;
-
- if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0)
- return m_imageCapture;
-
- if (qstrcmp(name,QVideoRendererControl_iid) == 0) {
- if (!m_videoRenderer) {
- m_videoRenderer = new DSVideoRendererControl(m_session, this);
- return m_videoRenderer;
- }
- }
-
- if (qstrcmp(name,QVideoDeviceSelectorControl_iid) == 0)
- return m_videoDevice;
-
- if (qstrcmp(name, QCameraViewfinderSettingsControl2_iid) == 0)
- return m_viewfinderSettings;
-
- if (qstrcmp(name, QCameraImageProcessingControl_iid) == 0)
- return m_imageProcessingControl;
-
- if (qstrcmp(name, QCameraExposureControl_iid) == 0)
- return m_exposureControl;
-
- if (qstrcmp(name, QCameraCaptureDestinationControl_iid) == 0)
- return m_captureDestinationControl;
-
- if (qstrcmp(name, QCameraCaptureBufferFormatControl_iid) == 0)
- return m_captureBufferFormatControl;
-
- if (qstrcmp(name, QMediaVideoProbeControl_iid) == 0) {
- if (!m_videoProbeControl)
- m_videoProbeControl = new DirectShowVideoProbeControl;
-
- m_videoProbeControl->ref();
- m_session->addVideoProbe(m_videoProbeControl);
- return m_videoProbeControl;
- }
-
- if (qstrcmp(name, QCameraZoomControl_iid) == 0)
- return m_zoomControl;
-
- if (qstrcmp(name, QImageEncoderControl_iid) == 0)
- return m_imageEncoderControl;
-
- return nullptr;
-}
-
-void DSCameraService::releaseControl(QMediaControl *control)
-{
- if (control == m_videoRenderer) {
- delete m_videoRenderer;
- m_videoRenderer = nullptr;
- return;
- }
-
- if (control == m_videoProbeControl) {
- m_session->removeVideoProbe(m_videoProbeControl);
- if (!m_videoProbeControl->deref()) {
- delete m_videoProbeControl;
- m_videoProbeControl = nullptr;
- }
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/dscameraservice.h b/src/plugins/directshow/camera/dscameraservice.h
deleted file mode 100644
index 6ea85b725..000000000
--- a/src/plugins/directshow/camera/dscameraservice.h
+++ /dev/null
@@ -1,91 +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 DSCAMERASERVICE_H
-#define DSCAMERASERVICE_H
-
-#include <QtCore/qobject.h>
-
-#include <qmediaservice.h>
-
-QT_BEGIN_NAMESPACE
-
-class DSCameraControl;
-class DSCameraSession;
-class DSVideoDeviceControl;
-class DSImageCaptureControl;
-class DSCameraViewfinderSettingsControl;
-class DSCameraImageProcessingControl;
-class DirectShowCameraExposureControl;
-class DirectShowCameraCaptureDestinationControl;
-class DirectShowCameraCaptureBufferFormatControl;
-class DirectShowVideoProbeControl;
-class DirectShowCameraZoomControl;
-class DirectShowCameraImageEncoderControl;
-
-class DSCameraService : public QMediaService
-{
- Q_OBJECT
-
-public:
- DSCameraService(QObject *parent = nullptr);
- ~DSCameraService() override;
-
- QMediaControl* requestControl(const char *name) override;
- void releaseControl(QMediaControl *control) override;
-
-private:
- DSCameraSession *m_session;
- DSCameraControl *m_control;
- DSVideoDeviceControl *m_videoDevice;
- QMediaControl *m_videoRenderer = nullptr;
- DSImageCaptureControl *m_imageCapture;
- DSCameraViewfinderSettingsControl *m_viewfinderSettings;
- DSCameraImageProcessingControl *m_imageProcessingControl;
- DirectShowCameraExposureControl *m_exposureControl;
- DirectShowCameraCaptureDestinationControl *m_captureDestinationControl;
- DirectShowCameraCaptureBufferFormatControl *m_captureBufferFormatControl;
- DirectShowVideoProbeControl *m_videoProbeControl = nullptr;
- DirectShowCameraZoomControl *m_zoomControl;
- DirectShowCameraImageEncoderControl *m_imageEncoderControl;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/directshow/camera/dscamerasession.cpp b/src/plugins/directshow/camera/dscamerasession.cpp
deleted file mode 100644
index 7ceefe2c5..000000000
--- a/src/plugins/directshow/camera/dscamerasession.cpp
+++ /dev/null
@@ -1,1177 +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 <QtCore/qdebug.h>
-#include <QFile>
-#include <QtConcurrent/QtConcurrentRun>
-#include <QtMultimedia/qabstractvideobuffer.h>
-#include <QtMultimedia/qvideosurfaceformat.h>
-#include <QtMultimedia/qcameraimagecapture.h>
-#include <private/qmemoryvideobuffer_p.h>
-
-#include "dscamerasession.h"
-#include "dsvideorenderer.h"
-#include "directshowsamplegrabber.h"
-#include "directshowcameraglobal.h"
-#include "directshowmediatype.h"
-#include "directshowutils.h"
-#include "directshowvideoprobecontrol.h"
-
-QT_BEGIN_NAMESPACE
-
-DSCameraSession::DSCameraSession(QObject *parent)
- : QObject(parent)
-{
- connect(this, &DSCameraSession::statusChanged,
- this, &DSCameraSession::updateReadyForCapture);
-
- m_deviceLostEventTimer.setSingleShot(true);
- connect(&m_deviceLostEventTimer, &QTimer::timeout, [&]() {
- IMediaEvent *pEvent = com_cast<IMediaEvent>(m_filterGraph, IID_IMediaEvent);
- if (!pEvent)
- return;
-
- long eventCode;
- LONG_PTR param1;
- LONG_PTR param2;
- while (pEvent->GetEvent(&eventCode, &param1, &param2, 0) == S_OK) {
- switch (eventCode) {
- case EC_DEVICE_LOST:
- unload();
- break;
- default:
- break;
- }
-
- pEvent->FreeEventParams(eventCode, param1, param2);
- }
-
- pEvent->Release();
- });
-}
-
-DSCameraSession::~DSCameraSession()
-{
- unload();
-}
-
-void DSCameraSession::setSurface(QAbstractVideoSurface* surface)
-{
- m_surface = surface;
-}
-
-void DSCameraSession::setDevice(const QString &device)
-{
- m_sourceDeviceName = device;
-}
-
-QCameraViewfinderSettings DSCameraSession::viewfinderSettings() const
-{
- return m_status == QCamera::ActiveStatus ? m_actualViewfinderSettings : m_viewfinderSettings;
-}
-
-void DSCameraSession::setViewfinderSettings(const QCameraViewfinderSettings &settings)
-{
- m_viewfinderSettings = settings;
-}
-
-qreal DSCameraSession::scaledImageProcessingParameterValue(
- const ImageProcessingParameterInfo &sourceValueInfo)
-{
- if (sourceValueInfo.currentValue == sourceValueInfo.defaultValue)
- return 0.0f;
- if (sourceValueInfo.currentValue < sourceValueInfo.defaultValue) {
- return ((sourceValueInfo.currentValue - sourceValueInfo.minimumValue)
- / qreal(sourceValueInfo.defaultValue - sourceValueInfo.minimumValue))
- + (-1.0f);
- }
- return ((sourceValueInfo.currentValue - sourceValueInfo.defaultValue)
- / qreal(sourceValueInfo.maximumValue - sourceValueInfo.defaultValue));
-}
-
-qint32 DSCameraSession::sourceImageProcessingParameterValue(
- qreal scaledValue, const ImageProcessingParameterInfo &valueRange)
-{
- if (qFuzzyIsNull(scaledValue))
- return valueRange.defaultValue;
- if (scaledValue < 0.0f) {
- return ((scaledValue - (-1.0f)) * (valueRange.defaultValue - valueRange.minimumValue))
- + valueRange.minimumValue;
- }
- return (scaledValue * (valueRange.maximumValue - valueRange.defaultValue))
- + valueRange.defaultValue;
-}
-
-static QCameraImageProcessingControl::ProcessingParameter searchRelatedResultingParameter(
- QCameraImageProcessingControl::ProcessingParameter sourceParameter)
-{
- if (sourceParameter == QCameraImageProcessingControl::WhiteBalancePreset)
- return QCameraImageProcessingControl::ColorTemperature;
- return sourceParameter;
-}
-
-bool DSCameraSession::isImageProcessingParameterSupported(
- QCameraImageProcessingControl::ProcessingParameter parameter) const
-{
- const QCameraImageProcessingControl::ProcessingParameter resultingParameter =
- searchRelatedResultingParameter(parameter);
-
- return m_imageProcessingParametersInfos.contains(resultingParameter);
-}
-
-bool DSCameraSession::isImageProcessingParameterValueSupported(
- QCameraImageProcessingControl::ProcessingParameter parameter,
- const QVariant &value) const
-{
- const QCameraImageProcessingControl::ProcessingParameter resultingParameter =
- searchRelatedResultingParameter(parameter);
-
- QMap<QCameraImageProcessingControl::ProcessingParameter,
- ImageProcessingParameterInfo>::const_iterator sourceValueInfo =
- m_imageProcessingParametersInfos.constFind(resultingParameter);
-
- if (sourceValueInfo == m_imageProcessingParametersInfos.constEnd())
- return false;
-
- switch (parameter) {
-
- case QCameraImageProcessingControl::WhiteBalancePreset: {
- const QCameraImageProcessing::WhiteBalanceMode checkedValue =
- value.value<QCameraImageProcessing::WhiteBalanceMode>();
- // Supports only the Manual and the Auto values
- if (checkedValue != QCameraImageProcessing::WhiteBalanceManual
- && checkedValue != QCameraImageProcessing::WhiteBalanceAuto) {
- return false;
- }
- }
- break;
-
- case QCameraImageProcessingControl::ColorTemperature: {
- const qint32 checkedValue = value.toInt();
- if (checkedValue < (*sourceValueInfo).minimumValue
- || checkedValue > (*sourceValueInfo).maximumValue) {
- return false;
- }
- }
- break;
-
- case QCameraImageProcessingControl::ContrastAdjustment: // falling back
- case QCameraImageProcessingControl::SaturationAdjustment: // falling back
- case QCameraImageProcessingControl::BrightnessAdjustment: // falling back
- case QCameraImageProcessingControl::SharpeningAdjustment: {
- const qint32 sourceValue = sourceImageProcessingParameterValue(
- value.toReal(), (*sourceValueInfo));
- if (sourceValue < (*sourceValueInfo).minimumValue
- || sourceValue > (*sourceValueInfo).maximumValue)
- return false;
- }
- break;
-
- default:
- return false;
- }
-
- return true;
-}
-
-QVariant DSCameraSession::imageProcessingParameter(
- QCameraImageProcessingControl::ProcessingParameter parameter) const
-{
- if (!m_graphBuilder) {
- auto it = m_pendingImageProcessingParametrs.find(parameter);
- return it != m_pendingImageProcessingParametrs.end() ? it.value() : QVariant();
- }
-
- const QCameraImageProcessingControl::ProcessingParameter resultingParameter =
- searchRelatedResultingParameter(parameter);
-
- QMap<QCameraImageProcessingControl::ProcessingParameter,
- ImageProcessingParameterInfo>::const_iterator sourceValueInfo =
- m_imageProcessingParametersInfos.constFind(resultingParameter);
-
- if (sourceValueInfo == m_imageProcessingParametersInfos.constEnd())
- return QVariant();
-
- switch (parameter) {
-
- case QCameraImageProcessingControl::WhiteBalancePreset:
- return QVariant::fromValue<QCameraImageProcessing::WhiteBalanceMode>(
- (*sourceValueInfo).capsFlags == VideoProcAmp_Flags_Auto
- ? QCameraImageProcessing::WhiteBalanceAuto
- : QCameraImageProcessing::WhiteBalanceManual);
-
- case QCameraImageProcessingControl::ColorTemperature:
- return QVariant::fromValue<qint32>((*sourceValueInfo).currentValue);
-
- case QCameraImageProcessingControl::ContrastAdjustment: // falling back
- case QCameraImageProcessingControl::SaturationAdjustment: // falling back
- case QCameraImageProcessingControl::BrightnessAdjustment: // falling back
- case QCameraImageProcessingControl::SharpeningAdjustment:
- return scaledImageProcessingParameterValue((*sourceValueInfo));
-
- default:
- return QVariant();
- }
-}
-
-void DSCameraSession::setImageProcessingParameter(
- QCameraImageProcessingControl::ProcessingParameter parameter,
- const QVariant &value)
-{
- if (!m_graphBuilder) {
- m_pendingImageProcessingParametrs.insert(parameter, value);
- return;
- }
-
- const QCameraImageProcessingControl::ProcessingParameter resultingParameter =
- searchRelatedResultingParameter(parameter);
-
- QMap<QCameraImageProcessingControl::ProcessingParameter,
- ImageProcessingParameterInfo>::iterator sourceValueInfo =
- m_imageProcessingParametersInfos.find(resultingParameter);
-
- if (sourceValueInfo == m_imageProcessingParametersInfos.end())
- return;
-
- LONG sourceValue = 0;
- LONG capsFlags = VideoProcAmp_Flags_Manual;
-
- switch (parameter) {
-
- case QCameraImageProcessingControl::WhiteBalancePreset: {
- const QCameraImageProcessing::WhiteBalanceMode checkedValue =
- value.value<QCameraImageProcessing::WhiteBalanceMode>();
- // Supports only the Manual and the Auto values
- if (checkedValue == QCameraImageProcessing::WhiteBalanceManual)
- capsFlags = VideoProcAmp_Flags_Manual;
- else if (checkedValue == QCameraImageProcessing::WhiteBalanceAuto)
- capsFlags = VideoProcAmp_Flags_Auto;
- else
- return;
-
- sourceValue = ((*sourceValueInfo).hasBeenExplicitlySet)
- ? (*sourceValueInfo).currentValue
- : (*sourceValueInfo).defaultValue;
- }
- break;
-
- case QCameraImageProcessingControl::ColorTemperature:
- sourceValue = value.isValid() ?
- value.value<qint32>() : (*sourceValueInfo).defaultValue;
- capsFlags = (*sourceValueInfo).capsFlags;
- break;
-
- case QCameraImageProcessingControl::ContrastAdjustment: // falling back
- case QCameraImageProcessingControl::SaturationAdjustment: // falling back
- case QCameraImageProcessingControl::BrightnessAdjustment: // falling back
- case QCameraImageProcessingControl::SharpeningAdjustment:
- if (value.isValid()) {
- sourceValue = sourceImageProcessingParameterValue(
- value.toReal(), (*sourceValueInfo));
- } else {
- sourceValue = (*sourceValueInfo).defaultValue;
- }
- break;
-
- default:
- return;
- }
-
- IAMVideoProcAmp *pVideoProcAmp = nullptr;
- HRESULT hr = m_graphBuilder->FindInterface(
- nullptr,
- nullptr,
- m_sourceFilter,
- IID_IAMVideoProcAmp,
- reinterpret_cast<void**>(&pVideoProcAmp)
- );
-
- if (FAILED(hr) || !pVideoProcAmp) {
- qWarning() << "failed to find the video proc amp";
- return;
- }
-
- hr = pVideoProcAmp->Set(
- (*sourceValueInfo).videoProcAmpProperty,
- sourceValue,
- capsFlags);
-
- pVideoProcAmp->Release();
-
- if (FAILED(hr)) {
- qWarning() << "failed to set the parameter value";
- } else {
- (*sourceValueInfo).capsFlags = capsFlags;
- (*sourceValueInfo).hasBeenExplicitlySet = true;
- (*sourceValueInfo).currentValue = sourceValue;
- }
-}
-
-bool DSCameraSession::getCameraControlInterface(IAMCameraControl **cameraControl) const
-{
- if (!m_sourceFilter) {
- qCDebug(qtDirectShowPlugin, "getCameraControlInterface failed: No capture filter!");
- return false;
- }
-
- if (!cameraControl) {
- qCDebug(qtDirectShowPlugin, "getCameraControlInterface failed: Invalid out argument!");
- return false;
- }
-
- if (FAILED(m_sourceFilter->QueryInterface(IID_IAMCameraControl, reinterpret_cast<void **>(cameraControl)))) {
- qCDebug(qtDirectShowPlugin, "getCameraControlInterface failed: Querying camera control failed!");
- return false;
- }
-
- return true;
-}
-
-bool DSCameraSession::isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const
-{
- return destination & (QCameraImageCapture::CaptureToFile | QCameraImageCapture::CaptureToBuffer);
-}
-
-QCameraImageCapture::CaptureDestinations DSCameraSession::captureDestination() const
-{
- return m_captureDestinations;
-}
-
-void DSCameraSession::setCaptureDestination(QCameraImageCapture::CaptureDestinations destinations)
-{
- if (m_captureDestinations == destinations)
- return;
-
- m_captureDestinations = destinations;
- Q_EMIT captureDestinationChanged(m_captureDestinations);
-}
-
-void DSCameraSession::addVideoProbe(DirectShowVideoProbeControl *probe)
-{
- const QMutexLocker locker(&m_probeMutex);
- m_videoProbeControl = probe;
-}
-
-void DSCameraSession::removeVideoProbe(DirectShowVideoProbeControl *probe)
-{
- Q_UNUSED(probe);
- Q_ASSERT(m_videoProbeControl == probe);
- const QMutexLocker locker(&m_probeMutex);
- m_videoProbeControl = nullptr;
-}
-
-bool DSCameraSession::load()
-{
- unload();
-
- setStatus(QCamera::LoadingStatus);
-
- bool succeeded = createFilterGraph();
- if (succeeded)
- setStatus(QCamera::LoadedStatus);
- else
- setStatus(QCamera::UnavailableStatus);
-
- return succeeded;
-}
-
-bool DSCameraSession::unload()
-{
- if (!m_graphBuilder)
- return false;
-
- if (!stopPreview())
- return false;
-
- setStatus(QCamera::UnloadingStatus);
-
- m_needsHorizontalMirroring = false;
- m_supportedViewfinderSettings.clear();
- m_supportedFormats.clear();
- SAFE_RELEASE(m_sourceFilter);
- SAFE_RELEASE(m_nullRendererFilter);
- SAFE_RELEASE(m_filterGraph);
- SAFE_RELEASE(m_graphBuilder);
- SAFE_RELEASE(m_outputPin);
-
- setStatus(QCamera::UnloadedStatus);
-
- return true;
-}
-
-bool DSCameraSession::startPreview()
-{
- if (m_previewStarted)
- return true;
-
- if (!m_graphBuilder)
- return false;
-
- setStatus(QCamera::StartingStatus);
-
- QString errorString;
- HRESULT hr = S_OK;
- IMediaControl* pControl = nullptr;
-
- if (!configurePreviewFormat()) {
- errorString = tr("Failed to configure preview format");
- goto failed;
- }
-
- if (!connectGraph()) {
- errorString = tr("Failed to connect graph");
- goto failed;
- }
-
- if (m_surface)
- m_surface->start(m_previewSurfaceFormat);
-
- hr = m_filterGraph->QueryInterface(IID_IMediaControl, reinterpret_cast<void**>(&pControl));
- if (FAILED(hr)) {
- errorString = tr("Failed to get stream control");
- goto failed;
- }
- hr = pControl->Run();
- pControl->Release();
-
- if (FAILED(hr)) {
- errorString = tr("Failed to start");
- goto failed;
- }
-
- setStatus(QCamera::ActiveStatus);
- m_previewStarted = true;
- return true;
-
-failed:
- // go back to a clean state
- if (m_surface && m_surface->isActive())
- m_surface->stop();
- disconnectGraph();
- setError(QCamera::CameraError, errorString, hr);
- return false;
-}
-
-bool DSCameraSession::stopPreview()
-{
- if (!m_previewStarted)
- return true;
-
- setStatus(QCamera::StoppingStatus);
-
- if (m_previewSampleGrabber)
- m_previewSampleGrabber->stop();
-
- QString errorString;
- IMediaControl* pControl = nullptr;
- HRESULT hr = m_filterGraph->QueryInterface(IID_IMediaControl,
- reinterpret_cast<void**>(&pControl));
- if (FAILED(hr)) {
- errorString = tr("Failed to get stream control");
- goto failed;
- }
-
- hr = pControl->Stop();
- pControl->Release();
- if (FAILED(hr)) {
- errorString = tr("Failed to stop");
- goto failed;
- }
-
- disconnectGraph();
-
- m_sourceFormat.clear();
-
- m_previewStarted = false;
- setStatus(QCamera::LoadedStatus);
- return true;
-
-failed:
- setError(QCamera::CameraError, errorString, hr);
- return false;
-}
-
-void DSCameraSession::setError(int error, const QString &errorString, HRESULT hr)
-{
- qErrnoWarning(hr, "[0x%x] %s", hr, qPrintable(errorString));
- emit cameraError(error, errorString);
- setStatus(QCamera::UnloadedStatus);
-}
-
-void DSCameraSession::setStatus(QCamera::Status status)
-{
- if (m_status == status)
- return;
-
- m_status = status;
- emit statusChanged(m_status);
-}
-
-bool DSCameraSession::isReadyForCapture()
-{
- return m_readyForCapture;
-}
-
-void DSCameraSession::updateReadyForCapture()
-{
- bool isReady = (m_status == QCamera::ActiveStatus && m_imageCaptureFileName.isEmpty());
- if (isReady != m_readyForCapture) {
- m_readyForCapture = isReady;
- emit readyForCaptureChanged(isReady);
- }
-}
-
-int DSCameraSession::captureImage(const QString &fileName)
-{
- ++m_imageIdCounter;
-
- if (!m_readyForCapture) {
- emit captureError(m_imageIdCounter, QCameraImageCapture::NotReadyError,
- tr("Camera not ready for capture"));
- return m_imageIdCounter;
- }
-
- const QString ext = !m_imageEncoderSettings.codec().isEmpty()
- ? m_imageEncoderSettings.codec().toLower()
- : QLatin1String("jpg");
- m_imageCaptureFileName = m_fileNameGenerator.generateFileName(fileName,
- QMediaStorageLocation::Pictures,
- QLatin1String("IMG_"),
- ext);
-
- updateReadyForCapture();
-
- m_captureMutex.lock();
- m_currentImageId = m_imageIdCounter;
- m_captureMutex.unlock();
-
- return m_imageIdCounter;
-}
-
-void DSCameraSession::onFrameAvailable(double time, const QByteArray &data)
-{
- // !!! Not called on the main thread
- Q_UNUSED(time);
-
- m_presentMutex.lock();
-
- // In case the source produces frames faster than we can display them,
- // only keep the most recent one
- m_currentFrame = QVideoFrame(new QMemoryVideoBuffer(data, m_stride),
- m_previewSize,
- m_previewPixelFormat);
-
- m_presentMutex.unlock();
-
- {
- const QMutexLocker locker(&m_probeMutex);
- if (m_currentFrame.isValid() && m_videoProbeControl)
- Q_EMIT m_videoProbeControl->videoFrameProbed(m_currentFrame);
- }
-
- // Image capture
- QMutexLocker locker(&m_captureMutex);
- if (m_currentImageId != -1 && !m_capturedFrame.isValid()) {
- m_capturedFrame = m_currentFrame;
- QMetaObject::invokeMethod(this, "imageExposed", Qt::QueuedConnection, Q_ARG(int, m_currentImageId));
- }
-
- QMetaObject::invokeMethod(this, "presentFrame", Qt::QueuedConnection);
-}
-
-void DSCameraSession::presentFrame()
-{
- // If no frames provided from ISampleGrabber for some time
- // the device might be potentially unplugged.
- m_deviceLostEventTimer.start(100);
-
- m_presentMutex.lock();
-
- if (m_currentFrame.isValid() && m_surface) {
- m_surface->present(m_currentFrame);
- m_currentFrame = QVideoFrame();
- }
-
- m_presentMutex.unlock();
-
- QImage captureImage;
- const int captureId = m_currentImageId;
-
- m_captureMutex.lock();
-
- if (m_capturedFrame.isValid()) {
-
- captureImage = m_capturedFrame.image();
-
- const bool needsVerticalMirroring = m_previewSurfaceFormat.scanLineDirection() != QVideoSurfaceFormat::TopToBottom;
- captureImage = captureImage.mirrored(m_needsHorizontalMirroring, needsVerticalMirroring); // also causes a deep copy of the data
-
- QtConcurrent::run(&DSCameraSession::processCapturedImage, this,
- m_currentImageId, m_captureDestinations, captureImage, m_imageCaptureFileName);
-
- m_imageCaptureFileName.clear();
- m_currentImageId = -1;
-
- m_capturedFrame = QVideoFrame();
- }
-
- m_captureMutex.unlock();
-
- if (!captureImage.isNull())
- emit imageCaptured(captureId, captureImage);
-
- updateReadyForCapture();
-}
-
-void DSCameraSession::processCapturedImage(int id,
- QCameraImageCapture::CaptureDestinations captureDestinations,
- const QImage &image,
- const QString &path)
-{
- const QString format = m_imageEncoderSettings.codec();
- if (captureDestinations & QCameraImageCapture::CaptureToFile) {
- if (image.save(path, !format.isEmpty() ? format.toUtf8().constData() : "JPG")) {
- Q_EMIT imageSaved(id, path);
- } else {
- Q_EMIT captureError(id, QCameraImageCapture::ResourceError,
- tr("Could not save image to file."));
- }
- }
-
- if (captureDestinations & QCameraImageCapture::CaptureToBuffer)
- Q_EMIT imageAvailable(id, QVideoFrame(image));
-}
-
-bool DSCameraSession::createFilterGraph()
-{
- // Previously containered in <qedit.h>.
- static const CLSID cLSID_NullRenderer = { 0xC1F400A4, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } };
-
- QString errorString;
- HRESULT hr;
- IMoniker* pMoniker = nullptr;
- ICreateDevEnum* pDevEnum = nullptr;
- IEnumMoniker* pEnum = nullptr;
-
- // Create the filter graph
- hr = CoCreateInstance(CLSID_FilterGraph, nullptr, CLSCTX_INPROC,
- IID_IGraphBuilder, reinterpret_cast<void**>(&m_filterGraph));
- if (FAILED(hr)) {
- errorString = tr("Failed to create filter graph");
- goto failed;
- }
-
- // Create the capture graph builder
- hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, nullptr, CLSCTX_INPROC,
- IID_ICaptureGraphBuilder2,
- reinterpret_cast<void**>(&m_graphBuilder));
- if (FAILED(hr)) {
- errorString = tr("Failed to create graph builder");
- goto failed;
- }
-
- // Attach the filter graph to the capture graph
- hr = m_graphBuilder->SetFiltergraph(m_filterGraph);
- if (FAILED(hr)) {
- errorString = tr("Failed to connect capture graph and filter graph");
- goto failed;
- }
-
- // Find the Capture device
- hr = CoCreateInstance(CLSID_SystemDeviceEnum, nullptr,
- CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
- reinterpret_cast<void**>(&pDevEnum));
- if (SUCCEEDED(hr)) {
- // Create an enumerator for the video capture category
- hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
- pDevEnum->Release();
- if (S_OK == hr) {
- pEnum->Reset();
- IMalloc *mallocInterface = nullptr;
- CoGetMalloc(1, (LPMALLOC*)&mallocInterface);
- //go through and find all video capture devices
- while (pEnum->Next(1, &pMoniker, nullptr) == S_OK) {
-
- BSTR strName = nullptr;
- hr = pMoniker->GetDisplayName(nullptr, nullptr, &strName);
- if (SUCCEEDED(hr)) {
- QString output = QString::fromWCharArray(strName);
- mallocInterface->Free(strName);
- if (m_sourceDeviceName.contains(output)) {
- hr = pMoniker->BindToObject(nullptr, nullptr, IID_IBaseFilter,
- reinterpret_cast<void**>(&m_sourceFilter));
- if (SUCCEEDED(hr)) {
- pMoniker->Release();
- break;
- }
- }
- }
- pMoniker->Release();
- }
- mallocInterface->Release();
- if (nullptr == m_sourceFilter)
- {
- if (m_sourceDeviceName.contains(QLatin1String("default")))
- {
- pEnum->Reset();
- // still have to loop to discard bind to storage failure case
- while (pEnum->Next(1, &pMoniker, nullptr) == S_OK) {
- IPropertyBag *pPropBag = nullptr;
-
- hr = pMoniker->BindToStorage(nullptr, nullptr, IID_IPropertyBag,
- reinterpret_cast<void**>(&pPropBag));
- if (FAILED(hr)) {
- pMoniker->Release();
- continue; // Don't panic yet
- }
-
- // No need to get the description, just grab it
-
- hr = pMoniker->BindToObject(nullptr, nullptr, IID_IBaseFilter,
- reinterpret_cast<void**>(&m_sourceFilter));
- pPropBag->Release();
- pMoniker->Release();
- if (SUCCEEDED(hr))
- break; // done, stop looping through
- qWarning("Object bind failed");
- }
- }
- }
- pEnum->Release();
- }
- }
-
- if (!m_sourceFilter) {
- errorString = tr("No capture device found");
- goto failed;
- }
-
- if (!DirectShowUtils::getPin(m_sourceFilter, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE, &m_outputPin, &hr))
- qWarning() << "Failed to get the pin for the video control:" << hr;
-
- // Sample grabber filter
- if (!m_previewSampleGrabber) {
- m_previewSampleGrabber = new DirectShowSampleGrabber(this);
- connect(m_previewSampleGrabber, &DirectShowSampleGrabber::bufferAvailable,
- this, &DSCameraSession::onFrameAvailable, Qt::DirectConnection);
- }
-
-
- // Null renderer. Input connected to the sample grabber's output. Simply
- // discard the samples it receives.
- hr = CoCreateInstance(cLSID_NullRenderer, nullptr, CLSCTX_INPROC,
- IID_IBaseFilter, (void**)&m_nullRendererFilter);
- if (FAILED(hr)) {
- errorString = tr("Failed to create null renderer");
- goto failed;
- }
-
- updateSourceCapabilities();
-
- return true;
-
-failed:
- m_needsHorizontalMirroring = false;
- SAFE_RELEASE(m_sourceFilter);
- SAFE_RELEASE(m_nullRendererFilter);
- SAFE_RELEASE(m_filterGraph);
- SAFE_RELEASE(m_graphBuilder);
- setError(QCamera::CameraError, errorString, hr);
-
- return false;
-}
-
-bool DSCameraSession::configurePreviewFormat()
-{
- // Resolve viewfinder settings
- int settingsIndex = 0;
- const QSize captureResolution = m_imageEncoderSettings.resolution();
- const QSize resolution = captureResolution.isValid() ? captureResolution : m_viewfinderSettings.resolution();
- QCameraViewfinderSettings resolvedViewfinderSettings;
- for (const QCameraViewfinderSettings &s : qAsConst(m_supportedViewfinderSettings)) {
- if ((resolution.isEmpty() || resolution == s.resolution())
- && (qFuzzyIsNull(m_viewfinderSettings.minimumFrameRate()) || qFuzzyCompare((float)m_viewfinderSettings.minimumFrameRate(), (float)s.minimumFrameRate()))
- && (qFuzzyIsNull(m_viewfinderSettings.maximumFrameRate()) || qFuzzyCompare((float)m_viewfinderSettings.maximumFrameRate(), (float)s.maximumFrameRate()))
- && (m_viewfinderSettings.pixelFormat() == QVideoFrame::Format_Invalid || m_viewfinderSettings.pixelFormat() == s.pixelFormat())
- && (m_viewfinderSettings.pixelAspectRatio().isEmpty() || m_viewfinderSettings.pixelAspectRatio() == s.pixelAspectRatio())) {
- resolvedViewfinderSettings = s;
- break;
- }
- ++settingsIndex;
- }
-
- if (resolvedViewfinderSettings.isNull()) {
- qWarning("Invalid viewfinder settings");
- return false;
- }
-
- m_actualViewfinderSettings = resolvedViewfinderSettings;
-
- m_sourceFormat = m_supportedFormats[settingsIndex];
- // Set frame rate.
- // We don't care about the minimumFrameRate, DirectShow only allows to set an
- // average frame rate, so set that to the maximumFrameRate.
- VIDEOINFOHEADER *videoInfo = reinterpret_cast<VIDEOINFOHEADER*>(m_sourceFormat->pbFormat);
- videoInfo->AvgTimePerFrame = 10000000 / resolvedViewfinderSettings.maximumFrameRate();
-
- m_previewPixelFormat = resolvedViewfinderSettings.pixelFormat();
- const AM_MEDIA_TYPE *resolvedGrabberFormat = &m_sourceFormat;
-
- if (m_surface) {
- const auto surfaceFormats = m_surface->supportedPixelFormats(QAbstractVideoBuffer::NoHandle);
- if (!surfaceFormats.contains(m_previewPixelFormat)) {
- if (surfaceFormats.contains(QVideoFrame::Format_RGB32)) {
- // As a fallback, we support RGB32, if the capture source doesn't support
- // that format, the graph builder will automatically insert a
- // converter (when possible).
-
- static const AM_MEDIA_TYPE rgb32GrabberFormat { MEDIATYPE_Video, MEDIASUBTYPE_ARGB32, 0, 0, 0, FORMAT_VideoInfo, nullptr, 0, nullptr};
- resolvedGrabberFormat = &rgb32GrabberFormat;
- m_previewPixelFormat = QVideoFrame::Format_RGB32;
-
- } else {
- qWarning() << "Video surface needs to support at least RGB32 pixel format";
- return false;
- }
- }
- }
-
- m_previewSize = resolvedViewfinderSettings.resolution();
- m_previewSurfaceFormat = QVideoSurfaceFormat(m_previewSize,
- m_previewPixelFormat,
- QAbstractVideoBuffer::NoHandle);
- m_previewSurfaceFormat.setScanLineDirection(DirectShowMediaType::scanLineDirection(m_previewPixelFormat, videoInfo->bmiHeader));
- m_stride = DirectShowMediaType::bytesPerLine(m_previewSurfaceFormat);
-
- HRESULT hr;
- IAMStreamConfig* pConfig = nullptr;
- hr = m_graphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
- m_sourceFilter, IID_IAMStreamConfig,
- reinterpret_cast<void**>(&pConfig));
- if (FAILED(hr)) {
- qWarning() << "Failed to get config for capture device";
- return false;
- }
-
- hr = pConfig->SetFormat(&m_sourceFormat);
-
- pConfig->Release();
-
- if (FAILED(hr)) {
- qWarning() << "Unable to set video format on capture device";
- return false;
- }
-
- if (!m_previewSampleGrabber->setMediaType(resolvedGrabberFormat))
- return false;
-
- m_previewSampleGrabber->start(DirectShowSampleGrabber::CallbackMethod::BufferCB);
-
- return true;
-}
-
-void DSCameraSession::updateImageProcessingParametersInfos()
-{
- if (!m_graphBuilder) {
- qWarning() << "failed to access to the graph builder";
- return;
- }
-
- IAMVideoProcAmp *pVideoProcAmp = nullptr;
- const HRESULT hr = m_graphBuilder->FindInterface(
- nullptr,
- nullptr,
- m_sourceFilter,
- IID_IAMVideoProcAmp,
- reinterpret_cast<void**>(&pVideoProcAmp)
- );
-
- if (FAILED(hr) || !pVideoProcAmp) {
- qWarning() << "failed to find the video proc amp";
- return;
- }
-
- for (int property = VideoProcAmp_Brightness; property <= VideoProcAmp_Gain; ++property) {
-
- QCameraImageProcessingControl::ProcessingParameter processingParameter; // not initialized
-
- switch (property) {
- case VideoProcAmp_Brightness:
- processingParameter = QCameraImageProcessingControl::BrightnessAdjustment;
- break;
- case VideoProcAmp_Contrast:
- processingParameter = QCameraImageProcessingControl::ContrastAdjustment;
- break;
- case VideoProcAmp_Saturation:
- processingParameter = QCameraImageProcessingControl::SaturationAdjustment;
- break;
- case VideoProcAmp_Sharpness:
- processingParameter = QCameraImageProcessingControl::SharpeningAdjustment;
- break;
- case VideoProcAmp_WhiteBalance:
- processingParameter = QCameraImageProcessingControl::ColorTemperature;
- break;
- default: // unsupported or not implemented yet parameter
- continue;
- }
-
- ImageProcessingParameterInfo sourceValueInfo;
- LONG steppingDelta = 0;
-
- HRESULT hr = pVideoProcAmp->GetRange(
- property,
- &sourceValueInfo.minimumValue,
- &sourceValueInfo.maximumValue,
- &steppingDelta,
- &sourceValueInfo.defaultValue,
- &sourceValueInfo.capsFlags);
-
- if (FAILED(hr))
- continue;
-
- hr = pVideoProcAmp->Get(
- property,
- &sourceValueInfo.currentValue,
- &sourceValueInfo.capsFlags);
-
- if (FAILED(hr))
- continue;
-
- sourceValueInfo.videoProcAmpProperty = static_cast<VideoProcAmpProperty>(property);
-
- m_imageProcessingParametersInfos.insert(processingParameter, sourceValueInfo);
- }
-
- pVideoProcAmp->Release();
-
- for (auto it = m_pendingImageProcessingParametrs.cbegin();
- it != m_pendingImageProcessingParametrs.cend();
- ++it) {
- setImageProcessingParameter(it.key(), it.value());
- }
- m_pendingImageProcessingParametrs.clear();
-}
-
-bool DSCameraSession::connectGraph()
-{
- HRESULT hr = m_filterGraph->AddFilter(m_sourceFilter, L"Capture Filter");
- if (FAILED(hr)) {
- qWarning() << "failed to add capture filter to graph";
- return false;
- }
-
- if (FAILED(m_filterGraph->AddFilter(m_previewSampleGrabber->filter(), L"Sample Grabber"))) {
- qWarning() << "failed to add sample grabber to graph";
- return false;
- }
-
- hr = m_filterGraph->AddFilter(m_nullRendererFilter, L"Null Renderer");
- if (FAILED(hr)) {
- qWarning() << "failed to add null renderer to graph";
- return false;
- }
-
- hr = m_graphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
- m_sourceFilter,
- m_previewSampleGrabber->filter(),
- m_nullRendererFilter);
- if (FAILED(hr)) {
- qWarning() << "Graph failed to connect filters" << hr;
- return false;
- }
-
- return true;
-}
-
-void DSCameraSession::disconnectGraph()
-{
- // To avoid increasing the memory usage every time the graph is re-connected it's
- // important that all filters are released; also the ones added by the "Intelligent Connect".
- IEnumFilters *enumFilters = nullptr;
- if (SUCCEEDED(m_filterGraph->EnumFilters(&enumFilters))) {
- IBaseFilter *filter = nullptr;
- while (enumFilters->Next(1, &filter, nullptr) == S_OK) {
- m_filterGraph->RemoveFilter(filter);
- enumFilters->Reset();
- filter->Release();
- }
- enumFilters->Release();
- }
-}
-
-static bool qt_frameRateRangeGreaterThan(const QCamera::FrameRateRange &r1, const QCamera::FrameRateRange &r2)
-{
- return r1.maximumFrameRate > r2.maximumFrameRate;
-}
-
-void DSCameraSession::updateSourceCapabilities()
-{
- HRESULT hr;
- AM_MEDIA_TYPE *pmt = nullptr;
- VIDEOINFOHEADER *pvi = nullptr;
- VIDEO_STREAM_CONFIG_CAPS scc;
- IAMStreamConfig* pConfig = nullptr;
-
- m_supportedViewfinderSettings.clear();
- m_needsHorizontalMirroring = false;
- m_supportedFormats.clear();
- m_imageProcessingParametersInfos.clear();
-
- IAMVideoControl *pVideoControl = nullptr;
- hr = m_graphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
- m_sourceFilter, IID_IAMVideoControl,
- reinterpret_cast<void**>(&pVideoControl));
- if (FAILED(hr)) {
- qWarning() << "Failed to get the video control";
- } else if (m_outputPin) {
- long supportedModes;
- hr = pVideoControl->GetCaps(m_outputPin, &supportedModes);
- if (FAILED(hr)) {
- qWarning() << "Failed to get the supported modes of the video control";
- } else if (supportedModes & VideoControlFlag_FlipHorizontal) {
- long mode;
- hr = pVideoControl->GetMode(m_outputPin, &mode);
- if (FAILED(hr))
- qWarning() << "Failed to get the mode of the video control";
- else if (supportedModes & VideoControlFlag_FlipHorizontal)
- m_needsHorizontalMirroring = (mode & VideoControlFlag_FlipHorizontal);
- }
- pVideoControl->Release();
- }
-
- hr = m_graphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
- m_sourceFilter, IID_IAMStreamConfig,
- reinterpret_cast<void**>(&pConfig));
- if (FAILED(hr)) {
- qWarning() << "failed to get config on capture device";
- return;
- }
-
- int iCount;
- int iSize;
- hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize);
- if (FAILED(hr)) {
- qWarning() << "failed to get capabilities";
- return;
- }
-
- for (int iIndex = 0; iIndex < iCount; ++iIndex) {
- hr = pConfig->GetStreamCaps(iIndex, &pmt, reinterpret_cast<BYTE*>(&scc));
- if (hr == S_OK) {
- QVideoFrame::PixelFormat pixelFormat = DirectShowMediaType::pixelFormatFromType(pmt);
-
- if (pmt->majortype == MEDIATYPE_Video
- && pmt->formattype == FORMAT_VideoInfo
- && pixelFormat != QVideoFrame::Format_Invalid) {
-
- pvi = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat);
- QSize resolution(pvi->bmiHeader.biWidth, pvi->bmiHeader.biHeight);
-
- QList<QCamera::FrameRateRange> frameRateRanges;
-
- if (pVideoControl && m_outputPin) {
- long listSize = 0;
- LONGLONG *frameRates = nullptr;
- SIZE size = { resolution.width(), resolution.height() };
- hr = pVideoControl->GetFrameRateList(m_outputPin, iIndex, size, &listSize, &frameRates);
- if (hr == S_OK && listSize > 0 && frameRates) {
- for (long i = 0; i < listSize; ++i) {
- qreal fr = qreal(10000000) / frameRates[i];
- frameRateRanges.append(QCamera::FrameRateRange(fr, fr));
- }
-
- // Make sure higher frame rates come first
- std::sort(frameRateRanges.begin(), frameRateRanges.end(), qt_frameRateRangeGreaterThan);
- }
-
- CoTaskMemFree(frameRates);
- }
-
- if (frameRateRanges.isEmpty()) {
- frameRateRanges.append(QCamera::FrameRateRange(qreal(10000000) / scc.MaxFrameInterval,
- qreal(10000000) / scc.MinFrameInterval));
- }
-
- for (const QCamera::FrameRateRange &frameRateRange : qAsConst(frameRateRanges)) {
- QCameraViewfinderSettings settings;
- settings.setResolution(resolution);
- settings.setMinimumFrameRate(frameRateRange.minimumFrameRate);
- settings.setMaximumFrameRate(frameRateRange.maximumFrameRate);
- settings.setPixelFormat(pixelFormat);
- settings.setPixelAspectRatio(1, 1);
- m_supportedViewfinderSettings.append(settings);
- m_supportedFormats.append(DirectShowMediaType(*pmt));
- }
- } else {
- OLECHAR *guidString = nullptr;
- StringFromCLSID(pmt->subtype, &guidString);
- if (guidString)
- qWarning() << "Unsupported media type:" << QString::fromWCharArray(guidString);
- ::CoTaskMemFree(guidString);
- }
-
- DirectShowMediaType::deleteType(pmt);
- }
- }
-
- pConfig->Release();
-
- updateImageProcessingParametersInfos();
-}
-
-QList<QSize> DSCameraSession::supportedResolutions(bool *continuous) const
-{
- if (continuous)
- *continuous = false;
-
- QList<QSize> res;
- for (auto &settings : m_supportedViewfinderSettings) {
- auto size = settings.resolution();
- if (!res.contains(size))
- res << size;
- }
-
- std::sort(res.begin(), res.end(), [](const QSize &r1, const QSize &r2) {
- return qlonglong(r1.width()) * r1.height() < qlonglong(r2.width()) * r2.height();
- });
-
- return res;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/dscamerasession.h b/src/plugins/directshow/camera/dscamerasession.h
deleted file mode 100644
index 9f88163b9..000000000
--- a/src/plugins/directshow/camera/dscamerasession.h
+++ /dev/null
@@ -1,242 +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 DSCAMERASESSION_H
-#define DSCAMERASESSION_H
-
-#include <QtCore/qobject.h>
-#include <QTime>
-#include <QUrl>
-#include <QMutex>
-#include <QTimer>
-#include <qcamera.h>
-#include <QtMultimedia/qvideoframe.h>
-#include <QtMultimedia/qabstractvideosurface.h>
-#include <QtMultimedia/qvideosurfaceformat.h>
-#include <QtMultimedia/qcameraimageprocessingcontrol.h>
-#include <QtMultimedia/qcameraimagecapture.h>
-#include <QtMultimedia/qmediaencodersettings.h>
-#include <private/qmediastoragelocation_p.h>
-
-#include <tchar.h>
-#include <dshow.h>
-#include <objbase.h>
-#include <initguid.h>
-#include "directshowmediatype.h"
-#ifdef Q_CC_MSVC
-# pragma comment(lib, "strmiids.lib")
-# pragma comment(lib, "ole32.lib")
-#endif // Q_CC_MSVC
-#include <windows.h>
-
-#ifdef Q_CC_MSVC
-# pragma include_alias("dxtrans.h","qedit.h")
-#endif // Q_CC_MSVC
-#define __IDxtCompositor_INTERFACE_DEFINED__
-#define __IDxtAlphaSetter_INTERFACE_DEFINED__
-#define __IDxtJpeg_INTERFACE_DEFINED__
-#define __IDxtKey_INTERFACE_DEFINED__
-
-struct ICaptureGraphBuilder2;
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowSampleGrabber;
-class DirectShowVideoProbeControl;
-
-class DSCameraSession : public QObject
-{
- Q_OBJECT
-public:
- DSCameraSession(QObject *parent = nullptr);
- ~DSCameraSession() override;
-
- QCamera::Status status() const { return m_status; }
-
- void setDevice(const QString &device);
-
- bool load();
- bool unload();
- bool startPreview();
- bool stopPreview();
-
- bool isReadyForCapture();
- int captureImage(const QString &fileName);
-
- void setSurface(QAbstractVideoSurface* surface);
-
- QCameraViewfinderSettings viewfinderSettings() const;
- void setViewfinderSettings(const QCameraViewfinderSettings &settings);
-
- QList<QCameraViewfinderSettings> supportedViewfinderSettings() const
- { return m_supportedViewfinderSettings; }
-
- bool isImageProcessingParameterSupported(
- QCameraImageProcessingControl::ProcessingParameter) const;
-
- bool isImageProcessingParameterValueSupported(
- QCameraImageProcessingControl::ProcessingParameter,
- const QVariant &) const;
-
- QVariant imageProcessingParameter(
- QCameraImageProcessingControl::ProcessingParameter) const;
-
- void setImageProcessingParameter(
- QCameraImageProcessingControl::ProcessingParameter,
- const QVariant &);
-
- bool getCameraControlInterface(IAMCameraControl **cameraControl) const;
-
- bool isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const;
- QCameraImageCapture::CaptureDestinations captureDestination() const;
- void setCaptureDestination(QCameraImageCapture::CaptureDestinations destinations);
-
- void addVideoProbe(DirectShowVideoProbeControl *probe);
- void removeVideoProbe(DirectShowVideoProbeControl *probe);
-
- QList<QSize> supportedResolutions(bool *continuous) const;
- QImageEncoderSettings imageEncoderSettings() const { return m_imageEncoderSettings; }
- void setImageEncoderSettings(const QImageEncoderSettings &settings)
- { m_imageEncoderSettings = settings; }
-
-Q_SIGNALS:
- void statusChanged(QCamera::Status);
- void imageExposed(int id);
- void imageCaptured(int id, const QImage &preview);
- void imageSaved(int id, const QString &fileName);
- void imageAvailable(int id, const QVideoFrame &buffer);
- void readyForCaptureChanged(bool);
- void captureError(int id, int error, const QString &errorString);
- void captureDestinationChanged(QCameraImageCapture::CaptureDestinations);
- void cameraError(int error, const QString &errorString);
-
-private Q_SLOTS:
- void presentFrame();
- void updateReadyForCapture();
-
-private:
- struct ImageProcessingParameterInfo
- {
- LONG minimumValue = 0;
- LONG maximumValue = 0;
- LONG defaultValue = 0;
- LONG currentValue = 0;
- LONG capsFlags = 0;
- bool hasBeenExplicitlySet = false;
- VideoProcAmpProperty videoProcAmpProperty = VideoProcAmp_Brightness;
- };
-
- void setStatus(QCamera::Status status);
-
- void onFrameAvailable(double time, const QByteArray &data);
- void processCapturedImage(int id, QCameraImageCapture::CaptureDestinations captureDestinations, const QImage &image, const QString &path);
-
- bool createFilterGraph();
- bool connectGraph();
- void disconnectGraph();
- void updateSourceCapabilities();
- bool configurePreviewFormat();
- void updateImageProcessingParametersInfos();
- void setError(int error, const QString &errorString, HRESULT hr);
-
- // These static functions are used for scaling of adjustable parameters,
- // which have the ranges from -1.0 to +1.0 in the QCameraImageProcessing API.
- static qreal scaledImageProcessingParameterValue(
- const ImageProcessingParameterInfo &sourceValueInfo);
- static qint32 sourceImageProcessingParameterValue(
- qreal scaledValue, const ImageProcessingParameterInfo &sourceValueInfo);
-
- QMutex m_presentMutex;
- QMutex m_captureMutex;
-
- ICaptureGraphBuilder2* m_graphBuilder = nullptr;
- IGraphBuilder* m_filterGraph = nullptr;
-
- // Source (camera)
- QString m_sourceDeviceName = QLatin1String("default");
- IBaseFilter* m_sourceFilter = nullptr;
- bool m_needsHorizontalMirroring = false;
- QList<DirectShowMediaType> m_supportedFormats;
- QList<QCameraViewfinderSettings> m_supportedViewfinderSettings;
- DirectShowMediaType m_sourceFormat;
- QMap<QCameraImageProcessingControl::ProcessingParameter, ImageProcessingParameterInfo> m_imageProcessingParametersInfos;
-
- // Preview
- DirectShowSampleGrabber *m_previewSampleGrabber = nullptr;
- IBaseFilter *m_nullRendererFilter = nullptr;
- QVideoFrame m_currentFrame;
- bool m_previewStarted = false;
- QAbstractVideoSurface* m_surface = nullptr;
- QVideoSurfaceFormat m_previewSurfaceFormat;
- QVideoFrame::PixelFormat m_previewPixelFormat = QVideoFrame::Format_RGB32;
- QSize m_previewSize;
- int m_stride = -1;
- QCameraViewfinderSettings m_viewfinderSettings;
- QCameraViewfinderSettings m_actualViewfinderSettings;
-
- // Image capture
- QString m_imageCaptureFileName;
- QMediaStorageLocation m_fileNameGenerator;
- bool m_readyForCapture = false;
- int m_imageIdCounter = 0;
- int m_currentImageId = -1;
- QVideoFrame m_capturedFrame;
- QCameraImageCapture::CaptureDestinations m_captureDestinations = QCameraImageCapture::CaptureToFile;
-
- // Video probe
- QMutex m_probeMutex;
- DirectShowVideoProbeControl *m_videoProbeControl = nullptr;
-
- QImageEncoderSettings m_imageEncoderSettings;
-
- // Internal state
- QCamera::Status m_status = QCamera::UnloadedStatus;
- QTimer m_deviceLostEventTimer;
-
- QMap<QCameraImageProcessingControl::ProcessingParameter, QVariant> m_pendingImageProcessingParametrs;
-
- IPin *m_outputPin = nullptr;
-
- friend class SampleGrabberCallbackPrivate;
-};
-
-QT_END_NAMESPACE
-
-
-#endif
diff --git a/src/plugins/directshow/camera/dscameraviewfindersettingscontrol.cpp b/src/plugins/directshow/camera/dscameraviewfindersettingscontrol.cpp
deleted file mode 100644
index 655a47d07..000000000
--- a/src/plugins/directshow/camera/dscameraviewfindersettingscontrol.cpp
+++ /dev/null
@@ -1,66 +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 "dscameraviewfindersettingscontrol.h"
-#include "dscamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-DSCameraViewfinderSettingsControl::DSCameraViewfinderSettingsControl(DSCameraSession *session)
- : QCameraViewfinderSettingsControl2(session)
- , m_session(session)
-{
-}
-
-QList<QCameraViewfinderSettings> DSCameraViewfinderSettingsControl::supportedViewfinderSettings() const
-{
- return m_session->supportedViewfinderSettings();
-}
-
-QCameraViewfinderSettings DSCameraViewfinderSettingsControl::viewfinderSettings() const
-{
- return m_session->viewfinderSettings();
-}
-
-void DSCameraViewfinderSettingsControl::setViewfinderSettings(const QCameraViewfinderSettings &settings)
-{
- m_session->setViewfinderSettings(settings);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/dscameraviewfindersettingscontrol.h b/src/plugins/directshow/camera/dscameraviewfindersettingscontrol.h
deleted file mode 100644
index a2b646edf..000000000
--- a/src/plugins/directshow/camera/dscameraviewfindersettingscontrol.h
+++ /dev/null
@@ -1,65 +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 DSCAMERAVIEWFINDERSETTINGSCONTROL_H
-#define DSCAMERAVIEWFINDERSETTINGSCONTROL_H
-
-#include <qcameraviewfindersettingscontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class DSCameraSession;
-
-class DSCameraViewfinderSettingsControl : public QCameraViewfinderSettingsControl2
-{
-public:
- DSCameraViewfinderSettingsControl(DSCameraSession *session);
-
- QList<QCameraViewfinderSettings> supportedViewfinderSettings() const override;
-
- QCameraViewfinderSettings viewfinderSettings() const override;
- void setViewfinderSettings(const QCameraViewfinderSettings &settings) override;
-
-private:
- DSCameraSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif // DSCAMERAVIEWFINDERSETTINGSCONTROL_H
diff --git a/src/plugins/directshow/camera/dsimagecapturecontrol.cpp b/src/plugins/directshow/camera/dsimagecapturecontrol.cpp
deleted file mode 100644
index e4d2eee30..000000000
--- a/src/plugins/directshow/camera/dsimagecapturecontrol.cpp
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <QtCore/QDebug>
-
-#include "dsimagecapturecontrol.h"
-
-QT_BEGIN_NAMESPACE
-
-DSImageCaptureControl::DSImageCaptureControl(DSCameraSession *session)
- : QCameraImageCaptureControl(session)
- , m_session(session)
-{
- connect(m_session, &DSCameraSession::imageExposed,
- this, &DSImageCaptureControl::imageExposed);
- connect(m_session, &DSCameraSession::imageCaptured,
- this, &DSImageCaptureControl::imageCaptured);
- connect(m_session, &DSCameraSession::imageSaved,
- this, &DSImageCaptureControl::imageSaved);
- connect(m_session, &DSCameraSession::readyForCaptureChanged,
- this, &DSImageCaptureControl::readyForCaptureChanged);
- connect(m_session, &DSCameraSession::captureError,
- this, &DSImageCaptureControl::error);
- connect(m_session, &DSCameraSession::imageAvailable,
- this, &DSImageCaptureControl::imageAvailable);
-}
-
-DSImageCaptureControl::~DSImageCaptureControl() = default;
-
-bool DSImageCaptureControl::isReadyForCapture() const
-{
- return m_session->isReadyForCapture();
-}
-
-int DSImageCaptureControl::capture(const QString &fileName)
-{
- return m_session->captureImage(fileName);
-}
-
-QCameraImageCapture::DriveMode DSImageCaptureControl::driveMode() const
-{
- return QCameraImageCapture::SingleImageCapture;
-}
-
-void DSImageCaptureControl::setDriveMode(QCameraImageCapture::DriveMode mode)
-{
- if (mode != QCameraImageCapture::SingleImageCapture)
- qWarning("Drive mode not supported.");
-}
-
-QT_END_NAMESPACE
-
diff --git a/src/plugins/directshow/camera/dsimagecapturecontrol.h b/src/plugins/directshow/camera/dsimagecapturecontrol.h
deleted file mode 100644
index c619de1a1..000000000
--- a/src/plugins/directshow/camera/dsimagecapturecontrol.h
+++ /dev/null
@@ -1,69 +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 DSIMAGECAPTURECONTROL_H
-#define DSIMAGECAPTURECONTROL_H
-
-#include <qcameraimagecapturecontrol.h>
-#include "dscamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-class DSImageCaptureControl : public QCameraImageCaptureControl
-{
- Q_OBJECT
-public:
- DSImageCaptureControl(DSCameraSession *session);
- ~DSImageCaptureControl() override;
-
- bool isReadyForCapture() const override;
- int capture(const QString &fileName) override;
-
- QCameraImageCapture::DriveMode driveMode() const override;
- void setDriveMode(QCameraImageCapture::DriveMode mode) override;
-
- void cancelCapture() override {}
-
-private:
- DSCameraSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif // DSCAPTURECONTROL_H
diff --git a/src/plugins/directshow/camera/dsvideodevicecontrol.cpp b/src/plugins/directshow/camera/dsvideodevicecontrol.cpp
deleted file mode 100644
index 0f08154f1..000000000
--- a/src/plugins/directshow/camera/dsvideodevicecontrol.cpp
+++ /dev/null
@@ -1,186 +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 <QDebug>
-#include <QFile>
-#include <qelapsedtimer.h>
-
-#include "dsvideodevicecontrol.h"
-#include "dscamerasession.h"
-
-#include <tchar.h>
-#include <dshow.h>
-#include <objbase.h>
-#include <initguid.h>
-#include <ocidl.h>
-#include <string.h>
-
-extern const CLSID CLSID_VideoInputDeviceCategory;
-
-QT_BEGIN_NAMESPACE
-
-Q_GLOBAL_STATIC(QList<DSVideoDeviceInfo>, deviceList)
-
-DSVideoDeviceControl::DSVideoDeviceControl(QObject *parent)
- : QVideoDeviceSelectorControl(parent)
-{
- m_session = qobject_cast<DSCameraSession*>(parent);
- selected = 0;
-}
-
-int DSVideoDeviceControl::deviceCount() const
-{
- updateDevices();
- return deviceList->count();
-}
-
-QString DSVideoDeviceControl::deviceName(int index) const
-{
- updateDevices();
-
- if (index >= 0 && index <= deviceList->count())
- return QString::fromUtf8(deviceList->at(index).first.constData());
-
- return QString();
-}
-
-QString DSVideoDeviceControl::deviceDescription(int index) const
-{
- updateDevices();
-
- if (index >= 0 && index <= deviceList->count())
- return deviceList->at(index).second;
-
- return QString();
-}
-
-int DSVideoDeviceControl::defaultDevice() const
-{
- return 0;
-}
-
-int DSVideoDeviceControl::selectedDevice() const
-{
- return selected;
-}
-
-void DSVideoDeviceControl::setSelectedDevice(int index)
-{
- updateDevices();
-
- if (index >= 0 && index < deviceList->count()) {
- if (m_session) {
- QString device = deviceList->at(index).first;
- if (device.startsWith("ds:"))
- device.remove(0,3);
- m_session->setDevice(device);
- }
- selected = index;
- }
-}
-
-const QList<DSVideoDeviceInfo> &DSVideoDeviceControl::availableDevices()
-{
- updateDevices();
- return *deviceList;
-}
-
-void DSVideoDeviceControl::updateDevices()
-{
- static QElapsedTimer timer;
- if (timer.isValid() && timer.elapsed() < 500) // ms
- return;
-
- deviceList->clear();
-
- ICreateDevEnum* pDevEnum = nullptr;
- IEnumMoniker* pEnum = nullptr;
- // Create the System device enumerator
- HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, nullptr,
- CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
- reinterpret_cast<void**>(&pDevEnum));
- if (SUCCEEDED(hr)) {
- // Create the enumerator for the video capture category
- hr = pDevEnum->CreateClassEnumerator(
- CLSID_VideoInputDeviceCategory, &pEnum, 0);
- if (S_OK == hr) {
- pEnum->Reset();
- // go through and find all video capture devices
- IMoniker* pMoniker = nullptr;
- IMalloc *mallocInterface = nullptr;
- CoGetMalloc(1, (LPMALLOC*)&mallocInterface);
- while (pEnum->Next(1, &pMoniker, nullptr) == S_OK) {
- BSTR strName = nullptr;
- hr = pMoniker->GetDisplayName(nullptr, nullptr, &strName);
- if (SUCCEEDED(hr)) {
- QString output(QString::fromWCharArray(strName));
- mallocInterface->Free(strName);
-
- DSVideoDeviceInfo devInfo;
- devInfo.first = output.toUtf8();
-
- IPropertyBag *pPropBag;
- hr = pMoniker->BindToStorage(nullptr, nullptr, IID_IPropertyBag,
- reinterpret_cast<void**>(&pPropBag));
- if (SUCCEEDED(hr)) {
- // Find the description
- VARIANT varName;
- varName.vt = VT_BSTR;
- hr = pPropBag->Read(L"FriendlyName", &varName, nullptr);
- if (SUCCEEDED(hr)) {
- output = QString::fromWCharArray(varName.bstrVal);
- }
- pPropBag->Release();
- }
- devInfo.second = output;
-
- deviceList->append(devInfo);
- }
- pMoniker->Release();
- }
- mallocInterface->Release();
- pEnum->Release();
- }
- pDevEnum->Release();
- }
-
- timer.restart();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/dsvideodevicecontrol.h b/src/plugins/directshow/camera/dsvideodevicecontrol.h
deleted file mode 100644
index 24a5b61a1..000000000
--- a/src/plugins/directshow/camera/dsvideodevicecontrol.h
+++ /dev/null
@@ -1,79 +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 DSVIDEODEVICECONTROL_H
-#define DSVIDEODEVICECONTROL_H
-
-#include <qvideodeviceselectorcontrol.h>
-#include <QStringList>
-
-QT_BEGIN_NAMESPACE
-class DSCameraSession;
-
-//QTM_USE_NAMESPACE
-
-using DSVideoDeviceInfo = QPair<QByteArray, QString>;
-
-class DSVideoDeviceControl : public QVideoDeviceSelectorControl
-{
- Q_OBJECT
-public:
- DSVideoDeviceControl(QObject *parent = nullptr);
-
- int deviceCount() const override;
- QString deviceName(int index) const override;
- QString deviceDescription(int index) const override;
- int defaultDevice() const override;
- int selectedDevice() const override;
-
- static const QList<DSVideoDeviceInfo> &availableDevices();
-
-public Q_SLOTS:
- void setSelectedDevice(int index) override;
-
-private:
- static void updateDevices();
-
- DSCameraSession* m_session;
- int selected;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/directshow/camera/dsvideorenderer.cpp b/src/plugins/directshow/camera/dsvideorenderer.cpp
deleted file mode 100644
index cde63af65..000000000
--- a/src/plugins/directshow/camera/dsvideorenderer.cpp
+++ /dev/null
@@ -1,67 +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 <QtCore/qdebug.h>
-
-#include "dsvideorenderer.h"
-
-QT_BEGIN_NAMESPACE
-
-DSVideoRendererControl::DSVideoRendererControl(DSCameraSession* session, QObject *parent)
- :QVideoRendererControl(parent),
- m_session(session)
-{
-}
-
-DSVideoRendererControl::~DSVideoRendererControl() = default;
-
-QAbstractVideoSurface* DSVideoRendererControl::surface() const
-{
- return m_surface;
-}
-
-void DSVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
-{
- m_surface = surface;
- if(m_session)
- m_session->setSurface(m_surface);
-}
-
-QT_END_NAMESPACE
-
diff --git a/src/plugins/directshow/camera/dsvideorenderer.h b/src/plugins/directshow/camera/dsvideorenderer.h
deleted file mode 100644
index a6a1f8103..000000000
--- a/src/plugins/directshow/camera/dsvideorenderer.h
+++ /dev/null
@@ -1,68 +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 DSVIDEORENDERER_H
-#define DSVIDEORENDERER_H
-
-#include <qvideorenderercontrol.h>
-#include "dscamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-
-class DSVideoRendererControl : public QVideoRendererControl
-{
- Q_OBJECT
-public:
- DSVideoRendererControl(DSCameraSession* session, QObject *parent = nullptr);
- ~DSVideoRendererControl() override;
-
- QAbstractVideoSurface *surface() const override;
- void setSurface(QAbstractVideoSurface *surface) override;
-
- void setSession(DSCameraSession* session);
-
-private:
- QAbstractVideoSurface* m_surface = nullptr;
- DSCameraSession* m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif // DSVIDEORENDERER_H
diff --git a/src/plugins/directshow/common/common.pri b/src/plugins/directshow/common/common.pri
deleted file mode 100644
index 43acff8a5..000000000
--- a/src/plugins/directshow/common/common.pri
+++ /dev/null
@@ -1,29 +0,0 @@
-INCLUDEPATH += $$PWD
-
-HEADERS += \
- $$PWD/directshowbasefilter.h \
- $$PWD/directshoweventloop.h \
- $$PWD/directshowglobal.h \
- $$PWD/directshowmediatype.h \
- $$PWD/directshowmediatypeenum.h \
- $$PWD/directshowobject.h \
- $$PWD/directshowpin.h \
- $$PWD/directshowpinenum.h \
- $$PWD/directshowvideobuffer.h \
- $$PWD/directshowutils.h \
- $$PWD/directshowvideoprobecontrol.h \
- $$PWD/directshowaudioprobecontrol.h \
- $$PWD/directshowsamplegrabber.h
-
-SOURCES += \
- $$PWD/directshowbasefilter.cpp \
- $$PWD/directshoweventloop.cpp \
- $$PWD/directshowmediatype.cpp \
- $$PWD/directshowmediatypeenum.cpp \
- $$PWD/directshowpin.cpp \
- $$PWD/directshowpinenum.cpp \
- $$PWD/directshowvideobuffer.cpp \
- $$PWD/directshowutils.cpp \
- $$PWD/directshowvideoprobecontrol.cpp \
- $$PWD/directshowaudioprobecontrol.cpp \
- $$PWD/directshowsamplegrabber.cpp
diff --git a/src/plugins/directshow/common/directshowaudioprobecontrol.cpp b/src/plugins/directshow/common/directshowaudioprobecontrol.cpp
deleted file mode 100644
index 55ef70178..000000000
--- a/src/plugins/directshow/common/directshowaudioprobecontrol.cpp
+++ /dev/null
@@ -1,57 +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 "directshowaudioprobecontrol.h"
-#include "directshowglobal.h"
-
-QT_BEGIN_NAMESPACE
-
-DirectShowAudioProbeControl::DirectShowAudioProbeControl(QObject *p)
- : QMediaAudioProbeControl(p)
-{
-
-}
-
-DirectShowAudioProbeControl::~DirectShowAudioProbeControl()
-{
- if (m_ref.deref())
- qCWarning(qtDirectShowPlugin, "QAudioProbe control destroyed while it's still being referenced!!!");
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/common/directshowaudioprobecontrol.h b/src/plugins/directshow/common/directshowaudioprobecontrol.h
deleted file mode 100644
index 034e958fd..000000000
--- a/src/plugins/directshow/common/directshowaudioprobecontrol.h
+++ /dev/null
@@ -1,64 +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 DIRECTSHOWAUDIOPROBECONTROL_H
-#define DIRECTSHOWAUDIOPROBECONTROL_H
-
-#include <qmediaaudioprobecontrol.h>
-
-#include <QtCore/qglobal.h>
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowAudioProbeControl : public QMediaAudioProbeControl
-{
- Q_OBJECT
-public:
- explicit DirectShowAudioProbeControl(QObject *p = nullptr);
- ~DirectShowAudioProbeControl() override;
-
- bool ref() { return m_ref.ref(); }
- bool deref() { return m_ref.deref(); }
-private:
- QAtomicInt m_ref;
-};
-
-QT_END_NAMESPACE
-
-#endif // DIRECTSHOWAUDIOPROBECONTROL_H
diff --git a/src/plugins/directshow/common/directshowbasefilter.cpp b/src/plugins/directshow/common/directshowbasefilter.cpp
deleted file mode 100644
index 6099981cf..000000000
--- a/src/plugins/directshow/common/directshowbasefilter.cpp
+++ /dev/null
@@ -1,251 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "directshowbasefilter.h"
-
-#include "directshowpinenum.h"
-
-#include <mutex>
-
-QT_BEGIN_NAMESPACE
-
-DirectShowBaseFilter::DirectShowBaseFilter()
- = default;
-
-DirectShowBaseFilter::~DirectShowBaseFilter()
-{
- if (m_clock) {
- m_clock->Release();
- m_clock = nullptr;
- }
-}
-
-HRESULT DirectShowBaseFilter::GetClassID(CLSID *pClassID)
-{
- *pClassID = CLSID_NULL;
- return S_OK;
-}
-
-HRESULT DirectShowBaseFilter::NotifyEvent(long eventCode, LONG_PTR eventParam1, LONG_PTR eventParam2)
-{
- IMediaEventSink *sink = m_sink;
- if (sink) {
- if (eventCode == EC_COMPLETE)
- eventParam2 = (LONG_PTR)(IBaseFilter*)this;
-
- return sink->Notify(eventCode, eventParam1, eventParam2);
- }
- return E_NOTIMPL;
-}
-
-HRESULT DirectShowBaseFilter::Run(REFERENCE_TIME tStart)
-{
- Q_UNUSED(tStart);
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- m_startTime = tStart;
-
- if (m_state == State_Stopped){
- HRESULT hr = Pause();
- if (FAILED(hr))
- return hr;
- }
-
- m_state = State_Running;
-
- return S_OK;
-}
-
-HRESULT DirectShowBaseFilter::Pause()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- if (m_state == State_Stopped) {
- const QList<DirectShowPin *> pinList = pins();
- for (DirectShowPin *pin : pinList) {
- if (pin->isConnected()) {
- HRESULT hr = pin->setActive(true);
- if (FAILED(hr))
- return hr;
- }
- }
- }
-
- m_state = State_Paused;
-
- return S_OK;
-}
-
-HRESULT DirectShowBaseFilter::Stop()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- HRESULT hr = S_OK;
-
- if (m_state != State_Stopped) {
- const QList<DirectShowPin *> pinList = pins();
- for (DirectShowPin *pin : pinList) {
- if (pin->isConnected()) {
- HRESULT hrTmp = pin->setActive(false);
- if (FAILED(hrTmp) && SUCCEEDED(hr))
- hr = hrTmp;
- }
- }
- }
-
- m_state = State_Stopped;
-
- return hr;
-}
-
-HRESULT DirectShowBaseFilter::GetState(DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
-{
- Q_UNUSED(dwMilliSecsTimeout);
-
- if (!pState) {
- return E_POINTER;
- } else {
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- *pState = m_state;
-
- return S_OK;
- }
-}
-
-HRESULT DirectShowBaseFilter::SetSyncSource(IReferenceClock *pClock)
-{
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- if (m_clock)
- m_clock->Release();
-
- m_clock = pClock;
-
- if (m_clock)
- m_clock->AddRef();
-
- return S_OK;
-}
-
-HRESULT DirectShowBaseFilter::GetSyncSource(IReferenceClock **ppClock)
-{
- if (!ppClock)
- return E_POINTER;
-
- if (!m_clock) {
- *ppClock = nullptr;
- return S_FALSE;
- }
- m_clock->AddRef();
- *ppClock = m_clock;
- return S_OK;
-}
-
-HRESULT DirectShowBaseFilter::EnumPins(IEnumPins **ppEnum)
-{
- if (!ppEnum)
- return E_POINTER;
- *ppEnum = new DirectShowPinEnum(this);
- return S_OK;
-}
-
-HRESULT DirectShowBaseFilter::FindPin(LPCWSTR Id, IPin **ppPin)
-{
- if (!ppPin || !Id)
- return E_POINTER;
-
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
- const QList<DirectShowPin *> pinList = pins();
- for (DirectShowPin *pin : pinList) {
- if (pin->name() == QStringView(Id)) {
- pin->AddRef();
- *ppPin = pin;
- return S_OK;
- }
- }
-
- *ppPin = nullptr;
- return VFW_E_NOT_FOUND;
-}
-
-HRESULT DirectShowBaseFilter::JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName)
-{
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- m_filterName = QString::fromWCharArray(pName);
- m_graph = pGraph;
- m_sink = nullptr;
-
- if (m_graph) {
- if (SUCCEEDED(m_graph->QueryInterface(IID_PPV_ARGS(&m_sink))))
- m_sink->Release(); // we don't keep a reference on it
- }
-
- return S_OK;
-}
-
-HRESULT DirectShowBaseFilter::QueryFilterInfo(FILTER_INFO *pInfo)
-{
- if (!pInfo)
- return E_POINTER;
-
- QString name = m_filterName;
-
- if (name.length() >= MAX_FILTER_NAME)
- name.truncate(MAX_FILTER_NAME - 1);
-
- int length = name.toWCharArray(pInfo->achName);
- pInfo->achName[length] = '\0';
-
- if (m_graph)
- m_graph->AddRef();
-
- pInfo->pGraph = m_graph;
-
- return S_OK;
-}
-
-HRESULT DirectShowBaseFilter::QueryVendorInfo(LPWSTR *pVendorInfo)
-{
- Q_UNUSED(pVendorInfo);
- return E_NOTIMPL;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/common/directshowbasefilter.h b/src/plugins/directshow/common/directshowbasefilter.h
deleted file mode 100644
index ce30891d7..000000000
--- a/src/plugins/directshow/common/directshowbasefilter.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 Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 DIRECTSHOWBASEFILTER_H
-#define DIRECTSHOWBASEFILTER_H
-
-#include "directshowpin.h"
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowBaseFilter : public IBaseFilter
-{
-public:
- DirectShowBaseFilter();
- virtual ~DirectShowBaseFilter();
-
- FILTER_STATE state() const { return m_state; }
- HRESULT NotifyEvent(long eventCode, LONG_PTR eventParam1, LONG_PTR eventParam2);
-
- virtual QList<DirectShowPin *> pins() = 0;
-
- // IPersist
- STDMETHODIMP GetClassID(CLSID *pClassID) override;
-
- // IMediaFilter
- STDMETHODIMP Run(REFERENCE_TIME tStart) override;
- STDMETHODIMP Pause() override;
- STDMETHODIMP Stop() override;
-
- STDMETHODIMP GetState(DWORD dwMilliSecsTimeout, FILTER_STATE *pState) override;
-
- STDMETHODIMP SetSyncSource(IReferenceClock *pClock) override;
- STDMETHODIMP GetSyncSource(IReferenceClock **ppClock) override;
-
- // IBaseFilter
- STDMETHODIMP EnumPins(IEnumPins **ppEnum) override;
- STDMETHODIMP FindPin(LPCWSTR Id, IPin **ppPin) override;
-
- STDMETHODIMP JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName) override;
-
- STDMETHODIMP QueryFilterInfo(FILTER_INFO *pInfo) override;
- STDMETHODIMP QueryVendorInfo(LPWSTR *pVendorInfo) override;
-
-protected:
- QRecursiveMutex m_mutex;
- FILTER_STATE m_state = State_Stopped;
- IFilterGraph *m_graph = nullptr;
- IReferenceClock *m_clock = nullptr;
- IMediaEventSink *m_sink = nullptr;
- QString m_filterName;
- REFERENCE_TIME m_startTime = 0;
-
-private:
- Q_DISABLE_COPY(DirectShowBaseFilter)
-};
-
-QT_END_NAMESPACE
-
-#endif // DIRECTSHOWBASEFILTER_H
diff --git a/src/plugins/directshow/common/directshoweventloop.cpp b/src/plugins/directshow/common/directshoweventloop.cpp
deleted file mode 100644
index 692c873cf..000000000
--- a/src/plugins/directshow/common/directshoweventloop.cpp
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <directshoweventloop.h>
-
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qcoreevent.h>
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowPostedEvent
-{
- Q_DISABLE_COPY(DirectShowPostedEvent)
-public:
- DirectShowPostedEvent(QObject *receiver, QEvent *event)
- : receiver(receiver)
- , event(event)
- {
- }
-
- ~DirectShowPostedEvent()
- {
- delete event;
- }
-
- QObject *receiver;
- QEvent *event;
- DirectShowPostedEvent *next = nullptr;
-};
-
-DirectShowEventLoop::DirectShowEventLoop(QObject *parent)
- : QObject(parent)
- , m_eventHandle(::CreateEvent(nullptr, FALSE, FALSE, nullptr))
- , m_waitHandle(::CreateEvent(nullptr, FALSE, FALSE, nullptr))
-{
-}
-
-DirectShowEventLoop::~DirectShowEventLoop()
-{
- ::CloseHandle(m_eventHandle);
- ::CloseHandle(m_waitHandle);
-
- for (DirectShowPostedEvent *post = m_postsHead; post; post = m_postsHead) {
- m_postsHead = m_postsHead->next;
-
- delete post;
- }
-}
-
-void DirectShowEventLoop::wait(QMutex *mutex)
-{
- ::ResetEvent(m_waitHandle);
-
- mutex->unlock();
-
- HANDLE handles[] = { m_eventHandle, m_waitHandle };
- while (::WaitForMultipleObjects(2, handles, false, INFINITE) == WAIT_OBJECT_0)
- processEvents();
-
- mutex->lock();
-}
-
-void DirectShowEventLoop::wake()
-{
- ::SetEvent(m_waitHandle);
-}
-
-void DirectShowEventLoop::postEvent(QObject *receiver, QEvent *event)
-{
- QMutexLocker locker(&m_mutex);
-
- DirectShowPostedEvent *post = new DirectShowPostedEvent(receiver, event);
-
- if (m_postsTail)
- m_postsTail->next = post;
- else
- m_postsHead = post;
-
- m_postsTail = post;
-
- QCoreApplication::postEvent(this, new QEvent(QEvent::User));
- ::SetEvent(m_eventHandle);
-}
-
-void DirectShowEventLoop::customEvent(QEvent *event)
-{
- if (event->type() == QEvent::User) {
- processEvents();
- } else {
- QObject::customEvent(event);
- }
-}
-
-void DirectShowEventLoop::processEvents()
-{
- QMutexLocker locker(&m_mutex);
-
- ::ResetEvent(m_eventHandle);
-
- while(m_postsHead) {
- DirectShowPostedEvent *post = m_postsHead;
- m_postsHead = m_postsHead->next;
-
- if (!m_postsHead)
- m_postsTail = nullptr;
-
- locker.unlock();
- QCoreApplication::sendEvent(post->receiver, post->event);
- delete post;
- locker.relock();
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/common/directshoweventloop.h b/src/plugins/directshow/common/directshoweventloop.h
deleted file mode 100644
index 984bd23a4..000000000
--- a/src/plugins/directshow/common/directshoweventloop.h
+++ /dev/null
@@ -1,80 +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 DIRECTSHOWEVENTLOOP_H
-#define DIRECTSHOWEVENTLOOP_H
-
-#include <QtCore/qmutex.h>
-#include <QtCore/qobject.h>
-#include <QtCore/qwaitcondition.h>
-
-#include <qt_windows.h>
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowPostedEvent;
-
-class DirectShowEventLoop : public QObject
-{
- Q_OBJECT
-public:
- DirectShowEventLoop(QObject *parent = nullptr);
- ~DirectShowEventLoop() override;
-
- void wait(QMutex *mutex);
- void wake();
-
- void postEvent(QObject *object, QEvent *event);
-
-protected:
- void customEvent(QEvent *event) override;
-
-private:
- void processEvents();
-
- DirectShowPostedEvent *m_postsHead = nullptr;
- DirectShowPostedEvent *m_postsTail = nullptr;
- HANDLE m_eventHandle;
- HANDLE m_waitHandle;
- QMutex m_mutex;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/directshow/common/directshowglobal.h b/src/plugins/directshow/common/directshowglobal.h
deleted file mode 100644
index 12693e4f1..000000000
--- a/src/plugins/directshow/common/directshowglobal.h
+++ /dev/null
@@ -1,160 +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 DIRECTSHOWGLOBAL_H
-#define DIRECTSHOWGLOBAL_H
-
-#include <dshow.h>
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qloggingcategory.h>
-
-QT_BEGIN_NAMESPACE
-
-Q_DECLARE_LOGGING_CATEGORY(qtDirectShowPlugin)
-
-QT_END_NAMESPACE
-
-template <typename T> T *com_cast(IUnknown *unknown, const IID &iid)
-{
- T *iface = nullptr;
- return unknown && unknown->QueryInterface(iid, reinterpret_cast<void **>(&iface)) == S_OK
- ? iface
- : nullptr;
-}
-
-template <typename T> T *com_new(const IID &clsid)
-{
- T *object = nullptr;
- return CoCreateInstance(
- clsid,
- nullptr,
- CLSCTX_INPROC_SERVER,
- IID_PPV_ARGS(&object)) == S_OK
- ? object
- : nullptr;
-}
-
-template <typename T> T *com_new(const IID &clsid, const IID &iid)
-{
- T *object = nullptr;
- return CoCreateInstance(
- clsid,
- nullptr,
- CLSCTX_INPROC_SERVER,
- iid,
- reinterpret_cast<void **>(&object)) == S_OK
- ? object
- : nullptr;
-}
-
-DEFINE_GUID(MEDIASUBTYPE_I420,
- 0x30323449,0x0000,0x0010,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71);
-
-#ifndef __IFilterGraph2_INTERFACE_DEFINED__
-#define __IFilterGraph2_INTERFACE_DEFINED__
-#define INTERFACE IFilterGraph2
-DECLARE_INTERFACE_(IFilterGraph2 ,IGraphBuilder)
-{
- STDMETHOD(AddSourceFilterForMoniker)(THIS_ IMoniker *, IBindCtx *, LPCWSTR,IBaseFilter **) PURE;
- STDMETHOD(ReconnectEx)(THIS_ IPin *, const AM_MEDIA_TYPE *) PURE;
- STDMETHOD(RenderEx)(IPin *, DWORD, DWORD *) PURE;
-};
-#undef INTERFACE
-#endif
-
-#ifndef __IAMFilterMiscFlags_INTERFACE_DEFINED__
-#define __IAMFilterMiscFlags_INTERFACE_DEFINED__
-#define INTERFACE IAMFilterMiscFlags
-DECLARE_INTERFACE_(IAMFilterMiscFlags ,IUnknown)
-{
- STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE;
- STDMETHOD_(ULONG,AddRef)(THIS) PURE;
- STDMETHOD_(ULONG,Release)(THIS) PURE;
- STDMETHOD_(ULONG,GetMiscFlags)(THIS) PURE;
-};
-#undef INTERFACE
-#endif
-
-#ifndef __IFileSourceFilter_INTERFACE_DEFINED__
-#define __IFileSourceFilter_INTERFACE_DEFINED__
-#define INTERFACE IFileSourceFilter
-DECLARE_INTERFACE_(IFileSourceFilter ,IUnknown)
-{
- STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE;
- STDMETHOD_(ULONG,AddRef)(THIS) PURE;
- STDMETHOD_(ULONG,Release)(THIS) PURE;
- STDMETHOD(Load)(THIS_ LPCOLESTR, const AM_MEDIA_TYPE *) PURE;
- STDMETHOD(GetCurFile)(THIS_ LPOLESTR *ppszFileName, AM_MEDIA_TYPE *) PURE;
-};
-#undef INTERFACE
-#endif
-
-#ifndef __IAMOpenProgress_INTERFACE_DEFINED__
-#define __IAMOpenProgress_INTERFACE_DEFINED__
-#undef INTERFACE
-#define INTERFACE IAMOpenProgress
-DECLARE_INTERFACE_(IAMOpenProgress ,IUnknown)
-{
- STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE;
- STDMETHOD_(ULONG,AddRef)(THIS) PURE;
- STDMETHOD_(ULONG,Release)(THIS) PURE;
- STDMETHOD(QueryProgress)(THIS_ LONGLONG *, LONGLONG *) PURE;
- STDMETHOD(AbortOperation)(THIS) PURE;
-};
-#undef INTERFACE
-#endif
-
-#ifndef __IFilterChain_INTERFACE_DEFINED__
-#define __IFilterChain_INTERFACE_DEFINED__
-#define INTERFACE IFilterChain
-DECLARE_INTERFACE_(IFilterChain ,IUnknown)
-{
- STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE;
- STDMETHOD_(ULONG,AddRef)(THIS) PURE;
- STDMETHOD_(ULONG,Release)(THIS) PURE;
- STDMETHOD(StartChain)(IBaseFilter *, IBaseFilter *) PURE;
- STDMETHOD(PauseChain)(IBaseFilter *, IBaseFilter *) PURE;
- STDMETHOD(StopChain)(IBaseFilter *, IBaseFilter *) PURE;
- STDMETHOD(RemoveChain)(IBaseFilter *, IBaseFilter *) PURE;
-};
-#undef INTERFACE
-#endif
-
-#endif
diff --git a/src/plugins/directshow/common/directshowmediatype.cpp b/src/plugins/directshow/common/directshowmediatype.cpp
deleted file mode 100644
index 3429f4848..000000000
--- a/src/plugins/directshow/common/directshowmediatype.cpp
+++ /dev/null
@@ -1,352 +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 "directshowmediatype.h"
-#include "directshowglobal.h"
-
-#include <initguid.h>
-
-DEFINE_GUID(MEDIASUBTYPE_Y800, 0x30303859, 0x0000, 0x0010, 0x80, 0x00,
- 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
-
-namespace
-{
- struct TypeLookup
- {
- QVideoFrame::PixelFormat pixelFormat;
- GUID mediaType;
- };
-
- static const TypeLookup qt_typeLookup[] =
- {
- { QVideoFrame::Format_ARGB32, MEDIASUBTYPE_ARGB32 },
- { QVideoFrame::Format_RGB32, MEDIASUBTYPE_RGB32 },
- { QVideoFrame::Format_BGR24, MEDIASUBTYPE_RGB24 },
- { QVideoFrame::Format_RGB565, MEDIASUBTYPE_RGB565 },
- { QVideoFrame::Format_RGB555, MEDIASUBTYPE_RGB555 },
- { QVideoFrame::Format_AYUV444, MEDIASUBTYPE_AYUV },
- { QVideoFrame::Format_YUYV, MEDIASUBTYPE_YUY2 },
- { QVideoFrame::Format_UYVY, MEDIASUBTYPE_UYVY },
- { QVideoFrame::Format_IMC1, MEDIASUBTYPE_IMC1 },
- { QVideoFrame::Format_IMC2, MEDIASUBTYPE_IMC2 },
- { QVideoFrame::Format_IMC3, MEDIASUBTYPE_IMC3 },
- { QVideoFrame::Format_IMC4, MEDIASUBTYPE_IMC4 },
- { QVideoFrame::Format_YV12, MEDIASUBTYPE_YV12 },
- { QVideoFrame::Format_NV12, MEDIASUBTYPE_NV12 },
- { QVideoFrame::Format_YUV420P, MEDIASUBTYPE_IYUV },
- { QVideoFrame::Format_YUV420P, MEDIASUBTYPE_I420 },
- { QVideoFrame::Format_Jpeg, MEDIASUBTYPE_MJPG },
- { QVideoFrame::Format_Y8, MEDIASUBTYPE_Y800 },
- };
-}
-
-bool DirectShowMediaType::isPartiallySpecified(const AM_MEDIA_TYPE *mediaType)
-{
- return mediaType->majortype == GUID_NULL || mediaType->formattype == GUID_NULL;
-}
-
-DirectShowMediaType::DirectShowMediaType()
- : mediaType({ GUID_NULL, GUID_NULL, TRUE, FALSE, 1, GUID_NULL, nullptr, 0, nullptr})
-{
-}
-
-DirectShowMediaType::DirectShowMediaType(const AM_MEDIA_TYPE &type)
- : DirectShowMediaType()
-{
- copy(&mediaType, &type);
-}
-
-DirectShowMediaType::DirectShowMediaType(AM_MEDIA_TYPE &&type)
- : DirectShowMediaType()
-{
- move(&mediaType, type);
-}
-
-DirectShowMediaType::DirectShowMediaType(const DirectShowMediaType &other)
- : DirectShowMediaType()
-{
- copy(&mediaType, &other.mediaType);
-}
-
-DirectShowMediaType::DirectShowMediaType(DirectShowMediaType &&other) noexcept
- : DirectShowMediaType()
-{
- move(&mediaType, other.mediaType);
-}
-
-DirectShowMediaType &DirectShowMediaType::operator=(const DirectShowMediaType &other)
-{
- copy(&mediaType, &other.mediaType);
- return *this;
-}
-
-DirectShowMediaType &DirectShowMediaType::operator=(DirectShowMediaType &&other) noexcept
-{
- move(&mediaType, other.mediaType);
- return *this;
-}
-
-void DirectShowMediaType::init(AM_MEDIA_TYPE *type)
-{
- Q_ASSERT(type);
- SecureZeroMemory(reinterpret_cast<void *>(type), sizeof(AM_MEDIA_TYPE));
- type->lSampleSize = 1;
- type->bFixedSizeSamples = TRUE;
-}
-
-void DirectShowMediaType::copy(AM_MEDIA_TYPE *target, const AM_MEDIA_TYPE *source)
-{
- if (!(target && source))
- return;
-
- if (target == source)
- return;
-
- clear(*target);
-
- copyToUninitialized(target, source);
-}
-
-void DirectShowMediaType::copyToUninitialized(AM_MEDIA_TYPE *target, const AM_MEDIA_TYPE *source)
-{
- *target = *source;
-
- if (source->cbFormat > 0) {
- target->pbFormat = reinterpret_cast<PBYTE>(CoTaskMemAlloc(source->cbFormat));
- memcpy(target->pbFormat, source->pbFormat, source->cbFormat);
- }
- if (target->pUnk)
- target->pUnk->AddRef();
-}
-
-void DirectShowMediaType::move(AM_MEDIA_TYPE *target, AM_MEDIA_TYPE **source)
-{
- if (!target || !source || !(*source))
- return;
-
- if (target == *source)
- return;
-
- clear(*target);
- *target = *(*source);
- SecureZeroMemory(reinterpret_cast<void *>(*source), sizeof(AM_MEDIA_TYPE));
- *source = nullptr;
-}
-
-void DirectShowMediaType::move(AM_MEDIA_TYPE *target, AM_MEDIA_TYPE &source)
-{
- AM_MEDIA_TYPE *srcPtr = &source;
- move(target, &srcPtr);
-}
-
-/**
- * @brief DirectShowMediaType::deleteType - Used for AM_MEDIA_TYPE structures that have
- * been allocated by CoTaskMemAlloc or CreateMediaType.
- * @param type
- */
-void DirectShowMediaType::deleteType(AM_MEDIA_TYPE *type)
-{
- if (!type)
- return;
-
- clear(*type);
- CoTaskMemFree(type);
-}
-
-bool DirectShowMediaType::isCompatible(const AM_MEDIA_TYPE *a, const AM_MEDIA_TYPE *b)
-{
- if (b->majortype != GUID_NULL && a->majortype != b->majortype)
- return false;
-
- if (b->subtype != GUID_NULL && a->subtype != b->subtype)
- return false;
-
- if (b->formattype != GUID_NULL) {
- if (a->formattype != b->formattype)
- return false;
- if (a->cbFormat != b->cbFormat)
- return false;
- if (a->cbFormat != 0 && memcmp(a->pbFormat, b->pbFormat, a->cbFormat) != 0)
- return false;
- }
-
- return true;
-}
-
-/**
- * @brief DirectShowMediaType::clear - Clears all member data, and releases allocated buffers.
- * Use this to release automatic AM_MEDIA_TYPE structures.
- * @param type
- */
-void DirectShowMediaType::clear(AM_MEDIA_TYPE &type)
-{
- if (type.cbFormat > 0)
- CoTaskMemFree(type.pbFormat);
-
- if (type.pUnk)
- type.pUnk->Release();
-
- SecureZeroMemory(&type, sizeof(type));
-}
-
-
-GUID DirectShowMediaType::convertPixelFormat(QVideoFrame::PixelFormat format)
-{
- for (const auto &lookupType : qt_typeLookup) {
- if (lookupType.pixelFormat == format)
- return lookupType.mediaType;
- }
-
- return MEDIASUBTYPE_None;
-}
-
-QVideoSurfaceFormat DirectShowMediaType::videoFormatFromType(const AM_MEDIA_TYPE *type)
-{
- if (!type)
- return QVideoSurfaceFormat();
-
- for (const auto &lookupType : qt_typeLookup) {
- if (IsEqualGUID(lookupType.mediaType, type->subtype) && type->cbFormat > 0) {
- if (IsEqualGUID(type->formattype, FORMAT_VideoInfo)) {
- VIDEOINFOHEADER *header = reinterpret_cast<VIDEOINFOHEADER *>(type->pbFormat);
-
- QVideoSurfaceFormat format(
- QSize(header->bmiHeader.biWidth, qAbs(header->bmiHeader.biHeight)),
- lookupType.pixelFormat);
-
- if (header->AvgTimePerFrame > 0)
- format.setFrameRate(10000 /header->AvgTimePerFrame);
-
- format.setScanLineDirection(scanLineDirection(format.pixelFormat(), header->bmiHeader));
-
- return format;
- }
- if (IsEqualGUID(type->formattype, FORMAT_VideoInfo2)) {
- VIDEOINFOHEADER2 *header = reinterpret_cast<VIDEOINFOHEADER2 *>(type->pbFormat);
-
- QVideoSurfaceFormat format(
- QSize(header->bmiHeader.biWidth, qAbs(header->bmiHeader.biHeight)),
- lookupType.pixelFormat);
-
- if (header->AvgTimePerFrame > 0)
- format.setFrameRate(10000 / header->AvgTimePerFrame);
-
- format.setScanLineDirection(scanLineDirection(format.pixelFormat(), header->bmiHeader));
-
- return format;
- }
- }
- }
- return QVideoSurfaceFormat();
-}
-
-QVideoFrame::PixelFormat DirectShowMediaType::pixelFormatFromType(const AM_MEDIA_TYPE *type)
-{
- if (!type)
- return QVideoFrame::Format_Invalid;
-
- for (const auto &lookupType : qt_typeLookup) {
- if (IsEqualGUID(lookupType.mediaType, type->subtype))
- return lookupType.pixelFormat;
- }
-
- return QVideoFrame::Format_Invalid;
-}
-
-#define PAD_TO_DWORD(x) (((x) + 3) & ~3)
-int DirectShowMediaType::bytesPerLine(const QVideoSurfaceFormat &format)
-{
- switch (format.pixelFormat()) {
- // 32 bpp packed formats.
- case QVideoFrame::Format_ARGB32:
- case QVideoFrame::Format_RGB32:
- case QVideoFrame::Format_AYUV444:
- return format.frameWidth() * 4;
- // 24 bpp packed formats.
- case QVideoFrame::Format_RGB24:
- case QVideoFrame::Format_BGR24:
- return PAD_TO_DWORD(format.frameWidth() * 3);
- // 16 bpp packed formats.
- case QVideoFrame::Format_RGB565:
- case QVideoFrame::Format_RGB555:
- case QVideoFrame::Format_YUYV:
- case QVideoFrame::Format_UYVY:
- return PAD_TO_DWORD(format.frameWidth() * 2);
- // Planar formats.
- case QVideoFrame::Format_YV12:
- case QVideoFrame::Format_YUV420P:
- case QVideoFrame::Format_IMC1:
- case QVideoFrame::Format_IMC2:
- case QVideoFrame::Format_IMC3:
- case QVideoFrame::Format_IMC4:
- case QVideoFrame::Format_NV12:
- return format.frameWidth();
- default:
- return 0;
- }
-}
-
-QVideoSurfaceFormat::Direction DirectShowMediaType::scanLineDirection(QVideoFrame::PixelFormat pixelFormat, const BITMAPINFOHEADER &bmiHeader)
-{
- /* MSDN http://msdn.microsoft.com/en-us/library/windows/desktop/dd318229(v=vs.85).aspx */
- /* For uncompressed RGB bitmaps:
- * if biHeight is positive, the bitmap is a bottom-up DIB with the origin at the lower left corner.
- * If biHeight is negative, the bitmap is a top-down DIB with the origin at the upper left corner.
- *
- * For YUV bitmaps:
- * the bitmap is always top-down, regardless of the sign of biHeight.
- * Decoders should offer YUV formats with postive biHeight, but for backward compatibility they should accept YUV formats with either positive or negative biHeight.
- *
- * For compressed formats:
- * biHeight must be positive, regardless of image orientation.
- */
- switch (pixelFormat)
- {
- case QVideoFrame::Format_ARGB32:
- case QVideoFrame::Format_RGB32:
- case QVideoFrame::Format_RGB24:
- case QVideoFrame::Format_RGB565:
- case QVideoFrame::Format_RGB555:
- return bmiHeader.biHeight < 0
- ? QVideoSurfaceFormat::TopToBottom
- : QVideoSurfaceFormat::BottomToTop;
- default:
- return QVideoSurfaceFormat::TopToBottom;
- }
-}
diff --git a/src/plugins/directshow/common/directshowmediatype.h b/src/plugins/directshow/common/directshowmediatype.h
deleted file mode 100644
index ee44329a5..000000000
--- a/src/plugins/directshow/common/directshowmediatype.h
+++ /dev/null
@@ -1,97 +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 DIRECTSHOWMEDIATYPE_H
-#define DIRECTSHOWMEDIATYPE_H
-
-#include <dshow.h>
-
-#include <qvideosurfaceformat.h>
-
-#include <dvdmedia.h>
-#include <QtCore/qglobal.h>
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowMediaType
-{
-public:
- DirectShowMediaType();
- DirectShowMediaType(const DirectShowMediaType &other);
- DirectShowMediaType(DirectShowMediaType &&other) noexcept;
- explicit DirectShowMediaType(const AM_MEDIA_TYPE &type);
- explicit DirectShowMediaType(AM_MEDIA_TYPE &&type);
- ~DirectShowMediaType() { clear(mediaType); }
-
- DirectShowMediaType &operator =(const DirectShowMediaType &other);
- DirectShowMediaType &operator =(DirectShowMediaType &&other) noexcept;
-
- void clear() { clear(mediaType); }
-
- inline AM_MEDIA_TYPE *operator &() Q_DECL_NOTHROW { return &mediaType; }
- inline AM_MEDIA_TYPE *operator ->() Q_DECL_NOTHROW { return &mediaType; }
-
- inline const AM_MEDIA_TYPE *operator &() const Q_DECL_NOTHROW { return &mediaType; }
- inline const AM_MEDIA_TYPE *operator ->() const Q_DECL_NOTHROW { return &mediaType; }
-
- static void init(AM_MEDIA_TYPE *type);
- static void copy(AM_MEDIA_TYPE *target, const AM_MEDIA_TYPE *source);
- static void copyToUninitialized(AM_MEDIA_TYPE *target, const AM_MEDIA_TYPE *source);
- static void move(AM_MEDIA_TYPE *target, AM_MEDIA_TYPE **source);
- static void move(AM_MEDIA_TYPE *target, AM_MEDIA_TYPE &source);
- static void clear(AM_MEDIA_TYPE &type);
- static void deleteType(AM_MEDIA_TYPE *type);
- static bool isPartiallySpecified(const AM_MEDIA_TYPE *mediaType);
- static bool isCompatible(const AM_MEDIA_TYPE *a, const AM_MEDIA_TYPE *b);
- static GUID convertPixelFormat(QVideoFrame::PixelFormat format);
-
- static QVideoSurfaceFormat videoFormatFromType(const AM_MEDIA_TYPE *type);
- static QVideoFrame::PixelFormat pixelFormatFromType(const AM_MEDIA_TYPE *type);
- static int bytesPerLine(const QVideoSurfaceFormat &format);
- static QVideoSurfaceFormat::Direction scanLineDirection(QVideoFrame::PixelFormat pixelFormat, const BITMAPINFOHEADER &bmiHeader);
-
-private:
- AM_MEDIA_TYPE mediaType;
-};
-
-Q_DECLARE_TYPEINFO(DirectShowMediaType, Q_MOVABLE_TYPE);
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/directshow/common/directshowmediatypeenum.cpp b/src/plugins/directshow/common/directshowmediatypeenum.cpp
deleted file mode 100644
index 02281bb98..000000000
--- a/src/plugins/directshow/common/directshowmediatypeenum.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "directshowmediatypeenum.h"
-
-#include "directshowpin.h"
-
-DirectShowMediaTypeEnum::DirectShowMediaTypeEnum(DirectShowPin *pin)
- : m_pin(pin)
- , m_mediaTypes(pin->supportedMediaTypes())
-{
- m_pin->AddRef();
-}
-
-DirectShowMediaTypeEnum::DirectShowMediaTypeEnum(const QList<DirectShowMediaType> &types)
- : m_mediaTypes(types)
-{
-}
-
-DirectShowMediaTypeEnum::~DirectShowMediaTypeEnum()
-{
- if (m_pin)
- m_pin->Release();
-}
-
-HRESULT DirectShowMediaTypeEnum::QueryInterface(REFIID riid, void **ppv)
-{
- if (ppv == nullptr)
- return E_POINTER;
- if (riid == IID_IUnknown)
- *ppv = static_cast<IUnknown *>(this);
- else if (riid == IID_IEnumMediaTypes)
- *ppv = static_cast<IEnumMediaTypes *>(this);
- else
- return E_NOINTERFACE;
- AddRef();
- return S_OK;
-}
-
-HRESULT DirectShowMediaTypeEnum::Next(ULONG cMediaTypes, AM_MEDIA_TYPE **ppMediaTypes, ULONG *pcFetched)
-{
- if (!ppMediaTypes || (!pcFetched && cMediaTypes != 1))
- return E_POINTER;
-
- ULONG count = qBound<ULONG>(0, cMediaTypes, m_mediaTypes.count() - m_index);
-
- for (ULONG i = 0; i < count; ++i, ++m_index) {
- ppMediaTypes[i] = reinterpret_cast<AM_MEDIA_TYPE *>(CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE)));
- DirectShowMediaType::copyToUninitialized(ppMediaTypes[i], &m_mediaTypes.at(m_index));
- }
-
- if (pcFetched)
- *pcFetched = count;
-
- return count == cMediaTypes ? S_OK : S_FALSE;
-}
-
-HRESULT DirectShowMediaTypeEnum::Skip(ULONG cMediaTypes)
-{
- m_index = qMin(int(m_index + cMediaTypes), m_mediaTypes.count());
- return m_index < m_mediaTypes.count() ? S_OK : S_FALSE;
-}
-
-HRESULT DirectShowMediaTypeEnum::Reset()
-{
- m_index = 0;
- return S_OK;
-}
-
-HRESULT DirectShowMediaTypeEnum::Clone(IEnumMediaTypes **ppEnum)
-{
- if (!ppEnum)
- return E_POINTER;
- *ppEnum = m_pin ? new DirectShowMediaTypeEnum(m_pin) : new DirectShowMediaTypeEnum(m_mediaTypes);
- return S_OK;
-}
-
diff --git a/src/plugins/directshow/common/directshowmediatypeenum.h b/src/plugins/directshow/common/directshowmediatypeenum.h
deleted file mode 100644
index a5c347004..000000000
--- a/src/plugins/directshow/common/directshowmediatypeenum.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef DIRECTSHOWMEDIATYPEENUM_H
-#define DIRECTSHOWMEDIATYPEENUM_H
-
-#include "directshowobject.h"
-#include <qlist.h>
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowPin;
-class DirectShowMediaType;
-
-class DirectShowMediaTypeEnum : public IEnumMediaTypes
-{
- COM_REF_MIXIN
-public:
- DirectShowMediaTypeEnum(DirectShowPin *pin);
- DirectShowMediaTypeEnum(const QList<DirectShowMediaType> &types);
- virtual ~DirectShowMediaTypeEnum();
-
- STDMETHODIMP QueryInterface(REFIID riid, void **ppv) override;
-
- // IEnumMediaTypes
- STDMETHODIMP Next(ULONG cMediaTypes, AM_MEDIA_TYPE **ppMediaTypes, ULONG *pcFetched) override;
- STDMETHODIMP Skip(ULONG cMediaTypes) override;
- STDMETHODIMP Reset() override;
- STDMETHODIMP Clone(IEnumMediaTypes **ppEnum) override;
-
-private:
- Q_DISABLE_COPY(DirectShowMediaTypeEnum)
-
- DirectShowPin *m_pin = nullptr;
- QList<DirectShowMediaType> m_mediaTypes;
- int m_index = 0;
-};
-
-QT_END_NAMESPACE
-
-#endif // DIRECTSHOWMEDIATYPEENUM_H
diff --git a/src/plugins/directshow/common/directshowobject.h b/src/plugins/directshow/common/directshowobject.h
deleted file mode 100644
index 5a8c05a3e..000000000
--- a/src/plugins/directshow/common/directshowobject.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef DIRECTSHOWOBJECT_H
-#define DIRECTSHOWOBJECT_H
-
-#include "directshowglobal.h"
-
-QT_BEGIN_NAMESPACE
-
-#define COM_REF_MIXIN \
- volatile ULONG m_ref = 1; \
-public: \
- STDMETHODIMP_(ULONG) AddRef() override \
- { \
- return InterlockedIncrement(&m_ref); \
- } \
- STDMETHODIMP_(ULONG) Release() override \
- { \
- const ULONG ref = InterlockedDecrement(&m_ref); \
- if (ref == 0) \
- delete this; \
- return ref; \
- }
-
-QT_END_NAMESPACE
-
-#endif // DIRECTSHOWOBJECT_H
diff --git a/src/plugins/directshow/common/directshowpin.cpp b/src/plugins/directshow/common/directshowpin.cpp
deleted file mode 100644
index d80b0b08e..000000000
--- a/src/plugins/directshow/common/directshowpin.cpp
+++ /dev/null
@@ -1,696 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "directshowpin.h"
-
-#include "directshowmediatype.h"
-#include "directshowbasefilter.h"
-#include "directshowmediatypeenum.h"
-
-#include <qdebug.h>
-
-#include <mutex>
-
-QT_BEGIN_NAMESPACE
-
-DirectShowPin::DirectShowPin(DirectShowBaseFilter *filter, const QString &name, PIN_DIRECTION direction)
- : m_filter(filter)
- , m_name(name)
- , m_direction(direction)
-{
-}
-
-DirectShowPin::~DirectShowPin() = default;
-
-HRESULT DirectShowPin::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
-{
- if (!pReceivePin)
- return E_POINTER;
-
- HRESULT hr = E_FAIL;
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- if (m_peerPin)
- return VFW_E_ALREADY_CONNECTED;
- if (m_filter->state() != State_Stopped)
- return VFW_E_NOT_STOPPED;
-
- PIN_DIRECTION pd;
- pReceivePin->QueryDirection(&pd);
- if (pd == m_direction)
- return VFW_E_INVALID_DIRECTION;
-
- if (pmt != nullptr && DirectShowMediaType::isPartiallySpecified(pmt)) {
- // If the type is fully specified, use it
- hr = tryConnect(pReceivePin, pmt);
- } else {
- IEnumMediaTypes *enumMediaTypes = nullptr;
-
- // First, try the receiving pin's preferred types
- if (SUCCEEDED(pReceivePin->EnumMediaTypes(&enumMediaTypes))) {
- hr = tryMediaTypes(pReceivePin, pmt, enumMediaTypes);
- enumMediaTypes->Release();
- }
- // Then, try this pin's preferred types
- if (FAILED(hr) && SUCCEEDED(EnumMediaTypes(&enumMediaTypes))) {
- hr = tryMediaTypes(pReceivePin, pmt, enumMediaTypes);
- enumMediaTypes->Release();
- }
- }
-
- if (FAILED(hr)) {
- return ((hr != E_FAIL) && (hr != E_INVALIDARG) && (hr != VFW_E_TYPE_NOT_ACCEPTED))
- ? hr : VFW_E_NO_ACCEPTABLE_TYPES;
- }
-
- return S_OK;
-}
-
-HRESULT DirectShowPin::tryMediaTypes(IPin *pin, const AM_MEDIA_TYPE *partialType, IEnumMediaTypes *enumMediaTypes)
-{
- HRESULT hr = enumMediaTypes->Reset();
- if (FAILED(hr))
- return hr;
-
- AM_MEDIA_TYPE *mediaType = nullptr;
- ULONG mediaCount = 0;
- HRESULT hrFailure = VFW_E_NO_ACCEPTABLE_TYPES;
-
- for (; enumMediaTypes->Next(1, &mediaType, &mediaCount) == S_OK;) {
-
- if (mediaType && (partialType == nullptr || DirectShowMediaType::isCompatible(mediaType, partialType))) {
- hr = tryConnect(pin, mediaType);
-
- if (FAILED(hr) && (hr != E_FAIL)
- && (hr != E_INVALIDARG)
- && (hr != VFW_E_TYPE_NOT_ACCEPTED)) {
- hrFailure = hr;
- }
- }
-
- if (mediaType)
- DirectShowMediaType::deleteType(mediaType);
-
- if (SUCCEEDED(hr))
- return S_OK;
- }
-
- return hrFailure;
-}
-
-HRESULT DirectShowPin::tryConnect(IPin *pin, const AM_MEDIA_TYPE *type)
-{
- if (!isMediaTypeSupported(type))
- return VFW_E_TYPE_NOT_ACCEPTED;
-
- m_peerPin = pin;
- m_peerPin->AddRef();
-
- HRESULT hr;
- if (!setMediaType(type)) {
- hr = VFW_E_TYPE_NOT_ACCEPTED;
- } else {
- hr = pin->ReceiveConnection(this, type);
- if (SUCCEEDED(hr)) {
- hr = completeConnection(pin);
- if (FAILED(hr))
- pin->Disconnect();
- }
- }
-
- if (FAILED(hr)) {
- connectionEnded();
- m_peerPin->Release();
- m_peerPin = nullptr;
- setMediaType(nullptr);
- return hr;
- }
-
- return S_OK;
-}
-
-HRESULT DirectShowPin::ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt)
-{
- if (!pConnector || !pmt)
- return E_POINTER;
-
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- if (m_peerPin)
- return VFW_E_ALREADY_CONNECTED;
- if (m_filter->state() != State_Stopped)
- return VFW_E_NOT_STOPPED;
-
- PIN_DIRECTION pd;
- pConnector->QueryDirection(&pd);
- if (pd == m_direction)
- return VFW_E_INVALID_DIRECTION;
-
- if (!isMediaTypeSupported(pmt))
- return VFW_E_TYPE_NOT_ACCEPTED;
-
- m_peerPin = pConnector;
- m_peerPin->AddRef();
-
- HRESULT hr;
- if (!setMediaType(pmt))
- hr = VFW_E_TYPE_NOT_ACCEPTED;
- else
- hr = completeConnection(pConnector);
-
- if (FAILED(hr)) {
- connectionEnded();
- m_peerPin->Release();
- m_peerPin = nullptr;
- setMediaType(nullptr);
- return hr;
- }
-
- return S_OK;
-}
-
-HRESULT DirectShowPin::Disconnect()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- if (m_filter->state() != State_Stopped)
- return VFW_E_NOT_STOPPED;
-
- if (m_peerPin) {
- HRESULT hr = connectionEnded();
- if (FAILED(hr))
- return hr;
-
- m_peerPin->Release();
- m_peerPin = nullptr;
-
- setMediaType(nullptr);
-
- return S_OK;
- }
-
- return S_FALSE;
-}
-
-HRESULT DirectShowPin::ConnectedTo(IPin **ppPin)
-{
- if (!ppPin)
- return E_POINTER;
-
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
- if (!m_peerPin) {
- *ppPin = nullptr;
- return VFW_E_NOT_CONNECTED;
- }
- m_peerPin->AddRef();
- *ppPin = m_peerPin;
- return S_OK;
-}
-
-HRESULT DirectShowPin::ConnectionMediaType(AM_MEDIA_TYPE *pmt)
-{
- if (!pmt)
- return E_POINTER;
-
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
- if (!m_peerPin) {
- DirectShowMediaType::init(pmt);
- return VFW_E_NOT_CONNECTED;
- }
- DirectShowMediaType::copy(pmt, &m_mediaType);
- return S_OK;
-}
-
-HRESULT DirectShowPin::QueryPinInfo(PIN_INFO *pInfo)
-{
- if (!pInfo)
- return E_POINTER;
-
- pInfo->pFilter = m_filter;
- if (m_filter)
- m_filter->AddRef();
- pInfo->dir = m_direction;
-
- QString name = m_name;
- if (name.length() >= MAX_PIN_NAME)
- name.truncate(MAX_PIN_NAME - 1);
- int length = name.toWCharArray(pInfo->achName);
- pInfo->achName[length] = '\0';
-
- return S_OK;
-}
-
-HRESULT DirectShowPin::QueryId(LPWSTR *Id)
-{
- if (!Id)
- return E_POINTER;
- const int bytes = (m_name.length() + 1) * 2;
- *Id = static_cast<LPWSTR>(::CoTaskMemAlloc(bytes));
- ::memcpy(*Id, m_name.utf16(), bytes);
- return S_OK;
-}
-
-HRESULT DirectShowPin::QueryAccept(const AM_MEDIA_TYPE *pmt)
-{
- if (!pmt)
- return E_POINTER;
-
- if (!isMediaTypeSupported(pmt))
- return S_FALSE;
-
- return S_OK;
-}
-
-HRESULT DirectShowPin::EnumMediaTypes(IEnumMediaTypes **ppEnum)
-{
- if (!ppEnum)
- return E_POINTER;
- *ppEnum = new DirectShowMediaTypeEnum(this);
- return S_OK;
-}
-
-HRESULT DirectShowPin::QueryInternalConnections(IPin **apPin, ULONG *nPin)
-{
- Q_UNUSED(apPin);
- Q_UNUSED(nPin);
- return E_NOTIMPL;
-}
-
-HRESULT DirectShowPin::EndOfStream()
-{
- return S_OK;
-}
-
-HRESULT DirectShowPin::BeginFlush()
-{
- return E_UNEXPECTED;
-}
-
-HRESULT DirectShowPin::EndFlush()
-{
- return E_UNEXPECTED;
-}
-
-HRESULT DirectShowPin::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
-{
- Q_UNUSED(tStart);
- Q_UNUSED(tStop);
- Q_UNUSED(dRate);
- return S_OK;
-}
-
-HRESULT DirectShowPin::QueryDirection(PIN_DIRECTION *pPinDir)
-{
- if (!pPinDir)
- return E_POINTER;
- *pPinDir = m_direction;
- return S_OK;
-}
-
-QList<DirectShowMediaType> DirectShowPin::supportedMediaTypes()
-{
- return QList<DirectShowMediaType>();
-}
-
-bool DirectShowPin::setMediaType(const AM_MEDIA_TYPE *type)
-{
- if (!type)
- m_mediaType.clear();
- else
- DirectShowMediaType::copy(&m_mediaType, type);
-
- return true;
-}
-
-HRESULT DirectShowPin::completeConnection(IPin *pin)
-{
- Q_UNUSED(pin);
- return S_OK;
-}
-
-HRESULT DirectShowPin::connectionEnded()
-{
- return S_OK;
-}
-
-HRESULT DirectShowPin::setActive(bool active)
-{
- Q_UNUSED(active);
- return S_OK;
-}
-
-
-/* DirectShowOutputPin */
-
-DirectShowOutputPin::DirectShowOutputPin(DirectShowBaseFilter *filter, const QString &name)
- : DirectShowPin(filter, name, PINDIR_OUTPUT)
-{
-
-}
-
-DirectShowOutputPin::~DirectShowOutputPin() = default;
-
-HRESULT DirectShowOutputPin::completeConnection(IPin *pin)
-{
- if (!pin)
- return E_POINTER;
-
- Q_ASSERT(m_inputPin == nullptr);
- Q_ASSERT(m_allocator == nullptr);
-
- HRESULT hr = pin->QueryInterface(IID_PPV_ARGS(&m_inputPin));
- if (FAILED(hr))
- return hr;
-
- ALLOCATOR_PROPERTIES prop;
- ZeroMemory(&prop, sizeof(prop));
- m_inputPin->GetAllocatorRequirements(&prop);
- if (prop.cBuffers <= 0)
- prop.cBuffers = 1;
- if (prop.cbBuffer <= 0)
- prop.cbBuffer = 1;
- if (prop.cbAlign <= 0)
- prop.cbAlign = 1;
-
- // Use the connected input pin's allocator if it has one
- hr = m_inputPin->GetAllocator(&m_allocator);
- if (SUCCEEDED(hr)) {
- ALLOCATOR_PROPERTIES actualProperties;
- hr = m_allocator->SetProperties(&prop, &actualProperties);
-
- if (SUCCEEDED(hr)) {
- hr = m_inputPin->NotifyAllocator(m_allocator, FALSE);
- if (SUCCEEDED(hr))
- return S_OK;
- }
-
- m_allocator->Release();
- m_allocator = nullptr;
- }
-
- // Otherwise, allocate its own allocator
- m_allocator = com_new<IMemAllocator>(CLSID_MemoryAllocator);
- if (!m_allocator) {
- hr = E_OUTOFMEMORY;
- } else {
- ALLOCATOR_PROPERTIES actualProperties;
- hr = m_allocator->SetProperties(&prop, &actualProperties);
-
- if (SUCCEEDED(hr)) {
- hr = m_inputPin->NotifyAllocator(m_allocator, FALSE);
- if (SUCCEEDED(hr))
- return S_OK;
- }
-
- m_allocator->Release();
- m_allocator = nullptr;
- }
-
- return hr;
-}
-
-HRESULT DirectShowOutputPin::connectionEnded()
-{
- if (m_allocator) {
- HRESULT hr = m_allocator->Decommit();
- if (FAILED(hr))
- return hr;
-
- m_allocator->Release();
- m_allocator = nullptr;
- }
-
- if (m_inputPin) {
- m_inputPin->Release();
- m_inputPin = nullptr;
- }
-
- return S_OK;
-}
-
-HRESULT DirectShowOutputPin::setActive(bool active)
-{
- if (!m_allocator)
- return VFW_E_NO_ALLOCATOR;
-
- return active ? m_allocator->Commit()
- : m_allocator->Decommit();
-}
-
-HRESULT DirectShowOutputPin::EndOfStream()
-{
- return E_UNEXPECTED;
-}
-
-
-/* DirectShowInputPin */
-
-DirectShowInputPin::DirectShowInputPin(DirectShowBaseFilter *filter, const QString &name)
- : DirectShowPin(filter, name, PINDIR_INPUT)
-{
- ZeroMemory(&m_sampleProperties, sizeof(m_sampleProperties));
-}
-
-DirectShowInputPin::~DirectShowInputPin() = default;
-
-HRESULT DirectShowInputPin::connectionEnded()
-{
- if (m_allocator) {
- HRESULT hr = m_allocator->Decommit();
- if (FAILED(hr))
- return hr;
-
- m_allocator->Release();
- m_allocator = nullptr;
- }
-
- return S_OK;
-}
-
-HRESULT DirectShowInputPin::setActive(bool active)
-{
- if (!active) {
- m_inErrorState = false;
-
- if (!m_allocator)
- return VFW_E_NO_ALLOCATOR;
-
- m_flushing = false;
- return m_allocator->Decommit();
- }
-
- return S_OK;
-}
-
-HRESULT DirectShowInputPin::EndOfStream()
-{
- if (m_filter->state() == State_Stopped)
- return VFW_E_WRONG_STATE;
- if (m_flushing)
- return S_FALSE;
- if (m_inErrorState)
- return VFW_E_RUNTIME_ERROR;
-
- return S_OK;
-}
-
-HRESULT DirectShowInputPin::BeginFlush()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
- m_flushing = true;
- return S_OK;
-}
-
-HRESULT DirectShowInputPin::EndFlush()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
- m_flushing = false;
- m_inErrorState = false;
- return S_OK;
-}
-
-HRESULT DirectShowInputPin::GetAllocator(IMemAllocator **ppAllocator)
-{
- if (!ppAllocator)
- return E_POINTER;
-
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- if (!m_allocator) {
- m_allocator = com_new<IMemAllocator>(CLSID_MemoryAllocator);;
- if (!m_allocator)
- return E_OUTOFMEMORY;
- }
-
- *ppAllocator = m_allocator;
- m_allocator->AddRef();
-
- return S_OK;
-}
-
-HRESULT DirectShowInputPin::NotifyAllocator(IMemAllocator *pAllocator, BOOL bReadOnly)
-{
- Q_UNUSED(bReadOnly);
-
- if (!pAllocator)
- return E_POINTER;
-
- const std::lock_guard<QRecursiveMutex> locker(m_mutex);
-
- if (m_allocator)
- m_allocator->Release();
-
- m_allocator = pAllocator;
- m_allocator->AddRef();
-
- return S_OK;
-}
-
-HRESULT DirectShowInputPin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps)
-{
- Q_UNUSED(pProps);
- return E_NOTIMPL;
-}
-
-HRESULT DirectShowInputPin::Receive(IMediaSample *pSample)
-{
- if (!pSample)
- return E_POINTER;
- if (m_filter->state() == State_Stopped)
- return VFW_E_WRONG_STATE;
- if (m_flushing)
- return S_FALSE;
- if (m_inErrorState)
- return VFW_E_RUNTIME_ERROR;
-
- HRESULT hr = S_OK;
-
- IMediaSample2 *sample2;
- if (SUCCEEDED(pSample->QueryInterface(IID_PPV_ARGS(&sample2)))) {
- hr = sample2->GetProperties(sizeof(m_sampleProperties),
- reinterpret_cast<PBYTE>(&m_sampleProperties));
- sample2->Release();
- if (FAILED(hr))
- return hr;
- } else {
- m_sampleProperties.cbData = sizeof(m_sampleProperties);
- m_sampleProperties.dwTypeSpecificFlags = 0;
- m_sampleProperties.dwStreamId = AM_STREAM_MEDIA;
- m_sampleProperties.dwSampleFlags = 0;
- if (pSample->IsDiscontinuity() == S_OK)
- m_sampleProperties.dwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY;
- if (pSample->IsPreroll() == S_OK)
- m_sampleProperties.dwSampleFlags |= AM_SAMPLE_PREROLL;
- if (pSample->IsSyncPoint() == S_OK)
- m_sampleProperties.dwSampleFlags |= AM_SAMPLE_SPLICEPOINT;
- if (SUCCEEDED(pSample->GetTime(&m_sampleProperties.tStart,
- &m_sampleProperties.tStop))) {
- m_sampleProperties.dwSampleFlags |= AM_SAMPLE_TIMEVALID | AM_SAMPLE_STOPVALID;
- }
- if (pSample->GetMediaType(&m_sampleProperties.pMediaType) == S_OK)
- m_sampleProperties.dwSampleFlags |= AM_SAMPLE_TYPECHANGED;
- pSample->GetPointer(&m_sampleProperties.pbBuffer);
- m_sampleProperties.lActual = pSample->GetActualDataLength();
- m_sampleProperties.cbBuffer = pSample->GetSize();
- }
-
-
- if (!(m_sampleProperties.dwSampleFlags & AM_SAMPLE_TYPECHANGED))
- return S_OK;
-
- if (isMediaTypeSupported(m_sampleProperties.pMediaType))
- return S_OK;
-
- m_inErrorState = true;
- EndOfStream();
- m_filter->NotifyEvent(EC_ERRORABORT, VFW_E_TYPE_NOT_ACCEPTED, 0);
- return VFW_E_INVALIDMEDIATYPE;
-}
-
-HRESULT DirectShowInputPin::ReceiveMultiple(IMediaSample **pSamples, long nSamples, long *nSamplesProcessed)
-{
- if (!pSamples || !nSamplesProcessed)
- return E_POINTER;
-
- HRESULT hr = S_OK;
- *nSamplesProcessed = 0;
- while (nSamples-- > 0) {
- hr = Receive(pSamples[*nSamplesProcessed]);
- if (hr != S_OK)
- break;
- (*nSamplesProcessed)++;
- }
- return hr;
-}
-
-HRESULT DirectShowInputPin::ReceiveCanBlock()
-{
- int outputPins = 0;
-
- const QList<DirectShowPin *> pinList = m_filter->pins();
- for (DirectShowPin *pin : pinList) {
- PIN_DIRECTION pd;
- HRESULT hr = pin->QueryDirection(&pd);
- if (FAILED(hr))
- return hr;
-
- if (pd == PINDIR_OUTPUT) {
- IPin *connected;
- hr = pin->ConnectedTo(&connected);
- if (SUCCEEDED(hr)) {
- ++outputPins;
- IMemInputPin *inputPin;
- hr = connected->QueryInterface(IID_PPV_ARGS(&inputPin));
- connected->Release();
- if (SUCCEEDED(hr)) {
- hr = inputPin->ReceiveCanBlock();
- inputPin->Release();
- if (hr != S_FALSE)
- return S_OK;
- } else {
- return S_OK;
- }
- }
- }
- }
-
- return outputPins == 0 ? S_OK : S_FALSE;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/common/directshowpin.h b/src/plugins/directshow/common/directshowpin.h
deleted file mode 100644
index 160191ef3..000000000
--- a/src/plugins/directshow/common/directshowpin.h
+++ /dev/null
@@ -1,180 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef DIRECTSHOWPIN_H
-#define DIRECTSHOWPIN_H
-
-#include "directshowobject.h"
-
-#include "directshowmediatype.h"
-#include <qstring.h>
-#include <qmutex.h>
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowBaseFilter;
-
-class DirectShowPin : public IPin
-{
-public:
- virtual ~DirectShowPin();
-
- QString name() const { return m_name; }
- bool isConnected() const { return m_peerPin != nullptr; }
-
- virtual bool isMediaTypeSupported(const AM_MEDIA_TYPE *type) = 0;
- virtual QList<DirectShowMediaType> supportedMediaTypes();
- virtual bool setMediaType(const AM_MEDIA_TYPE *type);
-
- virtual HRESULT completeConnection(IPin *pin);
- virtual HRESULT connectionEnded();
-
- virtual HRESULT setActive(bool active);
-
- // IPin
- STDMETHODIMP Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt) override;
- STDMETHODIMP ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt) override;
- STDMETHODIMP Disconnect() override;
- STDMETHODIMP ConnectedTo(IPin **ppPin) override;
-
- STDMETHODIMP ConnectionMediaType(AM_MEDIA_TYPE *pmt) override;
-
- STDMETHODIMP QueryPinInfo(PIN_INFO *pInfo) override;
- STDMETHODIMP QueryId(LPWSTR *Id) override;
-
- STDMETHODIMP QueryAccept(const AM_MEDIA_TYPE *pmt) override;
-
- STDMETHODIMP EnumMediaTypes(IEnumMediaTypes **ppEnum) override;
-
- STDMETHODIMP QueryInternalConnections(IPin **apPin, ULONG *nPin) override;
-
- STDMETHODIMP EndOfStream() override;
-
- STDMETHODIMP BeginFlush() override;
- STDMETHODIMP EndFlush() override;
-
- STDMETHODIMP NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) override;
-
- STDMETHODIMP QueryDirection(PIN_DIRECTION *pPinDir) override;
-
-protected:
- DirectShowPin(DirectShowBaseFilter *filter, const QString &name, PIN_DIRECTION direction);
-
- QRecursiveMutex m_mutex;
-
- DirectShowBaseFilter *m_filter;
- QString m_name;
- PIN_DIRECTION m_direction;
-
- IPin *m_peerPin = nullptr;
- DirectShowMediaType m_mediaType;
-
-private:
- Q_DISABLE_COPY(DirectShowPin)
- HRESULT tryMediaTypes(IPin *pin, const AM_MEDIA_TYPE *type, IEnumMediaTypes *enumMediaTypes);
- HRESULT tryConnect(IPin *pin, const AM_MEDIA_TYPE *type);
-};
-
-
-class DirectShowOutputPin : public DirectShowPin
-{
-public:
- ~DirectShowOutputPin() override;
-
- // DirectShowPin
- HRESULT completeConnection(IPin *pin) override;
- HRESULT connectionEnded() override;
- HRESULT setActive(bool active) override;
-
- // IPin
- STDMETHODIMP EndOfStream() override;
-
-protected:
- DirectShowOutputPin(DirectShowBaseFilter *filter, const QString &name);
-
- IMemAllocator *m_allocator = nullptr;
- IMemInputPin *m_inputPin = nullptr;
-
-private:
- Q_DISABLE_COPY(DirectShowOutputPin)
-};
-
-
-class DirectShowInputPin : public DirectShowPin
- , public IMemInputPin
-{
-public:
- ~DirectShowInputPin() override;
-
- const AM_SAMPLE2_PROPERTIES *currentSampleProperties() const { return &m_sampleProperties; }
-
- // DirectShowPin
- HRESULT connectionEnded() override;
- HRESULT setActive(bool active) override;
-
- // IPin
- STDMETHODIMP EndOfStream() override;
- STDMETHODIMP BeginFlush() override;
- STDMETHODIMP EndFlush() override;
-
- // IMemInputPin
- STDMETHODIMP GetAllocator(IMemAllocator **ppAllocator) override;
- STDMETHODIMP NotifyAllocator(IMemAllocator *pAllocator, BOOL bReadOnly) override;
- STDMETHODIMP GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps) override;
-
- STDMETHODIMP Receive(IMediaSample *pSample) override;
- STDMETHODIMP ReceiveMultiple(IMediaSample **pSamples, long nSamples,
- long *nSamplesProcessed) override;
- STDMETHODIMP ReceiveCanBlock() override;
-
-protected:
- DirectShowInputPin(DirectShowBaseFilter *filter, const QString &name);
-
- IMemAllocator *m_allocator = nullptr;
- bool m_flushing = false;
- bool m_inErrorState = false;
- AM_SAMPLE2_PROPERTIES m_sampleProperties;
-
-private:
- Q_DISABLE_COPY(DirectShowInputPin)
-};
-
-QT_END_NAMESPACE
-
-#endif // DIRECTSHOWPIN_H
diff --git a/src/plugins/directshow/common/directshowpinenum.cpp b/src/plugins/directshow/common/directshowpinenum.cpp
deleted file mode 100644
index 7ba1bb6e9..000000000
--- a/src/plugins/directshow/common/directshowpinenum.cpp
+++ /dev/null
@@ -1,125 +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 "directshowpinenum.h"
-#include "directshowbasefilter.h"
-
-QT_BEGIN_NAMESPACE
-
-DirectShowPinEnum::DirectShowPinEnum(DirectShowBaseFilter *filter)
- : m_filter(filter)
-{
- m_filter->AddRef();
- const QList<DirectShowPin *> pinList = filter->pins();
- for (DirectShowPin *pin : pinList) {
- pin->AddRef();
- m_pins.append(pin);
- }
-}
-
-DirectShowPinEnum::DirectShowPinEnum(const QList<IPin *> &pins)
- : m_pins(pins)
-{
- for (IPin *pin : qAsConst(m_pins))
- pin->AddRef();
-}
-
-DirectShowPinEnum::~DirectShowPinEnum()
-{
- for (IPin *pin : qAsConst(m_pins))
- pin->Release();
- if (m_filter)
- m_filter->Release();
-}
-
-HRESULT DirectShowPinEnum::QueryInterface(REFIID riid, void **ppv)
-{
- if (ppv == nullptr)
- return E_POINTER;
- if (riid == IID_IUnknown)
- *ppv = static_cast<IUnknown *>(this);
- else if (riid == IID_IEnumPins)
- *ppv = static_cast<IEnumPins *>(this);
- else
- return E_NOINTERFACE;
- AddRef();
- return S_OK;
-}
-
-HRESULT DirectShowPinEnum::Next(ULONG cPins, IPin **ppPins, ULONG *pcFetched)
-{
- if (!ppPins || (!pcFetched && cPins != 1))
- return E_POINTER;
-
- ULONG count = qBound<ULONG>(0, cPins, m_pins.count() - m_index);
-
- for (ULONG i = 0; i < count; ++i, ++m_index) {
- ppPins[i] = m_pins.at(m_index);
- ppPins[i]->AddRef();
- }
-
- if (pcFetched)
- *pcFetched = count;
-
- return count == cPins ? S_OK : S_FALSE;
-}
-
-HRESULT DirectShowPinEnum::Skip(ULONG cPins)
-{
- m_index = qMin(int(m_index + cPins), m_pins.count());
-
- return m_index < m_pins.count() ? S_OK : S_FALSE;
-}
-
-HRESULT DirectShowPinEnum::Reset()
-{
- m_index = 0;
-
- return S_OK;
-}
-
-HRESULT DirectShowPinEnum::Clone(IEnumPins **ppEnum)
-{
- if (!ppEnum)
- return E_POINTER;
- *ppEnum = m_filter ? new DirectShowPinEnum(m_filter) : new DirectShowPinEnum(m_pins);
- return S_OK;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/common/directshowpinenum.h b/src/plugins/directshow/common/directshowpinenum.h
deleted file mode 100644
index aada1a71e..000000000
--- a/src/plugins/directshow/common/directshowpinenum.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 DIRECTSHOWPINENUM_H
-#define DIRECTSHOWPINENUM_H
-
-#include <dshow.h>
-
-#include <QtCore/qlist.h>
-#include "directshowpin.h"
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowBaseFilter;
-
-class DirectShowPinEnum : public IEnumPins
-{
- COM_REF_MIXIN
-public:
- DirectShowPinEnum(DirectShowBaseFilter *filter);
- DirectShowPinEnum(const QList<IPin *> &pins);
- virtual ~DirectShowPinEnum();
-
- STDMETHODIMP QueryInterface(REFIID riid, void **ppv) override;
-
- // IEnumPins
- STDMETHODIMP Next(ULONG cPins, IPin **ppPins, ULONG *pcFetched) override;
- STDMETHODIMP Skip(ULONG cPins) override;
- STDMETHODIMP Reset() override;
- STDMETHODIMP Clone(IEnumPins **ppEnum) override;
-
-private:
- Q_DISABLE_COPY(DirectShowPinEnum)
-
- DirectShowBaseFilter *m_filter = nullptr;
- QList<IPin *> m_pins;
- int m_index = 0;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/directshow/common/directshowsamplegrabber.cpp b/src/plugins/directshow/common/directshowsamplegrabber.cpp
deleted file mode 100644
index a9e74f9db..000000000
--- a/src/plugins/directshow/common/directshowsamplegrabber.cpp
+++ /dev/null
@@ -1,197 +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 "directshowsamplegrabber.h"
-
-#include "directshowglobal.h"
-#include "directshowutils.h"
-
-
-QT_BEGIN_NAMESPACE
-
-// Previously contained in <qedit.h>.
-static const IID iID_ISampleGrabber = { 0x6B652FFF, 0x11FE, 0x4fce, { 0x92, 0xAD, 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F } };
-static const CLSID cLSID_SampleGrabber = { 0xC1F400A0, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } };
-
-class SampleGrabberCallbackPrivate : public ISampleGrabberCB
-{
- Q_DISABLE_COPY(SampleGrabberCallbackPrivate)
-public:
- explicit SampleGrabberCallbackPrivate(DirectShowSampleGrabber *grabber)
- : m_ref(1)
- , m_grabber(grabber)
- { }
-
- virtual ~SampleGrabberCallbackPrivate() = default;
-
- STDMETHODIMP_(ULONG) AddRef() override
- {
- return InterlockedIncrement(&m_ref);
- }
-
- STDMETHODIMP_(ULONG) Release() override
- {
- ULONG ref = InterlockedDecrement(&m_ref);
- if (ref == 0)
- delete this;
- return ref;
- }
-
- STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override
- {
- if (nullptr == ppvObject)
- return E_POINTER;
-
- if (riid == IID_IUnknown /*__uuidof(IUnknown) */ ) {
- AddRef();
- *ppvObject = static_cast<IUnknown *>(this);
- return S_OK;
- }
- if (riid == IID_ISampleGrabberCB /*__uuidof(ISampleGrabberCB)*/ ) {
- AddRef();
- *ppvObject = static_cast<ISampleGrabberCB *>(this);
- return S_OK;
- }
- return E_NOTIMPL;
- }
-
- STDMETHODIMP SampleCB(double time, IMediaSample *mediaSample) override
- {
- if (m_grabber)
- Q_EMIT m_grabber->sampleAvailable(time, mediaSample);
-
- return S_OK;
- }
-
- STDMETHODIMP BufferCB(double time, BYTE *buffer, long bufferLen) override
- {
- if (m_grabber) {
- // Deep copy, the data might be modified or freed after the callback returns
- QByteArray data(reinterpret_cast<const char *>(buffer), bufferLen);
- Q_EMIT m_grabber->bufferAvailable(time, data);
- }
- return S_OK;
- }
-
-private:
- ULONG m_ref;
- DirectShowSampleGrabber *m_grabber;
-};
-
-DirectShowSampleGrabber::DirectShowSampleGrabber(QObject *p)
- : QObject(p)
-{
- // Create sample grabber filter
- HRESULT hr = CoCreateInstance(cLSID_SampleGrabber, nullptr, CLSCTX_INPROC, iID_ISampleGrabber, reinterpret_cast<void **>(&m_sampleGrabber));
-
- if (FAILED(hr)) {
- qCWarning(qtDirectShowPlugin, "Failed to create sample grabber");
- return;
- }
-
- hr = m_sampleGrabber->QueryInterface(IID_IBaseFilter, reinterpret_cast<void **>(&m_filter));
- if (FAILED(hr))
- qCWarning(qtDirectShowPlugin, "Failed to get base filter interface");
-}
-
-DirectShowSampleGrabber::~DirectShowSampleGrabber()
-{
- stop();
- SAFE_RELEASE(m_sampleGabberCb);
- SAFE_RELEASE(m_filter);
- SAFE_RELEASE(m_sampleGrabber);
-}
-
-void DirectShowSampleGrabber::stop()
-{
- if (!m_sampleGrabber)
- return;
-
- if (FAILED(m_sampleGrabber->SetCallback(nullptr, static_cast<long>(m_callbackType)))) {
- qCWarning(qtDirectShowPlugin, "Failed to stop sample grabber callback");
- return;
- }
-}
-
-bool DirectShowSampleGrabber::getConnectedMediaType(AM_MEDIA_TYPE *mediaType)
-{
- Q_ASSERT(mediaType);
-
- if (!isValid())
- return false;
-
- if (FAILED(m_sampleGrabber->GetConnectedMediaType(mediaType))) {
- qCWarning(qtDirectShowPlugin, "Failed to retrieve the connected media type");
- return false;
- }
-
- return true;
-}
-
-bool DirectShowSampleGrabber::setMediaType(const AM_MEDIA_TYPE *mediaType)
-{
- Q_ASSERT(mediaType);
-
- if (FAILED(m_sampleGrabber->SetMediaType(mediaType))) {
- qCWarning(qtDirectShowPlugin, "Failed to set media type");
- return false;
- }
-
- return true;
-}
-
-void DirectShowSampleGrabber::start(DirectShowSampleGrabber::CallbackMethod method,
- bool oneShot,
- bool bufferSamples)
-{
- if (!m_sampleGrabber)
- return;
-
- stop();
-
- if (!m_sampleGabberCb)
- m_sampleGabberCb = new SampleGrabberCallbackPrivate(this);
-
- m_callbackType = method;
- m_sampleGrabber->SetCallback(m_sampleGabberCb, static_cast<long>(method));
- m_sampleGrabber->SetOneShot(oneShot);
- m_sampleGrabber->SetBufferSamples(bufferSamples);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/common/directshowsamplegrabber.h b/src/plugins/directshow/common/directshowsamplegrabber.h
deleted file mode 100644
index 5fc61de14..000000000
--- a/src/plugins/directshow/common/directshowsamplegrabber.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 DIRECTSHOWSAMPLEGRABBER_H
-#define DIRECTSHOWSAMPLEGRABBER_H
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qobject.h>
-
-// TODO: Fix this.
-#include <directshowcameraglobal.h>
-
-QT_BEGIN_NAMESPACE
-
-class SampleGrabberCallbackPrivate;
-
-class DirectShowSampleGrabber : public QObject
-{
- Q_OBJECT
-public:
- DirectShowSampleGrabber(QObject *p = nullptr);
- ~DirectShowSampleGrabber();
-
- // 0 = ISampleGrabberCB::SampleCB, 1 = ISampleGrabberCB::BufferCB
- enum class CallbackMethod : long
- {
- SampleCB,
- BufferCB
- };
-
- bool isValid() const { return m_filter && m_sampleGrabber; }
- bool getConnectedMediaType(AM_MEDIA_TYPE *mediaType);
- bool setMediaType(const AM_MEDIA_TYPE *mediaType);
-
- void stop();
- void start(CallbackMethod method, bool oneShot = false, bool bufferSamples = false);
-
- IBaseFilter *filter() const { return m_filter; }
-
-Q_SIGNALS:
- void sampleAvailable(double time, IMediaSample *sample);
- void bufferAvailable(double time, const QByteArray &data);
-
-private:
- IBaseFilter *m_filter = nullptr;
- ISampleGrabber *m_sampleGrabber = nullptr;
- SampleGrabberCallbackPrivate *m_sampleGabberCb = nullptr;
- CallbackMethod m_callbackType= CallbackMethod::BufferCB;
-};
-
-QT_END_NAMESPACE
-
-#endif // DIRECTSHOWSAMPLEGRABBER_H
diff --git a/src/plugins/directshow/common/directshowutils.cpp b/src/plugins/directshow/common/directshowutils.cpp
deleted file mode 100644
index 9222ad779..000000000
--- a/src/plugins/directshow/common/directshowutils.cpp
+++ /dev/null
@@ -1,346 +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 "directshowutils.h"
-
-QT_BEGIN_NAMESPACE
-
-/**
- * @brief DirectShowUtils::isPinConnected
- * @param pin
- * @param hrOut
- * @return
- */
-bool DirectShowUtils::isPinConnected(IPin *pin, HRESULT *hrOut)
-{
- IPin *connectedPin = nullptr;
- const ScopedSafeRelease<IPin> releasePin { &connectedPin };
- HRESULT hr = S_OK;
- if (!hrOut)
- hrOut = &hr;
-
- *hrOut = pin->ConnectedTo(&connectedPin);
- if (*hrOut == VFW_E_NOT_CONNECTED) { // Not an error in this case
- *hrOut = S_OK;
- return false;
- }
-
- if (FAILED(*hrOut)) {
- qCDebug(qtDirectShowPlugin, "Querying pin connection failed!");
- return false;
- }
-
- return true;
-}
-
-/**
- * @brief DirectShowUtils::hasPinDirection
- * @param pin
- * @param direction
- * @param hrOut
- * @return
- */
-bool DirectShowUtils::hasPinDirection(IPin *pin, PIN_DIRECTION direction, HRESULT *hrOut)
-{
- PIN_DIRECTION pinDir;
- HRESULT hr = S_OK;
- if (!hrOut)
- hrOut = &hr;
-
- *hrOut = pin->QueryDirection(&pinDir);
-
- if (FAILED(*hrOut)) {
- qCDebug(qtDirectShowPlugin, "Querying pin direction failed!");
- return false;
- }
-
- return (pinDir == direction);
-}
-
-bool pinMatchesCategory(IPin* pPin, REFGUID category)
-{
- bool found = false;
- IKsPropertySet *pKs = nullptr;
- DirectShowUtils::ScopedSafeRelease<IKsPropertySet> ks_property { &pKs };
- HRESULT hr = pPin->QueryInterface(IID_PPV_ARGS(&pKs));
-
- if (SUCCEEDED(hr)) {
- GUID pin_category;
- DWORD return_value;
- hr = pKs->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, NULL, 0,
- &pin_category, sizeof(pin_category), &return_value);
- if (SUCCEEDED(hr) && (return_value == sizeof(pin_category)))
- found = (pin_category == category);
- }
-
- return found;
-}
-
-/**
- * @brief DirectShowUtils::getPin
- * @param filter
- * @param pinDirection
- * @param pin
- * @param hrOut
- * @return
- */
-bool DirectShowUtils::getPin(IBaseFilter *filter, PIN_DIRECTION pinDirection, REFGUID category, IPin **pin, HRESULT *hrOut)
-{
- IEnumPins *enumPins = nullptr;
- const ScopedSafeRelease<IEnumPins> releaseEnumPins { &enumPins };
- HRESULT hr S_OK;
- if (!hrOut)
- hrOut = &hr;
-
- *hrOut = filter->EnumPins(&enumPins);
- if (FAILED(*hrOut)) {
- qCDebug(qtDirectShowPlugin, "Unable to retrieve pins from the filter!");
- return false;
- }
-
- enumPins->Reset();
- IPin *nextPin = nullptr;
- while (enumPins->Next(1, &nextPin, nullptr) == S_OK) {
- const ScopedSafeRelease<IPin> releasePin { &nextPin };
- PIN_DIRECTION currentPinDir;
- *hrOut = nextPin->QueryDirection(&currentPinDir);
- if (currentPinDir == pinDirection) {
- if (category == GUID_NULL || pinMatchesCategory(nextPin, category)) {
- *pin = nextPin;
- (*pin)->AddRef();
- return true;
- }
- }
- }
-
- return false;
-}
-
-/**
- * @brief DirectShowUtils::matchPin
- * @param pin
- * @param pinDirection
- * @param shouldBeConnected
- * @param hrOut
- * @return
- */
-bool DirectShowUtils::matchPin(IPin *pin, PIN_DIRECTION pinDirection, BOOL shouldBeConnected, HRESULT *hrOut)
-{
- HRESULT hr = S_OK;
- if (!hrOut)
- hrOut = &hr;
-
- const BOOL isConnected = isPinConnected(pin, hrOut);
- if (FAILED(*hrOut)) // Error reason will already be logged, so just return.
- return false;
-
- if (isConnected == shouldBeConnected)
- return hasPinDirection(pin, pinDirection, hrOut);
-
- return false;
-}
-
-/**
- * @brief DirectShowUtils::findUnconnectedPin
- * @param filter
- * @param pinDirection
- * @param pin
- * @param hrOut
- * @return
- */
-bool DirectShowUtils::findUnconnectedPin(IBaseFilter *filter, PIN_DIRECTION pinDirection, IPin **pin, HRESULT *hrOut)
-{
- HRESULT hr = S_OK;
- if (!hrOut)
- hrOut = &hr;
-
- IEnumPins *enumPins = nullptr;
- const ScopedSafeRelease<IEnumPins> releaseEnumPins { &enumPins };
- *hrOut = filter->EnumPins(&enumPins);
- if (FAILED(*hrOut)) {
- qCDebug(qtDirectShowPlugin, "Unable to retrieve pins from the DS filter");
- return false;
- }
-
- IPin *nextPin = nullptr;
- while (S_OK == enumPins->Next(1, &nextPin, nullptr)) {
- const ScopedSafeRelease<IPin> releaseNextPin { &nextPin };
- if (matchPin(nextPin, pinDirection, FALSE, hrOut)) {
- *pin = nextPin;
- (*pin)->AddRef();
- return true;
- }
- }
-
- qCDebug(qtDirectShowPlugin, "No unconnected pins found");
- *hrOut = VFW_E_NOT_FOUND;
-
- return false;
-}
-
-/**
- * @brief DirectShowUtils::connectFilters - Attempts to connect \a outputPin to \a filter
- * @param graph
- * @param outputPin
- * @param filter
- * @param hrOut
- * @return
- */
-bool DirectShowUtils::connectFilters(IGraphBuilder *graph, IPin *outputPin, IBaseFilter *filter, HRESULT *hrOut)
-{
-
- // Find an input pin on the downstream filter.
- HRESULT hr = S_OK;
- if (!hrOut)
- hrOut = &hr;
-
- IPin *inputPin = nullptr;
- const ScopedSafeRelease<IPin> releaseInputPin { &inputPin };
- if (!findUnconnectedPin(filter, PINDIR_INPUT, &inputPin, hrOut))
- return false;
-
-
- // Try to connect them.
- *hrOut = graph->Connect(outputPin, inputPin);
- if (FAILED(*hrOut)) {
- qCDebug(qtDirectShowPlugin, "Unable to connect output pin to filter!");
- return false;
- }
-
- return true;
-}
-
-/**
- * @brief DirectShowUtils::connectFilters - Attempts to connect \a filter to \a inputPin.
- * @param graph
- * @param filter
- * @param inputPin
- * @param hrOut
- * @return
- */
-bool DirectShowUtils::connectFilters(IGraphBuilder *graph, IBaseFilter *filter, IPin *inputPin, HRESULT *hrOut)
-{
- HRESULT hr = S_OK;
- if (!hrOut)
- hrOut = &hr;
-
- IPin *outputPin = nullptr;
- const ScopedSafeRelease<IPin> releaseOutputPin { &outputPin };
- // Find an output pin on the upstream filter.
- if (findUnconnectedPin(filter, PINDIR_OUTPUT, &outputPin, hrOut))
- return false;
-
- *hrOut = graph->Connect(outputPin, inputPin);
- if (FAILED(*hrOut)) {
- qCDebug(qtDirectShowPlugin, "Unable to connect filter to input pin!");
- return false;
- }
-
- return true;
-}
-
-/**
- * @brief DirectShowUtils::connectFilters - Attempts to connect the \a upstreamFilter to \a downstreamFilter.
- * @param graph
- * @param upstreamFilter
- * @param downstreamFilter
- * @param autoConnect - If set to true all filters in the graph will be considered.
- * @param hrOut
- * @return true if the the filters were connected, false otherwise.
- */
-bool DirectShowUtils::connectFilters(IGraphBuilder *graph,
- IBaseFilter *upstreamFilter,
- IBaseFilter *downstreamFilter,
- bool autoConnect,
- HRESULT *hrOut)
-{
- HRESULT hr = S_OK;
- if (!hrOut)
- hrOut = &hr;
-
- const auto findAndConnect = [graph, downstreamFilter, hrOut](IBaseFilter *filter) -> bool {
- IPin *outputPin = nullptr;
- const ScopedSafeRelease<IPin> releaseOutputPin { &outputPin };
- if (findUnconnectedPin(filter, PINDIR_OUTPUT, &outputPin, hrOut))
- return connectFilters(graph, outputPin, downstreamFilter, hrOut);
-
- return false;
- };
-
- // Try to connect to the upstream filter first.
- if (findAndConnect(upstreamFilter))
- return true;
-
- const auto getFilters = [graph, hrOut]() -> IEnumFilters * {
- IEnumFilters *f = nullptr;
- *hrOut = graph->EnumFilters(&f);
- return f;
- };
- IEnumFilters *filters = autoConnect ? getFilters()
- : nullptr;
- const ScopedSafeRelease<IEnumFilters> releaseEnumFilters { &filters };
- if (!filters) {
- qCDebug(qtDirectShowPlugin, "No filters found!");
- return false;
- }
-
- IBaseFilter *nextFilter = nullptr;
- while (S_OK == filters->Next(1, &nextFilter, nullptr)) {
- const ScopedSafeRelease<IBaseFilter> releaseNextFilter { &nextFilter };
- if (nextFilter && findAndConnect(nextFilter))
- return true;
- }
-
- return false;
-}
-
-thread_local static int g_refCount = 0;
-void DirectShowUtils::CoInitializeIfNeeded()
-{
- if (++g_refCount == 1)
- ::CoInitialize(nullptr);
-}
-
-void DirectShowUtils::CoUninitializeIfNeeded()
-{
- if (--g_refCount == 0)
- ::CoUninitialize();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/common/directshowutils.h b/src/plugins/directshow/common/directshowutils.h
deleted file mode 100644
index ec761abe6..000000000
--- a/src/plugins/directshow/common/directshowutils.h
+++ /dev/null
@@ -1,91 +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 DIRECTSHOWUTILS_H
-#define DIRECTSHOWUTILS_H
-
-#include "directshowglobal.h"
-
-QT_BEGIN_NAMESPACE
-
-namespace DirectShowUtils
-{
-template <typename T>
-void safeRelease(T **iface) {
- if (!iface)
- return;
-
- if (!*iface)
- return;
-
- (*iface)->Release();
- *iface = nullptr;
-}
-
-template <typename T>
-struct ScopedSafeRelease
-{
- T **iunknown;
- ~ScopedSafeRelease()
- {
- DirectShowUtils::safeRelease(iunknown);
- }
-};
-
-bool getPin(IBaseFilter *filter, PIN_DIRECTION pinDirection, REFGUID category, IPin **pin, HRESULT *hrOut);
-bool isPinConnected(IPin *pin, HRESULT *hrOut = nullptr);
-bool hasPinDirection(IPin *pin, PIN_DIRECTION direction, HRESULT *hrOut = nullptr);
-bool matchPin(IPin *pin, PIN_DIRECTION pinDirection, BOOL shouldBeConnected, HRESULT *hrOut = nullptr);
-bool findUnconnectedPin(IBaseFilter *filter, PIN_DIRECTION pinDirection, IPin **pin, HRESULT *hrOut = nullptr);
-bool connectFilters(IGraphBuilder *graph, IPin *outputPin, IBaseFilter *filter, HRESULT *hrOut = nullptr);
-bool connectFilters(IGraphBuilder *graph, IBaseFilter *filter, IPin *inputPin, HRESULT *hrOut = nullptr);
-bool connectFilters(IGraphBuilder *graph,
- IBaseFilter *upstreamFilter,
- IBaseFilter *downstreamFilter,
- bool autoConnect = false,
- HRESULT *hrOut = nullptr);
-
-void CoInitializeIfNeeded();
-void CoUninitializeIfNeeded();
-
-}
-
-QT_END_NAMESPACE
-
-#endif // DIRECTSHOWUTILS_H
diff --git a/src/plugins/directshow/common/directshowvideobuffer.cpp b/src/plugins/directshow/common/directshowvideobuffer.cpp
deleted file mode 100644
index fc329f58a..000000000
--- a/src/plugins/directshow/common/directshowvideobuffer.cpp
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "directshowvideobuffer.h"
-
-QT_BEGIN_NAMESPACE
-
-DirectShowVideoBuffer::DirectShowVideoBuffer(IMediaSample *sample, int bytesPerLine)
- : QAbstractVideoBuffer(NoHandle)
- , m_sample(sample)
- , m_bytesPerLine(bytesPerLine)
- , m_mapMode(NotMapped)
-{
- m_sample->AddRef();
-}
-
-DirectShowVideoBuffer::~DirectShowVideoBuffer()
-{
- m_sample->Release();
-}
-
-uchar *DirectShowVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine)
-{
- if (m_mapMode == NotMapped && mode != NotMapped) {
- if (numBytes)
- *numBytes = m_sample->GetActualDataLength();
-
- if (bytesPerLine)
- *bytesPerLine = m_bytesPerLine;
-
- BYTE *bytes = nullptr;
-
- if (m_sample->GetPointer(&bytes) == S_OK) {
- m_mapMode = mode;
-
- return reinterpret_cast<uchar *>(bytes);
- }
- }
- return nullptr;
-}
-
-void DirectShowVideoBuffer::unmap()
-{
- m_mapMode = NotMapped;
-}
-
-QAbstractVideoBuffer::MapMode DirectShowVideoBuffer::mapMode() const
-{
- return m_mapMode;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/common/directshowvideobuffer.h b/src/plugins/directshow/common/directshowvideobuffer.h
deleted file mode 100644
index a85a3ca34..000000000
--- a/src/plugins/directshow/common/directshowvideobuffer.h
+++ /dev/null
@@ -1,70 +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 DIRECTSHOWVIDEOBUFFER_H
-#define DIRECTSHOWVIDEOBUFFER_H
-
-#include <dshow.h>
-
-#include <qabstractvideobuffer.h>
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowVideoBuffer : public QAbstractVideoBuffer
-{
-public:
- DirectShowVideoBuffer(IMediaSample *sample, int bytesPerLine);
- ~DirectShowVideoBuffer() override;
-
- IMediaSample *sample() { return m_sample; }
-
- uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) override;
- void unmap() override;
-
- MapMode mapMode() const override;
-
-private:
- IMediaSample *m_sample;
- int m_bytesPerLine;
- MapMode m_mapMode;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/directshow/common/directshowvideoprobecontrol.cpp b/src/plugins/directshow/common/directshowvideoprobecontrol.cpp
deleted file mode 100644
index 0e2e68864..000000000
--- a/src/plugins/directshow/common/directshowvideoprobecontrol.cpp
+++ /dev/null
@@ -1,69 +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 "directshowvideoprobecontrol.h"
-#include "directshowglobal.h"
-
-QT_BEGIN_NAMESPACE
-
-DirectShowVideoProbeControl::DirectShowVideoProbeControl(QObject *p)
- : QMediaVideoProbeControl(p)
-{
-
-}
-
-DirectShowVideoProbeControl::~DirectShowVideoProbeControl()
-{
- if (m_ref.deref())
- qCWarning(qtDirectShowPlugin, "QVideoProbe control destroyed while it's still being referenced!!!");
-}
-
-void DirectShowVideoProbeControl::probeVideoFrame(const QVideoFrame &frame)
-{
- emit videoFrameProbed(frame);
- m_frameProbed = true;
-}
-
-void DirectShowVideoProbeControl::flushVideoFrame()
-{
- if (m_frameProbed)
- emit flush();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/common/directshowvideoprobecontrol.h b/src/plugins/directshow/common/directshowvideoprobecontrol.h
deleted file mode 100644
index 57839f8d3..000000000
--- a/src/plugins/directshow/common/directshowvideoprobecontrol.h
+++ /dev/null
@@ -1,67 +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 DIRECTSHOWVIDEOPROBECONTROL_H
-#define DIRECTSHOWVIDEOPROBECONTROL_H
-
-#include <qmediavideoprobecontrol.h>
-
-#include <QtCore/qglobal.h>
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowVideoProbeControl : public QMediaVideoProbeControl
-{
- Q_OBJECT
-public:
- explicit DirectShowVideoProbeControl(QObject *p = nullptr);
- ~DirectShowVideoProbeControl();
-
- bool ref() { return m_ref.ref(); }
- bool deref() { return m_ref.deref(); }
- void probeVideoFrame(const QVideoFrame &frame);
- void flushVideoFrame();
-private:
- QAtomicInt m_ref;
- bool m_frameProbed = false;
-};
-
-QT_END_NAMESPACE
-
-#endif // DIRECTSHOWVIDEOPROBECONTROL_H
diff --git a/src/plugins/directshow/directshow.json b/src/plugins/directshow/directshow.json
deleted file mode 100644
index b1783b5ed..000000000
--- a/src/plugins/directshow/directshow.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Keys": ["directshow"],
- "Services": ["org.qt-project.qt.camera", "org.qt-project.qt.mediaplayer"]
-}
diff --git a/src/plugins/directshow/directshow.pro b/src/plugins/directshow/directshow.pro
deleted file mode 100644
index 27bb3abb8..000000000
--- a/src/plugins/directshow/directshow.pro
+++ /dev/null
@@ -1,32 +0,0 @@
-TARGET = dsengine
-QT += multimedia-private
-
-win32:!qtHaveModule(opengl)|qtConfig(dynamicgl) {
- LIBS_PRIVATE += -lgdi32 -luser32
-}
-
-HEADERS += dsserviceplugin.h
-SOURCES += dsserviceplugin.cpp
-
-# Remove WINVER/_WIN32_WINNT definitions added to qt_build_config.prf
-# by qtbase/d57a7c41712f8627a462d893329dc3f0dbb52d32 since the multimedia
-# headers of MinGW 5.3/7.1 are too broken to compile with 0x0601.
-mingw {
- DEFINES -= WINVER=0x0601 _WIN32_WINNT=0x0601
- DEFINES += NO_DSHOW_STRSAFE
-}
-
-mingw {
- LIBS_PRIVATE += -lamstrmid
-}
-
-include(common/common.pri)
-include(player/player.pri)
-include(camera/camera.pri)
-
-OTHER_FILES += \
- directshow.json
-
-PLUGIN_TYPE = mediaservice
-PLUGIN_CLASS_NAME = DSServicePlugin
-load(qt_plugin)
diff --git a/src/plugins/directshow/dsserviceplugin.cpp b/src/plugins/directshow/dsserviceplugin.cpp
deleted file mode 100644
index 18a807fd1..000000000
--- a/src/plugins/directshow/dsserviceplugin.cpp
+++ /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$
-**
-****************************************************************************/
-
-#include <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include <dshow.h>
-
-#include <QtCore/qstring.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/QFile>
-
-#include "directshowglobal.h"
-#include "dsserviceplugin.h"
-
-#include "dsvideodevicecontrol.h"
-#include <dshow.h>
-#include "dscameraservice.h"
-
-#include "directshowplayerservice.h"
-
-#include <qmediaserviceproviderplugin.h>
-#include "directshowutils.h"
-
-extern const CLSID CLSID_VideoInputDeviceCategory;
-
-
-#ifndef _STRSAFE_H_INCLUDED_
-#include <tchar.h>
-#endif
-#include <dshow.h>
-#include <objbase.h>
-#include <initguid.h>
-#ifdef Q_CC_MSVC
-# pragma comment(lib, "strmiids.lib")
-# pragma comment(lib, "ole32.lib")
-#endif // Q_CC_MSVC
-#include <windows.h>
-#include <ocidl.h>
-
-QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(qtDirectShowPlugin, "qt.multimedia.plugins.directshow")
-
-QMediaService* DSServicePlugin::create(QString const& key)
-{
- if (key == QLatin1String(Q_MEDIASERVICE_CAMERA)) {
- DirectShowUtils::CoInitializeIfNeeded();
- return new DSCameraService;
- }
-
- if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) {
- DirectShowUtils::CoInitializeIfNeeded();
- return new DirectShowPlayerService;
- }
-
- return nullptr;
-}
-
-void DSServicePlugin::release(QMediaService *service)
-{
- delete service;
- DirectShowUtils::CoUninitializeIfNeeded();
-}
-
-QMediaServiceProviderHint::Features DSServicePlugin::supportedFeatures(
- const QByteArray &service) const
-{
- return service == Q_MEDIASERVICE_MEDIAPLAYER
- ? (QMediaServiceProviderHint::StreamPlayback | QMediaServiceProviderHint::VideoSurface)
- : QMediaServiceProviderHint::Features();
-}
-
-QByteArray DSServicePlugin::defaultDevice(const QByteArray &service) const
-{
- if (service == Q_MEDIASERVICE_CAMERA) {
- DirectShowUtils::CoInitializeIfNeeded();
- const QList<DSVideoDeviceInfo> &devs = DSVideoDeviceControl::availableDevices();
- DirectShowUtils::CoUninitializeIfNeeded();
- if (!devs.isEmpty())
- return devs.first().first;
- }
- return QByteArray();
-}
-
-QList<QByteArray> DSServicePlugin::devices(const QByteArray &service) const
-{
- QList<QByteArray> result;
-
- if (service == Q_MEDIASERVICE_CAMERA) {
- DirectShowUtils::CoInitializeIfNeeded();
- const QList<DSVideoDeviceInfo> &devs = DSVideoDeviceControl::availableDevices();
- DirectShowUtils::CoUninitializeIfNeeded();
- for (const DSVideoDeviceInfo &info : devs)
- result.append(info.first);
- }
-
- return result;
-}
-
-QString DSServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device)
-{
- if (service == Q_MEDIASERVICE_CAMERA) {
- DirectShowUtils::CoInitializeIfNeeded();
- const QList<DSVideoDeviceInfo> &devs = DSVideoDeviceControl::availableDevices();
- DirectShowUtils::CoUninitializeIfNeeded();
- for (const DSVideoDeviceInfo &info : devs) {
- if (info.first == device)
- return info.second;
- }
- }
- return QString();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/dsserviceplugin.h b/src/plugins/directshow/dsserviceplugin.h
deleted file mode 100644
index 55db9310c..000000000
--- a/src/plugins/directshow/dsserviceplugin.h
+++ /dev/null
@@ -1,73 +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 DSSERVICEPLUGIN_H
-#define DSSERVICEPLUGIN_H
-
-#include <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include "qmediaserviceproviderplugin.h"
-
-QT_BEGIN_NAMESPACE
-
-class DSServicePlugin
- : public QMediaServiceProviderPlugin
- , public QMediaServiceSupportedDevicesInterface
- , public QMediaServiceDefaultDeviceInterface
- , public QMediaServiceFeaturesInterface
-{
- Q_OBJECT
- Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
- Q_INTERFACES(QMediaServiceDefaultDeviceInterface)
- Q_INTERFACES(QMediaServiceFeaturesInterface)
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "directshow.json")
-
-public:
- QMediaService* create(QString const& key) override;
- void release(QMediaService *service) override;
-
- QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const override;
-
- QByteArray defaultDevice(const QByteArray &service) const override;
- QList<QByteArray> devices(const QByteArray &service) const override;
- QString deviceDescription(const QByteArray &service, const QByteArray &device) override;
-};
-
-QT_END_NAMESPACE
-
-#endif // DSSERVICEPLUGIN_H
diff --git a/src/plugins/directshow/player/directshowaudioendpointcontrol.cpp b/src/plugins/directshow/player/directshowaudioendpointcontrol.cpp
deleted file mode 100644
index f4e45cdd8..000000000
--- a/src/plugins/directshow/player/directshowaudioendpointcontrol.cpp
+++ /dev/null
@@ -1,161 +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 "directshowaudioendpointcontrol.h"
-
-#include "directshowglobal.h"
-#include "directshowplayerservice.h"
-
-QT_BEGIN_NAMESPACE
-
-DirectShowAudioEndpointControl::DirectShowAudioEndpointControl(
- DirectShowPlayerService *service, QObject *parent)
- : QAudioOutputSelectorControl(parent)
- , m_service(service)
-{
- if (CreateBindCtx(0, &m_bindContext) == S_OK) {
- m_deviceEnumerator = com_new<ICreateDevEnum>(CLSID_SystemDeviceEnum);
-
- updateEndpoints();
-
- setActiveOutput(m_defaultEndpoint);
- }
-}
-
-DirectShowAudioEndpointControl::~DirectShowAudioEndpointControl()
-{
- for (IMoniker *moniker : qAsConst(m_devices))
- moniker->Release();
-
- if (m_bindContext)
- m_bindContext->Release();
-
- if (m_deviceEnumerator)
- m_deviceEnumerator->Release();
-}
-
-QList<QString> DirectShowAudioEndpointControl::availableOutputs() const
-{
- return m_devices.keys();
-}
-
-QString DirectShowAudioEndpointControl::outputDescription(const QString &name) const
-{
-#ifdef __IPropertyBag_INTERFACE_DEFINED__
- QString description;
-
- if (IMoniker *moniker = m_devices.value(name, 0)) {
- IPropertyBag *propertyBag = nullptr;
- if (SUCCEEDED(moniker->BindToStorage(
- nullptr, nullptr, IID_IPropertyBag, reinterpret_cast<void **>(&propertyBag)))) {
- VARIANT name;
- VariantInit(&name);
- if (SUCCEEDED(propertyBag->Read(L"FriendlyName", &name, nullptr)))
- description = QString::fromWCharArray(name.bstrVal);
- VariantClear(&name);
- propertyBag->Release();
- }
- }
-
- return description;
-#else
- return name.section(QLatin1Char('\\'), -1);
-#endif
-}
-
-QString DirectShowAudioEndpointControl::defaultOutput() const
-{
- return m_defaultEndpoint;
-}
-
-QString DirectShowAudioEndpointControl::activeOutput() const
-{
- return m_activeEndpoint;
-}
-
-void DirectShowAudioEndpointControl::setActiveOutput(const QString &name)
-{
- if (m_activeEndpoint == name)
- return;
-
- if (IMoniker *moniker = m_devices.value(name, 0)) {
- IBaseFilter *filter = nullptr;
-
- if (moniker->BindToObject(
- m_bindContext,
- nullptr,
- IID_IBaseFilter,
- reinterpret_cast<void **>(&filter)) == S_OK) {
- m_service->setAudioOutput(filter);
-
- filter->Release();
- }
- }
-}
-
-void DirectShowAudioEndpointControl::updateEndpoints()
-{
- IMalloc *oleMalloc = nullptr;
- if (m_deviceEnumerator && CoGetMalloc(1, &oleMalloc) == S_OK) {
- IEnumMoniker *monikers = nullptr;
-
- if (m_deviceEnumerator->CreateClassEnumerator(
- CLSID_AudioRendererCategory, &monikers, 0) == S_OK) {
- for (IMoniker *moniker = nullptr; monikers->Next(1, &moniker, nullptr) == S_OK; moniker->Release()) {
- OLECHAR *string = nullptr;
- if (moniker->GetDisplayName(m_bindContext, nullptr, &string) == S_OK) {
- QString deviceId = QString::fromWCharArray(string);
- oleMalloc->Free(string);
-
- moniker->AddRef();
- m_devices.insert(deviceId, moniker);
-
- if (m_defaultEndpoint.isEmpty()
- || deviceId.endsWith(QLatin1String("Default DirectSound Device"))) {
- m_defaultEndpoint = deviceId;
- }
- }
- }
- monikers->Release();
- }
- oleMalloc->Release();
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/player/directshowaudioendpointcontrol.h b/src/plugins/directshow/player/directshowaudioendpointcontrol.h
deleted file mode 100644
index 05c4eb990..000000000
--- a/src/plugins/directshow/player/directshowaudioendpointcontrol.h
+++ /dev/null
@@ -1,82 +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 DIRECTSHOWAUDIOENDPOINTCONTROL_H
-#define DIRECTSHOWAUDIOENDPOINTCONTROL_H
-
-#include "qaudiooutputselectorcontrol.h"
-
-#include <dshow.h>
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowPlayerService;
-
-class DirectShowAudioEndpointControl : public QAudioOutputSelectorControl
-{
- Q_OBJECT
-public:
- DirectShowAudioEndpointControl(DirectShowPlayerService *service, QObject *parent = nullptr);
- ~DirectShowAudioEndpointControl() override;
-
- QList<QString> availableOutputs() const override;
-
- QString outputDescription(const QString &name) const override;
-
- QString defaultOutput() const override;
- QString activeOutput() const override;
-
- void setActiveOutput(const QString& name) override;
-
-private:
- void updateEndpoints();
-
- DirectShowPlayerService *m_service;
- IBindCtx *m_bindContext = nullptr;
- ICreateDevEnum *m_deviceEnumerator = nullptr;
-
- QMap<QString, IMoniker *> m_devices;
- QString m_defaultEndpoint;
- QString m_activeEndpoint;
-};
-
-QT_END_NAMESPACE
-
-#endif
-
diff --git a/src/plugins/directshow/player/directshowevrvideowindowcontrol.cpp b/src/plugins/directshow/player/directshowevrvideowindowcontrol.cpp
deleted file mode 100644
index 89bfc1467..000000000
--- a/src/plugins/directshow/player/directshowevrvideowindowcontrol.cpp
+++ /dev/null
@@ -1,66 +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 "directshowevrvideowindowcontrol.h"
-
-#include "directshowglobal.h"
-
-DirectShowEvrVideoWindowControl::DirectShowEvrVideoWindowControl(QObject *parent)
- : EvrVideoWindowControl(parent)
-{
-}
-
-DirectShowEvrVideoWindowControl::~DirectShowEvrVideoWindowControl()
-{
- if (m_evrFilter)
- m_evrFilter->Release();
-}
-
-IBaseFilter *DirectShowEvrVideoWindowControl::filter()
-{
- if (!m_evrFilter) {
- m_evrFilter = com_new<IBaseFilter>(clsid_EnhancedVideoRenderer);
- if (!setEvr(m_evrFilter)) {
- m_evrFilter->Release();
- m_evrFilter = nullptr;
- }
- }
-
- return m_evrFilter;
-}
diff --git a/src/plugins/directshow/player/directshowevrvideowindowcontrol.h b/src/plugins/directshow/player/directshowevrvideowindowcontrol.h
deleted file mode 100644
index edbde78d6..000000000
--- a/src/plugins/directshow/player/directshowevrvideowindowcontrol.h
+++ /dev/null
@@ -1,63 +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 DIRECTSHOWEVRVIDEOWINDOWCONTROL_H
-#define DIRECTSHOWEVRVIDEOWINDOWCONTROL_H
-
-#include "evrvideowindowcontrol.h"
-
-struct IBaseFilter;
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowEvrVideoWindowControl : public EvrVideoWindowControl
-{
-public:
- DirectShowEvrVideoWindowControl(QObject *parent = nullptr);
- ~DirectShowEvrVideoWindowControl();
-
- IBaseFilter *filter();
-
-private:
- IBaseFilter *m_evrFilter = nullptr;
-};
-
-QT_END_NAMESPACE
-
-#endif // DIRECTSHOWEVRVIDEOWINDOWCONTROL_H
diff --git a/src/plugins/directshow/player/directshowioreader.cpp b/src/plugins/directshow/player/directshowioreader.cpp
deleted file mode 100644
index 3318d57b5..000000000
--- a/src/plugins/directshow/player/directshowioreader.cpp
+++ /dev/null
@@ -1,462 +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 "directshowioreader.h"
-
-#include "directshoweventloop.h"
-#include "directshowglobal.h"
-#include "directshowiosource.h"
-
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qcoreevent.h>
-#include <QtCore/qiodevice.h>
-#include <QtCore/qthread.h>
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowSampleRequest
-{
-public:
- DirectShowSampleRequest(
- IMediaSample *sample, DWORD_PTR userData, LONGLONG position, LONG length, BYTE *buffer)
- : sample(sample)
- , userData(userData)
- , position(position)
- , length(length)
- , buffer(buffer)
- {
- }
-
- DirectShowSampleRequest *remove() { DirectShowSampleRequest *n = next; delete this; return n; }
-
- DirectShowSampleRequest *next = nullptr;
- IMediaSample *sample;
- DWORD_PTR userData;
- LONGLONG position;
- LONG length;
- BYTE *buffer;
- HRESULT result = S_FALSE;
-};
-
-DirectShowIOReader::DirectShowIOReader(
- QIODevice *device, DirectShowIOSource *source, DirectShowEventLoop *loop)
- : m_source(source)
- , m_device(device)
- , m_loop(loop)
-{
- moveToThread(device->thread());
-
- connect(device, &QIODevice::readyRead, this, &DirectShowIOReader::readyRead);
-}
-
-DirectShowIOReader::~DirectShowIOReader()
-{
- flushRequests();
-}
-
-HRESULT DirectShowIOReader::QueryInterface(REFIID riid, void **ppvObject)
-{
- return m_source->QueryInterface(riid, ppvObject);
-}
-
-ULONG DirectShowIOReader::AddRef()
-{
- return m_source->AddRef();
-}
-
-ULONG DirectShowIOReader::Release()
-{
- return m_source->Release();
-}
-
-// IAsyncReader
-HRESULT DirectShowIOReader::RequestAllocator(
- IMemAllocator *pPreferred, ALLOCATOR_PROPERTIES *pProps, IMemAllocator **ppActual)
-{
- if (!ppActual || !pProps)
- return E_POINTER;
-
- ALLOCATOR_PROPERTIES actualProperties;
-
- if (pProps->cbAlign == 0)
- pProps->cbAlign = 1;
-
- if (pPreferred && pPreferred->SetProperties(pProps, &actualProperties) == S_OK) {
- pPreferred->AddRef();
-
- *ppActual = pPreferred;
- m_source->setAllocator(*ppActual);
- return S_OK;
- }
-
- *ppActual = com_new<IMemAllocator>(CLSID_MemoryAllocator);
- if (*ppActual) {
- if ((*ppActual)->SetProperties(pProps, &actualProperties) == S_OK) {
- m_source->setAllocator(*ppActual);
- return S_OK;
- }
- (*ppActual)->Release();
- }
- ppActual = nullptr;
- return E_FAIL;
-}
-
-HRESULT DirectShowIOReader::Request(IMediaSample *pSample, DWORD_PTR dwUser)
-{
- QMutexLocker locker(&m_mutex);
-
- if (!pSample)
- return E_POINTER;
- if (m_flushing)
- return VFW_E_WRONG_STATE;
-
- REFERENCE_TIME startTime = 0;
- REFERENCE_TIME endTime = 0;
- BYTE *buffer;
-
- if (pSample->GetTime(&startTime, &endTime) != S_OK
- || pSample->GetPointer(&buffer) != S_OK) {
- return VFW_E_SAMPLE_TIME_NOT_SET;
- }
- LONGLONG position = startTime / 10000000;
- LONG length = qMin<qint64>((endTime - startTime) / 10000000, m_availableLength);
-
- auto request = new DirectShowSampleRequest(pSample, dwUser, position, length, buffer);
-
- if (m_pendingTail) {
- m_pendingTail->next = request;
- } else {
- m_pendingHead = request;
- m_loop->postEvent(this, new QEvent(QEvent::User));
- }
- m_pendingTail = request;
-
- return S_OK;
-}
-
-HRESULT DirectShowIOReader::WaitForNext(
- DWORD dwTimeout, IMediaSample **ppSample, DWORD_PTR *pdwUser)
-{
- if (!ppSample || !pdwUser)
- return E_POINTER;
-
- QMutexLocker locker(&m_mutex);
-
- do {
- if (m_readyHead) {
- DirectShowSampleRequest *request = m_readyHead;
-
- *ppSample = request->sample;
- *pdwUser = request->userData;
-
- HRESULT hr = request->result;
-
- m_readyHead = request->next;
-
- if (!m_readyHead)
- m_readyTail = nullptr;
-
- delete request;
-
- return hr;
- }
- if (m_flushing) {
- *ppSample = nullptr;
- *pdwUser = 0;
-
- return VFW_E_WRONG_STATE;
- }
- } while (m_wait.wait(&m_mutex, dwTimeout));
-
- *ppSample = nullptr;
- *pdwUser = 0;
-
- return VFW_E_TIMEOUT;
-}
-
-HRESULT DirectShowIOReader::SyncReadAligned(IMediaSample *pSample)
-{
- if (!pSample)
- return E_POINTER;
-
- REFERENCE_TIME startTime = 0;
- REFERENCE_TIME endTime = 0;
- BYTE *buffer;
-
- if (pSample->GetTime(&startTime, &endTime) != S_OK
- || pSample->GetPointer(&buffer) != S_OK) {
- return VFW_E_SAMPLE_TIME_NOT_SET;
- }
- LONGLONG position = startTime / 10000000;
- LONG length = (endTime - startTime) / 10000000;
-
- QMutexLocker locker(&m_mutex);
-
- if (thread() == QThread::currentThread()) {
- qint64 bytesRead = 0;
-
- HRESULT hr = blockingRead(position, length, buffer, &bytesRead);
- if (SUCCEEDED(hr))
- pSample->SetActualDataLength(bytesRead);
-
- return hr;
- }
- m_synchronousPosition = position;
- m_synchronousLength = length;
- m_synchronousBuffer = buffer;
-
- m_loop->postEvent(this, new QEvent(QEvent::User));
-
- m_wait.wait(&m_mutex);
-
- m_synchronousBuffer = nullptr;
-
- if (SUCCEEDED(m_synchronousResult))
- pSample->SetActualDataLength(m_synchronousBytesRead);
-
- return m_synchronousResult;
-}
-
-HRESULT DirectShowIOReader::SyncRead(LONGLONG llPosition, LONG lLength, BYTE *pBuffer)
-{
- if (!pBuffer)
- return E_POINTER;
-
- if (thread() == QThread::currentThread()) {
- qint64 bytesRead;
- return blockingRead(llPosition, lLength, pBuffer, &bytesRead);
- }
- QMutexLocker locker(&m_mutex);
-
- m_synchronousPosition = llPosition;
- m_synchronousLength = lLength;
- m_synchronousBuffer = pBuffer;
-
- m_loop->postEvent(this, new QEvent(QEvent::User));
-
- m_wait.wait(&m_mutex);
-
- m_synchronousBuffer = nullptr;
-
- return m_synchronousResult;
-}
-
-HRESULT DirectShowIOReader::Length(LONGLONG *pTotal, LONGLONG *pAvailable)
-{
- if (!pTotal || !pAvailable)
- return E_POINTER;
-
- QMutexLocker locker(&m_mutex);
- *pTotal = m_totalLength;
- *pAvailable = m_availableLength;
- return S_OK;
-}
-
-
-HRESULT DirectShowIOReader::BeginFlush()
-{
- QMutexLocker locker(&m_mutex);
-
- if (m_flushing)
- return S_FALSE;
-
- m_flushing = true;
-
- flushRequests();
-
- m_wait.wakeAll();
-
- return S_OK;
-}
-
-HRESULT DirectShowIOReader::EndFlush()
-{
- QMutexLocker locker(&m_mutex);
-
- if (!m_flushing)
- return S_FALSE;
-
- m_flushing = false;
-
- return S_OK;
-}
-
-void DirectShowIOReader::customEvent(QEvent *event)
-{
- if (event->type() == QEvent::User) {
- readyRead();
- } else {
- QObject::customEvent(event);
- }
-}
-
-void DirectShowIOReader::readyRead()
-{
- QMutexLocker locker(&m_mutex);
-
- m_availableLength = m_device->bytesAvailable() + m_device->pos();
- m_totalLength = m_device->size();
-
- if (m_synchronousBuffer) {
- if (nonBlockingRead(
- m_synchronousPosition,
- m_synchronousLength,
- m_synchronousBuffer,
- &m_synchronousBytesRead,
- &m_synchronousResult)) {
- m_wait.wakeAll();
- }
- } else {
- qint64 bytesRead = 0;
-
- while (m_pendingHead && nonBlockingRead(
- m_pendingHead->position,
- m_pendingHead->length,
- m_pendingHead->buffer,
- &bytesRead,
- &m_pendingHead->result)) {
- m_pendingHead->sample->SetActualDataLength(bytesRead);
-
- if (m_readyTail)
- m_readyTail->next = m_pendingHead;
- m_readyTail = m_pendingHead;
-
- m_pendingHead = m_pendingHead->next;
-
- m_readyTail->next = nullptr;
-
- if (!m_pendingHead)
- m_pendingTail = nullptr;
-
- if (!m_readyHead)
- m_readyHead = m_readyTail;
-
- m_wait.wakeAll();
- }
- }
-}
-
-HRESULT DirectShowIOReader::blockingRead(
- LONGLONG position, LONG length, BYTE *buffer, qint64 *bytesRead)
-{
- *bytesRead = 0;
-
- if (qint64(position) > m_device->size())
- return S_FALSE;
-
- const qint64 maxSize = qMin<qint64>(m_device->size(), position + length);
-
- while (m_device->bytesAvailable() + m_device->pos() < maxSize) {
- if (!m_device->waitForReadyRead(-1))
- return S_FALSE;
- }
-
- if (m_device->pos() != position && !m_device->seek(position))
- return S_FALSE;
-
- const qint64 maxBytes = qMin<qint64>(length, m_device->bytesAvailable());
-
- *bytesRead = m_device->read(reinterpret_cast<char *>(buffer), maxBytes);
-
- if (*bytesRead != length) {
- ::memset(buffer + *bytesRead, 0, length - *bytesRead);
-
- return S_FALSE;
- }
- return S_OK;
-}
-
-bool DirectShowIOReader::nonBlockingRead(
- LONGLONG position, LONG length, BYTE *buffer, qint64 *bytesRead, HRESULT *result)
-{
- const qint64 maxSize = qMin<qint64>(m_device->size(), position + length);
-
- if (position > m_device->size()) {
- *bytesRead = 0;
- *result = S_FALSE;
-
- return true;
- }
- if (m_device->bytesAvailable() + m_device->pos() >= maxSize) {
- if (m_device->pos() != position && !m_device->seek(position)) {
- *bytesRead = 0;
- *result = S_FALSE;
-
- return true;
- }
- const qint64 maxBytes = qMin<qint64>(length, m_device->bytesAvailable());
-
- *bytesRead = m_device->read(reinterpret_cast<char *>(buffer), maxBytes);
-
- if (*bytesRead != length) {
- ::memset(buffer + *bytesRead, 0, length - *bytesRead);
-
- *result = S_FALSE;
- } else {
- *result = S_OK;
- }
-
- return true;
- }
- return false;
-}
-
-void DirectShowIOReader::flushRequests()
-{
- while (m_pendingHead) {
- m_pendingHead->result = VFW_E_WRONG_STATE;
-
- if (m_readyTail)
- m_readyTail->next = m_pendingHead;
-
- m_readyTail = m_pendingHead;
-
- m_pendingHead = m_pendingHead->next;
-
- m_readyTail->next = nullptr;
-
- if (!m_pendingHead)
- m_pendingTail = nullptr;
-
- if (!m_readyHead)
- m_readyHead = m_readyTail;
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/player/directshowioreader.h b/src/plugins/directshow/player/directshowioreader.h
deleted file mode 100644
index a0f2d7adb..000000000
--- a/src/plugins/directshow/player/directshowioreader.h
+++ /dev/null
@@ -1,120 +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 DIRECTSHOWIOREADER_H
-#define DIRECTSHOWIOREADER_H
-
-#include <dshow.h>
-
-#include <QtCore/qmutex.h>
-#include <QtCore/qobject.h>
-#include <QtCore/qwaitcondition.h>
-
-QT_BEGIN_NAMESPACE
-class QIODevice;
-
-class DirectShowEventLoop;
-class DirectShowIOSource;
-class DirectShowSampleRequest;
-
-class DirectShowIOReader : public QObject, public IAsyncReader
-{
- Q_OBJECT
-public:
- DirectShowIOReader(QIODevice *device, DirectShowIOSource *source, DirectShowEventLoop *loop);
- ~DirectShowIOReader() override;
-
- // IUnknown
- HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override;
- ULONG STDMETHODCALLTYPE AddRef() override;
- ULONG STDMETHODCALLTYPE Release() override;
-
- // IAsyncReader
- HRESULT STDMETHODCALLTYPE RequestAllocator(
- IMemAllocator *pPreferred, ALLOCATOR_PROPERTIES *pProps,
- IMemAllocator **ppActual) override;
-
- HRESULT STDMETHODCALLTYPE Request(IMediaSample *pSample, DWORD_PTR dwUser) override;
-
- HRESULT STDMETHODCALLTYPE WaitForNext(
- DWORD dwTimeout, IMediaSample **ppSample, DWORD_PTR *pdwUser) override;
-
- HRESULT STDMETHODCALLTYPE SyncReadAligned(IMediaSample *pSample) override;
-
- HRESULT STDMETHODCALLTYPE SyncRead(LONGLONG llPosition, LONG lLength, BYTE *pBuffer) override;
-
- HRESULT STDMETHODCALLTYPE Length(LONGLONG *pTotal, LONGLONG *pAvailable) override;
-
- HRESULT STDMETHODCALLTYPE BeginFlush() override;
- HRESULT STDMETHODCALLTYPE EndFlush() override;
-
-protected:
- void customEvent(QEvent *event) override;
-
-private Q_SLOTS:
- void readyRead();
-
-private:
- HRESULT blockingRead(LONGLONG position, LONG length, BYTE *buffer, qint64 *bytesRead);
- bool nonBlockingRead(
- LONGLONG position, LONG length, BYTE *buffer, qint64 *bytesRead, HRESULT *result);
- void flushRequests();
-
- DirectShowIOSource *m_source;
- QIODevice *m_device;
- DirectShowEventLoop *m_loop;
- DirectShowSampleRequest *m_pendingHead = nullptr;
- DirectShowSampleRequest *m_pendingTail = nullptr;
- DirectShowSampleRequest *m_readyHead = nullptr;
- DirectShowSampleRequest *m_readyTail = nullptr;
- LONGLONG m_synchronousPosition = 0;
- LONG m_synchronousLength = 0;
- qint64 m_synchronousBytesRead = 0;
- BYTE *m_synchronousBuffer = nullptr;
- HRESULT m_synchronousResult = S_OK;
- LONGLONG m_totalLength = 0;
- LONGLONG m_availableLength = 0;
- bool m_flushing = false;
- QMutex m_mutex;
- QWaitCondition m_wait;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/directshow/player/directshowiosource.cpp b/src/plugins/directshow/player/directshowiosource.cpp
deleted file mode 100644
index 54c043c17..000000000
--- a/src/plugins/directshow/player/directshowiosource.cpp
+++ /dev/null
@@ -1,530 +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 "directshowiosource.h"
-
-#include "directshowglobal.h"
-#include "directshowmediatype.h"
-#include "directshowmediatypeenum.h"
-#include "directshowpinenum.h"
-
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qurl.h>
-
-QT_BEGIN_NAMESPACE
-
-static const GUID directshow_subtypes[] =
-{
- MEDIASUBTYPE_NULL,
- MEDIASUBTYPE_Avi,
- MEDIASUBTYPE_Asf,
- MEDIASUBTYPE_MPEG1Video,
- MEDIASUBTYPE_QTMovie,
- MEDIASUBTYPE_WAVE,
- MEDIASUBTYPE_AIFF,
- MEDIASUBTYPE_AU,
- MEDIASUBTYPE_DssVideo,
- MEDIASUBTYPE_MPEG1Audio,
- MEDIASUBTYPE_MPEG1System,
- MEDIASUBTYPE_MPEG1VideoCD
-};
-
-DirectShowIOSource::DirectShowIOSource(DirectShowEventLoop *loop)
- : m_loop(loop)
-{
- // This filter has only one possible output type, that is, a stream of data
- // with no particular subtype. The graph builder will try every demux/decode filters
- // to find one able to decode the stream.
- //
- // The filter works in pull mode, the downstream filter is responsible for requesting
- // samples from this one.
- //
- AM_MEDIA_TYPE type
- {
- MEDIATYPE_Stream, // majortype
- MEDIASUBTYPE_NULL, // subtype
- TRUE, // bFixedSizeSamples
- FALSE, // bTemporalCompression
- 1, // lSampleSize
- GUID_NULL, // formattype
- nullptr, // pUnk
- 0, // cbFormat
- nullptr, // pbFormat
- };
-
- for (const auto &directshowSubtype : directshow_subtypes) {
- type.subtype = directshowSubtype;
- m_supportedMediaTypes.append(DirectShowMediaType(type));
- }
-}
-
-DirectShowIOSource::~DirectShowIOSource()
-{
- Q_ASSERT(m_ref == 0);
-
- delete m_reader;
-}
-
-void DirectShowIOSource::setDevice(QIODevice *device)
-{
- Q_ASSERT(!m_reader);
-
- m_reader = new DirectShowIOReader(device, this, m_loop);
-}
-
-void DirectShowIOSource::setAllocator(IMemAllocator *allocator)
-{
- if (m_allocator == allocator)
- return;
-
- if (m_allocator)
- m_allocator->Release();
-
- m_allocator = allocator;
-
- if (m_allocator)
- m_allocator->AddRef();
-}
-
-// IUnknown
-HRESULT DirectShowIOSource::QueryInterface(REFIID riid, void **ppvObject)
-{
- // 2dd74950-a890-11d1-abe8-00a0c905f375
- static const GUID iid_IAmFilterMiscFlags = {
- 0x2dd74950, 0xa890, 0x11d1, {0xab, 0xe8, 0x00, 0xa0, 0xc9, 0x05, 0xf3, 0x75}};
-
- if (!ppvObject)
- return E_POINTER;
-
- if (riid == IID_IUnknown || riid == IID_IPersist || riid == IID_IMediaFilter
- || riid == IID_IBaseFilter) {
- *ppvObject = static_cast<IBaseFilter *>(this);
- } else if (riid == iid_IAmFilterMiscFlags) {
- *ppvObject = static_cast<IAMFilterMiscFlags *>(this);
- } else if (riid == IID_IPin) {
- *ppvObject = static_cast<IPin *>(this);
- } else if (riid == IID_IAsyncReader) {
- m_queriedForAsyncReader = true;
- *ppvObject = static_cast<IAsyncReader *>(m_reader);
- } else {
- *ppvObject = nullptr;
-
- return E_NOINTERFACE;
- }
-
- AddRef();
-
- return S_OK;
-}
-
-ULONG DirectShowIOSource::AddRef()
-{
- return InterlockedIncrement(&m_ref);
-}
-
-ULONG DirectShowIOSource::Release()
-{
- ULONG ref = InterlockedDecrement(&m_ref);
-
- if (ref == 0) {
- delete this;
- }
-
- return ref;
-}
-
-// IPersist
-HRESULT DirectShowIOSource::GetClassID(CLSID *pClassID)
-{
- *pClassID = CLSID_NULL;
-
- return S_OK;
-}
-
-// IMediaFilter
-HRESULT DirectShowIOSource::Run(REFERENCE_TIME tStart)
-{
- Q_UNUSED(tStart);
- QMutexLocker locker(&m_mutex);
-
- m_state = State_Running;
-
- return S_OK;
-}
-
-HRESULT DirectShowIOSource::Pause()
-{
- QMutexLocker locker(&m_mutex);
-
- m_state = State_Paused;
-
- return S_OK;
-}
-
-HRESULT DirectShowIOSource::Stop()
-{
- QMutexLocker locker(&m_mutex);
-
- m_state = State_Stopped;
-
- return S_OK;
-}
-
-HRESULT DirectShowIOSource::GetState(DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
-{
- Q_UNUSED(dwMilliSecsTimeout);
-
- if (!pState)
- return E_POINTER;
-
- QMutexLocker locker(&m_mutex);
- *pState = m_state;
- return S_OK;
-}
-
-HRESULT DirectShowIOSource::SetSyncSource(IReferenceClock *pClock)
-{
- QMutexLocker locker(&m_mutex);
-
- if (m_clock)
- m_clock->Release();
-
- m_clock = pClock;
-
- if (m_clock)
- m_clock->AddRef();
-
- return S_OK;
-}
-
-HRESULT DirectShowIOSource::GetSyncSource(IReferenceClock **ppClock)
-{
- if (!ppClock)
- return E_POINTER;
-
- if (!m_clock) {
- *ppClock = nullptr;
- return S_FALSE;
- }
- m_clock->AddRef();
- *ppClock = m_clock;
- return S_OK;
-}
-
-// IBaseFilter
-HRESULT DirectShowIOSource::EnumPins(IEnumPins **ppEnum)
-{
- if (!ppEnum)
- return E_POINTER;
-
- *ppEnum = new DirectShowPinEnum(QList<IPin *>() << this);
- return S_OK;
-}
-
-HRESULT DirectShowIOSource::FindPin(LPCWSTR Id, IPin **ppPin)
-{
- if (!ppPin || !Id)
- return E_POINTER;
-
- QMutexLocker locker(&m_mutex);
- if (m_pinId == QStringView(Id)) {
- AddRef();
- *ppPin = this;
- return S_OK;
- }
- *ppPin = nullptr;
- return VFW_E_NOT_FOUND;
-}
-
-HRESULT DirectShowIOSource::JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName)
-{
- QMutexLocker locker(&m_mutex);
-
- m_graph = pGraph;
- m_filterName = QString::fromWCharArray(pName);
-
- return S_OK;
-}
-
-HRESULT DirectShowIOSource::QueryFilterInfo(FILTER_INFO *pInfo)
-{
- if (!pInfo)
- return E_POINTER;
-
- QString name = m_filterName;
- if (name.length() >= MAX_FILTER_NAME)
- name.truncate(MAX_FILTER_NAME - 1);
-
- int length = name.toWCharArray(pInfo->achName);
- pInfo->achName[length] = '\0';
-
- if (m_graph)
- m_graph->AddRef();
-
- pInfo->pGraph = m_graph;
-
- return S_OK;
-}
-
-HRESULT DirectShowIOSource::QueryVendorInfo(LPWSTR *pVendorInfo)
-{
- Q_UNUSED(pVendorInfo);
-
- return E_NOTIMPL;
-}
-
-// IAMFilterMiscFlags
-ULONG DirectShowIOSource::GetMiscFlags()
-{
- return AM_FILTER_MISC_FLAGS_IS_SOURCE;
-}
-
-// IPin
-HRESULT DirectShowIOSource::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
-{
- if (!pReceivePin)
- return E_POINTER;
-
- QMutexLocker locker(&m_mutex);
-
- if (m_state != State_Stopped)
- return VFW_E_NOT_STOPPED;
-
- if (m_peerPin)
- return VFW_E_ALREADY_CONNECTED;
-
- // If we get a type from the graph manager, check that we support that
- if (pmt && pmt->majortype != MEDIATYPE_Stream)
- return VFW_E_TYPE_NOT_ACCEPTED;
-
- // This filter only works in pull mode, the downstream filter must query for the
- // AsyncReader interface during ReceiveConnection().
- // If it doesn't, we can't connect to it.
- m_queriedForAsyncReader = false;
- HRESULT hr = 0;
- // Negotiation of media type
- // - Complete'ish type (Stream with subtype specified).
- if (pmt && pmt->subtype != MEDIASUBTYPE_NULL /* aka. GUID_NULL */) {
- hr = pReceivePin->ReceiveConnection(this, pmt);
- // Update the media type for the current connection.
- if (SUCCEEDED(hr))
- DirectShowMediaType::copy(&m_connectionMediaType, pmt);
- } else if (pmt && pmt->subtype == MEDIATYPE_NULL) { // - Partial type (Stream, but no subtype specified).
- DirectShowMediaType::copy(&m_connectionMediaType, pmt);
- // Check if the receiving pin accepts any of the streaming subtypes.
- for (const DirectShowMediaType &t : qAsConst(m_supportedMediaTypes)) {
- m_connectionMediaType->subtype = t->subtype;
- hr = pReceivePin->ReceiveConnection(this, &m_connectionMediaType);
- if (SUCCEEDED(hr))
- break;
- }
- } else { // - No media type specified.
- // Check if the receiving pin accepts any of the streaming types.
- for (const DirectShowMediaType &t : qAsConst(m_supportedMediaTypes)) {
- hr = pReceivePin->ReceiveConnection(this, &t);
- if (SUCCEEDED(hr)) {
- m_connectionMediaType = t;
- break;
- }
- }
- }
-
- if (SUCCEEDED(hr) && m_queriedForAsyncReader) {
- m_peerPin = pReceivePin;
- m_peerPin->AddRef();
- } else {
- pReceivePin->Disconnect();
- if (m_allocator) {
- m_allocator->Release();
- m_allocator = nullptr;
- }
- if (!m_queriedForAsyncReader)
- hr = VFW_E_NO_TRANSPORT;
-
- m_connectionMediaType.clear();
- }
-
- return hr;
-}
-
-HRESULT DirectShowIOSource::ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt)
-{
- Q_UNUSED(pConnector);
- Q_UNUSED(pmt);
- // Output pin.
- return E_NOTIMPL;
-}
-
-HRESULT DirectShowIOSource::Disconnect()
-{
- QMutexLocker locker(&m_mutex);
-
- if (!m_peerPin)
- return S_FALSE;
- if (m_state != State_Stopped)
- return VFW_E_NOT_STOPPED;
-
- HRESULT hr = m_peerPin->Disconnect();
- if (!SUCCEEDED(hr))
- return hr;
-
- if (m_allocator) {
- m_allocator->Release();
- m_allocator = nullptr;
- }
-
- m_peerPin->Release();
- m_peerPin = nullptr;
-
- return S_OK;
-}
-
-HRESULT DirectShowIOSource::ConnectedTo(IPin **ppPin)
-{
- if (!ppPin)
- return E_POINTER;
-
- QMutexLocker locker(&m_mutex);
- if (!m_peerPin) {
- *ppPin = nullptr;
- return VFW_E_NOT_CONNECTED;
- }
- m_peerPin->AddRef();
- *ppPin = m_peerPin;
- return S_OK;
-}
-
-HRESULT DirectShowIOSource::ConnectionMediaType(AM_MEDIA_TYPE *pmt)
-{
- if (!pmt)
- return E_POINTER;
-
- QMutexLocker locker(&m_mutex);
- if (!m_peerPin) {
- pmt = nullptr;
- return VFW_E_NOT_CONNECTED;
- }
- DirectShowMediaType::copy(pmt, &m_connectionMediaType);
- return S_OK;
-}
-
-HRESULT DirectShowIOSource::QueryPinInfo(PIN_INFO *pInfo)
-{
- if (!pInfo)
- return E_POINTER;
-
- AddRef();
-
- pInfo->pFilter = this;
- pInfo->dir = PINDIR_OUTPUT;
-
- const int bytes = qMin(MAX_FILTER_NAME, (m_pinId.length() + 1) * 2);
-
- ::memcpy(pInfo->achName, m_pinId.utf16(), bytes);
-
- return S_OK;
-}
-
-HRESULT DirectShowIOSource::QueryId(LPWSTR *Id)
-{
- if (!Id)
- return E_POINTER;
-
- const int bytes = (m_pinId.length() + 1) * 2;
- *Id = static_cast<LPWSTR>(::CoTaskMemAlloc(bytes));
- ::memcpy(*Id, m_pinId.utf16(), bytes);
- return S_OK;
-}
-
-HRESULT DirectShowIOSource::QueryAccept(const AM_MEDIA_TYPE *pmt)
-{
- if (!pmt)
- return E_POINTER;
- return pmt->majortype == MEDIATYPE_Stream ? S_OK : S_FALSE;
-}
-
-HRESULT DirectShowIOSource::EnumMediaTypes(IEnumMediaTypes **ppEnum)
-{
- if (!ppEnum)
- return E_POINTER;
- *ppEnum = new DirectShowMediaTypeEnum(m_supportedMediaTypes);
- return S_OK;
-}
-
-HRESULT DirectShowIOSource::QueryInternalConnections(IPin **apPin, ULONG *nPin)
-{
- Q_UNUSED(apPin);
- Q_UNUSED(nPin);
-
- return E_NOTIMPL;
-}
-
-HRESULT DirectShowIOSource::EndOfStream()
-{
- return S_OK;
-}
-
-HRESULT DirectShowIOSource::BeginFlush()
-{
- return m_reader->BeginFlush();
-}
-
-HRESULT DirectShowIOSource::EndFlush()
-{
- return m_reader->EndFlush();
-}
-
-HRESULT DirectShowIOSource::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
-{
- Q_UNUSED(tStart);
- Q_UNUSED(tStop);
- Q_UNUSED(dRate);
-
- return S_OK;
-}
-
-HRESULT DirectShowIOSource::QueryDirection(PIN_DIRECTION *pPinDir)
-{
- if (!pPinDir)
- return E_POINTER;
- *pPinDir = PINDIR_OUTPUT;
- return S_OK;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/player/directshowiosource.h b/src/plugins/directshow/player/directshowiosource.h
deleted file mode 100644
index 837842518..000000000
--- a/src/plugins/directshow/player/directshowiosource.h
+++ /dev/null
@@ -1,140 +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 DIRECTSHOWIOSOURCE_H
-#define DIRECTSHOWIOSOURCE_H
-
-#include "directshowglobal.h"
-#include "directshowioreader.h"
-#include "directshowmediatype.h"
-
-#include <QtCore/qfile.h>
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowIOSource
- : public IBaseFilter
- , public IAMFilterMiscFlags
- , public IPin
-{
- Q_DISABLE_COPY(DirectShowIOSource)
-public:
- DirectShowIOSource(DirectShowEventLoop *loop);
- virtual ~DirectShowIOSource();
-
- void setDevice(QIODevice *device);
- void setAllocator(IMemAllocator *allocator);
-
- // IUnknown
- HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override;
- ULONG STDMETHODCALLTYPE AddRef() override;
- ULONG STDMETHODCALLTYPE Release() override;
-
- // IPersist
- HRESULT STDMETHODCALLTYPE GetClassID(CLSID *pClassID) override;
-
- // IMediaFilter
- HRESULT STDMETHODCALLTYPE Run(REFERENCE_TIME tStart) override;
- HRESULT STDMETHODCALLTYPE Pause() override;
- HRESULT STDMETHODCALLTYPE Stop() override;
-
- HRESULT STDMETHODCALLTYPE GetState(DWORD dwMilliSecsTimeout, FILTER_STATE *pState) override;
-
- HRESULT STDMETHODCALLTYPE SetSyncSource(IReferenceClock *pClock) override;
- HRESULT STDMETHODCALLTYPE GetSyncSource(IReferenceClock **ppClock) override;
-
- // IBaseFilter
- HRESULT STDMETHODCALLTYPE EnumPins(IEnumPins **ppEnum) override;
- HRESULT STDMETHODCALLTYPE FindPin(LPCWSTR Id, IPin **ppPin) override;
-
- HRESULT STDMETHODCALLTYPE JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName) override;
-
- HRESULT STDMETHODCALLTYPE QueryFilterInfo(FILTER_INFO *pInfo) override;
- HRESULT STDMETHODCALLTYPE QueryVendorInfo(LPWSTR *pVendorInfo) override;
-
- // IAMFilterMiscFlags
- ULONG STDMETHODCALLTYPE GetMiscFlags() override;
-
- // IPin
- HRESULT STDMETHODCALLTYPE Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt) override;
- HRESULT STDMETHODCALLTYPE ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt) override;
- HRESULT STDMETHODCALLTYPE Disconnect() override;
- HRESULT STDMETHODCALLTYPE ConnectedTo(IPin **ppPin) override;
-
- HRESULT STDMETHODCALLTYPE ConnectionMediaType(AM_MEDIA_TYPE *pmt) override;
-
- HRESULT STDMETHODCALLTYPE QueryPinInfo(PIN_INFO *pInfo) override;
- HRESULT STDMETHODCALLTYPE QueryId(LPWSTR *Id) override;
-
- HRESULT STDMETHODCALLTYPE QueryAccept(const AM_MEDIA_TYPE *pmt) override;
-
- HRESULT STDMETHODCALLTYPE EnumMediaTypes(IEnumMediaTypes **ppEnum) override;
-
- HRESULT STDMETHODCALLTYPE QueryInternalConnections(IPin **apPin, ULONG *nPin) override;
-
- HRESULT STDMETHODCALLTYPE EndOfStream() override;
-
- HRESULT STDMETHODCALLTYPE BeginFlush() override;
- HRESULT STDMETHODCALLTYPE EndFlush() override;
-
- HRESULT STDMETHODCALLTYPE NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop,
- double dRate) override;
-
- HRESULT STDMETHODCALLTYPE QueryDirection(PIN_DIRECTION *pPinDir) override;
-
-private:
- volatile LONG m_ref = 1;
- FILTER_STATE m_state = State_Stopped;
- DirectShowIOReader *m_reader = nullptr;
- DirectShowEventLoop *m_loop;
- IFilterGraph *m_graph = nullptr;
- IReferenceClock *m_clock = nullptr;
- IMemAllocator *m_allocator = nullptr;
- IPin *m_peerPin = nullptr;
- DirectShowMediaType m_connectionMediaType;
- QList<DirectShowMediaType> m_supportedMediaTypes;
- QString m_filterName;
- const QString m_pinId = QLatin1String("Data");
- bool m_queriedForAsyncReader = false;
- QMutex m_mutex;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/directshow/player/directshowmetadatacontrol.cpp b/src/plugins/directshow/player/directshowmetadatacontrol.cpp
deleted file mode 100644
index 61138951c..000000000
--- a/src/plugins/directshow/player/directshowmetadatacontrol.cpp
+++ /dev/null
@@ -1,697 +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 <dshow.h>
-#ifdef min
-#undef min
-#endif
-#ifdef max
-#undef max
-#endif
-
-#include <QtMultimedia/qmediametadata.h>
-#include <QtCore/qcoreapplication.h>
-#include <QSize>
-#include <qdatetime.h>
-#include <qimage.h>
-
-#include <initguid.h>
-#include <qnetwork.h>
-
-#include "directshowmetadatacontrol.h"
-#include "directshowplayerservice.h"
-
-#include <QtMultimedia/private/qtmultimedia-config_p.h>
-
-#if QT_CONFIG(wmsdk)
-#include <wmsdk.h>
-#endif
-
-#if QT_CONFIG(wshellitem)
-#include <shlobj.h>
-#include <propkeydef.h>
-#include <private/qsystemlibrary_p.h>
-
-DEFINE_PROPERTYKEY(PKEY_Author, 0xF29F85E0, 0x4FF9, 0x1068, 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9, 4);
-DEFINE_PROPERTYKEY(PKEY_Title, 0xF29F85E0, 0x4FF9, 0x1068, 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9, 2);
-DEFINE_PROPERTYKEY(PKEY_Media_SubTitle, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 38);
-DEFINE_PROPERTYKEY(PKEY_ParentalRating, 0x64440492, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 21);
-DEFINE_PROPERTYKEY(PKEY_Comment, 0xF29F85E0, 0x4FF9, 0x1068, 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9, 6);
-DEFINE_PROPERTYKEY(PKEY_Copyright, 0x64440492, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 11);
-DEFINE_PROPERTYKEY(PKEY_Media_ProviderStyle, 0x64440492, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 40);
-DEFINE_PROPERTYKEY(PKEY_Media_Year, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 5);
-DEFINE_PROPERTYKEY(PKEY_Media_DateEncoded, 0x2E4B640D, 0x5019, 0x46D8, 0x88, 0x81, 0x55, 0x41, 0x4C, 0xC5, 0xCA, 0xA0, 100);
-DEFINE_PROPERTYKEY(PKEY_Rating, 0x64440492, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 9);
-DEFINE_PROPERTYKEY(PKEY_Keywords, 0xF29F85E0, 0x4FF9, 0x1068, 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9, 5);
-DEFINE_PROPERTYKEY(PKEY_Language, 0xD5CDD502, 0x2E9C, 0x101B, 0x93, 0x97, 0x08, 0x00, 0x2B, 0x2C, 0xF9, 0xAE, 28);
-DEFINE_PROPERTYKEY(PKEY_Media_Publisher, 0x64440492, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 30);
-DEFINE_PROPERTYKEY(PKEY_Media_Duration, 0x64440490, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 3);
-DEFINE_PROPERTYKEY(PKEY_Audio_EncodingBitrate, 0x64440490, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 4);
-DEFINE_PROPERTYKEY(PKEY_Media_AverageLevel, 0x09EDD5B6, 0xB301, 0x43C5, 0x99, 0x90, 0xD0, 0x03, 0x02, 0xEF, 0xFD, 0x46, 100);
-DEFINE_PROPERTYKEY(PKEY_Audio_ChannelCount, 0x64440490, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 7);
-DEFINE_PROPERTYKEY(PKEY_Audio_PeakValue, 0x2579E5D0, 0x1116, 0x4084, 0xBD, 0x9A, 0x9B, 0x4F, 0x7C, 0xB4, 0xDF, 0x5E, 100);
-DEFINE_PROPERTYKEY(PKEY_Audio_SampleRate, 0x64440490, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 5);
-DEFINE_PROPERTYKEY(PKEY_Audio_Format, 0x64440490, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 2);
-DEFINE_PROPERTYKEY(PKEY_Music_AlbumTitle, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 4);
-DEFINE_PROPERTYKEY(PKEY_Music_AlbumArtist, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 13);
-DEFINE_PROPERTYKEY(PKEY_Music_Artist, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 2);
-DEFINE_PROPERTYKEY(PKEY_Music_Composer, 0x64440492, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 19);
-DEFINE_PROPERTYKEY(PKEY_Music_Conductor, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 36);
-DEFINE_PROPERTYKEY(PKEY_Music_Lyrics, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 12);
-DEFINE_PROPERTYKEY(PKEY_Music_Mood, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 39);
-DEFINE_PROPERTYKEY(PKEY_Music_TrackNumber, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 7);
-DEFINE_PROPERTYKEY(PKEY_Music_Genre, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 11);
-DEFINE_PROPERTYKEY(PKEY_ThumbnailStream, 0xF29F85E0, 0x4FF9, 0x1068, 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9, 27);
-DEFINE_PROPERTYKEY(PKEY_Video_FrameHeight, 0x64440491, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 4);
-DEFINE_PROPERTYKEY(PKEY_Video_FrameWidth, 0x64440491, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 3);
-DEFINE_PROPERTYKEY(PKEY_Video_HorizontalAspectRatio, 0x64440491, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 42);
-DEFINE_PROPERTYKEY(PKEY_Video_VerticalAspectRatio, 0x64440491, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 45);
-DEFINE_PROPERTYKEY(PKEY_Video_FrameRate, 0x64440491, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 6);
-DEFINE_PROPERTYKEY(PKEY_Video_EncodingBitrate, 0x64440491, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 8);
-DEFINE_PROPERTYKEY(PKEY_Video_Director, 0x64440492, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 20);
-DEFINE_PROPERTYKEY(PKEY_Video_Compression, 0x64440491, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 10);
-DEFINE_PROPERTYKEY(PKEY_Media_Writer, 0x64440492, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 23);
-
-static QString nameForGUIDString(const QString &guid)
-{
- // Audio formats
- if (guid == "{00001610-0000-0010-8000-00AA00389B71}" || guid == "{000000FF-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("MPEG AAC Audio");
- if (guid == "{00001600-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("MPEG ADTS AAC Audio");
- if (guid == "{00000092-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Dolby AC-3 SPDIF");
- if (guid == "{E06D802C-DB46-11CF-B4D1-00805F6CBBEA}" || guid == "{00002000-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Dolby AC-3");
- if (guid == "{A7FB87AF-2D02-42FB-A4D4-05CD93843BDD}")
- return QStringLiteral("Dolby Digital Plus");
- if (guid == "{00000009-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("DRM");
- if (guid == "{00000008-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Digital Theater Systems Audio (DTS)");
- if (guid == "{00000003-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("IEEE Float Audio");
- if (guid == "{00000055-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("MPEG Audio Layer-3 (MP3)");
- if (guid == "{00000050-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("MPEG-1 Audio");
- if (guid == "{2E6D7033-767A-494D-B478-F29D25DC9037}")
- return QStringLiteral("MPEG Audio Layer 1/2");
- if (guid == "{0000000A-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Windows Media Audio Voice");
- if (guid == "{00000001-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Uncompressed PCM Audio");
- if (guid == "{00000164-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Windows Media Audio 9 SPDIF");
- if (guid == "{00000161-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Windows Media Audio 8 (WMA2)");
- if (guid == "{00000162-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Windows Media Audio 9 (WMA3");
- if (guid == "{00000163-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Windows Media Audio 9 Lossless");
- if (guid == "{8D2FD10B-5841-4a6b-8905-588FEC1ADED9}")
- return QStringLiteral("Vorbis");
- if (guid == "{0000F1AC-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Free Lossless Audio Codec (FLAC)");
- if (guid == "{00006C61-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Apple Lossless Audio Codec (ALAC)");
-
- // Video formats
- if (guid == "{35327664-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("DVCPRO 25 (DV25)");
- if (guid == "{30357664-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("DVCPRO 50 (DV50)");
- if (guid == "{20637664-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("DVC/DV Video");
- if (guid == "{31687664-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("DVCPRO 100 (DVH1)");
- if (guid == "{64687664-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("HD-DVCR (DVHD)");
- if (guid == "{64737664-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("SDL-DVCR (DVSD)");
- if (guid == "{6C737664-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("SD-DVCR (DVSL)");
- if (guid == "{33363248-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("H.263 Video");
- if (guid == "{34363248-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("H.264 Video");
- if (guid == "{35363248-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("H.265 Video");
- if (guid == "{43564548-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("High Efficiency Video Coding (HEVC)");
- if (guid == "{3253344D-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("MPEG-4 part 2 Video (M4S2)");
- if (guid == "{47504A4D-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Motion JPEG (MJPG)");
- if (guid == "{3334504D-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Microsoft MPEG 4 version 3 (MP43)");
- if (guid == "{5334504D-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("ISO MPEG 4 version 1 (MP4S)");
- if (guid == "{5634504D-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("MPEG-4 part 2 Video (MP4V)");
- if (guid == "{E06D8026-DB46-11CF-B4D1-00805F6CBBEA}")
- return QStringLiteral("MPEG-2 Video");
- if (guid == "{3147504D-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("MPEG-1 Video");
- if (guid == "{3153534D-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Windows Media Screen 1 (MSS1)");
- if (guid == "{3253534D-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Windows Media Video 9 Screen (MSS2)");
- if (guid == "{31564D57-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Windows Media Video 7 (WMV1)");
- if (guid == "{32564D57-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Windows Media Video 8 (WMV2)");
- if (guid == "{33564D57-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Windows Media Video 9 (WMV3)");
- if (guid == "{31435657-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("Windows Media Video VC1 (WVC1)");
- if (guid == "{30385056-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("VP8 Video");
- if (guid == "{30395056-0000-0010-8000-00AA00389B71}")
- return QStringLiteral("VP9 Video");
- return QStringLiteral("Unknown codec");
-}
-
-typedef HRESULT (WINAPI *q_SHCreateItemFromParsingName)(PCWSTR, IBindCtx *, const GUID&, void **);
-static q_SHCreateItemFromParsingName sHCreateItemFromParsingName = nullptr;
-#endif
-
-#if QT_CONFIG(wmsdk)
-
-namespace
-{
- struct QWMMetaDataKey
- {
- QString qtName;
- const wchar_t *wmName;
-
- QWMMetaDataKey(const QString &qtn, const wchar_t *wmn) : qtName(qtn), wmName(wmn) { }
- };
-}
-
-using QWMMetaDataKeys = QList<QWMMetaDataKey>;
-Q_GLOBAL_STATIC(QWMMetaDataKeys, metadataKeys)
-
-static const QWMMetaDataKeys *qt_wmMetaDataKeys()
-{
- if (metadataKeys->isEmpty()) {
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Title, L"Title"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::SubTitle, L"WM/SubTitle"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Author, L"Author"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Comment, L"Comment"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Description, L"Description"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Category, L"WM/Category"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Genre, L"WM/Genre"));
- //metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Date, 0));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Year, L"WM/Year"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::UserRating, L"Rating"));
- //metadataKeys->append(QWMMetaDataKey(QMediaMetaData::MetaDatawords, 0));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Language, L"WM/Language"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Publisher, L"WM/Publisher"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Copyright, L"Copyright"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::ParentalRating, L"WM/ParentalRating"));
- //metadataKeys->append(QWMMetaDataKey(QMediaMetaData::RatingOrganisation, L"RatingOrganisation"));
-
- // Media
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Size, L"FileSize"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::MediaType, L"MediaType"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Duration, L"Duration"));
-
- // Audio
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::AudioBitRate, L"AudioBitRate"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::AudioCodec, L"AudioCodec"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::ChannelCount, L"ChannelCount"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::SampleRate, L"Frequency"));
-
- // Music
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::AlbumTitle, L"WM/AlbumTitle"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::AlbumArtist, L"WM/AlbumArtist"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::ContributingArtist, L"Author"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Composer, L"WM/Composer"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Conductor, L"WM/Conductor"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Lyrics, L"WM/Lyrics"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Mood, L"WM/Mood"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::TrackNumber, L"WM/TrackNumber"));
- //metadataKeys->append(QWMMetaDataKey(QMediaMetaData::TrackCount, 0));
- //metadataKeys->append(QWMMetaDataKey(QMediaMetaData::CoverArtUriSmall, 0));
- //metadataKeys->append(QWMMetaDataKey(QMediaMetaData::CoverArtUriLarge, 0));
-
- // Image/Video
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Resolution, L"WM/VideoHeight"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::PixelAspectRatio, L"AspectRatioX"));
-
- // Video
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::VideoFrameRate, L"WM/VideoFrameRate"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::VideoBitRate, L"VideoBitRate"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::VideoCodec, L"VideoCodec"));
-
- //metadataKeys->append(QWMMetaDataKey(QMediaMetaData::PosterUri, 0));
-
- // Movie
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::ChapterNumber, L"ChapterNumber"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Director, L"WM/Director"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::LeadPerformer, L"LeadPerformer"));
- metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Writer, L"WM/Writer"));
- }
-
- return metadataKeys;
-}
-
-static QVariant getValue(IWMHeaderInfo *header, const wchar_t *key)
-{
- WORD streamNumber = 0;
- WMT_ATTR_DATATYPE type = WMT_TYPE_DWORD;
- WORD size = 0;
-
- if (header->GetAttributeByName(&streamNumber, key, &type, nullptr, &size) == S_OK) {
- switch (type) {
- case WMT_TYPE_DWORD:
- if (size == sizeof(DWORD)) {
- DWORD word;
- if (header->GetAttributeByName(
- &streamNumber,
- key,
- &type,
- reinterpret_cast<BYTE *>(&word),
- &size) == S_OK) {
- return int(word);
- }
- }
- break;
- case WMT_TYPE_STRING:
- {
- QString string;
- string.resize(size / 2); // size is in bytes, string is in UTF16
-
- if (header->GetAttributeByName(
- &streamNumber,
- key,
- &type,
- reinterpret_cast<BYTE *>(string.data()),
- &size) == S_OK) {
- return string;
- }
- }
- break;
- case WMT_TYPE_BINARY:
- {
- QByteArray bytes;
- bytes.resize(size);
- if (header->GetAttributeByName(
- &streamNumber,
- key,
- &type,
- reinterpret_cast<BYTE *>(bytes.data()),
- &size) == S_OK) {
- return bytes;
- }
- }
- break;
- case WMT_TYPE_BOOL:
- if (size == sizeof(DWORD)) {
- DWORD word;
- if (header->GetAttributeByName(
- &streamNumber,
- key,
- &type,
- reinterpret_cast<BYTE *>(&word),
- &size) == S_OK) {
- return bool(word);
- }
- }
- break;
- case WMT_TYPE_QWORD:
- if (size == sizeof(QWORD)) {
- QWORD word;
- if (header->GetAttributeByName(
- &streamNumber,
- key,
- &type,
- reinterpret_cast<BYTE *>(&word),
- &size) == S_OK) {
- return qint64(word);
- }
- }
- break;
- case WMT_TYPE_WORD:
- if (size == sizeof(WORD)){
- WORD word;
- if (header->GetAttributeByName(
- &streamNumber,
- key,
- &type,
- reinterpret_cast<BYTE *>(&word),
- &size) == S_OK) {
- return short(word);
- }
- }
- break;
- case WMT_TYPE_GUID:
- if (size == 16) {
- }
- break;
- default:
- break;
- }
- }
- return QVariant();
-}
-#endif
-
-#if QT_CONFIG(wshellitem)
-static QVariant convertValue(const PROPVARIANT& var)
-{
- QVariant value;
- switch (var.vt) {
- case VT_LPWSTR:
- value = QString::fromUtf16(reinterpret_cast<const ushort*>(var.pwszVal));
- break;
- case VT_UI4:
- value = uint(var.ulVal);
- break;
- case VT_UI8:
- value = qulonglong(var.uhVal.QuadPart);
- break;
- case VT_BOOL:
- value = bool(var.boolVal);
- break;
- case VT_FILETIME:
- SYSTEMTIME sysDate;
- if (!FileTimeToSystemTime(&var.filetime, &sysDate))
- break;
- value = QDate(sysDate.wYear, sysDate.wMonth, sysDate.wDay);
- break;
- case VT_STREAM:
- {
- STATSTG stat;
- if (FAILED(var.pStream->Stat(&stat, STATFLAG_NONAME)))
- break;
- void *data = malloc(stat.cbSize.QuadPart);
- ULONG read = 0;
- if (FAILED(var.pStream->Read(data, stat.cbSize.QuadPart, &read))) {
- free(data);
- break;
- }
- value = QImage::fromData(reinterpret_cast<const uchar*>(data), read);
- free(data);
- }
- break;
- case VT_VECTOR | VT_LPWSTR:
- QStringList vList;
- for (ULONG i = 0; i < var.calpwstr.cElems; ++i)
- vList.append(QString::fromUtf16(reinterpret_cast<const ushort*>(var.calpwstr.pElems[i])));
- value = vList;
- break;
- }
- return value;
-}
-#endif
-
-DirectShowMetaDataControl::DirectShowMetaDataControl(QObject *parent)
- : QMetaDataReaderControl(parent)
-{
-}
-
-DirectShowMetaDataControl::~DirectShowMetaDataControl() = default;
-
-bool DirectShowMetaDataControl::isMetaDataAvailable() const
-{
- return m_available;
-}
-
-QVariant DirectShowMetaDataControl::metaData(const QString &key) const
-{
- return m_metadata.value(key);
-}
-
-QStringList DirectShowMetaDataControl::availableMetaData() const
-{
- return m_metadata.keys();
-}
-
-static QString convertBSTR(BSTR *string)
-{
- QString value = QString::fromUtf16(reinterpret_cast<ushort *>(*string),
- ::SysStringLen(*string));
-
- ::SysFreeString(*string);
- string = nullptr;
-
- return value;
-}
-
-void DirectShowMetaDataControl::setMetadata(const QVariantMap &metadata)
-{
- m_metadata = metadata;
- setMetadataAvailable(!m_metadata.isEmpty());
-}
-
-void DirectShowMetaDataControl::updateMetadata(const QString &fileSrc, QVariantMap &metadata)
-{
-#if QT_CONFIG(wshellitem)
- if (!sHCreateItemFromParsingName) {
- QSystemLibrary lib(QStringLiteral("shell32"));
- sHCreateItemFromParsingName = (q_SHCreateItemFromParsingName)(lib.resolve("SHCreateItemFromParsingName"));
- }
-
- if (!fileSrc.isEmpty() && sHCreateItemFromParsingName) {
- IShellItem2* shellItem = nullptr;
- if (sHCreateItemFromParsingName(reinterpret_cast<const WCHAR*>(fileSrc.utf16()),
- nullptr, IID_PPV_ARGS(&shellItem)) == S_OK) {
-
- IPropertyStore *pStore = nullptr;
- if (shellItem->GetPropertyStore(GPS_DEFAULT, IID_PPV_ARGS(&pStore)) == S_OK) {
- DWORD cProps;
- if (SUCCEEDED(pStore->GetCount(&cProps))) {
- for (DWORD i = 0; i < cProps; ++i)
- {
- PROPERTYKEY key;
- PROPVARIANT var;
- PropVariantInit(&var);
- if (FAILED(pStore->GetAt(i, &key)))
- continue;
- if (FAILED(pStore->GetValue(key, &var)))
- continue;
-
- if (IsEqualPropertyKey(key, PKEY_Author)) {
- metadata.insert(QMediaMetaData::Author, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Title)) {
- metadata.insert(QMediaMetaData::Title, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Media_SubTitle)) {
- metadata.insert(QMediaMetaData::SubTitle, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_ParentalRating)) {
- metadata.insert(QMediaMetaData::ParentalRating, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Comment)) {
- metadata.insert(QMediaMetaData::Description, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Copyright)) {
- metadata.insert(QMediaMetaData::Copyright, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Media_ProviderStyle)) {
- metadata.insert(QMediaMetaData::Genre, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Media_Year)) {
- metadata.insert(QMediaMetaData::Year, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Media_DateEncoded)) {
- metadata.insert(QMediaMetaData::Date, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Rating)) {
- metadata.insert(QMediaMetaData::UserRating,
- int((convertValue(var).toUInt() - 1) / qreal(98) * 100));
- } else if (IsEqualPropertyKey(key, PKEY_Keywords)) {
- metadata.insert(QMediaMetaData::Keywords, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Language)) {
- metadata.insert(QMediaMetaData::Language, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Media_Publisher)) {
- metadata.insert(QMediaMetaData::Publisher, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Media_Duration)) {
- metadata.insert(QMediaMetaData::Duration,
- (convertValue(var).toLongLong() + 10000) / 10000);
- } else if (IsEqualPropertyKey(key, PKEY_Audio_EncodingBitrate)) {
- metadata.insert(QMediaMetaData::AudioBitRate, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Media_AverageLevel)) {
- metadata.insert(QMediaMetaData::AverageLevel, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Audio_ChannelCount)) {
- metadata.insert(QMediaMetaData::ChannelCount, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Audio_PeakValue)) {
- metadata.insert(QMediaMetaData::PeakValue, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Audio_SampleRate)) {
- metadata.insert(QMediaMetaData::SampleRate, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Music_AlbumTitle)) {
- metadata.insert(QMediaMetaData::AlbumTitle, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Music_AlbumArtist)) {
- metadata.insert(QMediaMetaData::AlbumArtist, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Music_Artist)) {
- metadata.insert(QMediaMetaData::ContributingArtist, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Music_Composer)) {
- metadata.insert(QMediaMetaData::Composer, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Music_Conductor)) {
- metadata.insert(QMediaMetaData::Conductor, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Music_Lyrics)) {
- metadata.insert(QMediaMetaData::Lyrics, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Music_Mood)) {
- metadata.insert(QMediaMetaData::Mood, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Music_TrackNumber)) {
- metadata.insert(QMediaMetaData::TrackNumber, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Music_Genre)) {
- metadata.insert(QMediaMetaData::Genre, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_ThumbnailStream)) {
- metadata.insert(QMediaMetaData::ThumbnailImage, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Video_FrameHeight)) {
- QSize res;
- res.setHeight(convertValue(var).toUInt());
- if (SUCCEEDED(pStore->GetValue(PKEY_Video_FrameWidth, &var)))
- res.setWidth(convertValue(var).toUInt());
- metadata.insert(QMediaMetaData::Resolution, res);
- } else if (IsEqualPropertyKey(key, PKEY_Video_HorizontalAspectRatio)) {
- QSize aspectRatio;
- aspectRatio.setWidth(convertValue(var).toUInt());
- if (SUCCEEDED(pStore->GetValue(PKEY_Video_VerticalAspectRatio, &var)))
- aspectRatio.setHeight(convertValue(var).toUInt());
- metadata.insert(QMediaMetaData::PixelAspectRatio, aspectRatio);
- } else if (IsEqualPropertyKey(key, PKEY_Video_FrameRate)) {
- metadata.insert(QMediaMetaData::VideoFrameRate,
- convertValue(var).toReal() / 1000);
- } else if (IsEqualPropertyKey(key, PKEY_Video_EncodingBitrate)) {
- metadata.insert(QMediaMetaData::VideoBitRate, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Video_Director)) {
- metadata.insert(QMediaMetaData::Director, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Media_Writer)) {
- metadata.insert(QMediaMetaData::Writer, convertValue(var));
- } else if (IsEqualPropertyKey(key, PKEY_Video_Compression)) {
- metadata.insert(QMediaMetaData::VideoCodec, nameForGUIDString(convertValue(var).toString()));
- } else if (IsEqualPropertyKey(key, PKEY_Audio_Format)) {
- metadata.insert(QMediaMetaData::AudioCodec, nameForGUIDString(convertValue(var).toString()));
- }
-
- PropVariantClear(&var);
- }
- }
-
- pStore->Release();
- }
-
- shellItem->Release();
- }
- }
-#else
- Q_UNUSED(fileSrc);
- Q_UNUSED(metadata);
-#endif
-}
-
-void DirectShowMetaDataControl::updateMetadata(IFilterGraph2 *graph, IBaseFilter *source, QVariantMap &metadata)
-{
-#if QT_CONFIG(wmsdk)
- if (IWMHeaderInfo *info = com_cast<IWMHeaderInfo>(source, IID_IWMHeaderInfo)) {
- const auto keys = *qt_wmMetaDataKeys();
- for (const QWMMetaDataKey &key : keys) {
- QVariant var = getValue(info, key.wmName);
- if (var.isValid()) {
- if (key.qtName == QMediaMetaData::Duration) {
- // duration is provided in 100-nanosecond units, convert to milliseconds
- var = (var.toLongLong() + 10000) / 10000;
- } else if (key.qtName == QMediaMetaData::Resolution) {
- QSize res;
- res.setHeight(var.toUInt());
- res.setWidth(getValue(info, L"WM/VideoWidth").toUInt());
- var = res;
- } else if (key.qtName == QMediaMetaData::VideoFrameRate) {
- var = var.toReal() / 1000.f;
- } else if (key.qtName == QMediaMetaData::PixelAspectRatio) {
- QSize aspectRatio;
- aspectRatio.setWidth(var.toUInt());
- aspectRatio.setHeight(getValue(info, L"AspectRatioY").toUInt());
- var = aspectRatio;
- } else if (key.qtName == QMediaMetaData::UserRating) {
- var = (var.toUInt() - 1) / qreal(98) * 100;
- }
-
- metadata.insert(key.qtName, var);
- }
- }
-
- info->Release();
- }
-
- if (!metadata.isEmpty())
- return;
-#endif
- {
- IAMMediaContent *content = nullptr;
-
- if ((!graph || graph->QueryInterface(
- IID_IAMMediaContent, reinterpret_cast<void **>(&content)) != S_OK)
- && (!source || source->QueryInterface(
- IID_IAMMediaContent, reinterpret_cast<void **>(&content)) != S_OK)) {
- content = nullptr;
- }
-
- if (content) {
- BSTR string = nullptr;
-
- if (content->get_AuthorName(&string) == S_OK)
- metadata.insert(QMediaMetaData::Author, convertBSTR(&string));
-
- if (content->get_Title(&string) == S_OK)
- metadata.insert(QMediaMetaData::Title, convertBSTR(&string));
-
- if (content->get_Description(&string) == S_OK)
- metadata.insert(QMediaMetaData::Description, convertBSTR(&string));
-
- if (content->get_Rating(&string) == S_OK)
- metadata.insert(QMediaMetaData::UserRating, convertBSTR(&string));
-
- if (content->get_Copyright(&string) == S_OK)
- metadata.insert(QMediaMetaData::Copyright, convertBSTR(&string));
-
- content->Release();
- }
- }
-}
-
-void DirectShowMetaDataControl::setMetadataAvailable(bool available)
-{
- if (m_available == available)
- return;
-
- m_available = available;
-
- // If the metadata is not available, notify about it immediately.
- Qt::ConnectionType type = m_available ? Qt::QueuedConnection : Qt::AutoConnection;
- QMetaObject::invokeMethod(this, "metaDataAvailableChanged", type, Q_ARG(bool, m_available));
- // Currently the metadata is changed only with its availability.
- QMetaObject::invokeMethod(this, "metaDataChanged", type);
-}
diff --git a/src/plugins/directshow/player/directshowmetadatacontrol.h b/src/plugins/directshow/player/directshowmetadatacontrol.h
deleted file mode 100644
index e66127ab3..000000000
--- a/src/plugins/directshow/player/directshowmetadatacontrol.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 DIRECTSHOWMETADATACONTROL_H
-#define DIRECTSHOWMETADATACONTROL_H
-
-#include <dshow.h>
-
-#include <qmetadatareadercontrol.h>
-
-#include "directshowglobal.h"
-
-#include <QtCore/qcoreevent.h>
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowMetaDataControl : public QMetaDataReaderControl
-{
- Q_OBJECT
-public:
- DirectShowMetaDataControl(QObject *parent = nullptr);
- ~DirectShowMetaDataControl() override;
-
- bool isMetaDataAvailable() const override;
-
- QVariant metaData(const QString &key) const override;
- QStringList availableMetaData() const override;
-
- void setMetadata(const QVariantMap &metadata);
-
- static void updateMetadata(const QString &fileSrc, QVariantMap &metadata);
- static void updateMetadata(IFilterGraph2 *graph, IBaseFilter *source, QVariantMap &metadata);
-
-private:
- void setMetadataAvailable(bool available);
-
- enum Event
- {
- MetaDataChanged = QEvent::User
- };
-
- QVariantMap m_metadata;
- bool m_available = false;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/directshow/player/directshowplayercontrol.cpp b/src/plugins/directshow/player/directshowplayercontrol.cpp
deleted file mode 100644
index 50e8d6421..000000000
--- a/src/plugins/directshow/player/directshowplayercontrol.cpp
+++ /dev/null
@@ -1,397 +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 <dshow.h>
-
-#include "directshowplayercontrol.h"
-
-#include "directshowplayerservice.h"
-
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qmath.h>
-#include <qaudio.h>
-
-DirectShowPlayerControl::DirectShowPlayerControl(DirectShowPlayerService *service, QObject *parent)
- : QMediaPlayerControl(parent)
- , m_service(service)
-{
-}
-
-DirectShowPlayerControl::~DirectShowPlayerControl()
-{
- if (m_audio)
- m_audio->Release();
-}
-
-QMediaPlayer::State DirectShowPlayerControl::state() const
-{
- return m_state;
-}
-
-QMediaPlayer::MediaStatus DirectShowPlayerControl::mediaStatus() const
-{
- return m_status;
-}
-
-qint64 DirectShowPlayerControl::duration() const
-{
- return m_duration;
-}
-
-qint64 DirectShowPlayerControl::position() const
-{
- if (m_pendingPosition != -1)
- return m_pendingPosition;
-
- return m_service->position();
-}
-
-void DirectShowPlayerControl::setPosition(qint64 position)
-{
- if (m_status == QMediaPlayer::EndOfMedia) {
- m_status = QMediaPlayer::LoadedMedia;
- emit mediaStatusChanged(m_status);
- }
-
- if (m_state == QMediaPlayer::StoppedState) {
- if (m_pendingPosition != position) {
- m_pendingPosition = position;
- emit positionChanged(m_pendingPosition);
- }
- return;
- }
-
- m_service->seek(position);
- m_pendingPosition = -1;
-}
-
-int DirectShowPlayerControl::volume() const
-{
- return m_volume;
-}
-
-void DirectShowPlayerControl::setVolume(int volume)
-{
- int boundedVolume = qBound(0, volume, 100);
-
- if (m_volume == boundedVolume)
- return;
-
- m_volume = boundedVolume;
-
- if (!m_muted)
- setVolumeHelper(m_volume);
-
- emit volumeChanged(m_volume);
-}
-
-bool DirectShowPlayerControl::isMuted() const
-{
- return m_muted;
-}
-
-void DirectShowPlayerControl::setMuted(bool muted)
-{
- if (m_muted == muted)
- return;
-
- m_muted = muted;
-
- setVolumeHelper(m_muted ? 0 : m_volume);
-
- emit mutedChanged(m_muted);
-}
-
-void DirectShowPlayerControl::setVolumeHelper(int volume)
-{
- if (!m_audio)
- return;
-
- long adjustedVolume;
- if (volume == 0) {
- adjustedVolume = -10000; // -100 dB (lower limit for put_Volume())
- } else if (volume == 100) {
- adjustedVolume = 0;
- } else {
- adjustedVolume = QAudio::convertVolume(volume / qreal(100),
- QAudio::LinearVolumeScale,
- QAudio::DecibelVolumeScale) * 100;
- }
-
- m_audio->put_Volume(adjustedVolume);
-}
-
-int DirectShowPlayerControl::bufferStatus() const
-{
- return m_service->bufferStatus();
-}
-
-bool DirectShowPlayerControl::isAudioAvailable() const
-{
- return m_streamTypes & DirectShowPlayerService::AudioStream;
-}
-
-bool DirectShowPlayerControl::isVideoAvailable() const
-{
- return m_streamTypes & DirectShowPlayerService::VideoStream;
-}
-
-bool DirectShowPlayerControl::isSeekable() const
-{
- return m_seekable;
-}
-
-QMediaTimeRange DirectShowPlayerControl::availablePlaybackRanges() const
-{
- return m_service->availablePlaybackRanges();
-}
-
-qreal DirectShowPlayerControl::playbackRate() const
-{
- return m_playbackRate;
-}
-
-void DirectShowPlayerControl::setPlaybackRate(qreal rate)
-{
- if (!qFuzzyCompare(m_playbackRate, rate)) {
- m_service->setRate(rate);
-
- emit playbackRateChanged(m_playbackRate = rate);
- }
-}
-
-QMediaContent DirectShowPlayerControl::media() const
-{
- return m_media;
-}
-
-const QIODevice *DirectShowPlayerControl::mediaStream() const
-{
- return m_stream;
-}
-
-void DirectShowPlayerControl::setMedia(const QMediaContent &media, QIODevice *stream)
-{
- if (m_media == media && m_stream == stream)
- return;
-
- m_pendingPosition = -1;
- m_emitPosition = -1;
-
- m_media = media;
- m_stream = stream;
-
- m_updateProperties &= PlaybackRateProperty;
-
- m_service->load(media, stream);
-
- emit mediaChanged(m_media);
- emitPropertyChanges();
-}
-
-void DirectShowPlayerControl::play()
-{
- playOrPause(QMediaPlayer::PlayingState);
-}
-
-void DirectShowPlayerControl::pause()
-{
- playOrPause(QMediaPlayer::PausedState);
-}
-
-void DirectShowPlayerControl::playOrPause(QMediaPlayer::State state)
-{
- if (m_status == QMediaPlayer::NoMedia || state == QMediaPlayer::StoppedState)
- return;
- if (m_status == QMediaPlayer::InvalidMedia) {
- setMedia(m_media, m_stream);
- if (m_error != QMediaPlayer::NoError)
- return;
- }
-
- m_emitPosition = -1;
- m_state = state;
-
- if (m_pendingPosition != -1)
- setPosition(m_pendingPosition);
-
- if (state == QMediaPlayer::PausedState)
- m_service->pause();
- else
- m_service->play();
-
- emit stateChanged(m_state);
-}
-
-void DirectShowPlayerControl::stop()
-{
- m_emitPosition = -1;
- m_service->stop();
- emit stateChanged(m_state = QMediaPlayer::StoppedState);
-}
-
-void DirectShowPlayerControl::customEvent(QEvent *event)
-{
- if (event->type() == QEvent::Type(PropertiesChanged)) {
- emitPropertyChanges();
-
- event->accept();
- } else {
- QMediaPlayerControl::customEvent(event);
- }
-}
-
-void DirectShowPlayerControl::emitPropertyChanges()
-{
- int properties = m_updateProperties;
- m_updateProperties = 0;
-
- if (properties & StatusProperty)
- emit mediaStatusChanged(m_status);
-
- if ((properties & ErrorProperty) && m_error != QMediaPlayer::NoError)
- emit error(m_error, m_errorString);
-
- if (properties & PlaybackRateProperty)
- emit playbackRateChanged(m_playbackRate);
-
- if (properties & StreamTypesProperty) {
- emit audioAvailableChanged(m_streamTypes & DirectShowPlayerService::AudioStream);
- emit videoAvailableChanged(m_streamTypes & DirectShowPlayerService::VideoStream);
- }
-
- if (properties & PositionProperty && m_emitPosition != -1)
- emit positionChanged(m_emitPosition);
-
- if (properties & DurationProperty)
- emit durationChanged(m_duration);
-
- if (properties & SeekableProperty)
- emit seekableChanged(m_seekable);
-
- if (properties & StateProperty)
- emit stateChanged(m_state);
-}
-
-void DirectShowPlayerControl::scheduleUpdate(int properties)
-{
- if (m_updateProperties == 0)
- QCoreApplication::postEvent(this, new QEvent(QEvent::Type(PropertiesChanged)));
-
- m_updateProperties |= properties;
-}
-
-void DirectShowPlayerControl::updateState(QMediaPlayer::State state)
-{
- if (m_state != state) {
- m_state = state;
-
- scheduleUpdate(StateProperty);
- }
-}
-
-void DirectShowPlayerControl::updateStatus(QMediaPlayer::MediaStatus status)
-{
- if (m_status != status) {
- m_status = status;
-
- scheduleUpdate(StatusProperty);
- }
-}
-
-void DirectShowPlayerControl::updateMediaInfo(qint64 duration, int streamTypes, bool seekable)
-{
- int properties = 0;
-
- if (m_duration != duration) {
- m_duration = duration;
-
- properties |= DurationProperty;
- }
- if (m_streamTypes != streamTypes) {
- m_streamTypes = streamTypes;
-
- properties |= StreamTypesProperty;
- }
-
- if (m_seekable != seekable) {
- m_seekable = seekable;
-
- properties |= SeekableProperty;
- }
-
- if (properties != 0)
- scheduleUpdate(properties);
-}
-
-void DirectShowPlayerControl::updatePlaybackRate(qreal rate)
-{
- if (!qFuzzyCompare(m_playbackRate, rate)) {
- m_playbackRate = rate;
-
- scheduleUpdate(PlaybackRateProperty);
- }
-}
-
-void DirectShowPlayerControl::updateAudioOutput(IBaseFilter *filter)
-{
- if (m_audio)
- m_audio->Release();
-
- m_audio = com_cast<IBasicAudio>(filter, IID_IBasicAudio);
- setVolumeHelper(m_muted ? 0 : m_volume);
-}
-
-void DirectShowPlayerControl::updateError(QMediaPlayer::Error error, const QString &errorString)
-{
- m_error = error;
- m_errorString = errorString;
-
- if (m_error != QMediaPlayer::NoError)
- scheduleUpdate(ErrorProperty);
-}
-
-void DirectShowPlayerControl::updatePosition(qint64 position)
-{
- if (m_emitPosition != position) {
- m_emitPosition = position;
-
- scheduleUpdate(PositionProperty);
- }
-}
diff --git a/src/plugins/directshow/player/directshowplayercontrol.h b/src/plugins/directshow/player/directshowplayercontrol.h
deleted file mode 100644
index 122f5be2f..000000000
--- a/src/plugins/directshow/player/directshowplayercontrol.h
+++ /dev/null
@@ -1,153 +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 DIRECTSHOWPLAYERCONTROL_H
-#define DIRECTSHOWPLAYERCONTROL_H
-
-#include <dshow.h>
-
-#include "qmediacontent.h"
-#include "qmediaplayercontrol.h"
-
-#include <QtCore/qcoreevent.h>
-
-#include "directshowplayerservice.h"
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowPlayerControl : public QMediaPlayerControl
-{
- Q_OBJECT
-public:
- DirectShowPlayerControl(DirectShowPlayerService *service, QObject *parent = nullptr);
- ~DirectShowPlayerControl() override;
-
- QMediaPlayer::State state() const override;
-
- QMediaPlayer::MediaStatus mediaStatus() const override;
-
- qint64 duration() const override;
-
- qint64 position() const override;
- void setPosition(qint64 position) override;
-
- int volume() const override;
- void setVolume(int volume) override;
-
- bool isMuted() const override;
- void setMuted(bool muted) override;
-
- int bufferStatus() const override;
-
- bool isAudioAvailable() const override;
- bool isVideoAvailable() const override;
-
- bool isSeekable() const override;
-
- QMediaTimeRange availablePlaybackRanges() const override;
-
- qreal playbackRate() const override;
- void setPlaybackRate(qreal rate) override;
-
- QMediaContent media() const override;
- const QIODevice *mediaStream() const override;
- void setMedia(const QMediaContent &media, QIODevice *stream) override;
-
- void play() override;
- void pause() override;
- void stop() override;
-
- void updateState(QMediaPlayer::State state);
- void updateStatus(QMediaPlayer::MediaStatus status);
- void updateMediaInfo(qint64 duration, int streamTypes, bool seekable);
- void updatePlaybackRate(qreal rate);
- void updateAudioOutput(IBaseFilter *filter);
- void updateError(QMediaPlayer::Error error, const QString &errorString);
- void updatePosition(qint64 position);
-
-protected:
- void customEvent(QEvent *event) override;
-
-private:
- enum Properties
- {
- StateProperty = 0x01,
- StatusProperty = 0x02,
- StreamTypesProperty = 0x04,
- DurationProperty = 0x08,
- PlaybackRateProperty = 0x10,
- SeekableProperty = 0x20,
- ErrorProperty = 0x40,
- PositionProperty = 0x80
- };
-
- enum Event
- {
- PropertiesChanged = QEvent::User
- };
-
- void playOrPause(QMediaPlayer::State state);
-
- void scheduleUpdate(int properties);
- void emitPropertyChanges();
- void setVolumeHelper(int volume);
-
- DirectShowPlayerService *m_service;
- IBasicAudio *m_audio = nullptr;
- QIODevice *m_stream = nullptr;
- int m_updateProperties = 0;
- QMediaPlayer::State m_state = QMediaPlayer::StoppedState;
- QMediaPlayer::MediaStatus m_status = QMediaPlayer::NoMedia;
- QMediaPlayer::Error m_error = QMediaPlayer::NoError;
- int m_streamTypes = 0;
- int m_volume = 100;
- bool m_muted = false;
- qint64 m_emitPosition = -1;
- qint64 m_pendingPosition = -1;
- qint64 m_duration = 0;
- qreal m_playbackRate = 0;
- bool m_seekable = false;
- QMediaContent m_media;
- QString m_errorString;
-
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/directshow/player/directshowplayerservice.cpp b/src/plugins/directshow/player/directshowplayerservice.cpp
deleted file mode 100644
index ce5c9fe56..000000000
--- a/src/plugins/directshow/player/directshowplayerservice.cpp
+++ /dev/null
@@ -1,1812 +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 <dshow.h>
-#ifdef min
-#undef min
-#endif
-#ifdef max
-#undef max
-#endif
-
-#include "directshowplayerservice.h"
-
-#include "directshowaudioendpointcontrol.h"
-#include "directshowmetadatacontrol.h"
-#include "vmr9videowindowcontrol.h"
-#include "directshowiosource.h"
-#include "directshowplayercontrol.h"
-#include "directshowvideorenderercontrol.h"
-#include "directshowutils.h"
-#include "directshowglobal.h"
-#include "directshowaudioprobecontrol.h"
-#include "directshowvideoprobecontrol.h"
-#include "directshowsamplegrabber.h"
-
-#if QT_CONFIG(evr)
-#include "directshowevrvideowindowcontrol.h"
-#else
-#include <mmreg.h>
-#endif
-
-#include "qmediacontent.h"
-
-#include <QtMultimedia/private/qtmultimedia-config_p.h>
-
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qdatetime.h>
-#include <QtCore/qdir.h>
-#include <QtCore/qthread.h>
-#include <QtCore/qvarlengtharray.h>
-#include <QtCore/qsize.h>
-
-#include <QtMultimedia/qaudiobuffer.h>
-#include <QtMultimedia/qvideoframe.h>
-#include <QtMultimedia/private/qmemoryvideobuffer_p.h>
-
-#if QT_CONFIG(wmsdk)
-# include <wmsdk.h>
-#endif
-
-#ifndef Q_CC_MINGW
-# include <comdef.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-Q_GLOBAL_STATIC(DirectShowEventLoop, qt_directShowEventLoop)
-
-static QString comError(HRESULT hr)
-{
-#ifndef Q_CC_MINGW // MinGW 5.3 no longer has swprintf_s().
- _com_error error(hr);
- return QString::fromWCharArray(error.ErrorMessage());
-#else
- Q_UNUSED(hr);
- return QString();
-#endif
-}
-
-// QMediaPlayer uses millisecond time units, direct show uses 100 nanosecond units.
-static const int qt_directShowTimeScale = 10000;
-
-class DirectShowPlayerServiceThread : public QThread
-{
-public:
- DirectShowPlayerServiceThread(DirectShowPlayerService *service)
- : m_service(service)
- {
- }
-
-protected:
- void run() override { m_service->run(); }
-
-private:
- DirectShowPlayerService *m_service;
-};
-
-DirectShowPlayerService::DirectShowPlayerService(QObject *parent)
- : QMediaService(parent)
- , m_loop(qt_directShowEventLoop())
- , m_taskHandle(::CreateEvent(nullptr, FALSE, FALSE, nullptr))
-{
- m_playerControl = new DirectShowPlayerControl(this);
- m_metaDataControl = new DirectShowMetaDataControl(this);
- m_audioEndpointControl = new DirectShowAudioEndpointControl(this);
-
- m_taskThread = new DirectShowPlayerServiceThread(this);
- m_taskThread->start();
-}
-
-DirectShowPlayerService::~DirectShowPlayerService()
-{
- {
- QMutexLocker locker(&m_mutex);
-
- releaseGraph();
-
- m_pendingTasks = Shutdown;
- ::SetEvent(m_taskHandle);
- }
-
- m_taskThread->wait();
- delete m_taskThread;
-
- if (m_audioOutput) {
- m_audioOutput->Release();
- m_audioOutput = nullptr;
- }
-
- if (m_videoOutput) {
- m_videoOutput->Release();
- m_videoOutput = nullptr;
- }
-
- delete m_playerControl;
- delete m_audioEndpointControl;
- delete m_metaDataControl;
- delete m_videoRendererControl;
- delete m_videoWindowControl;
- delete m_audioProbeControl;
- delete m_videoProbeControl;
-
- ::CloseHandle(m_taskHandle);
-}
-
-QMediaControl *DirectShowPlayerService::requestControl(const char *name)
-{
- if (qstrcmp(name, QMediaPlayerControl_iid) == 0)
- return m_playerControl;
- if (qstrcmp(name, QAudioOutputSelectorControl_iid) == 0)
- return m_audioEndpointControl;
- if (qstrcmp(name, QMetaDataReaderControl_iid) == 0)
- return m_metaDataControl;
- if (qstrcmp(name, QVideoRendererControl_iid) == 0) {
- if (!m_videoRendererControl && !m_videoWindowControl) {
- m_videoRendererControl = new DirectShowVideoRendererControl(m_loop);
-
- connect(m_videoRendererControl, &DirectShowVideoRendererControl::filterChanged,
- this, &DirectShowPlayerService::videoOutputChanged);
-
- return m_videoRendererControl;
- }
- return nullptr;
- }
- if (qstrcmp(name, QVideoWindowControl_iid) == 0) {
- if (!m_videoRendererControl && !m_videoWindowControl) {
- IBaseFilter *filter{};
-
-#if QT_CONFIG(evr)
- if (!qgetenv("QT_DIRECTSHOW_NO_EVR").toInt()) {
- DirectShowEvrVideoWindowControl *evrControl = new DirectShowEvrVideoWindowControl;
- if ((filter = evrControl->filter()))
- m_videoWindowControl = evrControl;
- else
- delete evrControl;
- }
-#endif
- // Fall back to the VMR9 if the EVR is not available
- if (!m_videoWindowControl) {
- Vmr9VideoWindowControl *vmr9Control = new Vmr9VideoWindowControl;
- filter = vmr9Control->filter();
- m_videoWindowControl = vmr9Control;
- }
-
- setVideoOutput(filter);
-
- return m_videoWindowControl;
- }
- return nullptr;
- }
- if (qstrcmp(name, QMediaAudioProbeControl_iid) == 0) {
- if (!m_audioProbeControl)
- m_audioProbeControl = new DirectShowAudioProbeControl();
- m_audioProbeControl->ref();
- updateAudioProbe();
- return m_audioProbeControl;
- }
- if (qstrcmp(name, QMediaVideoProbeControl_iid) == 0) {
- if (!m_videoProbeControl)
- m_videoProbeControl = new DirectShowVideoProbeControl();
- m_videoProbeControl->ref();
- updateVideoProbe();
- return m_videoProbeControl;
- }
- return nullptr;
-}
-
-void DirectShowPlayerService::releaseControl(QMediaControl *control)
-{
- if (!control) {
- qWarning("QMediaService::releaseControl():"
- " Attempted release of null control");
- } else if (control == m_videoRendererControl) {
- setVideoOutput(nullptr);
-
- delete m_videoRendererControl;
-
- m_videoRendererControl = nullptr;
- } else if (control == m_videoWindowControl) {
- setVideoOutput(nullptr);
-
- delete m_videoWindowControl;
-
- m_videoWindowControl = nullptr;
- } else if (control == m_audioProbeControl) {
- if (!m_audioProbeControl->deref()) {
- DirectShowAudioProbeControl *old = m_audioProbeControl;
- m_audioProbeControl = nullptr;
- updateAudioProbe();
- delete old;
- }
- } else if (control == m_videoProbeControl) {
- if (!m_videoProbeControl->deref()) {
- DirectShowVideoProbeControl *old = m_videoProbeControl;
- m_videoProbeControl = nullptr;
- updateVideoProbe();
- delete old;
- }
- }
-}
-
-void DirectShowPlayerService::load(const QMediaContent &media, QIODevice *stream)
-{
- QMutexLocker locker(&m_mutex);
-
- m_pendingTasks = 0;
-
- if (m_graph)
- releaseGraph();
-
- m_url = media.request().url();
-
- m_stream = stream;
- m_error = QMediaPlayer::NoError;
- m_errorString = QString();
- m_position = 0;
- m_seekPosition = -1;
- m_duration = 0;
- m_streamTypes = 0;
- m_executedTasks = 0;
- m_buffering = false;
- m_seekable = false;
- m_atEnd = false;
- m_dontCacheNextSeekResult = false;
- m_metaDataControl->setMetadata(QVariantMap());
-
- if (m_url.isEmpty() && !stream) {
- m_pendingTasks = 0;
- m_graphStatus = NoMedia;
- } else if (stream && (!stream->isReadable() || stream->isSequential())) {
- m_pendingTasks = 0;
- m_graphStatus = InvalidMedia;
- m_error = QMediaPlayer::ResourceError;
- } else {
- // {36b73882-c2c8-11cf-8b46-00805f6cef60}
- static const GUID iid_IFilterGraph2 = {
- 0x36b73882, 0xc2c8, 0x11cf, {0x8b, 0x46, 0x00, 0x80, 0x5f, 0x6c, 0xef, 0x60} };
- m_graphStatus = Loading;
-
- DirectShowUtils::CoInitializeIfNeeded();
- m_graph = com_new<IFilterGraph2>(CLSID_FilterGraph, iid_IFilterGraph2);
- m_graphBuilder = com_new<ICaptureGraphBuilder2>(CLSID_CaptureGraphBuilder2, IID_ICaptureGraphBuilder2);
-
- // Attach the filter graph to the capture graph.
- HRESULT hr = m_graphBuilder->SetFiltergraph(m_graph);
- if (FAILED(hr)) {
- qCWarning(qtDirectShowPlugin, "[0x%x] Failed to attach filter to capture graph", hr);
- m_graphBuilder->Release();
- m_graphBuilder = nullptr;
- }
-
- if (stream)
- m_pendingTasks = SetStreamSource;
- else
- m_pendingTasks = SetUrlSource;
-
- ::SetEvent(m_taskHandle);
- }
-
- m_playerControl->updateError(m_error, m_errorString);
- m_playerControl->updateMediaInfo(m_duration, m_streamTypes, m_seekable);
- m_playerControl->updateState(QMediaPlayer::StoppedState);
- m_playerControl->updatePosition(m_position);
- updateStatus();
-}
-
-void DirectShowPlayerService::doSetUrlSource(QMutexLocker *locker)
-{
- IBaseFilter *source = nullptr;
-
- HRESULT hr = E_FAIL;
- if (m_url.scheme() == QLatin1String("http") || m_url.scheme() == QLatin1String("https")) {
- static const GUID clsid_WMAsfReader = {
- 0x187463a0, 0x5bb7, 0x11d3, {0xac, 0xbe, 0x00, 0x80, 0xc7, 0x5e, 0x24, 0x6e} };
-
- // {56a868a6-0ad4-11ce-b03a-0020af0ba770}
- static const GUID iid_IFileSourceFilter = {
- 0x56a868a6, 0x0ad4, 0x11ce, {0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70} };
-
- if (IFileSourceFilter *fileSource = com_new<IFileSourceFilter>(clsid_WMAsfReader, iid_IFileSourceFilter)) {
- locker->unlock();
- hr = fileSource->Load(reinterpret_cast<const OLECHAR *>(m_url.toString().utf16()), nullptr);
-
- if (SUCCEEDED(hr)) {
- source = com_cast<IBaseFilter>(fileSource, IID_IBaseFilter);
-
- if (!SUCCEEDED(hr = m_graph->AddFilter(source, L"Source")) && source) {
- source->Release();
- source = nullptr;
- }
- }
- fileSource->Release();
- locker->relock();
- }
- }
-
- if (!SUCCEEDED(hr)) {
- locker->unlock();
- const QString urlString = m_url.isLocalFile()
- ? QDir::toNativeSeparators(m_url.toLocalFile()) : m_url.toString();
- hr = m_graph->AddSourceFilter(
- reinterpret_cast<const OLECHAR *>(urlString.utf16()), L"Source", &source);
- locker->relock();
- }
-
- if (SUCCEEDED(hr)) {
- m_executedTasks = SetSource;
- m_pendingTasks |= Render;
-
- if (m_audioOutput)
- m_pendingTasks |= SetAudioOutput;
- if (m_videoOutput)
- m_pendingTasks |= SetVideoOutput;
- if (m_audioProbeControl)
- m_pendingTasks |= SetAudioProbe;
- if (m_videoProbeControl)
- m_pendingTasks |= SetVideoProbe;
-
- if (m_rate != 1.0)
- m_pendingTasks |= SetRate;
-
- m_source = source;
- } else {
- m_graphStatus = InvalidMedia;
-
- switch (hr) {
- case VFW_E_UNKNOWN_FILE_TYPE:
- m_error = QMediaPlayer::FormatError;
- m_errorString = QString();
- break;
- default:
- m_error = QMediaPlayer::ResourceError;
- m_errorString = QString();
- qWarning("DirectShowPlayerService::doSetUrlSource: Unresolved error code 0x%x (%s)",
- uint(hr), qPrintable(comError(hr)));
- break;
- }
-
- QCoreApplication::postEvent(this, new QEvent(QEvent::Type(Error)));
- }
-}
-
-void DirectShowPlayerService::doSetStreamSource(QMutexLocker *locker)
-{
- Q_UNUSED(locker);
- DirectShowIOSource *source = new DirectShowIOSource(m_loop);
- source->setDevice(m_stream);
-
- const HRESULT hr = m_graph->AddFilter(source, L"Source");
- if (SUCCEEDED(hr)) {
- m_executedTasks = SetSource;
- m_pendingTasks |= Render;
-
- if (m_audioOutput)
- m_pendingTasks |= SetAudioOutput;
- if (m_videoOutput)
- m_pendingTasks |= SetVideoOutput;
-
- if (m_rate != 1.0)
- m_pendingTasks |= SetRate;
-
- m_source = source;
- } else {
- source->Release();
-
- m_pendingTasks = 0;
- m_graphStatus = InvalidMedia;
-
- m_error = QMediaPlayer::ResourceError;
- m_errorString = QString();
- qWarning("DirectShowPlayerService::doPlay: Unresolved error code 0x%x (%s)",
- uint(hr), qPrintable(comError(hr)));
-
- QCoreApplication::postEvent(this, new QEvent(QEvent::Type(Error)));
- }
-}
-
-void DirectShowPlayerService::doRender(QMutexLocker *locker)
-{
- m_pendingTasks |= m_executedTasks & (Play | Pause);
-
- if (IMediaControl *control = com_cast<IMediaControl>(m_graph, IID_IMediaControl)) {
- control->Stop();
- control->Release();
- }
-
- if (m_pendingTasks & SetAudioOutput) {
- m_graph->AddFilter(m_audioOutput, L"AudioOutput");
-
- m_pendingTasks ^= SetAudioOutput;
- m_executedTasks |= SetAudioOutput;
- }
- if (m_pendingTasks & SetVideoOutput) {
- m_graph->AddFilter(m_videoOutput, L"VideoOutput");
-
- m_pendingTasks ^= SetVideoOutput;
- m_executedTasks |= SetVideoOutput;
- }
-
- if (m_pendingTasks & SetAudioProbe) {
- doSetAudioProbe(locker);
- m_pendingTasks ^= SetAudioProbe;
- m_executedTasks |= SetAudioProbe;
- }
-
- if (m_pendingTasks & SetVideoProbe) {
- doSetVideoProbe(locker);
- m_pendingTasks ^= SetVideoProbe;
- m_executedTasks |= SetVideoProbe;
- }
-
- IFilterGraph2 *graph = m_graph;
- graph->AddRef();
-
- QVarLengthArray<IBaseFilter *, 16> filters;
- m_source->AddRef();
- filters.append(m_source);
-
- bool rendered = false;
-
- HRESULT renderHr = S_OK;
-
- while (!filters.isEmpty()) {
- IEnumPins *pins = nullptr;
- IBaseFilter *filter = filters[filters.size() - 1];
- filters.removeLast();
-
- if (!(m_pendingTasks & ReleaseFilters) && SUCCEEDED(filter->EnumPins(&pins))) {
- int outputs = 0;
- for (IPin *pin = nullptr; pins->Next(1, &pin, nullptr) == S_OK; pin->Release()) {
- PIN_DIRECTION direction;
- if (pin->QueryDirection(&direction) == S_OK && direction == PINDIR_OUTPUT) {
- ++outputs;
-
- IPin *peer = nullptr;
- if (pin->ConnectedTo(&peer) == S_OK) {
- PIN_INFO peerInfo;
- if (SUCCEEDED(peer->QueryPinInfo(&peerInfo)))
- filters.append(peerInfo.pFilter);
- peer->Release();
- } else {
- locker->unlock();
- HRESULT hr = graph->RenderEx(pin, /*AM_RENDEREX_RENDERTOEXISTINGRENDERERS*/ 1, nullptr);
- if (SUCCEEDED(hr)) {
- rendered = true;
- m_error = QMediaPlayer::NoError;
- } else if (!(m_executedTasks & SetVideoOutput)) {
- // Do not return an error if no video output is set yet.
- rendered = true;
- // Remember the error in this case.
- // Handle it when playing is requested and no video output has been provided.
- m_error = QMediaPlayer::ResourceError;
- m_errorString = QString("%1: %2").arg(__FUNCTION__).arg(qt_error_string(hr));
- } else if (renderHr == S_OK || renderHr == VFW_E_NO_DECOMPRESSOR){
- renderHr = hr;
- }
- locker->relock();
- }
- }
- }
-
- pins->Release();
-
- if (outputs == 0)
- rendered = true;
- }
- filter->Release();
- }
-
- if (m_audioOutput && !isConnected(m_audioOutput, PINDIR_INPUT)) {
- graph->RemoveFilter(m_audioOutput);
-
- m_executedTasks &= ~SetAudioOutput;
- }
-
- if (m_videoOutput && !isConnected(m_videoOutput, PINDIR_INPUT)) {
- graph->RemoveFilter(m_videoOutput);
-
- m_executedTasks &= ~SetVideoOutput;
- }
-
- graph->Release();
-
- if (!(m_pendingTasks & ReleaseFilters)) {
- if (rendered) {
- if (!(m_executedTasks & FinalizeLoad))
- m_pendingTasks |= FinalizeLoad;
- } else {
- m_pendingTasks = 0;
-
- m_graphStatus = InvalidMedia;
-
- if (!m_audioOutput && !m_videoOutput) {
- m_error = QMediaPlayer::ResourceError;
- m_errorString = QString();
- } else {
- switch (renderHr) {
- case VFW_E_UNSUPPORTED_AUDIO:
- case VFW_E_UNSUPPORTED_VIDEO:
- case VFW_E_UNSUPPORTED_STREAM:
- m_error = QMediaPlayer::FormatError;
- m_errorString = QString();
- break;
- default:
- m_error = QMediaPlayer::ResourceError;
- m_errorString = QString();
- qWarning("DirectShowPlayerService::doRender: Unresolved error code 0x%x (%s)",
- uint(renderHr), qPrintable(comError(renderHr)));
- }
- }
-
- QCoreApplication::postEvent(this, new QEvent(QEvent::Type(Error)));
- }
-
- m_executedTasks |= Render;
- }
-}
-
-void DirectShowPlayerService::doFinalizeLoad(QMutexLocker *locker)
-{
- if (m_graphStatus != Loaded) {
- if (IMediaEvent *event = com_cast<IMediaEvent>(m_graph, IID_IMediaEvent)) {
- event->GetEventHandle(reinterpret_cast<OAEVENT *>(&m_eventHandle));
- event->Release();
- }
- if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph, IID_IMediaSeeking)) {
- LONGLONG duration = 0;
- seeking->GetDuration(&duration);
- m_duration = duration / qt_directShowTimeScale;
-
- DWORD capabilities = 0;
- seeking->GetCapabilities(&capabilities);
- m_seekable = capabilities & AM_SEEKING_CanSeekAbsolute;
-
- seeking->Release();
- }
- }
-
- if ((m_executedTasks & SetOutputs) == SetOutputs) {
- m_streamTypes = AudioStream | VideoStream;
- } else {
- m_streamTypes = findStreamTypes(m_source);
- }
-
- m_executedTasks |= FinalizeLoad;
-
- m_graphStatus = Loaded;
-
- // Do not block gui thread while updating metadata from file.
- locker->unlock();
- DirectShowMetaDataControl::updateMetadata(m_url.toString(), m_metadata);
- locker->relock();
-
- QCoreApplication::postEvent(this, new QEvent(QEvent::Type(FinalizedLoad)));
-}
-
-void DirectShowPlayerService::releaseGraph()
-{
- if (m_videoProbeControl)
- m_videoProbeControl->flushVideoFrame();
-
- if (m_graph) {
- if (m_executingTask != 0) {
- // {8E1C39A1-DE53-11cf-AA63-0080C744528D}
- static const GUID iid_IAMOpenProgress = {
- 0x8E1C39A1, 0xDE53, 0x11cf, {0xAA, 0x63, 0x00, 0x80, 0xC7, 0x44, 0x52, 0x8D} };
-
- if (IAMOpenProgress *progress = com_cast<IAMOpenProgress>(
- m_graph, iid_IAMOpenProgress)) {
- progress->AbortOperation();
- progress->Release();
- }
- m_graph->Abort();
- }
-
- m_pendingTasks = ReleaseGraph;
-
- ::SetEvent(m_taskHandle);
-
- m_loop->wait(&m_mutex);
- DirectShowUtils::CoUninitializeIfNeeded();
- }
-}
-
-void DirectShowPlayerService::doReleaseGraph(QMutexLocker *locker)
-{
- Q_UNUSED(locker);
-
- if (IMediaControl *control = com_cast<IMediaControl>(m_graph, IID_IMediaControl)) {
- control->Stop();
- control->Release();
- }
-
- doReleaseAudioProbe(locker);
- doReleaseVideoProbe(locker);
-
- if (m_source) {
- m_source->Release();
- m_source = nullptr;
- }
-
- m_eventHandle = nullptr;
-
- m_graph->Release();
- m_graph = nullptr;
-
- if (m_graphBuilder) {
- m_graphBuilder->Release();
- m_graphBuilder = nullptr;
- }
-
- m_loop->wake();
-}
-
-QT_WARNING_PUSH
-QT_WARNING_DISABLE_GCC("-Wmissing-field-initializers")
-
-void DirectShowPlayerService::doSetVideoProbe(QMutexLocker *locker)
-{
- Q_UNUSED(locker);
-
- if (!m_graph || !m_graphBuilder) {
- qCWarning(qtDirectShowPlugin, "Attempting to set a video probe without a valid graph!");
- return;
- }
-
- // Create the sample grabber, if necessary.
- if (!m_videoSampleGrabber) {
- m_videoSampleGrabber = new DirectShowSampleGrabber;
- connect(m_videoSampleGrabber, &DirectShowSampleGrabber::bufferAvailable, this, &DirectShowPlayerService::onVideoBufferAvailable);
- }
-
- if (FAILED(m_graph->AddFilter(m_videoSampleGrabber->filter(), L"Video Sample Grabber"))) {
- qCWarning(qtDirectShowPlugin, "Failed to add the video sample grabber into the graph!");
- return;
- }
-
- DirectShowMediaType mediaType({ MEDIATYPE_Video, MEDIASUBTYPE_ARGB32 });
- m_videoSampleGrabber->setMediaType(&mediaType);
-
- // Connect source filter to sample grabber filter.
- HRESULT hr = m_graphBuilder->RenderStream(nullptr, &MEDIATYPE_Video,
- m_source, nullptr, m_videoSampleGrabber->filter());
- if (FAILED(hr)) {
- qCWarning(qtDirectShowPlugin, "[0x%x] Failed to connect the video sample grabber", hr);
- return;
- }
-
- m_videoSampleGrabber->start(DirectShowSampleGrabber::CallbackMethod::BufferCB);
-}
-
-void DirectShowPlayerService::doSetAudioProbe(QMutexLocker *locker)
-{
- Q_UNUSED(locker);
-
- if (!m_graph) {
- qCWarning(qtDirectShowPlugin, "Attempting to set an audio probe without a valid graph!");
- return;
- }
-
- // Create the sample grabber, if necessary.
- if (!m_audioSampleGrabber) {
- m_audioSampleGrabber = new DirectShowSampleGrabber;
- connect(m_audioSampleGrabber, &DirectShowSampleGrabber::bufferAvailable, this, &DirectShowPlayerService::onAudioBufferAvailable);
- }
-
- static const AM_MEDIA_TYPE mediaType { MEDIATYPE_Audio, MEDIASUBTYPE_PCM };
- m_audioSampleGrabber->setMediaType(&mediaType);
-
- if (FAILED(m_graph->AddFilter(m_audioSampleGrabber->filter(), L"Audio Sample Grabber"))) {
- qCWarning(qtDirectShowPlugin, "Failed to add the audio sample grabber into the graph!");
- return;
- }
-
- if (!DirectShowUtils::connectFilters(m_graph, m_source, m_audioSampleGrabber->filter(), true)) {
- // Connect source filter to sample grabber filter.
- HRESULT hr = m_graphBuilder
- ? m_graphBuilder->RenderStream(nullptr, &MEDIATYPE_Audio,
- m_source, nullptr, m_audioSampleGrabber->filter())
- : E_FAIL;
- if (FAILED(hr)) {
- qCWarning(qtDirectShowPlugin, "[0x%x] Failed to connect the audio sample grabber", hr);
- return;
- }
- }
-
- m_audioSampleGrabber->start(DirectShowSampleGrabber::CallbackMethod::BufferCB);
-}
-
-QT_WARNING_POP
-
-void DirectShowPlayerService::doReleaseVideoProbe(QMutexLocker *locker)
-{
- Q_UNUSED(locker);
-
- if (!m_graph)
- return;
-
- if (!m_videoSampleGrabber)
- return;
-
- m_videoSampleGrabber->stop();
- HRESULT hr = m_graph->RemoveFilter(m_videoSampleGrabber->filter());
- if (FAILED(hr)) {
- qCWarning(qtDirectShowPlugin, "Failed to remove the video sample grabber!");
- return;
- }
-
- m_videoSampleGrabber->deleteLater();
- m_videoSampleGrabber = nullptr;
-}
-
-void DirectShowPlayerService::doReleaseAudioProbe(QMutexLocker *locker)
-{
- Q_UNUSED(locker);
-
- if (!m_graph)
- return;
-
- if (!m_audioSampleGrabber)
- return;
-
- m_audioSampleGrabber->stop();
- HRESULT hr = m_graph->RemoveFilter(m_audioSampleGrabber->filter());
- if (FAILED(hr)) {
- qCWarning(qtDirectShowPlugin, "Failed to remove the audio sample grabber!");
- return;
- }
-
- m_audioSampleGrabber->deleteLater();
- m_audioSampleGrabber = nullptr;
-}
-
-int DirectShowPlayerService::findStreamTypes(IBaseFilter *source) const
-{
- QVarLengthArray<IBaseFilter *, 16> filters;
- source->AddRef();
- filters.append(source);
-
- int streamTypes = 0;
-
- while (!filters.isEmpty()) {
- IEnumPins *pins = nullptr;
- IBaseFilter *filter = filters[filters.size() - 1];
- filters.removeLast();
-
- if (SUCCEEDED(filter->EnumPins(&pins))) {
- for (IPin *pin = nullptr; pins->Next(1, &pin, nullptr) == S_OK; pin->Release()) {
- PIN_DIRECTION direction;
- if (pin->QueryDirection(&direction) == S_OK && direction == PINDIR_OUTPUT) {
- DirectShowMediaType connectionType;
- if (SUCCEEDED(pin->ConnectionMediaType(&connectionType))) {
- IPin *peer = nullptr;
-
- if (connectionType->majortype == MEDIATYPE_Audio) {
- streamTypes |= AudioStream;
- } else if (connectionType->majortype == MEDIATYPE_Video) {
- streamTypes |= VideoStream;
- } else if (SUCCEEDED(pin->ConnectedTo(&peer))) {
- PIN_INFO peerInfo;
- if (SUCCEEDED(peer->QueryPinInfo(&peerInfo)))
- filters.append(peerInfo.pFilter);
- peer->Release();
- }
- } else {
- streamTypes |= findStreamType(pin);
- }
- }
- }
- pins->Release();
- }
- filter->Release();
- }
- return streamTypes;
-}
-
-int DirectShowPlayerService::findStreamType(IPin *pin) const
-{
- IEnumMediaTypes *types;
-
- if (SUCCEEDED(pin->EnumMediaTypes(&types))) {
- bool video = false;
- bool audio = false;
- bool other = false;
-
- for (AM_MEDIA_TYPE *type = nullptr;
- types->Next(1, &type, nullptr) == S_OK;
- DirectShowMediaType::deleteType(type)) {
- if (type->majortype == MEDIATYPE_Audio)
- audio = true;
- else if (type->majortype == MEDIATYPE_Video)
- video = true;
- else
- other = true;
- }
- types->Release();
-
- if (other)
- return 0;
- else if (audio && !video)
- return AudioStream;
- else if (!audio && video)
- return VideoStream;
- else
- return 0;
- } else {
- return 0;
- }
-}
-
-void DirectShowPlayerService::play()
-{
- QMutexLocker locker(&m_mutex);
-
- m_pendingTasks &= ~Pause;
- m_pendingTasks |= Play;
-
- if (m_executedTasks & Render) {
- if (m_executedTasks & Stop) {
- m_atEnd = false;
- if (m_seekPosition == -1) {
- m_dontCacheNextSeekResult = true;
- m_seekPosition = 0;
- m_position = 0;
- m_pendingTasks |= Seek;
- }
- m_executedTasks ^= Stop;
- }
-
- ::SetEvent(m_taskHandle);
- }
-
- updateStatus();
-}
-
-void DirectShowPlayerService::doPlay(QMutexLocker *locker)
-{
- // Invalidate if there is an error while loading.
- if (m_error != QMediaPlayer::NoError) {
- m_graphStatus = InvalidMedia;
- if (!m_errorString.isEmpty())
- qWarning("%s", qPrintable(m_errorString));
- m_errorString = QString();
- QCoreApplication::postEvent(this, new QEvent(QEvent::Type(Error)));
- return;
- }
-
- if (IMediaControl *control = com_cast<IMediaControl>(m_graph, IID_IMediaControl)) {
- locker->unlock();
- HRESULT hr = control->Run();
- locker->relock();
-
- control->Release();
-
- if (SUCCEEDED(hr)) {
- m_executedTasks |= Play;
-
- QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StatusChange)));
- } else {
- m_error = QMediaPlayer::ResourceError;
- m_errorString = QString();
- qWarning("DirectShowPlayerService::doPlay: Unresolved error code 0x%x (%s)",
- uint(hr), qPrintable(comError(hr)));
-
- QCoreApplication::postEvent(this, new QEvent(QEvent::Type(Error)));
- }
- }
-}
-
-void DirectShowPlayerService::pause()
-{
- QMutexLocker locker(&m_mutex);
-
- m_pendingTasks &= ~Play;
- m_pendingTasks |= Pause;
-
- if (m_executedTasks & Render) {
- if (m_executedTasks & Stop) {
- if (m_seekPosition == -1) {
- m_dontCacheNextSeekResult = true;
- m_seekPosition = 0;
- m_position = 0;
- m_pendingTasks |= Seek;
- }
- m_executedTasks ^= Stop;
- }
-
- ::SetEvent(m_taskHandle);
- }
-
- updateStatus();
-}
-
-void DirectShowPlayerService::doPause(QMutexLocker *locker)
-{
- if (IMediaControl *control = com_cast<IMediaControl>(m_graph, IID_IMediaControl)) {
- locker->unlock();
- HRESULT hr = control->Pause();
- locker->relock();
-
- control->Release();
-
- if (SUCCEEDED(hr)) {
- IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph, IID_IMediaSeeking);
- if (!m_atEnd && seeking) {
- LONGLONG position = 0;
-
- seeking->GetCurrentPosition(&position);
- seeking->Release();
-
- m_position = position / qt_directShowTimeScale;
- } else {
- m_position = 0;
- m_atEnd = false;
- }
-
- m_executedTasks |= Pause;
-
- QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StatusChange)));
- } else {
- m_error = QMediaPlayer::ResourceError;
- m_errorString = QString();
- qWarning("DirectShowPlayerService::doPause: Unresolved error code 0x%x (%s)",
- uint(hr), qPrintable(comError(hr)));
-
- QCoreApplication::postEvent(this, new QEvent(QEvent::Type(Error)));
- }
- }
-}
-
-void DirectShowPlayerService::stop()
-{
- QMutexLocker locker(&m_mutex);
-
- m_pendingTasks &= ~(Play | Pause | Seek);
-
- if ((m_executingTask | m_executedTasks) & (Play | Pause | Seek)) {
- m_pendingTasks |= Stop;
- if (m_videoProbeControl)
- m_videoProbeControl->flushVideoFrame();
-
- ::SetEvent(m_taskHandle);
-
- m_loop->wait(&m_mutex);
- }
-
- updateStatus();
-}
-
-void DirectShowPlayerService::doStop(QMutexLocker *locker)
-{
- Q_UNUSED(locker);
- if (m_executedTasks & (Play | Pause)) {
- if (IMediaControl *control = com_cast<IMediaControl>(m_graph, IID_IMediaControl)) {
- control->Stop();
- control->Release();
- }
-
- m_seekPosition = 0;
- m_position = 0;
- m_dontCacheNextSeekResult = true;
- m_pendingTasks |= Seek;
-
- m_executedTasks &= ~(Play | Pause);
-
- QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StatusChange)));
- }
-
- m_executedTasks |= Stop;
-
- m_loop->wake();
-}
-
-void DirectShowPlayerService::setRate(qreal rate)
-{
- QMutexLocker locker(&m_mutex);
-
- m_rate = rate;
-
- m_pendingTasks |= SetRate;
-
- if (m_executedTasks & FinalizeLoad)
- ::SetEvent(m_taskHandle);
-}
-
-void DirectShowPlayerService::doSetRate(QMutexLocker *locker)
-{
- if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph, IID_IMediaSeeking)) {
- // Cache current values as we can't query IMediaSeeking during a seek due to the
- // possibility of a deadlock when flushing the VideoSurfaceFilter.
- LONGLONG currentPosition = 0;
- seeking->GetCurrentPosition(&currentPosition);
- m_position = currentPosition / qt_directShowTimeScale;
-
- LONGLONG minimum = 0;
- LONGLONG maximum = 0;
- m_playbackRange = SUCCEEDED(seeking->GetAvailable(&minimum, &maximum))
- ? QMediaTimeRange(minimum / qt_directShowTimeScale, maximum / qt_directShowTimeScale)
- : QMediaTimeRange();
-
- locker->unlock();
- HRESULT hr = seeking->SetRate(m_rate);
- locker->relock();
-
- if (!SUCCEEDED(hr)) {
- qWarning("%s: Audio device or filter does not support rate: %.2f. " \
- "Falling back to previous value.", __FUNCTION__, m_rate);
-
- double rate = 0.0;
- m_rate = SUCCEEDED(seeking->GetRate(&rate))
- ? rate
- : 1.0;
- }
-
- seeking->Release();
- } else if (m_rate != 1.0) {
- m_rate = 1.0;
- }
- QCoreApplication::postEvent(this, new QEvent(QEvent::Type(RateChange)));
-}
-
-qint64 DirectShowPlayerService::position() const
-{
- QMutexLocker locker(const_cast<QMutex *>(&m_mutex));
-
- if (m_graphStatus == Loaded) {
- if (m_executingTask == Seek || m_executingTask == SetRate || (m_pendingTasks & Seek))
- return m_position;
- if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph, IID_IMediaSeeking)) {
- LONGLONG position = 0;
-
- seeking->GetCurrentPosition(&position);
- seeking->Release();
-
- const_cast<qint64 &>(m_position) = position / qt_directShowTimeScale;
-
- return m_position;
- }
- }
- return 0;
-}
-
-QMediaTimeRange DirectShowPlayerService::availablePlaybackRanges() const
-{
- QMutexLocker locker(const_cast<QMutex *>(&m_mutex));
-
- if (m_graphStatus == Loaded) {
- if (m_executingTask == Seek || m_executingTask == SetRate || (m_pendingTasks & Seek))
- return m_playbackRange;
- if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph, IID_IMediaSeeking)) {
- LONGLONG minimum = 0;
- LONGLONG maximum = 0;
-
- HRESULT hr = seeking->GetAvailable(&minimum, &maximum);
- seeking->Release();
-
- if (SUCCEEDED(hr))
- return QMediaTimeRange(minimum, maximum);
- }
- }
- return QMediaTimeRange();
-}
-
-void DirectShowPlayerService::seek(qint64 position)
-{
- QMutexLocker locker(&m_mutex);
-
- m_seekPosition = position;
-
- m_pendingTasks |= Seek;
-
- if (m_executedTasks & FinalizeLoad)
- ::SetEvent(m_taskHandle);
-}
-
-void DirectShowPlayerService::doSeek(QMutexLocker *locker)
-{
- if (m_seekPosition == -1)
- return;
-
- if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph, IID_IMediaSeeking)) {
- LONGLONG seekPosition = LONGLONG(m_seekPosition) * qt_directShowTimeScale;
-
- // Cache current values as we can't query IMediaSeeking during a seek due to the
- // possibility of a deadlock when flushing the VideoSurfaceFilter.
- LONGLONG currentPosition = 0;
- if (!m_dontCacheNextSeekResult) {
- seeking->GetCurrentPosition(&currentPosition);
- m_position = currentPosition / qt_directShowTimeScale;
- }
-
- LONGLONG minimum = 0;
- LONGLONG maximum = 0;
- m_playbackRange = SUCCEEDED(seeking->GetAvailable(&minimum, &maximum))
- ? QMediaTimeRange(
- minimum / qt_directShowTimeScale, maximum / qt_directShowTimeScale)
- : QMediaTimeRange();
-
- locker->unlock();
- seeking->SetPositions(
- &seekPosition, AM_SEEKING_AbsolutePositioning, nullptr, AM_SEEKING_NoPositioning);
- locker->relock();
-
- if (!m_dontCacheNextSeekResult) {
- seeking->GetCurrentPosition(&currentPosition);
- m_position = currentPosition / qt_directShowTimeScale;
- }
-
- seeking->Release();
-
- QCoreApplication::postEvent(this, new QEvent(QEvent::Type(PositionChange)));
- }
-
- m_seekPosition = -1;
- m_dontCacheNextSeekResult = false;
-}
-
-int DirectShowPlayerService::bufferStatus() const
-{
-#if QT_CONFIG(wmsdk)
- QMutexLocker locker(const_cast<QMutex *>(&m_mutex));
-
- if (IWMReaderAdvanced2 *reader = com_cast<IWMReaderAdvanced2>(
- m_source, IID_IWMReaderAdvanced2)) {
- DWORD percentage = 0;
-
- reader->GetBufferProgress(&percentage, nullptr);
- reader->Release();
-
- return percentage;
- }
- return 0;
-#else
- return 0;
-#endif
-}
-
-void DirectShowPlayerService::setAudioOutput(IBaseFilter *filter)
-{
- QMutexLocker locker(&m_mutex);
-
- if (m_graph) {
- if (m_audioOutput) {
- if (m_executedTasks & SetAudioOutput) {
- m_pendingTasks |= ReleaseAudioOutput;
-
- ::SetEvent(m_taskHandle);
-
- m_loop->wait(&m_mutex);
- }
- m_audioOutput->Release();
- }
-
- m_audioOutput = filter;
-
- if (m_audioOutput) {
- m_audioOutput->AddRef();
-
- m_pendingTasks |= SetAudioOutput;
-
- if (m_executedTasks & SetSource) {
- m_pendingTasks |= Render;
-
- ::SetEvent(m_taskHandle);
- }
- } else {
- m_pendingTasks &= ~ SetAudioOutput;
- }
- } else {
- if (m_audioOutput)
- m_audioOutput->Release();
-
- m_audioOutput = filter;
-
- if (m_audioOutput)
- m_audioOutput->AddRef();
- }
-
- m_playerControl->updateAudioOutput(m_audioOutput);
-}
-
-void DirectShowPlayerService::doReleaseAudioOutput(QMutexLocker *locker)
-{
- Q_UNUSED(locker);
- m_pendingTasks |= m_executedTasks & (Play | Pause);
-
- if (IMediaControl *control = com_cast<IMediaControl>(m_graph, IID_IMediaControl)) {
- control->Stop();
- control->Release();
- }
-
- IBaseFilter *decoder = getConnected(m_audioOutput, PINDIR_INPUT);
- if (!decoder) {
- decoder = m_audioOutput;
- decoder->AddRef();
- }
-
- // {DCFBDCF6-0DC2-45f5-9AB2-7C330EA09C29}
- static const GUID iid_IFilterChain = {
- 0xDCFBDCF6, 0x0DC2, 0x45f5, {0x9A, 0xB2, 0x7C, 0x33, 0x0E, 0xA0, 0x9C, 0x29} };
-
- if (IFilterChain *chain = com_cast<IFilterChain>(m_graph, iid_IFilterChain)) {
- chain->RemoveChain(decoder, m_audioOutput);
- chain->Release();
- } else {
- m_graph->RemoveFilter(m_audioOutput);
- }
-
- decoder->Release();
-
- m_executedTasks &= ~SetAudioOutput;
-
- m_loop->wake();
-}
-
-void DirectShowPlayerService::setVideoOutput(IBaseFilter *filter)
-{
- QMutexLocker locker(&m_mutex);
-
- if (m_graph) {
- if (m_videoOutput) {
- if (m_executedTasks & SetVideoOutput) {
- m_pendingTasks |= ReleaseVideoOutput;
-
- ::SetEvent(m_taskHandle);
-
- m_loop->wait(&m_mutex);
- }
- m_videoOutput->Release();
- }
-
- m_videoOutput = filter;
-
- if (m_videoOutput) {
- m_videoOutput->AddRef();
-
- m_pendingTasks |= SetVideoOutput;
-
- if (m_executedTasks & SetSource) {
- m_pendingTasks |= Render;
-
- ::SetEvent(m_taskHandle);
- }
- }
- } else {
- if (m_videoOutput)
- m_videoOutput->Release();
-
- m_videoOutput = filter;
-
- if (m_videoOutput)
- m_videoOutput->AddRef();
- }
-}
-
-void DirectShowPlayerService::updateAudioProbe()
-{
- QMutexLocker locker(&m_mutex);
-
- // Set/Activate the audio probe.
- if (m_graph) {
- // If we don't have a audio probe, then stop and release the audio sample grabber
- if (!m_audioProbeControl && (m_executedTasks & SetAudioProbe)) {
- m_pendingTasks |= ReleaseAudioProbe;
- ::SetEvent(m_taskHandle);
- m_loop->wait(&m_mutex);
- } else if (m_audioProbeControl) {
- m_pendingTasks |= SetAudioProbe;
- }
- }
-}
-
-void DirectShowPlayerService::updateVideoProbe()
-{
- QMutexLocker locker(&m_mutex);
-
- // Set/Activate the video probe.
- if (m_graph) {
- // If we don't have a video probe, then stop and release the video sample grabber
- if (!m_videoProbeControl && (m_executedTasks & SetVideoProbe)) {
- m_pendingTasks |= ReleaseVideoProbe;
- ::SetEvent(m_taskHandle);
- m_loop->wait(&m_mutex);
- } else if (m_videoProbeControl){
- m_pendingTasks |= SetVideoProbe;
- }
- }
-}
-
-void DirectShowPlayerService::doReleaseVideoOutput(QMutexLocker *locker)
-{
- Q_UNUSED(locker);
- m_pendingTasks |= m_executedTasks & (Play | Pause);
-
- if (IMediaControl *control = com_cast<IMediaControl>(m_graph, IID_IMediaControl)) {
- control->Stop();
- control->Release();
- }
-
- IBaseFilter *intermediate = nullptr;
- if (!SUCCEEDED(m_graph->FindFilterByName(L"Color Space Converter", &intermediate))) {
- intermediate = m_videoOutput;
- intermediate->AddRef();
- }
-
- IBaseFilter *decoder = getConnected(intermediate, PINDIR_INPUT);
- if (!decoder) {
- decoder = intermediate;
- decoder->AddRef();
- }
-
- // {DCFBDCF6-0DC2-45f5-9AB2-7C330EA09C29}
- static const GUID iid_IFilterChain = {
- 0xDCFBDCF6, 0x0DC2, 0x45f5, {0x9A, 0xB2, 0x7C, 0x33, 0x0E, 0xA0, 0x9C, 0x29} };
-
- if (IFilterChain *chain = com_cast<IFilterChain>(m_graph, iid_IFilterChain)) {
- chain->RemoveChain(decoder, m_videoOutput);
- chain->Release();
- } else {
- m_graph->RemoveFilter(m_videoOutput);
- }
-
- intermediate->Release();
- decoder->Release();
-
- m_executedTasks &= ~SetVideoOutput;
-
- m_loop->wake();
-}
-
-void DirectShowPlayerService::customEvent(QEvent *event)
-{
- if (event->type() == QEvent::Type(FinalizedLoad)) {
- QMutexLocker locker(&m_mutex);
-
- m_playerControl->updateMediaInfo(m_duration, m_streamTypes, m_seekable);
- if (m_metadata.isEmpty())
- DirectShowMetaDataControl::updateMetadata(m_graph, m_source, m_metadata);
-
- m_metaDataControl->setMetadata(m_metadata);
- m_metadata.clear();
-
- updateStatus();
- } else if (event->type() == QEvent::Type(Error)) {
- QMutexLocker locker(&m_mutex);
-
- if (m_error != QMediaPlayer::NoError) {
- m_playerControl->updateError(m_error, m_errorString);
- m_playerControl->updateMediaInfo(m_duration, m_streamTypes, m_seekable);
- m_playerControl->updateState(QMediaPlayer::StoppedState);
- updateStatus();
- }
- } else if (event->type() == QEvent::Type(RateChange)) {
- QMutexLocker locker(&m_mutex);
-
- m_playerControl->updatePlaybackRate(m_rate);
- } else if (event->type() == QEvent::Type(StatusChange)) {
- QMutexLocker locker(&m_mutex);
-
- updateStatus();
- m_playerControl->updatePosition(m_position);
- } else if (event->type() == QEvent::Type(DurationChange)) {
- QMutexLocker locker(&m_mutex);
-
- m_playerControl->updateMediaInfo(m_duration, m_streamTypes, m_seekable);
- } else if (event->type() == QEvent::Type(EndOfMedia)) {
- QMutexLocker locker(&m_mutex);
-
- if (m_atEnd) {
- m_playerControl->updateState(QMediaPlayer::StoppedState);
- m_playerControl->updateStatus(QMediaPlayer::EndOfMedia);
- m_playerControl->updatePosition(m_position);
- if (m_videoProbeControl)
- m_videoProbeControl->flushVideoFrame();
- }
- } else if (event->type() == QEvent::Type(PositionChange)) {
- QMutexLocker locker(&m_mutex);
-
- if (m_playerControl->mediaStatus() == QMediaPlayer::EndOfMedia)
- m_playerControl->updateStatus(QMediaPlayer::LoadedMedia);
- m_playerControl->updatePosition(m_position);
- // Emits only when seek has been performed.
- if (m_videoRendererControl)
- emit m_videoRendererControl->positionChanged(m_position);
- } else {
- QMediaService::customEvent(event);
- }
-}
-
-void DirectShowPlayerService::videoOutputChanged()
-{
- setVideoOutput(m_videoRendererControl->filter());
-}
-
-QT_WARNING_PUSH
-QT_WARNING_DISABLE_GCC("-Wmissing-field-initializers")
-
-void DirectShowPlayerService::onAudioBufferAvailable(double time, const QByteArray &data)
-{
- QMutexLocker locker(&m_mutex);
- if (!m_audioProbeControl || !m_audioSampleGrabber)
- return;
-
- DirectShowMediaType mt(AM_MEDIA_TYPE { GUID_NULL });
- const bool ok = m_audioSampleGrabber->getConnectedMediaType(&mt);
- if (!ok)
- return;
-
- if (mt->majortype != MEDIATYPE_Audio)
- return;
-
- if (mt->subtype != MEDIASUBTYPE_PCM)
- return;
-
- const bool isWfx = ((mt->formattype == FORMAT_WaveFormatEx) && (mt->cbFormat >= sizeof(WAVEFORMATEX)));
- WAVEFORMATEX *wfx = isWfx ? reinterpret_cast<WAVEFORMATEX *>(mt->pbFormat) : nullptr;
-
- if (!wfx)
- return;
-
- if (wfx->wFormatTag != WAVE_FORMAT_PCM && wfx->wFormatTag != WAVE_FORMAT_EXTENSIBLE)
- return;
-
- if ((wfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) && (wfx->cbSize >= sizeof(WAVEFORMATEXTENSIBLE))) {
- WAVEFORMATEXTENSIBLE *wfxe = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(wfx);
- if (wfxe->SubFormat != KSDATAFORMAT_SUBTYPE_PCM)
- return;
- }
-
- QAudioFormat format;
- format.setSampleRate(wfx->nSamplesPerSec);
- format.setChannelCount(wfx->nChannels);
- format.setSampleSize(wfx->wBitsPerSample);
- format.setCodec("audio/pcm");
- format.setByteOrder(QAudioFormat::LittleEndian);
- if (format.sampleSize() == 8)
- format.setSampleType(QAudioFormat::UnSignedInt);
- else
- format.setSampleType(QAudioFormat::SignedInt);
-
- const quint64 startTime = quint64(time * 1000.);
- QAudioBuffer audioBuffer(data,
- format,
- startTime);
-
- Q_EMIT m_audioProbeControl->audioBufferProbed(audioBuffer);
-}
-
-void DirectShowPlayerService::onVideoBufferAvailable(double time, const QByteArray &data)
-{
- Q_UNUSED(time);
-
- QMutexLocker locker(&m_mutex);
- if (!m_videoProbeControl || !m_videoSampleGrabber)
- return;
-
- DirectShowMediaType mt(AM_MEDIA_TYPE { GUID_NULL });
- const bool ok = m_videoSampleGrabber->getConnectedMediaType(&mt);
- if (!ok)
- return;
-
- if (mt->majortype != MEDIATYPE_Video)
- return;
-
- QVideoFrame::PixelFormat format = DirectShowMediaType::pixelFormatFromType(&mt);
- if (format == QVideoFrame::Format_Invalid) {
- qCWarning(qtDirectShowPlugin, "Invalid format, stopping video probes!");
- m_videoSampleGrabber->stop();
- return;
- }
-
- const QVideoSurfaceFormat &videoFormat = DirectShowMediaType::videoFormatFromType(&mt);
- if (!videoFormat.isValid())
- return;
-
- const QSize &size = videoFormat.frameSize();
-
- const int bytesPerLine = DirectShowMediaType::bytesPerLine(videoFormat);
- QVideoFrame frame(new QMemoryVideoBuffer(data, bytesPerLine),
- size,
- format);
-
- m_videoProbeControl->probeVideoFrame(frame);
-}
-
-QT_WARNING_POP
-
-void DirectShowPlayerService::graphEvent(QMutexLocker *locker)
-{
- Q_UNUSED(locker);
- if (IMediaEvent *event = com_cast<IMediaEvent>(m_graph, IID_IMediaEvent)) {
- long eventCode;
- LONG_PTR param1;
- LONG_PTR param2;
-
- while (event->GetEvent(&eventCode, &param1, &param2, 0) == S_OK) {
- switch (eventCode) {
- case EC_BUFFERING_DATA:
- m_buffering = param1;
-
- QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StatusChange)));
- break;
- case EC_COMPLETE:
- m_executedTasks &= ~(Play | Pause);
- m_executedTasks |= Stop;
-
- m_buffering = false;
- m_atEnd = true;
-
- if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph, IID_IMediaSeeking)) {
- LONGLONG position = 0;
-
- seeking->GetCurrentPosition(&position);
- seeking->Release();
-
- m_position = position / qt_directShowTimeScale;
- }
-
- QCoreApplication::postEvent(this, new QEvent(QEvent::Type(EndOfMedia)));
- break;
- case EC_LENGTH_CHANGED:
- if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph, IID_IMediaSeeking)) {
- LONGLONG duration = 0;
- seeking->GetDuration(&duration);
- m_duration = duration / qt_directShowTimeScale;
-
- DWORD capabilities = 0;
- seeking->GetCapabilities(&capabilities);
- m_seekable = capabilities & AM_SEEKING_CanSeekAbsolute;
-
- seeking->Release();
-
- QCoreApplication::postEvent(this, new QEvent(QEvent::Type(DurationChange)));
- }
- break;
- default:
- break;
- }
-
- event->FreeEventParams(eventCode, param1, param2);
- }
- event->Release();
- }
-}
-
-void DirectShowPlayerService::updateStatus()
-{
- switch (m_graphStatus) {
- case NoMedia:
- m_playerControl->updateStatus(QMediaPlayer::NoMedia);
- break;
- case Loading:
- m_playerControl->updateStatus(QMediaPlayer::LoadingMedia);
- break;
- case Loaded:
- if ((m_executingTask | m_executedTasks) & (Play | Pause)) {
- if (m_buffering)
- m_playerControl->updateStatus(QMediaPlayer::BufferingMedia);
- else
- m_playerControl->updateStatus(QMediaPlayer::BufferedMedia);
- } else {
- m_playerControl->updateStatus(QMediaPlayer::LoadedMedia);
- }
- break;
- case InvalidMedia:
- m_playerControl->updateStatus(QMediaPlayer::InvalidMedia);
- break;
- default:
- m_playerControl->updateStatus(QMediaPlayer::UnknownMediaStatus);
- }
-}
-
-bool DirectShowPlayerService::isConnected(IBaseFilter *filter, PIN_DIRECTION direction) const
-{
- bool connected = false;
-
- IEnumPins *pins = nullptr;
-
- if (SUCCEEDED(filter->EnumPins(&pins))) {
- for (IPin *pin = nullptr; pins->Next(1, &pin, nullptr) == S_OK; pin->Release()) {
- PIN_DIRECTION dir;
- if (SUCCEEDED(pin->QueryDirection(&dir)) && dir == direction) {
- IPin *peer = nullptr;
- if (SUCCEEDED(pin->ConnectedTo(&peer))) {
- connected = true;
-
- peer->Release();
- }
- }
- }
- pins->Release();
- }
- return connected;
-}
-
-IBaseFilter *DirectShowPlayerService::getConnected(
- IBaseFilter *filter, PIN_DIRECTION direction) const
-{
- IBaseFilter *connected = nullptr;
-
- IEnumPins *pins = nullptr;
-
- if (SUCCEEDED(filter->EnumPins(&pins))) {
- for (IPin *pin = nullptr; pins->Next(1, &pin, nullptr) == S_OK; pin->Release()) {
- PIN_DIRECTION dir;
- if (SUCCEEDED(pin->QueryDirection(&dir)) && dir == direction) {
- IPin *peer = nullptr;
- if (SUCCEEDED(pin->ConnectedTo(&peer))) {
- PIN_INFO info;
-
- if (SUCCEEDED(peer->QueryPinInfo(&info))) {
- if (connected) {
- qWarning("DirectShowPlayerService::getConnected: "
- "Multiple connected filters");
- connected->Release();
- }
- connected = info.pFilter;
- }
- peer->Release();
- }
- }
- }
- pins->Release();
- }
- return connected;
-}
-
-void DirectShowPlayerService::run()
-{
- QMutexLocker locker(&m_mutex);
-
- for (;;) {
- while (m_pendingTasks == 0) {
- DWORD result = 0;
-
- locker.unlock();
- if (m_eventHandle) {
- HANDLE handles[] = { m_taskHandle, m_eventHandle };
-
- result = ::WaitForMultipleObjects(2, handles, false, INFINITE);
- } else {
- result = ::WaitForSingleObject(m_taskHandle, INFINITE);
- }
- locker.relock();
-
- if (result == WAIT_OBJECT_0 + 1) {
- graphEvent(&locker);
- }
- }
-
- if (m_pendingTasks & ReleaseGraph) {
- m_pendingTasks ^= ReleaseGraph;
- m_executingTask = ReleaseGraph;
-
- doReleaseGraph(&locker);
- //if the graph is released, we should not process other operations later
- if (m_pendingTasks & Shutdown) {
- m_pendingTasks = 0;
- return;
- }
- m_pendingTasks = 0;
- } else if (m_pendingTasks & Shutdown) {
- return;
- } else if (m_pendingTasks & ReleaseAudioOutput) {
- m_pendingTasks ^= ReleaseAudioOutput;
- m_executingTask = ReleaseAudioOutput;
-
- doReleaseAudioOutput(&locker);
- } else if (m_pendingTasks & ReleaseVideoOutput) {
- m_pendingTasks ^= ReleaseVideoOutput;
- m_executingTask = ReleaseVideoOutput;
-
- doReleaseVideoOutput(&locker);
- } else if (m_pendingTasks & ReleaseAudioProbe) {
- m_pendingTasks ^= ReleaseAudioProbe;
- m_executingTask = ReleaseAudioProbe;
-
- doReleaseAudioProbe(&locker);
- } else if (m_pendingTasks & ReleaseVideoProbe) {
- m_pendingTasks ^= ReleaseVideoProbe;
- m_executingTask = ReleaseVideoProbe;
-
- doReleaseVideoProbe(&locker);
- } else if (m_pendingTasks & SetUrlSource) {
- m_pendingTasks ^= SetUrlSource;
- m_executingTask = SetUrlSource;
-
- doSetUrlSource(&locker);
- } else if (m_pendingTasks & SetStreamSource) {
- m_pendingTasks ^= SetStreamSource;
- m_executingTask = SetStreamSource;
-
- doSetStreamSource(&locker);
- } else if (m_pendingTasks & SetAudioProbe) {
- m_pendingTasks ^= SetAudioProbe;
- m_executingTask = SetAudioProbe;
-
- doSetAudioProbe(&locker);
- } else if (m_pendingTasks & SetVideoProbe) {
- m_pendingTasks ^= SetVideoProbe;
- m_executingTask = SetVideoProbe;
-
- doSetVideoProbe(&locker);
- } else if (m_pendingTasks & Render) {
- m_pendingTasks ^= Render;
- m_executingTask = Render;
-
- doRender(&locker);
- } else if (!(m_executedTasks & Render)) {
- m_pendingTasks &= ~(FinalizeLoad | SetRate | Stop | Pause | Seek | Play);
- } else if (m_pendingTasks & FinalizeLoad) {
- m_pendingTasks ^= FinalizeLoad;
- m_executingTask = FinalizeLoad;
-
- doFinalizeLoad(&locker);
- } else if (m_pendingTasks & Stop) {
- m_pendingTasks ^= Stop;
- m_executingTask = Stop;
-
- doStop(&locker);
- } else if (m_pendingTasks & SetRate) {
- m_pendingTasks ^= SetRate;
- m_executingTask = SetRate;
-
- doSetRate(&locker);
- } else if (m_pendingTasks & Pause) {
- m_pendingTasks ^= Pause;
- m_executingTask = Pause;
-
- doPause(&locker);
- } else if (m_pendingTasks & Seek) {
- m_pendingTasks ^= Seek;
- m_executingTask = Seek;
-
- doSeek(&locker);
- } else if (m_pendingTasks & Play) {
- m_pendingTasks ^= Play;
- m_executingTask = Play;
-
- doPlay(&locker);
- }
- m_executingTask = 0;
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/player/directshowplayerservice.h b/src/plugins/directshow/player/directshowplayerservice.h
deleted file mode 100644
index e0b011f4a..000000000
--- a/src/plugins/directshow/player/directshowplayerservice.h
+++ /dev/null
@@ -1,240 +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 DIRECTSHOWPLAYERSERVICE_H
-#define DIRECTSHOWPLAYERSERVICE_H
-
-#include <dshow.h>
-
-#include "qmediaplayer.h"
-#include "qmediaservice.h"
-#include "qmediatimerange.h"
-
-#include "directshoweventloop.h"
-#include "directshowglobal.h"
-
-#include <QtCore/qcoreevent.h>
-#include <QtCore/qmutex.h>
-#include <QtCore/qurl.h>
-#include <QtCore/qwaitcondition.h>
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowAudioEndpointControl;
-class DirectShowMetaDataControl;
-class DirectShowPlayerControl;
-class DirectShowVideoRendererControl;
-class DirectShowAudioProbeControl;
-class DirectShowVideoProbeControl;
-class DirectShowSampleGrabber;
-
-class QMediaContent;
-class QVideoWindowControl;
-
-class DirectShowPlayerService : public QMediaService
-{
- Q_OBJECT
-public:
- enum StreamType
- {
- AudioStream = 0x01,
- VideoStream = 0x02
- };
-
- DirectShowPlayerService(QObject *parent = nullptr);
- ~DirectShowPlayerService() override;
-
- QMediaControl *requestControl(const char *name) override;
- void releaseControl(QMediaControl *control) override;
-
- void load(const QMediaContent &media, QIODevice *stream);
- void play();
- void pause();
- void stop();
-
- qint64 position() const;
- QMediaTimeRange availablePlaybackRanges() const;
-
- void seek(qint64 position);
- void setRate(qreal rate);
-
- int bufferStatus() const;
-
- void setAudioOutput(IBaseFilter *filter);
- void setVideoOutput(IBaseFilter *filter);
-
-protected:
- void customEvent(QEvent *event) override;
-
-private Q_SLOTS:
- void videoOutputChanged();
-
- void onAudioBufferAvailable(double time, const QByteArray &data);
- void onVideoBufferAvailable(double time, const QByteArray &data);
-
-private:
- void releaseGraph();
- void updateStatus();
-
- void updateAudioProbe();
- void updateVideoProbe();
-
- int findStreamTypes(IBaseFilter *source) const;
- int findStreamType(IPin *pin) const;
-
- bool isConnected(IBaseFilter *filter, PIN_DIRECTION direction) const;
- IBaseFilter *getConnected(IBaseFilter *filter, PIN_DIRECTION direction) const;
-
- void run();
-
- void doSetUrlSource(QMutexLocker *locker);
- void doSetStreamSource(QMutexLocker *locker);
- void doRender(QMutexLocker *locker);
- void doFinalizeLoad(QMutexLocker *locker);
- void doSetRate(QMutexLocker *locker);
- void doSeek(QMutexLocker *locker);
- void doPlay(QMutexLocker *locker);
- void doPause(QMutexLocker *locker);
- void doStop(QMutexLocker *locker);
- void doReleaseAudioOutput(QMutexLocker *locker);
- void doReleaseVideoOutput(QMutexLocker *locker);
- void doReleaseGraph(QMutexLocker *locker);
- void doSetVideoProbe(QMutexLocker *locker);
- void doSetAudioProbe(QMutexLocker *locker);
- void doReleaseVideoProbe(QMutexLocker *locker);
- void doReleaseAudioProbe(QMutexLocker *locker);
-
- void graphEvent(QMutexLocker *locker);
-
- enum Task
- {
- Shutdown = 0x00001,
- SetUrlSource = 0x00002,
- SetStreamSource = 0x00004,
- SetSource = SetUrlSource | SetStreamSource,
- SetAudioOutput = 0x00008,
- SetVideoOutput = 0x00010,
- SetOutputs = SetAudioOutput | SetVideoOutput,
- SetAudioProbe = 0x00020,
- SetVideoProbe = 0x00040,
- SetProbes = SetAudioProbe | SetVideoProbe,
- Render = 0x00080,
- FinalizeLoad = 0x00100,
- SetRate = 0x00200,
- Seek = 0x00400,
- Play = 0x00800,
- Pause = 0x01000,
- Stop = 0x02000,
- ReleaseGraph = 0x04000,
- ReleaseAudioOutput = 0x08000,
- ReleaseVideoOutput = 0x10000,
- ReleaseAudioProbe = 0x20000,
- ReleaseVideoProbe = 0x40000,
- ReleaseFilters = ReleaseGraph | ReleaseAudioOutput
- | ReleaseVideoOutput | ReleaseAudioProbe
- | ReleaseVideoProbe
- };
-
- enum Event
- {
- FinalizedLoad = QEvent::User,
- Error,
- RateChange,
- Started,
- Paused,
- DurationChange,
- StatusChange,
- EndOfMedia,
- PositionChange
- };
-
- enum GraphStatus
- {
- NoMedia,
- Loading,
- Loaded,
- InvalidMedia
- };
-
- DirectShowPlayerControl *m_playerControl = nullptr;
- DirectShowMetaDataControl *m_metaDataControl = nullptr;
- DirectShowVideoRendererControl *m_videoRendererControl = nullptr;
- QVideoWindowControl *m_videoWindowControl = nullptr;
- DirectShowAudioEndpointControl *m_audioEndpointControl = nullptr;
- DirectShowAudioProbeControl *m_audioProbeControl = nullptr;
- DirectShowVideoProbeControl *m_videoProbeControl = nullptr;
- DirectShowSampleGrabber *m_audioSampleGrabber = nullptr;
- DirectShowSampleGrabber *m_videoSampleGrabber = nullptr;
-
- QThread *m_taskThread = nullptr;
- DirectShowEventLoop *m_loop;
- int m_pendingTasks = 0;
- int m_executingTask = 0;
- int m_executedTasks = 0;
- int m_streamTypes = 0;
- HANDLE m_taskHandle;
- HANDLE m_eventHandle = nullptr;
- GraphStatus m_graphStatus = NoMedia;
- QMediaPlayer::Error m_error = QMediaPlayer::NoError;
- QIODevice *m_stream = nullptr;
- IFilterGraph2 *m_graph = nullptr;
- ICaptureGraphBuilder2 *m_graphBuilder = nullptr;
- IBaseFilter *m_source = nullptr;
- IBaseFilter *m_audioOutput = nullptr;
- IBaseFilter *m_videoOutput = nullptr;
- qreal m_rate = 1;
- qint64 m_position = 0;
- qint64 m_seekPosition = -1;
- qint64 m_duration = 0;
- QMediaTimeRange m_playbackRange;
- QUrl m_url;
- QString m_errorString;
- QMutex m_mutex;
- bool m_buffering = false;
- bool m_seekable = false;
- bool m_atEnd = false;
- bool m_dontCacheNextSeekResult = false;
- QVariantMap m_metadata;
-
- friend class DirectShowPlayerServiceThread;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/directshow/player/directshowvideorenderercontrol.cpp b/src/plugins/directshow/player/directshowvideorenderercontrol.cpp
deleted file mode 100644
index 0b1f0de2f..000000000
--- a/src/plugins/directshow/player/directshowvideorenderercontrol.cpp
+++ /dev/null
@@ -1,121 +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 <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include "directshowvideorenderercontrol.h"
-
-#include "videosurfacefilter.h"
-
-#if QT_CONFIG(evr)
-#include "evrcustompresenter.h"
-#endif
-
-#include <qabstractvideosurface.h>
-
-DirectShowVideoRendererControl::DirectShowVideoRendererControl(DirectShowEventLoop *loop, QObject *parent)
- : QVideoRendererControl(parent)
- , m_loop(loop)
-{
-}
-
-DirectShowVideoRendererControl::~DirectShowVideoRendererControl()
-{
-#if QT_CONFIG(evr)
- if (m_evrPresenter) {
- m_evrPresenter->setSurface(nullptr);
- m_evrPresenter->Release();
- }
-#endif
- if (m_filter)
- m_filter->Release();
-}
-
-QAbstractVideoSurface *DirectShowVideoRendererControl::surface() const
-{
- return m_surface;
-}
-
-void DirectShowVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
-{
- if (m_surface == surface)
- return;
-
-#if QT_CONFIG(evr)
- if (m_evrPresenter) {
- m_evrPresenter->setSurface(nullptr);
- m_evrPresenter->Release();
- m_evrPresenter = nullptr;
- }
-#endif
-
- if (m_filter) {
- m_filter->Release();
- m_filter = nullptr;
- }
-
- m_surface = surface;
-
- if (m_surface) {
-#if QT_CONFIG(evr)
- if (!qgetenv("QT_DIRECTSHOW_NO_EVR").toInt()) {
- m_filter = com_new<IBaseFilter>(clsid_EnhancedVideoRenderer);
- m_evrPresenter = new EVRCustomPresenter(m_surface);
- connect(this, &DirectShowVideoRendererControl::positionChanged, m_evrPresenter, &EVRCustomPresenter::positionChanged);
- if (!m_evrPresenter->isValid() || !qt_evr_setCustomPresenter(m_filter, m_evrPresenter)) {
- m_filter->Release();
- m_filter = nullptr;
- m_evrPresenter->Release();
- m_evrPresenter = nullptr;
- }
- }
-
- if (!m_filter)
-#endif
- {
- m_filter = new VideoSurfaceFilter(m_surface, m_loop);
- }
- }
-
- emit filterChanged();
-}
-
-IBaseFilter *DirectShowVideoRendererControl::filter()
-{
- return m_filter;
-}
diff --git a/src/plugins/directshow/player/directshowvideorenderercontrol.h b/src/plugins/directshow/player/directshowvideorenderercontrol.h
deleted file mode 100644
index 9326a2748..000000000
--- a/src/plugins/directshow/player/directshowvideorenderercontrol.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 DIRECTSHOWVIDEORENDERERCONTROL_H
-#define DIRECTSHOWVIDEORENDERERCONTROL_H
-
-#include <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include <dshow.h>
-
-#include "qvideorenderercontrol.h"
-
-#include <QtMultimedia/private/qtmultimedia-config_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class DirectShowEventLoop;
-#if QT_CONFIG(evr)
-class EVRCustomPresenter;
-#endif
-
-class DirectShowVideoRendererControl : public QVideoRendererControl
-{
- Q_OBJECT
-public:
- DirectShowVideoRendererControl(DirectShowEventLoop *loop, QObject *parent = nullptr);
- ~DirectShowVideoRendererControl() override;
-
- QAbstractVideoSurface *surface() const override;
- void setSurface(QAbstractVideoSurface *surface) override;
-
- IBaseFilter *filter();
-
-Q_SIGNALS:
- void filterChanged();
- void positionChanged(qint64 position);
-
-private:
- DirectShowEventLoop *m_loop;
- QAbstractVideoSurface *m_surface = nullptr;
- IBaseFilter *m_filter = nullptr;
-#if QT_CONFIG(evr)
- EVRCustomPresenter *m_evrPresenter = nullptr;
-#endif
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/directshow/player/player.pri b/src/plugins/directshow/player/player.pri
deleted file mode 100644
index ec1066db7..000000000
--- a/src/plugins/directshow/player/player.pri
+++ /dev/null
@@ -1,45 +0,0 @@
-INCLUDEPATH += $$PWD
-
-QMAKE_USE += directshow
-LIBS += -lgdi32
-
-mingw: LIBS_PRIVATE += -lksuser
-
-qtHaveModule(widgets): QT += widgets
-
-HEADERS += \
- $$PWD/directshowioreader.h \
- $$PWD/directshowiosource.h \
- $$PWD/directshowplayercontrol.h \
- $$PWD/directshowplayerservice.h \
- $$PWD/directshowvideorenderercontrol.h \
- $$PWD/videosurfacefilter.h \
- $$PWD/directshowaudioendpointcontrol.h \
- $$PWD/directshowmetadatacontrol.h \
- $$PWD/vmr9videowindowcontrol.h
-
-SOURCES += \
- $$PWD/directshowioreader.cpp \
- $$PWD/directshowiosource.cpp \
- $$PWD/directshowplayercontrol.cpp \
- $$PWD/directshowplayerservice.cpp \
- $$PWD/directshowvideorenderercontrol.cpp \
- $$PWD/videosurfacefilter.cpp \
- $$PWD/directshowaudioendpointcontrol.cpp \
- $$PWD/directshowmetadatacontrol.cpp \
- $$PWD/vmr9videowindowcontrol.cpp
-
-qtConfig(evr) {
- include($$PWD/../../common/evr.pri)
-
- HEADERS += \
- $$PWD/directshowevrvideowindowcontrol.h
-
- SOURCES += \
- $$PWD/directshowevrvideowindowcontrol.cpp
-} else {
- LIBS += -lwinmm
-}
-
-qtConfig(wshellitem): \
- QT += core-private
diff --git a/src/plugins/directshow/player/videosurfacefilter.cpp b/src/plugins/directshow/player/videosurfacefilter.cpp
deleted file mode 100644
index 4b7afc266..000000000
--- a/src/plugins/directshow/player/videosurfacefilter.cpp
+++ /dev/null
@@ -1,810 +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 "videosurfacefilter.h"
-
-#include "directshoweventloop.h"
-#include "directshowglobal.h"
-#include "directshowvideobuffer.h"
-
-#include <QtCore/qthread.h>
-#include <QtCore/qloggingcategory.h>
-#include <qabstractvideosurface.h>
-
-#include <mutex>
-
-#include <initguid.h>
-
-QT_BEGIN_NAMESPACE
-
-Q_LOGGING_CATEGORY(qLcRenderFilter, "qt.multimedia.plugins.directshow.renderfilter")
-
-// { e23cad72-153d-406c-bf3f-4c4b523d96f2 }
-DEFINE_GUID(CLSID_VideoSurfaceFilter,
-0xe23cad72, 0x153d, 0x406c, 0xbf, 0x3f, 0x4c, 0x4b, 0x52, 0x3d, 0x96, 0xf2);
-
-class VideoSurfaceInputPin : public DirectShowInputPin
-{
- COM_REF_MIXIN
-public:
- VideoSurfaceInputPin(VideoSurfaceFilter *filter);
-
- STDMETHODIMP QueryInterface(REFIID riid, void **ppv) override;
-
- bool isMediaTypeSupported(const AM_MEDIA_TYPE *type) override;
- bool setMediaType(const AM_MEDIA_TYPE *type) override;
-
- HRESULT completeConnection(IPin *pin) override;
- HRESULT connectionEnded() override;
-
- // IPin
- STDMETHODIMP ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt) override;
- STDMETHODIMP Disconnect() override;
- STDMETHODIMP EndOfStream() override;
- STDMETHODIMP BeginFlush() override;
- STDMETHODIMP EndFlush() override;
-
- // IMemInputPin
- STDMETHODIMP GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps) override;
- STDMETHODIMP Receive(IMediaSample *pMediaSample) override;
-
-private:
- VideoSurfaceFilter *m_videoSurfaceFilter;
-};
-
-VideoSurfaceInputPin::VideoSurfaceInputPin(VideoSurfaceFilter *filter)
- : DirectShowInputPin(filter, QStringLiteral("Input"))
- , m_videoSurfaceFilter(filter)
-{
-}
-
-HRESULT VideoSurfaceInputPin::QueryInterface(REFIID riid, void **ppv)
-{
- if (ppv == nullptr)
- return E_POINTER;
- if (riid == IID_IUnknown)
- *ppv = static_cast<IUnknown *>(static_cast<DirectShowPin *>(this));
- else if (riid == IID_IPin)
- *ppv = static_cast<IPin *>(this);
- else if (riid == IID_IMemInputPin)
- *ppv =static_cast<IMemInputPin*>(this);
- else
- return E_NOINTERFACE;
- AddRef();
- return S_OK;
-}
-
-bool VideoSurfaceInputPin::isMediaTypeSupported(const AM_MEDIA_TYPE *type)
-{
- return m_videoSurfaceFilter->isMediaTypeSupported(type);
-}
-
-bool VideoSurfaceInputPin::setMediaType(const AM_MEDIA_TYPE *type)
-{
- if (!DirectShowInputPin::setMediaType(type))
- return false;
-
- return m_videoSurfaceFilter->setMediaType(type);
-}
-
-HRESULT VideoSurfaceInputPin::completeConnection(IPin *pin)
-{
- HRESULT hr = DirectShowInputPin::completeConnection(pin);
- if (FAILED(hr))
- return hr;
-
- return m_videoSurfaceFilter->completeConnection(pin);
-}
-
-HRESULT VideoSurfaceInputPin::connectionEnded()
-{
- HRESULT hr = DirectShowInputPin::connectionEnded();
- if (FAILED(hr))
- return hr;
-
- return m_videoSurfaceFilter->connectionEnded();
-}
-
-HRESULT VideoSurfaceInputPin::ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt)
-{
- QMutexLocker lock(&m_videoSurfaceFilter->m_mutex);
- return DirectShowInputPin::ReceiveConnection(pConnector, pmt);
-}
-
-HRESULT VideoSurfaceInputPin::Disconnect()
-{
- QMutexLocker lock(&m_videoSurfaceFilter->m_mutex);
- return DirectShowInputPin::Disconnect();
-}
-
-HRESULT VideoSurfaceInputPin::EndOfStream()
-{
- QMutexLocker lock(&m_videoSurfaceFilter->m_mutex);
- const std::lock_guard<QRecursiveMutex> renderLocker(m_videoSurfaceFilter->m_renderMutex);
-
- HRESULT hr = DirectShowInputPin::EndOfStream();
- if (hr != S_OK)
- return hr;
-
- return m_videoSurfaceFilter->EndOfStream();
-}
-
-HRESULT VideoSurfaceInputPin::BeginFlush()
-{
- QMutexLocker lock(&m_videoSurfaceFilter->m_mutex);
- {
- const std::lock_guard<QRecursiveMutex> renderLocker(m_videoSurfaceFilter->m_renderMutex);
- DirectShowInputPin::BeginFlush();
- m_videoSurfaceFilter->BeginFlush();
- }
- m_videoSurfaceFilter->resetEOS();
-
- return S_OK;
-}
-
-HRESULT VideoSurfaceInputPin::EndFlush()
-{
- QMutexLocker lock(&m_videoSurfaceFilter->m_mutex);
- const std::lock_guard<QRecursiveMutex> renderLocker(m_videoSurfaceFilter->m_renderMutex);
-
- HRESULT hr = m_videoSurfaceFilter->EndFlush();
- if (SUCCEEDED(hr))
- hr = DirectShowInputPin::EndFlush();
- return hr;
-}
-
-HRESULT VideoSurfaceInputPin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps)
-{
- if (!pProps)
- return E_POINTER;
-
- // We need at least two allocated buffers, one for holding the frame currently being
- // rendered and another one to decode the following frame at the same time.
- pProps->cBuffers = 2;
-
- return S_OK;
-}
-
-HRESULT VideoSurfaceInputPin::Receive(IMediaSample *pMediaSample)
-{
- HRESULT hr = m_videoSurfaceFilter->Receive(pMediaSample);
- if (FAILED(hr)) {
- QMutexLocker locker(&m_videoSurfaceFilter->m_mutex);
- if (m_videoSurfaceFilter->state() != State_Stopped && !m_flushing && !m_inErrorState) {
- m_videoSurfaceFilter->NotifyEvent(EC_ERRORABORT, hr, 0);
- {
- const std::lock_guard<QRecursiveMutex> renderLocker(m_videoSurfaceFilter->m_renderMutex);
- if (m_videoSurfaceFilter->m_running && !m_videoSurfaceFilter->m_EOSDelivered)
- m_videoSurfaceFilter->notifyEOS();
- }
- m_inErrorState = true;
- }
- }
-
- return hr;
-}
-
-
-VideoSurfaceFilter::VideoSurfaceFilter(QAbstractVideoSurface *surface, DirectShowEventLoop *loop, QObject *parent)
- : QObject(parent)
- , m_loop(loop)
- , m_surface(surface)
- , m_renderEvent(CreateEvent(nullptr, FALSE, FALSE, nullptr))
- , m_flushEvent(CreateEvent(nullptr, TRUE, FALSE, nullptr))
-{
- supportedFormatsChanged();
- connect(surface, &QAbstractVideoSurface::supportedFormatsChanged,
- this, &VideoSurfaceFilter::supportedFormatsChanged);
-}
-
-VideoSurfaceFilter::~VideoSurfaceFilter()
-{
- clearPendingSample();
-
- if (m_pin)
- m_pin->Release();
-
- CloseHandle(m_flushEvent);
- CloseHandle(m_renderEvent);
-}
-
-HRESULT VideoSurfaceFilter::QueryInterface(REFIID riid, void **ppv)
-{
- if (ppv == nullptr)
- return E_POINTER;
- if (riid == IID_IUnknown)
- *ppv = static_cast<IUnknown *>(static_cast<DirectShowBaseFilter *>(this));
- else if (riid == IID_IPersist || riid == IID_IMediaFilter || riid == IID_IBaseFilter)
- *ppv = static_cast<IBaseFilter *>(this);
- else if (riid == IID_IAMFilterMiscFlags)
- *ppv = static_cast<IAMFilterMiscFlags *>(this);
- else
- return E_NOINTERFACE;
- AddRef();
- return S_OK;
-}
-
-QList<DirectShowPin *> VideoSurfaceFilter::pins()
-{
- if (!m_pin)
- m_pin = new VideoSurfaceInputPin(this);
-
- return QList<DirectShowPin *>() << m_pin;
-}
-
-HRESULT VideoSurfaceFilter::GetClassID(CLSID *pClassID)
-{
- *pClassID = CLSID_VideoSurfaceFilter;
- return S_OK;
-}
-
-ULONG VideoSurfaceFilter::GetMiscFlags()
-{
- return AM_FILTER_MISC_FLAGS_IS_RENDERER;
-}
-
-void VideoSurfaceFilter::supportedFormatsChanged()
-{
- QWriteLocker writeLocker(&m_typesLock);
-
- qCDebug(qLcRenderFilter, "supportedFormatChanged");
-
- m_supportedTypes.clear();
-
- const QList<QVideoFrame::PixelFormat> formats = m_surface->supportedPixelFormats();
- m_supportedTypes.reserve(formats.count());
-
- for (QVideoFrame::PixelFormat format : formats) {
- GUID subtype = DirectShowMediaType::convertPixelFormat(format);
- if (!IsEqualGUID(subtype, MEDIASUBTYPE_None)) {
- qCDebug(qLcRenderFilter) << " " << format;
- m_supportedTypes.append(subtype);
- }
- }
-}
-
-bool VideoSurfaceFilter::isMediaTypeSupported(const AM_MEDIA_TYPE *type)
-{
- if (type->majortype != MEDIATYPE_Video || type->bFixedSizeSamples == FALSE)
- return false;
-
- QReadLocker readLocker(&m_typesLock);
-
- for (const GUID &supportedType : m_supportedTypes) {
- if (IsEqualGUID(supportedType, type->subtype))
- return true;
- }
-
- return false;
-}
-
-bool VideoSurfaceFilter::setMediaType(const AM_MEDIA_TYPE *type)
-{
- if (!type) {
- qCDebug(qLcRenderFilter, "clear media type");
- m_surfaceFormat = QVideoSurfaceFormat();
- m_bytesPerLine = 0;
- return true;
- }
- m_surfaceFormat = DirectShowMediaType::videoFormatFromType(type);
- m_bytesPerLine = DirectShowMediaType::bytesPerLine(m_surfaceFormat);
- qCDebug(qLcRenderFilter) << "setMediaType -->" << m_surfaceFormat;
- return m_surfaceFormat.isValid();
-}
-
-HRESULT VideoSurfaceFilter::completeConnection(IPin *pin)
-{
- Q_UNUSED(pin);
-
- qCDebug(qLcRenderFilter, "completeConnection");
-
- return startSurface() ? S_OK : VFW_E_TYPE_NOT_ACCEPTED;
-}
-
-HRESULT VideoSurfaceFilter::connectionEnded()
-{
- qCDebug(qLcRenderFilter, "connectionEnded");
-
- stopSurface();
-
- return S_OK;
-}
-
-HRESULT VideoSurfaceFilter::Run(REFERENCE_TIME tStart)
-{
- QMutexLocker locker(&m_mutex);
-
- if (m_state == State_Running)
- return S_OK;
-
- qCDebug(qLcRenderFilter, "Run (start=%lli)", tStart);
-
- HRESULT hr = DirectShowBaseFilter::Run(tStart);
- if (FAILED(hr))
- return hr;
-
- ResetEvent(m_flushEvent);
-
- IMemAllocator *allocator;
- if (SUCCEEDED(m_pin->GetAllocator(&allocator))) {
- allocator->Commit();
- allocator->Release();
- }
-
- const std::lock_guard<QRecursiveMutex> renderLocker(m_renderMutex);
-
- m_running = true;
-
- if (!m_pendingSample)
- checkEOS();
- else if (!scheduleSample(m_pendingSample))
- SetEvent(m_renderEvent); // render immediately
-
- return S_OK;
-}
-
-HRESULT VideoSurfaceFilter::Pause()
-{
- QMutexLocker locker(&m_mutex);
-
- if (m_state == State_Paused)
- return S_OK;
-
- qCDebug(qLcRenderFilter, "Pause");
-
- HRESULT hr = DirectShowBaseFilter::Pause();
- if (FAILED(hr))
- return hr;
-
- m_renderMutex.lock();
- m_EOSDelivered = false;
- m_running = false;
- m_renderMutex.unlock();
-
- resetEOSTimer();
- ResetEvent(m_flushEvent);
- unscheduleSample();
-
- IMemAllocator *allocator;
- if (SUCCEEDED(m_pin->GetAllocator(&allocator))) {
- allocator->Commit();
- allocator->Release();
- }
-
- return S_OK;
-}
-
-HRESULT VideoSurfaceFilter::Stop()
-{
- QMutexLocker locker(&m_mutex);
-
- if (m_state == State_Stopped)
- return S_OK;
-
- qCDebug(qLcRenderFilter, "Stop");
-
- DirectShowBaseFilter::Stop();
-
- clearPendingSample();
-
- m_renderMutex.lock();
- m_EOSDelivered = false;
- m_running = false;
- m_renderMutex.unlock();
-
- SetEvent(m_flushEvent);
- resetEOS();
- unscheduleSample();
- flushSurface();
-
- IMemAllocator *allocator;
- if (SUCCEEDED(m_pin->GetAllocator(&allocator))) {
- allocator->Decommit();
- allocator->Release();
- }
-
- return S_OK;
-}
-
-HRESULT VideoSurfaceFilter::EndOfStream()
-{
- const std::lock_guard<QRecursiveMutex> renderLocker(m_renderMutex);
-
- qCDebug(qLcRenderFilter, "EndOfStream");
-
- m_EOS = true;
-
- if (!m_pendingSample && m_running)
- checkEOS();
-
- stopSurface();
- return S_OK;
-}
-
-HRESULT VideoSurfaceFilter::BeginFlush()
-{
- qCDebug(qLcRenderFilter, "BeginFlush");
-
- SetEvent(m_flushEvent);
- unscheduleSample();
- clearPendingSample();
-
- return S_OK;
-}
-
-HRESULT VideoSurfaceFilter::EndFlush()
-{
- qCDebug(qLcRenderFilter, "EndFlush");
-
- ResetEvent(m_flushEvent);
- return S_OK;
-}
-
-HRESULT VideoSurfaceFilter::Receive(IMediaSample *pMediaSample)
-{
- {
- QMutexLocker locker(&m_mutex);
-
- qCDebug(qLcRenderFilter, "Receive (sample=%p)", pMediaSample);
-
- HRESULT hr = m_pin->DirectShowInputPin::Receive(pMediaSample);
- if (hr != S_OK) {
- qCDebug(qLcRenderFilter, " can't receive sample (error %X)", uint(hr));
- return E_FAIL;
- }
-
- // If the format dynamically changed, the sample contains information about the new format.
- // We need to reset the format and restart the QAbstractVideoSurface.
- if (m_pin->currentSampleProperties()->pMediaType
- && (!m_pin->setMediaType(m_pin->currentSampleProperties()->pMediaType)
- || !restartSurface())) {
- qCWarning(qLcRenderFilter, " dynamic format change failed, aborting rendering");
- NotifyEvent(EC_ERRORABORT, VFW_E_TYPE_NOT_ACCEPTED, 0);
- return VFW_E_INVALIDMEDIATYPE;
- }
-
- {
- const std::lock_guard<QRecursiveMutex> locker(m_renderMutex);
-
- if (m_pendingSample || m_EOS)
- return E_UNEXPECTED;
-
- if (m_running && !scheduleSample(pMediaSample)) {
- qCWarning(qLcRenderFilter, " sample can't be scheduled, discarding it");
- return S_OK;
- }
-
- m_pendingSample = pMediaSample;
- m_pendingSample->AddRef();
- m_pendingSampleEndTime = m_pin->currentSampleProperties()->tStop;
- }
-
- if (m_state == State_Paused) // Render immediately
- renderPendingSample();
- }
-
- qCDebug(qLcRenderFilter, " waiting for render time");
-
- // Wait for render time. The clock will wake us up whenever the time comes.
- // It can also be interrupted by a flush, pause or stop.
- HANDLE waitObjects[] = { m_flushEvent, m_renderEvent };
- DWORD result = WAIT_TIMEOUT;
- while (result == WAIT_TIMEOUT)
- result = WaitForMultipleObjects(2, waitObjects, FALSE, INFINITE);
-
- if (result == WAIT_OBJECT_0) {
- // render interrupted (flush, pause, stop)
- qCDebug(qLcRenderFilter, " rendering of sample %p interrupted", pMediaSample);
- return S_OK;
- }
-
- m_adviseCookie = 0;
-
- QMutexLocker locker(&m_mutex);
-
- // State might have changed just before the lock
- if (m_state == State_Stopped) {
- qCDebug(qLcRenderFilter, " state changed to Stopped, discarding sample (%p)", pMediaSample);
- return S_OK;
- }
-
- std::unique_lock<QRecursiveMutex> renderLocker(m_renderMutex);
-
- // Flush or pause might have happened just before the lock
- if (m_pendingSample && m_running) {
- renderLocker.unlock();
- renderPendingSample();
- renderLocker.lock();
- } else {
- qCDebug(qLcRenderFilter, " discarding sample (%p)", pMediaSample);
- }
-
- clearPendingSample();
- checkEOS();
- ResetEvent(m_renderEvent);
-
- return S_OK;
-}
-
-bool VideoSurfaceFilter::scheduleSample(IMediaSample *sample)
-{
- if (!sample)
- return false;
-
- qCDebug(qLcRenderFilter, "scheduleSample (sample=%p)", sample);
-
- REFERENCE_TIME sampleStart, sampleEnd;
- if (FAILED(sample->GetTime(&sampleStart, &sampleEnd)) || !m_clock) {
- qCDebug(qLcRenderFilter, " render now");
- SetEvent(m_renderEvent); // Render immediately
- return true;
- }
-
- if (sampleEnd < sampleStart) { // incorrect times
- qCWarning(qLcRenderFilter, " invalid sample times (start=%lli, end=%lli)", sampleStart, sampleEnd);
- return false;
- }
-
- HRESULT hr = m_clock->AdviseTime(m_startTime, sampleStart, (HEVENT)m_renderEvent, &m_adviseCookie);
- if (FAILED(hr)) {
- qCWarning(qLcRenderFilter, " clock failed to advise time (error=%X)", uint(hr));
- return false;
- }
-
- return true;
-}
-
-void VideoSurfaceFilter::unscheduleSample()
-{
- if (m_adviseCookie) {
- qCDebug(qLcRenderFilter, "unscheduleSample");
- m_clock->Unadvise(m_adviseCookie);
- m_adviseCookie = 0;
- }
-
- ResetEvent(m_renderEvent);
-}
-
-void VideoSurfaceFilter::clearPendingSample()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_renderMutex);
- if (m_pendingSample) {
- qCDebug(qLcRenderFilter, "clearPendingSample");
- m_pendingSample->Release();
- m_pendingSample = nullptr;
- }
-}
-
-void QT_WIN_CALLBACK EOSTimerCallback(UINT, UINT, DWORD_PTR dwUser, DWORD_PTR, DWORD_PTR)
-{
- VideoSurfaceFilter *that = reinterpret_cast<VideoSurfaceFilter *>(dwUser);
- that->onEOSTimerTimeout();
-}
-
-void VideoSurfaceFilter::onEOSTimerTimeout()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_renderMutex);
-
- if (m_EOSTimer) {
- m_EOSTimer = 0;
- checkEOS();
- }
-}
-
-void VideoSurfaceFilter::checkEOS()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_renderMutex);
-
- if (!m_EOS || m_EOSDelivered || m_EOSTimer)
- return;
-
- if (!m_clock) {
- notifyEOS();
- return;
- }
-
- REFERENCE_TIME eosTime = m_startTime + m_pendingSampleEndTime;
- REFERENCE_TIME currentTime;
- m_clock->GetTime(&currentTime);
- LONG delay = LONG((eosTime - currentTime) / 10000);
-
- if (delay < 1) {
- notifyEOS();
- } else {
- qCDebug(qLcRenderFilter, "will trigger EOS in %li", delay);
-
- m_EOSTimer = timeSetEvent(delay,
- 1,
- EOSTimerCallback,
- reinterpret_cast<DWORD_PTR>(this),
- TIME_ONESHOT | TIME_CALLBACK_FUNCTION | TIME_KILL_SYNCHRONOUS);
-
- if (!m_EOSTimer) {
- qDebug("Error with timer");
- notifyEOS();
- }
- }
-}
-
-void VideoSurfaceFilter::notifyEOS()
-{
- const std::lock_guard<QRecursiveMutex> locker(m_renderMutex);
-
- if (!m_running)
- return;
-
- qCDebug(qLcRenderFilter, "notifyEOS, delivering EC_COMPLETE event");
-
- m_EOSTimer = 0;
- m_EOSDelivered = true;
- NotifyEvent(EC_COMPLETE, S_OK, (LONG_PTR)(IBaseFilter *)this);
-}
-
-void VideoSurfaceFilter::resetEOS()
-{
- resetEOSTimer();
-
- const std::lock_guard<QRecursiveMutex> locker(m_renderMutex);
-
- if (m_EOS)
- qCDebug(qLcRenderFilter, "resetEOS (delivered=%s)", m_EOSDelivered ? "true" : "false");
-
- m_EOS = false;
- m_EOSDelivered = false;
- m_pendingSampleEndTime = 0;
-}
-
-void VideoSurfaceFilter::resetEOSTimer()
-{
- if (m_EOSTimer) {
- timeKillEvent(m_EOSTimer);
- m_EOSTimer = 0;
- }
-}
-
-bool VideoSurfaceFilter::startSurface()
-{
- if (QThread::currentThread() != thread()) {
- m_loop->postEvent(this, new QEvent(QEvent::Type(StartSurface)));
- m_waitSurface.wait(&m_mutex);
- return m_surfaceStarted;
- }
- m_surfaceStarted = m_surface->start(m_surfaceFormat);
- qCDebug(qLcRenderFilter, "startSurface %s", m_surfaceStarted ? "succeeded" : "failed");
- return m_surfaceStarted;
-}
-
-void VideoSurfaceFilter::stopSurface()
-{
- if (!m_surfaceStarted)
- return;
-
- if (QThread::currentThread() != thread()) {
- m_loop->postEvent(this, new QEvent(QEvent::Type(StopSurface)));
- m_waitSurface.wait(&m_mutex);
- } else {
- qCDebug(qLcRenderFilter, "stopSurface");
- m_surface->stop();
- m_surfaceStarted = false;
- }
-}
-
-bool VideoSurfaceFilter::restartSurface()
-{
- if (QThread::currentThread() != thread()) {
- m_loop->postEvent(this, new QEvent(QEvent::Type(RestartSurface)));
- m_waitSurface.wait(&m_mutex);
- return m_surfaceStarted;
- }
- m_surface->stop();
- m_surfaceStarted = m_surface->start(m_surfaceFormat);
- qCDebug(qLcRenderFilter, "restartSurface %s", m_surfaceStarted ? "succeeded" : "failed");
- return m_surfaceStarted;
-}
-
-void VideoSurfaceFilter::flushSurface()
-{
- if (QThread::currentThread() != thread()) {
- m_loop->postEvent(this, new QEvent(QEvent::Type(FlushSurface)));
- m_waitSurface.wait(&m_mutex);
- } else {
- qCDebug(qLcRenderFilter, "flushSurface");
- m_surface->present(QVideoFrame());
- }
-}
-
-void VideoSurfaceFilter::renderPendingSample()
-{
- if (QThread::currentThread() != thread()) {
- m_loop->postEvent(this, new QEvent(QEvent::Type(RenderSample)));
- m_waitSurface.wait(&m_mutex);
- } else {
- const std::lock_guard<QRecursiveMutex> locker(m_renderMutex);
- if (!m_pendingSample)
- return;
-
- qCDebug(qLcRenderFilter, "presentSample (sample=%p)", m_pendingSample);
-
- m_surface->present(QVideoFrame(new DirectShowVideoBuffer(m_pendingSample, m_bytesPerLine),
- m_surfaceFormat.frameSize(),
- m_surfaceFormat.pixelFormat()));
- }
-}
-
-bool VideoSurfaceFilter::event(QEvent *e)
-{
- switch (e->type()) {
- case StartSurface: {
- QMutexLocker locker(&m_mutex);
- startSurface();
- m_waitSurface.wakeAll();
- return true;
- }
- case StopSurface: {
- QMutexLocker locker(&m_mutex);
- stopSurface();
- m_waitSurface.wakeAll();
- return true;
- }
- case RestartSurface: {
- QMutexLocker locker(&m_mutex);
- restartSurface();
- m_waitSurface.wakeAll();
- return true;
- }
- case FlushSurface: {
- QMutexLocker locker(&m_mutex);
- flushSurface();
- m_waitSurface.wakeAll();
- return true;
- }
- case RenderSample: {
- QMutexLocker locker(&m_mutex);
- renderPendingSample();
- m_waitSurface.wakeAll();
- return true;
- }
- default:
- break;
- }
-
- return QObject::event(e);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/directshow/player/videosurfacefilter.h b/src/plugins/directshow/player/videosurfacefilter.h
deleted file mode 100644
index 9e56f4b65..000000000
--- a/src/plugins/directshow/player/videosurfacefilter.h
+++ /dev/null
@@ -1,161 +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 VIDEOSURFACEFILTER_H
-#define VIDEOSURFACEFILTER_H
-
-#include "directshowbasefilter.h"
-
-#include <QtCore/qcoreevent.h>
-#include <QtCore/qmutex.h>
-#include <qreadwritelock.h>
-#include <qsemaphore.h>
-#include <qwaitcondition.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAbstractVideoSurface;
-
-class DirectShowEventLoop;
-class VideoSurfaceInputPin;
-
-class VideoSurfaceFilter : public QObject
- , public DirectShowBaseFilter
- , public IAMFilterMiscFlags
-{
- Q_OBJECT
- COM_REF_MIXIN
-public:
- VideoSurfaceFilter(QAbstractVideoSurface *surface, DirectShowEventLoop *loop, QObject *parent = nullptr);
- ~VideoSurfaceFilter();
-
- STDMETHODIMP QueryInterface(REFIID riid, void **ppv) override;
-
- // DirectShowBaseFilter
- QList<DirectShowPin *> pins() override;
-
- // IPersist
- STDMETHODIMP GetClassID(CLSID *pClassID) override;
-
- // IMediaFilter
- STDMETHODIMP Run(REFERENCE_TIME tStart) override;
- STDMETHODIMP Pause() override;
- STDMETHODIMP Stop() override;
-
- // IAMFilterMiscFlags
- STDMETHODIMP_(ULONG) GetMiscFlags() override;
-
- // DirectShowPin (delegate)
- bool isMediaTypeSupported(const AM_MEDIA_TYPE *type);
- bool setMediaType(const AM_MEDIA_TYPE *type);
- HRESULT completeConnection(IPin *pin);
- HRESULT connectionEnded();
-
- // IPin (delegate)
- HRESULT EndOfStream();
- HRESULT BeginFlush();
- HRESULT EndFlush();
-
- // IMemInputPin (delegate)
- HRESULT Receive(IMediaSample *pMediaSample);
-
-private Q_SLOTS:
- void supportedFormatsChanged();
- void checkEOS();
-
-private:
- enum Events {
- StartSurface = QEvent::User,
- StopSurface = QEvent::User + 1,
- RestartSurface = QEvent::User + 2,
- FlushSurface = QEvent::User + 3,
- RenderSample = QEvent::User + 4
- };
-
- bool event(QEvent *) override;
-
- bool startSurface();
- void stopSurface();
- bool restartSurface();
- void flushSurface();
-
- bool scheduleSample(IMediaSample *sample);
- void unscheduleSample();
- void renderPendingSample();
- void clearPendingSample();
-
- void notifyEOS();
- void resetEOS();
- void resetEOSTimer();
- void onEOSTimerTimeout();
-
- friend void QT_WIN_CALLBACK EOSTimerCallback(UINT, UINT, DWORD_PTR dwUser, DWORD_PTR, DWORD_PTR);
-
- QMutex m_mutex;
-
- DirectShowEventLoop *m_loop;
- VideoSurfaceInputPin *m_pin = nullptr;
-
- QWaitCondition m_waitSurface;
- QAbstractVideoSurface *m_surface;
- QVideoSurfaceFormat m_surfaceFormat;
- int m_bytesPerLine = 0;
- bool m_surfaceStarted = false;
-
- QList<GUID> m_supportedTypes;
- QReadWriteLock m_typesLock;
-
- QRecursiveMutex m_renderMutex;
- bool m_running = false;
- IMediaSample *m_pendingSample = nullptr;
- REFERENCE_TIME m_pendingSampleEndTime = 0;
- HANDLE m_renderEvent;
- HANDLE m_flushEvent;
- DWORD_PTR m_adviseCookie = 0;
-
- bool m_EOS = false;
- bool m_EOSDelivered = false;
- UINT m_EOSTimer = 0;
-
- friend class VideoSurfaceInputPin;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/directshow/player/vmr9videowindowcontrol.cpp b/src/plugins/directshow/player/vmr9videowindowcontrol.cpp
deleted file mode 100644
index 63c945622..000000000
--- a/src/plugins/directshow/player/vmr9videowindowcontrol.cpp
+++ /dev/null
@@ -1,326 +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 "vmr9videowindowcontrol.h"
-
-#include "directshowglobal.h"
-
-#ifndef QT_NO_WIDGETS
-#include <QtGui/QPalette>
-#include <QtWidgets/QWidget>
-#endif
-
-Vmr9VideoWindowControl::Vmr9VideoWindowControl(QObject *parent)
- : QVideoWindowControl(parent)
- , m_filter(com_new<IBaseFilter>(CLSID_VideoMixingRenderer9))
-{
- if (IVMRFilterConfig9 *config = com_cast<IVMRFilterConfig9>(m_filter, IID_IVMRFilterConfig9)) {
- config->SetRenderingMode(VMR9Mode_Windowless);
- config->SetNumberOfStreams(1);
- config->Release();
- }
-}
-
-Vmr9VideoWindowControl::~Vmr9VideoWindowControl()
-{
- if (m_filter)
- m_filter->Release();
-}
-
-
-WId Vmr9VideoWindowControl::winId() const
-{
- return m_windowId;
-
-}
-
-void Vmr9VideoWindowControl::setWinId(WId id)
-{
- m_windowId = id;
-
-#ifndef QT_NO_WIDGETS
- if (QWidget *widget = QWidget::find(m_windowId)) {
- const QColor color = widget->palette().color(QPalette::Window);
-
- m_windowColor = RGB(color.red(), color.green(), color.blue());
- }
-#endif
-
- if (IVMRWindowlessControl9 *control = com_cast<IVMRWindowlessControl9>(
- m_filter, IID_IVMRWindowlessControl9)) {
- control->SetVideoClippingWindow(reinterpret_cast<HWND>(m_windowId));
- control->SetBorderColor(m_windowColor);
- control->Release();
- }
-}
-
-QRect Vmr9VideoWindowControl::displayRect() const
-{
- return m_displayRect;
-}
-
-void Vmr9VideoWindowControl::setDisplayRect(const QRect &rect)
-{
- m_displayRect = rect;
-
- if (IVMRWindowlessControl9 *control = com_cast<IVMRWindowlessControl9>(
- m_filter, IID_IVMRWindowlessControl9)) {
- RECT sourceRect = { 0, 0, 0, 0 };
- RECT displayRect = { rect.left(), rect.top(), rect.right() + 1, rect.bottom() + 1 };
-
- control->GetNativeVideoSize(&sourceRect.right, &sourceRect.bottom, nullptr, nullptr);
-
- if (m_aspectRatioMode == Qt::KeepAspectRatioByExpanding) {
- QSize clippedSize = rect.size();
- clippedSize.scale(sourceRect.right, sourceRect.bottom, Qt::KeepAspectRatio);
-
- sourceRect.left = (sourceRect.right - clippedSize.width()) / 2;
- sourceRect.top = (sourceRect.bottom - clippedSize.height()) / 2;
- sourceRect.right = sourceRect.left + clippedSize.width();
- sourceRect.bottom = sourceRect.top + clippedSize.height();
- }
-
- control->SetVideoPosition(&sourceRect, &displayRect);
- control->Release();
- }
-}
-
-bool Vmr9VideoWindowControl::isFullScreen() const
-{
- return m_fullScreen;
-}
-
-void Vmr9VideoWindowControl::setFullScreen(bool fullScreen)
-{
- emit fullScreenChanged(m_fullScreen = fullScreen);
-}
-
-void Vmr9VideoWindowControl::repaint()
-{
- PAINTSTRUCT paint;
-
- if (HDC dc = ::BeginPaint(reinterpret_cast<HWND>(m_windowId), &paint)) {
- HRESULT hr = E_FAIL;
-
- if (IVMRWindowlessControl9 *control = com_cast<IVMRWindowlessControl9>(
- m_filter, IID_IVMRWindowlessControl9)) {
- hr = control->RepaintVideo(reinterpret_cast<HWND>(m_windowId), dc);
- control->Release();
- }
-
- if (!SUCCEEDED(hr)) {
- HPEN pen = ::CreatePen(PS_SOLID, 1, m_windowColor);
- HBRUSH brush = ::CreateSolidBrush(m_windowColor);
- ::SelectObject(dc, pen);
- ::SelectObject(dc, brush);
-
- ::Rectangle(
- dc,
- m_displayRect.left(),
- m_displayRect.top(),
- m_displayRect.right() + 1,
- m_displayRect.bottom() + 1);
-
- ::DeleteObject(pen);
- ::DeleteObject(brush);
- }
- ::EndPaint(reinterpret_cast<HWND>(m_windowId), &paint);
- }
-}
-
-QSize Vmr9VideoWindowControl::nativeSize() const
-{
- QSize size;
-
- if (IVMRWindowlessControl9 *control = com_cast<IVMRWindowlessControl9>(
- m_filter, IID_IVMRWindowlessControl9)) {
- LONG width;
- LONG height;
-
- if (control->GetNativeVideoSize(&width, &height, nullptr, nullptr) == S_OK)
- size = QSize(width, height);
- control->Release();
- }
- return size;
-}
-
-Qt::AspectRatioMode Vmr9VideoWindowControl::aspectRatioMode() const
-{
- return m_aspectRatioMode;
-}
-
-void Vmr9VideoWindowControl::setAspectRatioMode(Qt::AspectRatioMode mode)
-{
- m_aspectRatioMode = mode;
-
- if (IVMRWindowlessControl9 *control = com_cast<IVMRWindowlessControl9>(
- m_filter, IID_IVMRWindowlessControl9)) {
- switch (mode) {
- case Qt::IgnoreAspectRatio:
- control->SetAspectRatioMode(VMR9ARMode_None);
- break;
- case Qt::KeepAspectRatio:
- control->SetAspectRatioMode(VMR9ARMode_LetterBox);
- break;
- case Qt::KeepAspectRatioByExpanding:
- control->SetAspectRatioMode(VMR9ARMode_LetterBox);
- setDisplayRect(m_displayRect);
- break;
- default:
- break;
- }
- control->Release();
- }
-}
-
-int Vmr9VideoWindowControl::brightness() const
-{
- return m_brightness;
-}
-
-void Vmr9VideoWindowControl::setBrightness(int brightness)
-{
- m_brightness = brightness;
-
- m_dirtyValues |= ProcAmpControl9_Brightness;
-
- setProcAmpValues();
-
- emit brightnessChanged(brightness);
-}
-
-int Vmr9VideoWindowControl::contrast() const
-{
- return m_contrast;
-}
-
-void Vmr9VideoWindowControl::setContrast(int contrast)
-{
- m_contrast = contrast;
-
- m_dirtyValues |= ProcAmpControl9_Contrast;
-
- setProcAmpValues();
-
- emit contrastChanged(contrast);
-}
-
-int Vmr9VideoWindowControl::hue() const
-{
- return m_hue;
-}
-
-void Vmr9VideoWindowControl::setHue(int hue)
-{
- m_hue = hue;
-
- m_dirtyValues |= ProcAmpControl9_Hue;
-
- setProcAmpValues();
-
- emit hueChanged(hue);
-}
-
-int Vmr9VideoWindowControl::saturation() const
-{
- return m_saturation;
-}
-
-void Vmr9VideoWindowControl::setSaturation(int saturation)
-{
- m_saturation = saturation;
-
- m_dirtyValues |= ProcAmpControl9_Saturation;
-
- setProcAmpValues();
-
- emit saturationChanged(saturation);
-}
-
-void Vmr9VideoWindowControl::setProcAmpValues()
-{
- if (IVMRMixerControl9 *control = com_cast<IVMRMixerControl9>(m_filter, IID_IVMRMixerControl9)) {
- VMR9ProcAmpControl procAmp;
- procAmp.dwSize = sizeof(VMR9ProcAmpControl);
- procAmp.dwFlags = m_dirtyValues;
-
- if (m_dirtyValues & ProcAmpControl9_Brightness) {
- procAmp.Brightness = scaleProcAmpValue(
- control, ProcAmpControl9_Brightness, m_brightness);
- }
- if (m_dirtyValues & ProcAmpControl9_Contrast) {
- procAmp.Contrast = scaleProcAmpValue(
- control, ProcAmpControl9_Contrast, m_contrast);
- }
- if (m_dirtyValues & ProcAmpControl9_Hue) {
- procAmp.Hue = scaleProcAmpValue(
- control, ProcAmpControl9_Hue, m_hue);
- }
- if (m_dirtyValues & ProcAmpControl9_Saturation) {
- procAmp.Saturation = scaleProcAmpValue(
- control, ProcAmpControl9_Saturation, m_saturation);
- }
-
- if (SUCCEEDED(control->SetProcAmpControl(0, &procAmp))) {
- m_dirtyValues = 0;
- }
-
- control->Release();
- }
-}
-
-float Vmr9VideoWindowControl::scaleProcAmpValue(
- IVMRMixerControl9 *control, VMR9ProcAmpControlFlags property, int value) const
-{
- float scaledValue = 0.0;
-
- VMR9ProcAmpControlRange range;
- range.dwSize = sizeof(VMR9ProcAmpControlRange);
- range.dwProperty = property;
-
- if (SUCCEEDED(control->GetProcAmpControlRange(0, &range))) {
- scaledValue = range.DefaultValue;
- if (value > 0)
- scaledValue += float(value) * (range.MaxValue - range.DefaultValue) / 100;
- else if (value < 0)
- scaledValue -= float(value) * (range.MinValue - range.DefaultValue) / 100;
- }
-
- return scaledValue;
-}
diff --git a/src/plugins/directshow/player/vmr9videowindowcontrol.h b/src/plugins/directshow/player/vmr9videowindowcontrol.h
deleted file mode 100644
index 2a6f008f3..000000000
--- a/src/plugins/directshow/player/vmr9videowindowcontrol.h
+++ /dev/null
@@ -1,108 +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 VMR9VIDEOWINDOWCONTROL_H
-#define VMR9VIDEOWINDOWCONTROL_H
-
-#include "qvideowindowcontrol.h"
-
-#include <dshow.h>
-#include <d3d9.h>
-#include <vmr9.h>
-
-QT_BEGIN_NAMESPACE
-
-class Vmr9VideoWindowControl : public QVideoWindowControl
-{
- Q_OBJECT
-public:
- Vmr9VideoWindowControl(QObject *parent = nullptr);
- ~Vmr9VideoWindowControl() override;
-
- IBaseFilter *filter() const { return m_filter; }
-
- WId winId() const override;
- void setWinId(WId id) override;
-
- QRect displayRect() const override;
- void setDisplayRect(const QRect &rect) override;
-
- bool isFullScreen() const override;
- void setFullScreen(bool fullScreen) override;
-
- void repaint() override;
-
- QSize nativeSize() const override;
-
- Qt::AspectRatioMode aspectRatioMode() const override;
- void setAspectRatioMode(Qt::AspectRatioMode mode) override;
-
- int brightness() const override;
- void setBrightness(int brightness) override;
-
- int contrast() const override;
- void setContrast(int contrast) override;
-
- int hue() const override;
- void setHue(int hue) override;
-
- int saturation() const override;
- void setSaturation(int saturation) override;
-
-private:
- void setProcAmpValues();
- float scaleProcAmpValue(
- IVMRMixerControl9 *control, VMR9ProcAmpControlFlags property, int value) const;
-
- IBaseFilter *m_filter;
- WId m_windowId = 0;
- COLORREF m_windowColor = RGB(0, 0, 0);
- DWORD m_dirtyValues = 0;
- Qt::AspectRatioMode m_aspectRatioMode = Qt::KeepAspectRatio;
- QRect m_displayRect;
- int m_brightness = 0;
- int m_contrast = 0;
- int m_hue = 0;
- int m_saturation = 0;
- bool m_fullScreen = false;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/gstreamer/audiodecoder/audiodecoder.json b/src/plugins/gstreamer/audiodecoder/audiodecoder.json
deleted file mode 100644
index 4314f2efa..000000000
--- a/src/plugins/gstreamer/audiodecoder/audiodecoder.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Keys": ["gstreameraudiodecode"],
- "Services": ["org.qt-project.qt.audiodecode"]
-}
diff --git a/src/plugins/gstreamer/audiodecoder/audiodecoder.pro b/src/plugins/gstreamer/audiodecoder/audiodecoder.pro
deleted file mode 100644
index 4e816e920..000000000
--- a/src/plugins/gstreamer/audiodecoder/audiodecoder.pro
+++ /dev/null
@@ -1,24 +0,0 @@
-TARGET = gstaudiodecoder
-
-include(../common.pri)
-
-INCLUDEPATH += $$PWD
-
-HEADERS += \
- $$PWD/qgstreameraudiodecodercontrol.h \
- $$PWD/qgstreameraudiodecoderservice.h \
- $$PWD/qgstreameraudiodecodersession.h \
- $$PWD/qgstreameraudiodecoderserviceplugin.h
-
-SOURCES += \
- $$PWD/qgstreameraudiodecodercontrol.cpp \
- $$PWD/qgstreameraudiodecoderservice.cpp \
- $$PWD/qgstreameraudiodecodersession.cpp \
- $$PWD/qgstreameraudiodecoderserviceplugin.cpp
-
-OTHER_FILES += \
- audiodecoder.json
-
-PLUGIN_TYPE = mediaservice
-PLUGIN_CLASS_NAME = QGstreamerAudioDecoderServicePlugin
-load(qt_plugin)
diff --git a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodercontrol.cpp b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodercontrol.cpp
deleted file mode 100644
index e9a7a5332..000000000
--- a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodercontrol.cpp
+++ /dev/null
@@ -1,139 +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 "qgstreameraudiodecodercontrol.h"
-#include "qgstreameraudiodecodersession.h"
-
-#include <QtCore/qdir.h>
-#include <QtCore/qsocketnotifier.h>
-#include <QtCore/qurl.h>
-#include <QtCore/qdebug.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-QT_BEGIN_NAMESPACE
-
-QGstreamerAudioDecoderControl::QGstreamerAudioDecoderControl(QGstreamerAudioDecoderSession *session, QObject *parent)
- : QAudioDecoderControl(parent)
- , m_session(session)
-{
- connect(m_session, SIGNAL(bufferAvailableChanged(bool)), this, SIGNAL(bufferAvailableChanged(bool)));
- connect(m_session, SIGNAL(bufferReady()), this, SIGNAL(bufferReady()));
- connect(m_session, SIGNAL(error(int,QString)), this, SIGNAL(error(int,QString)));
- connect(m_session, SIGNAL(formatChanged(QAudioFormat)), this, SIGNAL(formatChanged(QAudioFormat)));
- connect(m_session, SIGNAL(sourceChanged()), this, SIGNAL(sourceChanged()));
- connect(m_session, SIGNAL(stateChanged(QAudioDecoder::State)), this, SIGNAL(stateChanged(QAudioDecoder::State)));
- connect(m_session, SIGNAL(finished()), this, SIGNAL(finished()));
- connect(m_session, SIGNAL(positionChanged(qint64)), this, SIGNAL(positionChanged(qint64)));
- connect(m_session, SIGNAL(durationChanged(qint64)), this, SIGNAL(durationChanged(qint64)));
-}
-
-QGstreamerAudioDecoderControl::~QGstreamerAudioDecoderControl()
-{
-
-}
-
-QAudioDecoder::State QGstreamerAudioDecoderControl::state() const
-{
- return m_session->pendingState();
-}
-
-QString QGstreamerAudioDecoderControl::sourceFilename() const
-{
- return m_session->sourceFilename();
-}
-
-void QGstreamerAudioDecoderControl::setSourceFilename(const QString &fileName)
-{
- m_session->setSourceFilename(fileName);
-}
-
-QIODevice* QGstreamerAudioDecoderControl::sourceDevice() const
-{
- return m_session->sourceDevice();
-}
-
-void QGstreamerAudioDecoderControl::setSourceDevice(QIODevice *device)
-{
- m_session->setSourceDevice(device);
-}
-
-void QGstreamerAudioDecoderControl::start()
-{
- m_session->start();
-}
-
-void QGstreamerAudioDecoderControl::stop()
-{
- m_session->stop();
-}
-
-QAudioFormat QGstreamerAudioDecoderControl::audioFormat() const
-{
- return m_session->audioFormat();
-}
-
-void QGstreamerAudioDecoderControl::setAudioFormat(const QAudioFormat &format)
-{
- m_session->setAudioFormat(format);
-}
-
-QAudioBuffer QGstreamerAudioDecoderControl::read()
-{
- return m_session->read();
-}
-
-bool QGstreamerAudioDecoderControl::bufferAvailable() const
-{
- return m_session->bufferAvailable();
-}
-
-qint64 QGstreamerAudioDecoderControl::position() const
-{
- return m_session->position();
-}
-
-qint64 QGstreamerAudioDecoderControl::duration() const
-{
- return m_session->duration();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodercontrol.h b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodercontrol.h
deleted file mode 100644
index f5e26e553..000000000
--- a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodercontrol.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 QGSTREAMERPLAYERCONTROL_H
-#define QGSTREAMERPLAYERCONTROL_H
-
-#include <QtCore/qobject.h>
-#include <QtCore/qstack.h>
-
-#include <qaudioformat.h>
-#include <qaudiobuffer.h>
-#include <qaudiodecoder.h>
-#include <qaudiodecodercontrol.h>
-
-#include <limits.h>
-
-
-QT_BEGIN_NAMESPACE
-
-class QGstreamerAudioDecoderSession;
-class QGstreamerAudioDecoderService;
-
-class QGstreamerAudioDecoderControl : public QAudioDecoderControl
-{
- Q_OBJECT
-
-public:
- QGstreamerAudioDecoderControl(QGstreamerAudioDecoderSession *session, QObject *parent = 0);
- ~QGstreamerAudioDecoderControl();
-
- QAudioDecoder::State state() const override;
-
- QString sourceFilename() const override;
- void setSourceFilename(const QString &fileName) override;
-
- QIODevice* sourceDevice() const override;
- void setSourceDevice(QIODevice *device) override;
-
- void start() override;
- void stop() override;
-
- QAudioFormat audioFormat() const override;
- void setAudioFormat(const QAudioFormat &format) override;
-
- QAudioBuffer read() override;
- bool bufferAvailable() const override;
-
- qint64 position() const override;
- qint64 duration() const override;
-
-private:
- // Stuff goes here
-
- QGstreamerAudioDecoderSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderservice.cpp b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderservice.cpp
deleted file mode 100644
index 67b49be7e..000000000
--- a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderservice.cpp
+++ /dev/null
@@ -1,73 +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 <QtCore/qvariant.h>
-#include <QtCore/qdebug.h>
-
-#include "qgstreameraudiodecoderservice.h"
-#include "qgstreameraudiodecodercontrol.h"
-#include "qgstreameraudiodecodersession.h"
-
-QT_BEGIN_NAMESPACE
-
-QGstreamerAudioDecoderService::QGstreamerAudioDecoderService(QObject *parent)
- : QMediaService(parent)
-{
- m_session = new QGstreamerAudioDecoderSession(this);
- m_control = new QGstreamerAudioDecoderControl(m_session, this);
-}
-
-QGstreamerAudioDecoderService::~QGstreamerAudioDecoderService()
-{
-}
-
-QMediaControl *QGstreamerAudioDecoderService::requestControl(const char *name)
-{
- if (qstrcmp(name, QAudioDecoderControl_iid) == 0)
- return m_control;
-
- return 0;
-}
-
-void QGstreamerAudioDecoderService::releaseControl(QMediaControl *control)
-{
- Q_UNUSED(control);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderservice.h b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderservice.h
deleted file mode 100644
index 6e52b9d87..000000000
--- a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderservice.h
+++ /dev/null
@@ -1,69 +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 QGSTREAMERAUDIODECODERSERVICE_H
-#define QGSTREAMERAUDIODECODERSERVICE_H
-
-#include <QtCore/qobject.h>
-#include <QtCore/qiodevice.h>
-
-#include <qmediaservice.h>
-
-QT_BEGIN_NAMESPACE
-class QGstreamerAudioDecoderControl;
-class QGstreamerAudioDecoderSession;
-
-class QGstreamerAudioDecoderService : public QMediaService
-{
- Q_OBJECT
-public:
- QGstreamerAudioDecoderService(QObject *parent = 0);
- ~QGstreamerAudioDecoderService();
-
- QMediaControl *requestControl(const char *name) override;
- void releaseControl(QMediaControl *control) override;
-
-private:
- QGstreamerAudioDecoderControl *m_control;
- QGstreamerAudioDecoderSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderserviceplugin.cpp b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderserviceplugin.cpp
deleted file mode 100644
index 463ed2d92..000000000
--- a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderserviceplugin.cpp
+++ /dev/null
@@ -1,99 +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 "qgstreameraudiodecoderserviceplugin.h"
-
-#include "qgstreameraudiodecoderservice.h"
-#include <private/qgstutils_p.h>
-
-#include <QtCore/qstring.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/QDir>
-#include <QtCore/QDebug>
-
-// #define QT_SUPPORTEDMIMETYPES_DEBUG
-
-QMediaService* QGstreamerAudioDecoderServicePlugin::create(const QString &key)
-{
- QGstUtils::initializeGst();
-
- if (key == QLatin1String(Q_MEDIASERVICE_AUDIODECODER))
- return new QGstreamerAudioDecoderService;
-
- qWarning() << "Gstreamer audio decoder service plugin: unsupported key:" << key;
- return 0;
-}
-
-void QGstreamerAudioDecoderServicePlugin::release(QMediaService *service)
-{
- delete service;
-}
-
-QMultimedia::SupportEstimate QGstreamerAudioDecoderServicePlugin::hasSupport(const QString &mimeType,
- const QStringList &codecs) const
-{
- if (m_supportedMimeTypeSet.isEmpty())
- updateSupportedMimeTypes();
-
- return QGstUtils::hasSupport(mimeType, codecs, m_supportedMimeTypeSet);
-}
-
-static bool isDecoderOrDemuxer(GstElementFactory *factory)
-{
-#if GST_CHECK_VERSION(0, 10, 31)
- return gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_DEMUXER)
- || gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_DECODER
- | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO);
-#else
- return (factory
- && (qstrcmp(factory->details.klass, "Codec/Decoder/Audio") == 0
- || qstrcmp(factory->details.klass, "Codec/Demux") == 0));
-#endif
-}
-
-void QGstreamerAudioDecoderServicePlugin::updateSupportedMimeTypes() const
-{
- m_supportedMimeTypeSet = QGstUtils::supportedMimeTypes(isDecoderOrDemuxer);
-}
-
-QStringList QGstreamerAudioDecoderServicePlugin::supportedMimeTypes() const
-{
- return QStringList();
-}
-
diff --git a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderserviceplugin.h b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderserviceplugin.h
deleted file mode 100644
index d1b96043b..000000000
--- a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderserviceplugin.h
+++ /dev/null
@@ -1,72 +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 QGSTREAMERAUDIODECODERSERVICEPLUGIN_H
-#define QGSTREAMERAUDIODECODERSERVICEPLUGIN_H
-
-#include <qmediaserviceproviderplugin.h>
-#include <QtCore/qset.h>
-#include <QtCore/QObject>
-
-QT_BEGIN_NAMESPACE
-
-class QGstreamerAudioDecoderServicePlugin
- : public QMediaServiceProviderPlugin
- , public QMediaServiceSupportedFormatsInterface
-{
- Q_OBJECT
- Q_INTERFACES(QMediaServiceSupportedFormatsInterface)
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "audiodecoder.json")
-
-public:
- QMediaService* create(const QString &key) override;
- void release(QMediaService *service) override;
-
- QMultimedia::SupportEstimate hasSupport(const QString &mimeType, const QStringList &codecs) const override;
- QStringList supportedMimeTypes() const override;
-
-private:
- void updateSupportedMimeTypes() const;
-
- mutable QSet<QString> m_supportedMimeTypeSet;
-};
-
-QT_END_NAMESPACE
-
-#endif // QGSTREAMERAUDIODECODERSERVICEPLUGIN_H
diff --git a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp
deleted file mode 100644
index d6b8ad759..000000000
--- a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp
+++ /dev/null
@@ -1,614 +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$
-**
-****************************************************************************/
-//#define DEBUG_DECODER
-
-#include "qgstreameraudiodecodersession.h"
-#include <private/qgstreamerbushelper_p.h>
-
-#include <private/qgstutils_p.h>
-
-#include <gst/gstvalue.h>
-#include <gst/base/gstbasesrc.h>
-
-#include <QtCore/qdatetime.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qsize.h>
-#include <QtCore/qtimer.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qdir.h>
-#include <QtCore/qstandardpaths.h>
-#include <QtCore/qurl.h>
-
-#define MAX_BUFFERS_IN_QUEUE 4
-
-QT_BEGIN_NAMESPACE
-
-typedef enum {
- GST_PLAY_FLAG_VIDEO = 0x00000001,
- GST_PLAY_FLAG_AUDIO = 0x00000002,
- GST_PLAY_FLAG_TEXT = 0x00000004,
- GST_PLAY_FLAG_VIS = 0x00000008,
- GST_PLAY_FLAG_SOFT_VOLUME = 0x00000010,
- GST_PLAY_FLAG_NATIVE_AUDIO = 0x00000020,
- GST_PLAY_FLAG_NATIVE_VIDEO = 0x00000040,
- GST_PLAY_FLAG_DOWNLOAD = 0x00000080,
- GST_PLAY_FLAG_BUFFERING = 0x000000100
-} GstPlayFlags;
-
-QGstreamerAudioDecoderSession::QGstreamerAudioDecoderSession(QObject *parent)
- : QObject(parent),
- m_state(QAudioDecoder::StoppedState),
- m_pendingState(QAudioDecoder::StoppedState),
- m_busHelper(0),
- m_bus(0),
- m_playbin(0),
- m_outputBin(0),
- m_audioConvert(0),
- m_appSink(0),
-#if QT_CONFIG(gstreamer_app)
- m_appSrc(0),
-#endif
- mDevice(0),
- m_buffersAvailable(0),
- m_position(-1),
- m_duration(-1),
- m_durationQueries(0)
-{
- // Create pipeline here
- m_playbin = gst_element_factory_make(QT_GSTREAMER_PLAYBIN_ELEMENT_NAME, NULL);
-
- if (m_playbin != 0) {
- // Sort out messages
- m_bus = gst_element_get_bus(m_playbin);
- m_busHelper = new QGstreamerBusHelper(m_bus, this);
- m_busHelper->installMessageFilter(this);
-
- // Set the rest of the pipeline up
- setAudioFlags(true);
-
- m_audioConvert = gst_element_factory_make("audioconvert", NULL);
-
- m_outputBin = gst_bin_new("audio-output-bin");
- gst_bin_add(GST_BIN(m_outputBin), m_audioConvert);
-
- // add ghostpad
- GstPad *pad = gst_element_get_static_pad(m_audioConvert, "sink");
- Q_ASSERT(pad);
- gst_element_add_pad(GST_ELEMENT(m_outputBin), gst_ghost_pad_new("sink", pad));
- gst_object_unref(GST_OBJECT(pad));
-
- g_object_set(G_OBJECT(m_playbin), "audio-sink", m_outputBin, NULL);
-#if QT_CONFIG(gstreamer_app)
- g_signal_connect(G_OBJECT(m_playbin), "deep-notify::source", (GCallback) &QGstreamerAudioDecoderSession::configureAppSrcElement, (gpointer)this);
-#endif
-
- // Set volume to 100%
- gdouble volume = 1.0;
- g_object_set(G_OBJECT(m_playbin), "volume", volume, NULL);
- }
-}
-
-QGstreamerAudioDecoderSession::~QGstreamerAudioDecoderSession()
-{
- if (m_playbin) {
- stop();
-
- delete m_busHelper;
-#if QT_CONFIG(gstreamer_app)
- delete m_appSrc;
-#endif
- gst_object_unref(GST_OBJECT(m_bus));
- gst_object_unref(GST_OBJECT(m_playbin));
- }
-}
-
-#if QT_CONFIG(gstreamer_app)
-void QGstreamerAudioDecoderSession::configureAppSrcElement(GObject* object, GObject *orig, GParamSpec *pspec, QGstreamerAudioDecoderSession* self)
-{
- Q_UNUSED(object);
- Q_UNUSED(pspec);
-
- // In case we switch from appsrc to not
- if (!self->appsrc())
- return;
-
- GstElement *appsrc;
- g_object_get(orig, "source", &appsrc, NULL);
-
- if (!self->appsrc()->setup(appsrc))
- qWarning()<<"Could not setup appsrc element";
-
- g_object_unref(G_OBJECT(appsrc));
-}
-#endif
-
-bool QGstreamerAudioDecoderSession::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) == GST_OBJECT_CAST(m_playbin)) {
- 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
-
- QAudioDecoder::State prevState = m_state;
-
- switch (newState) {
- case GST_STATE_VOID_PENDING:
- case GST_STATE_NULL:
- m_state = QAudioDecoder::StoppedState;
- break;
- case GST_STATE_READY:
- m_state = QAudioDecoder::StoppedState;
- break;
- case GST_STATE_PLAYING:
- m_state = QAudioDecoder::DecodingState;
- break;
- case GST_STATE_PAUSED:
- m_state = QAudioDecoder::DecodingState;
-
- //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;
- }
-
- if (prevState != m_state)
- emit stateChanged(m_state);
- }
- break;
-
- case GST_MESSAGE_EOS:
- m_pendingState = m_state = QAudioDecoder::StoppedState;
- emit finished();
- emit stateChanged(m_state);
- 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);
- 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;
- }
- } else if (err->domain == GST_CORE_ERROR) {
- switch (err->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);
- }
- }
-
- return false;
-}
-
-QString QGstreamerAudioDecoderSession::sourceFilename() const
-{
- return mSource;
-}
-
-void QGstreamerAudioDecoderSession::setSourceFilename(const QString &fileName)
-{
- stop();
- mDevice = 0;
-#if QT_CONFIG(gstreamer_app)
- if (m_appSrc)
- m_appSrc->deleteLater();
- m_appSrc = 0;
-#endif
-
- bool isSignalRequired = (mSource != fileName);
- mSource = fileName;
- if (isSignalRequired)
- emit sourceChanged();
-}
-
-QIODevice *QGstreamerAudioDecoderSession::sourceDevice() const
-{
- return mDevice;
-}
-
-void QGstreamerAudioDecoderSession::setSourceDevice(QIODevice *device)
-{
- stop();
- mSource.clear();
- bool isSignalRequired = (mDevice != device);
- mDevice = device;
- if (isSignalRequired)
- emit sourceChanged();
-}
-
-void QGstreamerAudioDecoderSession::start()
-{
- if (!m_playbin) {
- processInvalidMedia(QAudioDecoder::ResourceError, "Playbin element is not valid");
- return;
- }
-
- addAppSink();
-
- if (!mSource.isEmpty()) {
- g_object_set(G_OBJECT(m_playbin), "uri", QUrl::fromLocalFile(mSource).toEncoded().constData(), NULL);
- } else if (mDevice) {
-#if QT_CONFIG(gstreamer_app)
- // make sure we can read from device
- if (!mDevice->isOpen() || !mDevice->isReadable()) {
- processInvalidMedia(QAudioDecoder::AccessDeniedError, "Unable to read from specified device");
- return;
- }
-
- if (!m_appSrc)
- m_appSrc = new QGstAppSrc(this);
- m_appSrc->setStream(mDevice);
-
- g_object_set(G_OBJECT(m_playbin), "uri", "appsrc://", NULL);
-#endif
- } else {
- return;
- }
-
- // Set audio format
- if (m_appSink) {
- if (mFormat.isValid()) {
- setAudioFlags(false);
- GstCaps *caps = QGstUtils::capsForAudioFormat(mFormat);
- gst_app_sink_set_caps(m_appSink, caps);
- gst_caps_unref(caps);
- } else {
- // We want whatever the native audio format is
- setAudioFlags(true);
- gst_app_sink_set_caps(m_appSink, NULL);
- }
- }
-
- m_pendingState = QAudioDecoder::DecodingState;
- if (gst_element_set_state(m_playbin, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
- qWarning() << "GStreamer; Unable to start decoding process";
- m_pendingState = m_state = QAudioDecoder::StoppedState;
-
- emit stateChanged(m_state);
- }
-}
-
-void QGstreamerAudioDecoderSession::stop()
-{
- if (m_playbin) {
- gst_element_set_state(m_playbin, GST_STATE_NULL);
- removeAppSink();
-
- QAudioDecoder::State oldState = m_state;
- m_pendingState = m_state = QAudioDecoder::StoppedState;
-
- // GStreamer thread is stopped. Can safely access m_buffersAvailable
- 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);
- }
-
- if (oldState != m_state)
- emit stateChanged(m_state);
- }
-}
-
-QAudioFormat QGstreamerAudioDecoderSession::audioFormat() const
-{
- return mFormat;
-}
-
-void QGstreamerAudioDecoderSession::setAudioFormat(const QAudioFormat &format)
-{
- if (mFormat != format) {
- mFormat = format;
- emit formatChanged(mFormat);
- }
-}
-
-QAudioBuffer QGstreamerAudioDecoderSession::read()
-{
- QAudioBuffer audioBuffer;
-
- int buffersAvailable;
- {
- QMutexLocker locker(&m_buffersMutex);
- buffersAvailable = m_buffersAvailable;
-
- // need to decrement before pulling a buffer
- // to make sure assert in QGstreamerAudioDecoderSession::new_buffer works
- m_buffersAvailable--;
- }
-
-
- if (buffersAvailable) {
- if (buffersAvailable == 1)
- emit bufferAvailableChanged(false);
-
- const char* bufferData = 0;
- int bufferSize = 0;
-
-#if GST_CHECK_VERSION(1,0,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);
-#else
- GstBuffer *buffer = gst_app_sink_pull_buffer(m_appSink);
- bufferData = (const char*)buffer->data;
- bufferSize = buffer->size;
- QAudioFormat format = QGstUtils::audioFormatForBuffer(buffer);
-#endif
-
- 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 GST_CHECK_VERSION(1,0,0)
- gst_buffer_unmap(buffer, &mapInfo);
- gst_sample_unref(sample);
-#else
- gst_buffer_unref(buffer);
-#endif
- }
-
- return audioBuffer;
-}
-
-bool QGstreamerAudioDecoderSession::bufferAvailable() const
-{
- QMutexLocker locker(&m_buffersMutex);
- return m_buffersAvailable > 0;
-}
-
-qint64 QGstreamerAudioDecoderSession::position() const
-{
- return m_position;
-}
-
-qint64 QGstreamerAudioDecoderSession::duration() const
-{
- return m_duration;
-}
-
-void QGstreamerAudioDecoderSession::processInvalidMedia(QAudioDecoder::Error errorCode, const QString& errorString)
-{
- stop();
- emit error(int(errorCode), errorString);
-}
-
-GstFlowReturn QGstreamerAudioDecoderSession::new_sample(GstAppSink *, gpointer user_data)
-{
- // "Note that the preroll buffer will also be returned as the first buffer when calling gst_app_sink_pull_buffer()."
- QGstreamerAudioDecoderSession *session = reinterpret_cast<QGstreamerAudioDecoderSession*>(user_data);
-
- int buffersAvailable;
- {
- QMutexLocker locker(&session->m_buffersMutex);
- buffersAvailable = session->m_buffersAvailable;
- session->m_buffersAvailable++;
- Q_ASSERT(session->m_buffersAvailable <= MAX_BUFFERS_IN_QUEUE);
- }
-
- if (!buffersAvailable)
- QMetaObject::invokeMethod(session, "bufferAvailableChanged", Qt::QueuedConnection, Q_ARG(bool, true));
- QMetaObject::invokeMethod(session, "bufferReady", Qt::QueuedConnection);
- return GST_FLOW_OK;
-}
-
-void QGstreamerAudioDecoderSession::setAudioFlags(bool wantNativeAudio)
-{
- int flags = 0;
- if (m_playbin) {
- g_object_get(G_OBJECT(m_playbin), "flags", &flags, NULL);
- // make sure not to use GST_PLAY_FLAG_NATIVE_AUDIO unless desired
- // it prevents audio format conversion
- flags &= ~(GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_TEXT | GST_PLAY_FLAG_VIS | GST_PLAY_FLAG_NATIVE_AUDIO);
- flags |= GST_PLAY_FLAG_AUDIO;
- if (wantNativeAudio)
- flags |= GST_PLAY_FLAG_NATIVE_AUDIO;
- g_object_set(G_OBJECT(m_playbin), "flags", flags, NULL);
- }
-}
-
-void QGstreamerAudioDecoderSession::addAppSink()
-{
- if (m_appSink)
- return;
-
- m_appSink = (GstAppSink*)gst_element_factory_make("appsink", NULL);
-
- GstAppSinkCallbacks callbacks;
- memset(&callbacks, 0, sizeof(callbacks));
-#if GST_CHECK_VERSION(1,0,0)
- callbacks.new_sample = &new_sample;
-#else
- callbacks.new_buffer = &new_sample;
-#endif
- gst_app_sink_set_callbacks(m_appSink, &callbacks, this, NULL);
- 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(GST_BIN(m_outputBin), GST_ELEMENT(m_appSink));
- gst_element_link(m_audioConvert, GST_ELEMENT(m_appSink));
-}
-
-void QGstreamerAudioDecoderSession::removeAppSink()
-{
- if (!m_appSink)
- return;
-
- gst_element_unlink(m_audioConvert, GST_ELEMENT(m_appSink));
- gst_bin_remove(GST_BIN(m_outputBin), GST_ELEMENT(m_appSink));
-
- m_appSink = 0;
-}
-
-void QGstreamerAudioDecoderSession::updateDuration()
-{
- gint64 gstDuration = 0;
- int duration = -1;
-
- if (m_playbin && qt_gst_element_query_duration(m_playbin, GST_FORMAT_TIME, &gstDuration))
- duration = gstDuration / 1000000;
-
- if (m_duration != duration) {
- m_duration = duration;
- emit durationChanged(m_duration);
- }
-
- if (m_duration > 0)
- m_durationQueries = 0;
-
- if (m_durationQueries > 0) {
- //increase delay between duration requests
- int delay = 25 << (5 - m_durationQueries);
- QTimer::singleShot(delay, this, SLOT(updateDuration()));
- m_durationQueries--;
- }
-}
-
-qint64 QGstreamerAudioDecoderSession::getPositionFromBuffer(GstBuffer* buffer)
-{
- qint64 position = GST_BUFFER_TIMESTAMP(buffer);
- if (position >= 0)
- position = position / G_GINT64_CONSTANT(1000); // microseconds
- else
- position = -1;
- return position;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.h b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.h
deleted file mode 100644
index 385908cbd..000000000
--- a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.h
+++ /dev/null
@@ -1,157 +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 QGSTREAMERPLAYERSESSION_H
-#define QGSTREAMERPLAYERSESSION_H
-
-#include <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include <QObject>
-#include <QtCore/qmutex.h>
-#include "qgstreameraudiodecodercontrol.h"
-#include <private/qgstreamerbushelper_p.h>
-#include "qaudiodecoder.h"
-
-#if QT_CONFIG(gstreamer_app)
-#include <private/qgstappsrc_p.h>
-#endif
-
-#include <gst/gst.h>
-#include <gst/app/gstappsink.h>
-
-QT_BEGIN_NAMESPACE
-
-class QGstreamerBusHelper;
-class QGstreamerMessage;
-
-class QGstreamerAudioDecoderSession : public QObject,
- public QGstreamerBusMessageFilter
-{
-Q_OBJECT
-Q_INTERFACES(QGstreamerBusMessageFilter)
-
-public:
- QGstreamerAudioDecoderSession(QObject *parent);
- virtual ~QGstreamerAudioDecoderSession();
-
- QGstreamerBusHelper *bus() const { return m_busHelper; }
-
- QAudioDecoder::State state() const { return m_state; }
- QAudioDecoder::State pendingState() const { return m_pendingState; }
-
- bool processBusMessage(const QGstreamerMessage &message) override;
-
-#if QT_CONFIG(gstreamer_app)
- QGstAppSrc *appsrc() const { return m_appSrc; }
- static void configureAppSrcElement(GObject*, GObject*, GParamSpec*,QGstreamerAudioDecoderSession* _this);
-#endif
-
- QString sourceFilename() const;
- void setSourceFilename(const QString &fileName);
-
- QIODevice* sourceDevice() const;
- void setSourceDevice(QIODevice *device);
-
- void start();
- void stop();
-
- QAudioFormat audioFormat() const;
- void setAudioFormat(const QAudioFormat &format);
-
- QAudioBuffer read();
- bool bufferAvailable() const;
-
- qint64 position() const;
- qint64 duration() const;
-
- static GstFlowReturn new_sample(GstAppSink *sink, gpointer user_data);
-
-signals:
- void stateChanged(QAudioDecoder::State newState);
- void formatChanged(const QAudioFormat &format);
- void sourceChanged();
-
- void error(int error, const QString &errorString);
-
- void bufferReady();
- void bufferAvailableChanged(bool available);
- void finished();
-
- void positionChanged(qint64 position);
- void durationChanged(qint64 duration);
-
-private slots:
- void updateDuration();
-
-private:
- void setAudioFlags(bool wantNativeAudio);
- void addAppSink();
- void removeAppSink();
-
- void processInvalidMedia(QAudioDecoder::Error errorCode, const QString& errorString);
- static qint64 getPositionFromBuffer(GstBuffer* buffer);
-
- QAudioDecoder::State m_state;
- QAudioDecoder::State m_pendingState;
- QGstreamerBusHelper *m_busHelper;
- GstBus *m_bus;
- GstElement *m_playbin;
- GstElement *m_outputBin;
- GstElement *m_audioConvert;
- GstAppSink *m_appSink;
-
-#if QT_CONFIG(gstreamer_app)
- QGstAppSrc *m_appSrc;
-#endif
-
- QString mSource;
- QIODevice *mDevice; // QWeakPointer perhaps
- QAudioFormat mFormat;
-
- mutable QMutex m_buffersMutex;
- int m_buffersAvailable;
-
- qint64 m_position;
- qint64 m_duration;
-
- int m_durationQueries;
-};
-
-QT_END_NAMESPACE
-
-#endif // QGSTREAMERPLAYERSESSION_H
diff --git a/src/plugins/gstreamer/camerabin/camerabin.json b/src/plugins/gstreamer/camerabin/camerabin.json
deleted file mode 100644
index 3246b4683..000000000
--- a/src/plugins/gstreamer/camerabin/camerabin.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Keys": ["gstreamercamerabin"],
- "Services": ["org.qt-project.qt.camera"]
-}
diff --git a/src/plugins/gstreamer/camerabin/camerabin.pro b/src/plugins/gstreamer/camerabin/camerabin.pro
deleted file mode 100644
index 183ddf2d4..000000000
--- a/src/plugins/gstreamer/camerabin/camerabin.pro
+++ /dev/null
@@ -1,85 +0,0 @@
-TARGET = gstcamerabin
-
-QT += multimedia-private
-
-include(../common.pri)
-
-INCLUDEPATH += $$PWD \
- $${SOURCE_DIR}/src/multimedia
-
-INCLUDEPATH += camerabin
-
-
-HEADERS += \
- $$PWD/camerabinserviceplugin.h \
- $$PWD/camerabinservice.h \
- $$PWD/camerabinsession.h \
- $$PWD/camerabincontrol.h \
- $$PWD/camerabinaudioencoder.h \
- $$PWD/camerabinimageencoder.h \
- $$PWD/camerabinrecorder.h \
- $$PWD/camerabincontainer.h \
- $$PWD/camerabinimagecapture.h \
- $$PWD/camerabinzoom.h \
- $$PWD/camerabinimageprocessing.h \
- $$PWD/camerabinmetadata.h \
- $$PWD/camerabinvideoencoder.h \
- $$PWD/camerabinresourcepolicy.h \
- $$PWD/camerabincapturedestination.h \
- $$PWD/camerabincapturebufferformat.h \
- $$PWD/camerabinviewfindersettings.h \
- $$PWD/camerabinviewfindersettings2.h \
- $$PWD/camerabininfocontrol.h
-
-SOURCES += \
- $$PWD/camerabinserviceplugin.cpp \
- $$PWD/camerabinservice.cpp \
- $$PWD/camerabinsession.cpp \
- $$PWD/camerabincontrol.cpp \
- $$PWD/camerabinaudioencoder.cpp \
- $$PWD/camerabincontainer.cpp \
- $$PWD/camerabinimagecapture.cpp \
- $$PWD/camerabinimageencoder.cpp \
- $$PWD/camerabinzoom.cpp \
- $$PWD/camerabinimageprocessing.cpp \
- $$PWD/camerabinmetadata.cpp \
- $$PWD/camerabinrecorder.cpp \
- $$PWD/camerabinvideoencoder.cpp \
- $$PWD/camerabinresourcepolicy.cpp \
- $$PWD/camerabincapturedestination.cpp \
- $$PWD/camerabinviewfindersettings.cpp \
- $$PWD/camerabinviewfindersettings2.cpp \
- $$PWD/camerabincapturebufferformat.cpp \
- $$PWD/camerabininfocontrol.cpp
-
-qtConfig(gstreamer_photography) {
- HEADERS += \
- $$PWD/camerabinfocus.h \
- $$PWD/camerabinexposure.h \
- $$PWD/camerabinflash.h \
- $$PWD/camerabinlocks.h
-
- SOURCES += \
- $$PWD/camerabinexposure.cpp \
- $$PWD/camerabinflash.cpp \
- $$PWD/camerabinfocus.cpp \
- $$PWD/camerabinlocks.cpp
-
- QMAKE_USE += gstreamer_photography
- DEFINES += GST_USE_UNSTABLE_API #prevents warnings because of unstable photography API
-}
-
-qtConfig(linux_v4l) {
- HEADERS += \
- $$PWD/camerabinv4limageprocessing.h
-
- SOURCES += \
- $$PWD/camerabinv4limageprocessing.cpp
-}
-
-OTHER_FILES += \
- camerabin.json
-
-PLUGIN_TYPE = mediaservice
-PLUGIN_CLASS_NAME = CameraBinServicePlugin
-load(qt_plugin)
diff --git a/src/plugins/gstreamer/camerabin/camerabinaudioencoder.cpp b/src/plugins/gstreamer/camerabin/camerabinaudioencoder.cpp
deleted file mode 100644
index e5e253194..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinaudioencoder.cpp
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include "camerabinaudioencoder.h"
-#include "camerabincontainer.h"
-#include <private/qgstutils_p.h>
-
-#include <QtCore/qdebug.h>
-
-QT_BEGIN_NAMESPACE
-
-CameraBinAudioEncoder::CameraBinAudioEncoder(QObject *parent)
- :QAudioEncoderSettingsControl(parent)
-#if QT_CONFIG(gstreamer_encodingprofiles)
- , m_codecs(QGstCodecsInfo::AudioEncoder)
-#endif
-{
-}
-
-CameraBinAudioEncoder::~CameraBinAudioEncoder()
-{
-}
-
-QStringList CameraBinAudioEncoder::supportedAudioCodecs() const
-{
-#if QT_CONFIG(gstreamer_encodingprofiles)
- return m_codecs.supportedCodecs();
-#else
- return QStringList();
-#endif
-}
-
-QString CameraBinAudioEncoder::codecDescription(const QString &codecName) const
-{
-#if QT_CONFIG(gstreamer_encodingprofiles)
- return m_codecs.codecDescription(codecName);
-#else
- Q_UNUSED(codecName);
- return QString();
-#endif
-}
-
-QList<int> CameraBinAudioEncoder::supportedSampleRates(const QAudioEncoderSettings &, bool *) const
-{
- //TODO check element caps to find actual values
-
- return QList<int>();
-}
-
-QAudioEncoderSettings CameraBinAudioEncoder::audioSettings() const
-{
- return m_audioSettings;
-}
-
-void CameraBinAudioEncoder::setAudioSettings(const QAudioEncoderSettings &settings)
-{
- if (m_audioSettings != settings) {
- m_audioSettings = settings;
- m_actualAudioSettings = settings;
- emit settingsChanged();
- }
-}
-
-QAudioEncoderSettings CameraBinAudioEncoder::actualAudioSettings() const
-{
- return m_actualAudioSettings;
-}
-
-void CameraBinAudioEncoder::setActualAudioSettings(const QAudioEncoderSettings &settings)
-{
- m_actualAudioSettings = settings;
-}
-
-void CameraBinAudioEncoder::resetActualSettings()
-{
- m_actualAudioSettings = m_audioSettings;
-}
-
-#if QT_CONFIG(gstreamer_encodingprofiles)
-
-GstEncodingProfile *CameraBinAudioEncoder::createProfile()
-{
- QString codec = m_actualAudioSettings.codec();
- QString preset = m_actualAudioSettings.encodingOption(QStringLiteral("preset")).toString();
- GstCaps *caps;
-
- if (codec.isEmpty())
- return 0;
- else
- caps = gst_caps_from_string(codec.toLatin1());
-
- GstEncodingProfile *profile = (GstEncodingProfile *)gst_encoding_audio_profile_new(
- caps,
- !preset.isEmpty() ? preset.toLatin1().constData() : NULL, //preset
- NULL, //restriction
- 0); //presence
-
- gst_caps_unref(caps);
-
- return profile;
-}
-
-#endif
-
-void CameraBinAudioEncoder::applySettings(GstElement *encoder)
-{
- GObjectClass * const objectClass = G_OBJECT_GET_CLASS(encoder);
- const char * const name = qt_gst_element_get_factory_name(encoder);
-
- const bool isVorbis = qstrcmp(name, "vorbisenc") == 0;
-
- const int bitRate = m_actualAudioSettings.bitRate();
- if (!isVorbis && bitRate == -1) {
- // Bit rate is invalid, don't evaluate the remaining conditions unless the encoder is
- // vorbisenc which is known to accept -1 as an unspecified bitrate.
- } else if (g_object_class_find_property(objectClass, "bitrate")) {
- g_object_set(G_OBJECT(encoder), "bitrate", bitRate, NULL);
- } else if (g_object_class_find_property(objectClass, "target-bitrate")) {
- g_object_set(G_OBJECT(encoder), "target-bitrate", bitRate, NULL);
- }
-
- if (isVorbis) {
- static const double qualities[] = { 0.1, 0.3, 0.5, 0.7, 1.0 };
- g_object_set(G_OBJECT(encoder), "quality", qualities[m_actualAudioSettings.quality()], NULL);
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinaudioencoder.h b/src/plugins/gstreamer/camerabin/camerabinaudioencoder.h
deleted file mode 100644
index b8091c8ca..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinaudioencoder.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 CAMERABINAUDIOENCODE_H
-#define CAMERABINAUDIOENCODE_H
-
-#include <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include <qaudioencodersettingscontrol.h>
-
-#include <QtCore/qstringlist.h>
-#include <QtCore/qmap.h>
-#include <QtCore/qset.h>
-
-#include <gst/gst.h>
-#include <gst/pbutils/pbutils.h>
-
-#if QT_CONFIG(gstreamer_encodingprofiles)
-#include <gst/pbutils/encoding-profile.h>
-#include <private/qgstcodecsinfo_p.h>
-#endif
-
-#include <qaudioformat.h>
-
-QT_BEGIN_NAMESPACE
-class CameraBinSession;
-
-class CameraBinAudioEncoder : public QAudioEncoderSettingsControl
-{
- Q_OBJECT
-public:
- CameraBinAudioEncoder(QObject *parent);
- virtual ~CameraBinAudioEncoder();
-
- QStringList supportedAudioCodecs() const override;
- QString codecDescription(const QString &codecName) const override;
-
- QStringList supportedEncodingOptions(const QString &codec) const;
- QVariant encodingOption(const QString &codec, const QString &name) const;
- void setEncodingOption(const QString &codec, const QString &name, const QVariant &value);
-
- QList<int> supportedSampleRates(const QAudioEncoderSettings &settings = QAudioEncoderSettings(),
- bool *isContinuous = 0) const override;
- QList<int> supportedChannelCounts(const QAudioEncoderSettings &settings = QAudioEncoderSettings()) const;
- QList<int> supportedSampleSizes(const QAudioEncoderSettings &settings = QAudioEncoderSettings()) const;
-
- QAudioEncoderSettings audioSettings() const override;
- void setAudioSettings(const QAudioEncoderSettings &) override;
-
- QAudioEncoderSettings actualAudioSettings() const;
- void setActualAudioSettings(const QAudioEncoderSettings&);
- void resetActualSettings();
-
-#if QT_CONFIG(gstreamer_encodingprofiles)
- GstEncodingProfile *createProfile();
-#endif
-
- void applySettings(GstElement *element);
-
-Q_SIGNALS:
- void settingsChanged();
-
-private:
-#if QT_CONFIG(gstreamer_encodingprofiles)
- QGstCodecsInfo m_codecs;
-#endif
-
- QAudioEncoderSettings m_actualAudioSettings;
- QAudioEncoderSettings m_audioSettings;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/gstreamer/camerabin/camerabincapturebufferformat.cpp b/src/plugins/gstreamer/camerabin/camerabincapturebufferformat.cpp
deleted file mode 100644
index 8b3e10546..000000000
--- a/src/plugins/gstreamer/camerabin/camerabincapturebufferformat.cpp
+++ /dev/null
@@ -1,76 +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 "camerabincapturebufferformat.h"
-#include "camerabinsession.h"
-
-QT_BEGIN_NAMESPACE
-
-CameraBinCaptureBufferFormat::CameraBinCaptureBufferFormat(CameraBinSession *session)
- :QCameraCaptureBufferFormatControl(session)
- , m_session(session)
- , m_format(QVideoFrame::Format_Jpeg)
-{
-}
-
-CameraBinCaptureBufferFormat::~CameraBinCaptureBufferFormat()
-{
-}
-
-QList<QVideoFrame::PixelFormat> CameraBinCaptureBufferFormat::supportedBufferFormats() const
-{
- //the exact YUV format is unknown with camerabin until the first capture is requested
- return QList<QVideoFrame::PixelFormat>()
- << QVideoFrame::Format_Jpeg;
-}
-
-QVideoFrame::PixelFormat CameraBinCaptureBufferFormat::bufferFormat() const
-{
- return m_format;
-}
-
-void CameraBinCaptureBufferFormat::setBufferFormat(QVideoFrame::PixelFormat format)
-{
- if (m_format != format) {
- m_format = format;
- emit bufferFormatChanged(format);
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabincapturebufferformat.h b/src/plugins/gstreamer/camerabin/camerabincapturebufferformat.h
deleted file mode 100644
index 7051913a7..000000000
--- a/src/plugins/gstreamer/camerabin/camerabincapturebufferformat.h
+++ /dev/null
@@ -1,74 +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 CAMERABINCAPTUREBUFFERFORMAT_H
-#define CAMERABINCAPTUREBUFFERFORMAT_H
-
-#include <qcamera.h>
-#include <qcameracapturebufferformatcontrol.h>
-
-#include <gst/gst.h>
-#include <glib.h>
-
-QT_BEGIN_NAMESPACE
-
-class CameraBinSession;
-
-QT_USE_NAMESPACE
-
-class CameraBinCaptureBufferFormat : public QCameraCaptureBufferFormatControl
-{
- Q_OBJECT
-public:
- CameraBinCaptureBufferFormat(CameraBinSession *session);
- virtual ~CameraBinCaptureBufferFormat();
-
- QList<QVideoFrame::PixelFormat> supportedBufferFormats() const override;
-
- QVideoFrame::PixelFormat bufferFormat() const override;
- void setBufferFormat(QVideoFrame::PixelFormat format) override;
-
-private:
- CameraBinSession *m_session;
- QVideoFrame::PixelFormat m_format;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/gstreamer/camerabin/camerabincapturedestination.cpp b/src/plugins/gstreamer/camerabin/camerabincapturedestination.cpp
deleted file mode 100644
index 90ae81c86..000000000
--- a/src/plugins/gstreamer/camerabin/camerabincapturedestination.cpp
+++ /dev/null
@@ -1,76 +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 "camerabincapturedestination.h"
-#include "camerabinsession.h"
-
-QT_BEGIN_NAMESPACE
-
-CameraBinCaptureDestination::CameraBinCaptureDestination(CameraBinSession *session)
- :QCameraCaptureDestinationControl(session)
- , m_session(session)
- , m_destination(QCameraImageCapture::CaptureToFile)
-{
-}
-
-CameraBinCaptureDestination::~CameraBinCaptureDestination()
-{
-}
-
-
-bool CameraBinCaptureDestination::isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const
-{
- //capture to buffer, file and both are supported.
- return destination & (QCameraImageCapture::CaptureToFile | QCameraImageCapture::CaptureToBuffer);
-}
-
-QCameraImageCapture::CaptureDestinations CameraBinCaptureDestination::captureDestination() const
-{
- return m_destination;
-}
-
-void CameraBinCaptureDestination::setCaptureDestination(QCameraImageCapture::CaptureDestinations destination)
-{
- if (m_destination != destination) {
- m_destination = destination;
- emit captureDestinationChanged(m_destination);
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabincapturedestination.h b/src/plugins/gstreamer/camerabin/camerabincapturedestination.h
deleted file mode 100644
index bc24a23d8..000000000
--- a/src/plugins/gstreamer/camerabin/camerabincapturedestination.h
+++ /dev/null
@@ -1,70 +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 CAMERABINCAPTUREDESTINATION_H
-#define CAMERABINCAPTUREDESTINATION_H
-
-#include <qcameraimagecapture.h>
-#include <qcameracapturedestinationcontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class CameraBinSession;
-
-QT_USE_NAMESPACE
-
-class CameraBinCaptureDestination : public QCameraCaptureDestinationControl
-{
- Q_OBJECT
-public:
- CameraBinCaptureDestination(CameraBinSession *session);
- virtual ~CameraBinCaptureDestination();
-
- bool isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const override;
- QCameraImageCapture::CaptureDestinations captureDestination() const override;
- void setCaptureDestination(QCameraImageCapture::CaptureDestinations destination) override;
-
-private:
- CameraBinSession *m_session;
- QCameraImageCapture::CaptureDestinations m_destination;
-};
-
-QT_END_NAMESPACE
-
-#endif // CAMERABINFLASHCONTROL_H
diff --git a/src/plugins/gstreamer/camerabin/camerabincontainer.cpp b/src/plugins/gstreamer/camerabin/camerabincontainer.cpp
deleted file mode 100644
index 0de9b25d8..000000000
--- a/src/plugins/gstreamer/camerabin/camerabincontainer.cpp
+++ /dev/null
@@ -1,152 +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 <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include "camerabincontainer.h"
-#include <private/qgstutils_p.h>
-
-#include <QtCore/qdebug.h>
-
-QT_BEGIN_NAMESPACE
-
-CameraBinContainer::CameraBinContainer(QObject *parent)
- :QMediaContainerControl(parent)
-#if QT_CONFIG(gstreamer_encodingprofiles)
- , m_supportedContainers(QGstCodecsInfo::Muxer)
-#endif
-{
-}
-
-QStringList CameraBinContainer::supportedContainers() const
-{
-#if QT_CONFIG(gstreamer_encodingprofiles)
- return m_supportedContainers.supportedCodecs();
-#else
- return QStringList();
-#endif
-}
-
-QString CameraBinContainer::containerDescription(const QString &formatMimeType) const
-{
-#if QT_CONFIG(gstreamer_encodingprofiles)
- return m_supportedContainers.codecDescription(formatMimeType);
-#else
- Q_UNUSED(formatMimeType);
- return QString();
-#endif
-}
-
-QString CameraBinContainer::containerFormat() const
-{
- return m_format;
-}
-
-void CameraBinContainer::setContainerFormat(const QString &format)
-{
-#if QT_CONFIG(gstreamer_encodingprofiles)
- if (m_format != format) {
- m_format = format;
- m_actualFormat = format;
- emit settingsChanged();
- }
-#endif
-}
-
-QString CameraBinContainer::actualContainerFormat() const
-{
- return m_actualFormat;
-}
-
-void CameraBinContainer::setActualContainerFormat(const QString &containerFormat)
-{
-#if QT_CONFIG(gstreamer_encodingprofiles)
- m_actualFormat = containerFormat;
-#endif
-}
-
-void CameraBinContainer::resetActualContainerFormat()
-{
- m_actualFormat = m_format;
-}
-
-#if QT_CONFIG(gstreamer_encodingprofiles)
-
-GstEncodingContainerProfile *CameraBinContainer::createProfile()
-{
- GstCaps *caps = nullptr;
-
- if (m_actualFormat.isEmpty()) {
- return 0;
- } else {
- QString format = m_actualFormat;
- const QStringList supportedFormats = m_supportedContainers.supportedCodecs();
-
- //if format is not in the list of supported gstreamer mime types,
- //try to find the mime type with matching extension
- if (!supportedFormats.contains(format)) {
- format.clear();
- QString extension = QGstUtils::fileExtensionForMimeType(m_actualFormat);
- for (const QString &formatCandidate : supportedFormats) {
- if (QGstUtils::fileExtensionForMimeType(formatCandidate) == extension) {
- format = formatCandidate;
- break;
- }
- }
- }
-
- if (format.isEmpty())
- return nullptr;
-
- caps = gst_caps_from_string(format.toLatin1());
- }
-
- GstEncodingContainerProfile *profile = (GstEncodingContainerProfile *)gst_encoding_container_profile_new(
- "camerabin2_profile",
- (gchar *)"custom camera profile",
- caps,
- NULL); //preset
-
- gst_caps_unref(caps);
-
- return profile;
-}
-
-#endif
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabincontainer.h b/src/plugins/gstreamer/camerabin/camerabincontainer.h
deleted file mode 100644
index 738e55e2a..000000000
--- a/src/plugins/gstreamer/camerabin/camerabincontainer.h
+++ /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$
-**
-****************************************************************************/
-
-
-#ifndef CAMERABINMEDIACONTAINERCONTROL_H
-#define CAMERABINMEDIACONTAINERCONTROL_H
-
-#include <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include <qmediacontainercontrol.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qset.h>
-
-#include <gst/gst.h>
-#include <gst/pbutils/pbutils.h>
-
-#if QT_CONFIG(gstreamer_encodingprofiles)
-#include <gst/pbutils/encoding-profile.h>
-#include <private/qgstcodecsinfo_p.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class CameraBinContainer : public QMediaContainerControl
-{
-Q_OBJECT
-public:
- CameraBinContainer(QObject *parent);
- virtual ~CameraBinContainer() {}
-
- QStringList supportedContainers() const override;
- QString containerDescription(const QString &formatMimeType) const override;
-
- QString containerFormat() const override;
- void setContainerFormat(const QString &format) override;
-
- QString actualContainerFormat() const;
- void setActualContainerFormat(const QString &containerFormat);
- void resetActualContainerFormat();
-
-#if QT_CONFIG(gstreamer_encodingprofiles)
- GstEncodingContainerProfile *createProfile();
-#endif
-
-Q_SIGNALS:
- void settingsChanged();
-
-private:
- QString m_format;
- QString m_actualFormat;
-
-#if QT_CONFIG(gstreamer_encodingprofiles)
- QGstCodecsInfo m_supportedContainers;
-#endif
-};
-
-QT_END_NAMESPACE
-
-#endif // CAMERABINMEDIACONTAINERCONTROL_H
diff --git a/src/plugins/gstreamer/camerabin/camerabincontrol.cpp b/src/plugins/gstreamer/camerabin/camerabincontrol.cpp
deleted file mode 100644
index 155887054..000000000
--- a/src/plugins/gstreamer/camerabin/camerabincontrol.cpp
+++ /dev/null
@@ -1,309 +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 <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include "camerabincontrol.h"
-#include "camerabincontainer.h"
-#include "camerabinaudioencoder.h"
-#include "camerabinvideoencoder.h"
-#include "camerabinimageencoder.h"
-#include "camerabinresourcepolicy.h"
-
-#include <QtCore/qdebug.h>
-#include <QtCore/qfile.h>
-#include <QtCore/qmetaobject.h>
-
-QT_BEGIN_NAMESPACE
-
-//#define CAMEABIN_DEBUG 1
-#define ENUM_NAME(c,e,v) (c::staticMetaObject.enumerator(c::staticMetaObject.indexOfEnumerator(e)).valueToKey((v)))
-
-CameraBinControl::CameraBinControl(CameraBinSession *session)
- :QCameraControl(session),
- m_session(session),
- m_state(QCamera::UnloadedState),
- m_reloadPending(false)
-{
- connect(m_session, SIGNAL(statusChanged(QCamera::Status)),
- this, SIGNAL(statusChanged(QCamera::Status)));
-
- connect(m_session, SIGNAL(viewfinderChanged()),
- SLOT(reloadLater()));
- connect(m_session, SIGNAL(readyChanged(bool)),
- SLOT(reloadLater()));
- connect(m_session, SIGNAL(error(int,QString)),
- SLOT(handleCameraError(int,QString)));
-
- m_resourcePolicy = new CamerabinResourcePolicy(this);
- connect(m_resourcePolicy, SIGNAL(resourcesGranted()),
- SLOT(handleResourcesGranted()));
- connect(m_resourcePolicy, SIGNAL(resourcesDenied()),
- SLOT(handleResourcesLost()));
- connect(m_resourcePolicy, SIGNAL(resourcesLost()),
- SLOT(handleResourcesLost()));
-
- connect(m_session, SIGNAL(busyChanged(bool)),
- SLOT(handleBusyChanged(bool)));
-}
-
-CameraBinControl::~CameraBinControl()
-{
-}
-
-QCamera::CaptureModes CameraBinControl::captureMode() const
-{
- return m_session->captureMode();
-}
-
-void CameraBinControl::setCaptureMode(QCamera::CaptureModes mode)
-{
- if (m_session->captureMode() != mode) {
- m_session->setCaptureMode(mode);
-
- if (m_state == QCamera::ActiveState) {
- m_resourcePolicy->setResourceSet(
- captureMode() == QCamera::CaptureStillImage ?
- CamerabinResourcePolicy::ImageCaptureResources :
- CamerabinResourcePolicy::VideoCaptureResources);
- }
- emit captureModeChanged(mode);
- }
-}
-
-bool CameraBinControl::isCaptureModeSupported(QCamera::CaptureModes mode) const
-{
- return mode == QCamera::CaptureStillImage || mode == QCamera::CaptureVideo;
-}
-
-void CameraBinControl::setState(QCamera::State state)
-{
-#ifdef CAMEABIN_DEBUG
- qDebug() << Q_FUNC_INFO << ENUM_NAME(QCamera, "State", state);
-#endif
- if (m_state != state) {
- m_state = state;
-
- //special case for stopping the camera while it's busy,
- //it should be delayed until the camera is idle
- if ((state == QCamera::LoadedState || state == QCamera::UnloadedState) &&
- m_session->status() == QCamera::ActiveStatus &&
- m_session->isBusy()) {
-#ifdef CAMEABIN_DEBUG
- qDebug() << Q_FUNC_INFO << "Camera is busy,"
- << state == QCamera::LoadedState ? "QCamera::stop()" : "QCamera::unload()"
- << "is delayed";
-#endif
- emit stateChanged(m_state);
- return;
- }
-
- CamerabinResourcePolicy::ResourceSet resourceSet = CamerabinResourcePolicy::NoResources;
- switch (state) {
- case QCamera::UnloadedState:
- resourceSet = CamerabinResourcePolicy::NoResources;
- break;
- case QCamera::LoadedState:
- resourceSet = CamerabinResourcePolicy::LoadedResources;
- break;
- case QCamera::ActiveState:
- resourceSet = captureMode() == QCamera::CaptureStillImage ?
- CamerabinResourcePolicy::ImageCaptureResources :
- CamerabinResourcePolicy::VideoCaptureResources;
- break;
- }
-
- m_resourcePolicy->setResourceSet(resourceSet);
-
- if (m_resourcePolicy->isResourcesGranted()) {
- //postpone changing to Active if the session is nor ready yet
- if (state == QCamera::ActiveState) {
- if (m_session->isReady()) {
- m_session->setState(state);
- } else {
-#ifdef CAMEABIN_DEBUG
- qDebug() << "Camera session is not ready yet, postpone activating";
-#endif
- }
- } else
- m_session->setState(state);
- }
-
- emit stateChanged(m_state);
- }
-}
-
-QCamera::State CameraBinControl::state() const
-{
- return m_state;
-}
-
-QCamera::Status CameraBinControl::status() const
-{
- return m_session->status();
-}
-
-void CameraBinControl::reloadLater()
-{
-#ifdef CAMEABIN_DEBUG
- qDebug() << "CameraBinControl: reload pipeline requested" << ENUM_NAME(QCamera, "State", m_state);
-#endif
- if (!m_reloadPending && m_state == QCamera::ActiveState) {
- m_reloadPending = true;
-
- if (!m_session->isBusy()) {
- m_session->setState(QCamera::LoadedState);
- QMetaObject::invokeMethod(this, "delayedReload", Qt::QueuedConnection);
- }
- }
-}
-
-void CameraBinControl::handleResourcesLost()
-{
-#ifdef CAMEABIN_DEBUG
- qDebug() << Q_FUNC_INFO << ENUM_NAME(QCamera, "State", m_state);
-#endif
- m_session->setState(QCamera::UnloadedState);
-}
-
-void CameraBinControl::handleResourcesGranted()
-{
-#ifdef CAMEABIN_DEBUG
- qDebug() << Q_FUNC_INFO << ENUM_NAME(QCamera, "State", m_state);
-#endif
-
- //camera will be started soon by delayedReload()
- if (m_reloadPending && m_state == QCamera::ActiveState)
- return;
-
- if (m_state == QCamera::ActiveState && m_session->isReady())
- m_session->setState(QCamera::ActiveState);
- else if (m_state == QCamera::LoadedState)
- m_session->setState(QCamera::LoadedState);
-}
-
-void CameraBinControl::handleBusyChanged(bool busy)
-{
- if (!busy && m_session->status() == QCamera::ActiveStatus) {
- if (m_state != QCamera::ActiveState) {
- //handle delayed stop()/unload() because of busy camera
-
- CamerabinResourcePolicy::ResourceSet resourceSet = CamerabinResourcePolicy::NoResources;
- switch (m_state) {
- case QCamera::UnloadedState:
- resourceSet = CamerabinResourcePolicy::NoResources;
- break;
- case QCamera::LoadedState:
- resourceSet = CamerabinResourcePolicy::LoadedResources;
- break;
- default:
- Q_ASSERT(false);
- break;
- }
-
- m_resourcePolicy->setResourceSet(resourceSet);
- m_session->setState(m_state);
- } else if (m_reloadPending) {
- //handle delayed reload because of busy camera
- m_session->setState(QCamera::LoadedState);
- QMetaObject::invokeMethod(this, "delayedReload", Qt::QueuedConnection);
- }
- }
-}
-
-void CameraBinControl::handleCameraError(int errorCode, const QString &errorString)
-{
- emit error(errorCode, errorString);
- setState(QCamera::UnloadedState);
-}
-
-void CameraBinControl::delayedReload()
-{
-#ifdef CAMEABIN_DEBUG
- qDebug() << "CameraBinControl: reload pipeline";
-#endif
- if (m_reloadPending) {
- m_reloadPending = false;
- if (m_state == QCamera::ActiveState &&
- m_session->isReady() &&
- m_resourcePolicy->isResourcesGranted()) {
- m_session->setState(QCamera::ActiveState);
- }
- }
-}
-
-bool CameraBinControl::canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const
-{
- Q_UNUSED(status);
-
- switch (changeType) {
- case QCameraControl::Viewfinder:
- return true;
- case QCameraControl::CaptureMode:
- case QCameraControl::ImageEncodingSettings:
- case QCameraControl::VideoEncodingSettings:
- case QCameraControl::ViewfinderSettings:
- default:
- return status != QCamera::ActiveStatus;
- }
-}
-
-#define VIEWFINDER_COLORSPACE_CONVERSION 0x00000004
-
-bool CameraBinControl::viewfinderColorSpaceConversion() const
-{
- gint flags = 0;
- g_object_get(G_OBJECT(m_session->cameraBin()), "flags", &flags, NULL);
-
- return flags & VIEWFINDER_COLORSPACE_CONVERSION;
-}
-
-void CameraBinControl::setViewfinderColorSpaceConversion(bool enabled)
-{
- gint flags = 0;
- g_object_get(G_OBJECT(m_session->cameraBin()), "flags", &flags, NULL);
-
- if (enabled)
- flags |= VIEWFINDER_COLORSPACE_CONVERSION;
- else
- flags &= ~VIEWFINDER_COLORSPACE_CONVERSION;
-
- g_object_set(G_OBJECT(m_session->cameraBin()), "flags", flags, NULL);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabincontrol.h b/src/plugins/gstreamer/camerabin/camerabincontrol.h
deleted file mode 100644
index 56c02dad4..000000000
--- a/src/plugins/gstreamer/camerabin/camerabincontrol.h
+++ /dev/null
@@ -1,101 +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 CAMERABINCONTROL_H
-#define CAMERABINCONTROL_H
-
-#include <QHash>
-#include <qcameracontrol.h>
-#include "camerabinsession.h"
-
-QT_BEGIN_NAMESPACE
-
-class CamerabinResourcePolicy;
-
-class CameraBinControl : public QCameraControl
-{
- Q_OBJECT
- Q_PROPERTY(bool viewfinderColorSpaceConversion READ viewfinderColorSpaceConversion WRITE setViewfinderColorSpaceConversion)
-public:
- CameraBinControl( CameraBinSession *session );
- virtual ~CameraBinControl();
-
- bool isValid() const { return true; }
-
- QCamera::State state() const override;
- void setState(QCamera::State state) override;
-
- QCamera::Status status() const override;
-
- QCamera::CaptureModes captureMode() const override;
- void setCaptureMode(QCamera::CaptureModes mode) override;
-
- bool isCaptureModeSupported(QCamera::CaptureModes mode) const override;
- bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const override;
- bool viewfinderColorSpaceConversion() const;
-
- CamerabinResourcePolicy *resourcePolicy() { return m_resourcePolicy; }
-
-public slots:
- void reloadLater();
- void setViewfinderColorSpaceConversion(bool enabled);
-
-private slots:
- void delayedReload();
-
- void handleResourcesGranted();
- void handleResourcesLost();
-
- void handleBusyChanged(bool);
- void handleCameraError(int error, const QString &errorString);
-
-private:
- void updateSupportedResolutions(const QString &device);
-
- CameraBinSession *m_session;
- QCamera::State m_state;
- CamerabinResourcePolicy *m_resourcePolicy;
-
- bool m_reloadPending;
-};
-
-QT_END_NAMESPACE
-
-#endif // CAMERABINCONTROL_H
diff --git a/src/plugins/gstreamer/camerabin/camerabinexposure.cpp b/src/plugins/gstreamer/camerabin/camerabinexposure.cpp
deleted file mode 100644
index 35d3e7c8f..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinexposure.cpp
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "camerabinexposure.h"
-#include "camerabinsession.h"
-#include <gst/interfaces/photography.h>
-
-#include <QDebug>
-
-#if !GST_CHECK_VERSION(1,0,0)
-typedef GstSceneMode GstPhotographySceneMode;
-#endif
-
-QT_BEGIN_NAMESPACE
-
-CameraBinExposure::CameraBinExposure(CameraBinSession *session)
- :QCameraExposureControl(session),
- m_session(session)
-{
-}
-
-CameraBinExposure::~CameraBinExposure()
-{
-}
-
-bool CameraBinExposure::isParameterSupported(ExposureParameter parameter) const
-{
- switch (parameter) {
- case QCameraExposureControl::ExposureCompensation:
- case QCameraExposureControl::ISO:
- case QCameraExposureControl::Aperture:
- case QCameraExposureControl::ShutterSpeed:
- return true;
- default:
- return false;
- }
-}
-
-QVariantList CameraBinExposure::supportedParameterRange(ExposureParameter parameter,
- bool *continuous) const
-{
- if (continuous)
- *continuous = false;
-
- QVariantList res;
- switch (parameter) {
- case QCameraExposureControl::ExposureCompensation:
- if (continuous)
- *continuous = true;
- res << -2.0 << 2.0;
- break;
- case QCameraExposureControl::ISO:
- res << 100 << 200 << 400;
- break;
- case QCameraExposureControl::Aperture:
- res << 2.8;
- break;
- default:
- break;
- }
-
- return res;
-}
-
-QVariant CameraBinExposure::requestedValue(ExposureParameter parameter) const
-{
- return m_requestedValues.value(parameter);
-}
-
-QVariant CameraBinExposure::actualValue(ExposureParameter parameter) const
-{
- switch (parameter) {
- case QCameraExposureControl::ExposureCompensation:
- {
- gfloat ev;
- gst_photography_get_ev_compensation(m_session->photography(), &ev);
- return QVariant(ev);
- }
- case QCameraExposureControl::ISO:
- {
- guint isoSpeed = 0;
- gst_photography_get_iso_speed(m_session->photography(), &isoSpeed);
- return QVariant(isoSpeed);
- }
- case QCameraExposureControl::Aperture:
- return QVariant(2.8);
- case QCameraExposureControl::ShutterSpeed:
- {
- guint32 shutterSpeed = 0;
- gst_photography_get_exposure(m_session->photography(), &shutterSpeed);
-
- return QVariant(shutterSpeed/1000000.0);
- }
- case QCameraExposureControl::ExposureMode:
- {
- GstPhotographySceneMode sceneMode;
- gst_photography_get_scene_mode(m_session->photography(), &sceneMode);
-
- switch (sceneMode) {
- case GST_PHOTOGRAPHY_SCENE_MODE_PORTRAIT:
- return QVariant::fromValue(QCameraExposure::ExposurePortrait);
- case GST_PHOTOGRAPHY_SCENE_MODE_SPORT:
- return QVariant::fromValue(QCameraExposure::ExposureSports);
- case GST_PHOTOGRAPHY_SCENE_MODE_NIGHT:
- return QVariant::fromValue(QCameraExposure::ExposureNight);
- case GST_PHOTOGRAPHY_SCENE_MODE_MANUAL:
- return QVariant::fromValue(QCameraExposure::ExposureManual);
- case GST_PHOTOGRAPHY_SCENE_MODE_LANDSCAPE:
- return QVariant::fromValue(QCameraExposure::ExposureLandscape);
-#if GST_CHECK_VERSION(1, 2, 0)
- case GST_PHOTOGRAPHY_SCENE_MODE_SNOW:
- return QVariant::fromValue(QCameraExposure::ExposureSnow);
- case GST_PHOTOGRAPHY_SCENE_MODE_BEACH:
- return QVariant::fromValue(QCameraExposure::ExposureBeach);
- case GST_PHOTOGRAPHY_SCENE_MODE_ACTION:
- return QVariant::fromValue(QCameraExposure::ExposureAction);
- case GST_PHOTOGRAPHY_SCENE_MODE_NIGHT_PORTRAIT:
- return QVariant::fromValue(QCameraExposure::ExposureNightPortrait);
- case GST_PHOTOGRAPHY_SCENE_MODE_THEATRE:
- return QVariant::fromValue(QCameraExposure::ExposureTheatre);
- case GST_PHOTOGRAPHY_SCENE_MODE_SUNSET:
- return QVariant::fromValue(QCameraExposure::ExposureSunset);
- case GST_PHOTOGRAPHY_SCENE_MODE_STEADY_PHOTO:
- return QVariant::fromValue(QCameraExposure::ExposureSteadyPhoto);
- case GST_PHOTOGRAPHY_SCENE_MODE_FIREWORKS:
- return QVariant::fromValue(QCameraExposure::ExposureFireworks);
- case GST_PHOTOGRAPHY_SCENE_MODE_PARTY:
- return QVariant::fromValue(QCameraExposure::ExposureParty);
- case GST_PHOTOGRAPHY_SCENE_MODE_CANDLELIGHT:
- return QVariant::fromValue(QCameraExposure::ExposureCandlelight);
- case GST_PHOTOGRAPHY_SCENE_MODE_BARCODE:
- return QVariant::fromValue(QCameraExposure::ExposureBarcode);
-#endif
- //no direct mapping available so mapping to auto mode
- case GST_PHOTOGRAPHY_SCENE_MODE_CLOSEUP:
- case GST_PHOTOGRAPHY_SCENE_MODE_AUTO:
- default:
- return QVariant::fromValue(QCameraExposure::ExposureAuto);
- }
- }
- case QCameraExposureControl::MeteringMode:
- return QCameraExposure::MeteringMatrix;
- default:
- return QVariant();
- }
-}
-
-bool CameraBinExposure::setValue(ExposureParameter parameter, const QVariant& value)
-{
- QVariant oldValue = actualValue(parameter);
-
- switch (parameter) {
- case QCameraExposureControl::ExposureCompensation:
- gst_photography_set_ev_compensation(m_session->photography(), value.toReal());
- break;
- case QCameraExposureControl::ISO:
- gst_photography_set_iso_speed(m_session->photography(), value.toInt());
- break;
- case QCameraExposureControl::Aperture:
- gst_photography_set_aperture(m_session->photography(), guint(value.toReal()*1000000));
- break;
- case QCameraExposureControl::ShutterSpeed:
- gst_photography_set_exposure(m_session->photography(), guint(value.toReal()*1000000));
- break;
- case QCameraExposureControl::ExposureMode:
- {
- QCameraExposure::ExposureMode mode = value.value<QCameraExposure::ExposureMode>();
- GstPhotographySceneMode sceneMode;
-
- gst_photography_get_scene_mode(m_session->photography(), &sceneMode);
-
- switch (mode) {
- case QCameraExposure::ExposureManual:
- sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_MANUAL;
- break;
- case QCameraExposure::ExposurePortrait:
- sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_PORTRAIT;
- break;
- case QCameraExposure::ExposureSports:
- sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SPORT;
- break;
- case QCameraExposure::ExposureNight:
- sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_NIGHT;
- break;
- case QCameraExposure::ExposureAuto:
- sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_AUTO;
- break;
- case QCameraExposure::ExposureLandscape:
- sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_LANDSCAPE;
- break;
-#if GST_CHECK_VERSION(1, 2, 0)
- case QCameraExposure::ExposureSnow:
- sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SNOW;
- break;
- case QCameraExposure::ExposureBeach:
- sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_BEACH;
- break;
- case QCameraExposure::ExposureAction:
- sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_ACTION;
- break;
- case QCameraExposure::ExposureNightPortrait:
- sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_NIGHT_PORTRAIT;
- break;
- case QCameraExposure::ExposureTheatre:
- sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_THEATRE;
- break;
- case QCameraExposure::ExposureSunset:
- sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SUNSET;
- break;
- case QCameraExposure::ExposureSteadyPhoto:
- sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_STEADY_PHOTO;
- break;
- case QCameraExposure::ExposureFireworks:
- sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_FIREWORKS;
- break;
- case QCameraExposure::ExposureParty:
- sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_PARTY;
- break;
- case QCameraExposure::ExposureCandlelight:
- sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_CANDLELIGHT;
- break;
- case QCameraExposure::ExposureBarcode:
- sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_BARCODE;
- break;
-#endif
- default:
- break;
- }
-
- gst_photography_set_scene_mode(m_session->photography(), sceneMode);
- break;
- }
- default:
- return false;
- }
-
- if (!qFuzzyCompare(m_requestedValues.value(parameter).toReal(), value.toReal())) {
- m_requestedValues[parameter] = value;
- emit requestedValueChanged(parameter);
- }
-
- QVariant newValue = actualValue(parameter);
- if (!qFuzzyCompare(oldValue.toReal(), newValue.toReal()))
- emit actualValueChanged(parameter);
-
- return true;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinexposure.h b/src/plugins/gstreamer/camerabin/camerabinexposure.h
deleted file mode 100644
index db16bed24..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinexposure.h
+++ /dev/null
@@ -1,75 +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 CAMERABINEXPOSURECONTROL_H
-#define CAMERABINEXPOSURECONTROL_H
-
-#include <qcamera.h>
-#include <qcameraexposurecontrol.h>
-
-#include <gst/gst.h>
-#include <glib.h>
-
-QT_BEGIN_NAMESPACE
-
-class CameraBinSession;
-
-class CameraBinExposure : public QCameraExposureControl
-{
- Q_OBJECT
-
-public:
- CameraBinExposure(CameraBinSession *session);
- virtual ~CameraBinExposure();
-
- bool isParameterSupported(ExposureParameter parameter) const override;
- QVariantList supportedParameterRange(ExposureParameter parameter, bool *continuous) const override;
-
- QVariant requestedValue(ExposureParameter parameter) const override;
- QVariant actualValue(ExposureParameter parameter) const override;
- bool setValue(ExposureParameter parameter, const QVariant &value) override;
-
-private:
- CameraBinSession *m_session;
- QHash<ExposureParameter, QVariant> m_requestedValues;
-};
-
-QT_END_NAMESPACE
-
-#endif // CAMERABINEXPOSURECONTROL_H
diff --git a/src/plugins/gstreamer/camerabin/camerabinflash.cpp b/src/plugins/gstreamer/camerabin/camerabinflash.cpp
deleted file mode 100644
index 2bf7a2776..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinflash.cpp
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "camerabinflash.h"
-#include "camerabinsession.h"
-#include <gst/interfaces/photography.h>
-
-#include <QDebug>
-
-#if !GST_CHECK_VERSION(1,0,0)
-typedef GstFlashMode GstPhotographyFlashMode;
-#endif
-
-QT_BEGIN_NAMESPACE
-
-CameraBinFlash::CameraBinFlash(CameraBinSession *session)
- :QCameraFlashControl(session),
- m_session(session)
-{
-}
-
-CameraBinFlash::~CameraBinFlash()
-{
-}
-
-QCameraExposure::FlashModes CameraBinFlash::flashMode() const
-{
- GstPhotographyFlashMode flashMode;
- gst_photography_get_flash_mode(m_session->photography(), &flashMode);
-
- QCameraExposure::FlashModes modes;
- switch (flashMode) {
- case GST_PHOTOGRAPHY_FLASH_MODE_AUTO: modes |= QCameraExposure::FlashAuto; break;
- case GST_PHOTOGRAPHY_FLASH_MODE_OFF: modes |= QCameraExposure::FlashOff; break;
- case GST_PHOTOGRAPHY_FLASH_MODE_ON: modes |= QCameraExposure::FlashOn; break;
- case GST_PHOTOGRAPHY_FLASH_MODE_FILL_IN: modes |= QCameraExposure::FlashFill; break;
- case GST_PHOTOGRAPHY_FLASH_MODE_RED_EYE: modes |= QCameraExposure::FlashRedEyeReduction; break;
- default:
- modes |= QCameraExposure::FlashAuto;
- break;
- }
- return modes;
-}
-
-void CameraBinFlash::setFlashMode(QCameraExposure::FlashModes mode)
-{
- GstPhotographyFlashMode flashMode;
- gst_photography_get_flash_mode(m_session->photography(), &flashMode);
-
- if (mode.testFlag(QCameraExposure::FlashAuto)) flashMode = GST_PHOTOGRAPHY_FLASH_MODE_AUTO;
- else if (mode.testFlag(QCameraExposure::FlashOff)) flashMode = GST_PHOTOGRAPHY_FLASH_MODE_OFF;
- else if (mode.testFlag(QCameraExposure::FlashOn)) flashMode = GST_PHOTOGRAPHY_FLASH_MODE_ON;
- else if (mode.testFlag(QCameraExposure::FlashFill)) flashMode = GST_PHOTOGRAPHY_FLASH_MODE_FILL_IN;
- else if (mode.testFlag(QCameraExposure::FlashRedEyeReduction)) flashMode = GST_PHOTOGRAPHY_FLASH_MODE_RED_EYE;
-
- gst_photography_set_flash_mode(m_session->photography(), flashMode);
-}
-
-bool CameraBinFlash::isFlashModeSupported(QCameraExposure::FlashModes mode) const
-{
- return mode == QCameraExposure::FlashOff ||
- mode == QCameraExposure::FlashOn ||
- mode == QCameraExposure::FlashAuto ||
- mode == QCameraExposure::FlashRedEyeReduction ||
- mode == QCameraExposure::FlashFill;
-}
-
-bool CameraBinFlash::isFlashReady() const
-{
- return true;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinflash.h b/src/plugins/gstreamer/camerabin/camerabinflash.h
deleted file mode 100644
index 0efd100a7..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinflash.h
+++ /dev/null
@@ -1,73 +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 CAMERABINFLASHCONTROL_H
-#define CAMERABINFLASHCONTROL_H
-
-#include <qcamera.h>
-#include <qcameraflashcontrol.h>
-
-#include <gst/gst.h>
-#include <glib.h>
-
-QT_BEGIN_NAMESPACE
-
-class CameraBinSession;
-
-class CameraBinFlash : public QCameraFlashControl
-{
- Q_OBJECT
-public:
- CameraBinFlash(CameraBinSession *session);
- virtual ~CameraBinFlash();
-
- QCameraExposure::FlashModes flashMode() const override;
- void setFlashMode(QCameraExposure::FlashModes mode) override;
- bool isFlashModeSupported(QCameraExposure::FlashModes mode) const override;
-
- bool isFlashReady() const override;
-
-private:
- CameraBinSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif // CAMERABINFLASHCONTROL_H
-
diff --git a/src/plugins/gstreamer/camerabin/camerabinfocus.cpp b/src/plugins/gstreamer/camerabin/camerabinfocus.cpp
deleted file mode 100644
index 33ac7e3d7..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinfocus.cpp
+++ /dev/null
@@ -1,540 +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 "camerabinfocus.h"
-#include "camerabinsession.h"
-
-#include <gst/interfaces/photography.h>
-
-#include <QDebug>
-#include <QtCore/qcoreevent.h>
-#include <QtCore/qmetaobject.h>
-
-#include <private/qgstutils_p.h>
-
-#if !GST_CHECK_VERSION(1,0,0)
-typedef GstFocusMode GstPhotographyFocusMode;
-#endif
-
-//#define CAMERABIN_DEBUG 1
-
-QT_BEGIN_NAMESPACE
-
-CameraBinFocus::CameraBinFocus(CameraBinSession *session)
- :QCameraFocusControl(session),
-#if GST_CHECK_VERSION(1,0,0)
- QGstreamerBufferProbe(ProbeBuffers),
-#endif
- m_session(session),
- m_cameraStatus(QCamera::UnloadedStatus),
- m_focusMode(QCameraFocus::AutoFocus),
- m_focusPointMode(QCameraFocus::FocusPointAuto),
- m_focusStatus(QCamera::Unlocked),
- m_focusZoneStatus(QCameraFocusZone::Selected),
- m_focusPoint(0.5, 0.5),
- m_focusRect(0, 0, 0.3, 0.3)
-{
- m_focusRect.moveCenter(m_focusPoint);
-
- gst_photography_set_focus_mode(m_session->photography(), GST_PHOTOGRAPHY_FOCUS_MODE_AUTO);
-
- connect(m_session, SIGNAL(statusChanged(QCamera::Status)),
- this, SLOT(_q_handleCameraStatusChange(QCamera::Status)));
-}
-
-CameraBinFocus::~CameraBinFocus()
-{
-}
-
-QCameraFocus::FocusModes CameraBinFocus::focusMode() const
-{
- return m_focusMode;
-}
-
-void CameraBinFocus::setFocusMode(QCameraFocus::FocusModes mode)
-{
- GstPhotographyFocusMode photographyMode;
-
- switch (mode) {
- case QCameraFocus::AutoFocus:
- photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_AUTO;
- break;
- case QCameraFocus::HyperfocalFocus:
- photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_HYPERFOCAL;
- break;
- case QCameraFocus::InfinityFocus:
- photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_INFINITY;
- break;
- case QCameraFocus::ContinuousFocus:
- photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_CONTINUOUS_NORMAL;
- break;
- case QCameraFocus::MacroFocus:
- photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_MACRO;
- break;
- default:
- if (mode & QCameraFocus::AutoFocus) {
- photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_AUTO;
- break;
- } else {
- return;
- }
- }
-
- if (gst_photography_set_focus_mode(m_session->photography(), photographyMode))
- m_focusMode = mode;
-}
-
-bool CameraBinFocus::isFocusModeSupported(QCameraFocus::FocusModes mode) const
-{
- switch (mode) {
- case QCameraFocus::AutoFocus:
- case QCameraFocus::HyperfocalFocus:
- case QCameraFocus::InfinityFocus:
- case QCameraFocus::ContinuousFocus:
- case QCameraFocus::MacroFocus:
- return true;
- default:
- return mode & QCameraFocus::AutoFocus;
- }
-}
-
-QCameraFocus::FocusPointMode CameraBinFocus::focusPointMode() const
-{
- return m_focusPointMode;
-}
-
-void CameraBinFocus::setFocusPointMode(QCameraFocus::FocusPointMode mode)
-{
- GstElement *source = m_session->cameraSource();
-
- if (m_focusPointMode == mode || !source)
- return;
-
-#if GST_CHECK_VERSION(1,0,0)
- if (m_focusPointMode == QCameraFocus::FocusPointFaceDetection) {
- g_object_set (G_OBJECT(source), "detect-faces", FALSE, NULL);
-
- if (GstPad *pad = gst_element_get_static_pad(source, "vfsrc")) {
- removeProbeFromPad(pad);
- gst_object_unref(GST_OBJECT(pad));
- }
-
- m_faceResetTimer.stop();
- m_faceFocusRects.clear();
-
- QMutexLocker locker(&m_mutex);
- m_faces.clear();
- }
-#endif
-
- if (m_focusPointMode != QCameraFocus::FocusPointAuto)
- resetFocusPoint();
-
- switch (mode) {
- case QCameraFocus::FocusPointAuto:
- case QCameraFocus::FocusPointCustom:
- break;
-#if GST_CHECK_VERSION(1,0,0)
- case QCameraFocus::FocusPointFaceDetection:
- if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "detect-faces")) {
- if (GstPad *pad = gst_element_get_static_pad(source, "vfsrc")) {
- addProbeToPad(pad);
- g_object_set (G_OBJECT(source), "detect-faces", TRUE, NULL);
- break;
- }
- }
- return;
-#endif
- default:
- return;
- }
-
- m_focusPointMode = mode;
- emit focusPointModeChanged(m_focusPointMode);
- emit focusZonesChanged();
-}
-
-bool CameraBinFocus::isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const
-{
- switch (mode) {
- case QCameraFocus::FocusPointAuto:
- case QCameraFocus::FocusPointCustom:
- return true;
-#if GST_CHECK_VERSION(1,0,0)
- case QCameraFocus::FocusPointFaceDetection:
- if (GstElement *source = m_session->cameraSource())
- return g_object_class_find_property(G_OBJECT_GET_CLASS(source), "detect-faces");
- return false;
-#endif
- default:
- return false;
- }
-}
-
-QPointF CameraBinFocus::customFocusPoint() const
-{
- return m_focusPoint;
-}
-
-void CameraBinFocus::setCustomFocusPoint(const QPointF &point)
-{
- if (m_focusPoint != point) {
- m_focusPoint = point;
-
- // Bound the focus point so the focus rect remains entirely within the unit square.
- m_focusPoint.setX(qBound(m_focusRect.width() / 2, m_focusPoint.x(), 1 - m_focusRect.width() / 2));
- m_focusPoint.setY(qBound(m_focusRect.height() / 2, m_focusPoint.y(), 1 - m_focusRect.height() / 2));
-
- if (m_focusPointMode == QCameraFocus::FocusPointCustom) {
- const QRectF focusRect = m_focusRect;
- m_focusRect.moveCenter(m_focusPoint);
-
- updateRegionOfInterest(m_focusRect);
-
- if (focusRect != m_focusRect) {
- emit focusZonesChanged();
- }
- }
-
- emit customFocusPointChanged(m_focusPoint);
- }
-}
-
-QCameraFocusZoneList CameraBinFocus::focusZones() const
-{
- QCameraFocusZoneList zones;
-
- if (m_focusPointMode != QCameraFocus::FocusPointFaceDetection) {
- zones.append(QCameraFocusZone(m_focusRect, m_focusZoneStatus));
-#if GST_CHECK_VERSION(1,0,0)
- } else for (const QRect &face : qAsConst(m_faceFocusRects)) {
- const QRectF normalizedRect(
- face.x() / qreal(m_viewfinderResolution.width()),
- face.y() / qreal(m_viewfinderResolution.height()),
- face.width() / qreal(m_viewfinderResolution.width()),
- face.height() / qreal(m_viewfinderResolution.height()));
- zones.append(QCameraFocusZone(normalizedRect, m_focusZoneStatus));
-#endif
- }
- return zones;
-}
-
-void CameraBinFocus::handleFocusMessage(GstMessage *gm)
-{
- //it's a sync message, so it's called from non main thread
- const GstStructure *structure = gst_message_get_structure(gm);
- if (gst_structure_has_name(structure, GST_PHOTOGRAPHY_AUTOFOCUS_DONE)) {
- gint status = GST_PHOTOGRAPHY_FOCUS_STATUS_NONE;
- gst_structure_get_int (structure, "status", &status);
- QCamera::LockStatus focusStatus = m_focusStatus;
- QCamera::LockChangeReason reason = QCamera::UserRequest;
-
- switch (status) {
- case GST_PHOTOGRAPHY_FOCUS_STATUS_FAIL:
- focusStatus = QCamera::Unlocked;
- reason = QCamera::LockFailed;
- break;
- case GST_PHOTOGRAPHY_FOCUS_STATUS_SUCCESS:
- focusStatus = QCamera::Locked;
- break;
- case GST_PHOTOGRAPHY_FOCUS_STATUS_NONE:
- break;
- case GST_PHOTOGRAPHY_FOCUS_STATUS_RUNNING:
- focusStatus = QCamera::Searching;
- break;
- default:
- break;
- }
-
- static int signalIndex = metaObject()->indexOfSlot(
- "_q_setFocusStatus(QCamera::LockStatus,QCamera::LockChangeReason)");
- metaObject()->method(signalIndex).invoke(this,
- Qt::QueuedConnection,
- Q_ARG(QCamera::LockStatus,focusStatus),
- Q_ARG(QCamera::LockChangeReason,reason));
- }
-}
-
-void CameraBinFocus::_q_setFocusStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason)
-{
-#ifdef CAMERABIN_DEBUG
- qDebug() << Q_FUNC_INFO << "Current:"
- << m_focusStatus
- << "New:"
- << status << reason;
-#endif
-
- if (m_focusStatus != status) {
- m_focusStatus = status;
-
- QCameraFocusZone::FocusZoneStatus zonesStatus =
- m_focusStatus == QCamera::Locked ?
- QCameraFocusZone::Focused : QCameraFocusZone::Selected;
-
- if (m_focusZoneStatus != zonesStatus) {
- m_focusZoneStatus = zonesStatus;
- emit focusZonesChanged();
- }
-
-#if GST_CHECK_VERSION(1,0,0)
- if (m_focusPointMode == QCameraFocus::FocusPointFaceDetection
- && m_focusStatus == QCamera::Unlocked) {
- _q_updateFaces();
- }
-#endif
-
- emit _q_focusStatusChanged(m_focusStatus, reason);
- }
-}
-
-void CameraBinFocus::_q_handleCameraStatusChange(QCamera::Status status)
-{
- m_cameraStatus = status;
- if (status == QCamera::ActiveStatus) {
- if (GstPad *pad = gst_element_get_static_pad(m_session->cameraSource(), "vfsrc")) {
- if (GstCaps *caps = qt_gst_pad_get_current_caps(pad)) {
- if (GstStructure *structure = gst_caps_get_structure(caps, 0)) {
- int width = 0;
- int height = 0;
- gst_structure_get_int(structure, "width", &width);
- gst_structure_get_int(structure, "height", &height);
- setViewfinderResolution(QSize(width, height));
- }
- gst_caps_unref(caps);
- }
- gst_object_unref(GST_OBJECT(pad));
- }
- if (m_focusPointMode == QCameraFocus::FocusPointCustom) {
- updateRegionOfInterest(m_focusRect);
- }
- } else {
- _q_setFocusStatus(QCamera::Unlocked, QCamera::LockLost);
-
- resetFocusPoint();
- }
-}
-
-void CameraBinFocus::_q_startFocusing()
-{
- _q_setFocusStatus(QCamera::Searching, QCamera::UserRequest);
- gst_photography_set_autofocus(m_session->photography(), TRUE);
-}
-
-void CameraBinFocus::_q_stopFocusing()
-{
- gst_photography_set_autofocus(m_session->photography(), FALSE);
- _q_setFocusStatus(QCamera::Unlocked, QCamera::UserRequest);
-}
-
-void CameraBinFocus::setViewfinderResolution(const QSize &resolution)
-{
- if (resolution != m_viewfinderResolution) {
- m_viewfinderResolution = resolution;
- if (!resolution.isEmpty()) {
- const QPointF center = m_focusRect.center();
- m_focusRect.setWidth(m_focusRect.height() * resolution.height() / resolution.width());
- m_focusRect.moveCenter(center);
- }
- }
-}
-
-void CameraBinFocus::resetFocusPoint()
-{
- const QRectF focusRect = m_focusRect;
- m_focusPoint = QPointF(0.5, 0.5);
- m_focusRect.moveCenter(m_focusPoint);
-
- updateRegionOfInterest(QList<QRect>());
-
- if (focusRect != m_focusRect) {
- emit customFocusPointChanged(m_focusPoint);
- emit focusZonesChanged();
- }
-}
-
-static void appendRegion(GValue *regions, int priority, const QRect &rectangle)
-{
- GstStructure *region = gst_structure_new(
- "region",
- "region-x" , G_TYPE_UINT , rectangle.x(),
- "region-y" , G_TYPE_UINT, rectangle.y(),
- "region-w" , G_TYPE_UINT , rectangle.width(),
- "region-h" , G_TYPE_UINT, rectangle.height(),
- "region-priority" , G_TYPE_UINT, priority,
- NULL);
-
- GValue regionValue = G_VALUE_INIT;
- g_value_init(&regionValue, GST_TYPE_STRUCTURE);
- gst_value_set_structure(&regionValue, region);
- gst_structure_free(region);
-
- gst_value_list_append_value(regions, &regionValue);
- g_value_unset(&regionValue);
-}
-
-void CameraBinFocus::updateRegionOfInterest(const QRectF &rectangle)
-{
- updateRegionOfInterest(QList<QRect>() << QRect(
- rectangle.x() * m_viewfinderResolution.width(),
- rectangle.y() * m_viewfinderResolution.height(),
- rectangle.width() * m_viewfinderResolution.width(),
- rectangle.height() * m_viewfinderResolution.height()));
-}
-
-void CameraBinFocus::updateRegionOfInterest(const QList<QRect> &rectangles)
-{
- if (m_cameraStatus != QCamera::ActiveStatus)
- return;
-
- GstElement * const cameraSource = m_session->cameraSource();
- if (!cameraSource)
- return;
-
- GValue regions = G_VALUE_INIT;
- g_value_init(&regions, GST_TYPE_LIST);
-
- if (rectangles.isEmpty()) {
- appendRegion(&regions, 0, QRect(0, 0, 0, 0));
- } else {
- // Add padding around small face rectangles so the auto focus has a reasonable amount
- // of image to work with.
- const int minimumDimension = qMin(
- m_viewfinderResolution.width(), m_viewfinderResolution.height()) * 0.3;
- const QRect viewfinderRectangle(QPoint(0, 0), m_viewfinderResolution);
-
- for (const QRect &rectangle : rectangles) {
- QRect paddedRectangle(
- 0,
- 0,
- qMax(rectangle.width(), minimumDimension),
- qMax(rectangle.height(), minimumDimension));
- paddedRectangle.moveCenter(rectangle.center());
-
- appendRegion(&regions, 1, viewfinderRectangle.intersected(paddedRectangle));
- }
- }
-
- GstStructure *regionsOfInterest = gst_structure_new(
- "regions-of-interest",
- "frame-width" , G_TYPE_UINT , m_viewfinderResolution.width(),
- "frame-height" , G_TYPE_UINT, m_viewfinderResolution.height(),
- NULL);
- gst_structure_set_value(regionsOfInterest, "regions", &regions);
- g_value_unset(&regions);
-
- GstEvent *event = gst_event_new_custom(GST_EVENT_CUSTOM_UPSTREAM, regionsOfInterest);
- gst_element_send_event(cameraSource, event);
-}
-
-#if GST_CHECK_VERSION(1,0,0)
-
-void CameraBinFocus::_q_updateFaces()
-{
- if (m_focusPointMode != QCameraFocus::FocusPointFaceDetection
- || m_focusStatus != QCamera::Unlocked) {
- return;
- }
-
- QList<QRect> faces;
-
- {
- QMutexLocker locker(&m_mutex);
- faces = m_faces;
- }
-
- if (!faces.isEmpty()) {
- m_faceResetTimer.stop();
- m_faceFocusRects = faces;
- updateRegionOfInterest(m_faceFocusRects);
- emit focusZonesChanged();
- } else {
- m_faceResetTimer.start(500, this);
- }
-}
-
-void CameraBinFocus::timerEvent(QTimerEvent *event)
-{
- if (event->timerId() == m_faceResetTimer.timerId()) {
- m_faceResetTimer.stop();
-
- if (m_focusStatus == QCamera::Unlocked) {
- m_faceFocusRects.clear();
- updateRegionOfInterest(m_faceFocusRects);
- emit focusZonesChanged();
- }
- } else {
- QCameraFocusControl::timerEvent(event);
- }
-}
-
-bool CameraBinFocus::probeBuffer(GstBuffer *buffer)
-{
- QList<QRect> faces;
-
-#if GST_CHECK_VERSION(1,1,3)
- gpointer state = NULL;
- const GstMetaInfo *info = GST_VIDEO_REGION_OF_INTEREST_META_INFO;
-
- while (GstMeta *meta = gst_buffer_iterate_meta(buffer, &state)) {
- if (meta->info->api != info->api)
- continue;
-
- GstVideoRegionOfInterestMeta *region = reinterpret_cast<GstVideoRegionOfInterestMeta *>(meta);
-
- faces.append(QRect(region->x, region->y, region->w, region->h));
- }
-#else
- Q_UNUSED(buffer);
-#endif
-
- QMutexLocker locker(&m_mutex);
-
- if (m_faces != faces) {
- m_faces = faces;
-
- static const int signalIndex = metaObject()->indexOfSlot("_q_updateFaces()");
- metaObject()->method(signalIndex).invoke(this, Qt::QueuedConnection);
- }
-
- return true;
-}
-
-#endif
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinfocus.h b/src/plugins/gstreamer/camerabin/camerabinfocus.h
deleted file mode 100644
index 0fca02c35..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinfocus.h
+++ /dev/null
@@ -1,134 +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 CAMERABINFOCUSCONTROL_H
-#define CAMERABINFOCUSCONTROL_H
-
-#include <qcamera.h>
-#include <qcamerafocuscontrol.h>
-
-#include <private/qgstreamerbufferprobe_p.h>
-
-#include <qbasictimer.h>
-#include <qlist.h>
-#include <qmutex.h>
-
-#include <gst/gst.h>
-#include <glib.h>
-
-QT_BEGIN_NAMESPACE
-
-class CameraBinSession;
-
-class CameraBinFocus
- : public QCameraFocusControl
-#if GST_CHECK_VERSION(1,0,0)
- , QGstreamerBufferProbe
-#endif
-{
- Q_OBJECT
-
-public:
- CameraBinFocus(CameraBinSession *session);
- virtual ~CameraBinFocus();
-
- QCameraFocus::FocusModes focusMode() const override;
- void setFocusMode(QCameraFocus::FocusModes mode) override;
- bool isFocusModeSupported(QCameraFocus::FocusModes mode) const override;
-
- QCameraFocus::FocusPointMode focusPointMode() const override;
- void setFocusPointMode(QCameraFocus::FocusPointMode mode) override;
- bool isFocusPointModeSupported(QCameraFocus::FocusPointMode) const override;
- QPointF customFocusPoint() const override;
- void setCustomFocusPoint(const QPointF &point) override;
-
- QCameraFocusZoneList focusZones() const override;
-
- void handleFocusMessage(GstMessage*);
- QCamera::LockStatus focusStatus() const { return m_focusStatus; }
-
-Q_SIGNALS:
- void _q_focusStatusChanged(QCamera::LockStatus status, QCamera::LockChangeReason reason);
-
-public Q_SLOTS:
- void _q_startFocusing();
- void _q_stopFocusing();
-
- void setViewfinderResolution(const QSize &resolution);
-
-#if GST_CHECK_VERSION(1,0,0)
-protected:
- void timerEvent(QTimerEvent *event) override;
-#endif
-
-private Q_SLOTS:
- void _q_setFocusStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason);
- void _q_handleCameraStatusChange(QCamera::Status status);
-
-#if GST_CHECK_VERSION(1,0,0)
- void _q_updateFaces();
-#endif
-
-private:
- void resetFocusPoint();
- void updateRegionOfInterest(const QRectF &rectangle);
- void updateRegionOfInterest(const QList<QRect> &rectangles);
-
-#if GST_CHECK_VERSION(1,0,0)
- bool probeBuffer(GstBuffer *buffer) override;
-#endif
-
- CameraBinSession *m_session;
- QCamera::Status m_cameraStatus;
- QCameraFocus::FocusModes m_focusMode;
- QCameraFocus::FocusPointMode m_focusPointMode;
- QCamera::LockStatus m_focusStatus;
- QCameraFocusZone::FocusZoneStatus m_focusZoneStatus;
- QPointF m_focusPoint;
- QRectF m_focusRect;
- QSize m_viewfinderResolution;
- QList<QRect> m_faces;
- QList<QRect> m_faceFocusRects;
- QBasicTimer m_faceResetTimer;
- mutable QMutex m_mutex;
-};
-
-QT_END_NAMESPACE
-
-#endif // CAMERABINFOCUSCONTROL_H
diff --git a/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp b/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp
deleted file mode 100644
index b164bc31a..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp
+++ /dev/null
@@ -1,380 +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 "camerabinimagecapture.h"
-#include "camerabincontrol.h"
-#include "camerabincapturedestination.h"
-#include "camerabincapturebufferformat.h"
-#include "camerabinsession.h"
-#include "camerabinresourcepolicy.h"
-#include <private/qgstvideobuffer_p.h>
-#include <private/qvideosurfacegstsink_p.h>
-#include <private/qgstutils_p.h>
-#include <QtMultimedia/qmediametadata.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qbuffer.h>
-#include <QtGui/qimagereader.h>
-
-//#define DEBUG_CAPTURE
-
-#define IMAGE_DONE_SIGNAL "image-done"
-
-QT_BEGIN_NAMESPACE
-
-CameraBinImageCapture::CameraBinImageCapture(CameraBinSession *session)
- :QCameraImageCaptureControl(session)
- , m_encoderProbe(this)
- , m_muxerProbe(this)
- , m_session(session)
- , m_jpegEncoderElement(0)
- , m_metadataMuxerElement(0)
- , m_requestId(0)
- , m_ready(false)
-{
- connect(m_session, SIGNAL(statusChanged(QCamera::Status)), SLOT(updateState()));
- connect(m_session, SIGNAL(imageExposed(int)), this, SIGNAL(imageExposed(int)));
- connect(m_session, SIGNAL(imageCaptured(int,QImage)), this, SIGNAL(imageCaptured(int,QImage)));
- connect(m_session->cameraControl()->resourcePolicy(), SIGNAL(canCaptureChanged()), this, SLOT(updateState()));
-
- m_session->bus()->installMessageFilter(this);
-}
-
-CameraBinImageCapture::~CameraBinImageCapture()
-{
-}
-
-bool CameraBinImageCapture::isReadyForCapture() const
-{
- return m_ready;
-}
-
-int CameraBinImageCapture::capture(const QString &fileName)
-{
- m_requestId++;
-
- if (!m_ready) {
- emit error(m_requestId, QCameraImageCapture::NotReadyError, tr("Camera not ready"));
- return m_requestId;
- }
-
-#ifdef DEBUG_CAPTURE
- qDebug() << Q_FUNC_INFO << m_requestId << fileName;
-#endif
- m_session->captureImage(m_requestId, fileName);
- return m_requestId;
-}
-
-void CameraBinImageCapture::cancelCapture()
-{
-}
-
-void CameraBinImageCapture::updateState()
-{
- bool ready = m_session->status() == QCamera::ActiveStatus
- && m_session->cameraControl()->resourcePolicy()->canCapture();
- if (m_ready != ready) {
-#ifdef DEBUG_CAPTURE
- qDebug() << "readyForCaptureChanged" << ready;
-#endif
- emit readyForCaptureChanged(m_ready = ready);
- }
-}
-
-#if GST_CHECK_VERSION(1,0,0)
-GstPadProbeReturn CameraBinImageCapture::encoderEventProbe(
- GstPad *, GstPadProbeInfo *info, gpointer user_data)
-{
- GstEvent * const event = gst_pad_probe_info_get_event(info);
-#else
-gboolean CameraBinImageCapture::encoderEventProbe(
- GstElement *, GstEvent *event, gpointer user_data)
-{
-#endif
- CameraBinImageCapture * const self = static_cast<CameraBinImageCapture *>(user_data);
- if (event && GST_EVENT_TYPE(event) == GST_EVENT_TAG) {
- GstTagList *gstTags;
- gst_event_parse_tag(event, &gstTags);
- QMap<QByteArray, QVariant> extendedTags = QGstUtils::gstTagListToMap(gstTags);
-
-#ifdef DEBUG_CAPTURE
- qDebug() << QString(gst_structure_to_string(gst_event_get_structure(event))).right(768);
- qDebug() << "Capture event probe" << extendedTags;
-#endif
-
- QVariantMap tags;
- tags[QMediaMetaData::ISOSpeedRatings] = extendedTags.value("capturing-iso-speed");
- tags[QMediaMetaData::DigitalZoomRatio] = extendedTags.value("capturing-digital-zoom-ratio");
- tags[QMediaMetaData::ExposureTime] = extendedTags.value("capturing-shutter-speed");
- tags[QMediaMetaData::WhiteBalance] = extendedTags.value("capturing-white-balance");
- tags[QMediaMetaData::Flash] = extendedTags.value("capturing-flash-fired");
- tags[QMediaMetaData::FocalLengthIn35mmFilm] = extendedTags.value("capturing-focal-length");
- tags[QMediaMetaData::MeteringMode] = extendedTags.value("capturing-metering-mode");
- tags[QMediaMetaData::ExposureMode] = extendedTags.value("capturing-exposure-mode");
- tags[QMediaMetaData::FNumber] = extendedTags.value("capturing-focal-ratio");
- tags[QMediaMetaData::ExposureMode] = extendedTags.value("capturing-exposure-mode");
-
- for (auto i = tags.cbegin(), end = tags.cend(); i != end; ++i) {
- if (i.value().isValid()) {
- QMetaObject::invokeMethod(self, "imageMetadataAvailable",
- Qt::QueuedConnection,
- Q_ARG(int, self->m_requestId),
- Q_ARG(QString, i.key()),
- Q_ARG(QVariant, i.value()));
- }
- }
- }
-#if GST_CHECK_VERSION(1,0,0)
- return GST_PAD_PROBE_OK;
-#else
- return TRUE;
-#endif
-}
-
-void CameraBinImageCapture::EncoderProbe::probeCaps(GstCaps *caps)
-{
-#if GST_CHECK_VERSION(1,0,0)
- capture->m_bufferFormat = QGstUtils::formatForCaps(caps, &capture->m_videoInfo);
-#else
- int bytesPerLine = 0;
- QVideoSurfaceFormat format = QGstUtils::formatForCaps(caps, &bytesPerLine);
- capture->m_bytesPerLine = bytesPerLine;
- capture->m_bufferFormat = format;
-#endif
-}
-
-bool CameraBinImageCapture::EncoderProbe::probeBuffer(GstBuffer *buffer)
-{
- CameraBinSession * const session = capture->m_session;
-
-#ifdef DEBUG_CAPTURE
- qDebug() << "Uncompressed buffer probe";
-#endif
-
- QCameraImageCapture::CaptureDestinations destination =
- session->captureDestinationControl()->captureDestination();
- QVideoFrame::PixelFormat format = session->captureBufferFormatControl()->bufferFormat();
-
- if (destination & QCameraImageCapture::CaptureToBuffer) {
- if (format != QVideoFrame::Format_Jpeg) {
-#ifdef DEBUG_CAPTURE
- qDebug() << "imageAvailable(uncompressed):" << format;
-#endif
-#if GST_CHECK_VERSION(1,0,0)
- QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, capture->m_videoInfo);
-#else
- QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, capture->m_bytesPerLine);
-#endif
-
- QVideoFrame frame(
- videoBuffer,
- capture->m_bufferFormat.frameSize(),
- capture->m_bufferFormat.pixelFormat());
-
- QMetaObject::invokeMethod(capture, "imageAvailable",
- Qt::QueuedConnection,
- Q_ARG(int, capture->m_requestId),
- Q_ARG(QVideoFrame, frame));
- }
- }
-
- //keep the buffer if capture to file or jpeg buffer capture was reuqsted
- bool keepBuffer = (destination & QCameraImageCapture::CaptureToFile) ||
- ((destination & QCameraImageCapture::CaptureToBuffer) &&
- format == QVideoFrame::Format_Jpeg);
-
- return keepBuffer;
-}
-
-void CameraBinImageCapture::MuxerProbe::probeCaps(GstCaps *caps)
-{
- capture->m_jpegResolution = QGstUtils::capsCorrectedResolution(caps);
-}
-
-bool CameraBinImageCapture::MuxerProbe::probeBuffer(GstBuffer *buffer)
-{
- CameraBinSession * const session = capture->m_session;
-
- QCameraImageCapture::CaptureDestinations destination =
- session->captureDestinationControl()->captureDestination();
-
- if ((destination & QCameraImageCapture::CaptureToBuffer) &&
- session->captureBufferFormatControl()->bufferFormat() == QVideoFrame::Format_Jpeg) {
-
- QSize resolution = capture->m_jpegResolution;
- //if resolution is not presented in caps, try to find it from encoded jpeg data:
-#if GST_CHECK_VERSION(1,0,0)
- GstMapInfo mapInfo;
- if (resolution.isEmpty() && gst_buffer_map(buffer, &mapInfo, GST_MAP_READ)) {
- QBuffer data;
- data.setData(reinterpret_cast<const char*>(mapInfo.data), mapInfo.size);
-
- QImageReader reader(&data, "JPEG");
- resolution = reader.size();
-
- gst_buffer_unmap(buffer, &mapInfo);
- }
-
- GstVideoInfo info;
- gst_video_info_set_format(
- &info, GST_VIDEO_FORMAT_ENCODED, resolution.width(), resolution.height());
- QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, info);
-#else
- if (resolution.isEmpty()) {
- QBuffer data;
- data.setData(reinterpret_cast<const char*>(GST_BUFFER_DATA(buffer)), GST_BUFFER_SIZE(buffer));
- QImageReader reader(&data, "JPEG");
- resolution = reader.size();
- }
-
- QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer,
- -1); //bytesPerLine is not available for jpegs
-#endif
-
-
- QVideoFrame frame(videoBuffer,
- resolution,
- QVideoFrame::Format_Jpeg);
- QMetaObject::invokeMethod(capture, "imageAvailable",
- Qt::QueuedConnection,
- Q_ARG(int, capture->m_requestId),
- Q_ARG(QVideoFrame, frame));
- }
-
-
- // Theoretically we could drop the buffer here when don't want to capture to file but that
- // prevents camerabin from recognizing that capture has been completed and returning
- // to its idle state.
- return true;
-}
-
-
-bool CameraBinImageCapture::processBusMessage(const QGstreamerMessage &message)
-{
- //Install metadata event and buffer probes
-
- //The image capture pipiline is built dynamically,
- //it's necessary to wait until jpeg encoder is added to pipeline
-
- GstMessage *gm = message.rawMessage();
- if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_STATE_CHANGED) {
- GstState oldState;
- GstState newState;
- GstState pending;
- gst_message_parse_state_changed(gm, &oldState, &newState, &pending);
-
- if (newState == GST_STATE_READY) {
- GstElement *element = GST_ELEMENT(GST_MESSAGE_SRC(gm));
- if (!element)
- return false;
-
- gchar *name = gst_element_get_name(element);
- QString elementName = QString::fromLatin1(name);
- g_free(name);
-#if !GST_CHECK_VERSION(1,0,0)
- GstElementClass *elementClass = GST_ELEMENT_GET_CLASS(element);
- QString elementLongName = elementClass->details.longname;
-#endif
- if (elementName.contains("jpegenc") && element != m_jpegEncoderElement) {
- m_jpegEncoderElement = element;
- GstPad *sinkpad = gst_element_get_static_pad(element, "sink");
-
- //metadata event probe is installed before jpeg encoder
- //to emit metadata available signal as soon as possible.
-#ifdef DEBUG_CAPTURE
- qDebug() << "install metadata probe";
-#endif
-#if GST_CHECK_VERSION(1,0,0)
- gst_pad_add_probe(
- sinkpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, encoderEventProbe, this, NULL);
-#else
- gst_pad_add_event_probe(sinkpad, G_CALLBACK(encoderEventProbe), this);
-#endif
-#ifdef DEBUG_CAPTURE
- qDebug() << "install uncompressed buffer probe";
-#endif
- m_encoderProbe.addProbeToPad(sinkpad, true);
-
- gst_object_unref(sinkpad);
- } else if ((elementName.contains("jifmux")
-#if !GST_CHECK_VERSION(1,0,0)
- || elementLongName == QLatin1String("JPEG stream muxer")
-#endif
- || elementName.startsWith("metadatamux"))
- && element != m_metadataMuxerElement) {
- //Jpeg encoded buffer probe is added after jifmux/metadatamux
- //element to ensure the resulting jpeg buffer contains capture metadata
- m_metadataMuxerElement = element;
-
- GstPad *srcpad = gst_element_get_static_pad(element, "src");
-#ifdef DEBUG_CAPTURE
- qDebug() << "install jpeg buffer probe";
-#endif
- m_muxerProbe.addProbeToPad(srcpad);
-
- gst_object_unref(srcpad);
- }
- }
- } else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) {
- if (GST_MESSAGE_SRC(gm) == (GstObject *)m_session->cameraBin()) {
- const GstStructure *structure = gst_message_get_structure(gm);
-
- if (gst_structure_has_name (structure, "image-done")) {
- const gchar *fileName = gst_structure_get_string (structure, "filename");
-#ifdef DEBUG_CAPTURE
- qDebug() << "Image saved" << fileName;
-#endif
-
- if (m_session->captureDestinationControl()->captureDestination() & QCameraImageCapture::CaptureToFile) {
- emit imageSaved(m_requestId, QString::fromUtf8(fileName));
- } else {
-#ifdef DEBUG_CAPTURE
- qDebug() << Q_FUNC_INFO << "Dropped saving file" << fileName;
-#endif
- QFileInfo info(QString::fromUtf8(fileName));
- if (info.exists() && info.isFile())
- QFile(info.absoluteFilePath()).remove();
- }
- }
- }
- }
-
- return false;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinimagecapture.h b/src/plugins/gstreamer/camerabin/camerabinimagecapture.h
deleted file mode 100644
index 5e00ab6d5..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinimagecapture.h
+++ /dev/null
@@ -1,123 +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 CAMERABINIMAGECAPTURECONTROL_H
-#define CAMERABINIMAGECAPTURECONTROL_H
-
-#include <qcameraimagecapturecontrol.h>
-#include "camerabinsession.h"
-
-#include <qvideosurfaceformat.h>
-
-#include <private/qgstreamerbufferprobe_p.h>
-
-#if GST_CHECK_VERSION(1,0,0)
-#include <gst/video/video.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class CameraBinImageCapture : public QCameraImageCaptureControl, public QGstreamerBusMessageFilter
-{
- Q_OBJECT
- Q_INTERFACES(QGstreamerBusMessageFilter)
-public:
- CameraBinImageCapture(CameraBinSession *session);
- virtual ~CameraBinImageCapture();
-
- QCameraImageCapture::DriveMode driveMode() const override { return QCameraImageCapture::SingleImageCapture; }
- void setDriveMode(QCameraImageCapture::DriveMode) override {}
-
- bool isReadyForCapture() const override;
- int capture(const QString &fileName) override;
- void cancelCapture() override;
-
- bool processBusMessage(const QGstreamerMessage &message) override;
-
-private slots:
- void updateState();
-
-private:
-#if GST_CHECK_VERSION(1,0,0)
- static GstPadProbeReturn encoderEventProbe(GstPad *, GstPadProbeInfo *info, gpointer user_data);
-#else
- static gboolean encoderEventProbe(GstElement *, GstEvent *event, gpointer user_data);
-#endif
-
- class EncoderProbe : public QGstreamerBufferProbe
- {
- public:
- EncoderProbe(CameraBinImageCapture *capture) : capture(capture) {}
- void probeCaps(GstCaps *caps) override;
- bool probeBuffer(GstBuffer *buffer) override;
-
- private:
- CameraBinImageCapture * const capture;
- } m_encoderProbe;
-
- class MuxerProbe : public QGstreamerBufferProbe
- {
- public:
- MuxerProbe(CameraBinImageCapture *capture) : capture(capture) {}
- void probeCaps(GstCaps *caps) override;
- bool probeBuffer(GstBuffer *buffer) override;
-
- private:
- CameraBinImageCapture * const capture;
-
- } m_muxerProbe;
-
- QVideoSurfaceFormat m_bufferFormat;
- QSize m_jpegResolution;
- CameraBinSession *m_session;
- GstElement *m_jpegEncoderElement;
- GstElement *m_metadataMuxerElement;
-#if GST_CHECK_VERSION(1,0,0)
- GstVideoInfo m_videoInfo;
-#else
- int m_bytesPerLine;
-#endif
- int m_requestId;
- bool m_ready;
-};
-
-QT_END_NAMESPACE
-
-#endif // CAMERABINCAPTURECORNTROL_H
diff --git a/src/plugins/gstreamer/camerabin/camerabinimageencoder.cpp b/src/plugins/gstreamer/camerabin/camerabinimageencoder.cpp
deleted file mode 100644
index 8c4eaec70..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinimageencoder.cpp
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "camerabinimageencoder.h"
-#include "camerabinsession.h"
-
-#include <QtCore/qdebug.h>
-
-QT_BEGIN_NAMESPACE
-
-CameraBinImageEncoder::CameraBinImageEncoder(CameraBinSession *session)
- :QImageEncoderControl(session), m_session(session)
-{
-}
-
-CameraBinImageEncoder::~CameraBinImageEncoder()
-{
-}
-
-QList<QSize> CameraBinImageEncoder::supportedResolutions(const QImageEncoderSettings &, bool *continuous) const
-{
- if (continuous)
- *continuous = false;
-
- return m_session->supportedResolutions(qMakePair<int,int>(0,0), continuous, QCamera::CaptureStillImage);
-}
-
-QStringList CameraBinImageEncoder::supportedImageCodecs() const
-{
- return QStringList() << "jpeg";
-}
-
-QString CameraBinImageEncoder::imageCodecDescription(const QString &codecName) const
-{
- if (codecName == "jpeg")
- return tr("JPEG image");
-
- return QString();
-}
-
-QImageEncoderSettings CameraBinImageEncoder::imageSettings() const
-{
- return m_settings;
-}
-
-void CameraBinImageEncoder::setImageSettings(const QImageEncoderSettings &settings)
-{
- m_settings = settings;
- emit settingsChanged();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinimageencoder.h b/src/plugins/gstreamer/camerabin/camerabinimageencoder.h
deleted file mode 100644
index 96f7ae7aa..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinimageencoder.h
+++ /dev/null
@@ -1,86 +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 CAMERABINIMAGEENCODE_H
-#define CAMERABINIMAGEENCODE_H
-
-#include <qimageencodercontrol.h>
-
-#include <QtCore/qstringlist.h>
-#include <QtCore/qmap.h>
-
-#include <gst/gst.h>
-QT_BEGIN_NAMESPACE
-
-class CameraBinSession;
-
-class CameraBinImageEncoder : public QImageEncoderControl
-{
- Q_OBJECT
-public:
- CameraBinImageEncoder(CameraBinSession *session);
- virtual ~CameraBinImageEncoder();
-
- QList<QSize> supportedResolutions(const QImageEncoderSettings &settings = QImageEncoderSettings(),
- bool *continuous = 0) const override;
-
- QStringList supportedImageCodecs() const override;
- QString imageCodecDescription(const QString &formatName) const override;
-
- QImageEncoderSettings imageSettings() const override;
- void setImageSettings(const QImageEncoderSettings &settings) override;
-
-Q_SIGNALS:
- void settingsChanged();
-
-private:
- QImageEncoderSettings m_settings;
-
- CameraBinSession *m_session;
-
- // Added
- QStringList m_codecs;
- QMap<QString,QByteArray> m_elementNames;
- QMap<QString,QString> m_codecDescriptions;
- QMap<QString,QStringList> m_codecOptions;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp b/src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp
deleted file mode 100644
index 9d281e10c..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp
+++ /dev/null
@@ -1,441 +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 <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include "camerabinimageprocessing.h"
-#include "camerabinsession.h"
-
-#if QT_CONFIG(linux_v4l)
-#include "camerabinv4limageprocessing.h"
-#endif
-
-#if GST_CHECK_VERSION(1,0,0)
-# include <gst/video/colorbalance.h>
-#else
-# include <gst/interfaces/colorbalance.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-CameraBinImageProcessing::CameraBinImageProcessing(CameraBinSession *session)
- : QCameraImageProcessingControl(session)
- , m_session(session)
- , m_whiteBalanceMode(QCameraImageProcessing::WhiteBalanceAuto)
-#if QT_CONFIG(linux_v4l)
- , m_v4lImageControl(nullptr)
-#endif
-{
-#if QT_CONFIG(gstreamer_photography)
- if (m_session->photography()) {
- m_mappedWbValues[GST_PHOTOGRAPHY_WB_MODE_AUTO] = QCameraImageProcessing::WhiteBalanceAuto;
- m_mappedWbValues[GST_PHOTOGRAPHY_WB_MODE_DAYLIGHT] = QCameraImageProcessing::WhiteBalanceSunlight;
- m_mappedWbValues[GST_PHOTOGRAPHY_WB_MODE_CLOUDY] = QCameraImageProcessing::WhiteBalanceCloudy;
- m_mappedWbValues[GST_PHOTOGRAPHY_WB_MODE_SUNSET] = QCameraImageProcessing::WhiteBalanceSunset;
- m_mappedWbValues[GST_PHOTOGRAPHY_WB_MODE_TUNGSTEN] = QCameraImageProcessing::WhiteBalanceTungsten;
- m_mappedWbValues[GST_PHOTOGRAPHY_WB_MODE_FLUORESCENT] = QCameraImageProcessing::WhiteBalanceFluorescent;
- unlockWhiteBalance();
- }
-
-#if GST_CHECK_VERSION(1, 0, 0)
- m_filterMap.insert(QCameraImageProcessing::ColorFilterNone, GST_PHOTOGRAPHY_COLOR_TONE_MODE_NORMAL);
- if (m_session->photography()) {
- m_filterMap.insert(QCameraImageProcessing::ColorFilterSepia, GST_PHOTOGRAPHY_COLOR_TONE_MODE_SEPIA);
- m_filterMap.insert(QCameraImageProcessing::ColorFilterGrayscale, GST_PHOTOGRAPHY_COLOR_TONE_MODE_GRAYSCALE);
- m_filterMap.insert(QCameraImageProcessing::ColorFilterNegative, GST_PHOTOGRAPHY_COLOR_TONE_MODE_NEGATIVE);
- m_filterMap.insert(QCameraImageProcessing::ColorFilterSolarize, GST_PHOTOGRAPHY_COLOR_TONE_MODE_SOLARIZE);
-#if GST_CHECK_VERSION(1, 2, 0)
- m_filterMap.insert(QCameraImageProcessing::ColorFilterPosterize, GST_PHOTOGRAPHY_COLOR_TONE_MODE_POSTERIZE);
- m_filterMap.insert(QCameraImageProcessing::ColorFilterWhiteboard, GST_PHOTOGRAPHY_COLOR_TONE_MODE_WHITEBOARD);
- m_filterMap.insert(QCameraImageProcessing::ColorFilterBlackboard, GST_PHOTOGRAPHY_COLOR_TONE_MODE_BLACKBOARD);
- m_filterMap.insert(QCameraImageProcessing::ColorFilterAqua, GST_PHOTOGRAPHY_COLOR_TONE_MODE_AQUA);
-#endif
- }
-#else
- m_filterMap.insert(QCameraImageProcessing::ColorFilterNone, GST_PHOTOGRAPHY_COLOUR_TONE_MODE_NORMAL);
- if (m_session->photography()) {
- m_filterMap.insert(QCameraImageProcessing::ColorFilterSepia, GST_PHOTOGRAPHY_COLOUR_TONE_MODE_SEPIA);
- m_filterMap.insert(QCameraImageProcessing::ColorFilterGrayscale, GST_PHOTOGRAPHY_COLOUR_TONE_MODE_GRAYSCALE);
- m_filterMap.insert(QCameraImageProcessing::ColorFilterNegative, GST_PHOTOGRAPHY_COLOUR_TONE_MODE_NEGATIVE);
- m_filterMap.insert(QCameraImageProcessing::ColorFilterSolarize, GST_PHOTOGRAPHY_COLOUR_TONE_MODE_SOLARIZE);
- }
-#endif
-#endif
-
-#if QT_CONFIG(linux_v4l)
- m_v4lImageControl = new CameraBinV4LImageProcessing(m_session);
- connect(m_session, &CameraBinSession::statusChanged,
- m_v4lImageControl, &CameraBinV4LImageProcessing::updateParametersInfo);
-#endif
-
- updateColorBalanceValues();
-}
-
-CameraBinImageProcessing::~CameraBinImageProcessing()
-{
-}
-
-void CameraBinImageProcessing::updateColorBalanceValues()
-{
- if (!GST_IS_COLOR_BALANCE(m_session->cameraBin())) {
- // Camerabin doesn't implement gstcolorbalance interface
- return;
- }
-
- GstColorBalance *balance = GST_COLOR_BALANCE(m_session->cameraBin());
- const GList *controls = gst_color_balance_list_channels(balance);
-
- const GList *item;
- GstColorBalanceChannel *channel;
- gint cur_value;
- qreal scaledValue = 0;
-
- for (item = controls; item; item = g_list_next (item)) {
- channel = (GstColorBalanceChannel *)item->data;
- cur_value = gst_color_balance_get_value (balance, channel);
-
- //map the [min_value..max_value] range to [-1.0 .. 1.0]
- if (channel->min_value != channel->max_value) {
- scaledValue = qreal(cur_value - channel->min_value) /
- (channel->max_value - channel->min_value) * 2 - 1;
- }
-
- if (!g_ascii_strcasecmp (channel->label, "brightness")) {
- m_values[QCameraImageProcessingControl::BrightnessAdjustment] = scaledValue;
- } else if (!g_ascii_strcasecmp (channel->label, "contrast")) {
- m_values[QCameraImageProcessingControl::ContrastAdjustment] = scaledValue;
- } else if (!g_ascii_strcasecmp (channel->label, "saturation")) {
- m_values[QCameraImageProcessingControl::SaturationAdjustment] = scaledValue;
- }
- }
-}
-
-bool CameraBinImageProcessing::setColorBalanceValue(const QString& channel, qreal value)
-{
-
- if (!GST_IS_COLOR_BALANCE(m_session->cameraBin())) {
- // Camerabin doesn't implement gstcolorbalance interface
- return false;
- }
-
- GstColorBalance *balance = GST_COLOR_BALANCE(m_session->cameraBin());
- const GList *controls = gst_color_balance_list_channels(balance);
-
- const GList *item;
- GstColorBalanceChannel *colorBalanceChannel;
-
- for (item = controls; item; item = g_list_next (item)) {
- colorBalanceChannel = (GstColorBalanceChannel *)item->data;
-
- if (!g_ascii_strcasecmp (colorBalanceChannel->label, channel.toLatin1())) {
- //map the [-1.0 .. 1.0] range to [min_value..max_value]
- gint scaledValue = colorBalanceChannel->min_value + qRound(
- (value+1.0)/2.0 * (colorBalanceChannel->max_value - colorBalanceChannel->min_value));
-
- gst_color_balance_set_value (balance, colorBalanceChannel, scaledValue);
- return true;
- }
- }
-
- return false;
-}
-
-QCameraImageProcessing::WhiteBalanceMode CameraBinImageProcessing::whiteBalanceMode() const
-{
- return m_whiteBalanceMode;
-}
-
-bool CameraBinImageProcessing::setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceMode mode)
-{
-#if QT_CONFIG(gstreamer_photography)
- if (isWhiteBalanceModeSupported(mode)) {
- m_whiteBalanceMode = mode;
-#if GST_CHECK_VERSION(1, 2, 0)
- GstPhotographyWhiteBalanceMode currentMode;
- if (gst_photography_get_white_balance_mode(m_session->photography(), &currentMode)
- && currentMode != GST_PHOTOGRAPHY_WB_MODE_MANUAL)
-#endif
- {
- unlockWhiteBalance();
- return true;
- }
- }
-#else
- Q_UNUSED(mode);
-#endif
- return false;
-}
-
-bool CameraBinImageProcessing::isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceMode mode) const
-{
-#if QT_CONFIG(gstreamer_photography)
- return m_mappedWbValues.values().contains(mode);
-#else
- Q_UNUSED(mode);
- return false;
-#endif
-}
-
-bool CameraBinImageProcessing::isParameterSupported(QCameraImageProcessingControl::ProcessingParameter parameter) const
-{
-#if QT_CONFIG(gstreamer_photography)
- if (parameter == QCameraImageProcessingControl::WhiteBalancePreset
- || parameter == QCameraImageProcessingControl::ColorFilter) {
- if (m_session->photography())
- return true;
- }
-#endif
-
- if (parameter == QCameraImageProcessingControl::Contrast
- || parameter == QCameraImageProcessingControl::Brightness
- || parameter == QCameraImageProcessingControl::Saturation) {
- if (GST_IS_COLOR_BALANCE(m_session->cameraBin()))
- return true;
- }
-
-#if QT_CONFIG(linux_v4l)
- if (m_v4lImageControl->isParameterSupported(parameter))
- return true;
-#endif
-
- return false;
-}
-
-bool CameraBinImageProcessing::isParameterValueSupported(QCameraImageProcessingControl::ProcessingParameter parameter, const QVariant &value) const
-{
- switch (parameter) {
- case ContrastAdjustment:
- case BrightnessAdjustment:
- case SaturationAdjustment: {
- const bool isGstColorBalanceValueSupported = GST_IS_COLOR_BALANCE(m_session->cameraBin())
- && qAbs(value.toReal()) <= 1.0;
-#if QT_CONFIG(linux_v4l)
- if (!isGstColorBalanceValueSupported)
- return m_v4lImageControl->isParameterValueSupported(parameter, value);
-#endif
- return isGstColorBalanceValueSupported;
- }
- case SharpeningAdjustment: {
-#if QT_CONFIG(linux_v4l)
- return m_v4lImageControl->isParameterValueSupported(parameter, value);
-#else
- return false;
-#endif
- }
- case WhiteBalancePreset: {
- const QCameraImageProcessing::WhiteBalanceMode mode =
- value.value<QCameraImageProcessing::WhiteBalanceMode>();
- const bool isPhotographyWhiteBalanceSupported = isWhiteBalanceModeSupported(mode);
-#if QT_CONFIG(linux_v4l)
- if (!isPhotographyWhiteBalanceSupported)
- return m_v4lImageControl->isParameterValueSupported(parameter, value);
-#endif
- return isPhotographyWhiteBalanceSupported;
- }
- case ColorTemperature: {
-#if QT_CONFIG(linux_v4l)
- return m_v4lImageControl->isParameterValueSupported(parameter, value);
-#else
- return false;
-#endif
- }
- case ColorFilter: {
- const QCameraImageProcessing::ColorFilter filter = value.value<QCameraImageProcessing::ColorFilter>();
-#if QT_CONFIG(gstreamer_photography)
- return m_filterMap.contains(filter);
-#else
- return filter == QCameraImageProcessing::ColorFilterNone;
-#endif
- }
- default:
- break;
- }
-
- return false;
-}
-
-QVariant CameraBinImageProcessing::parameter(
- QCameraImageProcessingControl::ProcessingParameter parameter) const
-{
- switch (parameter) {
- case QCameraImageProcessingControl::WhiteBalancePreset: {
- const QCameraImageProcessing::WhiteBalanceMode mode = whiteBalanceMode();
-#if QT_CONFIG(linux_v4l)
- if (mode == QCameraImageProcessing::WhiteBalanceAuto
- || mode == QCameraImageProcessing::WhiteBalanceManual) {
- return m_v4lImageControl->parameter(parameter);
- }
-#endif
- return QVariant::fromValue<QCameraImageProcessing::WhiteBalanceMode>(mode);
- }
- case QCameraImageProcessingControl::ColorTemperature: {
-#if QT_CONFIG(linux_v4l)
- return m_v4lImageControl->parameter(parameter);
-#else
- return QVariant();
-#endif
- }
- case QCameraImageProcessingControl::ColorFilter:
-#if QT_CONFIG(gstreamer_photography)
- if (GstPhotography *photography = m_session->photography()) {
-#if GST_CHECK_VERSION(1, 0, 0)
- GstPhotographyColorToneMode mode = GST_PHOTOGRAPHY_COLOR_TONE_MODE_NORMAL;
- gst_photography_get_color_tone_mode(photography, &mode);
-#else
- GstColourToneMode mode = GST_PHOTOGRAPHY_COLOUR_TONE_MODE_NORMAL;
- gst_photography_get_colour_tone_mode(photography, &mode);
-#endif
- return QVariant::fromValue(m_filterMap.key(mode, QCameraImageProcessing::ColorFilterNone));
- }
-#endif
- return QVariant::fromValue(QCameraImageProcessing::ColorFilterNone);
- default: {
- const bool isGstParameterSupported = m_values.contains(parameter);
-#if QT_CONFIG(linux_v4l)
- if (!isGstParameterSupported) {
- if (parameter == QCameraImageProcessingControl::BrightnessAdjustment
- || parameter == QCameraImageProcessingControl::ContrastAdjustment
- || parameter == QCameraImageProcessingControl::SaturationAdjustment
- || parameter == QCameraImageProcessingControl::SharpeningAdjustment) {
- return m_v4lImageControl->parameter(parameter);
- }
- }
-#endif
- return isGstParameterSupported
- ? QVariant(m_values.value(parameter))
- : QVariant();
- }
- }
-}
-
-void CameraBinImageProcessing::setParameter(QCameraImageProcessingControl::ProcessingParameter parameter,
- const QVariant &value)
-{
- switch (parameter) {
- case ContrastAdjustment: {
- if (!setColorBalanceValue("contrast", value.toReal())) {
-#if QT_CONFIG(linux_v4l)
- m_v4lImageControl->setParameter(parameter, value);
-#endif
- }
- }
- break;
- case BrightnessAdjustment: {
- if (!setColorBalanceValue("brightness", value.toReal())) {
-#if QT_CONFIG(linux_v4l)
- m_v4lImageControl->setParameter(parameter, value);
-#endif
- }
- }
- break;
- case SaturationAdjustment: {
- if (!setColorBalanceValue("saturation", value.toReal())) {
-#if QT_CONFIG(linux_v4l)
- m_v4lImageControl->setParameter(parameter, value);
-#endif
- }
- }
- break;
- case SharpeningAdjustment: {
-#if QT_CONFIG(linux_v4l)
- m_v4lImageControl->setParameter(parameter, value);
-#endif
- }
- break;
- case WhiteBalancePreset: {
- if (!setWhiteBalanceMode(value.value<QCameraImageProcessing::WhiteBalanceMode>())) {
-#if QT_CONFIG(linux_v4l)
- const QCameraImageProcessing::WhiteBalanceMode mode =
- value.value<QCameraImageProcessing::WhiteBalanceMode>();
- if (mode == QCameraImageProcessing::WhiteBalanceAuto
- || mode == QCameraImageProcessing::WhiteBalanceManual) {
- m_v4lImageControl->setParameter(parameter, value);
- return;
- }
-#endif
- }
- }
- break;
- case QCameraImageProcessingControl::ColorTemperature: {
-#if QT_CONFIG(linux_v4l)
- m_v4lImageControl->setParameter(parameter, value);
-#endif
- break;
- }
- case QCameraImageProcessingControl::ColorFilter:
-#if QT_CONFIG(gstreamer_photography)
- if (GstPhotography *photography = m_session->photography()) {
-#if GST_CHECK_VERSION(1, 0, 0)
- gst_photography_set_color_tone_mode(photography, m_filterMap.value(
- value.value<QCameraImageProcessing::ColorFilter>(),
- GST_PHOTOGRAPHY_COLOR_TONE_MODE_NORMAL));
-#else
- gst_photography_set_colour_tone_mode(photography, m_filterMap.value(
- value.value<QCameraImageProcessing::ColorFilter>(),
- GST_PHOTOGRAPHY_COLOUR_TONE_MODE_NORMAL));
-#endif
- }
-#endif
- break;
- default:
- break;
- }
-
- updateColorBalanceValues();
-}
-
-#if QT_CONFIG(gstreamer_photography)
-void CameraBinImageProcessing::lockWhiteBalance()
-{
-#if GST_CHECK_VERSION(1, 2, 0)
- if (GstPhotography *photography = m_session->photography()) {
- gst_photography_set_white_balance_mode(photography, GST_PHOTOGRAPHY_WB_MODE_MANUAL);
- }
-#endif
-}
-
-void CameraBinImageProcessing::unlockWhiteBalance()
-{
- if (GstPhotography *photography = m_session->photography()) {
- gst_photography_set_white_balance_mode(
- photography, m_mappedWbValues.key(m_whiteBalanceMode));
- }
-}
-#endif
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinimageprocessing.h b/src/plugins/gstreamer/camerabin/camerabinimageprocessing.h
deleted file mode 100644
index 259138e91..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinimageprocessing.h
+++ /dev/null
@@ -1,108 +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 CAMERABINIMAGEPROCESSINGCONTROL_H
-#define CAMERABINIMAGEPROCESSINGCONTROL_H
-
-#include <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include <qcamera.h>
-#include <qcameraimageprocessingcontrol.h>
-
-#include <gst/gst.h>
-#include <glib.h>
-
-#if QT_CONFIG(gstreamer_photography)
-# include <gst/interfaces/photography.h>
-# if !GST_CHECK_VERSION(1,0,0)
-typedef GstWhiteBalanceMode GstPhotographyWhiteBalanceMode;
-typedef GstColourToneMode GstPhotographyColorToneMode;
-# endif
-#endif
-
-QT_BEGIN_NAMESPACE
-
-#if QT_CONFIG(linux_v4l)
-class CameraBinV4LImageProcessing;
-#endif
-
-class CameraBinSession;
-
-class CameraBinImageProcessing : public QCameraImageProcessingControl
-{
- Q_OBJECT
-
-public:
- CameraBinImageProcessing(CameraBinSession *session);
- virtual ~CameraBinImageProcessing();
-
- QCameraImageProcessing::WhiteBalanceMode whiteBalanceMode() const;
- bool setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceMode mode);
- bool isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceMode mode) const;
-
- bool isParameterSupported(ProcessingParameter) const override;
- bool isParameterValueSupported(ProcessingParameter parameter, const QVariant &value) const override;
- QVariant parameter(ProcessingParameter parameter) const override;
- void setParameter(ProcessingParameter parameter, const QVariant &value) override;
-
-#if QT_CONFIG(gstreamer_photography)
- void lockWhiteBalance();
- void unlockWhiteBalance();
-#endif
-
-private:
- bool setColorBalanceValue(const QString& channel, qreal value);
- void updateColorBalanceValues();
-
-private:
- CameraBinSession *m_session;
- QMap<QCameraImageProcessingControl::ProcessingParameter, int> m_values;
-#if QT_CONFIG(gstreamer_photography)
- QMap<GstPhotographyWhiteBalanceMode, QCameraImageProcessing::WhiteBalanceMode> m_mappedWbValues;
- QMap<QCameraImageProcessing::ColorFilter, GstPhotographyColorToneMode> m_filterMap;
-#endif
- QCameraImageProcessing::WhiteBalanceMode m_whiteBalanceMode;
-
-#if QT_CONFIG(linux_v4l)
- CameraBinV4LImageProcessing *m_v4lImageControl;
-#endif
-};
-
-QT_END_NAMESPACE
-
-#endif // CAMERABINIMAGEPROCESSINGCONTROL_H
diff --git a/src/plugins/gstreamer/camerabin/camerabininfocontrol.cpp b/src/plugins/gstreamer/camerabin/camerabininfocontrol.cpp
deleted file mode 100644
index 6aa3aeb61..000000000
--- a/src/plugins/gstreamer/camerabin/camerabininfocontrol.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/****************************************************************************
-**
-** 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 "camerabininfocontrol.h"
-
-#include <private/qgstutils_p.h>
-
-QT_BEGIN_NAMESPACE
-
-CameraBinInfoControl::CameraBinInfoControl(GstElementFactory *sourceFactory, QObject *parent)
- : QCameraInfoControl(parent)
- , m_sourceFactory(sourceFactory)
-{
- gst_object_ref(GST_OBJECT(m_sourceFactory));
-}
-
-CameraBinInfoControl::~CameraBinInfoControl()
-{
- gst_object_unref(GST_OBJECT(m_sourceFactory));
-}
-
-QCamera::Position CameraBinInfoControl::cameraPosition(const QString &device) const
-{
- return QGstUtils::cameraPosition(device, m_sourceFactory);
-}
-
-int CameraBinInfoControl::cameraOrientation(const QString &device) const
-{
- return QGstUtils::cameraOrientation(device, m_sourceFactory);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabininfocontrol.h b/src/plugins/gstreamer/camerabin/camerabininfocontrol.h
deleted file mode 100644
index 9c801e804..000000000
--- a/src/plugins/gstreamer/camerabin/camerabininfocontrol.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef CAMERABININFOCONTROL_H
-#define CAMERABININFOCONTROL_H
-
-#include <qcamerainfocontrol.h>
-
-#include <gst/gst.h>
-
-QT_BEGIN_NAMESPACE
-
-class CameraBinInfoControl : public QCameraInfoControl
-{
- Q_OBJECT
-public:
- CameraBinInfoControl(GstElementFactory *sourceFactory, QObject *parent);
- ~CameraBinInfoControl();
-
- QCamera::Position cameraPosition(const QString &deviceName) const override;
- int cameraOrientation(const QString &deviceName) const override;
-
-private:
- GstElementFactory * const m_sourceFactory;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/gstreamer/camerabin/camerabinlocks.cpp b/src/plugins/gstreamer/camerabin/camerabinlocks.cpp
deleted file mode 100644
index 89be2ac5d..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinlocks.cpp
+++ /dev/null
@@ -1,259 +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 "camerabinlocks.h"
-#include "camerabinsession.h"
-#include "camerabinfocus.h"
-#include "camerabinimageprocessing.h"
-
-#include <QtCore/qcoreevent.h>
-
-#include <gst/interfaces/photography.h>
-
-#include <QDebug>
-
-QT_BEGIN_NAMESPACE
-
-CameraBinLocks::CameraBinLocks(CameraBinSession *session)
- :QCameraLocksControl(session),
- m_session(session),
- m_focus(m_session->cameraFocusControl())
-{
- connect(m_focus, SIGNAL(_q_focusStatusChanged(QCamera::LockStatus,QCamera::LockChangeReason)),
- this, SLOT(updateFocusStatus(QCamera::LockStatus,QCamera::LockChangeReason)));
-}
-
-CameraBinLocks::~CameraBinLocks()
-{
-}
-
-QCamera::LockTypes CameraBinLocks::supportedLocks() const
-{
- QCamera::LockTypes locks = QCamera::LockFocus;
-
-#if GST_CHECK_VERSION(1, 2, 0)
- if (GstPhotography *photography = m_session->photography()) {
- if (gst_photography_get_capabilities(photography) & GST_PHOTOGRAPHY_CAPS_WB_MODE)
- locks |= QCamera::LockWhiteBalance;
-
- if (GstElement *source = m_session->cameraSource()) {
- if (g_object_class_find_property(
- G_OBJECT_GET_CLASS(source), "exposure-mode")) {
- locks |= QCamera::LockExposure;
- }
- }
- }
-#endif
-
- return locks;
-}
-
-QCamera::LockStatus CameraBinLocks::lockStatus(QCamera::LockType lock) const
-{
- switch (lock) {
- case QCamera::LockFocus:
- return m_focus->focusStatus();
-#if GST_CHECK_VERSION(1, 2, 0)
- case QCamera::LockExposure:
- if (m_pendingLocks & QCamera::LockExposure)
- return QCamera::Searching;
- return isExposureLocked() ? QCamera::Locked : QCamera::Unlocked;
- case QCamera::LockWhiteBalance:
- if (m_pendingLocks & QCamera::LockWhiteBalance)
- return QCamera::Searching;
- return isWhiteBalanceLocked() ? QCamera::Locked : QCamera::Unlocked;
-#endif
- default:
- return QCamera::Unlocked;
- }
-}
-
-void CameraBinLocks::searchAndLock(QCamera::LockTypes locks)
-{
- m_pendingLocks &= ~locks;
-
- if (locks & QCamera::LockFocus) {
- m_pendingLocks |= QCamera::LockFocus;
- m_focus->_q_startFocusing();
- }
-#if GST_CHECK_VERSION(1, 2, 0)
- if (!m_pendingLocks)
- m_lockTimer.stop();
-
- if (locks & QCamera::LockExposure) {
- if (isExposureLocked()) {
- unlockExposure(QCamera::Searching, QCamera::UserRequest);
- m_pendingLocks |= QCamera::LockExposure;
- m_lockTimer.start(1000, this);
- } else {
- lockExposure(QCamera::UserRequest);
- }
- }
- if (locks & QCamera::LockWhiteBalance) {
- if (isWhiteBalanceLocked()) {
- unlockWhiteBalance(QCamera::Searching, QCamera::UserRequest);
- m_pendingLocks |= QCamera::LockWhiteBalance;
- m_lockTimer.start(1000, this);
- } else {
- lockWhiteBalance(QCamera::UserRequest);
- }
- }
-#endif
-
-}
-
-void CameraBinLocks::unlock(QCamera::LockTypes locks)
-{
- m_pendingLocks &= ~locks;
-
- if (locks & QCamera::LockFocus)
- m_focus->_q_stopFocusing();
-
-#if GST_CHECK_VERSION(1, 2, 0)
- if (!m_pendingLocks)
- m_lockTimer.stop();
-
- if (locks & QCamera::LockExposure)
- unlockExposure(QCamera::Unlocked, QCamera::UserRequest);
- if (locks & QCamera::LockWhiteBalance)
- unlockWhiteBalance(QCamera::Unlocked, QCamera::UserRequest);
-#endif
-}
-
-void CameraBinLocks::updateFocusStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason)
-{
- if (status != QCamera::Searching)
- m_pendingLocks &= ~QCamera::LockFocus;
-
-#if GST_CHECK_VERSION(1, 2, 0)
- if (status == QCamera::Locked && !m_lockTimer.isActive()) {
- if (m_pendingLocks & QCamera::LockExposure)
- lockExposure(QCamera::LockAcquired);
- if (m_pendingLocks & QCamera::LockWhiteBalance)
- lockWhiteBalance(QCamera::LockAcquired);
- }
-#endif
- emit lockStatusChanged(QCamera::LockFocus, status, reason);
-}
-
-#if GST_CHECK_VERSION(1, 2, 0)
-
-void CameraBinLocks::timerEvent(QTimerEvent *event)
-{
- if (event->timerId() != m_lockTimer.timerId())
- return QCameraLocksControl::timerEvent(event);
-
- m_lockTimer.stop();
-
- if (!(m_pendingLocks & QCamera::LockFocus)) {
- if (m_pendingLocks & QCamera::LockExposure)
- lockExposure(QCamera::LockAcquired);
- if (m_pendingLocks & QCamera::LockWhiteBalance)
- lockWhiteBalance(QCamera::LockAcquired);
- }
-}
-
-bool CameraBinLocks::isExposureLocked() const
-{
- if (GstElement *source = m_session->cameraSource()) {
- GstPhotographyExposureMode exposureMode = GST_PHOTOGRAPHY_EXPOSURE_MODE_AUTO;
- g_object_get (G_OBJECT(source), "exposure-mode", &exposureMode, NULL);
- return exposureMode == GST_PHOTOGRAPHY_EXPOSURE_MODE_MANUAL;
- } else {
- return false;
- }
-}
-
-void CameraBinLocks::lockExposure(QCamera::LockChangeReason reason)
-{
- GstElement *source = m_session->cameraSource();
- if (!source)
- return;
-
- m_pendingLocks &= ~QCamera::LockExposure;
- g_object_set(
- G_OBJECT(source),
- "exposure-mode",
- GST_PHOTOGRAPHY_EXPOSURE_MODE_MANUAL,
- NULL);
- emit lockStatusChanged(QCamera::LockExposure, QCamera::Locked, reason);
-}
-
-void CameraBinLocks::unlockExposure(QCamera::LockStatus status, QCamera::LockChangeReason reason)
-{
- GstElement *source = m_session->cameraSource();
- if (!source)
- return;
-
- g_object_set(
- G_OBJECT(source),
- "exposure-mode",
- GST_PHOTOGRAPHY_EXPOSURE_MODE_AUTO,
- NULL);
- emit lockStatusChanged(QCamera::LockExposure, status, reason);
-}
-
-bool CameraBinLocks::isWhiteBalanceLocked() const
-{
- if (GstPhotography *photography = m_session->photography()) {
- GstPhotographyWhiteBalanceMode whiteBalanceMode;
- return gst_photography_get_white_balance_mode(photography, &whiteBalanceMode)
- && whiteBalanceMode == GST_PHOTOGRAPHY_WB_MODE_MANUAL;
- } else {
- return false;
- }
-}
-
-void CameraBinLocks::lockWhiteBalance(QCamera::LockChangeReason reason)
-{
- m_pendingLocks &= ~QCamera::LockWhiteBalance;
- m_session->imageProcessingControl()->lockWhiteBalance();
- emit lockStatusChanged(QCamera::LockWhiteBalance, QCamera::Locked, reason);
-}
-
-void CameraBinLocks::unlockWhiteBalance(
- QCamera::LockStatus status, QCamera::LockChangeReason reason)
-{
- m_session->imageProcessingControl()->lockWhiteBalance();
- emit lockStatusChanged(QCamera::LockWhiteBalance, status, reason);
-}
-
-#endif
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinlocks.h b/src/plugins/gstreamer/camerabin/camerabinlocks.h
deleted file mode 100644
index cd592ffd2..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinlocks.h
+++ /dev/null
@@ -1,98 +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 CAMERABINLOCKSCONTROL_H
-#define CAMERABINLOCKSCONTROL_H
-
-#include <qcamera.h>
-#include <qcameralockscontrol.h>
-
-#include <QtCore/qbasictimer.h>
-
-#include <gst/gst.h>
-#include <glib.h>
-
-QT_BEGIN_NAMESPACE
-
-class CameraBinSession;
-class CameraBinFocus;
-
-class CameraBinLocks : public QCameraLocksControl
-{
- Q_OBJECT
-
-public:
- CameraBinLocks(CameraBinSession *session);
- virtual ~CameraBinLocks();
-
- QCamera::LockTypes supportedLocks() const override;
-
- QCamera::LockStatus lockStatus(QCamera::LockType lock) const override;
-
- void searchAndLock(QCamera::LockTypes locks) override;
- void unlock(QCamera::LockTypes locks) override;
-
-protected:
-#if GST_CHECK_VERSION(1, 2, 0)
- void timerEvent(QTimerEvent *event) override;
-#endif
-
-private slots:
- void updateFocusStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason);
-
-private:
-#if GST_CHECK_VERSION(1, 2, 0)
- bool isExposureLocked() const;
- void lockExposure(QCamera::LockChangeReason reason);
- void unlockExposure(QCamera::LockStatus status, QCamera::LockChangeReason reason);
-
- bool isWhiteBalanceLocked() const;
- void lockWhiteBalance(QCamera::LockChangeReason reason);
- void unlockWhiteBalance(QCamera::LockStatus status, QCamera::LockChangeReason reason);
-#endif
-
- CameraBinSession *m_session;
- CameraBinFocus *m_focus;
- QBasicTimer m_lockTimer;
- QCamera::LockTypes m_pendingLocks;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/gstreamer/camerabin/camerabinmetadata.cpp b/src/plugins/gstreamer/camerabin/camerabinmetadata.cpp
deleted file mode 100644
index afda2346d..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinmetadata.cpp
+++ /dev/null
@@ -1,234 +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 "camerabinmetadata.h"
-
-#include <QtMultimedia/qmediametadata.h>
-
-#include <gst/gst.h>
-#include <gst/gstversion.h>
-#include <private/qgstutils_p.h>
-
-#include <QDebug>
-
-QT_BEGIN_NAMESPACE
-
-namespace {
- struct QGStreamerMetaDataKey
- {
- QString qtName;
- const char *gstName;
- QVariant::Type type;
-
- QGStreamerMetaDataKey(const QString &qtn, const char *gstn, QVariant::Type t)
- : qtName(qtn)
- , gstName(gstn)
- , type(t)
- { }
- };
-}
-
-typedef QList<QGStreamerMetaDataKey> QGStreamerMetaDataKeys;
-Q_GLOBAL_STATIC(QGStreamerMetaDataKeys, metadataKeys)
-
-static const QGStreamerMetaDataKeys *qt_gstreamerMetaDataKeys()
-{
- if (metadataKeys->isEmpty()) {
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Title, GST_TAG_TITLE, QVariant::String));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::SubTitle, 0, QVariant::String));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Author, 0, QVariant::String));
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Comment, GST_TAG_COMMENT, QVariant::String));
-#if GST_CHECK_VERSION(0,10,31)
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Date, GST_TAG_DATE_TIME, QVariant::DateTime));
-#endif
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Description, GST_TAG_DESCRIPTION, QVariant::String));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Category, 0, QVariant::String));
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Genre, GST_TAG_GENRE, QVariant::String));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Year, 0, QVariant::Int));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::UserRating, , QVariant::Int));
-
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Language, GST_TAG_LANGUAGE_CODE, QVariant::String));
-
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Publisher, GST_TAG_ORGANIZATION, QVariant::String));
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Copyright, GST_TAG_COPYRIGHT, QVariant::String));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::ParentalRating, 0, QVariant::String));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::RatingOrganisation, 0, QVariant::String));
-
- // Media
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Size, 0, QVariant::Int));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::MediaType, 0, QVariant::String));
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Duration, GST_TAG_DURATION, QVariant::Int));
-
- // Audio
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::AudioBitRate, GST_TAG_BITRATE, QVariant::Int));
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::AudioCodec, GST_TAG_AUDIO_CODEC, QVariant::String));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::ChannelCount, 0, QVariant::Int));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::SampleRate, 0, QVariant::Int));
-
- // Music
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::AlbumTitle, GST_TAG_ALBUM, QVariant::String));
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::AlbumArtist, GST_TAG_ARTIST, QVariant::String));
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::ContributingArtist, GST_TAG_PERFORMER, QVariant::String));
-#if GST_CHECK_VERSION(0,10,19)
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Composer, GST_TAG_COMPOSER, QVariant::String));
-#endif
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Conductor, 0, QVariant::String));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Lyrics, 0, QVariant::String));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Mood, 0, QVariant::String));
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::TrackNumber, GST_TAG_TRACK_NUMBER, QVariant::Int));
-
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::CoverArtUrlSmall, 0, QVariant::String));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::CoverArtUrlLarge, 0, QVariant::String));
-
- // Image/Video
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Resolution, 0, QVariant::Size));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::PixelAspectRatio, 0, QVariant::Size));
-
- // Video
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::VideoFrameRate, 0, QVariant::String));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::VideoBitRate, 0, QVariant::Double));
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::VideoCodec, GST_TAG_VIDEO_CODEC, QVariant::String));
-
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::PosterUrl, 0, QVariant::String));
-
- // Movie
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::ChapterNumber, 0, QVariant::Int));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Director, 0, QVariant::String));
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::LeadPerformer, GST_TAG_PERFORMER, QVariant::String));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Writer, 0, QVariant::String));
-#if GST_CHECK_VERSION(0,10,30)
- // Photos
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::CameraManufacturer, GST_TAG_DEVICE_MANUFACTURER, QVariant::String));
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::CameraModel, GST_TAG_DEVICE_MODEL, QVariant::String));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Event, 0, QVariant::String));
- //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Subject, 0, QVariant::String));
-
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Orientation, GST_TAG_IMAGE_ORIENTATION, QVariant::String));
-
- // GPS
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::GPSLatitude, GST_TAG_GEO_LOCATION_LATITUDE, QVariant::Double));
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::GPSLongitude, GST_TAG_GEO_LOCATION_LONGITUDE, QVariant::Double));
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::GPSAltitude, GST_TAG_GEO_LOCATION_ELEVATION, QVariant::Double));
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::GPSTrack, GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION, QVariant::Double));
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::GPSSpeed, GST_TAG_GEO_LOCATION_MOVEMENT_SPEED, QVariant::Double));
- metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::GPSImgDirection, GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION, QVariant::Double));
-#endif
- }
-
- return metadataKeys;
-}
-
-CameraBinMetaData::CameraBinMetaData(QObject *parent)
- :QMetaDataWriterControl(parent)
-{
-}
-
-QVariant CameraBinMetaData::metaData(const QString &key) const
-{
-#if GST_CHECK_VERSION(0,10,30)
- if (key == QMediaMetaData::Orientation) {
- return QGstUtils::fromGStreamerOrientation(m_values.value(QByteArray(GST_TAG_IMAGE_ORIENTATION)));
- } else if (key == QMediaMetaData::GPSSpeed) {
- const double metersPerSec = m_values.value(QByteArray(GST_TAG_GEO_LOCATION_MOVEMENT_SPEED)).toDouble();
- return (metersPerSec * 3600) / 1000;
- }
-#endif
-
- const auto keys = *qt_gstreamerMetaDataKeys();
- for (const QGStreamerMetaDataKey &metadataKey : keys) {
- if (metadataKey.qtName == key)
- return m_values.value(QByteArray::fromRawData(metadataKey.gstName, qstrlen(metadataKey.gstName)));
- }
- return QVariant();
-}
-
-void CameraBinMetaData::setMetaData(const QString &key, const QVariant &value)
-{
- QVariant correctedValue = value;
-#if GST_CHECK_VERSION(0,10,30)
- if (value.isValid()) {
- if (key == QMediaMetaData::Orientation) {
- correctedValue = QGstUtils::toGStreamerOrientation(value);
- } else if (key == QMediaMetaData::GPSSpeed) {
- // kilometers per hour to meters per second.
- correctedValue = (value.toDouble() * 1000) / 3600;
- }
- }
-#endif
-
- const auto keys = *qt_gstreamerMetaDataKeys();
- for (const QGStreamerMetaDataKey &metadataKey : keys) {
- if (metadataKey.qtName == key) {
- const char *name = metadataKey.gstName;
-
- if (correctedValue.isValid()) {
- correctedValue.convert(metadataKey.type);
- m_values.insert(QByteArray::fromRawData(name, qstrlen(name)), correctedValue);
- } else {
- m_values.remove(QByteArray::fromRawData(name, qstrlen(name)));
- }
-
- emit QMetaDataWriterControl::metaDataChanged();
- emit metaDataChanged(m_values);
-
- return;
- }
- }
-}
-
-QStringList CameraBinMetaData::availableMetaData() const
-{
- static QMap<QByteArray, QString> keysMap;
- if (keysMap.isEmpty()) {
- const auto keys = *qt_gstreamerMetaDataKeys();
- for (const QGStreamerMetaDataKey &metadataKey : keys)
- keysMap[QByteArray(metadataKey.gstName)] = metadataKey.qtName;
- }
-
- QStringList res;
- for (auto it = m_values.keyBegin(), end = m_values.keyEnd(); it != end; ++it) {
- QString tag = keysMap.value(*it);
- if (!tag.isEmpty())
- res.append(tag);
- }
-
- return res;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinmetadata.h b/src/plugins/gstreamer/camerabin/camerabinmetadata.h
deleted file mode 100644
index 5fef7e388..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinmetadata.h
+++ /dev/null
@@ -1,71 +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 CAMERABINCAPTUREMETADATACONTROL_H
-#define CAMERABINCAPTUREMETADATACONTROL_H
-
-#include <qmetadatawritercontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class CameraBinMetaData : public QMetaDataWriterControl
-{
- Q_OBJECT
-public:
- CameraBinMetaData(QObject *parent);
- virtual ~CameraBinMetaData() {}
-
-
- bool isMetaDataAvailable() const override { return true; }
- bool isWritable() const override { return true; }
-
- QVariant metaData(const QString &key) const override;
- void setMetaData(const QString &key, const QVariant &value) override;
- QStringList availableMetaData() const override;
-
-Q_SIGNALS:
- void metaDataChanged(const QMap<QByteArray, QVariant>&);
-
-private:
- QMap<QByteArray, QVariant> m_values;
-};
-
-QT_END_NAMESPACE
-
-#endif // CAMERABINCAPTUREMETADATACONTROL_H
diff --git a/src/plugins/gstreamer/camerabin/camerabinrecorder.cpp b/src/plugins/gstreamer/camerabin/camerabinrecorder.cpp
deleted file mode 100644
index b3505231b..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinrecorder.cpp
+++ /dev/null
@@ -1,298 +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 "camerabinrecorder.h"
-#include "camerabincontrol.h"
-#include "camerabinresourcepolicy.h"
-#include "camerabinaudioencoder.h"
-#include "camerabinvideoencoder.h"
-#include "camerabincontainer.h"
-#include <QtCore/QDebug>
-
-
-QT_BEGIN_NAMESPACE
-
-CameraBinRecorder::CameraBinRecorder(CameraBinSession *session)
- :QMediaRecorderControl(session),
- m_session(session),
- m_state(QMediaRecorder::StoppedState),
- m_status(QMediaRecorder::UnloadedStatus)
-{
- connect(m_session, SIGNAL(statusChanged(QCamera::Status)), SLOT(updateStatus()));
- connect(m_session, SIGNAL(pendingStateChanged(QCamera::State)), SLOT(updateStatus()));
- connect(m_session, SIGNAL(busyChanged(bool)), SLOT(updateStatus()));
-
- connect(m_session, SIGNAL(durationChanged(qint64)), SIGNAL(durationChanged(qint64)));
- connect(m_session, SIGNAL(mutedChanged(bool)), this, SIGNAL(mutedChanged(bool)));
- connect(m_session->cameraControl()->resourcePolicy(), SIGNAL(canCaptureChanged()),
- this, SLOT(updateStatus()));
-}
-
-CameraBinRecorder::~CameraBinRecorder()
-{
-}
-
-QUrl CameraBinRecorder::outputLocation() const
-{
- return m_session->outputLocation();
-}
-
-bool CameraBinRecorder::setOutputLocation(const QUrl &sink)
-{
- m_session->setOutputLocation(sink);
- return true;
-}
-
-QMediaRecorder::State CameraBinRecorder::state() const
-{
- return m_state;
-}
-
-QMediaRecorder::Status CameraBinRecorder::status() const
-{
- return m_status;
-}
-
-void CameraBinRecorder::updateStatus()
-{
- QCamera::Status sessionStatus = m_session->status();
-
- QMediaRecorder::State oldState = m_state;
- QMediaRecorder::Status oldStatus = m_status;
-
- if (sessionStatus == QCamera::ActiveStatus &&
- m_session->captureMode().testFlag(QCamera::CaptureVideo)) {
-
- if (!m_session->cameraControl()->resourcePolicy()->canCapture()) {
- m_status = QMediaRecorder::UnavailableStatus;
- m_state = QMediaRecorder::StoppedState;
- m_session->stopVideoRecording();
- } else if (m_state == QMediaRecorder::RecordingState) {
- m_status = QMediaRecorder::RecordingStatus;
- } else {
- m_status = m_session->isBusy() ?
- QMediaRecorder::FinalizingStatus :
- QMediaRecorder::LoadedStatus;
- }
- } else {
- if (m_state == QMediaRecorder::RecordingState) {
- m_state = QMediaRecorder::StoppedState;
- m_session->stopVideoRecording();
- }
- m_status = m_session->pendingState() == QCamera::ActiveState
- && m_session->captureMode().testFlag(QCamera::CaptureVideo)
- ? QMediaRecorder::LoadingStatus
- : QMediaRecorder::UnloadedStatus;
- }
-
- if (m_state != oldState)
- emit stateChanged(m_state);
-
- if (m_status != oldStatus)
- emit statusChanged(m_status);
-}
-
-qint64 CameraBinRecorder::duration() const
-{
- return m_session->duration();
-}
-
-
-void CameraBinRecorder::applySettings()
-{
-#if QT_CONFIG(gstreamer_encodingprofiles)
- CameraBinContainer *containerControl = m_session->mediaContainerControl();
- CameraBinAudioEncoder *audioEncoderControl = m_session->audioEncodeControl();
- CameraBinVideoEncoder *videoEncoderControl = m_session->videoEncodeControl();
-
- containerControl->resetActualContainerFormat();
- audioEncoderControl->resetActualSettings();
- videoEncoderControl->resetActualSettings();
-
- //encodebin doesn't like the encoding profile with ANY caps,
- //if container and codecs are not specified,
- //try to find a commonly used supported combination
- if (containerControl->containerFormat().isEmpty() &&
- audioEncoderControl->audioSettings().codec().isEmpty() &&
- videoEncoderControl->videoSettings().codec().isEmpty()) {
-
- QList<QStringList> candidates;
-
- // By order of preference
-
- // .mp4 (h264, AAC)
- candidates.append(QStringList() << "video/quicktime, variant=(string)iso" << "video/x-h264" << "audio/mpeg, mpegversion=(int)4");
-
- // .mp4 (h264, AC3)
- candidates.append(QStringList() << "video/quicktime, variant=(string)iso" << "video/x-h264" << "audio/x-ac3");
-
- // .mp4 (h264, MP3)
- candidates.append(QStringList() << "video/quicktime, variant=(string)iso" << "video/x-h264" << "audio/mpeg, mpegversion=(int)1, layer=(int)3");
-
- // .mkv (h264, AAC)
- candidates.append(QStringList() << "video/x-matroska" << "video/x-h264" << "audio/mpeg, mpegversion=(int)4");
-
- // .mkv (h264, AC3)
- candidates.append(QStringList() << "video/x-matroska" << "video/x-h264" << "audio/x-ac3");
-
- // .mkv (h264, MP3)
- candidates.append(QStringList() << "video/x-matroska" << "video/x-h264" << "audio/mpeg, mpegversion=(int)1, layer=(int)3");
-
- // .mov (h264, AAC)
- candidates.append(QStringList() << "video/quicktime" << "video/x-h264" << "audio/mpeg, mpegversion=(int)4");
-
- // .mov (h264, MP3)
- candidates.append(QStringList() << "video/quicktime" << "video/x-h264" << "audio/mpeg, mpegversion=(int)1, layer=(int)3");
-
- // .webm (VP8, Vorbis)
- candidates.append(QStringList() << "video/webm" << "video/x-vp8" << "audio/x-vorbis");
-
- // .ogg (Theora, Vorbis)
- candidates.append(QStringList() << "application/ogg" << "video/x-theora" << "audio/x-vorbis");
-
- // .avi (DivX, MP3)
- candidates.append(QStringList() << "video/x-msvideo" << "video/x-divx" << "audio/mpeg, mpegversion=(int)1, layer=(int)3");
-
- for (const QStringList &candidate : qAsConst(candidates)) {
- if (containerControl->supportedContainers().contains(candidate[0]) &&
- videoEncoderControl->supportedVideoCodecs().contains(candidate[1]) &&
- audioEncoderControl->supportedAudioCodecs().contains(candidate[2])) {
- containerControl->setActualContainerFormat(candidate[0]);
-
- QVideoEncoderSettings videoSettings = videoEncoderControl->videoSettings();
- videoSettings.setCodec(candidate[1]);
- videoEncoderControl->setActualVideoSettings(videoSettings);
-
- QAudioEncoderSettings audioSettings = audioEncoderControl->audioSettings();
- audioSettings.setCodec(candidate[2]);
- audioEncoderControl->setActualAudioSettings(audioSettings);
-
- break;
- }
- }
- }
-#endif
-}
-
-#if QT_CONFIG(gstreamer_encodingprofiles)
-
-GstEncodingContainerProfile *CameraBinRecorder::videoProfile()
-{
- GstEncodingContainerProfile *containerProfile = m_session->mediaContainerControl()->createProfile();
-
- if (containerProfile) {
- GstEncodingProfile *audioProfile = m_session->audioEncodeControl()->createProfile();
- GstEncodingProfile *videoProfile = m_session->videoEncodeControl()->createProfile();
-
- if (audioProfile) {
- if (!gst_encoding_container_profile_add_profile(containerProfile, audioProfile))
- gst_encoding_profile_unref(audioProfile);
- }
- if (videoProfile) {
- if (!gst_encoding_container_profile_add_profile(containerProfile, videoProfile))
- gst_encoding_profile_unref(videoProfile);
- }
- }
-
- return containerProfile;
-}
-
-#endif
-
-void CameraBinRecorder::setState(QMediaRecorder::State state)
-{
- if (m_state == state)
- return;
-
- QMediaRecorder::State oldState = m_state;
- QMediaRecorder::Status oldStatus = m_status;
-
- switch (state) {
- case QMediaRecorder::StoppedState:
- m_state = state;
- m_status = QMediaRecorder::FinalizingStatus;
- m_session->stopVideoRecording();
- break;
- case QMediaRecorder::PausedState:
- emit error(QMediaRecorder::ResourceError, tr("QMediaRecorder::pause() is not supported by camerabin2."));
- break;
- case QMediaRecorder::RecordingState:
-
- if (m_session->status() != QCamera::ActiveStatus) {
- emit error(QMediaRecorder::ResourceError, tr("Service has not been started"));
- } else if (!m_session->cameraControl()->resourcePolicy()->canCapture()) {
- emit error(QMediaRecorder::ResourceError, tr("Recording permissions are not available"));
- } else {
- m_session->recordVideo();
- m_state = state;
- m_status = QMediaRecorder::RecordingStatus;
- emit actualLocationChanged(m_session->outputLocation());
- }
- }
-
- if (m_state != oldState)
- emit stateChanged(m_state);
-
- if (m_status != oldStatus)
- emit statusChanged(m_status);
-}
-
-bool CameraBinRecorder::isMuted() const
-{
- return m_session->isMuted();
-}
-
-qreal CameraBinRecorder::volume() const
-{
- return 1.0;
-}
-
-void CameraBinRecorder::setMuted(bool muted)
-{
- m_session->setMuted(muted);
-}
-
-void CameraBinRecorder::setVolume(qreal volume)
-{
- if (!qFuzzyCompare(volume, qreal(1.0)))
- qWarning() << "Media service doesn't support recorder audio gain.";
-}
-
-QT_END_NAMESPACE
-
diff --git a/src/plugins/gstreamer/camerabin/camerabinrecorder.h b/src/plugins/gstreamer/camerabin/camerabinrecorder.h
deleted file mode 100644
index 8bd922fb8..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinrecorder.h
+++ /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$
-**
-****************************************************************************/
-
-
-#ifndef CAMERABINRECORDERCONTROL_H
-#define CAMERABINRECORDERCONTROL_H
-
-#include <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include <qmediarecordercontrol.h>
-#include "camerabinsession.h"
-
-#if QT_CONFIG(gstreamer_encodingprofiles)
-#include <gst/pbutils/encoding-profile.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class CameraBinRecorder : public QMediaRecorderControl
-{
- Q_OBJECT
-
-public:
- CameraBinRecorder(CameraBinSession *session);
- virtual ~CameraBinRecorder();
-
- QUrl outputLocation() const override;
- bool setOutputLocation(const QUrl &sink) override;
-
- QMediaRecorder::State state() const override;
- QMediaRecorder::Status status() const override;
-
- qint64 duration() const override;
-
- bool isMuted() const override;
- qreal volume() const override;
-
- void applySettings() override;
-
-#if QT_CONFIG(gstreamer_encodingprofiles)
- GstEncodingContainerProfile *videoProfile();
-#endif
-
-public slots:
- void setState(QMediaRecorder::State state) override;
- void setMuted(bool) override;
- void setVolume(qreal volume) override;
-
- void updateStatus();
-
-private:
- CameraBinSession *m_session;
- QMediaRecorder::State m_state;
- QMediaRecorder::Status m_status;
-};
-
-QT_END_NAMESPACE
-
-#endif // CAMERABINCAPTURECORNTROL_H
diff --git a/src/plugins/gstreamer/camerabin/camerabinresourcepolicy.cpp b/src/plugins/gstreamer/camerabin/camerabinresourcepolicy.cpp
deleted file mode 100644
index 6da86e569..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinresourcepolicy.cpp
+++ /dev/null
@@ -1,242 +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 "QtMultimedia/private/qtmultimediaglobal_p.h"
-#include "camerabinresourcepolicy.h"
-//#define DEBUG_RESOURCE_POLICY
-#include <QtCore/qdebug.h>
-#include <QtCore/qset.h>
-
-#if QT_CONFIG(resourcepolicy)
-#include <policy/resource.h>
-#include <policy/resources.h>
-#include <policy/resource-set.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-CamerabinResourcePolicy::CamerabinResourcePolicy(QObject *parent) :
- QObject(parent),
- m_resourceSet(NoResources),
- m_releasingResources(false),
- m_canCapture(false)
-{
-#if QT_CONFIG(resourcepolicy)
- //loaded resource set is also kept requested for image and video capture sets
- m_resource = new ResourcePolicy::ResourceSet("camera");
- m_resource->setAlwaysReply();
- m_resource->initAndConnect();
-
- connect(m_resource, SIGNAL(resourcesGranted(QList<ResourcePolicy::ResourceType>)),
- SLOT(handleResourcesGranted()));
- connect(m_resource, SIGNAL(resourcesDenied()), SIGNAL(resourcesDenied()));
- connect(m_resource, SIGNAL(lostResources()), SLOT(handleResourcesLost()));
- connect(m_resource, SIGNAL(resourcesReleased()), SLOT(handleResourcesReleased()));
- connect(m_resource, SIGNAL(resourcesBecameAvailable(QList<ResourcePolicy::ResourceType>)),
- this, SLOT(resourcesAvailable()));
- connect(m_resource, SIGNAL(updateOK()), this, SLOT(updateCanCapture()));
-#endif
-}
-
-CamerabinResourcePolicy::~CamerabinResourcePolicy()
-{
-#if QT_CONFIG(resourcepolicy)
- //ensure the resources are released
- if (m_resourceSet != NoResources)
- setResourceSet(NoResources);
-
- //don't delete the resource set until resources are released
- if (m_releasingResources) {
- m_resource->connect(m_resource, SIGNAL(resourcesReleased()),
- SLOT(deleteLater()));
- } else {
- delete m_resource;
- m_resource = 0;
- }
-#endif
-}
-
-CamerabinResourcePolicy::ResourceSet CamerabinResourcePolicy::resourceSet() const
-{
- return m_resourceSet;
-}
-
-void CamerabinResourcePolicy::setResourceSet(CamerabinResourcePolicy::ResourceSet set)
-{
- CamerabinResourcePolicy::ResourceSet oldSet = m_resourceSet;
- m_resourceSet = set;
-
-#ifdef DEBUG_RESOURCE_POLICY
- qDebug() << Q_FUNC_INFO << set;
-#endif
-
-#if QT_CONFIG(resourcepolicy)
- QSet<ResourcePolicy::ResourceType> requestedTypes;
-
- switch (set) {
- case NoResources:
- break;
- case LoadedResources:
- requestedTypes << ResourcePolicy::LensCoverType //to detect lens cover is opened/closed
- << ResourcePolicy::VideoRecorderType; //to open camera device
- break;
- case ImageCaptureResources:
- requestedTypes << ResourcePolicy::LensCoverType
- << ResourcePolicy::VideoPlaybackType
- << ResourcePolicy::VideoRecorderType
- << ResourcePolicy::LedsType;
- break;
- case VideoCaptureResources:
- requestedTypes << ResourcePolicy::LensCoverType
- << ResourcePolicy::VideoPlaybackType
- << ResourcePolicy::VideoRecorderType
- << ResourcePolicy::AudioPlaybackType
- << ResourcePolicy::AudioRecorderType
- << ResourcePolicy::LedsType;
- break;
- }
-
- QSet<ResourcePolicy::ResourceType> currentTypes;
- const auto resources = m_resource->resources();
- currentTypes.reserve(resources.size());
- for (ResourcePolicy::Resource *resource : resources)
- currentTypes << resource->type();
-
- const auto diffCurrentWithRequested = currentTypes - requestedTypes;
- for (ResourcePolicy::ResourceType resourceType : diffCurrentWithRequested)
- m_resource->deleteResource(resourceType);
-
- const auto diffRequestedWithCurrent = requestedTypes - currentTypes;
- for (ResourcePolicy::ResourceType resourceType : diffRequestedWithCurrent) {
- if (resourceType == ResourcePolicy::LensCoverType) {
- ResourcePolicy::LensCoverResource *lensCoverResource = new ResourcePolicy::LensCoverResource;
- lensCoverResource->setOptional(true);
- m_resource->addResourceObject(lensCoverResource);
- } else if (resourceType == ResourcePolicy::AudioPlaybackType) {
- ResourcePolicy::Resource *resource = new ResourcePolicy::AudioResource;
- resource->setOptional(true);
- m_resource->addResourceObject(resource);
- } else if (resourceType == ResourcePolicy::AudioRecorderType) {
- ResourcePolicy::Resource *resource = new ResourcePolicy::AudioRecorderResource;
- resource->setOptional(true);
- m_resource->addResourceObject(resource);
- } else {
- m_resource->addResource(resourceType);
- }
- }
-
- m_resource->update();
- if (set != NoResources) {
- m_resource->acquire();
- } else {
- if (oldSet != NoResources) {
- m_releasingResources = true;
- m_resource->release();
- }
- }
-#else
- Q_UNUSED(oldSet);
- updateCanCapture();
-#endif
-}
-
-bool CamerabinResourcePolicy::isResourcesGranted() const
-{
-#if QT_CONFIG(resourcepolicy)
- const auto resources = m_resource->resources();
- for (ResourcePolicy::Resource *resource : resources)
- if (!resource->isOptional() && !resource->isGranted())
- return false;
-#endif
- return true;
-}
-
-void CamerabinResourcePolicy::handleResourcesLost()
-{
- updateCanCapture();
- emit resourcesLost();
-}
-
-void CamerabinResourcePolicy::handleResourcesGranted()
-{
- updateCanCapture();
- emit resourcesGranted();
-}
-
-void CamerabinResourcePolicy::handleResourcesReleased()
-{
-#if QT_CONFIG(resourcepolicy)
-#ifdef DEBUG_RESOURCE_POLICY
- qDebug() << Q_FUNC_INFO;
-#endif
- m_releasingResources = false;
-#endif
- updateCanCapture();
-}
-
-void CamerabinResourcePolicy::resourcesAvailable()
-{
-#if QT_CONFIG(resourcepolicy)
- if (m_resourceSet != NoResources) {
- m_resource->acquire();
- }
-#endif
-}
-
-bool CamerabinResourcePolicy::canCapture() const
-{
- return m_canCapture;
-}
-
-void CamerabinResourcePolicy::updateCanCapture()
-{
- const bool wasAbleToRecord = m_canCapture;
- m_canCapture = (m_resourceSet == VideoCaptureResources) || (m_resourceSet == ImageCaptureResources);
-#if QT_CONFIG(resourcepolicy)
- const auto resources = m_resource->resources();
- for (ResourcePolicy::Resource *resource : resources) {
- if (resource->type() != ResourcePolicy::LensCoverType)
- m_canCapture = m_canCapture && resource->isGranted();
- }
-#endif
- if (wasAbleToRecord != m_canCapture)
- emit canCaptureChanged();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinresourcepolicy.h b/src/plugins/gstreamer/camerabin/camerabinresourcepolicy.h
deleted file mode 100644
index c54595bcc..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinresourcepolicy.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 CAMERARESOURCEPOLICY_H
-#define CAMERARESOURCEPOLICY_H
-
-#include <QtCore/qobject.h>
-
-namespace ResourcePolicy {
-class ResourceSet;
-};
-
-QT_BEGIN_NAMESPACE
-
-class CamerabinResourcePolicy : public QObject
-{
- Q_OBJECT
-public:
- enum ResourceSet {
- NoResources,
- LoadedResources,
- ImageCaptureResources,
- VideoCaptureResources
- };
-
- CamerabinResourcePolicy(QObject *parent);
- ~CamerabinResourcePolicy();
-
- ResourceSet resourceSet() const;
- void setResourceSet(ResourceSet set);
-
- bool isResourcesGranted() const;
-
- bool canCapture() const;
-
-Q_SIGNALS:
- void resourcesDenied();
- void resourcesGranted();
- void resourcesLost();
- void canCaptureChanged();
-
-private Q_SLOTS:
- void handleResourcesLost();
- void handleResourcesGranted();
- void handleResourcesReleased();
- void resourcesAvailable();
- void updateCanCapture();
-
-
-private:
- ResourceSet m_resourceSet;
- ResourcePolicy::ResourceSet *m_resource;
- bool m_releasingResources;
- bool m_canCapture;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/gstreamer/camerabin/camerabinservice.cpp b/src/plugins/gstreamer/camerabin/camerabinservice.cpp
deleted file mode 100644
index a68ecfdbe..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinservice.cpp
+++ /dev/null
@@ -1,271 +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 <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include "camerabinservice.h"
-#include "camerabinsession.h"
-#include "camerabinrecorder.h"
-#include "camerabincontainer.h"
-#include "camerabinaudioencoder.h"
-#include "camerabinvideoencoder.h"
-#include "camerabinimageencoder.h"
-#include "camerabincontrol.h"
-#include "camerabinmetadata.h"
-#include "camerabininfocontrol.h"
-
-#if QT_CONFIG(gstreamer_photography)
-#include "camerabinexposure.h"
-#include "camerabinflash.h"
-#include "camerabinfocus.h"
-#include "camerabinlocks.h"
-#endif
-
-#include "camerabinimagecapture.h"
-#include "camerabinimageprocessing.h"
-#include "camerabincapturebufferformat.h"
-#include "camerabincapturedestination.h"
-#include "camerabinviewfindersettings.h"
-#include "camerabinviewfindersettings2.h"
-#include "camerabinzoom.h"
-#include <private/qgstreamerbushelper_p.h>
-#include <private/qgstutils_p.h>
-
-#include <private/qgstreameraudioinputselector_p.h>
-#include <private/qgstreamervideoinputdevicecontrol_p.h>
-
-#if defined(HAVE_WIDGETS)
-#include <private/qgstreamervideowidget_p.h>
-#endif
-#include <private/qgstreamervideowindow_p.h>
-#include <private/qgstreamervideorenderer_p.h>
-#include <private/qmediaserviceprovider_p.h>
-
-#include <QtCore/qdebug.h>
-
-QT_BEGIN_NAMESPACE
-
-CameraBinService::CameraBinService(GstElementFactory *sourceFactory, QObject *parent):
- QMediaService(parent),
- m_cameraInfoControl(0),
- m_viewfinderSettingsControl(0),
- m_viewfinderSettingsControl2(0)
-{
- m_captureSession = 0;
- m_metaDataControl = 0;
-
- m_audioInputSelector = 0;
- m_videoInputDevice = 0;
-
- m_videoOutput = 0;
- m_videoRenderer = 0;
- m_videoWindow = 0;
-#if defined(HAVE_WIDGETS)
- m_videoWidgetControl = 0;
-#endif
- m_imageCaptureControl = 0;
-
- m_captureSession = new CameraBinSession(sourceFactory, this);
- m_videoInputDevice = new QGstreamerVideoInputDeviceControl(sourceFactory, m_captureSession);
- m_imageCaptureControl = new CameraBinImageCapture(m_captureSession);
-
- connect(m_videoInputDevice, SIGNAL(selectedDeviceChanged(QString)),
- m_captureSession, SLOT(setDevice(QString)));
-
- if (m_videoInputDevice->deviceCount())
- m_captureSession->setDevice(m_videoInputDevice->deviceName(m_videoInputDevice->selectedDevice()));
-
- m_videoRenderer = new QGstreamerVideoRenderer(this);
-
- m_videoWindow = new QGstreamerVideoWindow(this);
- // If the GStreamer video sink is not available, don't provide the video window control since
- // it won't work anyway.
- if (!m_videoWindow->videoSink()) {
- delete m_videoWindow;
- m_videoWindow = 0;
- }
-#if defined(HAVE_WIDGETS)
- m_videoWidgetControl = new QGstreamerVideoWidgetControl(this);
-
- // If the GStreamer video sink is not available, don't provide the video widget control since
- // it won't work anyway. QVideoWidget will fall back to QVideoRendererControl in that case.
- if (!m_videoWidgetControl->videoSink()) {
- delete m_videoWidgetControl;
- m_videoWidgetControl = 0;
- }
-#endif
-
- m_audioInputSelector = new QGstreamerAudioInputSelector(this);
- connect(m_audioInputSelector, SIGNAL(activeInputChanged(QString)), m_captureSession, SLOT(setCaptureDevice(QString)));
-
- if (m_captureSession && m_audioInputSelector->availableInputs().size() > 0)
- m_captureSession->setCaptureDevice(m_audioInputSelector->defaultInput());
-
- m_metaDataControl = new CameraBinMetaData(this);
- connect(m_metaDataControl, SIGNAL(metaDataChanged(QMap<QByteArray,QVariant>)),
- m_captureSession, SLOT(setMetaData(QMap<QByteArray,QVariant>)));
-}
-
-CameraBinService::~CameraBinService()
-{
-}
-
-QMediaControl *CameraBinService::requestControl(const char *name)
-{
- if (!m_captureSession)
- return 0;
-
- if (!m_videoOutput) {
- if (qstrcmp(name, QVideoRendererControl_iid) == 0) {
- m_videoOutput = m_videoRenderer;
- } else if (qstrcmp(name, QVideoWindowControl_iid) == 0) {
- m_videoOutput = m_videoWindow;
- }
-#if defined(HAVE_WIDGETS)
- else if (qstrcmp(name, QVideoWidgetControl_iid) == 0) {
- m_videoOutput = m_videoWidgetControl;
- }
-#endif
-
- if (m_videoOutput) {
- m_captureSession->setViewfinder(m_videoOutput);
- return m_videoOutput;
- }
- }
-
- if (qstrcmp(name, QMediaVideoProbeControl_iid) == 0)
- return m_captureSession->videoProbe();
-
- if (qstrcmp(name,QAudioInputSelectorControl_iid) == 0)
- return m_audioInputSelector;
-
- if (qstrcmp(name,QVideoDeviceSelectorControl_iid) == 0)
- return m_videoInputDevice;
-
- if (qstrcmp(name,QMediaRecorderControl_iid) == 0)
- return m_captureSession->recorderControl();
-
- if (qstrcmp(name,QAudioEncoderSettingsControl_iid) == 0)
- return m_captureSession->audioEncodeControl();
-
- if (qstrcmp(name,QVideoEncoderSettingsControl_iid) == 0)
- return m_captureSession->videoEncodeControl();
-
- if (qstrcmp(name,QImageEncoderControl_iid) == 0)
- return m_captureSession->imageEncodeControl();
-
-
- if (qstrcmp(name,QMediaContainerControl_iid) == 0)
- return m_captureSession->mediaContainerControl();
-
- if (qstrcmp(name,QCameraControl_iid) == 0)
- return m_captureSession->cameraControl();
-
- if (qstrcmp(name,QMetaDataWriterControl_iid) == 0)
- return m_metaDataControl;
-
- if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0)
- return m_imageCaptureControl;
-
-#if QT_CONFIG(gstreamer_photography)
- if (qstrcmp(name, QCameraExposureControl_iid) == 0)
- return m_captureSession->cameraExposureControl();
-
- if (qstrcmp(name, QCameraFlashControl_iid) == 0)
- return m_captureSession->cameraFlashControl();
-
- if (qstrcmp(name, QCameraFocusControl_iid) == 0)
- return m_captureSession->cameraFocusControl();
-
- if (qstrcmp(name, QCameraLocksControl_iid) == 0)
- return m_captureSession->cameraLocksControl();
-#endif
-
- if (qstrcmp(name, QCameraZoomControl_iid) == 0)
- return m_captureSession->cameraZoomControl();
-
- if (qstrcmp(name, QCameraImageProcessingControl_iid) == 0)
- return m_captureSession->imageProcessingControl();
-
- if (qstrcmp(name, QCameraCaptureDestinationControl_iid) == 0)
- return m_captureSession->captureDestinationControl();
-
- if (qstrcmp(name, QCameraCaptureBufferFormatControl_iid) == 0)
- return m_captureSession->captureBufferFormatControl();
-
- if (qstrcmp(name, QCameraViewfinderSettingsControl_iid) == 0) {
- if (!m_viewfinderSettingsControl)
- m_viewfinderSettingsControl = new CameraBinViewfinderSettings(m_captureSession);
- return m_viewfinderSettingsControl;
- }
-
- if (qstrcmp(name, QCameraViewfinderSettingsControl2_iid) == 0) {
- if (!m_viewfinderSettingsControl2)
- m_viewfinderSettingsControl2 = new CameraBinViewfinderSettings2(m_captureSession);
- return m_viewfinderSettingsControl2;
- }
-
- if (qstrcmp(name, QCameraInfoControl_iid) == 0) {
- if (!m_cameraInfoControl)
- m_cameraInfoControl = new CameraBinInfoControl(m_captureSession->sourceFactory(), this);
- return m_cameraInfoControl;
- }
-
- return 0;
-}
-
-void CameraBinService::releaseControl(QMediaControl *control)
-{
- if (control && control == m_videoOutput) {
- m_videoOutput = 0;
- m_captureSession->setViewfinder(0);
- }
-}
-
-bool CameraBinService::isCameraBinAvailable()
-{
- GstElementFactory *factory = gst_element_factory_find(QT_GSTREAMER_CAMERABIN_ELEMENT_NAME);
- if (factory) {
- gst_object_unref(GST_OBJECT(factory));
- return true;
- }
-
- return false;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinservice.h b/src/plugins/gstreamer/camerabin/camerabinservice.h
deleted file mode 100644
index 44afef25f..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinservice.h
+++ /dev/null
@@ -1,104 +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 CAMERABINCAPTURESERVICE_H
-#define CAMERABINCAPTURESERVICE_H
-
-#include <qmediaservice.h>
-
-#include <gst/gst.h>
-
-QT_BEGIN_NAMESPACE
-class QAudioInputSelectorControl;
-class QVideoDeviceSelectorControl;
-
-
-class CameraBinSession;
-class CameraBinControl;
-class QGstreamerMessage;
-class QGstreamerBusHelper;
-class QGstreamerVideoRenderer;
-class QGstreamerVideoWindow;
-class QGstreamerVideoWidgetControl;
-class QGstreamerElementFactory;
-class CameraBinMetaData;
-class CameraBinImageCapture;
-class CameraBinMetaData;
-class CameraBinViewfinderSettings;
-class CameraBinViewfinderSettings2;
-
-class CameraBinService : public QMediaService
-{
- Q_OBJECT
-
-public:
- CameraBinService(GstElementFactory *sourceFactory, QObject *parent = 0);
- virtual ~CameraBinService();
-
- QMediaControl *requestControl(const char *name) override;
- void releaseControl(QMediaControl *) override;
-
- static bool isCameraBinAvailable();
-
-private:
- void setAudioPreview(GstElement*);
-
- CameraBinSession *m_captureSession;
- CameraBinMetaData *m_metaDataControl;
-
- QAudioInputSelectorControl *m_audioInputSelector;
- QVideoDeviceSelectorControl *m_videoInputDevice;
-
- QMediaControl *m_videoOutput;
-
- QMediaControl *m_videoRenderer;
- QGstreamerVideoWindow *m_videoWindow;
-#if defined(HAVE_WIDGETS)
- QGstreamerVideoWidgetControl *m_videoWidgetControl;
-#endif
- CameraBinImageCapture *m_imageCaptureControl;
- QMediaControl *m_cameraInfoControl;
-
- CameraBinViewfinderSettings *m_viewfinderSettingsControl;
- CameraBinViewfinderSettings2 *m_viewfinderSettingsControl2;
-};
-
-QT_END_NAMESPACE
-
-#endif // CAMERABINCAPTURESERVICE_H
diff --git a/src/plugins/gstreamer/camerabin/camerabinserviceplugin.cpp b/src/plugins/gstreamer/camerabin/camerabinserviceplugin.cpp
deleted file mode 100644
index 4f45a0a2f..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinserviceplugin.cpp
+++ /dev/null
@@ -1,163 +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 <QtCore/qstring.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/QDir>
-#include <QtCore/QDebug>
-
-#include "camerabinserviceplugin.h"
-
-#include "camerabinservice.h"
-#include <private/qgstutils_p.h>
-
-QT_BEGIN_NAMESPACE
-
-template <typename T, int N> static int lengthOf(const T(&)[N]) { return N; }
-
-CameraBinServicePlugin::CameraBinServicePlugin()
- : m_sourceFactory(0)
-{
-}
-
-CameraBinServicePlugin::~CameraBinServicePlugin()
-{
- if (m_sourceFactory)
- gst_object_unref(GST_OBJECT(m_sourceFactory));
-}
-
-QMediaService* CameraBinServicePlugin::create(const QString &key)
-{
- QGstUtils::initializeGst();
-
- if (key == QLatin1String(Q_MEDIASERVICE_CAMERA)) {
- if (!CameraBinService::isCameraBinAvailable()) {
- guint major, minor, micro, nano;
- gst_version(&major, &minor, &micro, &nano);
- qWarning("Error: cannot create camera service, the 'camerabin' plugin is missing for "
- "GStreamer %u.%u."
- "\nPlease install the 'bad' GStreamer plugin package.",
- major, minor);
- return nullptr;
- }
-
- return new CameraBinService(sourceFactory());
- }
-
- qWarning() << "Gstreamer camerabin service plugin: unsupported key:" << key;
- return 0;
-}
-
-void CameraBinServicePlugin::release(QMediaService *service)
-{
- delete service;
-}
-
-QMediaServiceProviderHint::Features CameraBinServicePlugin::supportedFeatures(
- const QByteArray &service) const
-{
- if (service == Q_MEDIASERVICE_CAMERA)
- return QMediaServiceProviderHint::VideoSurface;
-
- return QMediaServiceProviderHint::Features();
-}
-
-QByteArray CameraBinServicePlugin::defaultDevice(const QByteArray &service) const
-{
- return service == Q_MEDIASERVICE_CAMERA
- ? QGstUtils::enumerateCameras(sourceFactory()).value(0).name.toUtf8()
- : QByteArray();
-}
-
-QList<QByteArray> CameraBinServicePlugin::devices(const QByteArray &service) const
-{
-
- return service == Q_MEDIASERVICE_CAMERA
- ? QGstUtils::cameraDevices(m_sourceFactory)
- : QList<QByteArray>();
-}
-
-QString CameraBinServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &deviceName)
-{
- return service == Q_MEDIASERVICE_CAMERA
- ? QGstUtils::cameraDescription(deviceName, m_sourceFactory)
- : QString();
-}
-
-QVariant CameraBinServicePlugin::deviceProperty(const QByteArray &service, const QByteArray &device, const QByteArray &property)
-{
- Q_UNUSED(service);
- Q_UNUSED(device);
- Q_UNUSED(property);
- return QVariant();
-}
-
-QCamera::Position CameraBinServicePlugin::cameraPosition(const QByteArray &deviceName) const
-{
- return QGstUtils::cameraPosition(deviceName, m_sourceFactory);
-}
-
-int CameraBinServicePlugin::cameraOrientation(const QByteArray &deviceName) const
-{
- return QGstUtils::cameraOrientation(deviceName, m_sourceFactory);
-}
-
-GstElementFactory *CameraBinServicePlugin::sourceFactory() const
-{
- if (!m_sourceFactory) {
- GstElementFactory *factory = 0;
- const QByteArray envCandidate = qgetenv("QT_GSTREAMER_CAMERABIN_SRC");
- if (!envCandidate.isEmpty())
- factory = gst_element_factory_find(envCandidate.constData());
-
- static const char *candidates[] = { "subdevsrc", "wrappercamerabinsrc" };
- for (int i = 0; !factory && i < lengthOf(candidates); ++i)
- factory = gst_element_factory_find(candidates[i]);
-
- if (factory) {
- m_sourceFactory = GST_ELEMENT_FACTORY(gst_plugin_feature_load(
- GST_PLUGIN_FEATURE(factory)));
- gst_object_unref((GST_OBJECT(factory)));
- }
- }
-
- return m_sourceFactory;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinserviceplugin.h b/src/plugins/gstreamer/camerabin/camerabinserviceplugin.h
deleted file mode 100644
index b31e44459..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinserviceplugin.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 CAMERABINSERVICEPLUGIN_H
-#define CAMERABINSERVICEPLUGIN_H
-
-#include <qmediaserviceproviderplugin.h>
-#include <private/qgstreamervideoinputdevicecontrol_p.h>
-
-#include <gst/gst.h>
-
-QT_BEGIN_NAMESPACE
-
-class CameraBinServicePlugin
- : public QMediaServiceProviderPlugin
- , public QMediaServiceSupportedDevicesInterface
- , public QMediaServiceDefaultDeviceInterface
- , public QMediaServiceFeaturesInterface
- , public QMediaServiceCameraInfoInterface
-{
- Q_OBJECT
- Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
- Q_INTERFACES(QMediaServiceDefaultDeviceInterface)
- Q_INTERFACES(QMediaServiceFeaturesInterface)
- Q_INTERFACES(QMediaServiceCameraInfoInterface)
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "camerabin.json")
-public:
- CameraBinServicePlugin();
- ~CameraBinServicePlugin();
-
- QMediaService* create(const QString &key) override;
- void release(QMediaService *service) override;
-
- QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const override;
-
- QByteArray defaultDevice(const QByteArray &service) const override;
- QList<QByteArray> devices(const QByteArray &service) const override;
- QString deviceDescription(const QByteArray &service, const QByteArray &device) override;
- QVariant deviceProperty(const QByteArray &service, const QByteArray &device, const QByteArray &property);
-
- QCamera::Position cameraPosition(const QByteArray &device) const override;
- int cameraOrientation(const QByteArray &device) const override;
-
-private:
- GstElementFactory *sourceFactory() const;
-
- mutable GstElementFactory *m_sourceFactory;
-};
-
-QT_END_NAMESPACE
-
-#endif // QGSTREAMERCAPTURESERVICEPLUGIN_H
diff --git a/src/plugins/gstreamer/camerabin/camerabinsession.cpp b/src/plugins/gstreamer/camerabin/camerabinsession.cpp
deleted file mode 100644
index 22f5fb116..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinsession.cpp
+++ /dev/null
@@ -1,1587 +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 <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include "camerabinsession.h"
-#include "camerabincontrol.h"
-#include "camerabinrecorder.h"
-#include "camerabincontainer.h"
-#include "camerabinaudioencoder.h"
-#include "camerabinvideoencoder.h"
-#include "camerabinimageencoder.h"
-
-#if QT_CONFIG(gstreamer_photography)
-#include "camerabinexposure.h"
-#include "camerabinflash.h"
-#include "camerabinfocus.h"
-#include "camerabinlocks.h"
-#endif
-
-#include "camerabinzoom.h"
-#include "camerabinimageprocessing.h"
-#include "camerabinviewfindersettings.h"
-
-#include "camerabincapturedestination.h"
-#include "camerabincapturebufferformat.h"
-#include <private/qgstreamerbushelper_p.h>
-#include <private/qgstreamervideorendererinterface_p.h>
-#include <private/qgstutils_p.h>
-#include <qmediarecorder.h>
-#include <qvideosurfaceformat.h>
-
-#if QT_CONFIG(gstreamer_photography)
-#include <gst/interfaces/photography.h>
-#endif
-
-#include <gst/gsttagsetter.h>
-#include <gst/gstversion.h>
-
-#include <QtCore/qdebug.h>
-#include <QCoreApplication>
-#include <QtCore/qmetaobject.h>
-#include <QtGui/qdesktopservices.h>
-
-#include <QtGui/qimage.h>
-#include <QtCore/qdatetime.h>
-
-#include <algorithm>
-
-//#define CAMERABIN_DEBUG 1
-//#define CAMERABIN_DEBUG_DUMP_BIN 1
-#define ENUM_NAME(c,e,v) (c::staticMetaObject.enumerator(c::staticMetaObject.indexOfEnumerator(e)).valueToKey((v)))
-
-#define FILENAME_PROPERTY "location"
-#define MODE_PROPERTY "mode"
-#define MUTE_PROPERTY "mute"
-#define IMAGE_PP_PROPERTY "image-post-processing"
-#define IMAGE_ENCODER_PROPERTY "image-encoder"
-#define VIDEO_PP_PROPERTY "video-post-processing"
-#define VIEWFINDER_SINK_PROPERTY "viewfinder-sink"
-#define CAMERA_SOURCE_PROPERTY "camera-source"
-#define AUDIO_SOURCE_PROPERTY "audio-source"
-#define SUPPORTED_IMAGE_CAPTURE_CAPS_PROPERTY "image-capture-supported-caps"
-#define SUPPORTED_VIDEO_CAPTURE_CAPS_PROPERTY "video-capture-supported-caps"
-#define SUPPORTED_VIEWFINDER_CAPS_PROPERTY "viewfinder-supported-caps"
-#define AUDIO_CAPTURE_CAPS_PROPERTY "audio-capture-caps"
-#define IMAGE_CAPTURE_CAPS_PROPERTY "image-capture-caps"
-#define VIDEO_CAPTURE_CAPS_PROPERTY "video-capture-caps"
-#define VIEWFINDER_CAPS_PROPERTY "viewfinder-caps"
-#define PREVIEW_CAPS_PROPERTY "preview-caps"
-#define POST_PREVIEWS_PROPERTY "post-previews"
-
-
-#define CAPTURE_START "start-capture"
-#define CAPTURE_STOP "stop-capture"
-
-#define FILESINK_BIN_NAME "videobin-filesink"
-
-#define CAMERABIN_IMAGE_MODE 1
-#define CAMERABIN_VIDEO_MODE 2
-
-#define PREVIEW_CAPS_4_3 \
- "video/x-raw-rgb, width = (int) 640, height = (int) 480"
-
-QT_BEGIN_NAMESPACE
-
-CameraBinSession::CameraBinSession(GstElementFactory *sourceFactory, QObject *parent)
- :QObject(parent),
- m_recordingActive(false),
- m_status(QCamera::UnloadedStatus),
- m_pendingState(QCamera::UnloadedState),
- m_muted(false),
- m_busy(false),
- m_captureMode(QCamera::CaptureStillImage),
- m_audioInputFactory(0),
- m_videoInputFactory(0),
- m_viewfinder(0),
- m_viewfinderInterface(0),
-#if QT_CONFIG(gstreamer_photography)
- m_cameraExposureControl(0),
- m_cameraFlashControl(0),
- m_cameraFocusControl(0),
- m_cameraLocksControl(0),
-#endif
- m_cameraSrc(0),
- m_videoSrc(0),
- m_viewfinderElement(0),
- m_sourceFactory(sourceFactory),
- m_viewfinderHasChanged(true),
- m_inputDeviceHasChanged(true),
- m_usingWrapperCameraBinSrc(false),
- m_viewfinderProbe(this),
- m_audioSrc(0),
- m_audioConvert(0),
- m_capsFilter(0),
- m_fileSink(0),
- m_audioEncoder(0),
- m_videoEncoder(0),
- m_muxer(0)
-{
- if (m_sourceFactory)
- gst_object_ref(GST_OBJECT(m_sourceFactory));
- m_camerabin = gst_element_factory_make(QT_GSTREAMER_CAMERABIN_ELEMENT_NAME, "camerabin");
-
- g_signal_connect(G_OBJECT(m_camerabin), "notify::idle", G_CALLBACK(updateBusyStatus), this);
- g_signal_connect(G_OBJECT(m_camerabin), "element-added", G_CALLBACK(elementAdded), this);
- g_signal_connect(G_OBJECT(m_camerabin), "element-removed", G_CALLBACK(elementRemoved), this);
- qt_gst_object_ref_sink(m_camerabin);
-
- m_bus = gst_element_get_bus(m_camerabin);
-
- m_busHelper = new QGstreamerBusHelper(m_bus, this);
- m_busHelper->installMessageFilter(this);
-
- m_cameraControl = new CameraBinControl(this);
- m_audioEncodeControl = new CameraBinAudioEncoder(this);
- m_videoEncodeControl = new CameraBinVideoEncoder(this);
- m_imageEncodeControl = new CameraBinImageEncoder(this);
- m_recorderControl = new CameraBinRecorder(this);
- m_mediaContainerControl = new CameraBinContainer(this);
- m_cameraZoomControl = new CameraBinZoom(this);
- m_imageProcessingControl = new CameraBinImageProcessing(this);
- m_captureDestinationControl = new CameraBinCaptureDestination(this);
- m_captureBufferFormatControl = new CameraBinCaptureBufferFormat(this);
-
- QByteArray envFlags = qgetenv("QT_GSTREAMER_CAMERABIN_FLAGS");
- if (!envFlags.isEmpty())
- g_object_set(G_OBJECT(m_camerabin), "flags", envFlags.toInt(), NULL);
-
- //post image preview in RGB format
- g_object_set(G_OBJECT(m_camerabin), POST_PREVIEWS_PROPERTY, TRUE, NULL);
-
-#if GST_CHECK_VERSION(1,0,0)
- GstCaps *previewCaps = gst_caps_new_simple(
- "video/x-raw",
- "format", G_TYPE_STRING, "RGBx",
- NULL);
-#else
- GstCaps *previewCaps = gst_caps_from_string("video/x-raw-rgb");
-#endif
-
- g_object_set(G_OBJECT(m_camerabin), PREVIEW_CAPS_PROPERTY, previewCaps, NULL);
- gst_caps_unref(previewCaps);
-}
-
-CameraBinSession::~CameraBinSession()
-{
- if (m_camerabin) {
- if (m_viewfinderInterface)
- m_viewfinderInterface->stopRenderer();
-
- gst_element_set_state(m_camerabin, GST_STATE_NULL);
- gst_element_get_state(m_camerabin, NULL, NULL, GST_CLOCK_TIME_NONE);
- gst_object_unref(GST_OBJECT(m_bus));
- gst_object_unref(GST_OBJECT(m_camerabin));
- }
- if (m_viewfinderElement)
- gst_object_unref(GST_OBJECT(m_viewfinderElement));
-
- if (m_sourceFactory)
- gst_object_unref(GST_OBJECT(m_sourceFactory));
-
- if (m_cameraSrc)
- gst_object_unref(GST_OBJECT(m_cameraSrc));
-
- if (m_videoSrc)
- gst_object_unref(GST_OBJECT(m_videoSrc));
-}
-
-#if QT_CONFIG(gstreamer_photography)
-GstPhotography *CameraBinSession::photography()
-{
- if (GST_IS_PHOTOGRAPHY(m_camerabin)) {
- return GST_PHOTOGRAPHY(m_camerabin);
- }
-
- GstElement * const source = buildCameraSource();
-
- if (source && GST_IS_PHOTOGRAPHY(source))
- return GST_PHOTOGRAPHY(source);
-
- return 0;
-}
-
-CameraBinExposure *CameraBinSession::cameraExposureControl()
-{
- if (!m_cameraExposureControl && photography())
- m_cameraExposureControl = new CameraBinExposure(this);
- return m_cameraExposureControl;
-}
-
-CameraBinFlash *CameraBinSession::cameraFlashControl()
-{
- if (!m_cameraFlashControl && photography())
- m_cameraFlashControl = new CameraBinFlash(this);
- return m_cameraFlashControl;
-}
-
-CameraBinFocus *CameraBinSession::cameraFocusControl()
-{
- if (!m_cameraFocusControl && photography())
- m_cameraFocusControl = new CameraBinFocus(this);
- return m_cameraFocusControl;
-}
-
-CameraBinLocks *CameraBinSession::cameraLocksControl()
-{
- if (!m_cameraLocksControl && photography())
- m_cameraLocksControl = new CameraBinLocks(this);
- return m_cameraLocksControl;
-}
-#endif
-
-bool CameraBinSession::setupCameraBin()
-{
- if (!buildCameraSource())
- return false;
-
- if (m_viewfinderHasChanged) {
- if (m_viewfinderElement) {
- GstPad *pad = gst_element_get_static_pad(m_viewfinderElement, "sink");
- m_viewfinderProbe.removeProbeFromPad(pad);
- gst_object_unref(GST_OBJECT(pad));
- gst_object_unref(GST_OBJECT(m_viewfinderElement));
- }
-
- m_viewfinderElement = m_viewfinderInterface ? m_viewfinderInterface->videoSink() : 0;
-#if CAMERABIN_DEBUG
- qDebug() << Q_FUNC_INFO << "Viewfinder changed, reconfigure.";
-#endif
- m_viewfinderHasChanged = false;
- if (!m_viewfinderElement) {
- if (m_pendingState == QCamera::ActiveState)
- qWarning() << "Starting camera without viewfinder available";
- m_viewfinderElement = gst_element_factory_make("fakesink", NULL);
- }
-
- GstPad *pad = gst_element_get_static_pad(m_viewfinderElement, "sink");
- m_viewfinderProbe.addProbeToPad(pad);
- gst_object_unref(GST_OBJECT(pad));
-
- g_object_set(G_OBJECT(m_viewfinderElement), "sync", FALSE, NULL);
- qt_gst_object_ref_sink(GST_OBJECT(m_viewfinderElement));
- gst_element_set_state(m_camerabin, GST_STATE_NULL);
- g_object_set(G_OBJECT(m_camerabin), VIEWFINDER_SINK_PROPERTY, m_viewfinderElement, NULL);
- }
-
- return true;
-}
-
-static GstCaps *resolutionToCaps(const QSize &resolution,
- qreal frameRate = 0.0,
- QVideoFrame::PixelFormat pixelFormat = QVideoFrame::Format_Invalid)
-{
- GstCaps *caps = 0;
- if (pixelFormat == QVideoFrame::Format_Invalid)
- caps = QGstUtils::videoFilterCaps();
- else
- caps = QGstUtils::capsForFormats(QList<QVideoFrame::PixelFormat>() << pixelFormat);
-
- if (!resolution.isEmpty()) {
- gst_caps_set_simple(
- caps,
- "width", G_TYPE_INT, resolution.width(),
- "height", G_TYPE_INT, resolution.height(),
- NULL);
- }
-
- if (frameRate > 0.0) {
- gint numerator;
- gint denominator;
- qt_gst_util_double_to_fraction(frameRate, &numerator, &denominator);
-
- gst_caps_set_simple(
- caps,
- "framerate", GST_TYPE_FRACTION, numerator, denominator,
- NULL);
- }
-
- return caps;
-}
-
-void CameraBinSession::setupCaptureResolution()
-{
- QSize viewfinderResolution = m_viewfinderSettings.resolution();
- qreal viewfinderFrameRate = m_viewfinderSettings.maximumFrameRate();
- QVideoFrame::PixelFormat viewfinderPixelFormat = m_viewfinderSettings.pixelFormat();
- const QSize imageResolution = m_imageEncodeControl->imageSettings().resolution();
- const QSize videoResolution = m_videoEncodeControl->actualVideoSettings().resolution();
-
- // WrapperCameraBinSrc cannot have different caps on its imgsrc, vidsrc and vfsrc pads.
- // If capture resolution is specified, use it also for the viewfinder to avoid caps negotiation
- // to fail.
- if (m_usingWrapperCameraBinSrc) {
- if (viewfinderResolution.isEmpty()) {
- if (m_captureMode == QCamera::CaptureStillImage && !imageResolution.isEmpty())
- viewfinderResolution = imageResolution;
- else if (m_captureMode == QCamera::CaptureVideo && !videoResolution.isEmpty())
- viewfinderResolution = videoResolution;
- }
-
- // Make sure we don't use incompatible frame rate and pixel format with the new resolution
- if (viewfinderResolution != m_viewfinderSettings.resolution() &&
- (!qFuzzyIsNull(viewfinderFrameRate) || viewfinderPixelFormat != QVideoFrame::Format_Invalid)) {
-
- enum {
- Nothing = 0x0,
- OnlyFrameRate = 0x1,
- OnlyPixelFormat = 0x2,
- Both = 0x4
- };
- quint8 found = Nothing;
- auto viewfinderSettings = supportedViewfinderSettings();
- for (int i = 0; i < viewfinderSettings.count() && !(found & Both); ++i) {
- const QCameraViewfinderSettings &s = viewfinderSettings.at(i);
- if (s.resolution() == viewfinderResolution) {
- if ((qFuzzyIsNull(viewfinderFrameRate) || s.maximumFrameRate() == viewfinderFrameRate)
- && (viewfinderPixelFormat == QVideoFrame::Format_Invalid || s.pixelFormat() == viewfinderPixelFormat))
- found |= Both;
- else if (s.maximumFrameRate() == viewfinderFrameRate)
- found |= OnlyFrameRate;
- else if (s.pixelFormat() == viewfinderPixelFormat)
- found |= OnlyPixelFormat;
- }
- }
-
- if (found & Both) {
- // no-op
- } else if (found & OnlyPixelFormat) {
- viewfinderFrameRate = qreal(0);
- } else if (found & OnlyFrameRate) {
- viewfinderPixelFormat = QVideoFrame::Format_Invalid;
- } else {
- viewfinderPixelFormat = QVideoFrame::Format_Invalid;
- viewfinderFrameRate = qreal(0);
- }
- }
- }
-
- GstCaps *caps = resolutionToCaps(imageResolution);
- g_object_set(m_camerabin, IMAGE_CAPTURE_CAPS_PROPERTY, caps, NULL);
- gst_caps_unref(caps);
-
- qreal framerate = m_videoEncodeControl->videoSettings().frameRate();
- caps = resolutionToCaps(videoResolution, framerate);
- g_object_set(m_camerabin, VIDEO_CAPTURE_CAPS_PROPERTY, caps, NULL);
- gst_caps_unref(caps);
-
- caps = resolutionToCaps(viewfinderResolution, viewfinderFrameRate, viewfinderPixelFormat);
- g_object_set(m_camerabin, VIEWFINDER_CAPS_PROPERTY, caps, NULL);
- gst_caps_unref(caps);
-
- // Special case when using mfw_v4lsrc
- if (m_videoSrc && qstrcmp(qt_gst_element_get_factory_name(m_videoSrc), "mfw_v4lsrc") == 0) {
- int capMode = 0;
- if (viewfinderResolution == QSize(320, 240))
- capMode = 1;
- else if (viewfinderResolution == QSize(720, 480))
- capMode = 2;
- else if (viewfinderResolution == QSize(720, 576))
- capMode = 3;
- else if (viewfinderResolution == QSize(1280, 720))
- capMode = 4;
- else if (viewfinderResolution == QSize(1920, 1080))
- capMode = 5;
- g_object_set(G_OBJECT(m_videoSrc), "capture-mode", capMode, NULL);
-
- if (!qFuzzyIsNull(viewfinderFrameRate)) {
- int n, d;
- qt_gst_util_double_to_fraction(viewfinderFrameRate, &n, &d);
- g_object_set(G_OBJECT(m_videoSrc), "fps-n", n, NULL);
- g_object_set(G_OBJECT(m_videoSrc), "fps-d", d, NULL);
- }
- }
-
- if (m_videoEncoder)
- m_videoEncodeControl->applySettings(m_videoEncoder);
-}
-
-void CameraBinSession::setAudioCaptureCaps()
-{
- QAudioEncoderSettings settings = m_audioEncodeControl->audioSettings();
- const int sampleRate = settings.sampleRate();
- const int channelCount = settings.channelCount();
-
- if (sampleRate <= 0 && channelCount <=0)
- return;
-
-#if GST_CHECK_VERSION(1,0,0)
- GstStructure *structure = gst_structure_new_empty(QT_GSTREAMER_RAW_AUDIO_MIME);
-#else
- GstStructure *structure = gst_structure_new(
- QT_GSTREAMER_RAW_AUDIO_MIME,
- "endianness", G_TYPE_INT, 1234,
- "signed", G_TYPE_BOOLEAN, TRUE,
- "width", G_TYPE_INT, 16,
- "depth", G_TYPE_INT, 16,
- NULL);
-#endif
- if (sampleRate > 0)
- gst_structure_set(structure, "rate", G_TYPE_INT, sampleRate, NULL);
- if (channelCount > 0)
- gst_structure_set(structure, "channels", G_TYPE_INT, channelCount, NULL);
-
- GstCaps *caps = gst_caps_new_full(structure, NULL);
- g_object_set(G_OBJECT(m_camerabin), AUDIO_CAPTURE_CAPS_PROPERTY, caps, NULL);
- gst_caps_unref(caps);
-
- if (m_audioEncoder)
- m_audioEncodeControl->applySettings(m_audioEncoder);
-}
-
-GstElement *CameraBinSession::buildCameraSource()
-{
-#if CAMERABIN_DEBUG
- qDebug() << Q_FUNC_INFO;
-#endif
- if (m_inputDevice.isEmpty())
- return nullptr;
-
- if (!m_inputDeviceHasChanged)
- return m_cameraSrc;
-
- m_inputDeviceHasChanged = false;
- m_usingWrapperCameraBinSrc = false;
-
- GstElement *camSrc = 0;
- g_object_get(G_OBJECT(m_camerabin), CAMERA_SOURCE_PROPERTY, &camSrc, NULL);
-
- if (!m_cameraSrc && m_sourceFactory)
- m_cameraSrc = gst_element_factory_create(m_sourceFactory, "camera_source");
-
- // If gstreamer has set a default source use it.
- if (!m_cameraSrc)
- m_cameraSrc = camSrc;
-
- if (m_cameraSrc) {
-#if CAMERABIN_DEBUG
- qDebug() << "set camera device" << m_inputDevice;
-#endif
- m_usingWrapperCameraBinSrc = qstrcmp(qt_gst_element_get_factory_name(m_cameraSrc), "wrappercamerabinsrc") == 0;
-
- if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_cameraSrc), "video-source")) {
- if (!m_videoSrc) {
- /* QT_GSTREAMER_CAMERABIN_VIDEOSRC can be used to set the video source element.
-
- --- Usage
-
- QT_GSTREAMER_CAMERABIN_VIDEOSRC=[drivername=elementname[,drivername2=elementname2 ...],][elementname]
-
- --- Examples
-
- Always use 'somevideosrc':
- QT_GSTREAMER_CAMERABIN_VIDEOSRC="somevideosrc"
-
- Use 'somevideosrc' when the device driver is 'somedriver', otherwise use default:
- QT_GSTREAMER_CAMERABIN_VIDEOSRC="somedriver=somevideosrc"
-
- Use 'somevideosrc' when the device driver is 'somedriver', otherwise use 'somevideosrc2'
- QT_GSTREAMER_CAMERABIN_VIDEOSRC="somedriver=somevideosrc,somevideosrc2"
- */
- const QByteArray envVideoSource = qgetenv("QT_GSTREAMER_CAMERABIN_VIDEOSRC");
-
- if (!envVideoSource.isEmpty()) {
- const QList<QByteArray> sources = envVideoSource.split(',');
- for (const QByteArray &source : sources) {
- QList<QByteArray> keyValue = source.split('=');
- QByteArray name = keyValue.at(0);
- if (keyValue.count() > 1 && keyValue.at(0) == QGstUtils::cameraDriver(m_inputDevice, m_sourceFactory))
- name = keyValue.at(1);
-
- GError *error = NULL;
- GstElement *element = gst_parse_launch(name, &error);
-
- if (error) {
- g_printerr("ERROR: %s: %s\n", name.constData(), GST_STR_NULL(error->message));
- g_clear_error(&error);
- }
- if (element) {
- m_videoSrc = element;
- break;
- }
- }
- } else if (m_videoInputFactory) {
- m_videoSrc = m_videoInputFactory->buildElement();
- }
-
- if (!m_videoSrc)
- m_videoSrc = gst_element_factory_make("v4l2src", "camera_source");
-
- if (!m_videoSrc)
- m_videoSrc = gst_element_factory_make("ksvideosrc", "camera_source");
-
- if (!m_videoSrc)
- m_videoSrc = gst_element_factory_make("avfvideosrc", "camera_source");
-
- if (m_videoSrc)
- g_object_set(G_OBJECT(m_cameraSrc), "video-source", m_videoSrc, NULL);
- }
-
- if (m_videoSrc) {
- if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSrc), "device"))
- g_object_set(G_OBJECT(m_videoSrc), "device", m_inputDevice.toUtf8().constData(), NULL);
-
- if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSrc), "device-path"))
- g_object_set(G_OBJECT(m_videoSrc), "device-path", m_inputDevice.toUtf8().constData(), NULL);
-
- if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSrc), "device-index"))
- g_object_set(G_OBJECT(m_videoSrc), "device-index", m_inputDevice.toInt(), NULL);
- }
- } else if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_cameraSrc), "camera-device")) {
- if (m_inputDevice == QLatin1String("secondary")) {
- g_object_set(G_OBJECT(m_cameraSrc), "camera-device", 1, NULL);
- } else {
- g_object_set(G_OBJECT(m_cameraSrc), "camera-device", 0, NULL);
- }
- }
- }
-
- if (m_cameraSrc != camSrc) {
- g_object_set(G_OBJECT(m_camerabin), CAMERA_SOURCE_PROPERTY, m_cameraSrc, NULL);
- // Unref only if camSrc is not m_cameraSrc to prevent double unrefing.
- if (camSrc)
- gst_object_unref(GST_OBJECT(camSrc));
- }
-
- return m_cameraSrc;
-}
-
-void CameraBinSession::captureImage(int requestId, const QString &fileName)
-{
- const QString actualFileName = m_mediaStorageLocation.generateFileName(fileName,
- QMediaStorageLocation::Pictures,
- QLatin1String("IMG_"),
- QLatin1String("jpg"));
-
- m_requestId = requestId;
-
-#if CAMERABIN_DEBUG
- qDebug() << Q_FUNC_INFO << m_requestId << fileName << "actual file name:" << actualFileName;
-#endif
-
- g_object_set(G_OBJECT(m_camerabin), FILENAME_PROPERTY, actualFileName.toLocal8Bit().constData(), NULL);
-
- g_signal_emit_by_name(G_OBJECT(m_camerabin), CAPTURE_START, NULL);
-
- m_imageFileName = actualFileName;
-}
-
-void CameraBinSession::setCaptureMode(QCamera::CaptureModes mode)
-{
- m_captureMode = mode;
-
- switch (m_captureMode) {
- case QCamera::CaptureStillImage:
- g_object_set(m_camerabin, MODE_PROPERTY, CAMERABIN_IMAGE_MODE, NULL);
- break;
- case QCamera::CaptureVideo:
- g_object_set(m_camerabin, MODE_PROPERTY, CAMERABIN_VIDEO_MODE, NULL);
- break;
- }
-
- m_recorderControl->updateStatus();
-}
-
-QUrl CameraBinSession::outputLocation() const
-{
- //return the location service wrote data to, not one set by user, it can be empty.
- return m_actualSink;
-}
-
-bool CameraBinSession::setOutputLocation(const QUrl& sink)
-{
- if (!sink.isRelative() && !sink.isLocalFile()) {
- qWarning("Output location must be a local file");
- return false;
- }
-
- m_sink = m_actualSink = sink;
- return true;
-}
-
-void CameraBinSession::setDevice(const QString &device)
-{
- if (m_inputDevice != device) {
- m_inputDevice = device;
- m_inputDeviceHasChanged = true;
- }
-}
-
-void CameraBinSession::setAudioInput(QGstreamerElementFactory *audioInput)
-{
- m_audioInputFactory = audioInput;
-}
-
-void CameraBinSession::setVideoInput(QGstreamerElementFactory *videoInput)
-{
- m_videoInputFactory = videoInput;
- m_inputDeviceHasChanged = true;
-}
-
-bool CameraBinSession::isReady() const
-{
- //it's possible to use QCamera without any viewfinder attached
- return !m_viewfinderInterface || m_viewfinderInterface->isReady();
-}
-
-void CameraBinSession::setViewfinder(QObject *viewfinder)
-{
- if (m_viewfinderInterface)
- m_viewfinderInterface->stopRenderer();
-
- m_viewfinderInterface = qobject_cast<QGstreamerVideoRendererInterface*>(viewfinder);
- if (!m_viewfinderInterface)
- viewfinder = 0;
-
- if (m_viewfinder != viewfinder) {
- bool oldReady = isReady();
-
- if (m_viewfinder) {
- disconnect(m_viewfinder, SIGNAL(sinkChanged()),
- this, SLOT(handleViewfinderChange()));
- disconnect(m_viewfinder, SIGNAL(readyChanged(bool)),
- this, SIGNAL(readyChanged(bool)));
-
- m_busHelper->removeMessageFilter(m_viewfinder);
- }
-
- m_viewfinder = viewfinder;
- m_viewfinderHasChanged = true;
-
- if (m_viewfinder) {
- connect(m_viewfinder, SIGNAL(sinkChanged()),
- this, SLOT(handleViewfinderChange()));
- connect(m_viewfinder, SIGNAL(readyChanged(bool)),
- this, SIGNAL(readyChanged(bool)));
-
- m_busHelper->installMessageFilter(m_viewfinder);
- }
-
- emit viewfinderChanged();
- if (oldReady != isReady())
- emit readyChanged(isReady());
- }
-}
-
-static QList<QCameraViewfinderSettings> capsToViewfinderSettings(GstCaps *supportedCaps)
-{
- QList<QCameraViewfinderSettings> settings;
-
- if (!supportedCaps)
- return settings;
-
- supportedCaps = qt_gst_caps_normalize(supportedCaps);
-
- // Convert caps to QCameraViewfinderSettings
- for (uint i = 0; i < gst_caps_get_size(supportedCaps); ++i) {
- const GstStructure *structure = gst_caps_get_structure(supportedCaps, i);
-
- QCameraViewfinderSettings s;
- s.setResolution(QGstUtils::structureResolution(structure));
- s.setPixelFormat(QGstUtils::structurePixelFormat(structure));
- s.setPixelAspectRatio(QGstUtils::structurePixelAspectRatio(structure));
-
- QPair<qreal, qreal> frameRateRange = QGstUtils::structureFrameRateRange(structure);
- s.setMinimumFrameRate(frameRateRange.first);
- s.setMaximumFrameRate(frameRateRange.second);
-
- if (!s.resolution().isEmpty()
- && s.pixelFormat() != QVideoFrame::Format_Invalid
- && !settings.contains(s)) {
- settings.append(s);
- }
- }
-
- gst_caps_unref(supportedCaps);
- return settings;
-}
-
-QList<QCameraViewfinderSettings> CameraBinSession::supportedViewfinderSettings() const
-{
- if (m_status >= QCamera::LoadedStatus && m_supportedViewfinderSettings.isEmpty()) {
- m_supportedViewfinderSettings =
- capsToViewfinderSettings(supportedCaps(QCamera::CaptureViewfinder));
- }
-
- return m_supportedViewfinderSettings;
-}
-
-QCameraViewfinderSettings CameraBinSession::viewfinderSettings() const
-{
- return m_status == QCamera::ActiveStatus ? m_actualViewfinderSettings : m_viewfinderSettings;
-}
-
-void CameraBinSession::ViewfinderProbe::probeCaps(GstCaps *caps)
-{
- QGstreamerVideoProbeControl::probeCaps(caps);
-
- // Update actual viewfinder settings on viewfinder caps change
- const GstStructure *s = gst_caps_get_structure(caps, 0);
- const QPair<qreal, qreal> frameRate = QGstUtils::structureFrameRateRange(s);
- session->m_actualViewfinderSettings.setResolution(QGstUtils::structureResolution(s));
- session->m_actualViewfinderSettings.setMinimumFrameRate(frameRate.first);
- session->m_actualViewfinderSettings.setMaximumFrameRate(frameRate.second);
- session->m_actualViewfinderSettings.setPixelFormat(QGstUtils::structurePixelFormat(s));
- session->m_actualViewfinderSettings.setPixelAspectRatio(QGstUtils::structurePixelAspectRatio(s));
-}
-
-void CameraBinSession::handleViewfinderChange()
-{
- //the viewfinder will be reloaded
- //shortly when the pipeline is started
- m_viewfinderHasChanged = true;
- emit viewfinderChanged();
-}
-
-void CameraBinSession::setStatus(QCamera::Status status)
-{
- if (m_status == status)
- return;
-
- m_status = status;
- emit statusChanged(m_status);
-
- setStateHelper(m_pendingState);
-}
-
-QCamera::Status CameraBinSession::status() const
-{
- return m_status;
-}
-
-QCamera::State CameraBinSession::pendingState() const
-{
- return m_pendingState;
-}
-
-void CameraBinSession::setState(QCamera::State newState)
-{
- if (newState == m_pendingState)
- return;
-
- m_pendingState = newState;
- emit pendingStateChanged(m_pendingState);
-
-#if CAMERABIN_DEBUG
- qDebug() << Q_FUNC_INFO << newState;
-#endif
-
- setStateHelper(newState);
-}
-
-void CameraBinSession::setStateHelper(QCamera::State state)
-{
- switch (state) {
- case QCamera::UnloadedState:
- unload();
- break;
- case QCamera::LoadedState:
- if (m_status == QCamera::ActiveStatus)
- stop();
- else if (m_status == QCamera::UnloadedStatus)
- load();
- break;
- case QCamera::ActiveState:
- // If the viewfinder changed while in the loaded state, we need to reload the pipeline
- if (m_status == QCamera::LoadedStatus && !m_viewfinderHasChanged)
- start();
- else if (m_status == QCamera::UnloadedStatus || m_viewfinderHasChanged)
- load();
- }
-}
-
-void CameraBinSession::setError(int err, const QString &errorString)
-{
- // Emit only first error
- if (m_pendingState == QCamera::UnloadedState)
- return;
-
- setState(QCamera::UnloadedState);
- emit error(err, errorString);
- setStatus(QCamera::UnloadedStatus);
-}
-
-void CameraBinSession::load()
-{
- if (m_status != QCamera::UnloadedStatus && !m_viewfinderHasChanged)
- return;
-
- setStatus(QCamera::LoadingStatus);
-
- gst_element_set_state(m_camerabin, GST_STATE_NULL);
-
- if (!setupCameraBin()) {
- setError(QCamera::CameraError, QStringLiteral("No camera source available"));
- return;
- }
-
- m_recorderControl->applySettings();
-
-#if QT_CONFIG(gstreamer_encodingprofiles)
- GstEncodingContainerProfile *profile = m_recorderControl->videoProfile();
- if (profile) {
- g_object_set (G_OBJECT(m_camerabin),
- "video-profile",
- profile,
- NULL);
- gst_encoding_profile_unref(profile);
- }
-#endif
-
- gst_element_set_state(m_camerabin, GST_STATE_READY);
-}
-
-void CameraBinSession::unload()
-{
- if (m_status == QCamera::UnloadedStatus || m_status == QCamera::UnloadingStatus)
- return;
-
- setStatus(QCamera::UnloadingStatus);
-
- if (m_recordingActive)
- stopVideoRecording();
-
- if (m_viewfinderInterface)
- m_viewfinderInterface->stopRenderer();
-
- gst_element_set_state(m_camerabin, GST_STATE_NULL);
-
- if (m_busy)
- emit busyChanged(m_busy = false);
-
- m_supportedViewfinderSettings.clear();
-
- setStatus(QCamera::UnloadedStatus);
-}
-
-void CameraBinSession::start()
-{
- if (m_status != QCamera::LoadedStatus)
- return;
-
- setStatus(QCamera::StartingStatus);
-
- setAudioCaptureCaps();
-
- setupCaptureResolution();
-
- gst_element_set_state(m_camerabin, GST_STATE_PLAYING);
-}
-
-void CameraBinSession::stop()
-{
- if (m_status != QCamera::ActiveStatus)
- return;
-
- setStatus(QCamera::StoppingStatus);
-
- if (m_recordingActive)
- stopVideoRecording();
-
- if (m_viewfinderInterface)
- m_viewfinderInterface->stopRenderer();
-
- gst_element_set_state(m_camerabin, GST_STATE_READY);
-}
-
-bool CameraBinSession::isBusy() const
-{
- return m_busy;
-}
-
-void CameraBinSession::updateBusyStatus(GObject *o, GParamSpec *p, gpointer d)
-{
- Q_UNUSED(p);
- CameraBinSession *session = reinterpret_cast<CameraBinSession *>(d);
-
- gboolean idle = false;
- g_object_get(o, "idle", &idle, NULL);
- bool busy = !idle;
-
- if (session->m_busy != busy) {
- session->m_busy = busy;
- QMetaObject::invokeMethod(session, "busyChanged",
- Qt::QueuedConnection,
- Q_ARG(bool, busy));
- }
-}
-
-qint64 CameraBinSession::duration() const
-{
- if (m_camerabin) {
- GstElement *fileSink = gst_bin_get_by_name(GST_BIN(m_camerabin), FILESINK_BIN_NAME);
- if (fileSink) {
- GstFormat format = GST_FORMAT_TIME;
- gint64 duration = 0;
- bool ret = qt_gst_element_query_position(fileSink, format, &duration);
- gst_object_unref(GST_OBJECT(fileSink));
- if (ret)
- return duration / 1000000;
- }
- }
-
- return 0;
-}
-
-bool CameraBinSession::isMuted() const
-{
- return m_muted;
-}
-
-void CameraBinSession::setMuted(bool muted)
-{
- if (m_muted != muted) {
- m_muted = muted;
-
- if (m_camerabin)
- g_object_set(G_OBJECT(m_camerabin), MUTE_PROPERTY, m_muted, NULL);
- emit mutedChanged(m_muted);
- }
-}
-
-void CameraBinSession::setCaptureDevice(const QString &deviceName)
-{
- m_captureDevice = deviceName;
-}
-
-void CameraBinSession::setMetaData(const QMap<QByteArray, QVariant> &data)
-{
- m_metaData = data;
-
- if (m_camerabin)
- QGstUtils::setMetaData(m_camerabin, data);
-}
-
-bool CameraBinSession::processSyncMessage(const QGstreamerMessage &message)
-{
- GstMessage* gm = message.rawMessage();
-
- if (gm && GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) {
- const GstStructure *st = gst_message_get_structure(gm);
- const GValue *sampleValue = 0;
- if (m_captureMode == QCamera::CaptureStillImage
- && gst_structure_has_name(st, "preview-image")
-#if GST_CHECK_VERSION(1,0,0)
- && gst_structure_has_field_typed(st, "sample", GST_TYPE_SAMPLE)
- && (sampleValue = gst_structure_get_value(st, "sample"))) {
- GstSample * const sample = gst_value_get_sample(sampleValue);
- GstCaps * const previewCaps = gst_sample_get_caps(sample);
- GstBuffer * const buffer = gst_sample_get_buffer(sample);
-#else
- && gst_structure_has_field_typed(st, "buffer", GST_TYPE_BUFFER)
- && (sampleValue = gst_structure_get_value(st, "buffer"))) {
- GstBuffer * const buffer = gst_value_get_buffer(sampleValue);
-#endif
-
- QImage image;
-#if GST_CHECK_VERSION(1,0,0)
- GstVideoInfo previewInfo;
- if (gst_video_info_from_caps(&previewInfo, previewCaps))
- image = QGstUtils::bufferToImage(buffer, previewInfo);
-#else
- image = QGstUtils::bufferToImage(buffer);
- gst_buffer_unref(buffer);
-#endif
- if (!image.isNull()) {
- static QMetaMethod exposedSignal = QMetaMethod::fromSignal(&CameraBinSession::imageExposed);
- exposedSignal.invoke(this,
- Qt::QueuedConnection,
- Q_ARG(int,m_requestId));
-
- static QMetaMethod capturedSignal = QMetaMethod::fromSignal(&CameraBinSession::imageCaptured);
- capturedSignal.invoke(this,
- Qt::QueuedConnection,
- Q_ARG(int,m_requestId),
- Q_ARG(QImage,image));
- }
- return true;
- }
-#if QT_CONFIG(gstreamer_photography)
- if (gst_structure_has_name(st, GST_PHOTOGRAPHY_AUTOFOCUS_DONE))
- m_cameraFocusControl->handleFocusMessage(gm);
-#endif
- }
-
- return false;
-}
-
-bool CameraBinSession::processBusMessage(const QGstreamerMessage &message)
-{
- GstMessage* gm = message.rawMessage();
-
- if (gm) {
- if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ERROR) {
- GError *err;
- gchar *debug;
- gst_message_parse_error (gm, &err, &debug);
-
- QString message;
-
- if (err && err->message) {
- message = QString::fromUtf8(err->message);
- qWarning() << "CameraBin error:" << message;
-#if CAMERABIN_DEBUG
- qWarning() << QString::fromUtf8(debug);
-#endif
- }
-
- // Only report error messages from camerabin or video source
- if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_camerabin)
- || GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_videoSrc)) {
- if (message.isEmpty())
- message = tr("Camera error");
-
- setError(int(QMediaRecorder::ResourceError), message);
- }
-
-#ifdef CAMERABIN_DEBUG_DUMP_BIN
- _gst_debug_bin_to_dot_file_with_ts(GST_BIN(m_camerabin),
- GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL /* GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES*/),
- "camerabin_error");
-#endif
-
-
- if (err)
- g_error_free (err);
-
- if (debug)
- g_free (debug);
- }
-
- if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_WARNING) {
- GError *err;
- gchar *debug;
- gst_message_parse_warning (gm, &err, &debug);
-
- if (err && err->message)
- qWarning() << "CameraBin warning:" << QString::fromUtf8(err->message);
-
- if (err)
- g_error_free (err);
- if (debug)
- g_free (debug);
- }
-
- if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_camerabin)) {
- switch (GST_MESSAGE_TYPE(gm)) {
- case GST_MESSAGE_DURATION:
- break;
-
- case GST_MESSAGE_STATE_CHANGED:
- {
-
- GstState oldState;
- GstState newState;
- GstState pending;
-
- gst_message_parse_state_changed(gm, &oldState, &newState, &pending);
-
-
-#if CAMERABIN_DEBUG
- 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]);
-#endif
-
-#ifdef CAMERABIN_DEBUG_DUMP_BIN
- _gst_debug_bin_to_dot_file_with_ts(GST_BIN(m_camerabin),
- GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL /*GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES*/),
- "camerabin");
-#endif
-
- switch (newState) {
- case GST_STATE_VOID_PENDING:
- case GST_STATE_NULL:
- setStatus(QCamera::UnloadedStatus);
- break;
- case GST_STATE_READY:
- if (oldState == GST_STATE_NULL)
- m_supportedViewfinderSettings.clear();
-
- setMetaData(m_metaData);
- setStatus(QCamera::LoadedStatus);
- break;
- case GST_STATE_PLAYING:
- setStatus(QCamera::ActiveStatus);
- break;
- case GST_STATE_PAUSED:
- default:
- break;
- }
- }
- break;
- default:
- break;
- }
- }
- }
-
- return false;
-}
-
-QGstreamerVideoProbeControl *CameraBinSession::videoProbe()
-{
- return &m_viewfinderProbe;
-}
-
-QString CameraBinSession::currentContainerFormat() const
-{
- if (!m_muxer)
- return QString();
-
- QString format;
-
- if (GstPad *srcPad = gst_element_get_static_pad(m_muxer, "src")) {
- if (GstCaps *caps = qt_gst_pad_get_caps(srcPad)) {
- gchar *capsString = gst_caps_to_string(caps);
- format = QString::fromLatin1(capsString);
- if (capsString)
- g_free(capsString);
- gst_caps_unref(caps);
- }
- gst_object_unref(GST_OBJECT(srcPad));
- }
-
- return format;
-}
-
-void CameraBinSession::recordVideo()
-{
- QString format = currentContainerFormat();
- if (format.isEmpty())
- format = m_mediaContainerControl->actualContainerFormat();
-
- const QString fileName = m_sink.isLocalFile() ? m_sink.toLocalFile() : m_sink.toString();
- const QFileInfo fileInfo(fileName);
- const QString extension = fileInfo.suffix().isEmpty()
- ? QGstUtils::fileExtensionForMimeType(format)
- : fileInfo.suffix();
-
- const QString actualFileName = m_mediaStorageLocation.generateFileName(fileName,
- QMediaStorageLocation::Movies,
- QLatin1String("clip_"),
- extension);
-
- m_recordingActive = true;
- m_actualSink = QUrl::fromLocalFile(actualFileName);
-
- g_object_set(G_OBJECT(m_camerabin), FILENAME_PROPERTY, QFile::encodeName(actualFileName).constData(), NULL);
-
- g_signal_emit_by_name(G_OBJECT(m_camerabin), CAPTURE_START, NULL);
-}
-
-void CameraBinSession::stopVideoRecording()
-{
- m_recordingActive = false;
- g_signal_emit_by_name(G_OBJECT(m_camerabin), CAPTURE_STOP, NULL);
-}
-
-//internal, only used by CameraBinSession::supportedFrameRates.
-//recursively fills the list of framerates res from value data.
-static void readValue(const GValue *value, QList< QPair<int,int> > *res, bool *continuous)
-{
- if (GST_VALUE_HOLDS_FRACTION(value)) {
- int num = gst_value_get_fraction_numerator(value);
- int denum = gst_value_get_fraction_denominator(value);
-
- *res << QPair<int,int>(num, denum);
- } else if (GST_VALUE_HOLDS_FRACTION_RANGE(value)) {
- const GValue *rateValueMin = gst_value_get_fraction_range_min(value);
- const GValue *rateValueMax = gst_value_get_fraction_range_max(value);
-
- if (continuous)
- *continuous = true;
-
- readValue(rateValueMin, res, continuous);
- readValue(rateValueMax, res, continuous);
- } else if (GST_VALUE_HOLDS_LIST(value)) {
- for (uint i=0; i<gst_value_list_get_size(value); i++) {
- readValue(gst_value_list_get_value(value, i), res, continuous);
- }
- }
-}
-
-static bool rateLessThan(const QPair<int,int> &r1, const QPair<int,int> &r2)
-{
- return r1.first*r2.second < r2.first*r1.second;
-}
-
-GstCaps *CameraBinSession::supportedCaps(QCamera::CaptureModes mode) const
-{
- GstCaps *supportedCaps = 0;
-
- // When using wrappercamerabinsrc, get the supported caps directly from the video source element.
- // This makes sure we only get the caps actually supported by the video source element.
- if (m_videoSrc) {
- GstPad *pad = gst_element_get_static_pad(m_videoSrc, "src");
- if (pad) {
- supportedCaps = qt_gst_pad_get_caps(pad);
- gst_object_unref(GST_OBJECT(pad));
- }
- }
-
- // Otherwise, let the camerabin handle this.
- if (!supportedCaps) {
- const gchar *prop;
- switch (mode) {
- case QCamera::CaptureStillImage:
- prop = SUPPORTED_IMAGE_CAPTURE_CAPS_PROPERTY;
- break;
- case QCamera::CaptureVideo:
- prop = SUPPORTED_VIDEO_CAPTURE_CAPS_PROPERTY;
- break;
- case QCamera::CaptureViewfinder:
- default:
- prop = SUPPORTED_VIEWFINDER_CAPS_PROPERTY;
- break;
- }
-
- g_object_get(G_OBJECT(m_camerabin), prop, &supportedCaps, NULL);
- }
-
- return supportedCaps;
-}
-
-QList< QPair<int,int> > CameraBinSession::supportedFrameRates(const QSize &frameSize, bool *continuous) const
-{
- QList< QPair<int,int> > res;
-
- GstCaps *supportedCaps = this->supportedCaps(QCamera::CaptureVideo);
-
- if (!supportedCaps)
- return res;
-
- GstCaps *caps = 0;
-
- if (frameSize.isEmpty()) {
- caps = gst_caps_copy(supportedCaps);
- } else {
- GstCaps *filter = QGstUtils::videoFilterCaps();
- gst_caps_set_simple(
- filter,
- "width", G_TYPE_INT, frameSize.width(),
- "height", G_TYPE_INT, frameSize.height(),
- NULL);
-
- caps = gst_caps_intersect(supportedCaps, filter);
- gst_caps_unref(filter);
- }
- gst_caps_unref(supportedCaps);
-
- //simplify to the list of rates only:
- caps = gst_caps_make_writable(caps);
- for (uint i=0; i<gst_caps_get_size(caps); i++) {
- GstStructure *structure = gst_caps_get_structure(caps, i);
- gst_structure_set_name(structure, "video/x-raw");
-#if GST_CHECK_VERSION(1,2,0)
- gst_caps_set_features(caps, i, NULL);
-#endif
- const GValue *oldRate = gst_structure_get_value(structure, "framerate");
- if (!oldRate)
- continue;
-
- GValue rate;
- memset(&rate, 0, sizeof(rate));
- g_value_init(&rate, G_VALUE_TYPE(oldRate));
- g_value_copy(oldRate, &rate);
- gst_structure_remove_all_fields(structure);
- gst_structure_set_value(structure, "framerate", &rate);
- g_value_unset(&rate);
- }
-#if GST_CHECK_VERSION(1,0,0)
- caps = gst_caps_simplify(caps);
-#else
- gst_caps_do_simplify(caps);
-#endif
-
- for (uint i=0; i<gst_caps_get_size(caps); i++) {
- GstStructure *structure = gst_caps_get_structure(caps, i);
- const GValue *rateValue = gst_structure_get_value(structure, "framerate");
- if (!rateValue)
- continue;
-
- readValue(rateValue, &res, continuous);
- }
-
- std::sort(res.begin(), res.end(), rateLessThan);
-
-#if CAMERABIN_DEBUG
- qDebug() << "Supported rates:" << caps;
- qDebug() << res;
-#endif
-
- gst_caps_unref(caps);
-
- return res;
-}
-
-//internal, only used by CameraBinSession::supportedResolutions
-//recursively find the supported resolutions range.
-static QPair<int,int> valueRange(const GValue *value, bool *continuous)
-{
- int minValue = 0;
- int maxValue = 0;
-
- if (g_value_type_compatible(G_VALUE_TYPE(value), G_TYPE_INT)) {
- minValue = maxValue = g_value_get_int(value);
- } else if (GST_VALUE_HOLDS_INT_RANGE(value)) {
- minValue = gst_value_get_int_range_min(value);
- maxValue = gst_value_get_int_range_max(value);
- *continuous = true;
- } else if (GST_VALUE_HOLDS_LIST(value)) {
- for (uint i=0; i<gst_value_list_get_size(value); i++) {
- QPair<int,int> res = valueRange(gst_value_list_get_value(value, i), continuous);
-
- if (res.first > 0 && minValue > 0)
- minValue = qMin(minValue, res.first);
- else //select non 0 valid value
- minValue = qMax(minValue, res.first);
-
- maxValue = qMax(maxValue, res.second);
- }
- }
-
- return QPair<int,int>(minValue, maxValue);
-}
-
-static bool resolutionLessThan(const QSize &r1, const QSize &r2)
-{
- return qlonglong(r1.width()) * r1.height() < qlonglong(r2.width()) * r2.height();
-}
-
-
-QList<QSize> CameraBinSession::supportedResolutions(QPair<int,int> rate,
- bool *continuous,
- QCamera::CaptureModes mode) const
-{
- QList<QSize> res;
-
- if (continuous)
- *continuous = false;
-
- GstCaps *supportedCaps = this->supportedCaps(mode);
-
-#if CAMERABIN_DEBUG
- qDebug() << "Source caps:" << supportedCaps;
-#endif
-
- if (!supportedCaps)
- return res;
-
- GstCaps *caps = 0;
- bool isContinuous = false;
-
- if (rate.first <= 0 || rate.second <= 0) {
- caps = gst_caps_copy(supportedCaps);
- } else {
- GstCaps *filter = QGstUtils::videoFilterCaps();
- gst_caps_set_simple(
- filter,
- "framerate" , GST_TYPE_FRACTION , rate.first, rate.second,
- NULL);
- caps = gst_caps_intersect(supportedCaps, filter);
- gst_caps_unref(filter);
- }
- gst_caps_unref(supportedCaps);
-
- //simplify to the list of resolutions only:
- caps = gst_caps_make_writable(caps);
- for (uint i=0; i<gst_caps_get_size(caps); i++) {
- GstStructure *structure = gst_caps_get_structure(caps, i);
- gst_structure_set_name(structure, "video/x-raw");
-#if GST_CHECK_VERSION(1,2,0)
- gst_caps_set_features(caps, i, NULL);
-#endif
- const GValue *oldW = gst_structure_get_value(structure, "width");
- const GValue *oldH = gst_structure_get_value(structure, "height");
- if (!oldW || !oldH)
- continue;
-
- GValue w;
- memset(&w, 0, sizeof(GValue));
- GValue h;
- memset(&h, 0, sizeof(GValue));
- g_value_init(&w, G_VALUE_TYPE(oldW));
- g_value_init(&h, G_VALUE_TYPE(oldH));
- g_value_copy(oldW, &w);
- g_value_copy(oldH, &h);
- gst_structure_remove_all_fields(structure);
- gst_structure_set_value(structure, "width", &w);
- gst_structure_set_value(structure, "height", &h);
- g_value_unset(&w);
- g_value_unset(&h);
- }
-
-#if GST_CHECK_VERSION(1,0,0)
- caps = gst_caps_simplify(caps);
-#else
- gst_caps_do_simplify(caps);
-#endif
-
-
- for (uint i=0; i<gst_caps_get_size(caps); i++) {
- GstStructure *structure = gst_caps_get_structure(caps, i);
- const GValue *wValue = gst_structure_get_value(structure, "width");
- const GValue *hValue = gst_structure_get_value(structure, "height");
- if (!wValue || !hValue)
- continue;
-
- QPair<int,int> wRange = valueRange(wValue, &isContinuous);
- QPair<int,int> hRange = valueRange(hValue, &isContinuous);
-
- QSize minSize(wRange.first, hRange.first);
- QSize maxSize(wRange.second, hRange.second);
-
- if (!minSize.isEmpty())
- res << minSize;
-
- if (minSize != maxSize && !maxSize.isEmpty())
- res << maxSize;
- }
-
-
- std::sort(res.begin(), res.end(), resolutionLessThan);
-
- //if the range is continuos, populate is with the common rates
- if (isContinuous && res.size() >= 2) {
- //fill the ragne with common value
- static const QList<QSize> commonSizes =
- QList<QSize>() << QSize(128, 96)
- << QSize(160,120)
- << QSize(176, 144)
- << QSize(320, 240)
- << QSize(352, 288)
- << QSize(640, 480)
- << QSize(848, 480)
- << QSize(854, 480)
- << QSize(1024, 768)
- << QSize(1280, 720) // HD 720
- << QSize(1280, 1024)
- << QSize(1600, 1200)
- << QSize(1920, 1080) // HD
- << QSize(1920, 1200)
- << QSize(2048, 1536)
- << QSize(2560, 1600)
- << QSize(2580, 1936);
- QSize minSize = res.first();
- QSize maxSize = res.last();
- res.clear();
-
- for (const QSize &candidate : commonSizes) {
- int w = candidate.width();
- int h = candidate.height();
-
- if (w > maxSize.width() && h > maxSize.height())
- break;
-
- if (w >= minSize.width() && h >= minSize.height() &&
- w <= maxSize.width() && h <= maxSize.height())
- res << candidate;
- }
-
- if (res.isEmpty() || res.first() != minSize)
- res.prepend(minSize);
-
- if (res.last() != maxSize)
- res.append(maxSize);
- }
-
-#if CAMERABIN_DEBUG
- qDebug() << "Supported resolutions:" << gst_caps_to_string(caps);
- qDebug() << res;
-#endif
-
- gst_caps_unref(caps);
-
- if (continuous)
- *continuous = isContinuous;
-
- return res;
-}
-
-void CameraBinSession::elementAdded(GstBin *, GstElement *element, CameraBinSession *session)
-{
- GstElementFactory *factory = gst_element_get_factory(element);
-
- if (GST_IS_BIN(element)) {
- g_signal_connect(G_OBJECT(element), "element-added", G_CALLBACK(elementAdded), session);
- g_signal_connect(G_OBJECT(element), "element-removed", G_CALLBACK(elementRemoved), session);
- } else if (!factory) {
- // no-op
-#if GST_CHECK_VERSION(0,10,31)
- } else if (gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_AUDIO_ENCODER)) {
-#else
- } else if (strstr(gst_element_factory_get_klass(factory), "Encoder/Audio") != NULL) {
-#endif
- session->m_audioEncoder = element;
-
- session->m_audioEncodeControl->applySettings(element);
-#if GST_CHECK_VERSION(0,10,31)
- } else if (gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_VIDEO_ENCODER)) {
-#else
- } else if (strstr(gst_element_factory_get_klass(factory), "Encoder/Video") != NULL) {
-#endif
- session->m_videoEncoder = element;
-
- session->m_videoEncodeControl->applySettings(element);
-#if GST_CHECK_VERSION(0,10,31)
- } else if (gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_MUXER)) {
-#else
- } else if (strstr(gst_element_factory_get_klass(factory), "Muxer") != NULL) {
-#endif
- session->m_muxer = element;
- }
-}
-
-void CameraBinSession::elementRemoved(GstBin *, GstElement *element, CameraBinSession *session)
-{
- if (element == session->m_audioEncoder)
- session->m_audioEncoder = 0;
- else if (element == session->m_videoEncoder)
- session->m_videoEncoder = 0;
- else if (element == session->m_muxer)
- session->m_muxer = 0;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinsession.h b/src/plugins/gstreamer/camerabin/camerabinsession.h
deleted file mode 100644
index 999398fa4..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinsession.h
+++ /dev/null
@@ -1,291 +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 CAMERABINCAPTURESESSION_H
-#define CAMERABINCAPTURESESSION_H
-
-#include <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include <qmediarecordercontrol.h>
-
-#include <QtCore/qurl.h>
-#include <QtCore/qdir.h>
-
-#include <gst/gst.h>
-#if QT_CONFIG(gstreamer_photography)
-#include <gst/interfaces/photography.h>
-#endif
-
-#include <private/qgstreamerbushelper_p.h>
-#include <private/qgstreamervideoprobecontrol_p.h>
-#include <private/qmediastoragelocation_p.h>
-#include "qcamera.h"
-
-QT_BEGIN_NAMESPACE
-
-class QGstreamerMessage;
-class QGstreamerBusHelper;
-class CameraBinControl;
-class CameraBinAudioEncoder;
-class CameraBinVideoEncoder;
-class CameraBinImageEncoder;
-class CameraBinRecorder;
-class CameraBinContainer;
-class CameraBinExposure;
-class CameraBinFlash;
-class CameraBinFocus;
-class CameraBinImageProcessing;
-class CameraBinLocks;
-class CameraBinZoom;
-class CameraBinCaptureDestination;
-class CameraBinCaptureBufferFormat;
-class QGstreamerVideoRendererInterface;
-class CameraBinViewfinderSettings;
-
-class QGstreamerElementFactory
-{
-public:
- virtual GstElement *buildElement() = 0;
-};
-
-class CameraBinSession : public QObject,
- public QGstreamerBusMessageFilter,
- public QGstreamerSyncMessageFilter
-{
- Q_OBJECT
- Q_PROPERTY(qint64 duration READ duration NOTIFY durationChanged)
- Q_INTERFACES(QGstreamerBusMessageFilter QGstreamerSyncMessageFilter)
-public:
- CameraBinSession(GstElementFactory *sourceFactory, QObject *parent);
- ~CameraBinSession();
-
-#if QT_CONFIG(gstreamer_photography)
- GstPhotography *photography();
-#endif
- GstElement *cameraBin() { return m_camerabin; }
- GstElement *cameraSource() { return m_cameraSrc; }
- QGstreamerBusHelper *bus() { return m_busHelper; }
-
- QList< QPair<int,int> > supportedFrameRates(const QSize &frameSize, bool *continuous) const;
- QList<QSize> supportedResolutions(QPair<int,int> rate, bool *continuous, QCamera::CaptureModes mode) const;
-
- QCamera::CaptureModes captureMode() { return m_captureMode; }
- void setCaptureMode(QCamera::CaptureModes mode);
-
- QUrl outputLocation() const;
- bool setOutputLocation(const QUrl& sink);
-
- GstElement *buildCameraSource();
- GstElementFactory *sourceFactory() const { return m_sourceFactory; }
-
- CameraBinControl *cameraControl() const { return m_cameraControl; }
- CameraBinAudioEncoder *audioEncodeControl() const { return m_audioEncodeControl; }
- CameraBinVideoEncoder *videoEncodeControl() const { return m_videoEncodeControl; }
- CameraBinImageEncoder *imageEncodeControl() const { return m_imageEncodeControl; }
-
-#if QT_CONFIG(gstreamer_photography)
- CameraBinExposure *cameraExposureControl();
- CameraBinFlash *cameraFlashControl();
- CameraBinFocus *cameraFocusControl();
- CameraBinLocks *cameraLocksControl();
-#endif
-
- CameraBinZoom *cameraZoomControl() const { return m_cameraZoomControl; }
- CameraBinImageProcessing *imageProcessingControl() const { return m_imageProcessingControl; }
- CameraBinCaptureDestination *captureDestinationControl() const { return m_captureDestinationControl; }
- CameraBinCaptureBufferFormat *captureBufferFormatControl() const { return m_captureBufferFormatControl; }
-
- CameraBinRecorder *recorderControl() const { return m_recorderControl; }
- CameraBinContainer *mediaContainerControl() const { return m_mediaContainerControl; }
-
- QGstreamerElementFactory *audioInput() const { return m_audioInputFactory; }
- void setAudioInput(QGstreamerElementFactory *audioInput);
-
- QGstreamerElementFactory *videoInput() const { return m_videoInputFactory; }
- void setVideoInput(QGstreamerElementFactory *videoInput);
- bool isReady() const;
-
- QObject *viewfinder() const { return m_viewfinder; }
- void setViewfinder(QObject *viewfinder);
-
- QList<QCameraViewfinderSettings> supportedViewfinderSettings() const;
- QCameraViewfinderSettings viewfinderSettings() const;
- void setViewfinderSettings(const QCameraViewfinderSettings &settings) { m_viewfinderSettings = settings; }
-
- void captureImage(int requestId, const QString &fileName);
-
- QCamera::Status status() const;
- QCamera::State pendingState() const;
- bool isBusy() const;
-
- qint64 duration() const;
-
- void recordVideo();
- void stopVideoRecording();
-
- bool isMuted() const;
-
- QString device() const { return m_inputDevice; }
-
- bool processSyncMessage(const QGstreamerMessage &message) override;
- bool processBusMessage(const QGstreamerMessage &message) override;
-
- QGstreamerVideoProbeControl *videoProbe();
-
-signals:
- void statusChanged(QCamera::Status status);
- void pendingStateChanged(QCamera::State state);
- void durationChanged(qint64 duration);
- void error(int error, const QString &errorString);
- void imageExposed(int requestId);
- void imageCaptured(int requestId, const QImage &img);
- void mutedChanged(bool);
- void viewfinderChanged();
- void readyChanged(bool);
- void busyChanged(bool);
-
-public slots:
- void setDevice(const QString &device);
- void setState(QCamera::State);
- void setCaptureDevice(const QString &deviceName);
- void setMetaData(const QMap<QByteArray, QVariant>&);
- void setMuted(bool);
-
-private slots:
- void handleViewfinderChange();
- void setupCaptureResolution();
-
-private:
- void load();
- void unload();
- void start();
- void stop();
-
- void setStatus(QCamera::Status status);
- void setStateHelper(QCamera::State state);
- void setError(int error, const QString &errorString);
-
- bool setupCameraBin();
- void setAudioCaptureCaps();
- GstCaps *supportedCaps(QCamera::CaptureModes mode) const;
- static void updateBusyStatus(GObject *o, GParamSpec *p, gpointer d);
-
- QString currentContainerFormat() const;
-
- static void elementAdded(GstBin *bin, GstElement *element, CameraBinSession *session);
- static void elementRemoved(GstBin *bin, GstElement *element, CameraBinSession *session);
-
- QUrl m_sink;
- QUrl m_actualSink;
- bool m_recordingActive;
- QString m_captureDevice;
- QCamera::Status m_status;
- QCamera::State m_pendingState;
- QString m_inputDevice;
- bool m_muted;
- bool m_busy;
- QMediaStorageLocation m_mediaStorageLocation;
-
- QCamera::CaptureModes m_captureMode;
- QMap<QByteArray, QVariant> m_metaData;
-
- QGstreamerElementFactory *m_audioInputFactory;
- QGstreamerElementFactory *m_videoInputFactory;
- QObject *m_viewfinder;
- QGstreamerVideoRendererInterface *m_viewfinderInterface;
- mutable QList<QCameraViewfinderSettings> m_supportedViewfinderSettings;
- QCameraViewfinderSettings m_viewfinderSettings;
- QCameraViewfinderSettings m_actualViewfinderSettings;
-
- CameraBinControl *m_cameraControl;
- CameraBinAudioEncoder *m_audioEncodeControl;
- CameraBinVideoEncoder *m_videoEncodeControl;
- CameraBinImageEncoder *m_imageEncodeControl;
- CameraBinRecorder *m_recorderControl;
- CameraBinContainer *m_mediaContainerControl;
-#if QT_CONFIG(gstreamer_photography)
- CameraBinExposure *m_cameraExposureControl;
- CameraBinFlash *m_cameraFlashControl;
- CameraBinFocus *m_cameraFocusControl;
- CameraBinLocks *m_cameraLocksControl;
-#endif
- CameraBinZoom *m_cameraZoomControl;
- CameraBinImageProcessing *m_imageProcessingControl;
- CameraBinCaptureDestination *m_captureDestinationControl;
- CameraBinCaptureBufferFormat *m_captureBufferFormatControl;
-
- QGstreamerBusHelper *m_busHelper;
- GstBus* m_bus;
- GstElement *m_camerabin;
- GstElement *m_cameraSrc;
- GstElement *m_videoSrc;
- GstElement *m_viewfinderElement;
- GstElementFactory *m_sourceFactory;
- bool m_viewfinderHasChanged;
- bool m_inputDeviceHasChanged;
- bool m_usingWrapperCameraBinSrc;
-
- class ViewfinderProbe : public QGstreamerVideoProbeControl {
- public:
- ViewfinderProbe(CameraBinSession *s)
- : QGstreamerVideoProbeControl(s)
- , session(s)
- {}
-
- void probeCaps(GstCaps *caps) override;
-
- private:
- CameraBinSession * const session;
- } m_viewfinderProbe;
-
- GstElement *m_audioSrc;
- GstElement *m_audioConvert;
- GstElement *m_capsFilter;
- GstElement *m_fileSink;
- GstElement *m_audioEncoder;
- GstElement *m_videoEncoder;
- GstElement *m_muxer;
-
-public:
- QString m_imageFileName;
- int m_requestId;
-};
-
-QT_END_NAMESPACE
-
-#endif // CAMERABINCAPTURESESSION_H
diff --git a/src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.cpp b/src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.cpp
deleted file mode 100644
index 5b0c0d234..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.cpp
+++ /dev/null
@@ -1,316 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Denis Shienkov <denis.shienkov@gmail.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "camerabinv4limageprocessing.h"
-#include "camerabinsession.h"
-
-#include <QDebug>
-
-#include <private/qcore_unix_p.h>
-#include <linux/videodev2.h>
-
-QT_BEGIN_NAMESPACE
-
-CameraBinV4LImageProcessing::CameraBinV4LImageProcessing(CameraBinSession *session)
- : QCameraImageProcessingControl(session)
- , m_session(session)
-{
-}
-
-CameraBinV4LImageProcessing::~CameraBinV4LImageProcessing()
-{
-}
-
-bool CameraBinV4LImageProcessing::isParameterSupported(
- ProcessingParameter parameter) const
-{
- return m_parametersInfo.contains(parameter);
-}
-
-bool CameraBinV4LImageProcessing::isParameterValueSupported(
- ProcessingParameter parameter, const QVariant &value) const
-{
- QMap<ProcessingParameter, SourceParameterValueInfo>::const_iterator sourceValueInfo =
- m_parametersInfo.constFind(parameter);
- if (sourceValueInfo == m_parametersInfo.constEnd())
- return false;
-
- switch (parameter) {
-
- case QCameraImageProcessingControl::WhiteBalancePreset: {
- const QCameraImageProcessing::WhiteBalanceMode checkedValue =
- value.value<QCameraImageProcessing::WhiteBalanceMode>();
- const QCameraImageProcessing::WhiteBalanceMode firstAllowedValue =
- (*sourceValueInfo).minimumValue ? QCameraImageProcessing::WhiteBalanceAuto
- : QCameraImageProcessing::WhiteBalanceManual;
- const QCameraImageProcessing::WhiteBalanceMode secondAllowedValue =
- (*sourceValueInfo).maximumValue ? QCameraImageProcessing::WhiteBalanceAuto
- : QCameraImageProcessing::WhiteBalanceManual;
- if (checkedValue != firstAllowedValue
- && checkedValue != secondAllowedValue) {
- return false;
- }
- }
- break;
-
- case QCameraImageProcessingControl::ColorTemperature: {
- const qint32 checkedValue = value.toInt();
- if (checkedValue < (*sourceValueInfo).minimumValue
- || checkedValue > (*sourceValueInfo).maximumValue) {
- return false;
- }
- }
- break;
-
- case QCameraImageProcessingControl::ContrastAdjustment: // falling back
- case QCameraImageProcessingControl::SaturationAdjustment: // falling back
- case QCameraImageProcessingControl::BrightnessAdjustment: // falling back
- case QCameraImageProcessingControl::SharpeningAdjustment: {
- const qint32 sourceValue = sourceImageProcessingParameterValue(
- value.toReal(), (*sourceValueInfo));
- if (sourceValue < (*sourceValueInfo).minimumValue
- || sourceValue > (*sourceValueInfo).maximumValue) {
- return false;
- }
- }
- break;
-
- default:
- return false;
- }
-
- return true;
-}
-
-QVariant CameraBinV4LImageProcessing::parameter(
- ProcessingParameter parameter) const
-{
- QMap<ProcessingParameter, SourceParameterValueInfo>::const_iterator sourceValueInfo =
- m_parametersInfo.constFind(parameter);
- if (sourceValueInfo == m_parametersInfo.constEnd()) {
- if (!m_parametersInfo.empty())
- qWarning() << "Unable to get the unsupported parameter:" << parameter;
- return QVariant();
- }
-
- const QString deviceName = m_session->device();
- const int fd = qt_safe_open(deviceName.toLocal8Bit().constData(), O_RDONLY);
- if (fd == -1) {
- qWarning() << "Unable to open the camera" << deviceName
- << "for read to get the parameter value:" << qt_error_string(errno);
- return QVariant();
- }
-
- struct v4l2_control control;
- ::memset(&control, 0, sizeof(control));
- control.id = (*sourceValueInfo).cid;
-
- const bool ret = (::ioctl(fd, VIDIOC_G_CTRL, &control) == 0);
-
- qt_safe_close(fd);
-
- if (!ret) {
- qWarning() << "Unable to get the parameter value:" << parameter << ":" << qt_error_string(errno);
- return QVariant();
- }
-
- switch (parameter) {
-
- case QCameraImageProcessingControl::WhiteBalancePreset:
- return QVariant::fromValue<QCameraImageProcessing::WhiteBalanceMode>(
- control.value ? QCameraImageProcessing::WhiteBalanceAuto
- : QCameraImageProcessing::WhiteBalanceManual);
-
- case QCameraImageProcessingControl::ColorTemperature:
- return QVariant::fromValue<qint32>(control.value);
-
- case QCameraImageProcessingControl::ContrastAdjustment: // falling back
- case QCameraImageProcessingControl::SaturationAdjustment: // falling back
- case QCameraImageProcessingControl::BrightnessAdjustment: // falling back
- case QCameraImageProcessingControl::SharpeningAdjustment: {
- return scaledImageProcessingParameterValue(
- control.value, (*sourceValueInfo));
- }
-
- default:
- return QVariant();
- }
-}
-
-void CameraBinV4LImageProcessing::setParameter(
- ProcessingParameter parameter, const QVariant &value)
-{
- QMap<ProcessingParameter, SourceParameterValueInfo>::const_iterator sourceValueInfo =
- m_parametersInfo.constFind(parameter);
- if (sourceValueInfo == m_parametersInfo.constEnd()) {
- if (!m_parametersInfo.empty())
- qWarning() << "Unable to set the unsupported parameter:" << parameter;
- return;
- }
-
- const QString deviceName = m_session->device();
- const int fd = qt_safe_open(deviceName.toLocal8Bit().constData(), O_WRONLY);
- if (fd == -1) {
- qWarning() << "Unable to open the camera" << deviceName
- << "for write to set the parameter value:" << qt_error_string(errno);
- return;
- }
-
- struct v4l2_control control;
- ::memset(&control, 0, sizeof(control));
- control.id = (*sourceValueInfo).cid;
-
- switch (parameter) {
-
- case QCameraImageProcessingControl::WhiteBalancePreset: {
- const QCameraImageProcessing::WhiteBalanceMode m =
- value.value<QCameraImageProcessing::WhiteBalanceMode>();
- if (m != QCameraImageProcessing::WhiteBalanceAuto
- && m != QCameraImageProcessing::WhiteBalanceManual) {
- qt_safe_close(fd);
- return;
- }
-
- control.value = (m == QCameraImageProcessing::WhiteBalanceAuto) ? true : false;
- }
- break;
-
- case QCameraImageProcessingControl::ColorTemperature:
- control.value = value.toInt();
- break;
-
- case QCameraImageProcessingControl::ContrastAdjustment: // falling back
- case QCameraImageProcessingControl::SaturationAdjustment: // falling back
- case QCameraImageProcessingControl::BrightnessAdjustment: // falling back
- case QCameraImageProcessingControl::SharpeningAdjustment:
- control.value = sourceImageProcessingParameterValue(
- value.toReal(), (*sourceValueInfo));
- break;
-
- default:
- qt_safe_close(fd);
- return;
- }
-
- if (::ioctl(fd, VIDIOC_S_CTRL, &control) != 0)
- qWarning() << "Unable to set the parameter value:" << parameter << ":" << qt_error_string(errno);
-
- qt_safe_close(fd);
-}
-
-void CameraBinV4LImageProcessing::updateParametersInfo(
- QCamera::Status cameraStatus)
-{
- if (cameraStatus == QCamera::UnloadedStatus)
- m_parametersInfo.clear();
- else if (cameraStatus == QCamera::LoadedStatus) {
- const QString deviceName = m_session->device();
- const int fd = qt_safe_open(deviceName.toLocal8Bit().constData(), O_RDONLY);
- if (fd == -1) {
- qWarning() << "Unable to open the camera" << deviceName
- << "for read to query the parameter info:" << qt_error_string(errno);
- return;
- }
-
- static const struct SupportedParameterEntry {
- quint32 cid;
- QCameraImageProcessingControl::ProcessingParameter parameter;
- } supportedParametersEntries[] = {
- { V4L2_CID_AUTO_WHITE_BALANCE, QCameraImageProcessingControl::WhiteBalancePreset },
- { V4L2_CID_WHITE_BALANCE_TEMPERATURE, QCameraImageProcessingControl::ColorTemperature },
- { V4L2_CID_CONTRAST, QCameraImageProcessingControl::ContrastAdjustment },
- { V4L2_CID_SATURATION, QCameraImageProcessingControl::SaturationAdjustment },
- { V4L2_CID_BRIGHTNESS, QCameraImageProcessingControl::BrightnessAdjustment },
- { V4L2_CID_SHARPNESS, QCameraImageProcessingControl::SharpeningAdjustment }
- };
-
- for (int i = 0; i < int(sizeof(supportedParametersEntries) / sizeof(SupportedParameterEntry)); ++i) {
- struct v4l2_queryctrl queryControl;
- ::memset(&queryControl, 0, sizeof(queryControl));
- queryControl.id = supportedParametersEntries[i].cid;
-
- if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) != 0) {
- qWarning() << "Unable to query the parameter info:" << supportedParametersEntries[i].parameter
- << ":" << qt_error_string(errno);
- continue;
- }
-
- SourceParameterValueInfo sourceValueInfo;
- sourceValueInfo.cid = queryControl.id;
- sourceValueInfo.defaultValue = queryControl.default_value;
- sourceValueInfo.maximumValue = queryControl.maximum;
- sourceValueInfo.minimumValue = queryControl.minimum;
-
- m_parametersInfo.insert(supportedParametersEntries[i].parameter, sourceValueInfo);
- }
-
- qt_safe_close(fd);
- }
-}
-
-qreal CameraBinV4LImageProcessing::scaledImageProcessingParameterValue(
- qint32 sourceValue, const SourceParameterValueInfo &sourceValueInfo)
-{
- if (sourceValue == sourceValueInfo.defaultValue) {
- return 0.0f;
- } else if (sourceValue < sourceValueInfo.defaultValue) {
- return ((sourceValue - sourceValueInfo.minimumValue)
- / qreal(sourceValueInfo.defaultValue - sourceValueInfo.minimumValue))
- + (-1.0f);
- } else {
- return ((sourceValue - sourceValueInfo.defaultValue)
- / qreal(sourceValueInfo.maximumValue - sourceValueInfo.defaultValue));
- }
-}
-
-qint32 CameraBinV4LImageProcessing::sourceImageProcessingParameterValue(
- qreal scaledValue, const SourceParameterValueInfo &valueRange)
-{
- if (qFuzzyIsNull(scaledValue)) {
- return valueRange.defaultValue;
- } else if (scaledValue < 0.0f) {
- return ((scaledValue - (-1.0f)) * (valueRange.defaultValue - valueRange.minimumValue))
- + valueRange.minimumValue;
- } else {
- return (scaledValue * (valueRange.maximumValue - valueRange.defaultValue))
- + valueRange.defaultValue;
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.h b/src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.h
deleted file mode 100644
index a38dc78da..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Denis Shienkov <denis.shienkov@gmail.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 CAMERABINV4LIMAGEPROCESSINGCONTROL_H
-#define CAMERABINV4LIMAGEPROCESSINGCONTROL_H
-
-#include <qcamera.h>
-#include <qcameraimageprocessingcontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class CameraBinSession;
-
-class CameraBinV4LImageProcessing : public QCameraImageProcessingControl
-{
- Q_OBJECT
-
-public:
- CameraBinV4LImageProcessing(CameraBinSession *session);
- virtual ~CameraBinV4LImageProcessing();
-
- bool isParameterSupported(ProcessingParameter) const override;
- bool isParameterValueSupported(ProcessingParameter parameter, const QVariant &value) const override;
- QVariant parameter(ProcessingParameter parameter) const override;
- void setParameter(ProcessingParameter parameter, const QVariant &value) override;
-
-public slots:
- void updateParametersInfo(QCamera::Status cameraStatus);
-
-private:
- struct SourceParameterValueInfo {
- SourceParameterValueInfo()
- : cid(0)
- {
- }
-
- qint32 defaultValue;
- qint32 minimumValue;
- qint32 maximumValue;
- quint32 cid; // V4L control id
- };
-
- static qreal scaledImageProcessingParameterValue(
- qint32 sourceValue, const SourceParameterValueInfo &sourceValueInfo);
- static qint32 sourceImageProcessingParameterValue(
- qreal scaledValue, const SourceParameterValueInfo &valueRange);
-private:
- CameraBinSession *m_session;
- QMap<ProcessingParameter, SourceParameterValueInfo> m_parametersInfo;
-};
-
-QT_END_NAMESPACE
-
-#endif // CAMERABINV4LIMAGEPROCESSINGCONTROL_H
diff --git a/src/plugins/gstreamer/camerabin/camerabinvideoencoder.cpp b/src/plugins/gstreamer/camerabin/camerabinvideoencoder.cpp
deleted file mode 100644
index 5bba2ddb5..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinvideoencoder.cpp
+++ /dev/null
@@ -1,239 +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 <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include "camerabinvideoencoder.h"
-#include "camerabinsession.h"
-#include "camerabincontainer.h"
-#include <private/qgstutils_p.h>
-
-#include <QtCore/qdebug.h>
-
-QT_BEGIN_NAMESPACE
-
-CameraBinVideoEncoder::CameraBinVideoEncoder(CameraBinSession *session)
- :QVideoEncoderSettingsControl(session)
- , m_session(session)
-#if QT_CONFIG(gstreamer_encodingprofiles)
- , m_codecs(QGstCodecsInfo::VideoEncoder)
-#endif
-{
-}
-
-CameraBinVideoEncoder::~CameraBinVideoEncoder()
-{
-}
-
-QList<QSize> CameraBinVideoEncoder::supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous) const
-{
- if (continuous)
- *continuous = false;
-
- QPair<int,int> rate = rateAsRational(settings.frameRate());
-
- //select the closest supported rational rate to settings.frameRate()
-
- return m_session->supportedResolutions(rate, continuous, QCamera::CaptureVideo);
-}
-
-QList< qreal > CameraBinVideoEncoder::supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous) const
-{
- if (continuous)
- *continuous = false;
-
- QList< qreal > res;
-
- const auto rates = m_session->supportedFrameRates(settings.resolution(), continuous);
- for (const auto &rate : rates) {
- if (rate.second > 0)
- res << qreal(rate.first)/rate.second;
- }
-
- return res;
-}
-
-QStringList CameraBinVideoEncoder::supportedVideoCodecs() const
-{
-#if QT_CONFIG(gstreamer_encodingprofiles)
- return m_codecs.supportedCodecs();
-#else
- return QStringList();
-#endif
-}
-
-QString CameraBinVideoEncoder::videoCodecDescription(const QString &codecName) const
-{
-#if QT_CONFIG(gstreamer_encodingprofiles)
- return m_codecs.codecDescription(codecName);
-#else
- Q_UNUSED(codecName);
- return QString();
-#endif
-}
-
-QVideoEncoderSettings CameraBinVideoEncoder::videoSettings() const
-{
- return m_videoSettings;
-}
-
-void CameraBinVideoEncoder::setVideoSettings(const QVideoEncoderSettings &settings)
-{
- if (m_videoSettings != settings) {
- m_actualVideoSettings = settings;
- m_videoSettings = settings;
- emit settingsChanged();
- }
-}
-
-QVideoEncoderSettings CameraBinVideoEncoder::actualVideoSettings() const
-{
- return m_actualVideoSettings;
-}
-
-void CameraBinVideoEncoder::setActualVideoSettings(const QVideoEncoderSettings &settings)
-{
- m_actualVideoSettings = settings;
-}
-
-void CameraBinVideoEncoder::resetActualSettings()
-{
- m_actualVideoSettings = m_videoSettings;
-}
-
-
-QPair<int,int> CameraBinVideoEncoder::rateAsRational(qreal frameRate) const
-{
- if (frameRate > 0.001) {
- //convert to rational number
- QList<int> denumCandidates;
- denumCandidates << 1 << 2 << 3 << 5 << 10 << 25 << 30 << 50 << 100 << 1001 << 1000;
-
- qreal error = 1.0;
- int num = 1;
- int denum = 1;
-
- for (int curDenum : qAsConst(denumCandidates)) {
- int curNum = qRound(frameRate*curDenum);
- qreal curError = qAbs(qreal(curNum)/curDenum - frameRate);
-
- if (curError < error) {
- error = curError;
- num = curNum;
- denum = curDenum;
- }
-
- if (curError < 1e-8)
- break;
- }
-
- return QPair<int,int>(num,denum);
- }
-
- return QPair<int,int>();
-}
-
-#if QT_CONFIG(gstreamer_encodingprofiles)
-
-GstEncodingProfile *CameraBinVideoEncoder::createProfile()
-{
- QString codec = m_actualVideoSettings.codec();
- GstCaps *caps = !codec.isEmpty() ? gst_caps_from_string(codec.toLatin1()) : nullptr;
-
- if (!caps)
- return nullptr;
-
- QString preset = m_actualVideoSettings.encodingOption(QStringLiteral("preset")).toString();
- GstEncodingVideoProfile *profile = gst_encoding_video_profile_new(
- caps,
- !preset.isEmpty() ? preset.toLatin1().constData() : NULL, //preset
- NULL, //restriction
- 1); //presence
-
- gst_caps_unref(caps);
-
- gst_encoding_video_profile_set_pass(profile, 0);
- gst_encoding_video_profile_set_variableframerate(profile, TRUE);
-
- return (GstEncodingProfile *)profile;
-}
-
-#endif
-
-void CameraBinVideoEncoder::applySettings(GstElement *encoder)
-{
- GObjectClass * const objectClass = G_OBJECT_GET_CLASS(encoder);
- const char * const name = qt_gst_element_get_factory_name(encoder);
-
- const int bitRate = m_actualVideoSettings.bitRate();
- if (bitRate == -1) {
- // Bit rate is invalid, don't evaluate the remaining conditions.
- } else if (g_object_class_find_property(objectClass, "bitrate")) {
- g_object_set(G_OBJECT(encoder), "bitrate", bitRate, NULL);
- } else if (g_object_class_find_property(objectClass, "target-bitrate")) {
- g_object_set(G_OBJECT(encoder), "target-bitrate", bitRate, NULL);
- }
-
- if (qstrcmp(name, "theoraenc") == 0) {
- static const int qualities[] = { 8, 16, 32, 45, 60 };
- g_object_set(G_OBJECT(encoder), "quality", qualities[m_actualVideoSettings.quality()], NULL);
- } else if (qstrncmp(name, "avenc_", 6) == 0) {
- if (g_object_class_find_property(objectClass, "pass")) {
- static const int modes[] = { 0, 2, 512, 1024 };
- g_object_set(G_OBJECT(encoder), "pass", modes[m_actualVideoSettings.encodingMode()], NULL);
- }
- if (g_object_class_find_property(objectClass, "quantizer")) {
- static const double qualities[] = { 20, 8.0, 3.0, 2.5, 2.0 };
- g_object_set(G_OBJECT(encoder), "quantizer", qualities[m_actualVideoSettings.quality()], NULL);
- }
- } else if (qstrncmp(name, "omx", 3) == 0) {
- if (!g_object_class_find_property(objectClass, "control-rate")) {
- } else switch (m_actualVideoSettings.encodingMode()) {
- case QMultimedia::ConstantBitRateEncoding:
- g_object_set(G_OBJECT(encoder), "control-rate", 2, NULL);
- break;
- case QMultimedia::AverageBitRateEncoding:
- g_object_set(G_OBJECT(encoder), "control-rate", 1, NULL);
- break;
- default:
- g_object_set(G_OBJECT(encoder), "control-rate", 0, NULL);
- }
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinvideoencoder.h b/src/plugins/gstreamer/camerabin/camerabinvideoencoder.h
deleted file mode 100644
index 24013ceab..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinvideoencoder.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 CAMERABINVIDEOENCODE_H
-#define CAMERABINVIDEOENCODE_H
-
-#include <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include <qvideoencodersettingscontrol.h>
-
-#include <QtCore/qstringlist.h>
-#include <QtCore/qmap.h>
-#include <QtCore/qset.h>
-
-#include <gst/gst.h>
-#include <gst/pbutils/pbutils.h>
-
-#if QT_CONFIG(gstreamer_encodingprofiles)
-#include <gst/pbutils/encoding-profile.h>
-#include <private/qgstcodecsinfo_p.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class CameraBinSession;
-
-class CameraBinVideoEncoder : public QVideoEncoderSettingsControl
-{
- Q_OBJECT
-public:
- CameraBinVideoEncoder(CameraBinSession *session);
- virtual ~CameraBinVideoEncoder();
-
- QList<QSize> supportedResolutions(const QVideoEncoderSettings &settings = QVideoEncoderSettings(),
- bool *continuous = 0) const override;
-
- QList< qreal > supportedFrameRates(const QVideoEncoderSettings &settings = QVideoEncoderSettings(),
- bool *continuous = 0) const override;
-
- QPair<int,int> rateAsRational(qreal) const;
-
- QStringList supportedVideoCodecs() const override;
- QString videoCodecDescription(const QString &codecName) const override;
-
- QVideoEncoderSettings videoSettings() const override;
- void setVideoSettings(const QVideoEncoderSettings &settings) override;
-
- QVideoEncoderSettings actualVideoSettings() const;
- void setActualVideoSettings(const QVideoEncoderSettings&);
- void resetActualSettings();
-
-#if QT_CONFIG(gstreamer_encodingprofiles)
- GstEncodingProfile *createProfile();
-#endif
-
- void applySettings(GstElement *encoder);
-
-Q_SIGNALS:
- void settingsChanged();
-
-private:
- CameraBinSession *m_session;
-
-#if QT_CONFIG(gstreamer_encodingprofiles)
- QGstCodecsInfo m_codecs;
-#endif
-
- QVideoEncoderSettings m_actualVideoSettings;
- QVideoEncoderSettings m_videoSettings;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/gstreamer/camerabin/camerabinviewfindersettings.cpp b/src/plugins/gstreamer/camerabin/camerabinviewfindersettings.cpp
deleted file mode 100644
index 0e14a15e5..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinviewfindersettings.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Jolla Ltd.
-** 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 "camerabinviewfindersettings.h"
-#include "camerabinsession.h"
-
-QT_BEGIN_NAMESPACE
-
-
-CameraBinViewfinderSettings::CameraBinViewfinderSettings(CameraBinSession *session)
- : QCameraViewfinderSettingsControl(session)
- , m_session(session)
-{
-}
-
-CameraBinViewfinderSettings::~CameraBinViewfinderSettings()
-{
-}
-
-bool CameraBinViewfinderSettings::isViewfinderParameterSupported(ViewfinderParameter parameter) const
-{
- switch (parameter) {
- case Resolution:
- case PixelAspectRatio:
- case MinimumFrameRate:
- case MaximumFrameRate:
- case PixelFormat:
- return true;
- case UserParameter:
- return false;
- }
- return false;
-}
-
-QVariant CameraBinViewfinderSettings::viewfinderParameter(ViewfinderParameter parameter) const
-{
- switch (parameter) {
- case Resolution:
- return m_session->viewfinderSettings().resolution();
- case PixelAspectRatio:
- return m_session->viewfinderSettings().pixelAspectRatio();
- case MinimumFrameRate:
- return m_session->viewfinderSettings().minimumFrameRate();
- case MaximumFrameRate:
- return m_session->viewfinderSettings().maximumFrameRate();
- case PixelFormat:
- return m_session->viewfinderSettings().pixelFormat();
- case UserParameter:
- return QVariant();
- }
- return false;
-}
-
-void CameraBinViewfinderSettings::setViewfinderParameter(ViewfinderParameter parameter, const QVariant &value)
-{
- QCameraViewfinderSettings settings = m_session->viewfinderSettings();
-
- switch (parameter) {
- case Resolution:
- settings.setResolution(value.toSize());
- break;
- case PixelAspectRatio:
- settings.setPixelAspectRatio(value.toSize());
- break;
- case MinimumFrameRate:
- settings.setMinimumFrameRate(value.toReal());
- break;
- case MaximumFrameRate:
- settings.setMaximumFrameRate(value.toReal());
- break;
- case PixelFormat:
- settings.setPixelFormat(qvariant_cast<QVideoFrame::PixelFormat>(value));
- case UserParameter:
- break;
- }
-
- m_session->setViewfinderSettings(settings);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinviewfindersettings.h b/src/plugins/gstreamer/camerabin/camerabinviewfindersettings.h
deleted file mode 100644
index 59a0ca8a9..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinviewfindersettings.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Jolla Ltd.
-** 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 CAMERABINVIEWFINDERSETTINGS_H
-#define CAMERABINVIEWFINDERSETTINGS_H
-
-#include <qcameraviewfindersettingscontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class CameraBinSession;
-
-class CameraBinViewfinderSettings : public QCameraViewfinderSettingsControl
-{
- Q_OBJECT
-public:
- CameraBinViewfinderSettings(CameraBinSession *session);
- ~CameraBinViewfinderSettings();
-
- bool isViewfinderParameterSupported(ViewfinderParameter parameter) const override;
- QVariant viewfinderParameter(ViewfinderParameter parameter) const override;
- void setViewfinderParameter(ViewfinderParameter parameter, const QVariant &value) override;
-
-private:
- CameraBinSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/gstreamer/camerabin/camerabinviewfindersettings2.cpp b/src/plugins/gstreamer/camerabin/camerabinviewfindersettings2.cpp
deleted file mode 100644
index a7199216e..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinviewfindersettings2.cpp
+++ /dev/null
@@ -1,73 +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 "camerabinviewfindersettings2.h"
-#include "camerabinsession.h"
-
-QT_BEGIN_NAMESPACE
-
-CameraBinViewfinderSettings2::CameraBinViewfinderSettings2(CameraBinSession *session)
- : QCameraViewfinderSettingsControl2(session)
- , m_session(session)
-{
-
-}
-
-CameraBinViewfinderSettings2::~CameraBinViewfinderSettings2()
-{
-
-}
-
-QList<QCameraViewfinderSettings> CameraBinViewfinderSettings2::supportedViewfinderSettings() const
-{
- return m_session->supportedViewfinderSettings();
-}
-
-QCameraViewfinderSettings CameraBinViewfinderSettings2::viewfinderSettings() const
-{
- return m_session->viewfinderSettings();
-}
-
-void CameraBinViewfinderSettings2::setViewfinderSettings(const QCameraViewfinderSettings &settings)
-{
- m_session->setViewfinderSettings(settings);
-}
-
-QT_END_NAMESPACE
-
diff --git a/src/plugins/gstreamer/camerabin/camerabinviewfindersettings2.h b/src/plugins/gstreamer/camerabin/camerabinviewfindersettings2.h
deleted file mode 100644
index 5e03b007a..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinviewfindersettings2.h
+++ /dev/null
@@ -1,67 +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 CAMERABINVIEWFINDERSETTINGS2_H
-#define CAMERABINVIEWFINDERSETTINGS2_H
-
-#include <qcameraviewfindersettingscontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class CameraBinSession;
-
-class CameraBinViewfinderSettings2 : public QCameraViewfinderSettingsControl2
-{
- Q_OBJECT
-public:
- CameraBinViewfinderSettings2(CameraBinSession *session);
- ~CameraBinViewfinderSettings2();
-
- QList<QCameraViewfinderSettings> supportedViewfinderSettings() const override;
-
- QCameraViewfinderSettings viewfinderSettings() const override;
- void setViewfinderSettings(const QCameraViewfinderSettings &settings) override;
-
-private:
- CameraBinSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif // CAMERABINVIEWFINDERSETTINGS2_H
diff --git a/src/plugins/gstreamer/camerabin/camerabinzoom.cpp b/src/plugins/gstreamer/camerabin/camerabinzoom.cpp
deleted file mode 100644
index 401e13207..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinzoom.cpp
+++ /dev/null
@@ -1,147 +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 "camerabinzoom.h"
-#include "camerabinsession.h"
-
-#define ZOOM_PROPERTY "zoom"
-#define MAX_ZOOM_PROPERTY "max-zoom"
-
-QT_BEGIN_NAMESPACE
-
-CameraBinZoom::CameraBinZoom(CameraBinSession *session)
- : QCameraZoomControl(session)
- , m_session(session)
- , m_requestedOpticalZoom(1.0)
- , m_requestedDigitalZoom(1.0)
-{
- GstElement *camerabin = m_session->cameraBin();
- g_signal_connect(G_OBJECT(camerabin), "notify::zoom", G_CALLBACK(updateZoom), this);
- g_signal_connect(G_OBJECT(camerabin), "notify::max-zoom", G_CALLBACK(updateMaxZoom), this);
-}
-
-CameraBinZoom::~CameraBinZoom()
-{
-}
-
-qreal CameraBinZoom::maximumOpticalZoom() const
-{
- return 1.0;
-}
-
-qreal CameraBinZoom::maximumDigitalZoom() const
-{
- gfloat zoomFactor = 1.0;
- g_object_get(GST_BIN(m_session->cameraBin()), MAX_ZOOM_PROPERTY, &zoomFactor, NULL);
- return zoomFactor;
-}
-
-qreal CameraBinZoom::requestedDigitalZoom() const
-{
- return m_requestedDigitalZoom;
-}
-
-qreal CameraBinZoom::requestedOpticalZoom() const
-{
- return m_requestedOpticalZoom;
-}
-
-qreal CameraBinZoom::currentOpticalZoom() const
-{
- return 1.0;
-}
-
-qreal CameraBinZoom::currentDigitalZoom() const
-{
- gfloat zoomFactor = 1.0;
- g_object_get(GST_BIN(m_session->cameraBin()), ZOOM_PROPERTY, &zoomFactor, NULL);
- return zoomFactor;
-}
-
-void CameraBinZoom::zoomTo(qreal optical, qreal digital)
-{
- qreal oldDigitalZoom = currentDigitalZoom();
-
- if (m_requestedDigitalZoom != digital) {
- m_requestedDigitalZoom = digital;
- emit requestedDigitalZoomChanged(digital);
- }
-
- if (m_requestedOpticalZoom != optical) {
- m_requestedOpticalZoom = optical;
- emit requestedOpticalZoomChanged(optical);
- }
-
- digital = qBound(qreal(1.0), digital, maximumDigitalZoom());
- g_object_set(GST_BIN(m_session->cameraBin()), ZOOM_PROPERTY, digital, NULL);
-
- qreal newDigitalZoom = currentDigitalZoom();
- if (!qFuzzyCompare(oldDigitalZoom, newDigitalZoom))
- emit currentDigitalZoomChanged(digital);
-}
-
-void CameraBinZoom::updateZoom(GObject *o, GParamSpec *p, gpointer d)
-{
- Q_UNUSED(p);
-
- gfloat zoomFactor = 1.0;
- g_object_get(o, ZOOM_PROPERTY, &zoomFactor, NULL);
-
- CameraBinZoom *zoom = reinterpret_cast<CameraBinZoom *>(d);
-
- QMetaObject::invokeMethod(zoom, "currentDigitalZoomChanged",
- Qt::QueuedConnection,
- Q_ARG(qreal, zoomFactor));
-}
-
-void CameraBinZoom::updateMaxZoom(GObject *o, GParamSpec *p, gpointer d)
-{
- Q_UNUSED(p);
-
- gfloat zoomFactor = 1.0;
- g_object_get(o, MAX_ZOOM_PROPERTY, &zoomFactor, NULL);
-
- CameraBinZoom *zoom = reinterpret_cast<CameraBinZoom *>(d);
-
- QMetaObject::invokeMethod(zoom, "maximumDigitalZoomChanged",
- Qt::QueuedConnection,
- Q_ARG(qreal, zoomFactor));
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinzoom.h b/src/plugins/gstreamer/camerabin/camerabinzoom.h
deleted file mode 100644
index 858ada2da..000000000
--- a/src/plugins/gstreamer/camerabin/camerabinzoom.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 CAMERABINZOOMCONTROL_H
-#define CAMERABINZOOMCONTROL_H
-
-#include <qcamerazoomcontrol.h>
-#include <gst/gst.h>
-
-QT_BEGIN_NAMESPACE
-
-class CameraBinSession;
-
-class CameraBinZoom : public QCameraZoomControl
-{
- Q_OBJECT
-public:
- CameraBinZoom(CameraBinSession *session);
- virtual ~CameraBinZoom();
-
- qreal maximumOpticalZoom() const override;
- qreal maximumDigitalZoom() const override;
-
- qreal requestedOpticalZoom() const override;
- qreal requestedDigitalZoom() const override;
- qreal currentOpticalZoom() const override;
- qreal currentDigitalZoom() const override;
-
- void zoomTo(qreal optical, qreal digital) override;
-
-private:
- static void updateZoom(GObject *o, GParamSpec *p, gpointer d);
- static void updateMaxZoom(GObject *o, GParamSpec *p, gpointer d);
-
- CameraBinSession *m_session;
- qreal m_requestedOpticalZoom;
- qreal m_requestedDigitalZoom;
-};
-
-QT_END_NAMESPACE
-
-#endif // CAMERABINZOOMCONTROL_H
diff --git a/src/plugins/gstreamer/common.pri b/src/plugins/gstreamer/common.pri
deleted file mode 100644
index d0c5c7bdd..000000000
--- a/src/plugins/gstreamer/common.pri
+++ /dev/null
@@ -1,15 +0,0 @@
-QT += core-private multimedia-private multimediagsttools-private network
-
-qtHaveModule(widgets) {
- QT += widgets multimediawidgets-private
- DEFINES += HAVE_WIDGETS
-}
-
-QMAKE_USE += gstreamer
-
-qtConfig(resourcepolicy): \
- QMAKE_USE += libresourceqt5
-
-qtConfig(gstreamer_app): \
- QMAKE_USE += gstreamer_app
-
diff --git a/src/plugins/gstreamer/gstreamer.json b/src/plugins/gstreamer/gstreamer.json
deleted file mode 100644
index 0656cce4f..000000000
--- a/src/plugins/gstreamer/gstreamer.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Keys": ["gstreamer"],
- "Services": ["org.qt-project.qt.mediaplayer", "org.qt-project.qt.audiosource", "org.qt-project.qt.camera", "org.qt-project.qt.audiodecode"]
-}
diff --git a/src/plugins/gstreamer/gstreamer.pro b/src/plugins/gstreamer/gstreamer.pro
deleted file mode 100644
index 5fb8f83c6..000000000
--- a/src/plugins/gstreamer/gstreamer.pro
+++ /dev/null
@@ -1,10 +0,0 @@
-TEMPLATE = subdirs
-
-SUBDIRS += \
- audiodecoder \
- camerabin \
- mediaplayer \
- mediacapture
-
-OTHER_FILES += \
- gstreamer.json
diff --git a/src/plugins/gstreamer/mediacapture/mediacapture.json b/src/plugins/gstreamer/mediacapture/mediacapture.json
deleted file mode 100644
index 68ca3f55b..000000000
--- a/src/plugins/gstreamer/mediacapture/mediacapture.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Keys": ["gstreamermediacapture"],
- "Services": ["org.qt-project.qt.audiosource"]
-}
diff --git a/src/plugins/gstreamer/mediacapture/mediacapture.pro b/src/plugins/gstreamer/mediacapture/mediacapture.pro
deleted file mode 100644
index 17248a495..000000000
--- a/src/plugins/gstreamer/mediacapture/mediacapture.pro
+++ /dev/null
@@ -1,52 +0,0 @@
-TARGET = gstmediacapture
-
-include(../common.pri)
-
-INCLUDEPATH += $$PWD
-
-HEADERS += $$PWD/qgstreamercaptureservice.h \
- $$PWD/qgstreamercapturesession.h \
- $$PWD/qgstreameraudioencode.h \
- $$PWD/qgstreamervideoencode.h \
- $$PWD/qgstreamerrecordercontrol.h \
- $$PWD/qgstreamermediacontainercontrol.h \
- $$PWD/qgstreamercameracontrol.h \
- $$PWD/qgstreamercapturemetadatacontrol.h \
- $$PWD/qgstreamerimagecapturecontrol.h \
- $$PWD/qgstreamerimageencode.h \
- $$PWD/qgstreamercaptureserviceplugin.h
-
-SOURCES += $$PWD/qgstreamercaptureservice.cpp \
- $$PWD/qgstreamercapturesession.cpp \
- $$PWD/qgstreameraudioencode.cpp \
- $$PWD/qgstreamervideoencode.cpp \
- $$PWD/qgstreamerrecordercontrol.cpp \
- $$PWD/qgstreamermediacontainercontrol.cpp \
- $$PWD/qgstreamercameracontrol.cpp \
- $$PWD/qgstreamercapturemetadatacontrol.cpp \
- $$PWD/qgstreamerimagecapturecontrol.cpp \
- $$PWD/qgstreamerimageencode.cpp \
- $$PWD/qgstreamercaptureserviceplugin.cpp
-
-# Camera usage with gstreamer needs to have
-#CONFIG += use_gstreamer_camera
-
-use_gstreamer_camera:qtConfig(linux_v4l) {
- DEFINES += USE_GSTREAMER_CAMERA
-
- OTHER_FILES += \
- mediacapturecamera.json
-
- HEADERS += \
- $$PWD/qgstreamerv4l2input.h
- SOURCES += \
- $$PWD/qgstreamerv4l2input.cpp
-
-} else {
- OTHER_FILES += \
- mediacapture.json
-}
-
-PLUGIN_TYPE = mediaservice
-PLUGIN_CLASS_NAME = QGstreamerCaptureServicePlugin
-load(qt_plugin)
diff --git a/src/plugins/gstreamer/mediacapture/mediacapturecamera.json b/src/plugins/gstreamer/mediacapture/mediacapturecamera.json
deleted file mode 100644
index f5fba17e6..000000000
--- a/src/plugins/gstreamer/mediacapture/mediacapturecamera.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Keys": ["gstreamermediacapture"],
- "Services": ["org.qt-project.qt.audiosource", "org.qt-project.qt.camera"]
-}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreameraudioencode.cpp b/src/plugins/gstreamer/mediacapture/qgstreameraudioencode.cpp
deleted file mode 100644
index 1a35c5cf6..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreameraudioencode.cpp
+++ /dev/null
@@ -1,239 +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 "qgstreameraudioencode.h"
-#include "qgstreamercapturesession.h"
-#include "qgstreamermediacontainercontrol.h"
-#include <private/qgstutils_p.h>
-
-#include <QtCore/qdebug.h>
-
-#include <math.h>
-
-QGstreamerAudioEncode::QGstreamerAudioEncode(QObject *parent)
- :QAudioEncoderSettingsControl(parent)
- , m_codecs(QGstCodecsInfo::AudioEncoder)
-{
-}
-
-QGstreamerAudioEncode::~QGstreamerAudioEncode()
-{
-}
-
-QStringList QGstreamerAudioEncode::supportedAudioCodecs() const
-{
- return m_codecs.supportedCodecs();
-}
-
-QString QGstreamerAudioEncode::codecDescription(const QString &codecName) const
-{
- return m_codecs.codecDescription(codecName);
-}
-
-QStringList QGstreamerAudioEncode::supportedEncodingOptions(const QString &codec) const
-{
- return m_codecs.codecOptions(codec);
-}
-
-QVariant QGstreamerAudioEncode::encodingOption(
- const QString &codec, const QString &name) const
-{
- return m_options[codec].value(name);
-}
-
-void QGstreamerAudioEncode::setEncodingOption(
- const QString &codec, const QString &name, const QVariant &value)
-{
- m_options[codec][name] = value;
-}
-
-QList<int> QGstreamerAudioEncode::supportedSampleRates(const QAudioEncoderSettings &, bool *) const
-{
- //TODO check element caps to find actual values
-
- return QList<int>();
-}
-
-QAudioEncoderSettings QGstreamerAudioEncode::audioSettings() const
-{
- return m_audioSettings;
-}
-
-void QGstreamerAudioEncode::setAudioSettings(const QAudioEncoderSettings &settings)
-{
- m_audioSettings = settings;
-}
-
-
-GstElement *QGstreamerAudioEncode::createEncoder()
-{
- QString codec = m_audioSettings.codec();
- GstElement *encoderElement = gst_element_factory_make(m_codecs.codecElement(codec).constData(), NULL);
- if (!encoderElement)
- return 0;
-
- GstBin * encoderBin = GST_BIN(gst_bin_new("audio-encoder-bin"));
-
- GstElement *sinkCapsFilter = gst_element_factory_make("capsfilter", NULL);
- GstElement *srcCapsFilter = gst_element_factory_make("capsfilter", NULL);
-
- gst_bin_add_many(encoderBin, sinkCapsFilter, encoderElement, srcCapsFilter, NULL);
- gst_element_link_many(sinkCapsFilter, encoderElement, srcCapsFilter, NULL);
-
- // add ghostpads
- GstPad *pad = gst_element_get_static_pad(sinkCapsFilter, "sink");
- gst_element_add_pad(GST_ELEMENT(encoderBin), gst_ghost_pad_new("sink", pad));
- gst_object_unref(GST_OBJECT(pad));
-
- pad = gst_element_get_static_pad(srcCapsFilter, "src");
- gst_element_add_pad(GST_ELEMENT(encoderBin), gst_ghost_pad_new("src", pad));
- gst_object_unref(GST_OBJECT(pad));
-
- if (m_audioSettings.sampleRate() > 0 || m_audioSettings.channelCount() > 0) {
- GstCaps *caps = gst_caps_new_empty();
- GstStructure *structure = qt_gst_structure_new_empty(QT_GSTREAMER_RAW_AUDIO_MIME);
-
- if (m_audioSettings.sampleRate() > 0)
- gst_structure_set(structure, "rate", G_TYPE_INT, m_audioSettings.sampleRate(), NULL );
-
- if (m_audioSettings.channelCount() > 0)
- gst_structure_set(structure, "channels", G_TYPE_INT, m_audioSettings.channelCount(), NULL );
-
- gst_caps_append_structure(caps,structure);
-
- g_object_set(G_OBJECT(sinkCapsFilter), "caps", caps, NULL);
-
- gst_caps_unref(caps);
- }
-
- // Some encoders support several codecs. Setting a caps filter downstream with the desired
- // codec (which is actually a string representation of the caps) will make sure we use the
- // correct codec.
- GstCaps *caps = gst_caps_from_string(codec.toUtf8().constData());
- g_object_set(G_OBJECT(srcCapsFilter), "caps", caps, NULL);
- gst_caps_unref(caps);
-
- if (encoderElement) {
- if (m_audioSettings.encodingMode() == QMultimedia::ConstantQualityEncoding) {
- QMultimedia::EncodingQuality qualityValue = m_audioSettings.quality();
-
- if (codec == QLatin1String("audio/x-vorbis")) {
- double qualityTable[] = {
- 0.1, //VeryLow
- 0.3, //Low
- 0.5, //Normal
- 0.7, //High
- 1.0 //VeryHigh
- };
- g_object_set(G_OBJECT(encoderElement), "quality", qualityTable[qualityValue], NULL);
- } else if (codec == QLatin1String("audio/mpeg")) {
- g_object_set(G_OBJECT(encoderElement), "target", 0, NULL); //constant quality mode
- qreal quality[] = {
- 1, //VeryLow
- 3, //Low
- 5, //Normal
- 7, //High
- 9 //VeryHigh
- };
- g_object_set(G_OBJECT(encoderElement), "quality", quality[qualityValue], NULL);
- } else if (codec == QLatin1String("audio/x-speex")) {
- //0-10 range with default 8
- double qualityTable[] = {
- 2, //VeryLow
- 5, //Low
- 8, //Normal
- 9, //High
- 10 //VeryHigh
- };
- g_object_set(G_OBJECT(encoderElement), "quality", qualityTable[qualityValue], NULL);
- } else if (codec.startsWith("audio/AMR")) {
- int band[] = {
- 0, //VeryLow
- 2, //Low
- 4, //Normal
- 6, //High
- 7 //VeryHigh
- };
-
- g_object_set(G_OBJECT(encoderElement), "band-mode", band[qualityValue], NULL);
- }
- } else {
- int bitrate = m_audioSettings.bitRate();
- if (bitrate > 0) {
- if (codec == QLatin1String("audio/mpeg")) {
- g_object_set(G_OBJECT(encoderElement), "target", 1, NULL); //constant bitrate mode
- }
- g_object_set(G_OBJECT(encoderElement), "bitrate", bitrate, NULL);
- }
- }
-
- QMap<QString, QVariant> options = m_options.value(codec);
- for (auto it = options.cbegin(), end = options.cend(); it != end; ++it) {
- const QString &option = it.key();
- const QVariant &value = it.value();
-
- switch (value.type()) {
- case QVariant::Int:
- g_object_set(G_OBJECT(encoderElement), option.toLatin1(), value.toInt(), NULL);
- break;
- case QVariant::Bool:
- g_object_set(G_OBJECT(encoderElement), option.toLatin1(), value.toBool(), NULL);
- break;
- case QVariant::Double:
- g_object_set(G_OBJECT(encoderElement), option.toLatin1(), value.toDouble(), NULL);
- break;
- case QVariant::String:
- g_object_set(G_OBJECT(encoderElement), option.toLatin1(), value.toString().toUtf8().constData(), NULL);
- break;
- default:
- qWarning() << "unsupported option type:" << option << value;
- break;
- }
-
- }
- }
-
- return GST_ELEMENT(encoderBin);
-}
-
-
-QSet<QString> QGstreamerAudioEncode::supportedStreamTypes(const QString &codecName) const
-{
- return m_codecs.supportedStreamTypes(codecName);
-}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreameraudioencode.h b/src/plugins/gstreamer/mediacapture/qgstreameraudioencode.h
deleted file mode 100644
index 0cfbb4e91..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreameraudioencode.h
+++ /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$
-**
-****************************************************************************/
-
-#ifndef QGSTREAMERAUDIOENCODE_H
-#define QGSTREAMERAUDIOENCODE_H
-
-#include <qaudioencodersettingscontrol.h>
-
-#include <QtCore/qstringlist.h>
-#include <QtCore/qmap.h>
-#include <QtCore/qset.h>
-
-#include <gst/gst.h>
-
-#include <qaudioformat.h>
-#include <private/qgstcodecsinfo_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class QGstreamerCaptureSession;
-
-class QGstreamerAudioEncode : public QAudioEncoderSettingsControl
-{
- Q_OBJECT
-public:
- QGstreamerAudioEncode(QObject *parent);
- virtual ~QGstreamerAudioEncode();
-
- QStringList supportedAudioCodecs() const override;
- QString codecDescription(const QString &codecName) const override;
-
- QStringList supportedEncodingOptions(const QString &codec) const;
- QVariant encodingOption(const QString &codec, const QString &name) const;
- void setEncodingOption(const QString &codec, const QString &name, const QVariant &value);
-
- QList<int> supportedSampleRates(const QAudioEncoderSettings &settings = QAudioEncoderSettings(),
- bool *isContinuous = 0) const override;
- QList<int> supportedChannelCounts(const QAudioEncoderSettings &settings = QAudioEncoderSettings()) const;
- QList<int> supportedSampleSizes(const QAudioEncoderSettings &settings = QAudioEncoderSettings()) const;
-
- QAudioEncoderSettings audioSettings() const override;
- void setAudioSettings(const QAudioEncoderSettings &) override;
-
- GstElement *createEncoder();
-
- QSet<QString> supportedStreamTypes(const QString &codecName) const;
-
-private:
- QGstCodecsInfo m_codecs;
-
- QMap<QString, QMap<QString, QVariant> > m_options;
-
- QAudioEncoderSettings m_audioSettings;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.cpp
deleted file mode 100644
index 601a09e31..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.cpp
+++ /dev/null
@@ -1,197 +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 "qgstreamercameracontrol.h"
-#include "qgstreamerimageencode.h"
-
-#include <QtCore/qdebug.h>
-#include <QtCore/qfile.h>
-
-
-QGstreamerCameraControl::QGstreamerCameraControl(QGstreamerCaptureSession *session)
- :QCameraControl(session),
- m_captureMode(QCamera::CaptureStillImage),
- m_session(session),
- m_state(QCamera::UnloadedState),
- m_status(QCamera::UnloadedStatus),
- m_reloadPending(false)
-
-{
- connect(m_session, SIGNAL(stateChanged(QGstreamerCaptureSession::State)),
- this, SLOT(updateStatus()));
-
- connect(m_session->imageEncodeControl(), SIGNAL(settingsChanged()),
- SLOT(reloadLater()));
- connect(m_session, SIGNAL(viewfinderChanged()),
- SLOT(reloadLater()));
- connect(m_session, SIGNAL(readyChanged(bool)),
- SLOT(reloadLater()));
-
- m_session->setCaptureMode(QGstreamerCaptureSession::Image);
-}
-
-QGstreamerCameraControl::~QGstreamerCameraControl()
-{
-}
-
-void QGstreamerCameraControl::setCaptureMode(QCamera::CaptureModes mode)
-{
- if (m_captureMode == mode || !isCaptureModeSupported(mode))
- return;
-
- m_captureMode = mode;
-
- switch (mode) {
- case QCamera::CaptureViewfinder:
- case QCamera::CaptureStillImage:
- m_session->setCaptureMode(QGstreamerCaptureSession::Image);
- break;
- case QCamera::CaptureVideo:
- m_session->setCaptureMode(QGstreamerCaptureSession::AudioAndVideo);
- break;
- case QCamera::CaptureVideo | QCamera::CaptureStillImage:
- m_session->setCaptureMode(QGstreamerCaptureSession::AudioAndVideoAndImage);
- break;
- }
-
- emit captureModeChanged(mode);
- updateStatus();
- reloadLater();
-}
-
-bool QGstreamerCameraControl::isCaptureModeSupported(QCamera::CaptureModes mode) const
-{
- //only CaptureStillImage and CaptureVideo bits are allowed
- return (mode & (QCamera::CaptureStillImage | QCamera::CaptureVideo)) == mode;
-}
-
-void QGstreamerCameraControl::setState(QCamera::State state)
-{
- if (m_state == state)
- return;
-
- m_state = state;
- switch (state) {
- case QCamera::UnloadedState:
- case QCamera::LoadedState:
- m_session->setState(QGstreamerCaptureSession::StoppedState);
- break;
- case QCamera::ActiveState:
- //postpone changing to Active if the session is nor ready yet
- if (m_session->isReady()) {
- m_session->setState(QGstreamerCaptureSession::PreviewState);
- } else {
-#ifdef CAMEABIN_DEBUG
- qDebug() << "Camera session is not ready yet, postpone activating";
-#endif
- }
- break;
- default:
- emit error(QCamera::NotSupportedFeatureError, tr("State not supported."));
- }
-
- updateStatus();
- emit stateChanged(m_state);
-}
-
-QCamera::State QGstreamerCameraControl::state() const
-{
- return m_state;
-}
-
-void QGstreamerCameraControl::updateStatus()
-{
- QCamera::Status oldStatus = m_status;
-
- switch (m_state) {
- case QCamera::UnloadedState:
- m_status = QCamera::UnloadedStatus;
- break;
- case QCamera::LoadedState:
- m_status = QCamera::LoadedStatus;
- break;
- case QCamera::ActiveState:
- if (m_session->state() == QGstreamerCaptureSession::StoppedState)
- m_status = QCamera::StartingStatus;
- else
- m_status = QCamera::ActiveStatus;
- break;
- }
-
- if (oldStatus != m_status) {
- //qDebug() << "Status changed:" << m_status;
- emit statusChanged(m_status);
- }
-}
-
-void QGstreamerCameraControl::reloadLater()
-{
- //qDebug() << "reload pipeline requested";
- if (!m_reloadPending && m_state == QCamera::ActiveState) {
- m_reloadPending = true;
- m_session->setState(QGstreamerCaptureSession::StoppedState);
- QMetaObject::invokeMethod(this, "reloadPipeline", Qt::QueuedConnection);
- }
-}
-
-void QGstreamerCameraControl::reloadPipeline()
-{
- //qDebug() << "reload pipeline";
- if (m_reloadPending) {
- m_reloadPending = false;
- if (m_state == QCamera::ActiveState && m_session->isReady()) {
- m_session->setState(QGstreamerCaptureSession::PreviewState);
- }
- }
-}
-
-bool QGstreamerCameraControl::canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const
-{
- Q_UNUSED(status);
-
- switch (changeType) {
- case QCameraControl::CaptureMode:
- case QCameraControl::ImageEncodingSettings:
- case QCameraControl::VideoEncodingSettings:
- case QCameraControl::Viewfinder:
- return true;
- default:
- return false;
- }
-}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.h b/src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.h
deleted file mode 100644
index 0e53f0890..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.h
+++ /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$
-**
-****************************************************************************/
-
-
-#ifndef QGSTREAMERCAMERACONTROL_H
-#define QGSTREAMERCAMERACONTROL_H
-
-#include <QHash>
-#include <qcameracontrol.h>
-#include "qgstreamercapturesession.h"
-
-QT_BEGIN_NAMESPACE
-
-class QGstreamerCameraControl : public QCameraControl
-{
- Q_OBJECT
-public:
- QGstreamerCameraControl( QGstreamerCaptureSession *session );
- virtual ~QGstreamerCameraControl();
-
- bool isValid() const { return true; }
-
- QCamera::State state() const override;
- void setState(QCamera::State state) override;
-
- QCamera::Status status() const override { return m_status; }
-
- QCamera::CaptureModes captureMode() const override { return m_captureMode; }
- void setCaptureMode(QCamera::CaptureModes mode) override;
-
- bool isCaptureModeSupported(QCamera::CaptureModes mode) const override;
-
- QCamera::LockTypes supportedLocks() const
- {
- return QCamera::NoLock;
- }
-
- bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const override;
-
-public slots:
- void reloadLater();
-
-private slots:
- void updateStatus();
- void reloadPipeline();
-
-
-private:
- QCamera::CaptureModes m_captureMode;
- QGstreamerCaptureSession *m_session;
- QCamera::State m_state;
- QCamera::Status m_status;
- bool m_reloadPending;
-};
-
-QT_END_NAMESPACE
-
-#endif // QGSTREAMERCAMERACONTROL_H
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.cpp
deleted file mode 100644
index 6139c57bf..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.cpp
+++ /dev/null
@@ -1,158 +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 "qgstreamercapturemetadatacontrol.h"
-
-#include <QtMultimedia/qmediametadata.h>
-
-#include <gst/gst.h>
-#include <gst/gstversion.h>
-
-
-typedef QMap<QString, QByteArray> QGstreamerMetaDataKeyLookup;
-Q_GLOBAL_STATIC(QGstreamerMetaDataKeyLookup, metadataKeys)
-
-static const QGstreamerMetaDataKeyLookup *qt_gstreamerMetaDataKeys()
-{
- if (metadataKeys->isEmpty()) {
- metadataKeys->insert(QMediaMetaData::Title, GST_TAG_TITLE);
- metadataKeys->insert(QMediaMetaData::SubTitle, 0);
- //metadataKeys->insert(QMediaMetaData::Author, 0);
- metadataKeys->insert(QMediaMetaData::Comment, GST_TAG_COMMENT);
- metadataKeys->insert(QMediaMetaData::Description, GST_TAG_DESCRIPTION);
- //metadataKeys->insert(QMediaMetaData::Category, 0);
- metadataKeys->insert(QMediaMetaData::Genre, GST_TAG_GENRE);
- //metadataKeys->insert(QMediaMetaData::Year, 0);
- //metadataKeys->insert(QMediaMetaData::UserRating, 0);
-
- metadataKeys->insert(QMediaMetaData::Language, GST_TAG_LANGUAGE_CODE);
-
- metadataKeys->insert(QMediaMetaData::Publisher, GST_TAG_ORGANIZATION);
- metadataKeys->insert(QMediaMetaData::Copyright, GST_TAG_COPYRIGHT);
- //metadataKeys->insert(QMediaMetaData::ParentalRating, 0);
- //metadataKeys->insert(QMediaMetaData::RatingOrganisation, 0);
-
- // Media
- //metadataKeys->insert(QMediaMetaData::Size, 0);
- //metadataKeys->insert(QMediaMetaData::MediaType, 0);
- metadataKeys->insert(QMediaMetaData::Duration, GST_TAG_DURATION);
-
- // Audio
- metadataKeys->insert(QMediaMetaData::AudioBitRate, GST_TAG_BITRATE);
- metadataKeys->insert(QMediaMetaData::AudioCodec, GST_TAG_AUDIO_CODEC);
- //metadataKeys->insert(QMediaMetaData::ChannelCount, 0);
- //metadataKeys->insert(QMediaMetaData::SampleRate, 0);
-
- // Music
- metadataKeys->insert(QMediaMetaData::AlbumTitle, GST_TAG_ALBUM);
- metadataKeys->insert(QMediaMetaData::AlbumArtist, GST_TAG_ARTIST);
- metadataKeys->insert(QMediaMetaData::ContributingArtist, GST_TAG_PERFORMER);
- metadataKeys->insert(QMediaMetaData::Composer, GST_TAG_COMPOSER);
- //metadataKeys->insert(QMediaMetaData::Conductor, 0);
- //metadataKeys->insert(QMediaMetaData::Lyrics, 0);
- //metadataKeys->insert(QMediaMetaData::Mood, 0);
- metadataKeys->insert(QMediaMetaData::TrackNumber, GST_TAG_TRACK_NUMBER);
-
- //metadataKeys->insert(QMediaMetaData::CoverArtUrlSmall, 0);
- //metadataKeys->insert(QMediaMetaData::CoverArtUrlLarge, 0);
-
- // Image/Video
- //metadataKeys->insert(QMediaMetaData::Resolution, 0);
- //metadataKeys->insert(QMediaMetaData::PixelAspectRatio, 0);
-
- // Video
- //metadataKeys->insert(QMediaMetaData::VideoFrameRate, 0);
- //metadataKeys->insert(QMediaMetaData::VideoBitRate, 0);
- metadataKeys->insert(QMediaMetaData::VideoCodec, GST_TAG_VIDEO_CODEC);
-
- //metadataKeys->insert(QMediaMetaData::PosterUrl, 0);
-
- // Movie
- //metadataKeys->insert(QMediaMetaData::ChapterNumber, 0);
- //metadataKeys->insert(QMediaMetaData::Director, 0);
- metadataKeys->insert(QMediaMetaData::LeadPerformer, GST_TAG_PERFORMER);
- //metadataKeys->insert(QMediaMetaData::Writer, 0);
-
- // Photos
- //metadataKeys->insert(QMediaMetaData::CameraManufacturer, 0);
- //metadataKeys->insert(QMediaMetaData::CameraModel, 0);
- //metadataKeys->insert(QMediaMetaData::Event, 0);
- //metadataKeys->insert(QMediaMetaData::Subject, 0 }
- }
-
- return metadataKeys;
-}
-
-QGstreamerCaptureMetaDataControl::QGstreamerCaptureMetaDataControl(QObject *parent)
- :QMetaDataWriterControl(parent)
-{
-}
-
-QVariant QGstreamerCaptureMetaDataControl::metaData(const QString &key) const
-{
- QGstreamerMetaDataKeyLookup::const_iterator it = qt_gstreamerMetaDataKeys()->find(key);
- if (it != qt_gstreamerMetaDataKeys()->constEnd())
- return m_values.value(it.value());
-
- return QVariant();
-}
-
-void QGstreamerCaptureMetaDataControl::setMetaData(const QString &key, const QVariant &value)
-{
- QGstreamerMetaDataKeyLookup::const_iterator it = qt_gstreamerMetaDataKeys()->find(key);
- if (it != qt_gstreamerMetaDataKeys()->constEnd()) {
- m_values.insert(it.value(), value);
-
- emit QMetaDataWriterControl::metaDataChanged();
- emit QMetaDataWriterControl::metaDataChanged(key, value);
- emit metaDataChanged(m_values);
- }
-}
-
-QStringList QGstreamerCaptureMetaDataControl::availableMetaData() const
-{
- QStringList res;
- for (auto it = m_values.keyBegin(), end = m_values.keyEnd(); it != end; ++it) {
- QString tag = qt_gstreamerMetaDataKeys()->key(*it);
- if (!tag.isEmpty())
- res.append(tag);
- }
-
- return res;
-}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.h b/src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.h
deleted file mode 100644
index 6839bbe68..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.h
+++ /dev/null
@@ -1,71 +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 QGSTREAMERCAPTUREMETADATACONTROL_H
-#define QGSTREAMERCAPTUREMETADATACONTROL_H
-
-#include <qmetadatawritercontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QGstreamerCaptureMetaDataControl : public QMetaDataWriterControl
-{
- Q_OBJECT
-public:
- QGstreamerCaptureMetaDataControl(QObject *parent);
- ~QGstreamerCaptureMetaDataControl() {}
-
-
- bool isMetaDataAvailable() const override { return true; }
- bool isWritable() const override { return true; }
-
- QVariant metaData(const QString &key) const override;
- void setMetaData(const QString &key, const QVariant &value) override;
- QStringList availableMetaData() const override;
-
-Q_SIGNALS:
- void metaDataChanged(const QMap<QByteArray, QVariant>&);
-
-private:
- QMap<QByteArray, QVariant> m_values;
-};
-
-QT_END_NAMESPACE
-
-#endif // QGSTREAMERCAPTUREMETADATACONTROL_H
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp
deleted file mode 100644
index dc99cbe0e..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp
+++ /dev/null
@@ -1,227 +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 "qgstreamercaptureservice.h"
-#include "qgstreamercapturesession.h"
-#include "qgstreamerrecordercontrol.h"
-#include "qgstreamermediacontainercontrol.h"
-#include "qgstreameraudioencode.h"
-#include "qgstreamervideoencode.h"
-#include "qgstreamerimageencode.h"
-#include "qgstreamercameracontrol.h"
-#include <private/qgstreamerbushelper_p.h>
-#include "qgstreamercapturemetadatacontrol.h"
-
-#if defined(USE_GSTREAMER_CAMERA)
-#include "qgstreamerv4l2input.h"
-#endif
-
-#include "qgstreamerimagecapturecontrol.h"
-#include <private/qgstreameraudioinputselector_p.h>
-#include <private/qgstreamervideoinputdevicecontrol_p.h>
-#include <private/qgstreameraudioprobecontrol_p.h>
-
-#include <private/qgstreamervideorenderer_p.h>
-#include <private/qgstreamervideowindow_p.h>
-
-#if defined(HAVE_WIDGETS)
-#include <private/qgstreamervideowidget_p.h>
-#endif
-
-#include <qmediaserviceproviderplugin.h>
-
-QT_BEGIN_NAMESPACE
-
-QGstreamerCaptureService::QGstreamerCaptureService(const QString &service, QObject *parent)
- : QMediaService(parent)
- , m_captureSession(0)
- , m_cameraControl(0)
-#if defined(USE_GSTREAMER_CAMERA)
- , m_videoInput(0)
-#endif
- , m_metaDataControl(0)
- , m_audioInputSelector(0)
- , m_videoInputDevice(0)
- , m_videoOutput(0)
- , m_videoRenderer(0)
- , m_videoWindow(0)
-#if defined(HAVE_WIDGETS)
- , m_videoWidgetControl(0)
-#endif
- , m_imageCaptureControl(0)
- , m_audioProbeControl(0)
-{
- if (service == Q_MEDIASERVICE_AUDIOSOURCE) {
- m_captureSession = new QGstreamerCaptureSession(QGstreamerCaptureSession::Audio, this);
- }
-
-#if defined(USE_GSTREAMER_CAMERA)
- if (service == Q_MEDIASERVICE_CAMERA) {
- m_captureSession = new QGstreamerCaptureSession(QGstreamerCaptureSession::AudioAndVideo, this);
- m_cameraControl = new QGstreamerCameraControl(m_captureSession);
- m_videoInput = new QGstreamerV4L2Input(this);
- m_captureSession->setVideoInput(m_videoInput);
- m_videoInputDevice = new QGstreamerVideoInputDeviceControl(this);
-
- connect(m_videoInputDevice, SIGNAL(selectedDeviceChanged(QString)),
- m_videoInput, SLOT(setDevice(QString)));
-
- if (m_videoInputDevice->deviceCount())
- m_videoInput->setDevice(m_videoInputDevice->deviceName(m_videoInputDevice->selectedDevice()));
-
- m_videoRenderer = new QGstreamerVideoRenderer(this);
-
- m_videoWindow = new QGstreamerVideoWindow(this);
- // If the GStreamer video sink is not available, don't provide the video window control since
- // it won't work anyway.
- if (!m_videoWindow->videoSink()) {
- delete m_videoWindow;
- m_videoWindow = 0;
- }
-
-#if defined(HAVE_WIDGETS)
- m_videoWidgetControl = new QGstreamerVideoWidgetControl(this);
-
- // If the GStreamer video sink is not available, don't provide the video widget control since
- // it won't work anyway. QVideoWidget will fall back to QVideoRendererControl in that case.
- if (!m_videoWidgetControl->videoSink()) {
- delete m_videoWidgetControl;
- m_videoWidgetControl = 0;
- }
-#endif
- m_imageCaptureControl = new QGstreamerImageCaptureControl(m_captureSession);
- }
-#endif
-
- m_audioInputSelector = new QGstreamerAudioInputSelector(this);
- connect(m_audioInputSelector, SIGNAL(activeInputChanged(QString)), m_captureSession, SLOT(setCaptureDevice(QString)));
-
- if (m_captureSession && m_audioInputSelector->availableInputs().size() > 0)
- m_captureSession->setCaptureDevice(m_audioInputSelector->defaultInput());
-
- m_metaDataControl = new QGstreamerCaptureMetaDataControl(this);
- connect(m_metaDataControl, SIGNAL(metaDataChanged(QMap<QByteArray,QVariant>)),
- m_captureSession, SLOT(setMetaData(QMap<QByteArray,QVariant>)));
-}
-
-QGstreamerCaptureService::~QGstreamerCaptureService()
-{
-}
-
-QMediaControl *QGstreamerCaptureService::requestControl(const char *name)
-{
- if (!m_captureSession)
- return 0;
-
- if (qstrcmp(name,QAudioInputSelectorControl_iid) == 0)
- return m_audioInputSelector;
-
- if (qstrcmp(name,QVideoDeviceSelectorControl_iid) == 0)
- return m_videoInputDevice;
-
- if (qstrcmp(name,QMediaRecorderControl_iid) == 0)
- return m_captureSession->recorderControl();
-
- if (qstrcmp(name,QAudioEncoderSettingsControl_iid) == 0)
- return m_captureSession->audioEncodeControl();
-
- if (qstrcmp(name,QVideoEncoderSettingsControl_iid) == 0)
- return m_captureSession->videoEncodeControl();
-
- if (qstrcmp(name,QImageEncoderControl_iid) == 0)
- return m_captureSession->imageEncodeControl();
-
-
- if (qstrcmp(name,QMediaContainerControl_iid) == 0)
- return m_captureSession->mediaContainerControl();
-
- if (qstrcmp(name,QCameraControl_iid) == 0)
- return m_cameraControl;
-
- if (qstrcmp(name,QMetaDataWriterControl_iid) == 0)
- return m_metaDataControl;
-
- if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0)
- return m_imageCaptureControl;
-
- if (qstrcmp(name,QMediaAudioProbeControl_iid) == 0) {
- if (!m_audioProbeControl) {
- m_audioProbeControl = new QGstreamerAudioProbeControl(this);
- m_captureSession->addProbe(m_audioProbeControl);
- }
- m_audioProbeControl->ref.ref();
- return m_audioProbeControl;
- }
-
- if (!m_videoOutput) {
- if (qstrcmp(name, QVideoRendererControl_iid) == 0) {
- m_videoOutput = m_videoRenderer;
- } else if (qstrcmp(name, QVideoWindowControl_iid) == 0) {
- m_videoOutput = m_videoWindow;
- }
-#if defined(HAVE_WIDGETS)
- else if (qstrcmp(name, QVideoWidgetControl_iid) == 0) {
- m_videoOutput = m_videoWidgetControl;
- }
-#endif
-
- if (m_videoOutput) {
- m_captureSession->setVideoPreview(m_videoOutput);
- return m_videoOutput;
- }
- }
-
- return 0;
-}
-
-void QGstreamerCaptureService::releaseControl(QMediaControl *control)
-{
- if (!control) {
- return;
- } else if (control == m_videoOutput) {
- m_videoOutput = 0;
- m_captureSession->setVideoPreview(0);
- } else if (control == m_audioProbeControl && !m_audioProbeControl->ref.deref()) {
- m_captureSession->removeProbe(m_audioProbeControl);
- delete m_audioProbeControl;
- m_audioProbeControl = 0;
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h
deleted file mode 100644
index aba98d495..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.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 QGSTREAMERCAPTURESERVICE_H
-#define QGSTREAMERCAPTURESERVICE_H
-
-#include <qmediaservice.h>
-#include <qmediacontrol.h>
-
-#include <gst/gst.h>
-
-QT_BEGIN_NAMESPACE
-class QAudioInputSelectorControl;
-class QVideoDeviceSelectorControl;
-
-class QGstreamerAudioProbeControl;
-class QGstreamerCaptureSession;
-class QGstreamerCameraControl;
-class QGstreamerMessage;
-class QGstreamerBusHelper;
-class QGstreamerVideoRenderer;
-class QGstreamerVideoWindow;
-class QGstreamerVideoWidgetControl;
-class QGstreamerElementFactory;
-class QGstreamerCaptureMetaDataControl;
-class QGstreamerImageCaptureControl;
-class QGstreamerV4L2Input;
-
-class QGstreamerCaptureService : public QMediaService
-{
- Q_OBJECT
-
-public:
- QGstreamerCaptureService(const QString &service, QObject *parent = 0);
- virtual ~QGstreamerCaptureService();
-
- QMediaControl *requestControl(const char *name) override;
- void releaseControl(QMediaControl *) override;
-
-private:
- void setAudioPreview(GstElement *);
-
- QGstreamerCaptureSession *m_captureSession;
- QGstreamerCameraControl *m_cameraControl;
-#if defined(USE_GSTREAMER_CAMERA)
- QGstreamerV4L2Input *m_videoInput;
-#endif
- QGstreamerCaptureMetaDataControl *m_metaDataControl;
-
- QAudioInputSelectorControl *m_audioInputSelector;
- QVideoDeviceSelectorControl *m_videoInputDevice;
-
- QMediaControl *m_videoOutput;
-
- QGstreamerVideoRenderer *m_videoRenderer;
- QGstreamerVideoWindow *m_videoWindow;
-#if defined(HAVE_WIDGETS)
- QGstreamerVideoWidgetControl *m_videoWidgetControl;
-#endif
- QGstreamerImageCaptureControl *m_imageCaptureControl;
-
- QGstreamerAudioProbeControl *m_audioProbeControl;
-};
-
-QT_END_NAMESPACE
-
-#endif // QGSTREAMERCAPTURESERVICE_H
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercaptureserviceplugin.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercaptureserviceplugin.cpp
deleted file mode 100644
index 2a2dec60a..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamercaptureserviceplugin.cpp
+++ /dev/null
@@ -1,142 +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 <QtCore/qstring.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/QDir>
-#include <QtCore/QDebug>
-
-#include "qgstreamercaptureserviceplugin.h"
-
-//#define QT_SUPPORTEDMIMETYPES_DEBUG
-
-#include "qgstreamercaptureservice.h"
-#include <private/qgstutils_p.h>
-
-QMediaService* QGstreamerCaptureServicePlugin::create(const QString &key)
-{
- QGstUtils::initializeGst();
-
- if (key == QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE))
- return new QGstreamerCaptureService(key);
-
-#if defined(USE_GSTREAMER_CAMERA)
- if (key == QLatin1String(Q_MEDIASERVICE_CAMERA))
- return new QGstreamerCaptureService(key);
-#endif
-
- qWarning() << "Gstreamer capture service plugin: unsupported key:" << key;
- return 0;
-}
-
-void QGstreamerCaptureServicePlugin::release(QMediaService *service)
-{
- delete service;
-}
-
-#if defined(USE_GSTREAMER_CAMERA)
-QMediaServiceProviderHint::Features QGstreamerCaptureServicePlugin::supportedFeatures(
- const QByteArray &service) const
-{
- if (service == Q_MEDIASERVICE_CAMERA)
- return QMediaServiceProviderHint::VideoSurface;
-
- return QMediaServiceProviderHint::Features();
-}
-
-QByteArray QGstreamerCaptureServicePlugin::defaultDevice(const QByteArray &service) const
-{
- return service == Q_MEDIASERVICE_CAMERA
- ? QGstUtils::enumerateCameras().value(0).name.toUtf8()
- : QByteArray();
-}
-
-QList<QByteArray> QGstreamerCaptureServicePlugin::devices(const QByteArray &service) const
-{
- return service == Q_MEDIASERVICE_CAMERA ? QGstUtils::cameraDevices() : QList<QByteArray>();
-}
-
-QString QGstreamerCaptureServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device)
-{
- return service == Q_MEDIASERVICE_CAMERA ? QGstUtils::cameraDescription(device) : QString();
-}
-
-QVariant QGstreamerCaptureServicePlugin::deviceProperty(const QByteArray &service, const QByteArray &device, const QByteArray &property)
-{
- Q_UNUSED(service);
- Q_UNUSED(device);
- Q_UNUSED(property);
- return QVariant();
-}
-
-#endif
-
-QMultimedia::SupportEstimate QGstreamerCaptureServicePlugin::hasSupport(const QString &mimeType,
- const QStringList& codecs) const
-{
- if (m_supportedMimeTypeSet.isEmpty())
- updateSupportedMimeTypes();
-
- return QGstUtils::hasSupport(mimeType, codecs, m_supportedMimeTypeSet);
-}
-
-
-static bool isEncoderOrMuxer(GstElementFactory *factory)
-{
-#if GST_CHECK_VERSION(0, 10, 31)
- return gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_MUXER)
- || gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_ENCODER);
-#else
- return (factory
- && (qstrcmp(factory->details.klass, "Codec/Encoder/Audio") == 0
- || qstrcmp(factory->details.klass, "Codec/Encoder/Video") == 0
- || qstrcmp(factory->details.klass, "Codec/Muxer") == 0 ));
-#endif
-}
-
-void QGstreamerCaptureServicePlugin::updateSupportedMimeTypes() const
-{
- m_supportedMimeTypeSet = QGstUtils::supportedMimeTypes(isEncoderOrMuxer);
-}
-
-QStringList QGstreamerCaptureServicePlugin::supportedMimeTypes() const
-{
- return QStringList();
-}
-
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercaptureserviceplugin.h b/src/plugins/gstreamer/mediacapture/qgstreamercaptureserviceplugin.h
deleted file mode 100644
index d8b7e9768..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamercaptureserviceplugin.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 QGSTREAMERCAPTURESERVICEPLUGIN_H
-#define QGSTREAMERCAPTURESERVICEPLUGIN_H
-
-#include <qmediaserviceproviderplugin.h>
-#include <QtCore/qset.h>
-#include <QtCore/QObject>
-
-QT_BEGIN_NAMESPACE
-
-class QGstreamerCaptureServicePlugin
- : public QMediaServiceProviderPlugin
-#if defined(USE_GSTREAMER_CAMERA)
- , public QMediaServiceSupportedDevicesInterface
- , public QMediaServiceDefaultDeviceInterface
- , public QMediaServiceFeaturesInterface
-#endif
- , public QMediaServiceSupportedFormatsInterface
-{
- Q_OBJECT
-#if defined(USE_GSTREAMER_CAMERA)
- Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
- Q_INTERFACES(QMediaServiceDefaultDeviceInterface)
- Q_INTERFACES(QMediaServiceFeaturesInterface)
-#endif
- Q_INTERFACES(QMediaServiceSupportedFormatsInterface)
-#if defined(USE_GSTREAMER_CAMERA)
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "mediacapturecamera.json")
-#else
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "mediacapture.json")
-#endif
-public:
- QMediaService* create(const QString &key) override;
- void release(QMediaService *service) override;
-
-#if defined(USE_GSTREAMER_CAMERA)
- QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const override;
-
- QByteArray defaultDevice(const QByteArray &service) const override;
- QList<QByteArray> devices(const QByteArray &service) const override;
- QString deviceDescription(const QByteArray &service, const QByteArray &device) override;
- QVariant deviceProperty(const QByteArray &service, const QByteArray &device, const QByteArray &property);
-#endif
-
- QMultimedia::SupportEstimate hasSupport(const QString &mimeType, const QStringList &codecs) const override;
- QStringList supportedMimeTypes() const override;
-
-private:
- void updateSupportedMimeTypes() const;
-
- mutable QSet<QString> m_supportedMimeTypeSet; //for fast access
-};
-
-QT_END_NAMESPACE
-
-#endif // QGSTREAMERCAPTURESERVICEPLUGIN_H
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp
deleted file mode 100644
index 4363f6d3b..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp
+++ /dev/null
@@ -1,1032 +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 "qgstreamercapturesession.h"
-#include "qgstreamerrecordercontrol.h"
-#include "qgstreamermediacontainercontrol.h"
-#include "qgstreameraudioencode.h"
-#include "qgstreamervideoencode.h"
-#include "qgstreamerimageencode.h"
-#include <qmediarecorder.h>
-#include <private/qgstreamervideorendererinterface_p.h>
-#include <private/qgstreameraudioprobecontrol_p.h>
-#include <private/qgstreamerbushelper_p.h>
-#include <private/qgstutils_p.h>
-
-#include <gst/gsttagsetter.h>
-#include <gst/gstversion.h>
-#include <gst/video/video.h>
-
-#include <QtCore/qdebug.h>
-#include <QtCore/qurl.h>
-#include <QtCore/qset.h>
-#include <QCoreApplication>
-#include <QtCore/qmetaobject.h>
-#include <QtCore/qfile.h>
-#include <QtGui/qimage.h>
-
-QT_BEGIN_NAMESPACE
-
-QGstreamerCaptureSession::QGstreamerCaptureSession(QGstreamerCaptureSession::CaptureMode captureMode, QObject *parent)
- :QObject(parent),
- m_state(StoppedState),
- m_pendingState(StoppedState),
- m_waitingForEos(false),
- m_pipelineMode(EmptyPipeline),
- m_captureMode(captureMode),
- m_audioProbe(0),
- m_audioInputFactory(0),
- m_audioPreviewFactory(0),
- m_videoInputFactory(0),
- m_viewfinder(0),
- m_viewfinderInterface(0),
- m_audioSrc(0),
- m_audioTee(0),
- m_audioPreviewQueue(0),
- m_audioPreview(0),
- m_audioVolume(0),
- m_muted(false),
- m_volume(1.0),
- m_videoSrc(0),
- m_videoTee(0),
- m_videoPreviewQueue(0),
- m_videoPreview(0),
- m_imageCaptureBin(0),
- m_encodeBin(0),
- m_passImage(false),
- m_passPrerollImage(false)
-{
- m_pipeline = gst_pipeline_new("media-capture-pipeline");
- qt_gst_object_ref_sink(m_pipeline);
-
- m_bus = gst_element_get_bus(m_pipeline);
- m_busHelper = new QGstreamerBusHelper(m_bus, this);
- m_busHelper->installMessageFilter(this);
-
- m_audioEncodeControl = new QGstreamerAudioEncode(this);
- m_videoEncodeControl = new QGstreamerVideoEncode(this);
- m_imageEncodeControl = new QGstreamerImageEncode(this);
- m_recorderControl = new QGstreamerRecorderControl(this);
- connect(m_recorderControl, &QGstreamerRecorderControl::error, [](int e, const QString &str) {
- qWarning() << QMediaRecorder::Error(e) << ":" << str.toLatin1().constData();
- });
- m_mediaContainerControl = new QGstreamerMediaContainerControl(this);
-}
-
-QGstreamerCaptureSession::~QGstreamerCaptureSession()
-{
- setState(StoppedState);
- gst_element_set_state(m_pipeline, GST_STATE_NULL);
- gst_object_unref(GST_OBJECT(m_bus));
- gst_object_unref(GST_OBJECT(m_pipeline));
-}
-
-void QGstreamerCaptureSession::setCaptureMode(CaptureMode mode)
-{
- m_captureMode = mode;
-}
-
-GstElement *QGstreamerCaptureSession::buildEncodeBin()
-{
- GstElement *encodeBin = gst_bin_new("encode-bin");
-
- GstElement *muxer = gst_element_factory_make( m_mediaContainerControl->formatElementName().constData(), "muxer");
- if (!muxer) {
- qWarning() << "Could not create a media muxer element:" << m_mediaContainerControl->formatElementName();
- gst_object_unref(encodeBin);
- return 0;
- }
-
- // Output location was rejected in setOutputlocation() if not a local file
- QUrl actualSink = QUrl::fromLocalFile(QDir::currentPath()).resolved(m_sink);
- GstElement *fileSink = gst_element_factory_make("filesink", "filesink");
- g_object_set(G_OBJECT(fileSink), "location", QFile::encodeName(actualSink.toLocalFile()).constData(), NULL);
- gst_bin_add_many(GST_BIN(encodeBin), muxer, fileSink, NULL);
-
- if (!gst_element_link(muxer, fileSink)) {
- gst_object_unref(encodeBin);
- return 0;
- }
-
- if (m_captureMode & Audio) {
- GstElement *audioConvert = gst_element_factory_make("audioconvert", "audioconvert");
- GstElement *audioQueue = gst_element_factory_make("queue", "audio-encode-queue");
- m_audioVolume = gst_element_factory_make("volume", "volume");
- gst_bin_add_many(GST_BIN(encodeBin), audioConvert, audioQueue, m_audioVolume, NULL);
-
- GstElement *audioEncoder = m_audioEncodeControl->createEncoder();
- if (!audioEncoder) {
- gst_object_unref(encodeBin);
- qWarning() << "Could not create an audio encoder element:" << m_audioEncodeControl->audioSettings().codec();
- return 0;
- }
-
- gst_bin_add(GST_BIN(encodeBin), audioEncoder);
-
- if (!gst_element_link_many(audioConvert, audioQueue, m_audioVolume, audioEncoder, muxer, NULL)) {
- m_audioVolume = 0;
- gst_object_unref(encodeBin);
- return 0;
- }
-
- g_object_set(G_OBJECT(m_audioVolume), "mute", m_muted, NULL);
- g_object_set(G_OBJECT(m_audioVolume), "volume", m_volume, NULL);
-
- // add ghostpads
- GstPad *pad = gst_element_get_static_pad(audioConvert, "sink");
- gst_element_add_pad(GST_ELEMENT(encodeBin), gst_ghost_pad_new("audiosink", pad));
- gst_object_unref(GST_OBJECT(pad));
- }
-
- if (m_captureMode & Video) {
- GstElement *videoQueue = gst_element_factory_make("queue", "video-encode-queue");
- GstElement *colorspace = gst_element_factory_make(QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME, "videoconvert-encoder");
- GstElement *videoscale = gst_element_factory_make("videoscale","videoscale-encoder");
- gst_bin_add_many(GST_BIN(encodeBin), videoQueue, colorspace, videoscale, NULL);
-
- GstElement *videoEncoder = m_videoEncodeControl->createEncoder();
- if (!videoEncoder) {
- gst_object_unref(encodeBin);
- qWarning() << "Could not create a video encoder element:" << m_videoEncodeControl->videoSettings().codec();
- return 0;
- }
-
- gst_bin_add(GST_BIN(encodeBin), videoEncoder);
-
- if (!gst_element_link_many(videoQueue, colorspace, videoscale, videoEncoder, muxer, NULL)) {
- gst_object_unref(encodeBin);
- return 0;
- }
-
- // add ghostpads
- GstPad *pad = gst_element_get_static_pad(videoQueue, "sink");
- gst_element_add_pad(GST_ELEMENT(encodeBin), gst_ghost_pad_new("videosink", pad));
- gst_object_unref(GST_OBJECT(pad));
- }
-
- return encodeBin;
-}
-
-GstElement *QGstreamerCaptureSession::buildAudioSrc()
-{
- GstElement *audioSrc = 0;
- if (m_audioInputFactory)
- audioSrc = m_audioInputFactory->buildElement();
- else {
- QString elementName = "alsasrc";
- QString device;
-
- if (m_captureDevice.startsWith("alsa:")) {
- device = m_captureDevice.mid(QString("alsa:").length());
- } else if (m_captureDevice.startsWith("oss:")) {
- elementName = "osssrc";
- device = m_captureDevice.mid(QString("oss:").length());
- } else if (m_captureDevice.startsWith("pulseaudio:")) {
- elementName = "pulsesrc";
- } else {
- elementName = "autoaudiosrc";
- }
-
- audioSrc = gst_element_factory_make(elementName.toLatin1().constData(), "audio_src");
- if (audioSrc && !device.isEmpty())
- g_object_set(G_OBJECT(audioSrc), "device", device.toLocal8Bit().constData(), NULL);
- }
-
- if (!audioSrc) {
- emit error(int(QMediaRecorder::ResourceError), tr("Could not create an audio source element"));
- audioSrc = gst_element_factory_make("fakesrc", NULL);
- }
-
- return audioSrc;
-}
-
-GstElement *QGstreamerCaptureSession::buildAudioPreview()
-{
- GstElement *previewElement = 0;
-
- if (m_audioPreviewFactory) {
- previewElement = m_audioPreviewFactory->buildElement();
- } else {
-
-
-#if 1
- previewElement = gst_element_factory_make("fakesink", "audio-preview");
-#else
- GstElement *bin = gst_bin_new("audio-preview-bin");
- GstElement *visual = gst_element_factory_make("libvisual_lv_scope", "audio-preview");
- GstElement *sink = gst_element_factory_make("ximagesink", NULL);
- gst_bin_add_many(GST_BIN(bin), visual, sink, NULL);
- gst_element_link_many(visual,sink, NULL);
-
-
- // add ghostpads
- GstPad *pad = gst_element_get_static_pad(visual, "sink");
- Q_ASSERT(pad);
- gst_element_add_pad(GST_ELEMENT(bin), gst_ghost_pad_new("audiosink", pad));
- gst_object_unref(GST_OBJECT(pad));
-
- previewElement = bin;
-#endif
- }
-
- return previewElement;
-}
-
-GstElement *QGstreamerCaptureSession::buildVideoSrc()
-{
- GstElement *videoSrc = 0;
- if (m_videoInputFactory) {
- videoSrc = m_videoInputFactory->buildElement();
- } else {
- videoSrc = gst_element_factory_make("videotestsrc", "video_test_src");
- //videoSrc = gst_element_factory_make("v4l2src", "video_test_src");
- }
-
- return videoSrc;
-}
-
-GstElement *QGstreamerCaptureSession::buildVideoPreview()
-{
- GstElement *previewElement = 0;
-
- if (m_viewfinderInterface) {
- GstElement *bin = gst_bin_new("video-preview-bin");
- GstElement *colorspace = gst_element_factory_make(QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME, "videoconvert-preview");
- GstElement *capsFilter = gst_element_factory_make("capsfilter", "capsfilter-video-preview");
- GstElement *preview = m_viewfinderInterface->videoSink();
-
- gst_bin_add_many(GST_BIN(bin), colorspace, capsFilter, preview, NULL);
- gst_element_link(colorspace,capsFilter);
- gst_element_link(capsFilter,preview);
-
- QSize resolution;
- qreal frameRate = 0;
-
- if (m_captureMode & Video) {
- QVideoEncoderSettings videoSettings = m_videoEncodeControl->videoSettings();
- resolution = videoSettings.resolution();
- frameRate = videoSettings.frameRate();
- } else if (m_captureMode & Image) {
- resolution = m_imageEncodeControl->imageSettings().resolution();
- }
-
- GstCaps *caps = QGstUtils::videoFilterCaps();
-
- if (!resolution.isEmpty()) {
- gst_caps_set_simple(caps, "width", G_TYPE_INT, resolution.width(), NULL);
- gst_caps_set_simple(caps, "height", G_TYPE_INT, resolution.height(), NULL);
- }
- if (frameRate > 0.001) {
- QPair<int,int> rate = m_videoEncodeControl->rateAsRational();
-
- //qDebug() << "frame rate:" << num << denum;
-
- gst_caps_set_simple(caps, "framerate", GST_TYPE_FRACTION, rate.first, rate.second, NULL);
- }
-
- //qDebug() << "set video preview caps filter:" << gst_caps_to_string(caps);
-
- g_object_set(G_OBJECT(capsFilter), "caps", caps, NULL);
-
- gst_caps_unref(caps);
-
- // add ghostpads
- GstPad *pad = gst_element_get_static_pad(colorspace, "sink");
- Q_ASSERT(pad);
- gst_element_add_pad(GST_ELEMENT(bin), gst_ghost_pad_new("videosink", pad));
- gst_object_unref(GST_OBJECT(pad));
-
- previewElement = bin;
- } else {
-#if 1
- previewElement = gst_element_factory_make("fakesink", "video-preview");
-#else
- GstElement *bin = gst_bin_new("video-preview-bin");
- GstElement *colorspace = gst_element_factory_make(QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME, "videoconvert-preview");
- GstElement *preview = gst_element_factory_make("ximagesink", "video-preview");
- gst_bin_add_many(GST_BIN(bin), colorspace, preview, NULL);
- gst_element_link(colorspace,preview);
-
- // add ghostpads
- GstPad *pad = gst_element_get_static_pad(colorspace, "sink");
- Q_ASSERT(pad);
- gst_element_add_pad(GST_ELEMENT(bin), gst_ghost_pad_new("videosink", pad));
- gst_object_unref(GST_OBJECT(pad));
-
- previewElement = bin;
-#endif
- }
-
- return previewElement;
-}
-
-void QGstreamerCaptureSession::probeCaps(GstCaps *caps)
-{
-#if GST_CHECK_VERSION(1,0,0)
- gst_video_info_from_caps(&m_previewInfo, caps);
-#else
- Q_UNUSED(caps);
-#endif
-}
-
-bool QGstreamerCaptureSession::probeBuffer(GstBuffer *buffer)
-{
- if (m_passPrerollImage) {
- m_passImage = false;
- m_passPrerollImage = false;
-
- return true;
- } else if (!m_passImage) {
- return false;
- }
-
- m_passImage = false;
-
-#if GST_CHECK_VERSION(1,0,0)
- QImage img = QGstUtils::bufferToImage(buffer, m_previewInfo);
-#else
- QImage img = QGstUtils::bufferToImage(buffer);
-#endif
-
- if (img.isNull())
- return true;
-
- static QMetaMethod exposedSignal = QMetaMethod::fromSignal(&QGstreamerCaptureSession::imageExposed);
- exposedSignal.invoke(this,
- Qt::QueuedConnection,
- Q_ARG(int,m_imageRequestId));
-
- static QMetaMethod capturedSignal = QMetaMethod::fromSignal(&QGstreamerCaptureSession::imageCaptured);
- capturedSignal.invoke(this,
- Qt::QueuedConnection,
- Q_ARG(int,m_imageRequestId),
- Q_ARG(QImage,img));
-
- return true;
-}
-
-static gboolean saveImageFilter(GstElement *element,
- GstBuffer *buffer,
- GstPad *pad,
- void *appdata)
-{
- Q_UNUSED(element);
- Q_UNUSED(pad);
- QGstreamerCaptureSession *session = (QGstreamerCaptureSession *)appdata;
-
- QString fileName = session->m_imageFileName;
-
- if (!fileName.isEmpty()) {
- QFile f(fileName);
- if (f.open(QFile::WriteOnly)) {
-#if GST_CHECK_VERSION(1,0,0)
- 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);
- }
-#else
- f.write(reinterpret_cast<const char *>(buffer->data), buffer->size);
-#endif
- f.close();
-
- static QMetaMethod savedSignal = QMetaMethod::fromSignal(&QGstreamerCaptureSession::imageSaved);
- savedSignal.invoke(session,
- Qt::QueuedConnection,
- Q_ARG(int,session->m_imageRequestId),
- Q_ARG(QString,fileName));
- }
- }
-
- return TRUE;
-}
-
-GstElement *QGstreamerCaptureSession::buildImageCapture()
-{
- GstElement *bin = gst_bin_new("image-capture-bin");
- GstElement *queue = gst_element_factory_make("queue", "queue-image-capture");
- GstElement *colorspace = gst_element_factory_make(QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME, "videoconvert-image-capture");
- GstElement *encoder = gst_element_factory_make("jpegenc", "image-encoder");
- GstElement *sink = gst_element_factory_make("fakesink","sink-image-capture");
-
- GstPad *pad = gst_element_get_static_pad(queue, "src");
- Q_ASSERT(pad);
-
- addProbeToPad(pad, false);
-
- gst_object_unref(GST_OBJECT(pad));
-
- g_object_set(G_OBJECT(sink), "signal-handoffs", TRUE, NULL);
- g_signal_connect(G_OBJECT(sink), "handoff", G_CALLBACK(saveImageFilter), this);
-
- gst_bin_add_many(GST_BIN(bin), queue, colorspace, encoder, sink, NULL);
- gst_element_link_many(queue, colorspace, encoder, sink, NULL);
-
- // add ghostpads
- pad = gst_element_get_static_pad(queue, "sink");
- Q_ASSERT(pad);
- gst_element_add_pad(GST_ELEMENT(bin), gst_ghost_pad_new("imagesink", pad));
- gst_object_unref(GST_OBJECT(pad));
-
- m_passImage = false;
- m_passPrerollImage = true;
- m_imageFileName = QString();
-
- return bin;
-}
-
-void QGstreamerCaptureSession::captureImage(int requestId, const QString &fileName)
-{
- m_imageRequestId = requestId;
- m_imageFileName = fileName;
- m_passImage = true;
-}
-
-
-#define REMOVE_ELEMENT(element) { if (element) {gst_bin_remove(GST_BIN(m_pipeline), element); element = 0;} }
-#define UNREF_ELEMENT(element) { if (element) { gst_object_unref(GST_OBJECT(element)); element = 0; } }
-
-bool QGstreamerCaptureSession::rebuildGraph(QGstreamerCaptureSession::PipelineMode newMode)
-{
- removeAudioBufferProbe();
- REMOVE_ELEMENT(m_audioSrc);
- REMOVE_ELEMENT(m_audioPreview);
- REMOVE_ELEMENT(m_audioPreviewQueue);
- REMOVE_ELEMENT(m_audioTee);
- REMOVE_ELEMENT(m_videoSrc);
- REMOVE_ELEMENT(m_videoPreview);
- REMOVE_ELEMENT(m_videoPreviewQueue);
- REMOVE_ELEMENT(m_videoTee);
- REMOVE_ELEMENT(m_encodeBin);
- REMOVE_ELEMENT(m_imageCaptureBin);
- m_audioVolume = 0;
-
- bool ok = true;
-
- switch (newMode) {
- case EmptyPipeline:
- break;
- case PreviewPipeline:
- if (m_captureMode & Audio) {
- m_audioSrc = buildAudioSrc();
- m_audioPreview = buildAudioPreview();
-
- ok &= m_audioSrc && m_audioPreview;
-
- if (ok) {
- gst_bin_add_many(GST_BIN(m_pipeline), m_audioSrc, m_audioPreview, NULL);
- ok &= gst_element_link(m_audioSrc, m_audioPreview);
- } else {
- UNREF_ELEMENT(m_audioSrc);
- UNREF_ELEMENT(m_audioPreview);
- }
- }
- if (m_captureMode & Video || m_captureMode & Image) {
- m_videoSrc = buildVideoSrc();
- m_videoTee = gst_element_factory_make("tee", "video-preview-tee");
- m_videoPreviewQueue = gst_element_factory_make("queue", "video-preview-queue");
- m_videoPreview = buildVideoPreview();
- m_imageCaptureBin = buildImageCapture();
-
- ok &= m_videoSrc && m_videoTee && m_videoPreviewQueue && m_videoPreview && m_imageCaptureBin;
-
- if (ok) {
- gst_bin_add_many(GST_BIN(m_pipeline), m_videoSrc, m_videoTee,
- m_videoPreviewQueue, m_videoPreview,
- m_imageCaptureBin, NULL);
-
- ok &= gst_element_link(m_videoSrc, m_videoTee);
- ok &= gst_element_link(m_videoTee, m_videoPreviewQueue);
- ok &= gst_element_link(m_videoPreviewQueue, m_videoPreview);
- ok &= gst_element_link(m_videoTee, m_imageCaptureBin);
- } else {
- UNREF_ELEMENT(m_videoSrc);
- UNREF_ELEMENT(m_videoTee);
- UNREF_ELEMENT(m_videoPreviewQueue);
- UNREF_ELEMENT(m_videoPreview);
- UNREF_ELEMENT(m_imageCaptureBin);
- }
- }
- break;
- case RecordingPipeline:
- m_encodeBin = buildEncodeBin();
- gst_bin_add(GST_BIN(m_pipeline), m_encodeBin);
-
- if (m_captureMode & Audio) {
- m_audioSrc = buildAudioSrc();
- ok &= m_audioSrc != 0;
-
- gst_bin_add(GST_BIN(m_pipeline), m_audioSrc);
- ok &= gst_element_link(m_audioSrc, m_encodeBin);
- }
-
- if (m_captureMode & Video) {
- m_videoSrc = buildVideoSrc();
- ok &= m_videoSrc != 0;
-
- gst_bin_add(GST_BIN(m_pipeline), m_videoSrc);
- ok &= gst_element_link(m_videoSrc, m_encodeBin);
- }
-
- if (!m_metaData.isEmpty())
- setMetaData(m_metaData);
-
- break;
- case PreviewAndRecordingPipeline:
- m_encodeBin = buildEncodeBin();
- if (m_encodeBin)
- gst_bin_add(GST_BIN(m_pipeline), m_encodeBin);
-
- ok &= m_encodeBin != 0;
-
- if (ok && m_captureMode & Audio) {
- m_audioSrc = buildAudioSrc();
- m_audioPreview = buildAudioPreview();
- m_audioTee = gst_element_factory_make("tee", NULL);
- m_audioPreviewQueue = gst_element_factory_make("queue", NULL);
-
- ok &= m_audioSrc && m_audioPreview && m_audioTee && m_audioPreviewQueue;
-
- if (ok) {
- gst_bin_add_many(GST_BIN(m_pipeline), m_audioSrc, m_audioTee,
- m_audioPreviewQueue, m_audioPreview, NULL);
- ok &= gst_element_link(m_audioSrc, m_audioTee);
- ok &= gst_element_link(m_audioTee, m_audioPreviewQueue);
- ok &= gst_element_link(m_audioPreviewQueue, m_audioPreview);
- ok &= gst_element_link(m_audioTee, m_encodeBin);
- } else {
- UNREF_ELEMENT(m_audioSrc);
- UNREF_ELEMENT(m_audioPreview);
- UNREF_ELEMENT(m_audioTee);
- UNREF_ELEMENT(m_audioPreviewQueue);
- }
- }
-
- if (ok && (m_captureMode & Video || m_captureMode & Image)) {
- m_videoSrc = buildVideoSrc();
- m_videoPreview = buildVideoPreview();
- m_videoTee = gst_element_factory_make("tee", NULL);
- m_videoPreviewQueue = gst_element_factory_make("queue", NULL);
-
- ok &= m_videoSrc && m_videoPreview && m_videoTee && m_videoPreviewQueue;
-
- if (ok) {
- gst_bin_add_many(GST_BIN(m_pipeline), m_videoSrc, m_videoTee,
- m_videoPreviewQueue, m_videoPreview, NULL);
- ok &= gst_element_link(m_videoSrc, m_videoTee);
- ok &= gst_element_link(m_videoTee, m_videoPreviewQueue);
- ok &= gst_element_link(m_videoPreviewQueue, m_videoPreview);
- } else {
- UNREF_ELEMENT(m_videoSrc);
- UNREF_ELEMENT(m_videoTee);
- UNREF_ELEMENT(m_videoPreviewQueue);
- UNREF_ELEMENT(m_videoPreview);
- }
-
- if (ok && (m_captureMode & Video))
- ok &= gst_element_link(m_videoTee, m_encodeBin);
- }
-
- if (!m_metaData.isEmpty())
- setMetaData(m_metaData);
-
-
- break;
- }
-
- if (!ok) {
- emit error(int(QMediaRecorder::FormatError),tr("Failed to build media capture pipeline."));
- }
-
- dumpGraph( QString("rebuild_graph_%1_%2").arg(m_pipelineMode).arg(newMode) );
-#ifdef QT_GST_CAPTURE_DEBUG
- if (m_encodeBin) {
- QString fileName = QString("rebuild_graph_encode_%1_%2").arg(m_pipelineMode).arg(newMode);
- GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(m_encodeBin), GST_DEBUG_GRAPH_SHOW_ALL, fileName.toLatin1());
- }
-#endif
-
- if (ok) {
- addAudioBufferProbe();
- m_pipelineMode = newMode;
- } else {
- m_pipelineMode = EmptyPipeline;
-
- REMOVE_ELEMENT(m_audioSrc);
- REMOVE_ELEMENT(m_audioPreview);
- REMOVE_ELEMENT(m_audioPreviewQueue);
- REMOVE_ELEMENT(m_audioTee);
- REMOVE_ELEMENT(m_videoSrc);
- REMOVE_ELEMENT(m_videoPreview);
- REMOVE_ELEMENT(m_videoPreviewQueue);
- REMOVE_ELEMENT(m_videoTee);
- REMOVE_ELEMENT(m_encodeBin);
- }
-
- return ok;
-}
-
-void QGstreamerCaptureSession::dumpGraph(const QString &fileName)
-{
-#ifdef QT_GST_CAPTURE_DEBUG
- GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(m_pipeline),
- 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.toLatin1());
-#else
- Q_UNUSED(fileName);
-#endif
-}
-
-QUrl QGstreamerCaptureSession::outputLocation() const
-{
- return m_sink;
-}
-
-bool QGstreamerCaptureSession::setOutputLocation(const QUrl& sink)
-{
- if (!sink.isRelative() && !sink.isLocalFile()) {
- qWarning("Output location must be a local file");
- return false;
- }
-
- m_sink = sink;
- return true;
-}
-
-void QGstreamerCaptureSession::setAudioInput(QGstreamerElementFactory *audioInput)
-{
- m_audioInputFactory = audioInput;
-}
-
-void QGstreamerCaptureSession::setAudioPreview(QGstreamerElementFactory *audioPreview)
-{
- m_audioPreviewFactory = audioPreview;
-}
-
-void QGstreamerCaptureSession::setVideoInput(QGstreamerVideoInput *videoInput)
-{
- m_videoInputFactory = videoInput;
-}
-
-void QGstreamerCaptureSession::setVideoPreview(QObject *viewfinder)
-{
- m_viewfinderInterface = qobject_cast<QGstreamerVideoRendererInterface*>(viewfinder);
- if (!m_viewfinderInterface)
- viewfinder = 0;
-
- if (m_viewfinder != viewfinder) {
- bool oldReady = isReady();
-
- if (m_viewfinder) {
- disconnect(m_viewfinder, SIGNAL(sinkChanged()),
- this, SIGNAL(viewfinderChanged()));
- disconnect(m_viewfinder, SIGNAL(readyChanged(bool)),
- this, SIGNAL(readyChanged(bool)));
-
- m_busHelper->removeMessageFilter(m_viewfinder);
- }
-
- m_viewfinder = viewfinder;
- //m_viewfinderHasChanged = true;
-
- if (m_viewfinder) {
- connect(m_viewfinder, SIGNAL(sinkChanged()),
- this, SIGNAL(viewfinderChanged()));
- connect(m_viewfinder, SIGNAL(readyChanged(bool)),
- this, SIGNAL(readyChanged(bool)));
-
- m_busHelper->installMessageFilter(m_viewfinder);
- }
-
- emit viewfinderChanged();
- if (oldReady != isReady())
- emit readyChanged(isReady());
- }
-}
-
-bool QGstreamerCaptureSession::isReady() const
-{
- //it's possible to use QCamera without any viewfinder attached
- return !m_viewfinderInterface || m_viewfinderInterface->isReady();
-}
-
-QGstreamerCaptureSession::State QGstreamerCaptureSession::state() const
-{
- return m_state;
-}
-
-QGstreamerCaptureSession::State QGstreamerCaptureSession::pendingState() const
-{
- return m_pendingState;
-}
-
-void QGstreamerCaptureSession::setState(QGstreamerCaptureSession::State newState)
-{
- if (newState == m_pendingState && !m_waitingForEos)
- return;
-
- m_pendingState = newState;
-
- PipelineMode newMode = EmptyPipeline;
-
- switch (newState) {
- case PausedState:
- case RecordingState:
- newMode = PreviewAndRecordingPipeline;
- break;
- case PreviewState:
- newMode = PreviewPipeline;
- break;
- case StoppedState:
- newMode = EmptyPipeline;
- break;
- }
-
- if (newMode != m_pipelineMode) {
- if (m_pipelineMode == PreviewAndRecordingPipeline) {
- if (!m_waitingForEos) {
- m_waitingForEos = true;
- //qDebug() << "Waiting for EOS";
- // Unless gstreamer is in GST_STATE_PLAYING our EOS message will not be received.
- gst_element_set_state(m_pipeline, GST_STATE_PLAYING);
- //with live sources it's necessary to send EOS even to pipeline
- //before going to STOPPED state
- gst_element_send_event(m_pipeline, gst_event_new_eos());
-
- return;
- } else {
- m_waitingForEos = false;
- //qDebug() << "EOS received";
- }
- }
-
- //select suitable default codecs/containers, if necessary
- m_recorderControl->applySettings();
-
- gst_element_set_state(m_pipeline, GST_STATE_NULL);
-
- if (!rebuildGraph(newMode)) {
- m_pendingState = StoppedState;
- m_state = StoppedState;
- emit stateChanged(StoppedState);
-
- return;
- }
- }
-
- switch (newState) {
- case PausedState:
- gst_element_set_state(m_pipeline, GST_STATE_PAUSED);
- break;
- case RecordingState:
- case PreviewState:
- gst_element_set_state(m_pipeline, GST_STATE_PLAYING);
- break;
- case StoppedState:
- gst_element_set_state(m_pipeline, GST_STATE_NULL);
- }
-
- //we have to do it here, since gstreamer will not emit bus messages any more
- if (newState == StoppedState) {
- m_state = StoppedState;
- emit stateChanged(StoppedState);
- }
-}
-
-
-qint64 QGstreamerCaptureSession::duration() const
-{
- gint64 duration = 0;
- if (m_encodeBin && qt_gst_element_query_position(m_encodeBin, GST_FORMAT_TIME, &duration))
- return duration / 1000000;
- else
- return 0;
-}
-
-void QGstreamerCaptureSession::setCaptureDevice(const QString &deviceName)
-{
- m_captureDevice = deviceName;
-}
-
-void QGstreamerCaptureSession::setMetaData(const QMap<QByteArray, QVariant> &data)
-{
- //qDebug() << "QGstreamerCaptureSession::setMetaData" << data;
- m_metaData = data;
-
- if (m_encodeBin)
- QGstUtils::setMetaData(GST_BIN(m_encodeBin), data);
-}
-
-bool QGstreamerCaptureSession::processBusMessage(const QGstreamerMessage &message)
-{
- GstMessage* gm = message.rawMessage();
-
- if (gm) {
- if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ERROR) {
- GError *err;
- gchar *debug;
- gst_message_parse_error (gm, &err, &debug);
- emit error(int(QMediaRecorder::ResourceError),QString::fromUtf8(err->message));
- g_error_free (err);
- g_free (debug);
- }
-
- if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_pipeline)) {
- switch (GST_MESSAGE_TYPE(gm)) {
- case GST_MESSAGE_DURATION:
- break;
-
- case GST_MESSAGE_EOS:
- if (m_waitingForEos)
- setState(m_pendingState);
- break;
-
- case GST_MESSAGE_STATE_CHANGED:
- {
-
- GstState oldState;
- GstState newState;
- GstState pending;
-
- gst_message_parse_state_changed(gm, &oldState, &newState, &pending);
-
- 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]);
-
- #define ENUM_NAME(c,e,v) (c::staticMetaObject.enumerator(c::staticMetaObject.indexOfEnumerator(e)).valueToKey((v)))
-
- qDebug() << "Current session state:" << ENUM_NAME(QGstreamerCaptureSession,"State",m_state);
- qDebug() << "Pending session state:" << ENUM_NAME(QGstreamerCaptureSession,"State",m_pendingState);
- */
-
- switch (newState) {
- case GST_STATE_VOID_PENDING:
- case GST_STATE_NULL:
- case GST_STATE_READY:
- if (m_state != StoppedState && m_pendingState == StoppedState) {
- emit stateChanged(m_state = StoppedState);
- dumpGraph("stopped");
- }
- break;
- case GST_STATE_PAUSED:
- if (m_state != PausedState && m_pendingState == PausedState)
- emit stateChanged(m_state = PausedState);
- dumpGraph("paused");
-
- if (m_pipelineMode == RecordingPipeline && !m_metaData.isEmpty())
- setMetaData(m_metaData);
- break;
- case GST_STATE_PLAYING:
- {
- if ((m_pendingState == PreviewState || m_pendingState == RecordingState) &&
- m_state != m_pendingState)
- {
- m_state = m_pendingState;
- emit stateChanged(m_state);
- }
-
- if (m_pipelineMode == PreviewPipeline)
- dumpGraph("preview");
- else
- dumpGraph("recording");
- }
- break;
- }
- }
- break;
- default:
- break;
- }
- //qDebug() << "New session state:" << ENUM_NAME(QGstreamerCaptureSession,"State",m_state);
- }
- }
- return false;
-}
-
-void QGstreamerCaptureSession::setMuted(bool muted)
-{
- if (bool(m_muted) != muted) {
- m_muted = muted;
- if (m_audioVolume)
- g_object_set(G_OBJECT(m_audioVolume), "mute", m_muted, NULL);
-
- emit mutedChanged(muted);
- }
-}
-
-void QGstreamerCaptureSession::setVolume(qreal volume)
-{
- if (!qFuzzyCompare(double(volume), m_volume)) {
- m_volume = volume;
- if (m_audioVolume)
- g_object_set(G_OBJECT(m_audioVolume), "volume", m_volume, NULL);
-
- emit volumeChanged(volume);
- }
-}
-
-void QGstreamerCaptureSession::addProbe(QGstreamerAudioProbeControl* probe)
-{
- Q_ASSERT(!m_audioProbe);
- m_audioProbe = probe;
- addAudioBufferProbe();
-}
-
-void QGstreamerCaptureSession::removeProbe(QGstreamerAudioProbeControl* probe)
-{
- Q_ASSERT(m_audioProbe == probe);
- removeAudioBufferProbe();
- m_audioProbe = 0;
-}
-
-GstPad *QGstreamerCaptureSession::getAudioProbePad()
-{
- // first see if preview element is available
- if (m_audioPreview) {
- GstPad *pad = gst_element_get_static_pad(m_audioPreview, "sink");
- if (pad)
- return pad;
- }
-
- // preview element is not available,
- // try to use sink pin of audio encoder.
- if (m_encodeBin) {
- GstElement *audioEncoder = gst_bin_get_by_name(GST_BIN(m_encodeBin), "audio-encoder-bin");
- if (audioEncoder) {
- GstPad *pad = gst_element_get_static_pad(audioEncoder, "sink");
- gst_object_unref(audioEncoder);
- if (pad)
- return pad;
- }
- }
-
- return 0;
-}
-
-void QGstreamerCaptureSession::removeAudioBufferProbe()
-{
- if (!m_audioProbe)
- return;
-
- GstPad *pad = getAudioProbePad();
- if (pad) {
- m_audioProbe->removeProbeFromPad(pad);
- gst_object_unref(GST_OBJECT(pad));
- }
-}
-
-void QGstreamerCaptureSession::addAudioBufferProbe()
-{
- if (!m_audioProbe)
- return;
-
- GstPad *pad = getAudioProbePad();
- if (pad) {
- m_audioProbe->addProbeToPad(pad);
- gst_object_unref(GST_OBJECT(pad));
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h b/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h
deleted file mode 100644
index e0c9107a7..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h
+++ /dev/null
@@ -1,244 +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 QGSTREAMERCAPTURESESSION_H
-#define QGSTREAMERCAPTURESESSION_H
-
-#include <qmediarecordercontrol.h>
-#include <qmediarecorder.h>
-
-#include <QtCore/qmutex.h>
-#include <QtCore/qurl.h>
-
-#include <gst/gst.h>
-#include <gst/video/video.h>
-
-#include <private/qgstreamerbushelper_p.h>
-#include <private/qgstreamerbufferprobe_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class QGstreamerMessage;
-class QGstreamerBusHelper;
-class QGstreamerAudioEncode;
-class QGstreamerVideoEncode;
-class QGstreamerImageEncode;
-class QGstreamerRecorderControl;
-class QGstreamerMediaContainerControl;
-class QGstreamerVideoRendererInterface;
-class QGstreamerAudioProbeControl;
-
-class QGstreamerElementFactory
-{
-public:
- virtual GstElement *buildElement() = 0;
- virtual void prepareWinId() {}
-};
-
-class QGstreamerVideoInput : public QGstreamerElementFactory
-{
-public:
- virtual QList<qreal> supportedFrameRates(const QSize &frameSize = QSize()) const = 0;
- virtual QList<QSize> supportedResolutions(qreal frameRate = -1) const = 0;
-};
-
-class QGstreamerCaptureSession
- : public QObject
- , public QGstreamerBusMessageFilter
- , private QGstreamerBufferProbe
-{
- Q_OBJECT
- Q_PROPERTY(qint64 duration READ duration NOTIFY durationChanged)
- Q_ENUMS(State)
- Q_ENUMS(CaptureMode)
- Q_INTERFACES(QGstreamerBusMessageFilter)
-public:
- enum CaptureMode { Audio = 1,
- Video = 2,
- Image = 4,
- AudioAndVideo = Audio | Video,
- AudioAndVideoAndImage = Audio | Video | Image
- };
- enum State { StoppedState, PreviewState, PausedState, RecordingState };
-
- QGstreamerCaptureSession(CaptureMode captureMode, QObject *parent);
- ~QGstreamerCaptureSession();
-
- QGstreamerBusHelper *bus() { return m_busHelper; }
-
- CaptureMode captureMode() const { return m_captureMode; }
- void setCaptureMode(CaptureMode);
-
- QUrl outputLocation() const;
- bool setOutputLocation(const QUrl& sink);
-
- QGstreamerAudioEncode *audioEncodeControl() const { return m_audioEncodeControl; }
- QGstreamerVideoEncode *videoEncodeControl() const { return m_videoEncodeControl; }
- QGstreamerImageEncode *imageEncodeControl() const { return m_imageEncodeControl; }
-
- QGstreamerRecorderControl *recorderControl() const { return m_recorderControl; }
- QGstreamerMediaContainerControl *mediaContainerControl() const { return m_mediaContainerControl; }
-
- QGstreamerElementFactory *audioInput() const { return m_audioInputFactory; }
- void setAudioInput(QGstreamerElementFactory *audioInput);
-
- QGstreamerElementFactory *audioPreview() const { return m_audioPreviewFactory; }
- void setAudioPreview(QGstreamerElementFactory *audioPreview);
-
- QGstreamerVideoInput *videoInput() const { return m_videoInputFactory; }
- void setVideoInput(QGstreamerVideoInput *videoInput);
-
- QObject *videoPreview() const { return m_viewfinder; }
- void setVideoPreview(QObject *viewfinder);
-
- void captureImage(int requestId, const QString &fileName);
-
- State state() const;
- State pendingState() const;
-
- qint64 duration() const;
- bool isMuted() const { return m_muted; }
- qreal volume() const { return m_volume; }
-
- bool isReady() const;
-
- bool processBusMessage(const QGstreamerMessage &message) override;
-
- void addProbe(QGstreamerAudioProbeControl* probe);
- void removeProbe(QGstreamerAudioProbeControl* probe);
-
-signals:
- void stateChanged(QGstreamerCaptureSession::State state);
- void durationChanged(qint64 duration);
- void error(int error, const QString &errorString);
- void imageExposed(int requestId);
- void imageCaptured(int requestId, const QImage &img);
- void imageSaved(int requestId, const QString &path);
- void mutedChanged(bool);
- void volumeChanged(qreal);
- void readyChanged(bool);
- void viewfinderChanged();
-
-public slots:
- void setState(QGstreamerCaptureSession::State);
- void setCaptureDevice(const QString &deviceName);
-
- void dumpGraph(const QString &fileName);
-
- void setMetaData(const QMap<QByteArray, QVariant>&);
- void setMuted(bool);
- void setVolume(qreal volume);
-
-private:
- void probeCaps(GstCaps *caps) override;
- bool probeBuffer(GstBuffer *buffer) override;
-
- enum PipelineMode { EmptyPipeline, PreviewPipeline, RecordingPipeline, PreviewAndRecordingPipeline };
-
- GstElement *buildEncodeBin();
- GstElement *buildAudioSrc();
- GstElement *buildAudioPreview();
- GstElement *buildVideoSrc();
- GstElement *buildVideoPreview();
- GstElement *buildImageCapture();
-
- bool rebuildGraph(QGstreamerCaptureSession::PipelineMode newMode);
-
- GstPad *getAudioProbePad();
- void removeAudioBufferProbe();
- void addAudioBufferProbe();
-
- QUrl m_sink;
- QString m_captureDevice;
- State m_state;
- State m_pendingState;
- bool m_waitingForEos;
- PipelineMode m_pipelineMode;
- QGstreamerCaptureSession::CaptureMode m_captureMode;
- QMap<QByteArray, QVariant> m_metaData;
-
- QGstreamerAudioProbeControl *m_audioProbe;
-
- QGstreamerElementFactory *m_audioInputFactory;
- QGstreamerElementFactory *m_audioPreviewFactory;
- QGstreamerVideoInput *m_videoInputFactory;
- QObject *m_viewfinder;
- QGstreamerVideoRendererInterface *m_viewfinderInterface;
-
- QGstreamerAudioEncode *m_audioEncodeControl;
- QGstreamerVideoEncode *m_videoEncodeControl;
- QGstreamerImageEncode *m_imageEncodeControl;
- QGstreamerRecorderControl *m_recorderControl;
- QGstreamerMediaContainerControl *m_mediaContainerControl;
-
- QGstreamerBusHelper *m_busHelper;
- GstBus* m_bus;
- GstElement *m_pipeline;
-
- GstElement *m_audioSrc;
- GstElement *m_audioTee;
- GstElement *m_audioPreviewQueue;
- GstElement *m_audioPreview;
- GstElement *m_audioVolume;
- gboolean m_muted;
- double m_volume;
-
- GstElement *m_videoSrc;
- GstElement *m_videoTee;
- GstElement *m_videoPreviewQueue;
- GstElement *m_videoPreview;
-
- GstElement *m_imageCaptureBin;
-
- GstElement *m_encodeBin;
-
-#if GST_CHECK_VERSION(1,0,0)
- GstVideoInfo m_previewInfo;
-#endif
-
-public:
- bool m_passImage;
- bool m_passPrerollImage;
- QString m_imageFileName;
- int m_imageRequestId;
-};
-
-QT_END_NAMESPACE
-
-#endif // QGSTREAMERCAPTURESESSION_H
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.cpp b/src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.cpp
deleted file mode 100644
index 120c19af6..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.cpp
+++ /dev/null
@@ -1,113 +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 "qgstreamerimagecapturecontrol.h"
-#include <QtCore/QDebug>
-#include <QtCore/QDir>
-
-QGstreamerImageCaptureControl::QGstreamerImageCaptureControl(QGstreamerCaptureSession *session)
- :QCameraImageCaptureControl(session), m_session(session), m_ready(false), m_lastId(0)
-{
- connect(m_session, SIGNAL(stateChanged(QGstreamerCaptureSession::State)), SLOT(updateState()));
- connect(m_session, SIGNAL(imageExposed(int)), this, SIGNAL(imageExposed(int)));
- connect(m_session, SIGNAL(imageCaptured(int,QImage)), this, SIGNAL(imageCaptured(int,QImage)));
- connect(m_session, SIGNAL(imageSaved(int,QString)), this, SIGNAL(imageSaved(int,QString)));
-}
-
-QGstreamerImageCaptureControl::~QGstreamerImageCaptureControl()
-{
-}
-
-bool QGstreamerImageCaptureControl::isReadyForCapture() const
-{
- return m_ready;
-}
-
-int QGstreamerImageCaptureControl::capture(const QString &fileName)
-{
- m_lastId++;
-
- //it's allowed to request image capture while camera is starting
- if (m_session->pendingState() == QGstreamerCaptureSession::StoppedState ||
- !(m_session->captureMode() & QGstreamerCaptureSession::Image)) {
- //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, m_lastId),
- Q_ARG(int, QCameraImageCapture::NotReadyError),
- Q_ARG(QString,tr("Not ready to capture")));
-
- return m_lastId;
- }
-
- QString path = fileName;
- if (path.isEmpty()) {
- int lastImage = 0;
- QDir outputDir = QDir::currentPath();
- const auto list = outputDir.entryList(QStringList() << "img_*.jpg");
- for (const QString &fileName : list) {
- int imgNumber = QStringView{fileName}.mid(4, fileName.size()-8).toInt();
- lastImage = qMax(lastImage, imgNumber);
- }
-
- path = QString("img_%1.jpg").arg(lastImage+1,
- 4, //fieldWidth
- 10,
- QLatin1Char('0'));
- }
-
- m_session->captureImage(m_lastId, path);
-
- return m_lastId;
-}
-
-void QGstreamerImageCaptureControl::cancelCapture()
-{
-
-}
-
-void QGstreamerImageCaptureControl::updateState()
-{
- bool ready = (m_session->state() == QGstreamerCaptureSession::PreviewState) &&
- (m_session->captureMode() & QGstreamerCaptureSession::Image);
-
- if (m_ready != ready) {
- emit readyForCaptureChanged(m_ready = ready);
- }
-}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.h b/src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.h
deleted file mode 100644
index f58cf09c5..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.h
+++ /dev/null
@@ -1,74 +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 QGSTREAMERIMAGECAPTURECONTROL_H
-#define QGSTREAMERIMAGECAPTURECONTROL_H
-
-#include <qcameraimagecapturecontrol.h>
-#include "qgstreamercapturesession.h"
-
-QT_BEGIN_NAMESPACE
-
-class QGstreamerImageCaptureControl : public QCameraImageCaptureControl
-{
- Q_OBJECT
-public:
- QGstreamerImageCaptureControl(QGstreamerCaptureSession *session);
- virtual ~QGstreamerImageCaptureControl();
-
- QCameraImageCapture::DriveMode driveMode() const override { return QCameraImageCapture::SingleImageCapture; }
- void setDriveMode(QCameraImageCapture::DriveMode) override {}
-
- bool isReadyForCapture() const override;
- int capture(const QString &fileName) override;
- void cancelCapture() override;
-
-private slots:
- void updateState();
-
-private:
- QGstreamerCaptureSession *m_session;
- bool m_ready;
- int m_lastId;
-};
-
-QT_END_NAMESPACE
-
-#endif // QGSTREAMERCAPTURECORNTROL_H
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerimageencode.cpp b/src/plugins/gstreamer/mediacapture/qgstreamerimageencode.cpp
deleted file mode 100644
index f9e6ce9ef..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamerimageencode.cpp
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "qgstreamerimageencode.h"
-#include "qgstreamercapturesession.h"
-
-#include <QtCore/qdebug.h>
-
-#include <math.h>
-
-QGstreamerImageEncode::QGstreamerImageEncode(QGstreamerCaptureSession *session)
- :QImageEncoderControl(session), m_session(session)
-{
-}
-
-QGstreamerImageEncode::~QGstreamerImageEncode()
-{
-}
-
-QList<QSize> QGstreamerImageEncode::supportedResolutions(const QImageEncoderSettings &, bool *continuous) const
-{
- if (continuous)
- *continuous = m_session->videoInput() != 0;
-
- return m_session->videoInput() ? m_session->videoInput()->supportedResolutions() : QList<QSize>();
-}
-
-QStringList QGstreamerImageEncode::supportedImageCodecs() const
-{
- return QStringList() << "jpeg";
-}
-
-QString QGstreamerImageEncode::imageCodecDescription(const QString &codecName) const
-{
- if (codecName == "jpeg")
- return tr("JPEG image encoder");
-
- return QString();
-}
-
-QImageEncoderSettings QGstreamerImageEncode::imageSettings() const
-{
- return m_settings;
-}
-
-void QGstreamerImageEncode::setImageSettings(const QImageEncoderSettings &settings)
-{
- if (m_settings != settings) {
- m_settings = settings;
- emit settingsChanged();
- }
-}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerimageencode.h b/src/plugins/gstreamer/mediacapture/qgstreamerimageencode.h
deleted file mode 100644
index f3ebd3e90..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamerimageencode.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 QGSTREAMERIMAGEENCODE_H
-#define QGSTREAMERIMAGEENCODE_H
-
-#include <qimageencodercontrol.h>
-
-#include <QtCore/qstringlist.h>
-#include <QtCore/qmap.h>
-
-#include <gst/gst.h>
-
-QT_BEGIN_NAMESPACE
-
-class QGstreamerCaptureSession;
-
-class QGstreamerImageEncode : public QImageEncoderControl
-{
- Q_OBJECT
-public:
- QGstreamerImageEncode(QGstreamerCaptureSession *session);
- virtual ~QGstreamerImageEncode();
-
- QList<QSize> supportedResolutions(const QImageEncoderSettings &settings = QImageEncoderSettings(),
- bool *continuous = 0) const override;
-
- QStringList supportedImageCodecs() const override;
- QString imageCodecDescription(const QString &codecName) const override;
-
- QImageEncoderSettings imageSettings() const override;
- void setImageSettings(const QImageEncoderSettings &settings) override;
-
-Q_SIGNALS:
- void settingsChanged();
-
-private:
- QImageEncoderSettings m_settings;
-
- QGstreamerCaptureSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.cpp b/src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.cpp
deleted file mode 100644
index 33351476d..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.cpp
+++ /dev/null
@@ -1,60 +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 "qgstreamermediacontainercontrol.h"
-
-#include <private/qgstutils_p.h>
-
-#include <QtCore/qdebug.h>
-
-QGstreamerMediaContainerControl::QGstreamerMediaContainerControl(QObject *parent)
- :QMediaContainerControl(parent)
- , m_containers(QGstCodecsInfo::Muxer)
-{
-}
-
-QSet<QString> QGstreamerMediaContainerControl::supportedStreamTypes(const QString &container) const
-{
- return m_containers.supportedStreamTypes(container);
-}
-
-QString QGstreamerMediaContainerControl::containerExtension() const
-{
- return QGstUtils::fileExtensionForMimeType(m_format);
-}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.h b/src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.h
deleted file mode 100644
index 02c7346b1..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.h
+++ /dev/null
@@ -1,80 +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 QGSTREAMERMEDIACONTAINERCONTROL_H
-#define QGSTREAMERMEDIACONTAINERCONTROL_H
-
-#include <qmediacontainercontrol.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qset.h>
-
-#include <private/qgstcodecsinfo_p.h>
-
-#include <gst/gst.h>
-
-QT_BEGIN_NAMESPACE
-
-class QGstreamerMediaContainerControl : public QMediaContainerControl
-{
-Q_OBJECT
-public:
- QGstreamerMediaContainerControl(QObject *parent);
- ~QGstreamerMediaContainerControl() {}
-
- QStringList supportedContainers() const override { return m_containers.supportedCodecs(); }
- QString containerFormat() const override { return m_format; }
- void setContainerFormat(const QString &formatMimeType) override { m_format = formatMimeType; }
-
- QString containerDescription(const QString &formatMimeType) const override { return m_containers.codecDescription(formatMimeType); }
-
- QByteArray formatElementName() const { return m_containers.codecElement(containerFormat()); }
-
- QSet<QString> supportedStreamTypes(const QString &container) const;
-
- QString containerExtension() const;
-
-private:
- QString m_format;
- QGstCodecsInfo m_containers;
-};
-
-QT_END_NAMESPACE
-
-#endif // QGSTREAMERMEDIACONTAINERCONTROL_H
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp b/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp
deleted file mode 100644
index d7f4ec035..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp
+++ /dev/null
@@ -1,372 +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 "qgstreamerrecordercontrol.h"
-#include "qgstreameraudioencode.h"
-#include "qgstreamervideoencode.h"
-#include "qgstreamermediacontainercontrol.h"
-#include <QtCore/QDebug>
-#include <QtGui/qdesktopservices.h>
-#include <QStandardPaths>
-
-QGstreamerRecorderControl::QGstreamerRecorderControl(QGstreamerCaptureSession *session)
- :QMediaRecorderControl(session),
- m_session(session),
- m_state(QMediaRecorder::StoppedState),
- m_status(QMediaRecorder::UnloadedStatus)
-{
- connect(m_session, SIGNAL(stateChanged(QGstreamerCaptureSession::State)), SLOT(updateStatus()));
- connect(m_session, SIGNAL(error(int,QString)), SLOT(handleSessionError(int,QString)));
- connect(m_session, SIGNAL(durationChanged(qint64)), SIGNAL(durationChanged(qint64)));
- connect(m_session, SIGNAL(mutedChanged(bool)), SIGNAL(mutedChanged(bool)));
- connect(m_session, SIGNAL(volumeChanged(qreal)), SIGNAL(volumeChanged(qreal)));
- m_hasPreviewState = m_session->captureMode() != QGstreamerCaptureSession::Audio;
-}
-
-QGstreamerRecorderControl::~QGstreamerRecorderControl()
-{
-}
-
-QUrl QGstreamerRecorderControl::outputLocation() const
-{
- return m_session->outputLocation();
-}
-
-bool QGstreamerRecorderControl::setOutputLocation(const QUrl &sink)
-{
- m_outputLocation = sink;
- m_session->setOutputLocation(sink);
- return true;
-}
-
-
-QMediaRecorder::State QGstreamerRecorderControl::state() const
-{
- return m_state;
-}
-
-QMediaRecorder::Status QGstreamerRecorderControl::status() const
-{
- static QMediaRecorder::Status statusTable[3][3] = {
- //Stopped recorder state:
- { QMediaRecorder::LoadedStatus, QMediaRecorder::FinalizingStatus, QMediaRecorder::FinalizingStatus },
- //Recording recorder state:
- { QMediaRecorder::StartingStatus, QMediaRecorder::RecordingStatus, QMediaRecorder::PausedStatus },
- //Paused recorder state:
- { QMediaRecorder::StartingStatus, QMediaRecorder::RecordingStatus, QMediaRecorder::PausedStatus }
- };
-
- QMediaRecorder::State sessionState = QMediaRecorder::StoppedState;
-
- switch ( m_session->state() ) {
- case QGstreamerCaptureSession::RecordingState:
- sessionState = QMediaRecorder::RecordingState;
- break;
- case QGstreamerCaptureSession::PausedState:
- sessionState = QMediaRecorder::PausedState;
- break;
- case QGstreamerCaptureSession::PreviewState:
- case QGstreamerCaptureSession::StoppedState:
- sessionState = QMediaRecorder::StoppedState;
- break;
- }
-
- return statusTable[m_state][sessionState];
-}
-
-void QGstreamerRecorderControl::updateStatus()
-{
- QMediaRecorder::Status newStatus = status();
- if (m_status != newStatus) {
- m_status = newStatus;
- emit statusChanged(m_status);
- // If stop has been called and session state became stopped.
- if (m_status == QMediaRecorder::LoadedStatus)
- emit stateChanged(m_state);
- }
-}
-
-void QGstreamerRecorderControl::handleSessionError(int code, const QString &description)
-{
- emit error(code, description);
- stop();
-}
-
-qint64 QGstreamerRecorderControl::duration() const
-{
- return m_session->duration();
-}
-
-void QGstreamerRecorderControl::setState(QMediaRecorder::State state)
-{
- switch (state) {
- case QMediaRecorder::StoppedState:
- stop();
- break;
- case QMediaRecorder::PausedState:
- pause();
- break;
- case QMediaRecorder::RecordingState:
- record();
- break;
- }
-}
-
-void QGstreamerRecorderControl::record()
-{
- if (m_state == QMediaRecorder::RecordingState)
- return;
-
- m_state = QMediaRecorder::RecordingState;
-
- if (m_outputLocation.isEmpty()) {
- QString container = m_session->mediaContainerControl()->containerExtension();
- if (container.isEmpty())
- container = "raw";
-
- m_session->setOutputLocation(QUrl(generateFileName(defaultDir(), container)));
- }
-
- m_session->dumpGraph("before-record");
- if (!m_hasPreviewState || m_session->state() != QGstreamerCaptureSession::StoppedState) {
- m_session->setState(QGstreamerCaptureSession::RecordingState);
- } else
- emit error(QMediaRecorder::ResourceError, tr("Service has not been started"));
-
- m_session->dumpGraph("after-record");
-
- emit stateChanged(m_state);
- updateStatus();
-
- emit actualLocationChanged(m_session->outputLocation());
-}
-
-void QGstreamerRecorderControl::pause()
-{
- if (m_state == QMediaRecorder::PausedState)
- return;
-
- m_state = QMediaRecorder::PausedState;
-
- m_session->dumpGraph("before-pause");
- if (!m_hasPreviewState || m_session->state() != QGstreamerCaptureSession::StoppedState) {
- m_session->setState(QGstreamerCaptureSession::PausedState);
- } else
- emit error(QMediaRecorder::ResourceError, tr("Service has not been started"));
-
- emit stateChanged(m_state);
- updateStatus();
-}
-
-void QGstreamerRecorderControl::stop()
-{
- if (m_state == QMediaRecorder::StoppedState)
- return;
-
- m_state = QMediaRecorder::StoppedState;
-
- if (!m_hasPreviewState) {
- m_session->setState(QGstreamerCaptureSession::StoppedState);
- } else {
- if (m_session->state() != QGstreamerCaptureSession::StoppedState)
- m_session->setState(QGstreamerCaptureSession::PreviewState);
- }
-
- updateStatus();
-}
-
-void QGstreamerRecorderControl::applySettings()
-{
- //Check the codecs are compatible with container,
- //and choose the compatible codecs/container if omitted
- QGstreamerAudioEncode *audioEncodeControl = m_session->audioEncodeControl();
- QGstreamerVideoEncode *videoEncodeControl = m_session->videoEncodeControl();
- QGstreamerMediaContainerControl *mediaContainerControl = m_session->mediaContainerControl();
-
- bool needAudio = m_session->captureMode() & QGstreamerCaptureSession::Audio;
- bool needVideo = m_session->captureMode() & QGstreamerCaptureSession::Video;
-
- QStringList containerCandidates;
- if (mediaContainerControl->containerFormat().isEmpty())
- containerCandidates = mediaContainerControl->supportedContainers();
- else
- containerCandidates << mediaContainerControl->containerFormat();
-
-
- QStringList audioCandidates;
- if (needAudio) {
- QAudioEncoderSettings audioSettings = audioEncodeControl->audioSettings();
- if (audioSettings.codec().isEmpty())
- audioCandidates = audioEncodeControl->supportedAudioCodecs();
- else
- audioCandidates << audioSettings.codec();
- }
-
- QStringList videoCandidates;
- if (needVideo) {
- QVideoEncoderSettings videoSettings = videoEncodeControl->videoSettings();
- if (videoSettings.codec().isEmpty())
- videoCandidates = videoEncodeControl->supportedVideoCodecs();
- else
- videoCandidates << videoSettings.codec();
- }
-
- QString container;
- QString audioCodec;
- QString videoCodec;
-
- for (const QString &containerCandidate : qAsConst(containerCandidates)) {
- QSet<QString> supportedTypes = mediaContainerControl->supportedStreamTypes(containerCandidate);
-
- audioCodec.clear();
- videoCodec.clear();
-
- if (needAudio) {
- bool found = false;
- for (const QString &audioCandidate : qAsConst(audioCandidates)) {
- QSet<QString> audioTypes = audioEncodeControl->supportedStreamTypes(audioCandidate);
- if (audioTypes.intersects(supportedTypes)) {
- found = true;
- audioCodec = audioCandidate;
- break;
- }
- }
- if (!found)
- continue;
- }
-
- if (needVideo) {
- bool found = false;
- for (const QString &videoCandidate : qAsConst(videoCandidates)) {
- QSet<QString> videoTypes = videoEncodeControl->supportedStreamTypes(videoCandidate);
- if (videoTypes.intersects(supportedTypes)) {
- found = true;
- videoCodec = videoCandidate;
- break;
- }
- }
- if (!found)
- continue;
- }
-
- container = containerCandidate;
- break;
- }
-
- if (container.isEmpty()) {
- emit error(QMediaRecorder::FormatError, tr("Not compatible codecs and container format."));
- } else {
- mediaContainerControl->setContainerFormat(container);
-
- if (needAudio) {
- QAudioEncoderSettings audioSettings = audioEncodeControl->audioSettings();
- audioSettings.setCodec(audioCodec);
- audioEncodeControl->setAudioSettings(audioSettings);
- }
-
- if (needVideo) {
- QVideoEncoderSettings videoSettings = videoEncodeControl->videoSettings();
- videoSettings.setCodec(videoCodec);
- videoEncodeControl->setVideoSettings(videoSettings);
- }
- }
-}
-
-
-bool QGstreamerRecorderControl::isMuted() const
-{
- return m_session->isMuted();
-}
-
-qreal QGstreamerRecorderControl::volume() const
-{
- return m_session->volume();
-}
-
-void QGstreamerRecorderControl::setMuted(bool muted)
-{
- m_session->setMuted(muted);
-}
-
-void QGstreamerRecorderControl::setVolume(qreal volume)
-{
- m_session->setVolume(volume);
-}
-
-QDir QGstreamerRecorderControl::defaultDir() const
-{
- QStringList dirCandidates;
-
- if (m_session->captureMode() & QGstreamerCaptureSession::Video)
- dirCandidates << QStandardPaths::writableLocation(QStandardPaths::MoviesLocation);
- else
- dirCandidates << QStandardPaths::writableLocation(QStandardPaths::MusicLocation);
-
- dirCandidates << QDir::home().filePath("Documents");
- dirCandidates << QDir::home().filePath("My Documents");
- dirCandidates << QDir::homePath();
- dirCandidates << QDir::currentPath();
- dirCandidates << QDir::tempPath();
-
- for (const QString &path : qAsConst(dirCandidates)) {
- QDir dir(path);
- if (dir.exists() && QFileInfo(path).isWritable())
- return dir;
- }
-
- return QDir();
-}
-
-QString QGstreamerRecorderControl::generateFileName(const QDir &dir, const QString &ext) const
-{
-
- int lastClip = 0;
- const auto list = dir.entryList(QStringList() << QString("clip_*.%1").arg(ext));
- for (const QString &fileName : list) {
- int imgNumber = QStringView{fileName}.mid(5, fileName.size()-6-ext.length()).toInt();
- lastClip = qMax(lastClip, imgNumber);
- }
-
- QString name = QString("clip_%1.%2").arg(lastClip+1,
- 4, //fieldWidth
- 10,
- QLatin1Char('0')).arg(ext);
-
- return dir.absoluteFilePath(name);
-}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.h b/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.h
deleted file mode 100644
index b80716f4c..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.h
+++ /dev/null
@@ -1,97 +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 QGSTREAMERRECORDERCONTROL_H
-#define QGSTREAMERRECORDERCONTROL_H
-
-#include <QtCore/QDir>
-
-#include <qmediarecordercontrol.h>
-#include "qgstreamercapturesession.h"
-
-QT_BEGIN_NAMESPACE
-
-class QGstreamerRecorderControl : public QMediaRecorderControl
-{
- Q_OBJECT
-
-public:
- QGstreamerRecorderControl(QGstreamerCaptureSession *session);
- virtual ~QGstreamerRecorderControl();
-
- QUrl outputLocation() const override;
- bool setOutputLocation(const QUrl &sink) override;
-
- QMediaRecorder::State state() const override;
- QMediaRecorder::Status status() const override;
-
- qint64 duration() const override;
-
- bool isMuted() const override;
- qreal volume() const override;
-
- void applySettings() override;
-
-public slots:
- void setState(QMediaRecorder::State state) override;
- void record();
- void pause();
- void stop();
- void setMuted(bool) override;
- void setVolume(qreal volume) override;
-
-private slots:
- void updateStatus();
- void handleSessionError(int code, const QString &description);
-
-private:
- QDir defaultDir() const;
- QString generateFileName(const QDir &dir, const QString &ext) const;
-
- QUrl m_outputLocation;
- QGstreamerCaptureSession *m_session;
- QMediaRecorder::State m_state;
- QMediaRecorder::Status m_status;
- bool m_hasPreviewState;
-};
-
-QT_END_NAMESPACE
-
-#endif // QGSTREAMERCAPTURECORNTROL_H
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.cpp b/src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.cpp
deleted file mode 100644
index ef0832f7e..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.cpp
+++ /dev/null
@@ -1,286 +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 "qgstreamerv4l2input.h"
-
-#include <QtCore/qdebug.h>
-#include <QtCore/qfile.h>
-
-#include <private/qcore_unix_p.h>
-#include <linux/videodev2.h>
-
-#include <algorithm>
-
-QT_BEGIN_NAMESPACE
-static inline uint qHash(const QSize& key) { return uint(key.width()*256+key.height()); }
-
-static bool operator<(const QSize &s1, const QSize s2)
-{
- return s1.width()*s1.height() < s2.width()*s2.height();
-}
-QT_END_NAMESPACE
-
-QGstreamerV4L2Input::QGstreamerV4L2Input(QObject *parent)
- :QObject(parent)
-{
-}
-
-QGstreamerV4L2Input::~QGstreamerV4L2Input()
-{
-}
-
-GstElement *QGstreamerV4L2Input::buildElement()
-{
- GstElement *camera = gst_element_factory_make("v4l2src", "camera_source");
- if (camera && !m_device.isEmpty() )
- g_object_set(G_OBJECT(camera), "device", m_device.constData(), NULL);
-
- return camera;
-}
-
-void QGstreamerV4L2Input::setDevice(const QByteArray &newDevice)
-{
- if (m_device != newDevice) {
- m_device = newDevice;
- updateSupportedResolutions(newDevice);
- }
-}
-
-void QGstreamerV4L2Input::setDevice(const QString &device)
-{
- setDevice(QFile::encodeName(device));
-}
-
-void QGstreamerV4L2Input::updateSupportedResolutions(const QByteArray &device)
-{
- m_frameRates.clear();
- m_resolutions.clear();
- m_ratesByResolution.clear();
-
- QSet<QSize> allResolutions;
- QSet<int> allFrameRates;
-
- QFile f(device);
-
- if (!f.open(QFile::ReadOnly))
- return;
-
- int fd = f.handle();
-
- //get the list of formats:
- QList<quint32> supportedFormats;
-
- {
- v4l2_fmtdesc fmt;
- memset(&fmt, 0, sizeof(v4l2_fmtdesc));
-
- fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- int sanity = 0;
-
- for (fmt.index = 0;; fmt.index++) {
- if (sanity++ > 8)
- break;
- if( ::ioctl(fd, VIDIOC_ENUM_FMT, &fmt) == -1) {
- if(errno == EINVAL)
- break;
- }
- supportedFormats.append(fmt.pixelformat);
- }
- }
-
- QList<QSize> commonSizes;
- commonSizes << QSize(128, 96)
- <<QSize(160,120)
- <<QSize(176, 144)
- <<QSize(320, 240)
- <<QSize(352, 288)
- <<QSize(640, 480)
- <<QSize(1024, 768)
- <<QSize(1280, 1024)
- <<QSize(1600, 1200)
- <<QSize(1920, 1200)
- <<QSize(2048, 1536)
- <<QSize(2560, 1600)
- <<QSize(2580, 1936);
-
- QList<int> commonRates;
- commonRates << 05*1000 << 75*1000 << 10*1000 << 15*1000 << 20*1000
- << 24*1000 << 25*1000 << 30*1000 << 50*1000 << 60*1000;
-
-
- //get the list of resolutions:
-
- for (quint32 format : qAsConst(supportedFormats)) {
- struct v4l2_frmsizeenum formatSize;
- memset(&formatSize, 0, sizeof(formatSize));
- formatSize.pixel_format = format;
-
- QList<QSize> sizeList;
-
- if (0) {
- char formatStr[5];
- memcpy(formatStr, &format, 4);
- formatStr[4] = 0;
- //qDebug() << "trying format" << formatStr;
- }
-
- for (int i=0;;i++) {
- formatSize.index = i;
- if (ioctl (fd, VIDIOC_ENUM_FRAMESIZES, &formatSize) < 0)
- break;
-
- if (formatSize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
- sizeList.append(QSize(formatSize.discrete.width, formatSize.discrete.height));
- } else {
-
- for (const QSize& candidate : qAsConst(commonSizes)) {
- if (candidate.width() <= (int)formatSize.stepwise.max_width &&
- candidate.height() >= (int)formatSize.stepwise.min_width &&
- candidate.width() % formatSize.stepwise.step_width == 0 &&
- candidate.height() <= (int)formatSize.stepwise.max_height &&
- candidate.height() >= (int)formatSize.stepwise.min_height &&
- candidate.height() % formatSize.stepwise.step_height == 0) {
- sizeList.append(candidate);
- }
- }
-
- if (!sizeList.contains(QSize(formatSize.stepwise.min_width, formatSize.stepwise.min_height)))
- sizeList.prepend(QSize(formatSize.stepwise.min_width, formatSize.stepwise.min_height));
-
- if (!sizeList.contains(QSize(formatSize.stepwise.max_width, formatSize.stepwise.max_height)))
- sizeList.append(QSize(formatSize.stepwise.max_width, formatSize.stepwise.max_height));
-
- break; //stepwise values are returned only for index 0
- }
-
- }
-
- //and frameRates for each resolution.
-
- for (const QSize &s : qAsConst(sizeList)) {
- allResolutions.insert(s);
-
- struct v4l2_frmivalenum formatInterval;
- memset(&formatInterval, 0, sizeof(formatInterval));
- formatInterval.pixel_format = format;
- formatInterval.width = s.width();
- formatInterval.height = s.height();
-
- QList<int> frameRates; //in 1/1000 of fps
-
- for (int i=0; ; i++) {
- formatInterval.index = i;
-
- if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &formatInterval) < 0)
- break;
-
- if (formatInterval.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
- //converts seconds to fps*1000
- if (formatInterval.discrete.numerator)
- frameRates.append(qRound(formatInterval.discrete.denominator*1000.0 / formatInterval.discrete.numerator));
- } else {
- if (formatInterval.stepwise.min.numerator == 0 ||
- formatInterval.stepwise.max.numerator == 0) {
- qWarning() << "received invalid frame interval";
- break;
- }
-
-
- int minRate = qRound(formatInterval.stepwise.min.denominator*1000.0 /
- formatInterval.stepwise.min.numerator);
-
- int maxRate = qRound(formatInterval.stepwise.max.denominator*1000.0 /
- formatInterval.stepwise.max.numerator);
-
-
- for (int candidate : qAsConst(commonRates)) {
- if (candidate >= minRate && candidate <= maxRate)
- frameRates.append(candidate);
- }
-
- if (!frameRates.contains(minRate))
- frameRates.prepend(minRate);
-
- if (!frameRates.contains(maxRate))
- frameRates.append(maxRate);
-
- break; //stepwise values are returned only for index 0
- }
- }
- allFrameRates.unite(frameRates.toSet());
- m_ratesByResolution[s].unite(frameRates.toSet());
- }
- }
-
- f.close();
-
- for (int rate : qAsConst(allFrameRates)) {
- m_frameRates.append(rate/1000.0);
- }
-
- std::sort(m_frameRates.begin(), m_frameRates.end());
-
- m_resolutions = allResolutions.toList();
- std::sort(m_resolutions.begin(), m_resolutions.end());
-
- //qDebug() << "frame rates:" << m_frameRates;
- //qDebug() << "resolutions:" << m_resolutions;
-}
-
-
-QList<qreal> QGstreamerV4L2Input::supportedFrameRates(const QSize &frameSize) const
-{
- if (frameSize.isEmpty())
- return m_frameRates;
- else {
- QList<qreal> res;
- const auto rates = m_ratesByResolution[frameSize];
- res.reserve(rates.size());
- for (int rate : rates) {
- res.append(rate/1000.0);
- }
- return res;
- }
-}
-
-QList<QSize> QGstreamerV4L2Input::supportedResolutions(qreal frameRate) const
-{
- Q_UNUSED(frameRate);
- return m_resolutions;
-}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.h b/src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.h
deleted file mode 100644
index a82c7cf4b..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 QGSTREAMERV4L2INPUT_H
-#define QGSTREAMERV4L2INPUT_H
-
-#include <QtCore/qhash.h>
-#include <QtCore/qbytearray.h>
-#include <QtCore/qlist.h>
-#include <QtCore/qsize.h>
-#include "qgstreamercapturesession.h"
-
-QT_BEGIN_NAMESPACE
-
-class QGstreamerV4L2Input : public QObject, public QGstreamerVideoInput
-{
- Q_OBJECT
-public:
- QGstreamerV4L2Input(QObject *parent = 0);
- virtual ~QGstreamerV4L2Input();
-
- GstElement *buildElement() override;
-
- QList<qreal> supportedFrameRates(const QSize &frameSize = QSize()) const override;
- QList<QSize> supportedResolutions(qreal frameRate = -1) const override;
-
- QByteArray device() const;
-
-public slots:
- void setDevice(const QByteArray &device);
- void setDevice(const QString &device);
-
-private:
- void updateSupportedResolutions(const QByteArray &device);
-
- QList<qreal> m_frameRates;
- QList<QSize> m_resolutions;
-
- QHash<QSize, QSet<int> > m_ratesByResolution;
-
- QByteArray m_device;
-};
-
-QT_END_NAMESPACE
-
-#endif // QGSTREAMERV4L2INPUT_H
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp b/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp
deleted file mode 100644
index a2ed1d288..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp
+++ /dev/null
@@ -1,296 +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 "qgstreamervideoencode.h"
-#include "qgstreamercapturesession.h"
-#include "qgstreamermediacontainercontrol.h"
-#include <private/qgstutils_p.h>
-#include <QtCore/qdebug.h>
-
-#include <math.h>
-
-QGstreamerVideoEncode::QGstreamerVideoEncode(QGstreamerCaptureSession *session)
- :QVideoEncoderSettingsControl(session), m_session(session)
- , m_codecs(QGstCodecsInfo::VideoEncoder)
-{
-}
-
-QGstreamerVideoEncode::~QGstreamerVideoEncode()
-{
-}
-
-QList<QSize> QGstreamerVideoEncode::supportedResolutions(const QVideoEncoderSettings &, bool *continuous) const
-{
- if (continuous)
- *continuous = m_session->videoInput() != 0;
-
- return m_session->videoInput() ? m_session->videoInput()->supportedResolutions() : QList<QSize>();
-}
-
-QList< qreal > QGstreamerVideoEncode::supportedFrameRates(const QVideoEncoderSettings &, bool *continuous) const
-{
- if (continuous)
- *continuous = false;
-
- return m_session->videoInput() ? m_session->videoInput()->supportedFrameRates() : QList<qreal>();
-}
-
-QStringList QGstreamerVideoEncode::supportedVideoCodecs() const
-{
- return m_codecs.supportedCodecs();
-}
-
-QString QGstreamerVideoEncode::videoCodecDescription(const QString &codecName) const
-{
- return m_codecs.codecDescription(codecName);
-}
-
-QStringList QGstreamerVideoEncode::supportedEncodingOptions(const QString &codec) const
-{
- return m_codecs.codecOptions(codec);
-}
-
-QVariant QGstreamerVideoEncode::encodingOption(const QString &codec, const QString &name) const
-{
- return m_options[codec].value(name);
-}
-
-void QGstreamerVideoEncode::setEncodingOption(
- const QString &codec, const QString &name, const QVariant &value)
-{
- m_options[codec][name] = value;
-}
-
-QVideoEncoderSettings QGstreamerVideoEncode::videoSettings() const
-{
- return m_videoSettings;
-}
-
-void QGstreamerVideoEncode::setVideoSettings(const QVideoEncoderSettings &settings)
-{
- m_videoSettings = settings;
-}
-
-GstElement *QGstreamerVideoEncode::createEncoder()
-{
- QString codec = m_videoSettings.codec();
- GstElement *encoderElement = gst_element_factory_make(m_codecs.codecElement(codec).constData(), "video-encoder");
- if (!encoderElement)
- return 0;
-
- GstBin *encoderBin = GST_BIN(gst_bin_new("video-encoder-bin"));
-
- GstElement *sinkCapsFilter = gst_element_factory_make("capsfilter", "capsfilter-video");
- GstElement *srcCapsFilter = gst_element_factory_make("capsfilter", "capsfilter-video");
- gst_bin_add_many(encoderBin, sinkCapsFilter, srcCapsFilter, NULL);
-
- GstElement *colorspace = gst_element_factory_make(QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME, NULL);
- gst_bin_add(encoderBin, colorspace);
- gst_bin_add(encoderBin, encoderElement);
-
- gst_element_link_many(sinkCapsFilter, colorspace, encoderElement, srcCapsFilter, NULL);
-
- // add ghostpads
- GstPad *pad = gst_element_get_static_pad(sinkCapsFilter, "sink");
- gst_element_add_pad(GST_ELEMENT(encoderBin), gst_ghost_pad_new("sink", pad));
- gst_object_unref(GST_OBJECT(pad));
-
- pad = gst_element_get_static_pad(srcCapsFilter, "src");
- gst_element_add_pad(GST_ELEMENT(encoderBin), gst_ghost_pad_new("src", pad));
- gst_object_unref(GST_OBJECT(pad));
-
- if (encoderElement) {
- if (m_videoSettings.encodingMode() == QMultimedia::ConstantQualityEncoding) {
- QMultimedia::EncodingQuality qualityValue = m_videoSettings.quality();
-
- if (codec == QLatin1String("video/x-h264")) {
- //constant quantizer mode
- g_object_set(G_OBJECT(encoderElement), "pass", 4, NULL);
- int qualityTable[] = {
- 50, //VeryLow
- 35, //Low
- 21, //Normal
- 15, //High
- 8 //VeryHigh
- };
- g_object_set(G_OBJECT(encoderElement), "quantizer", qualityTable[qualityValue], NULL);
- } else if (codec == QLatin1String("video/x-xvid")) {
- //constant quantizer mode
- g_object_set(G_OBJECT(encoderElement), "pass", 3, NULL);
- int qualityTable[] = {
- 32, //VeryLow
- 12, //Low
- 5, //Normal
- 3, //High
- 2 //VeryHigh
- };
- int quant = qualityTable[qualityValue];
- g_object_set(G_OBJECT(encoderElement), "quantizer", quant, NULL);
- } else if (codec.startsWith(QLatin1String("video/mpeg"))) {
- //constant quantizer mode
- g_object_set(G_OBJECT(encoderElement), "pass", 2, NULL);
- //quant from 1 to 30, default ~3
- double qualityTable[] = {
- 20, //VeryLow
- 8.0, //Low
- 3.0, //Normal
- 2.5, //High
- 2.0 //VeryHigh
- };
- double quant = qualityTable[qualityValue];
- g_object_set(G_OBJECT(encoderElement), "quantizer", quant, NULL);
- } else if (codec == QLatin1String("video/x-theora")) {
- int qualityTable[] = {
- 8, //VeryLow
- 16, //Low
- 32, //Normal
- 45, //High
- 60 //VeryHigh
- };
- //quality from 0 to 63
- int quality = qualityTable[qualityValue];
- g_object_set(G_OBJECT(encoderElement), "quality", quality, NULL);
- }
- } else {
- int bitrate = m_videoSettings.bitRate();
- if (bitrate > 0) {
- g_object_set(G_OBJECT(encoderElement), "bitrate", bitrate, NULL);
- }
- }
-
- QMap<QString,QVariant> options = m_options.value(codec);
- for (auto it = options.cbegin(), end = options.cend(); it != end; ++it) {
- const QString &option = it.key();
- const QVariant &value = it.value();
-
- switch (value.type()) {
- case QVariant::Int:
- g_object_set(G_OBJECT(encoderElement), option.toLatin1(), value.toInt(), NULL);
- break;
- case QVariant::Bool:
- g_object_set(G_OBJECT(encoderElement), option.toLatin1(), value.toBool(), NULL);
- break;
- case QVariant::Double:
- g_object_set(G_OBJECT(encoderElement), option.toLatin1(), value.toDouble(), NULL);
- break;
- case QVariant::String:
- g_object_set(G_OBJECT(encoderElement), option.toLatin1(), value.toString().toUtf8().constData(), NULL);
- break;
- default:
- qWarning() << "unsupported option type:" << option << value;
- break;
- }
-
- }
- }
-
- if (!m_videoSettings.resolution().isEmpty() || m_videoSettings.frameRate() > 0.001) {
- GstCaps *caps = QGstUtils::videoFilterCaps();
-
- if (!m_videoSettings.resolution().isEmpty()) {
- gst_caps_set_simple(
- caps,
- "width", G_TYPE_INT, m_videoSettings.resolution().width(),
- "height", G_TYPE_INT, m_videoSettings.resolution().height(),
- NULL);
- }
-
- if (m_videoSettings.frameRate() > 0.001) {
- QPair<int,int> rate = rateAsRational();
- gst_caps_set_simple(
- caps,
- "framerate", GST_TYPE_FRACTION, rate.first, rate.second,
- NULL);
- }
-
- //qDebug() << "set video caps filter:" << gst_caps_to_string(caps);
-
- g_object_set(G_OBJECT(sinkCapsFilter), "caps", caps, NULL);
-
- gst_caps_unref(caps);
- }
-
- // Some encoders support several codecs. Setting a caps filter downstream with the desired
- // codec (which is actually a string representation of the caps) will make sure we use the
- // correct codec.
- GstCaps *caps = gst_caps_from_string(codec.toUtf8().constData());
- g_object_set(G_OBJECT(srcCapsFilter), "caps", caps, NULL);
- gst_caps_unref(caps);
-
- return GST_ELEMENT(encoderBin);
-}
-
-QPair<int,int> QGstreamerVideoEncode::rateAsRational() const
-{
- qreal frameRate = m_videoSettings.frameRate();
-
- if (frameRate > 0.001) {
- //convert to rational number
- QList<int> denumCandidates;
- denumCandidates << 1 << 2 << 3 << 5 << 10 << 1001 << 1000;
-
- qreal error = 1.0;
- int num = 1;
- int denum = 1;
-
- for (int curDenum : qAsConst(denumCandidates)) {
- int curNum = qRound(frameRate*curDenum);
- qreal curError = qAbs(qreal(curNum)/curDenum - frameRate);
-
- if (curError < error) {
- error = curError;
- num = curNum;
- denum = curDenum;
- }
-
- if (curError < 1e-8)
- break;
- }
-
- return QPair<int,int>(num,denum);
- }
-
- return QPair<int,int>();
-}
-
-
-QSet<QString> QGstreamerVideoEncode::supportedStreamTypes(const QString &codecName) const
-{
- return m_codecs.supportedStreamTypes(codecName);
-}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.h b/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.h
deleted file mode 100644
index a35e2b456..000000000
--- a/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.h
+++ /dev/null
@@ -1,97 +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 QGSTREAMERVIDEOENCODE_H
-#define QGSTREAMERVIDEOENCODE_H
-
-#include <qvideoencodersettingscontrol.h>
-
-#include <QtCore/qstringlist.h>
-#include <QtCore/qmap.h>
-#include <QtCore/qset.h>
-
-#include <private/qgstcodecsinfo_p.h>
-
-#include <gst/gst.h>
-
-QT_BEGIN_NAMESPACE
-
-class QGstreamerCaptureSession;
-
-class QGstreamerVideoEncode : public QVideoEncoderSettingsControl
-{
- Q_OBJECT
-public:
- QGstreamerVideoEncode(QGstreamerCaptureSession *session);
- virtual ~QGstreamerVideoEncode();
-
- QList<QSize> supportedResolutions(const QVideoEncoderSettings &settings = QVideoEncoderSettings(),
- bool *continuous = 0) const override;
-
- QList< qreal > supportedFrameRates(const QVideoEncoderSettings &settings = QVideoEncoderSettings(),
- bool *continuous = 0) const override;
-
- QPair<int,int> rateAsRational() const;
-
- QStringList supportedVideoCodecs() const override;
- QString videoCodecDescription(const QString &codecName) const override;
-
- QVideoEncoderSettings videoSettings() const override;
- void setVideoSettings(const QVideoEncoderSettings &settings) override;
-
- QStringList supportedEncodingOptions(const QString &codec) const;
- QVariant encodingOption(const QString &codec, const QString &name) const;
- void setEncodingOption(const QString &codec, const QString &name, const QVariant &value);
-
- GstElement *createEncoder();
-
- QSet<QString> supportedStreamTypes(const QString &codecName) const;
-
-private:
- QGstreamerCaptureSession *m_session;
-
- QGstCodecsInfo m_codecs;
-
- QVideoEncoderSettings m_videoSettings;
- QMap<QString, QMap<QString, QVariant> > m_options;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/gstreamer/mediaplayer/mediaplayer.json b/src/plugins/gstreamer/mediaplayer/mediaplayer.json
deleted file mode 100644
index bd1a7e64d..000000000
--- a/src/plugins/gstreamer/mediaplayer/mediaplayer.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Keys": ["gstreamermediaplayer"],
- "Services": ["org.qt-project.qt.mediaplayer"]
-}
diff --git a/src/plugins/gstreamer/mediaplayer/mediaplayer.pro b/src/plugins/gstreamer/mediaplayer/mediaplayer.pro
deleted file mode 100644
index 8150d8f5b..000000000
--- a/src/plugins/gstreamer/mediaplayer/mediaplayer.pro
+++ /dev/null
@@ -1,26 +0,0 @@
-TARGET = gstmediaplayer
-
-include(../common.pri)
-
-INCLUDEPATH += $$PWD
-
-HEADERS += \
- $$PWD/qgstreamerplayerservice.h \
- $$PWD/qgstreamerstreamscontrol.h \
- $$PWD/qgstreamermetadataprovider.h \
- $$PWD/qgstreameravailabilitycontrol.h \
- $$PWD/qgstreamerplayerserviceplugin.h
-
-SOURCES += \
- $$PWD/qgstreamerplayerservice.cpp \
- $$PWD/qgstreamerstreamscontrol.cpp \
- $$PWD/qgstreamermetadataprovider.cpp \
- $$PWD/qgstreameravailabilitycontrol.cpp \
- $$PWD/qgstreamerplayerserviceplugin.cpp
-
-OTHER_FILES += \
- mediaplayer.json
-
-PLUGIN_TYPE = mediaservice
-PLUGIN_CLASS_NAME = QGstreamerPlayerServicePlugin
-load(qt_plugin)
diff --git a/src/plugins/gstreamer/mediaplayer/qgstreameravailabilitycontrol.cpp b/src/plugins/gstreamer/mediaplayer/qgstreameravailabilitycontrol.cpp
deleted file mode 100644
index d2bfbb03a..000000000
--- a/src/plugins/gstreamer/mediaplayer/qgstreameravailabilitycontrol.cpp
+++ /dev/null
@@ -1,64 +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 "qgstreameravailabilitycontrol.h"
-#include <private/qmediaresourceset_p.h>
-
-QT_BEGIN_NAMESPACE
-
-QGStreamerAvailabilityControl::QGStreamerAvailabilityControl(
- QMediaPlayerResourceSetInterface *resources, QObject *parent)
- : QMediaAvailabilityControl(parent)
- , m_resources(resources)
-{
- Q_ASSERT(m_resources);
- connect(m_resources, SIGNAL(availabilityChanged(bool)), this, SLOT(handleAvailabilityChanged()));
-}
-
-void QGStreamerAvailabilityControl::handleAvailabilityChanged()
-{
- emit availabilityChanged(this->availability());
-}
-
-QMultimedia::AvailabilityStatus QGStreamerAvailabilityControl::availability() const
-{
- return m_resources->isAvailable() ? QMultimedia::Available : QMultimedia::Busy;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/mediaplayer/qgstreameravailabilitycontrol.h b/src/plugins/gstreamer/mediaplayer/qgstreameravailabilitycontrol.h
deleted file mode 100644
index e100fccc8..000000000
--- a/src/plugins/gstreamer/mediaplayer/qgstreameravailabilitycontrol.h
+++ /dev/null
@@ -1,65 +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 QGSTREAMERAVAILABILITYCONTROL_H
-#define QGSTREAMERAVAILABILITYCONTROL_H
-
-#include <QObject>
-#include <qmediaavailabilitycontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QMediaPlayerResourceSetInterface;
-class QGStreamerAvailabilityControl : public QMediaAvailabilityControl
-{
- Q_OBJECT
-public:
- QGStreamerAvailabilityControl(QMediaPlayerResourceSetInterface *resources, QObject *parent = 0);
- QMultimedia::AvailabilityStatus availability() const override;
-
-private Q_SLOTS:
- void handleAvailabilityChanged();
-
-private:
- QMediaPlayerResourceSetInterface *m_resources = nullptr;
-};
-
-QT_END_NAMESPACE
-
-#endif // QGSTREAMERAVAILABILITYCONTROL_H
diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp
deleted file mode 100644
index bd503d3a1..000000000
--- a/src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp
+++ /dev/null
@@ -1,190 +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 "qgstreamermetadataprovider.h"
-#include <private/qgstreamerplayersession_p.h>
-#include <QDebug>
-#include <QtMultimedia/qmediametadata.h>
-
-#include <gst/gstversion.h>
-#include <private/qgstutils_p.h>
-
-QT_BEGIN_NAMESPACE
-
-typedef QMap<QByteArray, QString> QGstreamerMetaDataKeyLookup;
-Q_GLOBAL_STATIC(QGstreamerMetaDataKeyLookup, metadataKeys)
-
-static const QGstreamerMetaDataKeyLookup *qt_gstreamerMetaDataKeys()
-{
- if (metadataKeys->isEmpty()) {
- metadataKeys->insert(GST_TAG_TITLE, QMediaMetaData::Title);
- //metadataKeys->insert(0, QMediaMetaData::SubTitle);
- //metadataKeys->insert(0, QMediaMetaData::Author);
- metadataKeys->insert(GST_TAG_COMMENT, QMediaMetaData::Comment);
- metadataKeys->insert(GST_TAG_DESCRIPTION, QMediaMetaData::Description);
- //metadataKeys->insert(0, QMediaMetaData::Category);
- metadataKeys->insert(GST_TAG_GENRE, QMediaMetaData::Genre);
- metadataKeys->insert("year", QMediaMetaData::Year);
- //metadataKeys->insert(0, QMediaMetaData::UserRating);
-
- metadataKeys->insert(GST_TAG_LANGUAGE_CODE, QMediaMetaData::Language);
-
- metadataKeys->insert(GST_TAG_ORGANIZATION, QMediaMetaData::Publisher);
- metadataKeys->insert(GST_TAG_COPYRIGHT, QMediaMetaData::Copyright);
- //metadataKeys->insert(0, QMediaMetaData::ParentalRating);
- //metadataKeys->insert(0, QMediaMetaData::RatingOrganisation);
-
- // Media
- //metadataKeys->insert(0, QMediaMetaData::Size);
- //metadataKeys->insert(0,QMediaMetaData::MediaType );
- metadataKeys->insert(GST_TAG_DURATION, QMediaMetaData::Duration);
-
- // Audio
- metadataKeys->insert(GST_TAG_BITRATE, QMediaMetaData::AudioBitRate);
- metadataKeys->insert(GST_TAG_AUDIO_CODEC, QMediaMetaData::AudioCodec);
- //metadataKeys->insert(0, QMediaMetaData::ChannelCount);
- //metadataKeys->insert(0, QMediaMetaData::SampleRate);
-
- // Music
- metadataKeys->insert(GST_TAG_ALBUM, QMediaMetaData::AlbumTitle);
-#if GST_CHECK_VERSION(0, 10, 25)
- metadataKeys->insert(GST_TAG_ALBUM_ARTIST, QMediaMetaData::AlbumArtist);
-#endif
- metadataKeys->insert(GST_TAG_ARTIST, QMediaMetaData::ContributingArtist);
- //metadataKeys->insert(0, QMediaMetaData::Conductor);
- //metadataKeys->insert(0, QMediaMetaData::Lyrics);
- //metadataKeys->insert(0, QMediaMetaData::Mood);
- metadataKeys->insert(GST_TAG_TRACK_NUMBER, QMediaMetaData::TrackNumber);
-
- //metadataKeys->insert(0, QMediaMetaData::CoverArtUrlSmall);
- //metadataKeys->insert(0, QMediaMetaData::CoverArtUrlLarge);
- metadataKeys->insert(GST_TAG_PREVIEW_IMAGE, QMediaMetaData::ThumbnailImage);
- metadataKeys->insert(GST_TAG_IMAGE, QMediaMetaData::CoverArtImage);
-
- // Image/Video
- metadataKeys->insert("resolution", QMediaMetaData::Resolution);
- metadataKeys->insert("pixel-aspect-ratio", QMediaMetaData::PixelAspectRatio);
-#if GST_CHECK_VERSION(0,10,30)
- metadataKeys->insert(GST_TAG_IMAGE_ORIENTATION, QMediaMetaData::Orientation);
-#endif
-
- // Video
- //metadataKeys->insert(0, QMediaMetaData::VideoFrameRate);
- //metadataKeys->insert(0, QMediaMetaData::VideoBitRate);
- metadataKeys->insert(GST_TAG_VIDEO_CODEC, QMediaMetaData::VideoCodec);
-
- //metadataKeys->insert(0, QMediaMetaData::PosterUrl);
-
- // Movie
- //metadataKeys->insert(0, QMediaMetaData::ChapterNumber);
- //metadataKeys->insert(0, QMediaMetaData::Director);
- metadataKeys->insert(GST_TAG_PERFORMER, QMediaMetaData::LeadPerformer);
- //metadataKeys->insert(0, QMediaMetaData::Writer);
-
- // Photos
- //metadataKeys->insert(0, QMediaMetaData::CameraManufacturer);
- //metadataKeys->insert(0, QMediaMetaData::CameraModel);
- //metadataKeys->insert(0, QMediaMetaData::Event);
- //metadataKeys->insert(0, QMediaMetaData::Subject);
- }
-
- return metadataKeys;
-}
-
-QGstreamerMetaDataProvider::QGstreamerMetaDataProvider(QGstreamerPlayerSession *session, QObject *parent)
- :QMetaDataReaderControl(parent), m_session(session)
-{
- connect(m_session, SIGNAL(tagsChanged()), SLOT(updateTags()));
-}
-
-QGstreamerMetaDataProvider::~QGstreamerMetaDataProvider()
-{
-}
-
-bool QGstreamerMetaDataProvider::isMetaDataAvailable() const
-{
- return !m_session->tags().isEmpty();
-}
-
-bool QGstreamerMetaDataProvider::isWritable() const
-{
- return false;
-}
-
-QVariant QGstreamerMetaDataProvider::metaData(const QString &key) const
-{
-#if GST_CHECK_VERSION(0,10,30)
- if (key == QMediaMetaData::Orientation)
- return QGstUtils::fromGStreamerOrientation(m_tags.value(key));
-#endif
- return m_tags.value(key);
-}
-
-QStringList QGstreamerMetaDataProvider::availableMetaData() const
-{
- return m_tags.keys();
-}
-
-void QGstreamerMetaDataProvider::updateTags()
-{
- QVariantMap oldTags = m_tags;
- m_tags.clear();
- bool changed = false;
-
- const auto tags = m_session->tags();
- for (auto i = tags.cbegin(), end = tags.cend(); i != end; ++i) {
- //use gstreamer native keys for elements not in our key map
- QString key = qt_gstreamerMetaDataKeys()->value(i.key(), i.key());
- m_tags.insert(key, i.value());
- if (i.value() != oldTags.value(key)) {
- changed = true;
- emit metaDataChanged(key, i.value());
- }
- }
-
- if (oldTags.isEmpty() != m_tags.isEmpty()) {
- emit metaDataAvailableChanged(isMetaDataAvailable());
- changed = true;
- }
-
- if (changed)
- emit metaDataChanged();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.h b/src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.h
deleted file mode 100644
index 691453a42..000000000
--- a/src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.h
+++ /dev/null
@@ -1,72 +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 QGSTREAMERMETADATAPROVIDER_H
-#define QGSTREAMERMETADATAPROVIDER_H
-
-#include <qmetadatareadercontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QGstreamerPlayerSession;
-
-class QGstreamerMetaDataProvider : public QMetaDataReaderControl
-{
- Q_OBJECT
-public:
- QGstreamerMetaDataProvider( QGstreamerPlayerSession *session, QObject *parent );
- virtual ~QGstreamerMetaDataProvider();
-
- bool isMetaDataAvailable() const override;
- bool isWritable() const;
-
- QVariant metaData(const QString &key) const override;
- QStringList availableMetaData() const override;
-
-private slots:
- void updateTags();
-
-private:
- QGstreamerPlayerSession *m_session = nullptr;
- QVariantMap m_tags;
-};
-
-QT_END_NAMESPACE
-
-#endif // QGSTREAMERMETADATAPROVIDER_H
diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp
deleted file mode 100644
index 4bf4a0a57..000000000
--- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp
+++ /dev/null
@@ -1,193 +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 <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include <QtCore/qvariant.h>
-#include <QtCore/qdebug.h>
-
-#if defined(HAVE_WIDGETS)
-#include <QtWidgets/qwidget.h>
-#endif
-
-#include "qgstreamerplayerservice.h"
-#include "qgstreamermetadataprovider.h"
-#include "qgstreameravailabilitycontrol.h"
-
-#if defined(HAVE_WIDGETS)
-#include <private/qgstreamervideowidget_p.h>
-#endif
-#include <private/qgstreamervideowindow_p.h>
-#include <private/qgstreamervideorenderer_p.h>
-
-#include "qgstreamerstreamscontrol.h"
-#include <private/qgstreameraudioprobecontrol_p.h>
-#include <private/qgstreamervideoprobecontrol_p.h>
-#include <private/qgstreamerplayersession_p.h>
-#include <private/qgstreamerplayercontrol_p.h>
-
-#include <private/qmediaplaylistnavigator_p.h>
-#include <qmediaplaylist.h>
-#include <private/qmediaresourceset_p.h>
-
-QT_BEGIN_NAMESPACE
-
-QGstreamerPlayerService::QGstreamerPlayerService(QObject *parent)
- : QMediaService(parent)
-{
- m_session = new QGstreamerPlayerSession(this);
- m_control = new QGstreamerPlayerControl(m_session, this);
- m_metaData = new QGstreamerMetaDataProvider(m_session, this);
- m_streamsControl = new QGstreamerStreamsControl(m_session,this);
- m_availabilityControl = new QGStreamerAvailabilityControl(m_control->resources(), this);
- m_videoRenderer = new QGstreamerVideoRenderer(this);
- m_videoWindow = new QGstreamerVideoWindow(this);
- // If the GStreamer video sink is not available, don't provide the video window control since
- // it won't work anyway.
- if (!m_videoWindow->videoSink()) {
- delete m_videoWindow;
- m_videoWindow = 0;
- }
-
-#if defined(HAVE_WIDGETS)
- m_videoWidget = new QGstreamerVideoWidgetControl(this);
-
- // If the GStreamer video sink is not available, don't provide the video widget control since
- // it won't work anyway.
- // QVideoWidget will fall back to QVideoRendererControl in that case.
- if (!m_videoWidget->videoSink()) {
- delete m_videoWidget;
- m_videoWidget = 0;
- }
-#endif
-}
-
-QGstreamerPlayerService::~QGstreamerPlayerService()
-{
-}
-
-QMediaControl *QGstreamerPlayerService::requestControl(const char *name)
-{
- if (qstrcmp(name,QMediaPlayerControl_iid) == 0)
- return m_control;
-
- if (qstrcmp(name,QMetaDataReaderControl_iid) == 0)
- return m_metaData;
-
- if (qstrcmp(name,QMediaStreamsControl_iid) == 0)
- return m_streamsControl;
-
- if (qstrcmp(name, QMediaAvailabilityControl_iid) == 0)
- return m_availabilityControl;
-
- if (qstrcmp(name, QMediaVideoProbeControl_iid) == 0) {
- if (!m_videoProbeControl) {
- increaseVideoRef();
- m_videoProbeControl = new QGstreamerVideoProbeControl(this);
- m_session->addProbe(m_videoProbeControl);
- }
- m_videoProbeControl->ref.ref();
- return m_videoProbeControl;
- }
-
- if (qstrcmp(name, QMediaAudioProbeControl_iid) == 0) {
- if (!m_audioProbeControl) {
- m_audioProbeControl = new QGstreamerAudioProbeControl(this);
- m_session->addProbe(m_audioProbeControl);
- }
- m_audioProbeControl->ref.ref();
- return m_audioProbeControl;
- }
-
- if (!m_videoOutput) {
- if (qstrcmp(name, QVideoRendererControl_iid) == 0)
- m_videoOutput = m_videoRenderer;
- else if (qstrcmp(name, QVideoWindowControl_iid) == 0)
- m_videoOutput = m_videoWindow;
-#if defined(HAVE_WIDGETS)
- else if (qstrcmp(name, QVideoWidgetControl_iid) == 0)
- m_videoOutput = m_videoWidget;
-#endif
-
- if (m_videoOutput) {
- increaseVideoRef();
- m_control->setVideoOutput(m_videoOutput);
- return m_videoOutput;
- }
- }
-
- return 0;
-}
-
-void QGstreamerPlayerService::releaseControl(QMediaControl *control)
-{
- if (!control) {
- return;
- } else if (control == m_videoOutput) {
- m_videoOutput = 0;
- m_control->setVideoOutput(0);
- decreaseVideoRef();
- } else if (control == m_videoProbeControl && !m_videoProbeControl->ref.deref()) {
- m_session->removeProbe(m_videoProbeControl);
- delete m_videoProbeControl;
- m_videoProbeControl = 0;
- decreaseVideoRef();
- } else if (control == m_audioProbeControl && !m_audioProbeControl->ref.deref()) {
- m_session->removeProbe(m_audioProbeControl);
- delete m_audioProbeControl;
- m_audioProbeControl = 0;
- }
-}
-
-void QGstreamerPlayerService::increaseVideoRef()
-{
- m_videoReferenceCount++;
- if (m_videoReferenceCount == 1) {
- m_control->resources()->setVideoEnabled(true);
- }
-}
-
-void QGstreamerPlayerService::decreaseVideoRef()
-{
- m_videoReferenceCount--;
- if (m_videoReferenceCount == 0) {
- m_control->resources()->setVideoEnabled(false);
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h
deleted file mode 100644
index f88f0ae38..000000000
--- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h
+++ /dev/null
@@ -1,99 +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 QGSTREAMERPLAYERSERVICE_H
-#define QGSTREAMERPLAYERSERVICE_H
-
-#include <QtCore/qobject.h>
-#include <QtCore/qiodevice.h>
-
-#include <qmediaservice.h>
-
-QT_BEGIN_NAMESPACE
-class QMediaPlayerControl;
-class QMediaPlaylist;
-class QMediaPlaylistNavigator;
-
-class QGstreamerMetaData;
-class QGstreamerPlayerControl;
-class QGstreamerPlayerSession;
-class QGstreamerMetaDataProvider;
-class QGstreamerStreamsControl;
-class QGstreamerVideoRenderer;
-class QGstreamerVideoWindow;
-class QGstreamerVideoWidgetControl;
-class QGStreamerAvailabilityControl;
-class QGstreamerAudioProbeControl;
-class QGstreamerVideoProbeControl;
-
-class QGstreamerPlayerService : public QMediaService
-{
- Q_OBJECT
-public:
- QGstreamerPlayerService(QObject *parent = 0);
- ~QGstreamerPlayerService();
-
- QMediaControl *requestControl(const char *name) override;
- void releaseControl(QMediaControl *control) override;
-
-private:
- QGstreamerPlayerControl *m_control = nullptr;
- QGstreamerPlayerSession *m_session = nullptr;
- QGstreamerMetaDataProvider *m_metaData = nullptr;
- QGstreamerStreamsControl *m_streamsControl = nullptr;
- QGStreamerAvailabilityControl *m_availabilityControl = nullptr;
-
- QGstreamerAudioProbeControl *m_audioProbeControl = nullptr;
- QGstreamerVideoProbeControl *m_videoProbeControl = nullptr;
-
- QMediaControl *m_videoOutput = nullptr;
- QMediaControl *m_videoRenderer = nullptr;
- QGstreamerVideoWindow *m_videoWindow = nullptr;
-#if defined(HAVE_WIDGETS)
- QGstreamerVideoWidgetControl *m_videoWidget = nullptr;
-#endif
-
- void increaseVideoRef();
- void decreaseVideoRef();
- int m_videoReferenceCount = 0;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.cpp
deleted file mode 100644
index db266a10e..000000000
--- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.cpp
+++ /dev/null
@@ -1,113 +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 <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/QDir>
-#include <QtCore/QDebug>
-
-#include "qgstreamerplayerserviceplugin.h"
-
-//#define QT_SUPPORTEDMIMETYPES_DEBUG
-
-#include "qgstreamerplayerservice.h"
-#include <private/qgstutils_p.h>
-
-QMediaService* QGstreamerPlayerServicePlugin::create(const QString &key)
-{
- QGstUtils::initializeGst();
-
- if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER))
- return new QGstreamerPlayerService;
-
- qWarning() << "Gstreamer service plugin: unsupported key:" << key;
- return 0;
-}
-
-void QGstreamerPlayerServicePlugin::release(QMediaService *service)
-{
- delete service;
-}
-
-QMediaServiceProviderHint::Features QGstreamerPlayerServicePlugin::supportedFeatures(
- const QByteArray &service) const
-{
- if (service == Q_MEDIASERVICE_MEDIAPLAYER)
- return
-#if QT_CONFIG(gstreamer_app)
- QMediaServiceProviderHint::StreamPlayback |
-#endif
- QMediaServiceProviderHint::VideoSurface;
- else
- return QMediaServiceProviderHint::Features();
-}
-
-QMultimedia::SupportEstimate QGstreamerPlayerServicePlugin::hasSupport(const QString &mimeType,
- const QStringList &codecs) const
-{
- if (m_supportedMimeTypeSet.isEmpty())
- updateSupportedMimeTypes();
-
- return QGstUtils::hasSupport(mimeType, codecs, m_supportedMimeTypeSet);
-}
-
-static bool isDecoderOrDemuxer(GstElementFactory *factory)
-{
-#if GST_CHECK_VERSION(0, 10 ,31)
- return gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_DEMUXER)
- || gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_DECODER);
-#else
- return (factory
- && (qstrcmp(factory->details.klass, "Codec/Decoder/Audio") == 0
- || qstrcmp(factory->details.klass, "Codec/Decoder/Video") == 0
- || qstrcmp(factory->details.klass, "Codec/Demux") == 0 ));
-#endif
-}
-
-void QGstreamerPlayerServicePlugin::updateSupportedMimeTypes() const
-{
- m_supportedMimeTypeSet = QGstUtils::supportedMimeTypes(isDecoderOrDemuxer);
-}
-
-QStringList QGstreamerPlayerServicePlugin::supportedMimeTypes() const
-{
- return QStringList();
-}
-
diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.h b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.h
deleted file mode 100644
index 71eece23e..000000000
--- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 QGSTREAMERPLAYERSERVICEPLUGIN_H
-#define QGSTREAMERPLAYERSERVICEPLUGIN_H
-
-#include <qmediaserviceproviderplugin.h>
-#include <QtCore/qset.h>
-#include <QtCore/QObject>
-
-QT_BEGIN_NAMESPACE
-
-
-class QGstreamerPlayerServicePlugin
- : public QMediaServiceProviderPlugin
- , public QMediaServiceFeaturesInterface
- , public QMediaServiceSupportedFormatsInterface
-{
- Q_OBJECT
- Q_INTERFACES(QMediaServiceFeaturesInterface)
- Q_INTERFACES(QMediaServiceSupportedFormatsInterface)
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "mediaplayer.json")
-public:
- QMediaService* create(const QString &key) override;
- void release(QMediaService *service) override;
-
- QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const override;
-
- QMultimedia::SupportEstimate hasSupport(const QString &mimeType, const QStringList &codecs) const override;
- QStringList supportedMimeTypes() const override;
-
-private:
- void updateSupportedMimeTypes() const;
-
- mutable QSet<QString> m_supportedMimeTypeSet; //for fast access
-};
-
-QT_END_NAMESPACE
-
-#endif // QGSTREAMERPLAYERSERVICEPLUGIN_H
-
diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerstreamscontrol.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerstreamscontrol.cpp
deleted file mode 100644
index 4f5c3f0b2..000000000
--- a/src/plugins/gstreamer/mediaplayer/qgstreamerstreamscontrol.cpp
+++ /dev/null
@@ -1,87 +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 "qgstreamerstreamscontrol.h"
-#include <private/qgstreamerplayersession_p.h>
-
-QGstreamerStreamsControl::QGstreamerStreamsControl(QGstreamerPlayerSession *session, QObject *parent)
- :QMediaStreamsControl(parent), m_session(session)
-{
- connect(m_session, SIGNAL(streamsChanged()), SIGNAL(streamsChanged()));
-}
-
-QGstreamerStreamsControl::~QGstreamerStreamsControl()
-{
-}
-
-int QGstreamerStreamsControl::streamCount()
-{
- return m_session->streamCount();
-}
-
-QMediaStreamsControl::StreamType QGstreamerStreamsControl::streamType(int streamNumber)
-{
- return m_session->streamType(streamNumber);
-}
-
-QVariant QGstreamerStreamsControl::metaData(int streamNumber, const QString &key)
-{
- return m_session->streamProperties(streamNumber).value(key);
-}
-
-bool QGstreamerStreamsControl::isActive(int streamNumber)
-{
- return streamNumber != -1 && streamNumber == m_session->activeStream(streamType(streamNumber));
-}
-
-void QGstreamerStreamsControl::setActive(int streamNumber, bool state)
-{
- QMediaStreamsControl::StreamType type = m_session->streamType(streamNumber);
- if (type == QMediaStreamsControl::UnknownStream)
- return;
-
- if (state)
- m_session->setActiveStream(type, streamNumber);
- else {
- //only one active stream of certain type is supported
- if (m_session->activeStream(type) == streamNumber)
- m_session->setActiveStream(type, -1);
- }
-}
-
diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerstreamscontrol.h b/src/plugins/gstreamer/mediaplayer/qgstreamerstreamscontrol.h
deleted file mode 100644
index 41932095c..000000000
--- a/src/plugins/gstreamer/mediaplayer/qgstreamerstreamscontrol.h
+++ /dev/null
@@ -1,71 +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 QGSTREAMERSTREAMSCONTROL_H
-#define QGSTREAMERSTREAMSCONTROL_H
-
-#include <qmediastreamscontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class QGstreamerPlayerSession;
-
-class QGstreamerStreamsControl : public QMediaStreamsControl
-{
- Q_OBJECT
-public:
- QGstreamerStreamsControl(QGstreamerPlayerSession *session, QObject *parent);
- virtual ~QGstreamerStreamsControl();
-
- int streamCount() override;
- StreamType streamType(int streamNumber) override;
-
- QVariant metaData(int streamNumber, const QString &key) override;
-
- bool isActive(int streamNumber) override;
- void setActive(int streamNumber, bool state) override;
-
-private:
- QGstreamerPlayerSession *m_session = nullptr;
-};
-
-QT_END_NAMESPACE
-
-#endif // QGSTREAMERSTREAMSCONTROL_H
-
diff --git a/src/plugins/m3u/m3u.json b/src/plugins/m3u/m3u.json
deleted file mode 100644
index 2d69fab8e..000000000
--- a/src/plugins/m3u/m3u.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "Keys": ["m3u"]
-}
diff --git a/src/plugins/m3u/m3u.pro b/src/plugins/m3u/m3u.pro
deleted file mode 100644
index d46911e0d..000000000
--- a/src/plugins/m3u/m3u.pro
+++ /dev/null
@@ -1,12 +0,0 @@
-TARGET = qtmultimedia_m3u
-QT += multimedia-private
-
-HEADERS += qm3uhandler.h
-SOURCES += qm3uhandler.cpp
-
-OTHER_FILES += \
- m3u.json
-
-PLUGIN_TYPE = playlistformats
-PLUGIN_CLASS_NAME = QM3uPlaylistPlugin
-load(qt_plugin)
diff --git a/src/plugins/m3u/qm3uhandler.cpp b/src/plugins/m3u/qm3uhandler.cpp
deleted file mode 100644
index b88e3e292..000000000
--- a/src/plugins/m3u/qm3uhandler.cpp
+++ /dev/null
@@ -1,229 +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 "qm3uhandler.h"
-#include <QtCore/qiodevice.h>
-#include <QtCore/qfileinfo.h>
-#include <QtCore/qtextstream.h>
-#include <QFile>
-#include <QUrl>
-
-
-class QM3uPlaylistReader : public QMediaPlaylistReader
-{
-public:
- QM3uPlaylistReader(QIODevice *device)
- :m_ownDevice(false), m_device(device), m_textStream(new QTextStream(m_device))
- {
- readItem();
- }
-
- QM3uPlaylistReader(const QUrl& location)
- :m_location(location), m_ownDevice(true)
- {
- QFile *f = new QFile(location.toLocalFile());
- if (f->open(QIODevice::ReadOnly | QIODevice::Text)) {
- m_device = f;
- m_textStream = new QTextStream(m_device);
- readItem();
- } else {
- delete f;
- m_device = 0;
- m_textStream = 0;
- }
- }
-
- virtual ~QM3uPlaylistReader()
- {
- if (m_ownDevice) {
- delete m_device;
- }
- delete m_textStream;
- }
-
- bool atEnd() const override
- {
- //we can't just use m_textStream->atEnd(),
- //for files with empty lines/comments at end
- return nextResource.isNull();
- }
-
- QMediaContent readItem() override
- {
- QMediaContent item;
- if (!nextResource.isNull())
- item = QMediaContent(nextResource);
-
- nextResource = QMediaContent();
-
- while (m_textStream && !m_textStream->atEnd()) {
- QString line = m_textStream->readLine().trimmed();
- if (line.isEmpty() || line[0] == '#' || line.size() > 4096)
- continue;
-
- QUrl fileUrl = QUrl::fromLocalFile(line);
- QUrl url(line);
-
- //m3u may contain url encoded entries or absolute/relative file names
- //prefer existing file if any
- QList<QUrl> candidates;
- if (!m_location.isEmpty()) {
- candidates << m_location.resolved(fileUrl);
- candidates << m_location.resolved(url);
- }
- candidates << fileUrl;
- candidates << url;
-
- for (const QUrl &candidate : qAsConst(candidates)) {
- if (QFile::exists(candidate.toLocalFile())) {
- nextResource = candidate;
- break;
- }
- }
-
- if (nextResource.isNull()) {
- //assume the relative urls are file names, not encoded urls if m3u is local file
- if (!m_location.isEmpty() && url.isRelative()) {
- if (m_location.scheme() == QLatin1String("file"))
- nextResource = m_location.resolved(fileUrl);
- else
- nextResource = m_location.resolved(url);
- } else {
- nextResource = QMediaContent(QUrl::fromUserInput(line));
- }
- }
-
- break;
- }
-
- return item;
- }
-
- void close() override
- {
- }
-
-private:
- QUrl m_location;
- bool m_ownDevice;
- QIODevice *m_device;
- QTextStream *m_textStream;
- QMediaContent nextResource;
-};
-
-class QM3uPlaylistWriter : public QMediaPlaylistWriter
-{
-public:
- QM3uPlaylistWriter(QIODevice *device)
- :m_device(device), m_textStream(new QTextStream(m_device))
- {
- }
-
- virtual ~QM3uPlaylistWriter()
- {
- delete m_textStream;
- }
-
- bool writeItem(const QMediaContent& item) override
- {
- *m_textStream << item.request().url().toString() << Qt::endl;
- return true;
- }
-
- void close() override
- {
- }
-
-private:
- QIODevice *m_device;
- QTextStream *m_textStream;
-};
-
-
-QM3uPlaylistPlugin::QM3uPlaylistPlugin(QObject *parent)
- :QMediaPlaylistIOPlugin(parent)
-{
-}
-
-QM3uPlaylistPlugin::~QM3uPlaylistPlugin()
-{
-}
-
-bool QM3uPlaylistPlugin::canRead(QIODevice *device, const QByteArray &format) const
-{
- return device->isReadable() && (format == "m3u" || format == "m3u8" || format.isEmpty());
-}
-
-bool QM3uPlaylistPlugin::canRead(const QUrl& location, const QByteArray &format) const
-{
- if (!QFileInfo(location.toLocalFile()).isReadable())
- return false;
-
- if (format == "m3u" || format == "m3u8")
- return true;
-
- if (!format.isEmpty())
- return false;
- QString localFile = location.toLocalFile().toLower();
- return localFile.endsWith(QLatin1String("m3u")) || localFile.endsWith(QLatin1String("m3u8"));
-}
-
-bool QM3uPlaylistPlugin::canWrite(QIODevice *device, const QByteArray &format) const
-{
- return device->isOpen() && device->isWritable() && (format == "m3u" || format == "m3u8");
-}
-
-QMediaPlaylistReader *QM3uPlaylistPlugin::createReader(QIODevice *device, const QByteArray &format)
-{
- Q_UNUSED(format);
- return new QM3uPlaylistReader(device);
-}
-
-QMediaPlaylistReader *QM3uPlaylistPlugin::createReader(const QUrl& location, const QByteArray &format)
-{
- Q_UNUSED(format);
- return new QM3uPlaylistReader(location);
-}
-
-QMediaPlaylistWriter *QM3uPlaylistPlugin::createWriter(QIODevice *device, const QByteArray &format)
-{
- Q_UNUSED(format);
- return new QM3uPlaylistWriter(device);
-}
-
diff --git a/src/plugins/m3u/qm3uhandler.h b/src/plugins/m3u/qm3uhandler.h
deleted file mode 100644
index 1bc0684d3..000000000
--- a/src/plugins/m3u/qm3uhandler.h
+++ /dev/null
@@ -1,68 +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 QM3UHANDLER_H
-#define QM3UHANDLER_H
-
-#include <private/qmediaplaylistioplugin_p.h>
-#include <QtCore/QObject>
-
-QT_USE_NAMESPACE
-
-class QM3uPlaylistPlugin : public QMediaPlaylistIOPlugin
-{
- Q_OBJECT
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaplaylistio/5.0" FILE "m3u.json")
-
-public:
- explicit QM3uPlaylistPlugin(QObject *parent = 0);
- virtual ~QM3uPlaylistPlugin();
-
- bool canRead(QIODevice *device, const QByteArray &format = QByteArray()) const override;
- bool canRead(const QUrl& location, const QByteArray &format = QByteArray()) const override;
-
- bool canWrite(QIODevice *device, const QByteArray &format) const override;
-
- QMediaPlaylistReader *createReader(QIODevice *device, const QByteArray &format = QByteArray()) override;
- QMediaPlaylistReader *createReader(const QUrl& location, const QByteArray &format = QByteArray()) override;
-
- QMediaPlaylistWriter *createWriter(QIODevice *device, const QByteArray &format) override;
-};
-
-#endif // QM3UHANDLER_H
diff --git a/src/plugins/multimedia/CMakeLists.txt b/src/plugins/multimedia/CMakeLists.txt
new file mode 100644
index 000000000..5bc39c1f8
--- /dev/null
+++ b/src/plugins/multimedia/CMakeLists.txt
@@ -0,0 +1,24 @@
+# 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)
+ add_subdirectory(gstreamer)
+endif()
+if(ANDROID)
+ add_subdirectory(android)
+endif()
+if(WASM)
+ add_subdirectory(wasm)
+endif()
+if(APPLE AND NOT WATCHOS)
+ add_subdirectory(darwin)
+endif()
+if(QT_FEATURE_wmf)
+ add_subdirectory(windows)
+endif()
+if(QT_FEATURE_mmrenderer)
+ add_subdirectory(qnx)
+endif()
diff --git a/src/plugins/multimedia/android/CMakeLists.txt b/src/plugins/multimedia/android/CMakeLists.txt
new file mode 100644
index 000000000..31a94ff4f
--- /dev/null
+++ b/src/plugins/multimedia/android/CMakeLists.txt
@@ -0,0 +1,62 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_plugin(QAndroidMediaPlugin
+ OUTPUT_NAME androidmediaplugin
+ PLUGIN_TYPE multimedia
+ SOURCES
+ common/qandroidaudiooutput_p.h
+ common/qandroidaudioinput.cpp common/qandroidaudioinput_p.h
+ audio/qandroidaudiodecoder.cpp audio/qandroidaudiodecoder_p.h
+ common/qandroidglobal_p.h
+ common/qandroidmultimediautils.cpp common/qandroidmultimediautils_p.h
+ common/qandroidvideosink.cpp common/qandroidvideosink_p.h
+ common/qandroidvideooutput.cpp common/qandroidvideooutput_p.h
+ mediacapture/qandroidcamera.cpp mediacapture/qandroidcamera_p.h
+ mediacapture/qandroidimagecapture.cpp mediacapture/qandroidimagecapture_p.h
+ mediacapture/qandroidcamerasession.cpp mediacapture/qandroidcamerasession_p.h
+ mediacapture/qandroidmediacapturesession.cpp mediacapture/qandroidmediacapturesession_p.h
+ mediacapture/qandroidcapturesession.cpp mediacapture/qandroidcapturesession_p.h
+ mediacapture/qandroidmediaencoder.cpp mediacapture/qandroidmediaencoder_p.h
+ mediaplayer/qandroidmediaplayer.cpp mediaplayer/qandroidmediaplayer_p.h
+ mediaplayer/qandroidmetadata.cpp mediaplayer/qandroidmetadata_p.h
+ qandroidformatsinfo.cpp qandroidformatsinfo_p.h
+ qandroidintegration.cpp qandroidintegration_p.h
+ wrappers/jni/androidcamera.cpp wrappers/jni/androidcamera_p.h
+ wrappers/jni/androidmediametadataretriever.cpp wrappers/jni/androidmediametadataretriever_p.h
+ wrappers/jni/androidmediaplayer.cpp wrappers/jni/androidmediaplayer_p.h
+ wrappers/jni/androidmediarecorder.cpp wrappers/jni/androidmediarecorder_p.h
+ 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
+ mediacapture
+ mediaplayer
+ wrappers/jni
+ ../android
+ LIBRARIES
+ Qt::MultimediaPrivate
+ Qt::CorePrivate
+ OpenSLES
+ mediandk
+)
+
+set_property(TARGET QAndroidMediaPlugin APPEND PROPERTY QT_ANDROID_BUNDLED_JAR_DEPENDENCIES
+ jar/Qt${QtMultimedia_VERSION_MAJOR}AndroidMultimedia.jar:org.qtproject.qt.android.multimedia.QtMultimediaUtils
+)
+set_property(TARGET QAndroidMediaPlugin APPEND PROPERTY QT_ANDROID_LIB_DEPENDENCIES
+ ${INSTALL_PLUGINSDIR}/multimedia/libplugins_multimedia_androidmediaplugin.so
+)
+set_property(TARGET QAndroidMediaPlugin APPEND PROPERTY QT_ANDROID_PERMISSIONS
+ android.permission.CAMERA android.permission.RECORD_AUDIO
+ android.permission.BLUETOOTH
+ android.permission.MODIFY_AUDIO_SETTINGS
+)
diff --git a/src/plugins/multimedia/android/android.json b/src/plugins/multimedia/android/android.json
new file mode 100644
index 000000000..6843bd330
--- /dev/null
+++ b/src/plugins/multimedia/android/android.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "android" ]
+}
diff --git a/src/plugins/multimedia/android/audio/qandroidaudiodecoder.cpp b/src/plugins/multimedia/android/audio/qandroidaudiodecoder.cpp
new file mode 100644
index 000000000..d200a72b5
--- /dev/null
+++ b/src/plugins/multimedia/android/audio/qandroidaudiodecoder.cpp
@@ -0,0 +1,437 @@
+// 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>
+#include <QtCore/qjniobject.h>
+#include <QtCore/qjnienvironment.h>
+#include <QtCore/private/qandroidextras_p.h>
+#include <qloggingcategory.h>
+#include <QTimer>
+#include <QFile>
+#include <QDir>
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+QT_BEGIN_NAMESPACE
+
+static const char tempFile[] = "encoded.wav";
+constexpr int dequeueTimeout = 5000;
+static Q_LOGGING_CATEGORY(adLogger, "QAndroidAudioDecoder")
+
+Decoder::Decoder()
+ : m_format(AMediaFormat_new())
+{}
+
+Decoder::~Decoder()
+{
+ if (m_codec) {
+ AMediaCodec_delete(m_codec);
+ m_codec = nullptr;
+ }
+
+ if (m_extractor) {
+ AMediaExtractor_delete(m_extractor);
+ m_extractor = nullptr;
+ }
+
+ if (m_format) {
+ AMediaFormat_delete(m_format);
+ m_format = nullptr;
+ }
+}
+
+void Decoder::stop()
+{
+ if (!m_codec)
+ return;
+
+ const media_status_t err = AMediaCodec_stop(m_codec);
+ if (err != AMEDIA_OK)
+ qCWarning(adLogger) << "stop() error: " << err;
+}
+
+void Decoder::setSource(const QUrl &source)
+{
+ const QJniObject path = QJniObject::callStaticObjectMethod(
+ "org/qtproject/qt/android/multimedia/QtMultimediaUtils",
+ "getMimeType",
+ "(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String;",
+ QNativeInterface::QAndroidApplication::context().object(),
+ QJniObject::fromString(source.path()).object());
+
+ const QString mime = path.isValid() ? path.toString() : "";
+
+ if (!mime.isEmpty() && !mime.contains("audio", Qt::CaseInsensitive)) {
+ m_formatError = tr("Cannot set source, invalid mime type for the source provided.");
+ return;
+ }
+
+ if (!m_extractor)
+ m_extractor = AMediaExtractor_new();
+
+ 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 = file.size();
+ media_status_t status = AMediaExtractor_setDataSourceFd(m_extractor, fd, 0,
+ size > 0 ? size : LONG_MAX);
+ close(fd);
+
+ if (status != AMEDIA_OK) {
+ if (m_extractor) {
+ AMediaExtractor_delete(m_extractor);
+ m_extractor = nullptr;
+ }
+ m_formatError = tr("Setting source for Audio Decoder failed.");
+ }
+}
+
+void Decoder::createDecoder()
+{
+ // get encoded format for decoder
+ m_format = AMediaExtractor_getTrackFormat(m_extractor, 0);
+
+ const char *mime;
+ if (!AMediaFormat_getString(m_format, AMEDIAFORMAT_KEY_MIME, &mime)) {
+ if (m_extractor) {
+ AMediaExtractor_delete(m_extractor);
+ m_extractor = nullptr;
+ }
+ emit error(QAudioDecoder::FormatError, tr("Format not supported by Audio Decoder."));
+
+ return;
+ }
+
+ // get audio duration from source
+ int64_t durationUs;
+ AMediaFormat_getInt64(m_format, AMEDIAFORMAT_KEY_DURATION, &durationUs);
+ emit durationChanged(durationUs / 1000);
+
+ // set default output audio format from input file
+ if (!m_outputFormat.isValid()) {
+ int32_t sampleRate;
+ AMediaFormat_getInt32(m_format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &sampleRate);
+ m_outputFormat.setSampleRate(sampleRate);
+ int32_t channelCount;
+ AMediaFormat_getInt32(m_format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &channelCount);
+ m_outputFormat.setChannelCount(channelCount);
+ m_outputFormat.setSampleFormat(QAudioFormat::Int16);
+ }
+
+ m_codec = AMediaCodec_createDecoderByType(mime);
+}
+
+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;
+ }
+
+ createDecoder();
+
+ if (!m_codec) {
+ emit error(QAudioDecoder::ResourceError, tr("Audio Decoder could not be created."));
+ return;
+ }
+
+ media_status_t status = AMediaCodec_configure(m_codec, m_format, nullptr /* surface */,
+ nullptr /* crypto */, 0);
+
+ if (status != AMEDIA_OK) {
+ emit error(QAudioDecoder::ResourceError, tr("Audio Decoder failed configuration."));
+ return;
+ }
+
+ status = AMediaCodec_start(m_codec);
+ if (status != AMEDIA_OK) {
+ emit error(QAudioDecoder::ResourceError, tr("Audio Decoder failed to start."));
+ return;
+ }
+
+ AMediaExtractor_selectTrack(m_extractor, 0);
+
+ emit decodingChanged(true);
+ m_inputEOS = false;
+ while (!m_inputEOS) {
+ // handle input buffer
+ const ssize_t bufferIdx = AMediaCodec_dequeueInputBuffer(m_codec, dequeueTimeout);
+
+ if (bufferIdx >= 0) {
+ size_t bufferSize = {};
+ uint8_t *buffer = AMediaCodec_getInputBuffer(m_codec, bufferIdx, &bufferSize);
+ const int sample = AMediaExtractor_readSampleData(m_extractor, buffer, bufferSize);
+ if (sample < 0) {
+ m_inputEOS = true;
+ break;
+ }
+
+ const int64_t presentationTimeUs = AMediaExtractor_getSampleTime(m_extractor);
+ AMediaCodec_queueInputBuffer(m_codec, bufferIdx, 0, sample, presentationTimeUs,
+ m_inputEOS ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+ AMediaExtractor_advance(m_extractor);
+
+ // handle output buffer
+ AMediaCodecBufferInfo info;
+ ssize_t idx = AMediaCodec_dequeueOutputBuffer(m_codec, &info, dequeueTimeout);
+
+ while (idx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED
+ || idx == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+ if (idx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED)
+ qCWarning(adLogger) << "dequeueOutputBuffer() status: outputFormat changed";
+
+ idx = AMediaCodec_dequeueOutputBuffer(m_codec, &info, dequeueTimeout);
+ }
+
+ if (idx >= 0) {
+ if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM)
+ break;
+
+ if (info.size > 0) {
+ size_t bufferSize;
+ const uint8_t *bufferData = AMediaCodec_getOutputBuffer(m_codec, idx,
+ &bufferSize);
+ const QByteArray data((const char*)(bufferData + info.offset), info.size);
+ auto audioBuffer = QAudioBuffer(data, m_outputFormat, presentationTimeUs);
+ if (presentationTimeUs >= 0)
+ emit positionChanged(std::move(audioBuffer), presentationTimeUs / 1000);
+
+ AMediaCodec_releaseOutputBuffer(m_codec, idx, false);
+ }
+ } else if (idx == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+ qCWarning(adLogger) << "dequeueOutputBuffer() status: try again later";
+ break;
+ } else {
+ qCWarning(adLogger) <<
+ "AMediaCodec_dequeueOutputBuffer() status: invalid buffer idx " << idx;
+ }
+ } else {
+ qCWarning(adLogger) << "dequeueInputBuffer() status: invalid buffer idx " << bufferIdx;
+ }
+ }
+ emit finished();
+}
+
+QAndroidAudioDecoder::QAndroidAudioDecoder(QAudioDecoder *parent)
+ : QPlatformAudioDecoder(parent),
+ m_decoder(new Decoder())
+{
+ connect(m_decoder, &Decoder::positionChanged, this, &QAndroidAudioDecoder::positionChanged);
+ connect(m_decoder, &Decoder::durationChanged, this, &QAndroidAudioDecoder::durationChanged);
+ connect(m_decoder, &Decoder::error, this, &QAndroidAudioDecoder::error);
+ connect(m_decoder, &Decoder::finished, this, &QAndroidAudioDecoder::finished);
+ connect(m_decoder, &Decoder::decodingChanged, this, &QPlatformAudioDecoder::setIsDecoding);
+ connect(this, &QAndroidAudioDecoder::setSourceUrl, m_decoder, & Decoder::setSource);
+}
+
+QAndroidAudioDecoder::~QAndroidAudioDecoder()
+{
+ m_decoder->thread()->quit();
+ m_decoder->thread()->wait();
+ delete m_threadDecoder;
+ delete m_decoder;
+}
+
+void QAndroidAudioDecoder::setSource(const QUrl &fileName)
+{
+ if (!requestPermissions())
+ return;
+
+ if (isDecoding())
+ return;
+
+ m_device = nullptr;
+ error(QAudioDecoder::NoError, QStringLiteral(""));
+
+ if (m_source != fileName) {
+ m_source = fileName;
+ emit setSourceUrl(m_source);
+ sourceChanged();
+ }
+}
+
+void QAndroidAudioDecoder::setSourceDevice(QIODevice *device)
+{
+ if (isDecoding())
+ return;
+
+ m_source.clear();
+ if (m_device != device) {
+ m_device = device;
+
+ if (!requestPermissions())
+ return;
+
+ sourceChanged();
+ }
+}
+
+void QAndroidAudioDecoder::start()
+{
+ if (isDecoding())
+ return;
+
+ 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);
+ m_threadDecoder->start();
+ }
+
+ decode();
+}
+
+void QAndroidAudioDecoder::stop()
+{
+ if (!isDecoding() && m_position < 0 && m_duration < 0)
+ return;
+
+ m_decoder->stop();
+ m_audioBuffer.clear();
+ m_position = -1;
+ m_duration = -1;
+ setIsDecoding(false);
+
+ emit bufferAvailableChanged(false);
+ emit QPlatformAudioDecoder::positionChanged(m_position);
+}
+
+QAudioBuffer QAndroidAudioDecoder::read()
+{
+ if (!m_audioBuffer.isEmpty()) {
+ QPair<QAudioBuffer, int> buffer = m_audioBuffer.takeFirst();
+ m_position = buffer.second;
+ emit QPlatformAudioDecoder::positionChanged(buffer.second);
+ return buffer.first;
+ }
+
+ // no buffers available
+ return {};
+}
+
+bool QAndroidAudioDecoder::bufferAvailable() const
+{
+ return m_audioBuffer.size() > 0;
+}
+
+qint64 QAndroidAudioDecoder::position() const
+{
+ return m_position;
+}
+
+qint64 QAndroidAudioDecoder::duration() const
+{
+ return m_duration;
+}
+
+void QAndroidAudioDecoder::positionChanged(QAudioBuffer audioBuffer, qint64 position)
+{
+ m_audioBuffer.append(QPair<QAudioBuffer, int>(audioBuffer, position));
+ m_position = position;
+ emit bufferReady();
+}
+
+void QAndroidAudioDecoder::durationChanged(qint64 duration)
+{
+ m_duration = duration;
+ emit QPlatformAudioDecoder::durationChanged(duration);
+}
+
+void QAndroidAudioDecoder::error(const QAudioDecoder::Error err, const QString &errorString)
+{
+ stop();
+ emit QPlatformAudioDecoder::error(err, errorString);
+}
+
+void QAndroidAudioDecoder::finished()
+{
+ emit bufferAvailableChanged(m_audioBuffer.size() > 0);
+
+ if (m_duration != -1)
+ emit durationChanged(m_duration);
+
+ // remove temp file when decoding is finished
+ QFile(QString(QDir::tempPath()).append(QString::fromUtf8(tempFile))).remove();
+ emit QPlatformAudioDecoder::finished();
+}
+
+bool QAndroidAudioDecoder::requestPermissions()
+{
+ const auto writeRes = QtAndroidPrivate::requestPermission(QStringLiteral("android.permission.WRITE_EXTERNAL_STORAGE"));
+ if (writeRes.result() == QtAndroidPrivate::Authorized)
+ return true;
+
+ return false;
+}
+
+void QAndroidAudioDecoder::decode()
+{
+ if (m_device) {
+ connect(m_device, &QIODevice::readyRead, this, &QAndroidAudioDecoder::readDevice);
+ if (m_device->bytesAvailable())
+ readDevice();
+ } else {
+ QTimer::singleShot(0, m_decoder, &Decoder::doDecode);
+ }
+}
+
+bool QAndroidAudioDecoder::createTempFile()
+{
+ QFile file = QFile(QDir::tempPath().append(QString::fromUtf8(tempFile)), this);
+
+ bool success = file.open(QIODevice::QIODevice::ReadWrite);
+ if (!success)
+ 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 temporary file"));
+
+ file.close();
+ m_deviceBuffer.clear();
+ if (success)
+ m_decoder->setSource(file.fileName());
+
+ return success;
+}
+
+void QAndroidAudioDecoder::readDevice() {
+ m_deviceBuffer.append(m_device->readAll());
+ if (m_device->atEnd()) {
+ disconnect(m_device, &QIODevice::readyRead, this, &QAndroidAudioDecoder::readDevice);
+ if (!createTempFile()) {
+ m_deviceBuffer.clear();
+ stop();
+ return;
+ }
+ QTimer::singleShot(0, m_decoder, &Decoder::doDecode);
+ }
+}
+
+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
new file mode 100644
index 000000000..65a0f1855
--- /dev/null
+++ b/src/plugins/multimedia/android/audio/qandroidaudiodecoder_p.h
@@ -0,0 +1,118 @@
+// 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
+
+//
+// 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/qplatformaudiodecoder_p.h"
+
+#include <QtCore/qurl.h>
+#include <QThread>
+
+#include "media/NdkMediaCodec.h"
+#include "media/NdkMediaExtractor.h"
+#include "media/NdkMediaFormat.h"
+#include "media/NdkMediaError.h"
+
+
+QT_USE_NAMESPACE
+
+class Decoder : public QObject
+{
+ Q_OBJECT
+public:
+ Decoder();
+ ~Decoder();
+
+public slots:
+ void setSource(const QUrl &source);
+ void doDecode();
+ void stop();
+
+signals:
+ void positionChanged(const QAudioBuffer &buffer, qint64 position);
+ void durationChanged(const qint64 duration);
+ void error(const QAudioDecoder::Error error, const QString &errorString);
+ void finished();
+ void decodingChanged(bool decoding);
+
+private:
+ void createDecoder();
+
+ AMediaCodec *m_codec = nullptr;
+ AMediaExtractor *m_extractor = nullptr;
+ AMediaFormat *m_format = nullptr;
+
+ QAudioFormat m_outputFormat;
+ QString m_formatError;
+ bool m_inputEOS;
+};
+
+
+class QAndroidAudioDecoder : public QPlatformAudioDecoder
+{
+ Q_OBJECT
+public:
+ QAndroidAudioDecoder(QAudioDecoder *parent);
+ virtual ~QAndroidAudioDecoder();
+
+ QUrl source() const override { return m_source; }
+ void setSource(const QUrl &fileName) override;
+
+ QIODevice *sourceDevice() const override { return m_device; }
+ void setSourceDevice(QIODevice *device) override;
+
+ void start() override;
+ void stop() override;
+
+ QAudioFormat audioFormat() const override { return {}; }
+ void setAudioFormat(const QAudioFormat &/*format*/) override {}
+
+ QAudioBuffer read() override;
+ bool bufferAvailable() const override;
+
+ qint64 position() const override;
+ qint64 duration() const override;
+
+signals:
+ void setSourceUrl(const QUrl &source);
+
+private slots:
+ void positionChanged(QAudioBuffer audioBuffer, qint64 position);
+ void durationChanged(qint64 duration);
+ void error(const QAudioDecoder::Error error, const QString &errorString);
+ void readDevice();
+ void finished();
+
+private:
+ bool requestPermissions();
+ bool createTempFile();
+ void decode();
+
+ QIODevice *m_device = nullptr;
+ Decoder *m_decoder;
+
+ QList<QPair<QAudioBuffer, int>> m_audioBuffer;
+ QUrl m_source;
+
+ qint64 m_position = -1;
+ qint64 m_duration = -1;
+
+ QByteArray m_deviceBuffer;
+
+ QThread *m_threadDecoder = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDAUDIODECODER_P_H
diff --git a/src/plugins/multimedia/android/common/qandroidaudioinput.cpp b/src/plugins/multimedia/android/common/qandroidaudioinput.cpp
new file mode 100644
index 000000000..a1eb9258b
--- /dev/null
+++ b/src/plugins/multimedia/android/common/qandroidaudioinput.cpp
@@ -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
+
+#include "qandroidaudioinput_p.h"
+
+#include <qaudioinput.h>
+
+#include <QtCore/qjniobject.h>
+
+QT_BEGIN_NAMESPACE
+
+QAndroidAudioInput::QAndroidAudioInput(QAudioInput *parent)
+ : QObject(parent),
+ QPlatformAudioInput(parent)
+{
+ m_muted = isMuted();
+}
+
+QAndroidAudioInput::~QAndroidAudioInput()
+{
+ setMuted(m_muted);
+}
+
+void QAndroidAudioInput::setMuted(bool muted)
+{
+ bool isInputMuted = isMuted();
+ if (muted != isInputMuted) {
+ QJniObject::callStaticMethod<void>(
+ "org/qtproject/qt/android/multimedia/QtAudioDeviceManager",
+ "setInputMuted",
+ "(Z)V",
+ muted);
+ emit mutedChanged(muted);
+ }
+}
+
+bool QAndroidAudioInput::isMuted() const
+{
+ return QJniObject::callStaticMethod<jboolean>(
+ "org/qtproject/qt/android/multimedia/QtAudioDeviceManager",
+ "isMicrophoneMute",
+ "()Z");
+}
+
+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
new file mode 100644
index 000000000..ef59da8ec
--- /dev/null
+++ b/src/plugins/multimedia/android/common/qandroidaudioinput_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 QANDROIDAUDIOINPUT_H
+#define QANDROIDAUDIOINPUT_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 <private/qtmultimediaglobal_p.h>
+#include <private/qplatformaudioinput_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_MULTIMEDIA_EXPORT QAndroidAudioInput : public QObject, public QPlatformAudioInput
+{
+ Q_OBJECT
+
+public:
+ explicit QAndroidAudioInput(QAudioInput *parent);
+ ~QAndroidAudioInput();
+
+ void setMuted(bool muted) override;
+
+ bool isMuted() const;
+
+Q_SIGNALS:
+ void mutedChanged(bool muted);
+
+private:
+ bool m_muted = false;
+
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/android/common/qandroidaudiooutput_p.h b/src/plugins/multimedia/android/common/qandroidaudiooutput_p.h
new file mode 100644
index 000000000..d5d25b458
--- /dev/null
+++ b/src/plugins/multimedia/android/common/qandroidaudiooutput_p.h
@@ -0,0 +1,30 @@
+// 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
+
+//
+// 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>
+
+QT_BEGIN_NAMESPACE
+
+class Q_MULTIMEDIA_EXPORT QAndroidAudioOutput : public QPlatformAudioOutput
+{
+public:
+ QAndroidAudioOutput(QAudioOutput *qq) : QPlatformAudioOutput(qq) {}
+};
+
+QT_END_NAMESPACE
+
+
+#endif // QANDROIDAUDIOOUTPUT_H
diff --git a/src/plugins/multimedia/android/common/qandroidglobal_p.h b/src/plugins/multimedia/android/common/qandroidglobal_p.h
new file mode 100644
index 000000000..1022fa061
--- /dev/null
+++ b/src/plugins/multimedia/android/common/qandroidglobal_p.h
@@ -0,0 +1,28 @@
+// 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
+
+//
+// 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/qglobal.h>
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qtAndroidMediaPlugin)
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDGLOBAL_H
diff --git a/src/plugins/multimedia/android/common/qandroidmultimediautils.cpp b/src/plugins/multimedia/android/common/qandroidmultimediautils.cpp
new file mode 100644
index 000000000..6e4b95fe9
--- /dev/null
+++ b/src/plugins/multimedia/android/common/qandroidmultimediautils.cpp
@@ -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
+
+#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
+
+int qt_findClosestValue(const QList<int> &list, int value)
+{
+ if (list.size() < 2)
+ return 0;
+
+ int begin = 0;
+ int end = list.size() - 1;
+ int pivot = begin + (end - begin) / 2;
+ int v = list.at(pivot);
+
+ while (end - begin > 1) {
+ if (value == v)
+ return pivot;
+
+ if (value > v)
+ begin = pivot;
+ else
+ end = pivot;
+
+ pivot = begin + (end - begin) / 2;
+ v = list.at(pivot);
+ }
+
+ return value - v >= list.at(pivot + 1) - value ? pivot + 1 : pivot;
+}
+
+bool qt_sizeLessThan(const QSize &s1, const QSize &s2)
+{
+ return s1.width() * s1.height() < s2.width() * s2.height();
+}
+
+QVideoFrameFormat::PixelFormat qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat f)
+{
+ switch (f) {
+ case AndroidCamera::NV21:
+ return QVideoFrameFormat::Format_NV21;
+ case AndroidCamera::YV12:
+ return QVideoFrameFormat::Format_YV12;
+ case AndroidCamera::YUY2:
+ return QVideoFrameFormat::Format_YUYV;
+ case AndroidCamera::JPEG:
+ return QVideoFrameFormat::Format_Jpeg;
+ default:
+ return QVideoFrameFormat::Format_Invalid;
+ }
+}
+
+AndroidCamera::ImageFormat qt_androidImageFormatFromPixelFormat(QVideoFrameFormat::PixelFormat f)
+{
+ switch (f) {
+ case QVideoFrameFormat::Format_NV21:
+ return AndroidCamera::NV21;
+ case QVideoFrameFormat::Format_YV12:
+ return AndroidCamera::YV12;
+ case QVideoFrameFormat::Format_YUYV:
+ return AndroidCamera::YUY2;
+ case QVideoFrameFormat::Format_Jpeg:
+ return AndroidCamera::JPEG;
+ default:
+ return AndroidCamera::UnknownImageFormat;
+ }
+}
+
+static bool androidRequestPermission(const QString &permission)
+{
+ if (QNativeInterface::QAndroidApplication::sdkVersion() < 23)
+ return true;
+
+ // Permission already granted?
+ if (QtAndroidPrivate::checkPermission(permission).result() == QtAndroidPrivate::Authorized)
+ return true;
+
+ if (QtAndroidPrivate::requestPermission(permission).result() != QtAndroidPrivate::Authorized)
+ return false;
+
+ return true;
+}
+
+static bool androidCheckPermission(const QPermission &permission)
+{
+ return qApp->checkPermission(permission) == Qt::PermissionStatus::Granted;
+}
+
+bool qt_androidCheckCameraPermission()
+{
+ const QCameraPermission permission;
+ const auto granted = androidCheckPermission(permission);
+ if (!granted)
+ qCDebug(qtAndroidMediaPlugin, "Camera permission not granted!");
+ return granted;
+}
+
+bool qt_androidCheckMicrophonePermission()
+{
+ const QMicrophonePermission permission;
+ const auto granted = androidCheckPermission(permission);
+ if (!granted)
+ qCDebug(qtAndroidMediaPlugin, "Microphone permission not granted!");
+ return granted;
+}
+
+bool qt_androidRequestWriteStoragePermission()
+{
+ if (!androidRequestPermission(QStringLiteral("android.permission.WRITE_EXTERNAL_STORAGE"))) {
+ qCDebug(qtAndroidMediaPlugin, "Storage permission denied by user!");
+ return false;
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/android/common/qandroidmultimediautils_p.h b/src/plugins/multimedia/android/common/qandroidmultimediautils_p.h
new file mode 100644
index 000000000..5fe841e8c
--- /dev/null
+++ b/src/plugins/multimedia/android/common/qandroidmultimediautils_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 QANDROIDMULTIMEDIAUTILS_H
+#define QANDROIDMULTIMEDIAUTILS_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 <qsize.h>
+#include "androidcamera_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// return the index of the closest value to <value> in <list>
+// (binary search)
+int qt_findClosestValue(const QList<int> &list, int value);
+
+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_androidRequestWriteStoragePermission();
+
+bool qt_androidCheckCameraPermission();
+bool qt_androidCheckMicrophonePermission();
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMULTIMEDIAUTILS_H
diff --git a/src/plugins/multimedia/android/common/qandroidvideooutput.cpp b/src/plugins/multimedia/android/common/qandroidvideooutput.cpp
new file mode 100644
index 000000000..0724a8359
--- /dev/null
+++ b/src/plugins/multimedia/android/common/qandroidvideooutput.cpp
@@ -0,0 +1,468 @@
+// 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/qhwvideobuffer_p.h>
+#include <private/qvideoframeconverter_p.h>
+#include <private/qplatformvideosink_p.h>
+#include <private/qvideoframe_p.h>
+#include <qvideosink.h>
+#include <qopenglcontext.h>
+#include <qopenglfunctions.h>
+#include <qvideoframeformat.h>
+#include <qthread.h>
+#include <qfile.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidVideoFrameTextures : public QVideoFrameTextures
+{
+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;
+};
+
+class AndroidTextureVideoBuffer : public QRhiWithThreadGuard, public QHwVideoBuffer
+{
+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)),
+ QHwVideoBuffer(QVideoFrame::RhiTextureHandle, m_guardRhi.get()),
+ m_size(size),
+ m_tex(std::move(tex))
+ {}
+
+ MapData map(QtVideo::MapMode mode) override;
+
+ void unmap() override
+ {
+ m_image = {};
+ m_mapMode = QtVideo::MapMode::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;
+ QtVideo::MapMode m_mapMode = QtVideo::MapMode::NotMapped;
+};
+
+class ImageFromVideoFrameHelper : public QHwVideoBuffer
+{
+public:
+ ImageFromVideoFrameHelper(AndroidTextureVideoBuffer &atvb)
+ : QHwVideoBuffer(QVideoFrame::RhiTextureHandle, atvb.rhi()), m_atvb(atvb)
+ {}
+ std::unique_ptr<QVideoFrameTextures> mapTextures(QRhi *rhi) override
+ {
+ return m_atvb.mapTextures(rhi);
+ }
+
+ MapData map(QtVideo::MapMode) override { return {}; }
+ void unmap() override {}
+
+private:
+ AndroidTextureVideoBuffer &m_atvb;
+};
+
+QAbstractVideoBuffer::MapData AndroidTextureVideoBuffer::map(QtVideo::MapMode mode)
+{
+ QAbstractVideoBuffer::MapData mapData;
+
+ if (m_mapMode == QtVideo::MapMode::NotMapped && mode == QtVideo::MapMode::ReadOnly) {
+ m_mapMode = QtVideo::MapMode::ReadOnly;
+ m_image = qImageFromVideoFrame(QVideoFramePrivate::createFrame(
+ std::make_unique<ImageFromVideoFrameHelper>(*this),
+ QVideoFrameFormat(m_size, QVideoFrameFormat::Format_RGBA8888)));
+ mapData.planeCount = 1;
+ mapData.bytesPerLine[0] = m_image.bytesPerLine();
+ mapData.dataSize[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();
+ // flip it back, see
+ // http://androidxref.com/9.0.0_r3/xref/frameworks/native/libs/gui/GLConsumer.cpp#866
+ // (NB our matrix ctor takes row major)
+ static const QMatrix4x4 flipV(1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, -1.0f, 0.0f, 1.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+ m *= flipV;
+ return m;
+}
+
+class AndroidTextureThread : public QThread
+{
+ Q_OBJECT
+public:
+ AndroidTextureThread(QAndroidTextureVideoOutput * vo)
+ : QThread()
+ , m_videoOutput(vo)
+ {
+ }
+
+ ~AndroidTextureThread() {
+ QMetaObject::invokeMethod(this,
+ &AndroidTextureThread::clearSurfaceTexture, Qt::BlockingQueuedConnection);
+ this->quit();
+ this->wait();
+ }
+
+ void start()
+ {
+ QThread::start();
+ moveToThread(this);
+ }
+
+ void initRhi(QOpenGLContext *context)
+ {
+ QRhiGles2InitParams params;
+ params.shareContext = context;
+ params.fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
+ m_rhi.reset(QRhi::create(QRhi::OpenGLES2, &params));
+ }
+
+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 clearFrame() { emit newFrame({}); }
+
+ void setFrameSize(QSize size) { m_size = size; }
+
+ void clearSurfaceTexture()
+ {
+ m_surfaceTexture.reset();
+ m_texture.reset();
+ m_textureCopy.reset();
+ m_rhi.reset();
+ }
+
+ AndroidSurfaceTexture *createSurfaceTexture(QRhi *rhi)
+ {
+ if (m_surfaceTexture)
+ return m_surfaceTexture.get();
+
+ QOpenGLContext *ctx = rhi
+ ? static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles())->context
+ : nullptr;
+ initRhi(ctx);
+
+ 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); });
+
+ m_textureCopy = std::make_unique<TextureCopy>(m_rhi.get(), m_texture.get());
+
+ } else {
+ m_texture.reset();
+ m_surfaceTexture.reset();
+ }
+
+ return m_surfaceTexture.get();
+ }
+
+signals:
+ void newFrame(const QVideoFrame &);
+
+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;
+};
+
+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);
+}
+
+QAndroidTextureVideoOutput::QAndroidTextureVideoOutput(QVideoSink *sink, QObject *parent)
+ : QAndroidVideoOutput(parent)
+ , m_sink(sink)
+{
+ if (!m_sink) {
+ qDebug() << "Cannot create QAndroidTextureVideoOutput without a sink.";
+ m_surfaceThread = nullptr;
+ return;
+ }
+
+ startNewSurfaceThread();
+}
+
+void QAndroidTextureVideoOutput::startNewSurfaceThread()
+{
+ m_surfaceThread = std::make_shared<AndroidTextureThread>(this);
+ connect(m_surfaceThread.get(), &AndroidTextureThread::newFrame,
+ this, &QAndroidTextureVideoOutput::newFrame);
+ m_surfaceThread->start();
+}
+
+QAndroidTextureVideoOutput::~QAndroidTextureVideoOutput()
+{
+ // Make sure that no more VideFrames will be created by surfaceThread
+ QMetaObject::invokeMethod(m_surfaceThread.get(),
+ &AndroidTextureThread::clearSurfaceTexture, Qt::BlockingQueuedConnection);
+}
+
+void QAndroidTextureVideoOutput::setSubtitle(const QString &subtitle)
+{
+ if (m_sink) {
+ auto *sink = m_sink->platformVideoSink();
+ if (sink)
+ sink->setSubtitleText(subtitle);
+ }
+}
+
+bool QAndroidTextureVideoOutput::shouldTextureBeUpdated() const
+{
+ return m_sink->rhi() && m_surfaceCreatedWithoutRhi;
+}
+
+AndroidSurfaceTexture *QAndroidTextureVideoOutput::surfaceTexture()
+{
+ if (!m_sink)
+ 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::setVideoSize(const QSize &size)
+{
+ if (m_nativeSize == size)
+ return;
+
+ m_nativeSize = size;
+ QMetaObject::invokeMethod(m_surfaceThread.get(),
+ [&](){ m_surfaceThread->setFrameSize(size); },
+ Qt::BlockingQueuedConnection);
+}
+
+void QAndroidTextureVideoOutput::stop()
+{
+ m_nativeSize = {};
+ QMetaObject::invokeMethod(m_surfaceThread.get(), [&](){ m_surfaceThread->clearFrame(); });
+}
+
+void QAndroidTextureVideoOutput::reset()
+{
+ if (m_sink)
+ m_sink->platformVideoSink()->setVideoFrame({});
+ QMetaObject::invokeMethod(m_surfaceThread.get(), &AndroidTextureThread::clearSurfaceTexture);
+}
+
+void QAndroidTextureVideoOutput::newFrame(const QVideoFrame &frame)
+{
+ 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
new file mode 100644
index 000000000..7c9be5aee
--- /dev/null
+++ b/src/plugins/multimedia/android/common/qandroidvideooutput_p.h
@@ -0,0 +1,93 @@
+// 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
+
+//
+// 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 <qsize.h>
+#include <qmutex.h>
+#include <qreadwritelock.h>
+#include <qabstractvideobuffer.h>
+#include <qmatrix4x4.h>
+#include <qoffscreensurface.h>
+#include <rhi/qrhi.h>
+
+QT_BEGIN_NAMESPACE
+
+class AndroidSurfaceTexture;
+class AndroidSurfaceHolder;
+class QVideoSink;
+
+class QAndroidVideoOutput : public QObject
+{
+ Q_OBJECT
+public:
+ virtual ~QAndroidVideoOutput() { }
+
+ virtual AndroidSurfaceTexture *surfaceTexture() { return 0; }
+ virtual AndroidSurfaceHolder *surfaceHolder() { return 0; }
+
+ virtual bool isReady() { return true; }
+
+ virtual void setVideoSize(const QSize &) { }
+ virtual void start() { }
+ virtual void stop() { }
+ virtual void reset() { }
+ virtual QSize getVideoSize() const { return QSize(0, 0); }
+
+Q_SIGNALS:
+ void readyChanged(bool);
+
+protected:
+ QAndroidVideoOutput(QObject *parent) : QObject(parent) { }
+};
+
+class AndroidTextureThread;
+class QAndroidTextureVideoOutput : public QAndroidVideoOutput
+{
+ Q_OBJECT
+public:
+ explicit QAndroidTextureVideoOutput(QVideoSink *sink, QObject *parent = 0);
+ ~QAndroidTextureVideoOutput() override;
+
+ QVideoSink *surface() const { return m_sink; }
+ bool shouldTextureBeUpdated() const;
+
+ AndroidSurfaceTexture *surfaceTexture() override;
+
+ void setVideoSize(const QSize &) override;
+ void stop() override;
+ void reset() override;
+ QSize getVideoSize() const override { return m_nativeSize; }
+
+ void setSubtitle(const QString &subtitle);
+ std::shared_ptr<AndroidTextureThread> getSurfaceThread() { return m_surfaceThread; }
+private Q_SLOTS:
+ void newFrame(const QVideoFrame &);
+
+private:
+ void startNewSurfaceThread();
+ QVideoSink *m_sink = nullptr;
+ QSize m_nativeSize;
+ bool m_surfaceCreatedWithoutRhi = false;
+
+ std::shared_ptr<AndroidTextureThread> m_surfaceThread;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QList<QRhiResource *>)
+Q_DECLARE_METATYPE(QRhi*)
+
+#endif // QANDROIDVIDEOOUTPUT_H
diff --git a/src/plugins/multimedia/android/common/qandroidvideosink.cpp b/src/plugins/multimedia/android/common/qandroidvideosink.cpp
new file mode 100644
index 000000000..3da5eab31
--- /dev/null
+++ b/src/plugins/multimedia/android/common/qandroidvideosink.cpp
@@ -0,0 +1,35 @@
+// 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 <rhi/qrhi.h>
+
+#include <QtCore/qdebug.h>
+
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+QAndroidVideoSink::QAndroidVideoSink(QVideoSink *parent)
+ : QPlatformVideoSink(parent)
+{
+}
+
+QAndroidVideoSink::~QAndroidVideoSink()
+{
+}
+
+void QAndroidVideoSink::setRhi(QRhi *rhi)
+{
+ if (rhi && rhi->backend() != QRhi::OpenGLES2)
+ rhi = nullptr;
+ if (m_rhi == 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
new file mode 100644
index 000000000..9afc58f65
--- /dev/null
+++ b/src/plugins/multimedia/android/common/qandroidvideosink_p.h
@@ -0,0 +1,41 @@
+// 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
+
+//
+// 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/qplatformvideosink_p.h>
+
+#include <qvideosink.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidVideoSink
+ : public QPlatformVideoSink
+{
+ Q_OBJECT
+public:
+ explicit QAndroidVideoSink(QVideoSink *parent = 0);
+ ~QAndroidVideoSink();
+
+ void setRhi(QRhi *rhi) override;
+
+private:
+ QRhi *m_rhi = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidcamera.cpp b/src/plugins/multimedia/android/mediacapture/qandroidcamera.cpp
new file mode 100644
index 000000000..52d2e00f6
--- /dev/null
+++ b/src/plugins/multimedia/android/mediacapture/qandroidcamera.cpp
@@ -0,0 +1,562 @@
+// 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"
+#include "qandroidcapturesession_p.h"
+#include "qandroidmediacapturesession_p.h"
+#include <qmediadevices.h>
+#include <qcameradevice.h>
+#include <qtimer.h>
+#include "qandroidmultimediautils_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QAndroidCamera::QAndroidCamera(QCamera *camera)
+ : QPlatformCamera(camera)
+{
+ Q_ASSERT(camera);
+}
+
+QAndroidCamera::~QAndroidCamera()
+{
+}
+
+void QAndroidCamera::setActive(bool active)
+{
+ if (m_cameraSession) {
+ m_cameraSession->setActive(active);
+ } else {
+ isPendingSetActive = active;
+ }
+}
+
+bool QAndroidCamera::isActive() const
+{
+ return m_cameraSession ? m_cameraSession->isActive() : false;
+}
+
+void QAndroidCamera::setCamera(const QCameraDevice &camera)
+{
+ m_cameraDev = camera;
+
+ if (m_cameraSession) {
+ int id = 0;
+ auto cameras = QMediaDevices::videoInputs();
+ for (int i = 0; i < cameras.size(); ++i) {
+ if (cameras.at(i) == camera) {
+ id = i;
+ break;
+ }
+ }
+ if (id != m_cameraSession->getSelectedCameraId()) {
+ m_cameraSession->setSelectedCameraId(id);
+ reactivateCameraSession();
+ }
+ }
+}
+
+void QAndroidCamera::reactivateCameraSession()
+{
+ if (m_cameraSession->isActive()) {
+ if (m_service->captureSession() &&
+ m_service->captureSession()->state() == QMediaRecorder::RecordingState) {
+ m_service->captureSession()->stop();
+ qWarning() << "Changing camera during recording not supported";
+ }
+ m_cameraSession->setActive(false);
+ m_cameraSession->setActive(true);
+ }
+}
+
+bool QAndroidCamera::setCameraFormat(const QCameraFormat &format)
+{
+ m_cameraFormat = format;
+
+ if (m_cameraSession)
+ m_cameraSession->setCameraFormat(m_cameraFormat);
+
+ return true;
+}
+
+void QAndroidCamera::setCaptureSession(QPlatformMediaCaptureSession *session)
+{
+ QAndroidMediaCaptureSession *captureSession = static_cast<QAndroidMediaCaptureSession *>(session);
+ if (m_service == captureSession)
+ return;
+
+ m_service = captureSession;
+ if (!m_service) {
+ disconnect(m_cameraSession,nullptr,this,nullptr);
+ m_cameraSession = nullptr;
+ return;
+ }
+
+ m_cameraSession = m_service->cameraSession();
+ Q_ASSERT(m_cameraSession);
+ if (!m_cameraFormat.isNull())
+ m_cameraSession->setCameraFormat(m_cameraFormat);
+
+ setCamera(m_cameraDev);
+
+ 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)
+{
+ if (!m_cameraSession || !m_cameraSession->camera())
+ return;
+
+ if (isFocusModeSupported(mode)) {
+ QString focusMode;
+
+ switch (mode) {
+ case QCamera::FocusModeHyperfocal:
+ focusMode = QLatin1String("edof");
+ break;
+ case QCamera::FocusModeInfinity: // not 100%, but close
+ focusMode = QLatin1String("infinity");
+ break;
+ case QCamera::FocusModeManual:
+ focusMode = QLatin1String("fixed");
+ break;
+ case QCamera::FocusModeAutoNear:
+ focusMode = QLatin1String("macro");
+ break;
+ case QCamera::FocusModeAuto:
+ case QCamera::FocusModeAutoFar:
+ if (1) { // ###?
+ focusMode = QLatin1String("continuous-video");
+ } else {
+ focusMode = QLatin1String("continuous-picture");
+ }
+ break;
+ }
+
+ m_cameraSession->camera()->setFocusMode(focusMode);
+
+ // reset focus position
+ m_cameraSession->camera()->cancelAutoFocus();
+
+ focusModeChanged(mode);
+ }
+}
+
+bool QAndroidCamera::isFocusModeSupported(QCamera::FocusMode mode) const
+{
+ return (m_cameraSession && m_cameraSession->camera()) ? m_supportedFocusModes.contains(mode) : false;
+}
+
+void QAndroidCamera::onCameraOpened()
+{
+ Q_ASSERT(m_cameraSession);
+ connect(m_cameraSession->camera(), &AndroidCamera::previewSizeChanged, this, &QAndroidCamera::setCameraFocusArea);
+
+ m_supportedFocusModes.clear();
+ m_continuousPictureFocusSupported = false;
+ m_continuousVideoFocusSupported = false;
+ m_focusPointSupported = false;
+
+ QStringList focusModes = m_cameraSession->camera()->getSupportedFocusModes();
+ for (int i = 0; i < focusModes.size(); ++i) {
+ const QString &focusMode = focusModes.at(i);
+ if (focusMode == QLatin1String("continuous-picture")) {
+ m_supportedFocusModes << QCamera::FocusModeAuto;
+ m_continuousPictureFocusSupported = true;
+ } else if (focusMode == QLatin1String("continuous-video")) {
+ m_supportedFocusModes << QCamera::FocusModeAuto;
+ m_continuousVideoFocusSupported = true;
+ } else if (focusMode == QLatin1String("edof")) {
+ m_supportedFocusModes << QCamera::FocusModeHyperfocal;
+ } else if (focusMode == QLatin1String("fixed")) {
+ m_supportedFocusModes << QCamera::FocusModeManual;
+ } else if (focusMode == QLatin1String("infinity")) {
+ m_supportedFocusModes << QCamera::FocusModeInfinity;
+ } else if (focusMode == QLatin1String("macro")) {
+ m_supportedFocusModes << QCamera::FocusModeAutoNear;
+ }
+ }
+
+ if (m_cameraSession->camera()->getMaxNumFocusAreas() > 0)
+ m_focusPointSupported = true;
+
+ auto m = focusMode();
+ if (!m_supportedFocusModes.contains(m))
+ m = QCamera::FocusModeAuto;
+
+ setFocusMode(m);
+ setCustomFocusPoint(focusPoint());
+
+ if (m_cameraSession->camera()->isZoomSupported()) {
+ m_zoomRatios = m_cameraSession->camera()->getZoomRatios();
+ qreal maxZoom = m_zoomRatios.last() / qreal(100);
+ maximumZoomFactorChanged(maxZoom);
+ zoomTo(1, -1);
+ } else {
+ m_zoomRatios.clear();
+ maximumZoomFactorChanged(1.0);
+ }
+
+ m_minExposureCompensationIndex = m_cameraSession->camera()->getMinExposureCompensation();
+ m_maxExposureCompensationIndex = m_cameraSession->camera()->getMaxExposureCompensation();
+ m_exposureCompensationStep = m_cameraSession->camera()->getExposureCompensationStep();
+ exposureCompensationRangeChanged(m_minExposureCompensationIndex*m_exposureCompensationStep,
+ m_maxExposureCompensationIndex*m_exposureCompensationStep);
+
+ m_supportedExposureModes.clear();
+ QStringList sceneModes = m_cameraSession->camera()->getSupportedSceneModes();
+ if (!sceneModes.isEmpty()) {
+ for (int i = 0; i < sceneModes.size(); ++i) {
+ const QString &sceneMode = sceneModes.at(i);
+ if (sceneMode == QLatin1String("auto"))
+ m_supportedExposureModes << QCamera::ExposureAuto;
+ else if (sceneMode == QLatin1String("beach"))
+ m_supportedExposureModes << QCamera::ExposureBeach;
+ else if (sceneMode == QLatin1String("night"))
+ m_supportedExposureModes << QCamera::ExposureNight;
+ else if (sceneMode == QLatin1String("portrait"))
+ m_supportedExposureModes << QCamera::ExposurePortrait;
+ else if (sceneMode == QLatin1String("snow"))
+ m_supportedExposureModes << QCamera::ExposureSnow;
+ else if (sceneMode == QLatin1String("sports"))
+ m_supportedExposureModes << QCamera::ExposureSports;
+ else if (sceneMode == QLatin1String("action"))
+ m_supportedExposureModes << QCamera::ExposureAction;
+ else if (sceneMode == QLatin1String("landscape"))
+ m_supportedExposureModes << QCamera::ExposureLandscape;
+ else if (sceneMode == QLatin1String("night-portrait"))
+ m_supportedExposureModes << QCamera::ExposureNightPortrait;
+ else if (sceneMode == QLatin1String("theatre"))
+ m_supportedExposureModes << QCamera::ExposureTheatre;
+ else if (sceneMode == QLatin1String("sunset"))
+ m_supportedExposureModes << QCamera::ExposureSunset;
+ else if (sceneMode == QLatin1String("steadyphoto"))
+ m_supportedExposureModes << QCamera::ExposureSteadyPhoto;
+ else if (sceneMode == QLatin1String("fireworks"))
+ m_supportedExposureModes << QCamera::ExposureFireworks;
+ else if (sceneMode == QLatin1String("party"))
+ m_supportedExposureModes << QCamera::ExposureParty;
+ else if (sceneMode == QLatin1String("candlelight"))
+ m_supportedExposureModes << QCamera::ExposureCandlelight;
+ else if (sceneMode == QLatin1String("barcode"))
+ m_supportedExposureModes << QCamera::ExposureBarcode;
+ }
+ }
+
+ setExposureCompensation(exposureCompensation());
+ setExposureMode(exposureMode());
+
+ isFlashSupported = false;
+ isFlashAutoSupported = false;
+ isTorchSupported = false;
+
+ QStringList flashModes = m_cameraSession->camera()->getSupportedFlashModes();
+ for (int i = 0; i < flashModes.size(); ++i) {
+ const QString &flashMode = flashModes.at(i);
+ if (flashMode == QLatin1String("auto"))
+ isFlashAutoSupported = true;
+ else if (flashMode == QLatin1String("on"))
+ isFlashSupported = true;
+ else if (flashMode == QLatin1String("torch"))
+ isTorchSupported = true;
+ }
+
+ setFlashMode(flashMode());
+
+ m_supportedWhiteBalanceModes.clear();
+ QStringList whiteBalanceModes = m_cameraSession->camera()->getSupportedWhiteBalance();
+ for (int i = 0; i < whiteBalanceModes.size(); ++i) {
+ const QString &wb = whiteBalanceModes.at(i);
+ if (wb == QLatin1String("auto")) {
+ m_supportedWhiteBalanceModes.insert(QCamera::WhiteBalanceAuto,
+ QStringLiteral("auto"));
+ } else if (wb == QLatin1String("cloudy-daylight")) {
+ m_supportedWhiteBalanceModes.insert(QCamera::WhiteBalanceCloudy,
+ QStringLiteral("cloudy-daylight"));
+ } else if (wb == QLatin1String("daylight")) {
+ m_supportedWhiteBalanceModes.insert(QCamera::WhiteBalanceSunlight,
+ QStringLiteral("daylight"));
+ } else if (wb == QLatin1String("fluorescent")) {
+ m_supportedWhiteBalanceModes.insert(QCamera::WhiteBalanceFluorescent,
+ QStringLiteral("fluorescent"));
+ } else if (wb == QLatin1String("incandescent")) {
+ m_supportedWhiteBalanceModes.insert(QCamera::WhiteBalanceTungsten,
+ QStringLiteral("incandescent"));
+ } else if (wb == QLatin1String("shade")) {
+ m_supportedWhiteBalanceModes.insert(QCamera::WhiteBalanceShade,
+ QStringLiteral("shade"));
+ } else if (wb == QLatin1String("twilight")) {
+ m_supportedWhiteBalanceModes.insert(QCamera::WhiteBalanceSunset,
+ QStringLiteral("twilight"));
+ } else if (wb == QLatin1String("warm-fluorescent")) {
+ m_supportedWhiteBalanceModes.insert(QCamera::WhiteBalanceFlash,
+ QStringLiteral("warm-fluorescent"));
+ }
+ }
+
+}
+
+//void QAndroidCameraFocusControl::onCameraCaptureModeChanged()
+//{
+// if (m_cameraSession->camera() && m_focusMode == QCamera::FocusModeAudio) {
+// QString focusMode;
+// if ((m_cameraSession->captureMode().testFlag(QCamera::CaptureVideo) && m_continuousVideoFocusSupported)
+// || !m_continuousPictureFocusSupported) {
+// focusMode = QLatin1String("continuous-video");
+// } else {
+// focusMode = QLatin1String("continuous-picture");
+// }
+// m_cameraSession->camera()->setFocusMode(focusMode);
+// m_cameraSession->camera()->cancelAutoFocus();
+// }
+//}
+
+static QRect adjustedArea(const QRectF &area)
+{
+ // Qt maps focus points in the range (0.0, 0.0) -> (1.0, 1.0)
+ // Android maps focus points in the range (-1000, -1000) -> (1000, 1000)
+ // Converts an area in Qt coordinates to Android coordinates
+ return QRect(-1000 + qRound(area.x() * 2000),
+ -1000 + qRound(area.y() * 2000),
+ qRound(area.width() * 2000),
+ qRound(area.height() * 2000))
+ .intersected(QRect(-1000, -1000, 2000, 2000));
+}
+
+void QAndroidCamera::setCameraFocusArea()
+{
+ if (!m_cameraSession)
+ return;
+
+ QList<QRect> areas;
+ auto focusPoint = customFocusPoint();
+ if (QRectF(0., 0., 1., 1.).contains(focusPoint)) {
+ // in FocusPointAuto mode, leave the area list empty
+ // to let the driver choose the focus point.
+ QSize viewportSize = m_cameraSession->camera()->previewSize();
+
+ if (!viewportSize.isValid())
+ return;
+
+ // Set up a 50x50 pixel focus area around the focal point
+ QSizeF focusSize(50.f / viewportSize.width(), 50.f / viewportSize.height());
+ float x = qBound(qreal(0),
+ focusPoint.x() - (focusSize.width() / 2),
+ 1.f - focusSize.width());
+ float y = qBound(qreal(0),
+ focusPoint.y() - (focusSize.height() / 2),
+ 1.f - focusSize.height());
+
+ QRectF area(QPointF(x, y), focusSize);
+
+ areas.append(adjustedArea(area));
+ }
+ m_cameraSession->camera()->setFocusAreas(areas);
+}
+
+void QAndroidCamera::zoomTo(float factor, float rate)
+{
+ Q_UNUSED(rate);
+
+ if (zoomFactor() == factor)
+ return;
+
+ if (!m_cameraSession || !m_cameraSession->camera())
+ return;
+
+ factor = qBound(qreal(1), factor, maxZoomFactor());
+ int validZoomIndex = qt_findClosestValue(m_zoomRatios, qRound(factor * 100));
+ float newZoom = m_zoomRatios.at(validZoomIndex) / qreal(100);
+ m_cameraSession->camera()->setZoom(validZoomIndex);
+ zoomFactorChanged(newZoom);
+}
+
+void QAndroidCamera::setFlashMode(QCamera::FlashMode mode)
+{
+ if (!m_cameraSession || !m_cameraSession->camera())
+ return;
+
+ if (!isFlashModeSupported(mode))
+ return;
+
+ QString flashMode;
+ if (mode == QCamera::FlashAuto)
+ flashMode = QLatin1String("auto");
+ else if (mode == QCamera::FlashOn)
+ flashMode = QLatin1String("on");
+ else // FlashOff
+ flashMode = QLatin1String("off");
+
+ m_cameraSession->camera()->setFlashMode(flashMode);
+ flashModeChanged(mode);
+}
+
+bool QAndroidCamera::isFlashModeSupported(QCamera::FlashMode mode) const
+{
+ if (!m_cameraSession || !m_cameraSession->camera())
+ return false;
+ switch (mode) {
+ case QCamera::FlashOff:
+ return true;
+ case QCamera::FlashOn:
+ return isFlashSupported;
+ case QCamera::FlashAuto:
+ return isFlashAutoSupported;
+ }
+}
+
+bool QAndroidCamera::isFlashReady() const
+{
+ // Android doesn't have an API for that
+ return true;
+}
+
+void QAndroidCamera::setTorchMode(QCamera::TorchMode mode)
+{
+ if (!m_cameraSession)
+ return;
+ auto *camera = m_cameraSession->camera();
+ if (!camera || !isTorchSupported || mode == QCamera::TorchAuto)
+ return;
+
+ if (mode == QCamera::TorchOn) {
+ camera->setFlashMode(QLatin1String("torch"));
+ } else if (mode == QCamera::TorchOff) {
+ // if torch was enabled, it first needs to be turned off before restoring the flash mode
+ camera->setFlashMode(QLatin1String("off"));
+ setFlashMode(flashMode());
+ }
+ torchModeChanged(mode);
+}
+
+bool QAndroidCamera::isTorchModeSupported(QCamera::TorchMode mode) const
+{
+ if (!m_cameraSession || !m_cameraSession->camera())
+ return false;
+ switch (mode) {
+ case QCamera::TorchOff:
+ return true;
+ case QCamera::TorchOn:
+ return isTorchSupported;
+ case QCamera::TorchAuto:
+ return false;
+ }
+}
+
+void QAndroidCamera::setExposureMode(QCamera::ExposureMode mode)
+{
+ if (exposureMode() == mode)
+ return;
+
+ if (!m_cameraSession || !m_cameraSession->camera())
+ return;
+
+ if (!m_supportedExposureModes.contains(mode))
+ return;
+
+ QString sceneMode;
+ switch (mode) {
+ case QCamera::ExposureAuto:
+ sceneMode = QLatin1String("auto");
+ break;
+ case QCamera::ExposureSports:
+ sceneMode = QLatin1String("sports");
+ break;
+ case QCamera::ExposurePortrait:
+ sceneMode = QLatin1String("portrait");
+ break;
+ case QCamera::ExposureBeach:
+ sceneMode = QLatin1String("beach");
+ break;
+ case QCamera::ExposureSnow:
+ sceneMode = QLatin1String("snow");
+ break;
+ case QCamera::ExposureNight:
+ sceneMode = QLatin1String("night");
+ break;
+ case QCamera::ExposureAction:
+ sceneMode = QLatin1String("action");
+ break;
+ case QCamera::ExposureLandscape:
+ sceneMode = QLatin1String("landscape");
+ break;
+ case QCamera::ExposureNightPortrait:
+ sceneMode = QLatin1String("night-portrait");
+ break;
+ case QCamera::ExposureTheatre:
+ sceneMode = QLatin1String("theatre");
+ break;
+ case QCamera::ExposureSunset:
+ sceneMode = QLatin1String("sunset");
+ break;
+ case QCamera::ExposureSteadyPhoto:
+ sceneMode = QLatin1String("steadyphoto");
+ break;
+ case QCamera::ExposureFireworks:
+ sceneMode = QLatin1String("fireworks");
+ break;
+ case QCamera::ExposureParty:
+ sceneMode = QLatin1String("party");
+ break;
+ case QCamera::ExposureCandlelight:
+ sceneMode = QLatin1String("candlelight");
+ break;
+ case QCamera::ExposureBarcode:
+ sceneMode = QLatin1String("barcode");
+ break;
+ default:
+ sceneMode = QLatin1String("auto");
+ mode = QCamera::ExposureAuto;
+ break;
+ }
+
+ m_cameraSession->camera()->setSceneMode(sceneMode);
+ exposureModeChanged(mode);
+}
+
+bool QAndroidCamera::isExposureModeSupported(QCamera::ExposureMode mode) const
+{
+ return m_supportedExposureModes.contains(mode);
+}
+
+void QAndroidCamera::setExposureCompensation(float bias)
+{
+ if (exposureCompensation() == bias || !m_cameraSession || !m_cameraSession->camera())
+ return;
+
+ int biasIndex = qRound(bias / m_exposureCompensationStep);
+ biasIndex = qBound(m_minExposureCompensationIndex, biasIndex, m_maxExposureCompensationIndex);
+ float comp = biasIndex * m_exposureCompensationStep;
+ m_cameraSession->camera()->setExposureCompensation(biasIndex);
+ exposureCompensationChanged(comp);
+}
+
+bool QAndroidCamera::isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const
+{
+ return m_supportedWhiteBalanceModes.contains(mode);
+}
+
+void QAndroidCamera::setWhiteBalanceMode(QCamera::WhiteBalanceMode mode)
+{
+ if (!m_cameraSession)
+ return;
+ auto *camera = m_cameraSession->camera();
+ if (!camera)
+ return;
+ QString wb = m_supportedWhiteBalanceModes.value(mode, QString());
+ if (!wb.isEmpty()) {
+ camera->setWhiteBalance(wb);
+ whiteBalanceModeChanged(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
new file mode 100644
index 000000000..77bbc3133
--- /dev/null
+++ b/src/plugins/multimedia/android/mediacapture/qandroidcamera_p.h
@@ -0,0 +1,99 @@
+// 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
+#define QANDROIDCAMERACONTROL_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 <qmap.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCameraSession;
+class QAndroidCameraVideoRendererControl;
+class QAndroidMediaCaptureSession;
+
+class QAndroidCamera : public QPlatformCamera
+{
+ Q_OBJECT
+public:
+ explicit QAndroidCamera(QCamera *camera);
+ virtual ~QAndroidCamera();
+
+ 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 zoomTo(float factor, float rate) override;
+
+ void setFlashMode(QCamera::FlashMode mode) override;
+ bool isFlashModeSupported(QCamera::FlashMode mode) const override;
+ bool isFlashReady() 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;
+
+ bool isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const override;
+ void setWhiteBalanceMode(QCamera::WhiteBalanceMode mode) override;
+
+private Q_SLOTS:
+ void onCameraOpened();
+ void setCameraFocusArea();
+
+private:
+ void reactivateCameraSession();
+
+ QAndroidCameraSession *m_cameraSession = nullptr;
+ QAndroidMediaCaptureSession *m_service = nullptr;
+
+ QList<QCamera::FocusMode> m_supportedFocusModes;
+ bool m_continuousPictureFocusSupported = false;
+ bool m_continuousVideoFocusSupported = false;
+ bool m_focusPointSupported = false;
+
+ QList<int> m_zoomRatios;
+
+ QList<QCamera::ExposureMode> m_supportedExposureModes;
+ int m_minExposureCompensationIndex;
+ int m_maxExposureCompensationIndex;
+ qreal m_exposureCompensationStep;
+
+ bool isFlashSupported = false;
+ bool isFlashAutoSupported = false;
+ bool isTorchSupported = false;
+ bool isPendingSetActive = false;
+ QCameraDevice m_cameraDev;
+
+ QMap<QCamera::WhiteBalanceMode, QString> m_supportedWhiteBalanceModes;
+ QCameraFormat m_cameraFormat;
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAMERACONTROL_H
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidcamerasession.cpp b/src/plugins/multimedia/android/mediacapture/qandroidcamerasession.cpp
new file mode 100644
index 000000000..7eda1175f
--- /dev/null
+++ b/src/plugins/multimedia/android/mediacapture/qandroidcamerasession.cpp
@@ -0,0 +1,808 @@
+// 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"
+
+#include "androidcamera_p.h"
+#include "androidmultimediautils_p.h"
+#include "qandroidvideooutput_p.h"
+#include "qandroidmultimediautils_p.h"
+#include "androidmediarecorder_p.h"
+#include <qvideosink.h>
+#include <QtConcurrent/qtconcurrentrun.h>
+#include <qfile.h>
+#include <qguiapplication.h>
+#include <qscreen.h>
+#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>
+#include <private/qvideoframe_p.h>
+#include <QImageWriter>
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC(QList<QCameraDevice>, g_availableCameras)
+
+QAndroidCameraSession::QAndroidCameraSession(QObject *parent)
+ : QObject(parent)
+ , m_selectedCamera(0)
+ , m_camera(0)
+ , m_videoOutput(0)
+ , m_savedState(-1)
+ , m_previewStarted(false)
+ , m_readyForCapture(false)
+ , m_currentImageCaptureId(-1)
+ , m_previewCallback(0)
+ , m_keepActive(false)
+{
+ if (qApp) {
+ connect(qApp, &QGuiApplication::applicationStateChanged,
+ this, &QAndroidCameraSession::onApplicationStateChanged);
+
+ auto screen = qApp->primaryScreen();
+ if (screen) {
+ connect(screen, &QScreen::orientationChanged, this,
+ &QAndroidCameraSession::updateOrientation);
+ enableRotation();
+ }
+ }
+}
+
+QAndroidCameraSession::~QAndroidCameraSession()
+{
+ if (m_sink)
+ disconnect(m_retryPreviewConnection);
+ close();
+}
+
+//void QAndroidCameraSession::setCaptureMode(QCamera::CaptureModes mode)
+//{
+// if (m_captureMode == mode || !isCaptureModeSupported(mode))
+// return;
+
+// m_captureMode = mode;
+// emit captureModeChanged(m_captureMode);
+
+// if (m_previewStarted && m_captureMode.testFlag(QCamera::CaptureStillImage))
+// applyResolution(m_actualImageSettings.resolution());
+//}
+
+void QAndroidCameraSession::setActive(bool active)
+{
+ if (m_active == active)
+ return;
+
+ // If the application is inactive, the camera shouldn't be started. Save the desired state
+ // instead and it will be set when the application becomes active.
+ if (active && qApp->applicationState() == Qt::ApplicationInactive) {
+ m_isStateSaved = true;
+ m_savedState = active;
+ return;
+ }
+
+ m_isStateSaved = false;
+ m_active = active;
+ setActiveHelper(m_active);
+ emit activeChanged(m_active);
+}
+
+void QAndroidCameraSession::setActiveHelper(bool active)
+{
+ if (!active) {
+ stopPreview();
+ close();
+ } else {
+ if (!m_camera && !open()) {
+ emit error(QCamera::CameraError, QStringLiteral("Failed to open camera"));
+ return;
+ }
+ startPreview();
+ }
+}
+
+void QAndroidCameraSession::updateAvailableCameras()
+{
+ g_availableCameras->clear();
+
+ const int numCameras = AndroidCamera::getNumberOfCameras();
+ for (int i = 0; i < numCameras; ++i) {
+ QCameraDevicePrivate *info = new QCameraDevicePrivate;
+ AndroidCamera::getCameraInfo(i, info);
+
+ if (!info->id.isEmpty()) {
+ // Add supported picture and video sizes to the camera info
+ AndroidCamera *camera = AndroidCamera::open(i);
+
+ if (camera) {
+ info->videoFormats = camera->getSupportedFormats();
+ info->photoResolutions = camera->getSupportedPictureSizes();
+ }
+
+ delete camera;
+ g_availableCameras->append(info->create());
+ }
+ }
+}
+
+const QList<QCameraDevice> &QAndroidCameraSession::availableCameras()
+{
+ if (g_availableCameras->isEmpty())
+ updateAvailableCameras();
+
+ return *g_availableCameras;
+}
+
+bool QAndroidCameraSession::open()
+{
+ close();
+
+ m_camera = AndroidCamera::open(m_selectedCamera);
+
+ if (m_camera) {
+ connect(m_camera, &AndroidCamera::pictureExposed,
+ this, &QAndroidCameraSession::onCameraPictureExposed);
+ connect(m_camera, &AndroidCamera::lastPreviewFrameFetched,
+ this, &QAndroidCameraSession::onLastPreviewFrameFetched,
+ Qt::DirectConnection);
+ connect(m_camera, &AndroidCamera::newPreviewFrame,
+ this, &QAndroidCameraSession::onNewPreviewFrame,
+ Qt::DirectConnection);
+ connect(m_camera, &AndroidCamera::pictureCaptured,
+ this, &QAndroidCameraSession::onCameraPictureCaptured);
+ connect(m_camera, &AndroidCamera::previewStarted,
+ this, &QAndroidCameraSession::onCameraPreviewStarted);
+ connect(m_camera, &AndroidCamera::previewStopped,
+ this, &QAndroidCameraSession::onCameraPreviewStopped);
+ connect(m_camera, &AndroidCamera::previewFailedToStart,
+ this, &QAndroidCameraSession::onCameraPreviewFailedToStart);
+ connect(m_camera, &AndroidCamera::takePictureFailed,
+ this, &QAndroidCameraSession::onCameraTakePictureFailed);
+
+ if (m_camera->getPreviewFormat() != AndroidCamera::NV21)
+ m_camera->setPreviewFormat(AndroidCamera::NV21);
+
+ m_camera->notifyNewFrames(m_previewCallback);
+
+ emit opened();
+ setActive(true);
+ }
+
+ return m_camera != 0;
+}
+
+void QAndroidCameraSession::close()
+{
+ if (!m_camera)
+ return;
+
+ stopPreview();
+
+ m_readyForCapture = false;
+ m_currentImageCaptureId = -1;
+ m_currentImageCaptureFileName.clear();
+ m_actualImageSettings = m_requestedImageSettings;
+
+ m_camera->release();
+ delete m_camera;
+ m_camera = 0;
+
+ setActive(false);
+}
+
+void QAndroidCameraSession::setVideoOutput(QAndroidVideoOutput *output)
+{
+ if (m_videoOutput) {
+ m_videoOutput->stop();
+ m_videoOutput->reset();
+ }
+
+ if (output) {
+ m_videoOutput = output;
+ if (m_videoOutput->isReady()) {
+ onVideoOutputReady(true);
+ } else {
+ connect(m_videoOutput, &QAndroidVideoOutput::readyChanged,
+ this, &QAndroidCameraSession::onVideoOutputReady);
+ }
+ } else {
+ m_videoOutput = 0;
+ }
+}
+
+void QAndroidCameraSession::setCameraFormat(const QCameraFormat &format)
+{
+ m_requestedFpsRange.min = format.minFrameRate();
+ m_requestedFpsRange.max = format.maxFrameRate();
+ m_requestedPixelFromat = AndroidCamera::AndroidImageFormatFromQtPixelFormat(format.pixelFormat());
+
+ m_requestedImageSettings.setResolution(format.resolution());
+ m_actualImageSettings.setResolution(format.resolution());
+ if (m_readyForCapture)
+ applyResolution(m_actualImageSettings.resolution());
+}
+
+void QAndroidCameraSession::applyResolution(const QSize &captureSize, bool restartPreview)
+{
+ if (!m_camera)
+ return;
+
+ const QSize currentViewfinderResolution = m_camera->previewSize();
+ const AndroidCamera::ImageFormat currentPreviewFormat = m_camera->getPreviewFormat();
+ const AndroidCamera::FpsRange currentFpsRange = m_camera->getPreviewFpsRange();
+
+ // -- 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()) {
+ // According to the Android doc, if getPreferredPreviewSizeForVideo() returns null, it means
+ // the preview size cannot be different from the capture size
+ adjustedViewfinderResolution = captureSize;
+ } else {
+ qreal captureAspectRatio = 0;
+ if (validCaptureSize)
+ captureAspectRatio = qreal(captureSize.width()) / qreal(captureSize.height());
+
+ if (validCaptureSize) {
+ // search for viewfinder resolution with the same aspect ratio
+ qreal minAspectDiff = 1;
+ QSize closestResolution;
+ for (int i = previewSizes.count() - 1; i >= 0; --i) {
+ const QSize &size = previewSizes.at(i);
+ const qreal sizeAspect = qreal(size.width()) / size.height();
+ if (qFuzzyCompare(captureAspectRatio, sizeAspect)) {
+ adjustedViewfinderResolution = size;
+ break;
+ } else if (minAspectDiff > qAbs(sizeAspect - captureAspectRatio)) {
+ closestResolution = size;
+ minAspectDiff = qAbs(sizeAspect - captureAspectRatio);
+ }
+ }
+ if (!adjustedViewfinderResolution.isValid()) {
+ qWarning("Cannot find a viewfinder resolution matching the capture aspect ratio.");
+ if (closestResolution.isValid()) {
+ adjustedViewfinderResolution = closestResolution;
+ qWarning("Using closest viewfinder resolution.");
+ } else {
+ return;
+ }
+ }
+ } else {
+ adjustedViewfinderResolution = previewSizes.last();
+ }
+ }
+
+ // -- adjust pixel format
+
+ AndroidCamera::ImageFormat adjustedPreviewFormat = m_requestedPixelFromat;
+ if (adjustedPreviewFormat == AndroidCamera::UnknownImageFormat)
+ adjustedPreviewFormat = AndroidCamera::NV21;
+
+ // -- adjust FPS
+
+ 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 cameraOutputResolution = adjustedViewfinderResolution;
+ QSize videoOutputResolution = adjustedViewfinderResolution;
+ QSize currentVideoOutputResolution = m_videoOutput ? m_videoOutput->getVideoSize() : QSize(0, 0);
+ const int rotation = currentCameraRotation();
+ // 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 != cameraOutputResolution
+ || (m_videoOutput && currentVideoOutputResolution != videoOutputResolution)
+ || currentPreviewFormat != adjustedPreviewFormat || currentFpsRange.min != adjustedFps.min
+ || currentFpsRange.max != adjustedFps.max) {
+ if (m_videoOutput) {
+ 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(cameraOutputResolution);
+ m_camera->setPreviewFormat(adjustedPreviewFormat);
+ m_camera->setPreviewFpsRange(adjustedFps);
+
+ // restart preview
+ if (m_previewStarted && restartPreview)
+ m_camera->startPreview();
+ }
+}
+
+QList<QSize> QAndroidCameraSession::getSupportedPreviewSizes() const
+{
+ return m_camera ? m_camera->getSupportedPreviewSizes() : QList<QSize>();
+}
+
+QList<QVideoFrameFormat::PixelFormat> QAndroidCameraSession::getSupportedPixelFormats() const
+{
+ QList<QVideoFrameFormat::PixelFormat> formats;
+
+ if (!m_camera)
+ return formats;
+
+ const QList<AndroidCamera::ImageFormat> nativeFormats = m_camera->getSupportedPreviewFormats();
+
+ formats.reserve(nativeFormats.size());
+
+ for (AndroidCamera::ImageFormat nativeFormat : nativeFormats) {
+ QVideoFrameFormat::PixelFormat format = AndroidCamera::QtPixelFormatFromAndroidImageFormat(nativeFormat);
+ if (format != QVideoFrameFormat::Format_Invalid)
+ formats.append(format);
+ }
+
+ return formats;
+}
+
+QList<AndroidCamera::FpsRange> QAndroidCameraSession::getSupportedPreviewFpsRange() const
+{
+ return m_camera ? m_camera->getSupportedPreviewFpsRange() : QList<AndroidCamera::FpsRange>();
+}
+
+
+bool QAndroidCameraSession::startPreview()
+{
+ if (!m_camera || !m_videoOutput)
+ return false;
+
+ if (m_previewStarted)
+ return true;
+
+ if (!m_videoOutput->isReady())
+ return true; // delay starting until the video output is ready
+
+ Q_ASSERT(m_videoOutput->surfaceTexture() || m_videoOutput->surfaceHolder());
+
+ if ((m_videoOutput->surfaceTexture() && !m_camera->setPreviewTexture(m_videoOutput->surfaceTexture()))
+ || (m_videoOutput->surfaceHolder() && !m_camera->setPreviewDisplay(m_videoOutput->surfaceHolder())))
+ return false;
+
+ applyResolution(m_actualImageSettings.resolution());
+
+ AndroidMultimediaUtils::enableOrientationListener(true);
+
+ updateOrientation();
+ m_camera->startPreview();
+ m_previewStarted = true;
+ m_videoOutput->start();
+
+ return true;
+}
+
+QSize QAndroidCameraSession::getDefaultResolution() const
+{
+ const bool hasHighQualityProfile = AndroidCamcorderProfile::hasProfile(
+ m_camera->cameraId(),
+ AndroidCamcorderProfile::Quality(AndroidCamcorderProfile::QUALITY_HIGH));
+
+ if (hasHighQualityProfile) {
+ const AndroidCamcorderProfile camProfile = AndroidCamcorderProfile::get(
+ m_camera->cameraId(),
+ AndroidCamcorderProfile::Quality(AndroidCamcorderProfile::QUALITY_HIGH));
+
+ return QSize(camProfile.getValue(AndroidCamcorderProfile::videoFrameWidth),
+ camProfile.getValue(AndroidCamcorderProfile::videoFrameHeight));
+ }
+ return QSize();
+}
+
+void QAndroidCameraSession::stopPreview()
+{
+ if (!m_camera || !m_previewStarted)
+ return;
+
+ AndroidMultimediaUtils::enableOrientationListener(false);
+
+ m_camera->stopPreview();
+ m_camera->setPreviewSize(QSize());
+ m_camera->setPreviewTexture(0);
+ m_camera->setPreviewDisplay(0);
+
+ if (m_videoOutput) {
+ m_videoOutput->stop();
+ }
+ m_previewStarted = false;
+}
+
+void QAndroidCameraSession::setImageSettings(const QImageEncoderSettings &settings)
+{
+ if (m_requestedImageSettings == settings)
+ return;
+
+ m_requestedImageSettings = m_actualImageSettings = settings;
+
+ applyImageSettings();
+
+ if (m_readyForCapture)
+ applyResolution(m_actualImageSettings.resolution());
+}
+
+void QAndroidCameraSession::enableRotation()
+{
+ m_rotationEnabled = true;
+}
+
+void QAndroidCameraSession::disableRotation()
+{
+ m_rotationEnabled = false;
+}
+
+void QAndroidCameraSession::updateOrientation()
+{
+ if (!m_camera || !m_rotationEnabled)
+ return;
+
+ m_camera->setDisplayOrientation(currentCameraRotation());
+ applyResolution(m_actualImageSettings.resolution());
+}
+
+
+int QAndroidCameraSession::currentCameraRotation() const
+{
+ if (!m_camera)
+ return 0;
+
+ auto screen = QGuiApplication::primaryScreen();
+ auto screenOrientation = screen->orientation();
+ if (screenOrientation == Qt::PrimaryOrientation)
+ screenOrientation = screen->primaryOrientation();
+
+ int deviceOrientation = 0;
+ switch (screenOrientation) {
+ case Qt::PrimaryOrientation:
+ case Qt::PortraitOrientation:
+ break;
+ case Qt::LandscapeOrientation:
+ deviceOrientation = 90;
+ break;
+ case Qt::InvertedPortraitOrientation:
+ deviceOrientation = 180;
+ break;
+ case Qt::InvertedLandscapeOrientation:
+ deviceOrientation = 270;
+ break;
+ }
+
+ int nativeCameraOrientation = m_camera->getNativeOrientation();
+
+ int rotation;
+ // subtract natural camera orientation and physical device orientation
+ if (m_camera->getFacing() == AndroidCamera::CameraFacingFront) {
+ rotation = (nativeCameraOrientation + deviceOrientation) % 360;
+ rotation = (360 - rotation) % 360; // compensate the mirror
+ } else { // back-facing camera
+ rotation = (nativeCameraOrientation - deviceOrientation + 360) % 360;
+ }
+ return rotation;
+}
+
+void QAndroidCameraSession::setPreviewFormat(AndroidCamera::ImageFormat format)
+{
+ if (format == AndroidCamera::UnknownImageFormat)
+ return;
+
+ m_camera->setPreviewFormat(format);
+}
+
+void QAndroidCameraSession::setPreviewCallback(PreviewCallback *callback)
+{
+ m_videoFrameCallbackMutex.lock();
+ m_previewCallback = callback;
+ if (m_camera)
+ m_camera->notifyNewFrames(m_previewCallback);
+ m_videoFrameCallbackMutex.unlock();
+}
+
+void QAndroidCameraSession::applyImageSettings()
+{
+ if (!m_camera)
+ return;
+
+ // only supported format right now.
+ m_actualImageSettings.setFormat(QImageCapture::JPEG);
+
+ const QSize requestedResolution = m_requestedImageSettings.resolution();
+ const QList<QSize> supportedResolutions = m_camera->getSupportedPictureSizes();
+ if (!requestedResolution.isValid()) {
+ m_actualImageSettings.setResolution(getDefaultResolution());
+ } else if (!supportedResolutions.contains(requestedResolution)) {
+ // if the requested resolution is not supported, find the closest one
+ int reqPixelCount = requestedResolution.width() * requestedResolution.height();
+ QList<int> supportedPixelCounts;
+ for (int i = 0; i < supportedResolutions.size(); ++i) {
+ const QSize &s = supportedResolutions.at(i);
+ supportedPixelCounts.append(s.width() * s.height());
+ }
+ int closestIndex = qt_findClosestValue(supportedPixelCounts, reqPixelCount);
+ m_actualImageSettings.setResolution(supportedResolutions.at(closestIndex));
+ }
+ m_camera->setPictureSize(m_actualImageSettings.resolution());
+
+ int jpegQuality = 100;
+ switch (m_requestedImageSettings.quality()) {
+ case QImageCapture::VeryLowQuality:
+ jpegQuality = 20;
+ break;
+ case QImageCapture::LowQuality:
+ jpegQuality = 40;
+ break;
+ case QImageCapture::NormalQuality:
+ jpegQuality = 60;
+ break;
+ case QImageCapture::HighQuality:
+ jpegQuality = 80;
+ break;
+ case QImageCapture::VeryHighQuality:
+ jpegQuality = 100;
+ break;
+ }
+ m_camera->setJpegQuality(jpegQuality);
+}
+
+bool QAndroidCameraSession::isReadyForCapture() const
+{
+ return isActive() && m_readyForCapture;
+}
+
+void QAndroidCameraSession::setReadyForCapture(bool ready)
+{
+ if (m_readyForCapture == ready)
+ return;
+
+ m_readyForCapture = ready;
+ emit readyForCaptureChanged(ready);
+}
+
+int QAndroidCameraSession::captureImage()
+{
+ const int newImageCaptureId = m_currentImageCaptureId + 1;
+
+ if (!isReadyForCapture()) {
+ emit imageCaptureError(newImageCaptureId, QImageCapture::NotReadyError,
+ QPlatformImageCapture::msgCameraNotReady());
+ return newImageCaptureId;
+ }
+
+ setReadyForCapture(false);
+
+ m_currentImageCaptureId = newImageCaptureId;
+
+ applyResolution(m_actualImageSettings.resolution());
+ m_camera->takePicture();
+
+ return m_currentImageCaptureId;
+}
+
+int QAndroidCameraSession::capture(const QString &fileName)
+{
+ m_currentImageCaptureFileName = fileName;
+ m_imageCaptureToBuffer = false;
+ return captureImage();
+}
+
+int QAndroidCameraSession::captureToBuffer()
+{
+ m_currentImageCaptureFileName.clear();
+ m_imageCaptureToBuffer = true;
+ return captureImage();
+}
+
+void QAndroidCameraSession::onCameraTakePictureFailed()
+{
+ emit imageCaptureError(m_currentImageCaptureId, QImageCapture::ResourceError,
+ tr("Failed to capture image"));
+
+ // Preview needs to be restarted and the preview call back must be setup again
+ m_camera->startPreview();
+}
+
+void QAndroidCameraSession::onCameraPictureExposed()
+{
+ if (!m_camera)
+ return;
+
+ emit imageExposed(m_currentImageCaptureId);
+ m_camera->fetchLastPreviewFrame();
+}
+
+void QAndroidCameraSession::onLastPreviewFrameFetched(const QVideoFrame &frame)
+{
+ if (!m_camera)
+ return;
+
+ updateOrientation();
+
+ (void)QtConcurrent::run(&QAndroidCameraSession::processPreviewImage, this,
+ m_currentImageCaptureId, frame, currentCameraRotation());
+}
+
+void QAndroidCameraSession::processPreviewImage(int id, const QVideoFrame &frame, int rotation)
+{
+ // Preview display of front-facing cameras is flipped horizontally, but the frame data
+ // we get here is not. Flip it ourselves if the camera is front-facing to match what the user
+ // sees on the viewfinder.
+ QTransform transform;
+ transform.rotate(rotation);
+
+ if (m_camera->getFacing() == AndroidCamera::CameraFacingFront)
+ transform.scale(-1, 1);
+
+ emit imageCaptured(id, frame.toImage().transformed(transform));
+}
+
+void QAndroidCameraSession::onNewPreviewFrame(const QVideoFrame &frame)
+{
+ if (!m_camera)
+ return;
+
+ m_videoFrameCallbackMutex.lock();
+
+ if (m_previewCallback)
+ m_previewCallback->onFrameAvailable(frame);
+
+ m_videoFrameCallbackMutex.unlock();
+}
+
+void QAndroidCameraSession::onCameraPictureCaptured(const QByteArray &bytes,
+ QVideoFrameFormat::PixelFormat format, QSize size,int bytesPerLine)
+{
+ 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)
+ m_camera->startPreview();
+}
+
+void QAndroidCameraSession::onCameraPreviewStarted()
+{
+ setReadyForCapture(true);
+}
+
+void QAndroidCameraSession::onCameraPreviewFailedToStart()
+{
+ if (isActive()) {
+ Q_EMIT error(QCamera::CameraError, tr("Camera preview failed to start."));
+
+ AndroidMultimediaUtils::enableOrientationListener(false);
+ m_camera->setPreviewSize(QSize());
+ m_camera->setPreviewTexture(0);
+ if (m_videoOutput) {
+ m_videoOutput->stop();
+ m_videoOutput->reset();
+ }
+ m_previewStarted = false;
+
+ setActive(false);
+ setReadyForCapture(false);
+ }
+}
+
+void QAndroidCameraSession::onCameraPreviewStopped()
+{
+ if (!m_previewStarted)
+ setActive(false);
+ setReadyForCapture(false);
+}
+
+void QAndroidCameraSession::processCapturedImage(int id, const QByteArray &bytes, const QString &fileName)
+{
+ const QString actualFileName = QMediaStorageLocation::generateFileName(
+ fileName, QStandardPaths::PicturesLocation, QLatin1String("jpg"));
+ 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;
+ }
+
+ 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 = QVideoFramePrivate::createFrame(
+ std::make_unique<QMemoryVideoBuffer>(bytes, bytesPerLine),
+ QVideoFrameFormat(size, format));
+ emit imageAvailable(id, frame);
+}
+
+void QAndroidCameraSession::onVideoOutputReady(bool ready)
+{
+ if (ready && m_active)
+ startPreview();
+}
+
+void QAndroidCameraSession::onApplicationStateChanged()
+{
+
+ switch (QGuiApplication::applicationState()) {
+ case Qt::ApplicationInactive:
+ if (!m_keepActive && m_active) {
+ m_savedState = m_active;
+ setActive(false);
+ m_isStateSaved = true;
+ }
+ break;
+ case Qt::ApplicationActive:
+ if (m_isStateSaved) {
+ setActive(m_savedState);
+ m_isStateSaved = false;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void QAndroidCameraSession::setKeepAlive(bool keepAlive)
+{
+ m_keepActive = keepAlive;
+}
+
+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(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
new file mode 100644
index 000000000..3b56d9c3b
--- /dev/null
+++ b/src/plugins/multimedia/android/mediacapture/qandroidcamerasession_p.h
@@ -0,0 +1,166 @@
+// 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
+
+//
+// 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 <qcamera.h>
+#include <QImageCapture>
+#include <QSet>
+#include <QMutex>
+#include <private/qplatformimagecapture_p.h>
+#include "androidcamera_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidVideoOutput;
+class QAndroidTextureVideoOutput ;
+class QVideoSink;
+
+class QAndroidCameraSession : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QAndroidCameraSession(QObject *parent = 0);
+ ~QAndroidCameraSession();
+
+ static const QList<QCameraDevice> &availableCameras();
+
+ void setSelectedCameraId(int cameraId) { m_selectedCamera = cameraId; }
+ int getSelectedCameraId() { return m_selectedCamera; }
+ AndroidCamera *camera() const { return m_camera; }
+
+ bool isActive() const { return m_active; }
+ void setActive(bool active);
+
+ void applyResolution(const QSize &captureSize = QSize(), bool restartPreview = true);
+
+ QAndroidVideoOutput *videoOutput() const { return m_videoOutput; }
+ void setVideoOutput(QAndroidVideoOutput *output);
+
+ void setCameraFormat(const QCameraFormat &format);
+
+ QList<QSize> getSupportedPreviewSizes() const;
+ QList<QVideoFrameFormat::PixelFormat> getSupportedPixelFormats() const;
+ QList<AndroidCamera::FpsRange> getSupportedPreviewFpsRange() const;
+
+ QImageEncoderSettings imageSettings() const { return m_actualImageSettings; }
+ void setImageSettings(const QImageEncoderSettings &settings);
+
+ bool isReadyForCapture() const;
+ void setReadyForCapture(bool ready);
+ int capture(const QString &fileName);
+ int captureToBuffer();
+
+ int currentCameraRotation() const;
+
+ void setPreviewFormat(AndroidCamera::ImageFormat format);
+
+ struct PreviewCallback
+ {
+ virtual void onFrameAvailable(const QVideoFrame &frame) = 0;
+ };
+ void setPreviewCallback(PreviewCallback *callback);
+
+ void setVideoSink(QVideoSink *surface);
+
+ void disableRotation();
+ void enableRotation();
+
+ void setKeepAlive(bool keepAlive);
+
+Q_SIGNALS:
+ void activeChanged(bool);
+ void error(int error, const QString &errorString);
+ void opened();
+
+ void readyForCaptureChanged(bool);
+ void imageExposed(int id);
+ void imageCaptured(int id, const QImage &preview);
+ void imageMetadataAvailable(int id, const QMediaMetaData &key);
+ void imageAvailable(int id, const QVideoFrame &buffer);
+ void imageSaved(int id, const QString &fileName);
+ void imageCaptureError(int id, int error, const QString &errorString);
+
+private Q_SLOTS:
+ void onVideoOutputReady(bool ready);
+ void updateOrientation();
+
+ void onApplicationStateChanged();
+
+ void onCameraTakePictureFailed();
+ void onCameraPictureExposed();
+ void onCameraPictureCaptured(const QByteArray &bytes, QVideoFrameFormat::PixelFormat format, QSize size, int bytesPerLine);
+ void onLastPreviewFrameFetched(const QVideoFrame &frame);
+ void onNewPreviewFrame(const QVideoFrame &frame);
+ void onCameraPreviewStarted();
+ void onCameraPreviewFailedToStart();
+ void onCameraPreviewStopped();
+
+private:
+ static void updateAvailableCameras();
+
+ bool open();
+ void close();
+
+ bool startPreview();
+ void stopPreview();
+
+ void applyImageSettings();
+
+ void processPreviewImage(int id, const QVideoFrame &frame, int rotation);
+ 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);
+
+ int captureImage();
+
+ QSize getDefaultResolution() const;
+
+ int m_selectedCamera;
+ AndroidCamera *m_camera;
+ QAndroidVideoOutput *m_videoOutput;
+
+ bool m_active = false;
+ bool m_isStateSaved = false;
+ bool m_savedState = false;
+ bool m_previewStarted;
+
+ bool m_rotationEnabled = false;
+
+ QVideoSink *m_sink = nullptr;
+ QAndroidTextureVideoOutput *m_textureOutput = nullptr;
+
+ QImageEncoderSettings m_requestedImageSettings;
+ QImageEncoderSettings m_actualImageSettings;
+ AndroidCamera::FpsRange m_requestedFpsRange;
+ AndroidCamera::ImageFormat m_requestedPixelFromat = AndroidCamera::ImageFormat::NV21;
+
+ bool m_readyForCapture;
+ int m_currentImageCaptureId;
+ QString m_currentImageCaptureFileName;
+ bool m_imageCaptureToBuffer;
+
+ QMutex m_videoFrameCallbackMutex;
+ PreviewCallback *m_previewCallback;
+ bool m_keepActive;
+ QMetaObject::Connection m_retryPreviewConnection;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAMERASESSION_H
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidcapturesession.cpp b/src/plugins/multimedia/android/mediacapture/qandroidcapturesession.cpp
new file mode 100644
index 000000000..3b005e4a5
--- /dev/null
+++ b/src/plugins/multimedia/android/mediacapture/qandroidcapturesession.cpp
@@ -0,0 +1,473 @@
+// 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"
+#include "qandroidvideooutput_p.h"
+#include "qandroidglobal_p.h"
+#include <private/qplatformaudioinput_p.h>
+#include <private/qplatformaudiooutput_p.h>
+#include <private/qmediarecorder_p.h>
+#include <private/qmediastoragelocation_p.h>
+#include <QtCore/qmimetype.h>
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+QAndroidCaptureSession::QAndroidCaptureSession()
+ : QObject()
+ , m_mediaRecorder(0)
+ , m_cameraSession(0)
+ , m_duration(0)
+ , m_state(QMediaRecorder::StoppedState)
+ , m_outputFormat(AndroidMediaRecorder::DefaultOutputFormat)
+ , m_audioEncoder(AndroidMediaRecorder::DefaultAudioEncoder)
+ , m_videoEncoder(AndroidMediaRecorder::DefaultVideoEncoder)
+{
+ m_notifyTimer.setInterval(1000);
+ connect(&m_notifyTimer, &QTimer::timeout, this, &QAndroidCaptureSession::updateDuration);
+}
+
+QAndroidCaptureSession::~QAndroidCaptureSession()
+{
+ stop();
+ m_mediaRecorder = nullptr;
+ if (m_audioInput && m_audioOutput)
+ AndroidMediaPlayer::stopSoundStreaming();
+}
+
+void QAndroidCaptureSession::setCameraSession(QAndroidCameraSession *cameraSession)
+{
+ if (m_cameraSession) {
+ disconnect(m_connOpenCamera);
+ disconnect(m_connActiveChangedCamera);
+ }
+
+ m_cameraSession = cameraSession;
+ if (m_cameraSession) {
+ m_connOpenCamera = connect(cameraSession, &QAndroidCameraSession::opened,
+ this, &QAndroidCaptureSession::onCameraOpened);
+ m_connActiveChangedCamera = connect(cameraSession, &QAndroidCameraSession::activeChanged,
+ this, [this](bool isActive) {
+ if (!isActive)
+ stop();
+ });
+ }
+}
+
+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)
+{
+ if (m_audioOutput == output)
+ return;
+
+ if (m_audioOutput)
+ disconnect(m_audioOutputChanged);
+
+ m_audioOutput = output;
+
+ 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
+{
+ return m_state;
+}
+
+void QAndroidCaptureSession::setKeepAlive(bool keepAlive)
+{
+ if (m_cameraSession)
+ m_cameraSession->setKeepAlive(keepAlive);
+}
+
+
+void QAndroidCaptureSession::start(QMediaEncoderSettings &settings, const QUrl &outputLocation)
+{
+ if (m_state == QMediaRecorder::RecordingState)
+ return;
+
+ if (!m_cameraSession && !m_audioInput) {
+ updateError(QMediaRecorder::ResourceError, QLatin1String("No devices are set"));
+ return;
+ }
+
+ setKeepAlive(true);
+
+ const bool validCameraSession = m_cameraSession && m_cameraSession->camera();
+
+ if (validCameraSession && !qt_androidCheckCameraPermission()) {
+ updateError(QMediaRecorder::ResourceError, QLatin1String("Camera permission denied."));
+ setKeepAlive(false);
+ return;
+ }
+
+ if (m_audioInput && !qt_androidCheckMicrophonePermission()) {
+ updateError(QMediaRecorder::ResourceError, QLatin1String("Microphone permission denied."));
+ setKeepAlive(false);
+ return;
+ }
+
+ m_mediaRecorder = std::make_shared<AndroidMediaRecorder>();
+ connect(m_mediaRecorder.get(), &AndroidMediaRecorder::error, this,
+ &QAndroidCaptureSession::onError);
+ connect(m_mediaRecorder.get(), &AndroidMediaRecorder::info, this,
+ &QAndroidCaptureSession::onInfo);
+
+ applySettings(settings);
+
+ // Set audio/video sources
+ if (validCameraSession) {
+ m_cameraSession->camera()->stopPreviewSynchronous();
+ m_cameraSession->camera()->unlock();
+
+ m_mediaRecorder->setCamera(m_cameraSession->camera());
+ m_mediaRecorder->setVideoSource(AndroidMediaRecorder::Camera);
+ }
+
+ if (m_audioInput) {
+ m_mediaRecorder->setAudioInput(m_audioInput->device.id());
+ if (!m_mediaRecorder->isAudioSourceSet())
+ m_mediaRecorder->setAudioSource(AndroidMediaRecorder::DefaultAudioSource);
+ }
+
+ // Set output format
+ m_mediaRecorder->setOutputFormat(m_outputFormat);
+
+ // Set video encoder settings
+ if (validCameraSession) {
+ m_mediaRecorder->setVideoSize(settings.videoResolution());
+ m_mediaRecorder->setVideoFrameRate(qRound(settings.videoFrameRate()));
+ m_mediaRecorder->setVideoEncodingBitRate(settings.videoBitRate());
+ m_mediaRecorder->setVideoEncoder(m_videoEncoder);
+
+ // media recorder is also compensanting the mirror on front camera
+ auto rotation = m_cameraSession->currentCameraRotation();
+ if (m_cameraSession->camera()->getFacing() == AndroidCamera::CameraFacingFront)
+ rotation = (360 - rotation) % 360; // remove mirror compensation
+
+ m_mediaRecorder->setOrientationHint(rotation);
+ }
+
+ // Set audio encoder settings
+ if (m_audioInput) {
+ m_mediaRecorder->setAudioChannels(settings.audioChannelCount());
+ m_mediaRecorder->setAudioEncodingBitRate(settings.audioBitRate());
+ m_mediaRecorder->setAudioSamplingRate(settings.audioSampleRate());
+ m_mediaRecorder->setAudioEncoder(m_audioEncoder);
+ }
+
+ QString extension = settings.mimeType().preferredSuffix();
+ // Set output file
+ auto location = outputLocation.toString(QUrl::PreferLocalFile);
+ QString filePath = location;
+ if (QUrl(filePath).scheme() != QLatin1String("content")) {
+ filePath = QMediaStorageLocation::generateFileName(
+ location, m_cameraSession ? QStandardPaths::MoviesLocation
+ : QStandardPaths::MusicLocation, extension);
+ }
+
+ m_usedOutputLocation = QUrl::fromLocalFile(filePath);
+ m_outputLocationIsStandard = location.isEmpty() || QFileInfo(location).isRelative();
+ m_mediaRecorder->setOutputFile(filePath);
+
+ if (validCameraSession) {
+ m_cameraSession->disableRotation();
+ }
+
+ if (!m_mediaRecorder->prepare()) {
+ updateError(QMediaRecorder::FormatError,
+ QLatin1String("Unable to prepare the media recorder."));
+ restartViewfinder();
+
+ return;
+ }
+
+ if (!m_mediaRecorder->start()) {
+ updateError(QMediaRecorder::FormatError, QMediaRecorderPrivate::msgFailedStartRecording());
+ restartViewfinder();
+
+ return;
+ }
+
+ m_elapsedTime.start();
+ m_notifyTimer.start();
+ updateDuration();
+
+ if (validCameraSession) {
+ m_cameraSession->setReadyForCapture(false);
+
+ // Preview frame callback is cleared when setting up the camera with the media recorder.
+ // We need to reset it.
+ m_cameraSession->camera()->setupPreviewFrameCallback();
+ }
+
+ m_state = QMediaRecorder::RecordingState;
+ emit stateChanged(m_state);
+}
+
+void QAndroidCaptureSession::stop(bool error)
+{
+ if (m_state == QMediaRecorder::StoppedState || m_mediaRecorder == nullptr)
+ return;
+
+ m_mediaRecorder->stop();
+ m_notifyTimer.stop();
+ updateDuration();
+ m_elapsedTime.invalidate();
+
+ m_mediaRecorder = nullptr;
+
+ if (m_cameraSession && m_cameraSession->isActive()) {
+ // Viewport needs to be restarted after recording
+ restartViewfinder();
+ }
+
+ if (!error) {
+ // if the media is saved into the standard media location, register it
+ // with the Android media scanner so it appears immediately in apps
+ // such as the gallery.
+ if (m_outputLocationIsStandard)
+ AndroidMultimediaUtils::registerMediaFile(m_usedOutputLocation.toLocalFile());
+
+ emit actualLocationChanged(m_usedOutputLocation);
+ }
+
+ m_state = QMediaRecorder::StoppedState;
+ emit stateChanged(m_state);
+}
+
+qint64 QAndroidCaptureSession::duration() const
+{
+ return m_duration;
+}
+
+void QAndroidCaptureSession::applySettings(QMediaEncoderSettings &settings)
+{
+ // container settings
+ auto fileFormat = settings.mediaFormat().fileFormat();
+ if (!m_cameraSession && fileFormat == QMediaFormat::AAC) {
+ m_outputFormat = AndroidMediaRecorder::AAC_ADTS;
+ } else if (fileFormat == QMediaFormat::Ogg) {
+ m_outputFormat = AndroidMediaRecorder::OGG;
+ } else if (fileFormat == QMediaFormat::WebM) {
+ m_outputFormat = AndroidMediaRecorder::WEBM;
+// } else if (fileFormat == QLatin1String("3gp")) {
+// m_outputFormat = AndroidMediaRecorder::THREE_GPP;
+ } else {
+ // fallback to MP4
+ m_outputFormat = AndroidMediaRecorder::MPEG_4;
+ }
+
+ // audio settings
+ if (settings.audioChannelCount() <= 0)
+ settings.setAudioChannelCount(m_defaultSettings.audioChannels);
+ if (settings.audioBitRate() <= 0)
+ settings.setAudioBitRate(m_defaultSettings.audioBitRate);
+ if (settings.audioSampleRate() <= 0)
+ settings.setAudioSampleRate(m_defaultSettings.audioSampleRate);
+
+ if (settings.audioCodec() == QMediaFormat::AudioCodec::AAC)
+ m_audioEncoder = AndroidMediaRecorder::AAC;
+ else if (settings.audioCodec() == QMediaFormat::AudioCodec::Opus)
+ m_audioEncoder = AndroidMediaRecorder::OPUS;
+ else if (settings.audioCodec() == QMediaFormat::AudioCodec::Vorbis)
+ m_audioEncoder = AndroidMediaRecorder::VORBIS;
+ else
+ m_audioEncoder = m_defaultSettings.audioEncoder;
+
+
+ // video settings
+ if (m_cameraSession && m_cameraSession->camera()) {
+ if (settings.videoResolution().isEmpty()) {
+ settings.setVideoResolution(m_defaultSettings.videoResolution);
+ } else if (!m_supportedResolutions.contains(settings.videoResolution())) {
+ // if the requested resolution is not supported, find the closest one
+ QSize reqSize = settings.videoResolution();
+ int reqPixelCount = reqSize.width() * reqSize.height();
+ QList<int> supportedPixelCounts;
+ for (int i = 0; i < m_supportedResolutions.size(); ++i) {
+ const QSize &s = m_supportedResolutions.at(i);
+ supportedPixelCounts.append(s.width() * s.height());
+ }
+ int closestIndex = qt_findClosestValue(supportedPixelCounts, reqPixelCount);
+ settings.setVideoResolution(m_supportedResolutions.at(closestIndex));
+ }
+
+ if (settings.videoFrameRate() <= 0)
+ settings.setVideoFrameRate(m_defaultSettings.videoFrameRate);
+ if (settings.videoBitRate() <= 0)
+ settings.setVideoBitRate(m_defaultSettings.videoBitRate);
+
+ if (settings.videoCodec() == QMediaFormat::VideoCodec::H264)
+ m_videoEncoder = AndroidMediaRecorder::H264;
+ else if (settings.videoCodec() == QMediaFormat::VideoCodec::H265)
+ m_videoEncoder = AndroidMediaRecorder::HEVC;
+ else if (settings.videoCodec() == QMediaFormat::VideoCodec::MPEG4)
+ m_videoEncoder = AndroidMediaRecorder::MPEG_4_SP;
+ else
+ m_videoEncoder = m_defaultSettings.videoEncoder;
+
+ }
+}
+
+void QAndroidCaptureSession::restartViewfinder()
+{
+
+ setKeepAlive(false);
+
+ if (!m_cameraSession)
+ return;
+
+ if (m_cameraSession && m_cameraSession->camera()) {
+ m_cameraSession->camera()->reconnect();
+ m_cameraSession->camera()->stopPreviewSynchronous();
+ m_cameraSession->camera()->startPreview();
+ m_cameraSession->setReadyForCapture(true);
+ m_cameraSession->enableRotation();
+ }
+
+ m_mediaRecorder = nullptr;
+}
+
+void QAndroidCaptureSession::updateDuration()
+{
+ if (m_elapsedTime.isValid())
+ m_duration = m_elapsedTime.elapsed();
+
+ emit durationChanged(m_duration);
+}
+
+void QAndroidCaptureSession::onCameraOpened()
+{
+ m_supportedResolutions.clear();
+ m_supportedFramerates.clear();
+
+ // get supported resolutions from predefined profiles
+ for (int i = 0; i < 8; ++i) {
+ CaptureProfile profile = getProfile(i);
+ if (!profile.isNull) {
+ if (i == AndroidCamcorderProfile::QUALITY_HIGH)
+ m_defaultSettings = profile;
+
+ if (!m_supportedResolutions.contains(profile.videoResolution))
+ m_supportedResolutions.append(profile.videoResolution);
+ if (!m_supportedFramerates.contains(profile.videoFrameRate))
+ m_supportedFramerates.append(profile.videoFrameRate);
+ }
+ }
+
+ 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)
+{
+ CaptureProfile profile;
+ const bool hasProfile = AndroidCamcorderProfile::hasProfile(m_cameraSession->camera()->cameraId(),
+ AndroidCamcorderProfile::Quality(id));
+
+ if (hasProfile) {
+ AndroidCamcorderProfile camProfile = AndroidCamcorderProfile::get(m_cameraSession->camera()->cameraId(),
+ AndroidCamcorderProfile::Quality(id));
+
+ profile.outputFormat = AndroidMediaRecorder::OutputFormat(camProfile.getValue(AndroidCamcorderProfile::fileFormat));
+ profile.audioEncoder = AndroidMediaRecorder::AudioEncoder(camProfile.getValue(AndroidCamcorderProfile::audioCodec));
+ profile.audioBitRate = camProfile.getValue(AndroidCamcorderProfile::audioBitRate);
+ profile.audioChannels = camProfile.getValue(AndroidCamcorderProfile::audioChannels);
+ profile.audioSampleRate = camProfile.getValue(AndroidCamcorderProfile::audioSampleRate);
+ profile.videoEncoder = AndroidMediaRecorder::VideoEncoder(camProfile.getValue(AndroidCamcorderProfile::videoCodec));
+ profile.videoBitRate = camProfile.getValue(AndroidCamcorderProfile::videoBitRate);
+ profile.videoFrameRate = camProfile.getValue(AndroidCamcorderProfile::videoFrameRate);
+ profile.videoResolution = QSize(camProfile.getValue(AndroidCamcorderProfile::videoFrameWidth),
+ camProfile.getValue(AndroidCamcorderProfile::videoFrameHeight));
+
+ if (profile.outputFormat == AndroidMediaRecorder::MPEG_4)
+ profile.outputFileExtension = QStringLiteral("mp4");
+ else if (profile.outputFormat == AndroidMediaRecorder::THREE_GPP)
+ profile.outputFileExtension = QStringLiteral("3gp");
+ else if (profile.outputFormat == AndroidMediaRecorder::AMR_NB_Format)
+ profile.outputFileExtension = QStringLiteral("amr");
+ else if (profile.outputFormat == AndroidMediaRecorder::AMR_WB_Format)
+ profile.outputFileExtension = QStringLiteral("awb");
+
+ profile.isNull = false;
+ }
+
+ return profile;
+}
+
+void QAndroidCaptureSession::onError(int what, int extra)
+{
+ Q_UNUSED(what);
+ Q_UNUSED(extra);
+ stop(true);
+ updateError(QMediaRecorder::ResourceError, QLatin1String("Unknown error."));
+}
+
+void QAndroidCaptureSession::onInfo(int what, int extra)
+{
+ Q_UNUSED(extra);
+ if (what == 800) {
+ // MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
+ stop();
+ updateError(QMediaRecorder::OutOfSpaceError, QLatin1String("Maximum duration reached."));
+ } else if (what == 801) {
+ // MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED
+ stop();
+ 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
new file mode 100644
index 000000000..161d47994
--- /dev/null
+++ b/src/plugins/multimedia/android/mediacapture/qandroidcapturesession_p.h
@@ -0,0 +1,158 @@
+// 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
+
+//
+// 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 <qmediarecorder.h>
+#include <qurl.h>
+#include <qelapsedtimer.h>
+#include <qtimer.h>
+#include "androidmediarecorder_p.h"
+#include "qandroidmediaencoder_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAudioInput;
+class QAndroidCameraSession;
+
+class QAndroidCaptureSession : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QAndroidCaptureSession();
+ ~QAndroidCaptureSession();
+
+ QList<QSize> supportedResolutions() const { return m_supportedResolutions; }
+ QList<qreal> supportedFrameRates() const { return m_supportedFramerates; }
+
+ void setCameraSession(QAndroidCameraSession *cameraSession = 0);
+ void setAudioInput(QPlatformAudioInput *input);
+ void setAudioOutput(QPlatformAudioOutput *output);
+
+ QMediaRecorder::RecorderState state() const;
+
+ void start(QMediaEncoderSettings &settings, const QUrl &outputLocation);
+ void stop(bool error = false);
+
+ qint64 duration() const;
+
+ QMediaEncoderSettings encoderSettings() { return m_encoderSettings; }
+
+ void setMediaEncoder(QAndroidMediaEncoder *encoder) { m_mediaEncoder = encoder; }
+
+ void stateChanged(QMediaRecorder::RecorderState state) {
+ if (m_mediaEncoder)
+ m_mediaEncoder->stateChanged(state);
+ }
+ void durationChanged(qint64 position)
+ {
+ if (m_mediaEncoder)
+ m_mediaEncoder->durationChanged(position);
+ }
+ void actualLocationChanged(const QUrl &location)
+ {
+ if (m_mediaEncoder)
+ m_mediaEncoder->actualLocationChanged(location);
+ }
+ void updateError(int error, const QString &errorString)
+ {
+ if (m_mediaEncoder)
+ m_mediaEncoder->updateError(QMediaRecorder::Error(error), errorString);
+ }
+
+private Q_SLOTS:
+ void updateDuration();
+ void onCameraOpened();
+
+ void onError(int what, int extra);
+ void onInfo(int what, int extra);
+
+private:
+ void applySettings(QMediaEncoderSettings &settings);
+
+ struct CaptureProfile {
+ AndroidMediaRecorder::OutputFormat outputFormat;
+ QString outputFileExtension;
+
+ AndroidMediaRecorder::AudioEncoder audioEncoder;
+ int audioBitRate;
+ int audioChannels;
+ int audioSampleRate;
+
+ AndroidMediaRecorder::VideoEncoder videoEncoder;
+ int videoBitRate;
+ int videoFrameRate;
+ QSize videoResolution;
+
+ bool isNull;
+
+ CaptureProfile()
+ : outputFormat(AndroidMediaRecorder::MPEG_4)
+ , outputFileExtension(QLatin1String("mp4"))
+ , audioEncoder(AndroidMediaRecorder::DefaultAudioEncoder)
+ , audioBitRate(128000)
+ , audioChannels(2)
+ , audioSampleRate(44100)
+ , videoEncoder(AndroidMediaRecorder::DefaultVideoEncoder)
+ , videoBitRate(1)
+ , videoFrameRate(-1)
+ , videoResolution(1280, 720)
+ , isNull(true)
+ { }
+ };
+
+ CaptureProfile getProfile(int id);
+
+ void restartViewfinder();
+ void updateStreamingState();
+
+ QAndroidMediaEncoder *m_mediaEncoder = nullptr;
+ std::shared_ptr<AndroidMediaRecorder> m_mediaRecorder;
+ QAndroidCameraSession *m_cameraSession;
+
+ QPlatformAudioInput *m_audioInput = nullptr;
+ QPlatformAudioOutput *m_audioOutput = nullptr;
+
+ QElapsedTimer m_elapsedTime;
+ QTimer m_notifyTimer;
+ qint64 m_duration;
+
+ QMediaRecorder::RecorderState m_state;
+ QUrl m_usedOutputLocation;
+ bool m_outputLocationIsStandard = false;
+
+ CaptureProfile m_defaultSettings;
+
+ QMediaEncoderSettings m_encoderSettings;
+ AndroidMediaRecorder::OutputFormat m_outputFormat;
+ AndroidMediaRecorder::AudioEncoder m_audioEncoder;
+ AndroidMediaRecorder::VideoEncoder m_videoEncoder;
+
+ QList<QSize> m_supportedResolutions;
+ QList<qreal> m_supportedFramerates;
+
+ QMetaObject::Connection m_audioInputChanged;
+ QMetaObject::Connection m_audioOutputChanged;
+ QMetaObject::Connection m_connOpenCamera;
+ QMetaObject::Connection m_connActiveChangedCamera;
+
+ void setKeepAlive(bool keepAlive);
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAPTURESESSION_H
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidimagecapture.cpp b/src/plugins/multimedia/android/mediacapture/qandroidimagecapture.cpp
new file mode 100644
index 000000000..4105851ed
--- /dev/null
+++ b/src/plugins/multimedia/android/mediacapture/qandroidimagecapture.cpp
@@ -0,0 +1,73 @@
+// 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"
+
+#include "qandroidcamerasession_p.h"
+#include "qandroidmediacapturesession_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QAndroidImageCapture::QAndroidImageCapture(QImageCapture *parent)
+ : QPlatformImageCapture(parent)
+{
+}
+
+bool QAndroidImageCapture::isReadyForCapture() const
+{
+ return m_session->isReadyForCapture();
+}
+
+int QAndroidImageCapture::capture(const QString &fileName)
+{
+ return m_session->capture(fileName);
+}
+
+int QAndroidImageCapture::captureToBuffer()
+{
+ return m_session->captureToBuffer();
+}
+
+QImageEncoderSettings QAndroidImageCapture::imageSettings() const
+{
+ return m_session->imageSettings();
+}
+
+void QAndroidImageCapture::setImageSettings(const QImageEncoderSettings &settings)
+{
+ m_session->setImageSettings(settings);
+}
+
+void QAndroidImageCapture::setCaptureSession(QPlatformMediaCaptureSession *session)
+{
+ QAndroidMediaCaptureSession *captureSession = static_cast<QAndroidMediaCaptureSession *>(session);
+ if (m_service == captureSession)
+ return;
+
+ m_service = captureSession;
+ if (!m_service) {
+ disconnect(m_session, nullptr, this, nullptr);
+ return;
+ }
+
+ m_session = m_service->cameraSession();
+ Q_ASSERT(m_session);
+
+ connect(m_session, &QAndroidCameraSession::readyForCaptureChanged,
+ this, &QAndroidImageCapture::readyForCaptureChanged);
+ connect(m_session, &QAndroidCameraSession::imageExposed,
+ this, &QAndroidImageCapture::imageExposed);
+ connect(m_session, &QAndroidCameraSession::imageCaptured,
+ this, &QAndroidImageCapture::imageCaptured);
+ connect(m_session, &QAndroidCameraSession::imageMetadataAvailable,
+ this, &QAndroidImageCapture::imageMetadataAvailable);
+ connect(m_session, &QAndroidCameraSession::imageAvailable,
+ this, &QAndroidImageCapture::imageAvailable);
+ connect(m_session, &QAndroidCameraSession::imageSaved,
+ this, &QAndroidImageCapture::imageSaved);
+ connect(m_session, &QAndroidCameraSession::imageCaptureError,
+ 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
new file mode 100644
index 000000000..ac273c195
--- /dev/null
+++ b/src/plugins/multimedia/android/mediacapture/qandroidimagecapture_p.h
@@ -0,0 +1,48 @@
+// 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
+
+//
+// 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/qplatformimagecapture_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCameraSession;
+class QAndroidMediaCaptureSession;
+
+class QAndroidImageCapture : public QPlatformImageCapture
+{
+ Q_OBJECT
+public:
+ explicit QAndroidImageCapture(QImageCapture *parent = nullptr);
+
+ bool isReadyForCapture() const override;
+
+ int capture(const QString &fileName) override;
+ int captureToBuffer() override;
+
+ QImageEncoderSettings imageSettings() const override;
+ void setImageSettings(const QImageEncoderSettings &settings) override;
+
+ void setCaptureSession(QPlatformMediaCaptureSession *session);
+
+private:
+ QAndroidCameraSession *m_session;
+ QAndroidMediaCaptureSession *m_service;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAMERAIMAGECAPTURECONTROL_H
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidmediacapturesession.cpp b/src/plugins/multimedia/android/mediacapture/qandroidmediacapturesession.cpp
new file mode 100644
index 000000000..e2b551d35
--- /dev/null
+++ b/src/plugins/multimedia/android/mediacapture/qandroidmediacapturesession.cpp
@@ -0,0 +1,115 @@
+// 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"
+
+#include "qandroidmediaencoder_p.h"
+#include "qandroidcapturesession_p.h"
+#include "qandroidcamera_p.h"
+#include "qandroidcamerasession_p.h"
+#include "qandroidimagecapture_p.h"
+#include "qmediadevices.h"
+#include "qaudiodevice.h"
+
+QT_BEGIN_NAMESPACE
+
+QAndroidMediaCaptureSession::QAndroidMediaCaptureSession()
+ : m_captureSession(new QAndroidCaptureSession())
+ , m_cameraSession(new QAndroidCameraSession())
+{
+}
+
+QAndroidMediaCaptureSession::~QAndroidMediaCaptureSession()
+{
+ delete m_captureSession;
+ delete m_cameraSession;
+}
+
+QPlatformCamera *QAndroidMediaCaptureSession::camera()
+{
+ return m_cameraControl;
+}
+
+void QAndroidMediaCaptureSession::setCamera(QPlatformCamera *camera)
+{
+ if (camera) {
+ m_captureSession->setCameraSession(m_cameraSession);
+ } else {
+ m_captureSession->setCameraSession(nullptr);
+ }
+
+ QAndroidCamera *control = static_cast<QAndroidCamera *>(camera);
+ if (m_cameraControl == control)
+ return;
+
+ if (m_cameraControl)
+ m_cameraControl->setCaptureSession(nullptr);
+
+ m_cameraControl = control;
+ if (m_cameraControl)
+ m_cameraControl->setCaptureSession(this);
+
+ emit cameraChanged();
+}
+
+QPlatformImageCapture *QAndroidMediaCaptureSession::imageCapture()
+{
+ return m_imageCaptureControl;
+}
+
+void QAndroidMediaCaptureSession::setImageCapture(QPlatformImageCapture *imageCapture)
+{
+ QAndroidImageCapture *control = static_cast<QAndroidImageCapture *>(imageCapture);
+ if (m_imageCaptureControl == control)
+ return;
+
+ if (m_imageCaptureControl)
+ m_imageCaptureControl->setCaptureSession(nullptr);
+
+ m_imageCaptureControl = control;
+ if (m_imageCaptureControl)
+ m_imageCaptureControl->setCaptureSession(this);
+}
+
+QPlatformMediaRecorder *QAndroidMediaCaptureSession::mediaRecorder()
+{
+ return m_encoder;
+}
+
+void QAndroidMediaCaptureSession::setMediaRecorder(QPlatformMediaRecorder *recorder)
+{
+ QAndroidMediaEncoder *control = static_cast<QAndroidMediaEncoder *>(recorder);
+
+ if (m_encoder == control)
+ return;
+
+ if (m_encoder)
+ m_encoder->setCaptureSession(nullptr);
+
+ m_encoder = control;
+ if (m_encoder)
+ m_encoder->setCaptureSession(this);
+
+ emit encoderChanged();
+
+}
+
+void QAndroidMediaCaptureSession::setAudioInput(QPlatformAudioInput *input)
+{
+ m_captureSession->setAudioInput(input);
+}
+
+void QAndroidMediaCaptureSession::setAudioOutput(QPlatformAudioOutput *output)
+{
+ m_captureSession->setAudioOutput(output);
+}
+
+void QAndroidMediaCaptureSession::setVideoPreview(QVideoSink *sink)
+{
+ m_cameraSession->setVideoSink(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
new file mode 100644
index 000000000..90c792c32
--- /dev/null
+++ b/src/plugins/multimedia/android/mediacapture/qandroidmediacapturesession_p.h
@@ -0,0 +1,66 @@
+// 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
+
+//
+// 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/qplatformmediacapture_p.h>
+#include <private/qplatformmediaintegration_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidMediaEncoder;
+class QAndroidCaptureSession;
+class QAndroidCamera;
+class QAndroidCameraSession;
+class QAndroidImageCapture;
+
+class QAndroidMediaCaptureSession : public QPlatformMediaCaptureSession
+{
+ Q_OBJECT
+
+public:
+ explicit QAndroidMediaCaptureSession();
+ virtual ~QAndroidMediaCaptureSession();
+
+ 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;
+
+ void setVideoPreview(QVideoSink *sink) override;
+
+ void setAudioOutput(QPlatformAudioOutput *output) override;
+
+ QAndroidCaptureSession *captureSession() const { return m_captureSession; }
+ QAndroidCameraSession *cameraSession() const { return m_cameraSession; }
+
+private:
+ QAndroidMediaEncoder *m_encoder = nullptr;
+ QAndroidCaptureSession *m_captureSession = nullptr;
+ QAndroidCamera *m_cameraControl = nullptr;
+ QAndroidCameraSession *m_cameraSession = nullptr;
+ QAndroidImageCapture *m_imageCaptureControl = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAPTURESERVICE_H
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidmediaencoder.cpp b/src/plugins/multimedia/android/mediacapture/qandroidmediaencoder.cpp
new file mode 100644
index 000000000..d3449312d
--- /dev/null
+++ b/src/plugins/multimedia/android/mediacapture/qandroidmediaencoder.cpp
@@ -0,0 +1,72 @@
+// 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"
+#include "qandroidcapturesession_p.h"
+#include "qandroidmediacapturesession_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QAndroidMediaEncoder::QAndroidMediaEncoder(QMediaRecorder *parent)
+ : QPlatformMediaRecorder(parent)
+{
+}
+
+bool QAndroidMediaEncoder::isLocationWritable(const QUrl &location) const
+{
+ return location.isValid()
+ && (location.isLocalFile() || location.isRelative());
+}
+
+QMediaRecorder::RecorderState QAndroidMediaEncoder::state() const
+{
+ return m_session ? m_session->state() : QMediaRecorder::StoppedState;
+}
+
+qint64 QAndroidMediaEncoder::duration() const
+{
+ return m_session ? m_session->duration() : 0;
+
+}
+
+void QAndroidMediaEncoder::record(QMediaEncoderSettings &settings)
+{
+ if (m_session)
+ m_session->start(settings, outputLocation());
+}
+
+void QAndroidMediaEncoder::stop()
+{
+ if (m_session)
+ m_session->stop();
+}
+
+void QAndroidMediaEncoder::setOutputLocation(const QUrl &location)
+{
+ if (location.isLocalFile()) {
+ qt_androidRequestWriteStoragePermission();
+ }
+ QPlatformMediaRecorder::setOutputLocation(location);
+}
+
+void QAndroidMediaEncoder::setCaptureSession(QPlatformMediaCaptureSession *session)
+{
+ QAndroidMediaCaptureSession *captureSession = static_cast<QAndroidMediaCaptureSession *>(session);
+ if (m_service == captureSession)
+ return;
+
+ if (m_service)
+ stop();
+ if (m_session)
+ m_session->setMediaEncoder(nullptr);
+
+ m_service = captureSession;
+ if (!m_service)
+ return;
+ m_session = m_service->captureSession();
+ Q_ASSERT(m_session);
+ m_session->setMediaEncoder(this);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidmediaencoder_p.h b/src/plugins/multimedia/android/mediacapture/qandroidmediaencoder_p.h
new file mode 100644
index 000000000..b46268449
--- /dev/null
+++ b/src/plugins/multimedia/android/mediacapture/qandroidmediaencoder_p.h
@@ -0,0 +1,50 @@
+// 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
+
+//
+// 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>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCaptureSession;
+class QAndroidMediaCaptureSession;
+
+class QAndroidMediaEncoder : public QPlatformMediaRecorder
+{
+public:
+ explicit QAndroidMediaEncoder(QMediaRecorder *parent);
+
+ bool isLocationWritable(const QUrl &location) const override;
+ QMediaRecorder::RecorderState state() const override;
+ qint64 duration() const override;
+
+ void setCaptureSession(QPlatformMediaCaptureSession *session);
+
+ void setOutputLocation(const QUrl &location) override;
+ void record(QMediaEncoderSettings &settings) override;
+ void stop() override;
+
+private:
+ friend class QAndroidCaptureSession;
+
+ QAndroidCaptureSession *m_session = nullptr;
+ QAndroidMediaCaptureSession *m_service = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMEDIAENCODER_H
diff --git a/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer.cpp b/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer.cpp
new file mode 100644
index 000000000..b257a8986
--- /dev/null
+++ b/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer.cpp
@@ -0,0 +1,999 @@
+// 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"
+#include "qandroidvideooutput_p.h"
+#include "qandroidmetadata_p.h"
+#include "qandroidaudiooutput_p.h"
+#include "qaudiooutput.h"
+
+#include <private/qplatformvideosink_p.h>
+#include <qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(lcMediaPlayer, "qt.multimedia.mediaplayer.android")
+
+class StateChangeNotifier
+{
+public:
+ StateChangeNotifier(QAndroidMediaPlayer *mp)
+ : mControl(mp)
+ , mPreviousState(mp->state())
+ , mPreviousMediaStatus(mp->mediaStatus())
+ {
+ ++mControl->mActiveStateChangeNotifiers;
+ }
+
+ ~StateChangeNotifier()
+ {
+ if (--mControl->mActiveStateChangeNotifiers)
+ return;
+
+ if (mPreviousMediaStatus != mControl->mediaStatus())
+ Q_EMIT mControl->mediaStatusChanged(mControl->mediaStatus());
+
+ if (mPreviousState != mControl->state())
+ Q_EMIT mControl->stateChanged(mControl->state());
+ }
+
+private:
+ QAndroidMediaPlayer *mControl;
+ QMediaPlayer::PlaybackState mPreviousState;
+ QMediaPlayer::MediaStatus mPreviousMediaStatus;
+};
+
+QAndroidMediaPlayer::QAndroidMediaPlayer(QMediaPlayer *parent)
+ : QPlatformMediaPlayer(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);
+ connect(mMediaPlayer, &AndroidMediaPlayer::error, this, &QAndroidMediaPlayer::onError);
+ connect(mMediaPlayer, &AndroidMediaPlayer::stateChanged, this,
+ &QAndroidMediaPlayer::onStateChanged);
+ connect(mMediaPlayer, &AndroidMediaPlayer::videoSizeChanged, this,
+ &QAndroidMediaPlayer::onVideoSizeChanged);
+ connect(mMediaPlayer, &AndroidMediaPlayer::progressChanged, this,
+ &QAndroidMediaPlayer::positionChanged);
+ connect(mMediaPlayer, &AndroidMediaPlayer::durationChanged, this,
+ &QAndroidMediaPlayer::durationChanged);
+ connect(mMediaPlayer, &AndroidMediaPlayer::tracksInfoChanged, this,
+ &QAndroidMediaPlayer::updateTrackInfo);
+}
+
+QAndroidMediaPlayer::~QAndroidMediaPlayer()
+{
+ if (m_videoSink)
+ disconnect(m_videoSink->platformVideoSink(), nullptr, this, nullptr);
+
+ mMediaPlayer->disconnect();
+ mMediaPlayer->release();
+ delete mMediaPlayer;
+}
+
+qint64 QAndroidMediaPlayer::duration() const
+{
+ if (mediaStatus() == QMediaPlayer::NoMedia)
+ return 0;
+
+ if ((mState & (AndroidMediaPlayer::Prepared
+ | AndroidMediaPlayer::Started
+ | AndroidMediaPlayer::Paused
+ | AndroidMediaPlayer::Stopped
+ | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
+ return 0;
+ }
+
+ return mMediaPlayer->getDuration();
+}
+
+qint64 QAndroidMediaPlayer::position() const
+{
+ if (mediaStatus() == QMediaPlayer::EndOfMedia)
+ return duration();
+
+ if ((mState & (AndroidMediaPlayer::Prepared
+ | AndroidMediaPlayer::Started
+ | AndroidMediaPlayer::Paused
+ | AndroidMediaPlayer::PlaybackCompleted))) {
+ return mMediaPlayer->getCurrentPosition();
+ }
+
+ return (mPendingPosition == -1) ? 0 : mPendingPosition;
+}
+
+void QAndroidMediaPlayer::setPosition(qint64 position)
+{
+ if (!isSeekable())
+ return;
+
+ const int seekPosition = (position > INT_MAX) ? INT_MAX : position;
+
+ qint64 currentPosition = mMediaPlayer->getCurrentPosition();
+ if (seekPosition == currentPosition) {
+ // update position - will send a new frame of this position
+ // for consistency with other platforms
+ mMediaPlayer->seekTo(seekPosition);
+ return;
+ }
+ StateChangeNotifier notifier(this);
+
+ if (mediaStatus() == QMediaPlayer::EndOfMedia)
+ setMediaStatus(QMediaPlayer::LoadedMedia);
+
+ if ((mState & (AndroidMediaPlayer::Prepared
+ | AndroidMediaPlayer::Started
+ | AndroidMediaPlayer::Paused
+ | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
+ mPendingPosition = seekPosition;
+ } else {
+ mMediaPlayer->seekTo(seekPosition);
+
+ if (mPendingPosition != -1) {
+ mPendingPosition = -1;
+ }
+ }
+
+ Q_EMIT positionChanged(seekPosition);
+}
+
+void QAndroidMediaPlayer::setVolume(float volume)
+{
+ if ((mState & (AndroidMediaPlayer::Idle
+ | AndroidMediaPlayer::Initialized
+ | AndroidMediaPlayer::Stopped
+ | AndroidMediaPlayer::Prepared
+ | AndroidMediaPlayer::Started
+ | AndroidMediaPlayer::Paused
+ | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
+ mPendingVolume = volume;
+ return;
+ }
+
+ mMediaPlayer->setVolume(qRound(volume*100.));
+ mPendingVolume = -1;
+}
+
+void QAndroidMediaPlayer::setMuted(bool muted)
+{
+ if ((mState & (AndroidMediaPlayer::Idle
+ | AndroidMediaPlayer::Initialized
+ | AndroidMediaPlayer::Stopped
+ | AndroidMediaPlayer::Prepared
+ | AndroidMediaPlayer::Started
+ | AndroidMediaPlayer::Paused
+ | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
+ mPendingMute = muted;
+ return;
+ }
+
+ mMediaPlayer->setMuted(muted);
+ mPendingMute = -1;
+}
+
+QMediaMetaData QAndroidMediaPlayer::metaData() const
+{
+ return QAndroidMetaData::extractMetadata(mMediaContent);
+}
+
+float QAndroidMediaPlayer::bufferProgress() const
+{
+ return mBufferFilled ? 1. : mBufferPercent;
+}
+
+bool QAndroidMediaPlayer::isAudioAvailable() const
+{
+ return mAudioAvailable;
+}
+
+bool QAndroidMediaPlayer::isVideoAvailable() const
+{
+ return mVideoAvailable;
+}
+
+QMediaTimeRange QAndroidMediaPlayer::availablePlaybackRanges() const
+{
+ return mAvailablePlaybackRange;
+}
+
+void QAndroidMediaPlayer::updateAvailablePlaybackRanges()
+{
+ if (mBuffering) {
+ const qint64 pos = position();
+ const qint64 end = (duration() / 100) * mBufferPercent;
+ mAvailablePlaybackRange.addInterval(pos, end);
+ } else if (isSeekable()) {
+ mAvailablePlaybackRange = QMediaTimeRange(0, duration());
+ } else {
+ mAvailablePlaybackRange = QMediaTimeRange();
+ }
+
+// #### Q_EMIT availablePlaybackRangesChanged(mAvailablePlaybackRange);
+}
+
+qreal QAndroidMediaPlayer::playbackRate() const
+{
+ return mCurrentPlaybackRate;
+}
+
+void QAndroidMediaPlayer::setPlaybackRate(qreal 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;
+ }
+
+ if (mMediaPlayer->setPlaybackRate(rate)) {
+ mCurrentPlaybackRate = rate;
+ Q_EMIT playbackRateChanged(rate);
+ }
+}
+
+QUrl QAndroidMediaPlayer::media() const
+{
+ return mMediaContent;
+}
+
+const QIODevice *QAndroidMediaPlayer::mediaStream() const
+{
+ return mMediaStream;
+}
+
+void QAndroidMediaPlayer::setMedia(const QUrl &mediaContent,
+ QIODevice *stream)
+{
+ StateChangeNotifier notifier(this);
+
+ mReloadingMedia = (mMediaContent == mediaContent) && !mPendingSetMedia;
+
+ if (!mReloadingMedia) {
+ mMediaContent = mediaContent;
+ mMediaStream = stream;
+ }
+
+ if (mediaContent.isEmpty()) {
+ setMediaStatus(QMediaPlayer::NoMedia);
+ } else {
+ if (mVideoOutput && !mVideoOutput->isReady()) {
+ // if a video output is set but the video texture is not ready, delay loading the media
+ // since it can cause problems on some hardware
+ mPendingSetMedia = true;
+ return;
+ }
+
+ if (mVideoSize.isValid() && mVideoOutput)
+ mVideoOutput->setVideoSize(mVideoSize);
+
+ if (mVideoOutput &&
+ (mMediaPlayer->display() == 0 || mVideoOutput->shouldTextureBeUpdated()))
+ mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
+ mMediaPlayer->setDataSource(QNetworkRequest(mediaContent));
+ mMediaPlayer->prepareAsync();
+
+ if (!mReloadingMedia)
+ setMediaStatus(QMediaPlayer::LoadingMedia);
+ }
+
+ resetBufferingProgress();
+
+ mReloadingMedia = false;
+}
+
+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) {
+ return;
+ }
+
+ if (mVideoOutput) {
+ delete mVideoOutput;
+ mVideoOutput = nullptr;
+ mMediaPlayer->setDisplay(nullptr);
+ }
+
+ mVideoOutput = new QAndroidTextureVideoOutput(sink, this);
+ connect(mVideoOutput, &QAndroidTextureVideoOutput::readyChanged, this,
+ &QAndroidMediaPlayer::onVideoOutputReady);
+ connect(mMediaPlayer, &AndroidMediaPlayer::timedTextChanged, mVideoOutput,
+ &QAndroidTextureVideoOutput::setSubtitle);
+
+ if (mVideoOutput->isReady())
+ mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
+
+ connect(m_videoSink->platformVideoSink(), &QPlatformVideoSink::rhiChanged, this, [&]()
+ { mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture()); });
+}
+
+void QAndroidMediaPlayer::setAudioOutput(QPlatformAudioOutput *output)
+{
+ if (m_audioOutput == output)
+ return;
+ if (m_audioOutput)
+ m_audioOutput->q->disconnect(this);
+ m_audioOutput = static_cast<QAndroidAudioOutput *>(output);
+ if (m_audioOutput) {
+ connect(m_audioOutput->q, &QAudioOutput::deviceChanged, this, &QAndroidMediaPlayer::updateAudioDevice);
+ connect(m_audioOutput->q, &QAudioOutput::volumeChanged, this, &QAndroidMediaPlayer::setVolume);
+ connect(m_audioOutput->q, &QAudioOutput::mutedChanged, this, &QAndroidMediaPlayer::setMuted);
+ updateAudioDevice();
+ }
+}
+
+void QAndroidMediaPlayer::updateAudioDevice()
+{
+ if (m_audioOutput)
+ mMediaPlayer->setAudioOutput(m_audioOutput->device.id());
+}
+
+void QAndroidMediaPlayer::play()
+{
+ StateChangeNotifier notifier(this);
+
+ resetCurrentLoop();
+
+ // We need to prepare the mediaplayer again.
+ if ((mState & AndroidMediaPlayer::Stopped) && !mMediaContent.isEmpty()) {
+ setMedia(mMediaContent, mMediaStream);
+ }
+
+ if (!mMediaContent.isEmpty())
+ stateChanged(QMediaPlayer::PlayingState);
+
+ if ((mState & (AndroidMediaPlayer::Prepared
+ | AndroidMediaPlayer::Started
+ | AndroidMediaPlayer::Paused
+ | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
+ mPendingState = QMediaPlayer::PlayingState;
+ return;
+ }
+
+ if (mVideoOutput)
+ mVideoOutput->start();
+
+ updateAudioDevice();
+
+ if (mHasPendingPlaybackRate) {
+ mHasPendingPlaybackRate = false;
+ if (mMediaPlayer->setPlaybackRate(mCurrentPlaybackRate))
+ return;
+ mCurrentPlaybackRate = mMediaPlayer->playbackRate();
+ Q_EMIT playbackRateChanged(mCurrentPlaybackRate);
+ }
+
+ mMediaPlayer->play();
+}
+
+void QAndroidMediaPlayer::pause()
+{
+ // cannot pause without media
+ if (mediaStatus() == QMediaPlayer::NoMedia)
+ return;
+
+ StateChangeNotifier notifier(this);
+
+ stateChanged(QMediaPlayer::PausedState);
+
+ if ((mState & (AndroidMediaPlayer::Started
+ | AndroidMediaPlayer::Paused
+ | AndroidMediaPlayer::PlaybackCompleted
+ | AndroidMediaPlayer::Prepared
+ | AndroidMediaPlayer::Stopped)) == 0) {
+ mPendingState = QMediaPlayer::PausedState;
+ return;
+ }
+
+ const qint64 currentPosition = mMediaPlayer->getCurrentPosition();
+ setPosition(currentPosition);
+
+ mMediaPlayer->pause();
+}
+
+void QAndroidMediaPlayer::stop()
+{
+ StateChangeNotifier notifier(this);
+
+ stateChanged(QMediaPlayer::StoppedState);
+
+ if ((mState & (AndroidMediaPlayer::Prepared
+ | AndroidMediaPlayer::Started
+ | AndroidMediaPlayer::Stopped
+ | AndroidMediaPlayer::Paused
+ | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
+ if ((mState & (AndroidMediaPlayer::Idle | AndroidMediaPlayer::Uninitialized | AndroidMediaPlayer::Error)) == 0)
+ mPendingState = QMediaPlayer::StoppedState;
+ return;
+ }
+
+ if (mCurrentPlaybackRate != 1.)
+ // Playback rate need to by reapplied
+ mHasPendingPlaybackRate = true;
+
+ if (mVideoOutput)
+ mVideoOutput->stop();
+
+ mMediaPlayer->stop();
+}
+
+void QAndroidMediaPlayer::onInfo(qint32 what, qint32 extra)
+{
+ StateChangeNotifier notifier(this);
+
+ Q_UNUSED(extra);
+ switch (what) {
+ case AndroidMediaPlayer::MEDIA_INFO_UNKNOWN:
+ break;
+ case AndroidMediaPlayer::MEDIA_INFO_VIDEO_TRACK_LAGGING:
+ // IGNORE
+ break;
+ case AndroidMediaPlayer::MEDIA_INFO_VIDEO_RENDERING_START:
+ break;
+ case AndroidMediaPlayer::MEDIA_INFO_BUFFERING_START:
+ mPendingState = state();
+ stateChanged(QMediaPlayer::PausedState);
+ setMediaStatus(QMediaPlayer::StalledMedia);
+ break;
+ case AndroidMediaPlayer::MEDIA_INFO_BUFFERING_END:
+ if (state() != QMediaPlayer::StoppedState)
+ flushPendingStates();
+ break;
+ case AndroidMediaPlayer::MEDIA_INFO_BAD_INTERLEAVING:
+ break;
+ case AndroidMediaPlayer::MEDIA_INFO_NOT_SEEKABLE:
+ seekableChanged(false);
+ break;
+ case AndroidMediaPlayer::MEDIA_INFO_METADATA_UPDATE:
+ Q_EMIT metaDataChanged();
+ break;
+ }
+}
+
+void QAndroidMediaPlayer::onError(qint32 what, qint32 extra)
+{
+ StateChangeNotifier notifier(this);
+
+ QString errorString;
+ QMediaPlayer::Error error = QMediaPlayer::ResourceError;
+
+ switch (what) {
+ case AndroidMediaPlayer::MEDIA_ERROR_UNKNOWN:
+ errorString = QLatin1String("Error:");
+ break;
+ case AndroidMediaPlayer::MEDIA_ERROR_SERVER_DIED:
+ errorString = QLatin1String("Error: Server died");
+ error = QMediaPlayer::ResourceError;
+ break;
+ case AndroidMediaPlayer::MEDIA_ERROR_INVALID_STATE:
+ errorString = QLatin1String("Error: Invalid state");
+ error = QMediaPlayer::ResourceError;
+ break;
+ }
+
+ switch (extra) {
+ case AndroidMediaPlayer::MEDIA_ERROR_IO: // Network OR file error
+ errorString += QLatin1String(" (I/O operation failed)");
+ error = QMediaPlayer::NetworkError;
+ setMediaStatus(QMediaPlayer::InvalidMedia);
+ break;
+ case AndroidMediaPlayer::MEDIA_ERROR_MALFORMED:
+ errorString += QLatin1String(" (Malformed bitstream)");
+ error = QMediaPlayer::FormatError;
+ setMediaStatus(QMediaPlayer::InvalidMedia);
+ break;
+ case AndroidMediaPlayer::MEDIA_ERROR_UNSUPPORTED:
+ errorString += QLatin1String(" (Unsupported media)");
+ error = QMediaPlayer::FormatError;
+ setMediaStatus(QMediaPlayer::InvalidMedia);
+ break;
+ case AndroidMediaPlayer::MEDIA_ERROR_TIMED_OUT:
+ errorString += QLatin1String(" (Timed out)");
+ break;
+ case AndroidMediaPlayer::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
+ errorString += QLatin1String(" (Unable to start progressive playback')");
+ error = QMediaPlayer::FormatError;
+ setMediaStatus(QMediaPlayer::InvalidMedia);
+ break;
+ case AndroidMediaPlayer::MEDIA_ERROR_BAD_THINGS_ARE_GOING_TO_HAPPEN:
+ 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;
+ }
+
+ Q_EMIT QPlatformMediaPlayer::error(error, errorString);
+}
+
+void QAndroidMediaPlayer::onBufferingChanged(qint32 percent)
+{
+ StateChangeNotifier notifier(this);
+
+ mBuffering = percent != 100;
+ mBufferPercent = percent;
+
+ updateAvailablePlaybackRanges();
+
+ if (state() != QMediaPlayer::StoppedState)
+ setMediaStatus(mBuffering ? QMediaPlayer::BufferingMedia : QMediaPlayer::BufferedMedia);
+
+ updateBufferStatus();
+}
+
+void QAndroidMediaPlayer::onVideoSizeChanged(qint32 width, qint32 height)
+{
+ QSize newSize(width, height);
+
+ if (width == 0 || height == 0 || newSize == mVideoSize)
+ return;
+
+ setVideoAvailable(true);
+ mVideoSize = newSize;
+
+ if (mVideoOutput)
+ mVideoOutput->setVideoSize(mVideoSize);
+}
+
+void QAndroidMediaPlayer::onStateChanged(qint32 state)
+{
+ // If reloading, don't report state changes unless the new state is Prepared or Error.
+ if ((mState & AndroidMediaPlayer::Stopped)
+ && (state & (AndroidMediaPlayer::Prepared | AndroidMediaPlayer::Error | AndroidMediaPlayer::Uninitialized)) == 0) {
+ return;
+ }
+
+ StateChangeNotifier notifier(this);
+
+ mState = state;
+ switch (mState) {
+ case AndroidMediaPlayer::Idle:
+ break;
+ case AndroidMediaPlayer::Initialized:
+ break;
+ case AndroidMediaPlayer::Preparing:
+ if (!mReloadingMedia)
+ setMediaStatus(QMediaPlayer::LoadingMedia);
+ break;
+ case AndroidMediaPlayer::Prepared:
+ setMediaStatus(QMediaPlayer::LoadedMedia);
+ if (mBuffering) {
+ setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
+ : QMediaPlayer::BufferingMedia);
+ } else {
+ onBufferingChanged(100);
+ }
+ setPosition(0);
+ Q_EMIT metaDataChanged();
+ setAudioAvailable(true);
+ flushPendingStates();
+ break;
+ case AndroidMediaPlayer::Started:
+ stateChanged(QMediaPlayer::PlayingState);
+ if (mBuffering) {
+ setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
+ : QMediaPlayer::BufferingMedia);
+ } else {
+ setMediaStatus(QMediaPlayer::BufferedMedia);
+ }
+ Q_EMIT positionChanged(position());
+ break;
+ case AndroidMediaPlayer::Paused:
+ stateChanged(QMediaPlayer::PausedState);
+ if (mediaStatus() == QMediaPlayer::EndOfMedia) {
+ setPosition(0);
+ setMediaStatus(QMediaPlayer::BufferedMedia);
+ } else {
+ Q_EMIT positionChanged(position());
+ }
+ break;
+ case AndroidMediaPlayer::Error:
+ stateChanged(QMediaPlayer::StoppedState);
+ setMediaStatus(QMediaPlayer::InvalidMedia);
+ mMediaPlayer->release();
+ Q_EMIT positionChanged(0);
+ break;
+ case AndroidMediaPlayer::Stopped:
+ stateChanged(QMediaPlayer::StoppedState);
+ setMediaStatus(QMediaPlayer::LoadedMedia);
+ Q_EMIT positionChanged(0);
+ break;
+ case AndroidMediaPlayer::PlaybackCompleted:
+ if (doLoop()) {
+ setPosition(0);
+ mMediaPlayer->play();
+ break;
+ }
+ stateChanged(QMediaPlayer::StoppedState);
+ setMediaStatus(QMediaPlayer::EndOfMedia);
+ break;
+ case AndroidMediaPlayer::Uninitialized:
+ // reset some properties (unless we reload the same media)
+ if (!mReloadingMedia) {
+ resetBufferingProgress();
+ mPendingPosition = -1;
+ mPendingSetMedia = false;
+ mPendingState = -1;
+
+ Q_EMIT durationChanged(0);
+ Q_EMIT positionChanged(0);
+
+ setAudioAvailable(false);
+ setVideoAvailable(false);
+ seekableChanged(true);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if ((mState & (AndroidMediaPlayer::Stopped | AndroidMediaPlayer::Uninitialized)) != 0) {
+ mMediaPlayer->setDisplay(0);
+ if (mVideoOutput) {
+ mVideoOutput->stop();
+ }
+ }
+}
+
+int QAndroidMediaPlayer::trackCount(TrackType trackType)
+{
+ if (!mTracksMetadata.contains(trackType))
+ return -1;
+
+ auto tracks = mTracksMetadata.value(trackType);
+ return tracks.count();
+}
+
+QMediaMetaData QAndroidMediaPlayer::trackMetaData(TrackType trackType, int streamNumber)
+{
+ if (!mTracksMetadata.contains(trackType))
+ return QMediaMetaData();
+
+ auto tracks = mTracksMetadata.value(trackType);
+ if (tracks.count() < streamNumber)
+ return QMediaMetaData();
+
+ QAndroidMetaData trackInfo = tracks.at(streamNumber);
+ return static_cast<QMediaMetaData>(trackInfo);
+}
+
+QPlatformMediaPlayer::TrackType convertTrackType(AndroidMediaPlayer::TrackType type)
+{
+ switch (type) {
+ case AndroidMediaPlayer::TrackType::Video:
+ return QPlatformMediaPlayer::TrackType::VideoStream;
+ case AndroidMediaPlayer::TrackType::Audio:
+ return QPlatformMediaPlayer::TrackType::AudioStream;
+ case AndroidMediaPlayer::TrackType::TimedText:
+ return QPlatformMediaPlayer::TrackType::SubtitleStream;
+ case AndroidMediaPlayer::TrackType::Subtitle:
+ return QPlatformMediaPlayer::TrackType::SubtitleStream;
+ case AndroidMediaPlayer::TrackType::Unknown:
+ case AndroidMediaPlayer::TrackType::Metadata:
+ return QPlatformMediaPlayer::TrackType::NTrackTypes;
+ }
+
+ return QPlatformMediaPlayer::TrackType::NTrackTypes;
+}
+
+int QAndroidMediaPlayer::convertTrackNumber(int androidTrackNumber)
+{
+ int trackNumber = androidTrackNumber;
+
+ int videoTrackCount = trackCount(QPlatformMediaPlayer::TrackType::VideoStream);
+ if (trackNumber <= videoTrackCount)
+ return trackNumber;
+
+ trackNumber = trackNumber - videoTrackCount;
+
+ int audioTrackCount = trackCount(QPlatformMediaPlayer::TrackType::AudioStream);
+ if (trackNumber <= audioTrackCount)
+ return trackNumber;
+
+ trackNumber = trackNumber - audioTrackCount;
+
+ auto subtitleTracks = mTracksMetadata.value(QPlatformMediaPlayer::TrackType::SubtitleStream);
+ int timedTextCount = 0;
+ int subtitleTextCount = 0;
+ for (const auto &track : subtitleTracks) {
+ if (track.androidTrackType() == 3) // 3 == TimedText
+ timedTextCount++;
+
+ if (track.androidTrackType() == 4) // 4 == Subtitle
+ subtitleTextCount++;
+ }
+
+ if (trackNumber <= timedTextCount)
+ return trackNumber;
+
+ trackNumber = trackNumber - timedTextCount;
+
+ if (trackNumber <= subtitleTextCount)
+ return trackNumber;
+
+ return -1;
+}
+
+int QAndroidMediaPlayer::activeTrack(TrackType trackType)
+{
+ int androidTrackNumber = -1;
+
+ switch (trackType) {
+ case QPlatformMediaPlayer::TrackType::VideoStream: {
+ if (!mIsVideoTrackEnabled)
+ return -1;
+ androidTrackNumber = mMediaPlayer->activeTrack(AndroidMediaPlayer::TrackType::Video);
+ }
+ case QPlatformMediaPlayer::TrackType::AudioStream: {
+ if (!mIsAudioTrackEnabled)
+ return -1;
+
+ androidTrackNumber = mMediaPlayer->activeTrack(AndroidMediaPlayer::TrackType::Audio);
+ }
+ case QPlatformMediaPlayer::TrackType::SubtitleStream: {
+ int timedTextSelectedTrack =
+ mMediaPlayer->activeTrack(AndroidMediaPlayer::TrackType::TimedText);
+
+ if (timedTextSelectedTrack > -1) {
+ androidTrackNumber = timedTextSelectedTrack;
+ break;
+ }
+
+ int subtitleSelectedTrack =
+ mMediaPlayer->activeTrack(AndroidMediaPlayer::TrackType::Subtitle);
+ if (subtitleSelectedTrack > -1) {
+ androidTrackNumber = subtitleSelectedTrack;
+ break;
+ }
+
+ return -1;
+ }
+ case QPlatformMediaPlayer::TrackType::NTrackTypes:
+ return -1;
+ }
+
+ return convertTrackNumber(androidTrackNumber);
+}
+
+void QAndroidMediaPlayer::disableTrack(TrackType trackType)
+{
+ const auto track = activeTrack(trackType);
+
+ switch (trackType) {
+ case VideoStream: {
+ if (track > -1) {
+ mMediaPlayer->setDisplay(nullptr);
+ mIsVideoTrackEnabled = false;
+ }
+ break;
+ }
+ case AudioStream: {
+ if (track > -1) {
+ mMediaPlayer->setMuted(true);
+ mMediaPlayer->blockAudio();
+ mIsAudioTrackEnabled = false;
+ }
+ break;
+ }
+ case SubtitleStream: {
+ // subtitles and timedtext tracks can be selected at the same time so deselect both
+ int subtitleSelectedTrack =
+ mMediaPlayer->activeTrack(AndroidMediaPlayer::TrackType::Subtitle);
+ if (subtitleSelectedTrack > -1)
+ mMediaPlayer->deselectTrack(subtitleSelectedTrack);
+
+ int timedTextSelectedTrack =
+ mMediaPlayer->activeTrack(AndroidMediaPlayer::TrackType::TimedText);
+ if (timedTextSelectedTrack > -1)
+ mMediaPlayer->deselectTrack(timedTextSelectedTrack);
+
+ break;
+ }
+ case NTrackTypes:
+ break;
+ }
+}
+
+void QAndroidMediaPlayer::setActiveTrack(TrackType trackType, int streamNumber)
+{
+
+ if (!mTracksMetadata.contains(trackType)) {
+ qCWarning(lcMediaPlayer)
+ << "Trying to set a active track which type has no available tracks.";
+ return;
+ }
+
+ const auto &tracks = mTracksMetadata.value(trackType);
+ if (streamNumber > tracks.count()) {
+ qCWarning(lcMediaPlayer) << "Trying to set a active track that does not exist.";
+ return;
+ }
+
+ // in case of < 0 deselect tracktype
+ if (streamNumber < 0) {
+ disableTrack(trackType);
+ return;
+ }
+
+ const auto currentTrack = activeTrack(trackType);
+ if (streamNumber == currentTrack) {
+ return;
+ }
+
+ if (trackType == TrackType::VideoStream && !mIsVideoTrackEnabled) {
+ // enable video stream
+ mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
+ mIsVideoTrackEnabled = true;
+ }
+
+ if (trackType == TrackType::AudioStream && !mIsAudioTrackEnabled) {
+ // enable audio stream
+ mMediaPlayer->unblockAudio();
+ mMediaPlayer->setMuted(false);
+ mIsAudioTrackEnabled = true;
+ }
+
+ if (trackType == TrackType::SubtitleStream) {
+ // subtitles and timedtext tracks can be selected at the same time so deselect both before
+ // selecting a new one
+ disableTrack(TrackType::SubtitleStream);
+ }
+
+ const auto &trackInfo = tracks.at(streamNumber);
+ const auto &trackNumber = trackInfo.androidTrackNumber();
+ mMediaPlayer->selectTrack(trackNumber);
+
+ emit activeTracksChanged();
+}
+
+void QAndroidMediaPlayer::positionChanged(qint64 position)
+{
+ QPlatformMediaPlayer::positionChanged(position);
+}
+
+void QAndroidMediaPlayer::durationChanged(qint64 duration)
+{
+ QPlatformMediaPlayer::durationChanged(duration);
+}
+
+void QAndroidMediaPlayer::onVideoOutputReady(bool ready)
+{
+ if ((mMediaPlayer->display() == 0) && mVideoOutput && ready)
+ mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
+
+ flushPendingStates();
+}
+
+void QAndroidMediaPlayer::setMediaStatus(QMediaPlayer::MediaStatus status)
+{
+ mediaStatusChanged(status);
+
+ if (status == QMediaPlayer::NoMedia || status == QMediaPlayer::InvalidMedia) {
+ Q_EMIT durationChanged(0);
+ Q_EMIT metaDataChanged();
+ setAudioAvailable(false);
+ setVideoAvailable(false);
+ }
+
+ if (status == QMediaPlayer::EndOfMedia)
+ Q_EMIT positionChanged(position());
+
+ updateBufferStatus();
+}
+
+void QAndroidMediaPlayer::setAudioAvailable(bool available)
+{
+ if (mAudioAvailable == available)
+ return;
+
+ mAudioAvailable = available;
+ Q_EMIT audioAvailableChanged(mAudioAvailable);
+}
+
+void QAndroidMediaPlayer::setVideoAvailable(bool available)
+{
+ if (mVideoAvailable == available)
+ return;
+
+ if (!available)
+ mVideoSize = QSize();
+
+ mVideoAvailable = available;
+ Q_EMIT videoAvailableChanged(mVideoAvailable);
+}
+
+void QAndroidMediaPlayer::resetBufferingProgress()
+{
+ mBuffering = false;
+ mBufferPercent = 0;
+ mAvailablePlaybackRange = QMediaTimeRange();
+}
+
+void QAndroidMediaPlayer::flushPendingStates()
+{
+ if (mPendingSetMedia) {
+ setMedia(mMediaContent, 0);
+ mPendingSetMedia = false;
+ return;
+ }
+
+ const int newState = mPendingState;
+ mPendingState = -1;
+
+ if (mPendingPosition != -1)
+ setPosition(mPendingPosition);
+ if (mPendingVolume >= 0)
+ setVolume(mPendingVolume);
+ if (mPendingMute != -1)
+ setMuted((mPendingMute == 1));
+
+ switch (newState) {
+ case QMediaPlayer::PlayingState:
+ play();
+ break;
+ case QMediaPlayer::PausedState:
+ pause();
+ break;
+ case QMediaPlayer::StoppedState:
+ stop();
+ break;
+ default:
+ break;
+ }
+}
+
+void QAndroidMediaPlayer::updateBufferStatus()
+{
+ const auto &status = mediaStatus();
+ bool bufferFilled = (status == QMediaPlayer::BufferedMedia || status == QMediaPlayer::BufferingMedia);
+
+ if (mBufferFilled != bufferFilled)
+ mBufferFilled = bufferFilled;
+
+ emit bufferProgressChanged(bufferProgress());
+}
+
+void QAndroidMediaPlayer::updateTrackInfo()
+{
+ const auto &androidTracksInfo = mMediaPlayer->tracksInfo();
+
+ // prepare mTracksMetadata
+ mTracksMetadata[TrackType::VideoStream] = QList<QAndroidMetaData>();
+ mTracksMetadata[TrackType::AudioStream] = QList<QAndroidMetaData>();
+ mTracksMetadata[TrackType::SubtitleStream] = QList<QAndroidMetaData>();
+ mTracksMetadata[TrackType::NTrackTypes] = QList<QAndroidMetaData>();
+
+ for (const auto &androidTrackInfo : androidTracksInfo) {
+
+ const auto &mediaPlayerType = convertTrackType(androidTrackInfo.trackType);
+ auto &tracks = mTracksMetadata[mediaPlayerType];
+
+ const QAndroidMetaData metadata(mediaPlayerType, androidTrackInfo.trackType,
+ androidTrackInfo.trackNumber, androidTrackInfo.mimeType,
+ androidTrackInfo.language);
+ tracks.append(metadata);
+ }
+
+ emit tracksChanged();
+}
+
+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
new file mode 100644
index 000000000..dd2a3469d
--- /dev/null
+++ b/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer_p.h
@@ -0,0 +1,127 @@
+// 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
+
+//
+// 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 <qandroidmetadata_p.h>
+#include <qmap.h>
+#include <qsize.h>
+#include <qurl.h>
+
+QT_BEGIN_NAMESPACE
+
+class AndroidMediaPlayer;
+class QAndroidTextureVideoOutput;
+class QAndroidMediaPlayerVideoRendererControl;
+class QAndroidAudioOutput;
+
+class QAndroidMediaPlayer : public QObject, public QPlatformMediaPlayer
+{
+ Q_OBJECT
+
+public:
+ explicit QAndroidMediaPlayer(QMediaPlayer *parent = 0);
+ ~QAndroidMediaPlayer() override;
+
+ 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;
+
+ QMediaMetaData metaData() const override;
+
+ void setVideoSink(QVideoSink *surface) override;
+
+ void setAudioOutput(QPlatformAudioOutput *output) override;
+ void updateAudioDevice();
+
+ void setPosition(qint64 position) override;
+ void play() override;
+ void pause() override;
+ void stop() override;
+
+ int trackCount(TrackType trackType) override;
+ QMediaMetaData trackMetaData(TrackType trackType, int streamNumber) override;
+ int activeTrack(TrackType trackType) override;
+ void setActiveTrack(TrackType trackType, int streamNumber) override;
+
+private Q_SLOTS:
+ void setVolume(float volume);
+ void setMuted(bool muted);
+ void onVideoOutputReady(bool ready);
+ void onError(qint32 what, qint32 extra);
+ void onInfo(qint32 what, qint32 extra);
+ void onBufferingChanged(qint32 percent);
+ void onVideoSizeChanged(qint32 width, qint32 height);
+ void onStateChanged(qint32 state);
+ void positionChanged(qint64 position);
+ void durationChanged(qint64 duration);
+
+private:
+ AndroidMediaPlayer *mMediaPlayer = nullptr;
+ QAndroidAudioOutput *m_audioOutput = nullptr;
+ QUrl mMediaContent;
+ QIODevice *mMediaStream = nullptr;
+ QAndroidTextureVideoOutput *mVideoOutput = nullptr;
+ QVideoSink *m_videoSink = nullptr;
+ int mBufferPercent = -1;
+ bool mBufferFilled = false;
+ bool mAudioAvailable = false;
+ bool mVideoAvailable = false;
+ QSize mVideoSize;
+ bool mBuffering = false;
+ QMediaTimeRange mAvailablePlaybackRange;
+ int mState;
+ int mPendingState = -1;
+ qint64 mPendingPosition = -1;
+ bool mPendingSetMedia = false;
+ float mPendingVolume = -1;
+ int mPendingMute = -1;
+ bool mReloadingMedia = false;
+ int mActiveStateChangeNotifiers = 0;
+ qreal mCurrentPlaybackRate = 1.;
+ bool mHasPendingPlaybackRate = false; // we need this because the rate can theoretically be negative
+ QMap<TrackType, QList<QAndroidMetaData>> mTracksMetadata;
+
+ bool mIsVideoTrackEnabled = true;
+ bool mIsAudioTrackEnabled = true;
+
+ void setMediaStatus(QMediaPlayer::MediaStatus status);
+ void setAudioAvailable(bool available);
+ void setVideoAvailable(bool available);
+ void updateAvailablePlaybackRanges();
+ void resetBufferingProgress();
+ void flushPendingStates();
+ void updateBufferStatus();
+ void updateTrackInfo();
+ void setSubtitle(QString subtitle);
+ void disableTrack(TrackType trackType);
+
+ int convertTrackNumber(int androidTrackNumber);
+ friend class StateChangeNotifier;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMEDIAPLAYERCONTROL_H
diff --git a/src/plugins/multimedia/android/mediaplayer/qandroidmetadata.cpp b/src/plugins/multimedia/android/mediaplayer/qandroidmetadata.cpp
new file mode 100644
index 000000000..b01845fa7
--- /dev/null
+++ b/src/plugins/multimedia/android/mediaplayer/qandroidmetadata.cpp
@@ -0,0 +1,163 @@
+// 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"
+
+#include "androidmediametadataretriever_p.h"
+#include <QtMultimedia/qmediametadata.h>
+#include <qsize.h>
+#include <QDate>
+#include <QtCore/qlist.h>
+#include <QtConcurrent/qtconcurrentrun.h>
+#include <QLoggingCategory>
+
+QT_BEGIN_NAMESPACE
+
+// Genre name ordered by ID
+// see: http://id3.org/id3v2.3.0#Appendix_A_-_Genre_List_from_ID3v1
+static const char* qt_ID3GenreNames[] =
+{
+ "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz",
+ "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno",
+ "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno",
+ "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental",
+ "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass", "Soul", "Punk",
+ "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave",
+ "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy",
+ "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American",
+ "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal",
+ "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk",
+ "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", "Celtic",
+ "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock",
+ "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour",
+ "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
+ "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad",
+ "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", "A capella",
+ "Euro-House", "Dance Hall"
+};
+
+QMediaMetaData QAndroidMetaData::extractMetadata(const QUrl &url)
+{
+ QMediaMetaData metadata;
+
+ if (!url.isEmpty()) {
+ AndroidMediaMetadataRetriever retriever;
+ if (!retriever.setDataSource(url))
+ return metadata;
+
+ QString mimeType = retriever.extractMetadata(AndroidMediaMetadataRetriever::MimeType);
+ if (!mimeType.isNull())
+ metadata.insert(QMediaMetaData::MediaType, mimeType);
+
+ bool isVideo = !retriever.extractMetadata(AndroidMediaMetadataRetriever::HasVideo).isNull()
+ || mimeType.startsWith(QStringLiteral("video"));
+
+ QString string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Album);
+ if (!string.isNull())
+ metadata.insert(QMediaMetaData::AlbumTitle, string);
+
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::AlbumArtist);
+ if (!string.isNull())
+ metadata.insert(QMediaMetaData::AlbumArtist, string);
+
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Artist);
+ if (!string.isNull()) {
+ metadata.insert(isVideo ? QMediaMetaData::LeadPerformer
+ : QMediaMetaData::ContributingArtist,
+ string.split(QLatin1Char('/'), Qt::SkipEmptyParts));
+ }
+
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Author);
+ if (!string.isNull())
+ metadata.insert(QMediaMetaData::Author, string.split(QLatin1Char('/'), Qt::SkipEmptyParts));
+
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Bitrate);
+ if (!string.isNull()) {
+ metadata.insert(isVideo ? QMediaMetaData::VideoBitRate
+ : QMediaMetaData::AudioBitRate,
+ string.toInt());
+ }
+
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::CDTrackNumber);
+ if (!string.isNull())
+ metadata.insert(QMediaMetaData::TrackNumber, string.toInt());
+
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Composer);
+ if (!string.isNull())
+ metadata.insert(QMediaMetaData::Composer, string.split(QLatin1Char('/'), Qt::SkipEmptyParts));
+
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Date);
+ if (!string.isNull())
+ metadata.insert(QMediaMetaData::Date, QDateTime::fromString(string, QStringLiteral("yyyyMMddTHHmmss.zzzZ")).date());
+
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Duration);
+ if (!string.isNull())
+ metadata.insert(QMediaMetaData::Duration, string.toLongLong());
+
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Genre);
+ if (!string.isNull()) {
+ // The genre can be returned as an ID3v2 id, get the name for it in that case
+ if (string.startsWith(QLatin1Char('(')) && string.endsWith(QLatin1Char(')'))) {
+ bool ok = false;
+ const int genreId = QStringView{string}.mid(1, string.length() - 2).toInt(&ok);
+ if (ok && genreId >= 0 && genreId <= 125)
+ string = QLatin1String(qt_ID3GenreNames[genreId]);
+ }
+ metadata.insert(QMediaMetaData::Genre, string);
+ }
+
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Title);
+ if (!string.isNull())
+ metadata.insert(QMediaMetaData::Title, string);
+
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::VideoHeight);
+ if (!string.isNull()) {
+ const int height = string.toInt();
+ const int width = retriever.extractMetadata(AndroidMediaMetadataRetriever::VideoWidth).toInt();
+ metadata.insert(QMediaMetaData::Resolution, QSize(width, height));
+ }
+
+// string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Writer);
+// if (!string.isNull())
+// metadata.insert(QMediaMetaData::Writer, string.split('/', Qt::SkipEmptyParts));
+
+ }
+
+ return metadata;
+}
+
+QLocale::Language getLocaleLanguage(const QString &language)
+{
+ // undefined language or uncoded language
+ if (language == QLatin1String("und") || language == QStringLiteral("mis"))
+ return QLocale::AnyLanguage;
+
+ return QLocale::codeToLanguage(language, QLocale::ISO639Part2);
+}
+
+QAndroidMetaData::QAndroidMetaData(int trackType, int androidTrackType, int androidTrackNumber,
+ const QString &mimeType, const QString &language)
+ : mTrackType(trackType),
+ mAndroidTrackType(androidTrackType),
+ mAndroidTrackNumber(androidTrackNumber)
+{
+ insert(QMediaMetaData::MediaType, mimeType);
+ insert(QMediaMetaData::Language, getLocaleLanguage(language));
+}
+
+int QAndroidMetaData::trackType() const
+{
+ return mTrackType;
+}
+
+int QAndroidMetaData::androidTrackType() const
+{
+ return mAndroidTrackType;
+}
+
+int QAndroidMetaData::androidTrackNumber() const
+{
+ return mAndroidTrackNumber;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/android/mediaplayer/qandroidmetadata_p.h b/src/plugins/multimedia/android/mediaplayer/qandroidmetadata_p.h
new file mode 100644
index 000000000..1bbad92dd
--- /dev/null
+++ b/src/plugins/multimedia/android/mediaplayer/qandroidmetadata_p.h
@@ -0,0 +1,47 @@
+// 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
+
+//
+// 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 <qurl.h>
+#include <QMutex>
+#include <QVariant>
+
+QT_BEGIN_NAMESPACE
+
+class AndroidMediaMetadataRetriever;
+
+class QAndroidMetaData : public QMediaMetaData
+{
+public:
+ static QMediaMetaData extractMetadata(const QUrl &url);
+
+ QAndroidMetaData(int trackType, int androidTrackType, int androidTrackNumber,
+ const QString &mimeType, const QString &language);
+
+ int trackType() const;
+ int androidTrackType() const;
+ int androidTrackNumber() const;
+
+private:
+ int mTrackType;
+ int mAndroidTrackType;
+ int mAndroidTrackNumber;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMETADATA_H
diff --git a/src/plugins/multimedia/android/qandroidformatsinfo.cpp b/src/plugins/multimedia/android/qandroidformatsinfo.cpp
new file mode 100644
index 000000000..3b23340ce
--- /dev/null
+++ b/src/plugins/multimedia/android/qandroidformatsinfo.cpp
@@ -0,0 +1,160 @@
+// 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"
+
+#include <QtCore/qjnienvironment.h>
+#include <QtCore/qjniobject.h>
+#include <qcoreapplication.h>
+
+static const char encoderFilter[] = ".encoder";
+static const char decoderFilter[] = ".decoder";
+
+QT_BEGIN_NAMESPACE
+
+QAndroidFormatInfo::QAndroidFormatInfo()
+{
+ // Audio/Video/Image formats with their decoder/encoder information is documented at
+ // https://developer.android.com/guide/topics/media/media-formats
+
+ const QJniObject codecsArrayObject = QJniObject::callStaticObjectMethod(
+ "org/qtproject/qt/android/multimedia/QtMultimediaUtils",
+ "getMediaCodecs",
+ "()[Ljava/lang/String;");
+ QStringList codecs;
+ QJniEnvironment env;
+ const jobjectArray devsArray = codecsArrayObject.object<jobjectArray>();
+ for (int i = 0; i < env->GetArrayLength(devsArray); ++i) {
+ const QString codec = QJniObject(env->GetObjectArrayElement(devsArray, i)).toString();
+ if (codec.contains(QStringLiteral("encoder")))
+ m_supportedEncoders.append(codec);
+ else
+ m_supportedDecoders.append(codec);
+ }
+
+ auto removeUnspecifiedValues = [](QList<CodecMap> &map) {
+ for (CodecMap &codec : map) {
+ codec.audio.removeAll(QMediaFormat::AudioCodec::Unspecified);
+ codec.video.removeAll(QMediaFormat::VideoCodec::Unspecified);
+ }
+ erase_if(map, [](const CodecMap &codec) {
+ return codec.audio.isEmpty() && codec.video.isEmpty();
+ });
+ };
+
+ {
+ const QMediaFormat::AudioCodec aac = hasDecoder(QMediaFormat::AudioCodec::AAC);
+ const QMediaFormat::AudioCodec mp3 = hasDecoder(QMediaFormat::AudioCodec::MP3);
+ const QMediaFormat::AudioCodec flac = hasDecoder(QMediaFormat::AudioCodec::FLAC);
+ const QMediaFormat::AudioCodec opus = hasDecoder(QMediaFormat::AudioCodec::Opus);
+ const QMediaFormat::AudioCodec vorbis = hasDecoder(QMediaFormat::AudioCodec::Vorbis);
+
+ const QMediaFormat::VideoCodec vp8 = hasDecoder(QMediaFormat::VideoCodec::VP8);
+ const QMediaFormat::VideoCodec vp9 = hasDecoder(QMediaFormat::VideoCodec::VP9);
+ const QMediaFormat::VideoCodec h264 = hasDecoder(QMediaFormat::VideoCodec::H264);
+ const QMediaFormat::VideoCodec h265 = hasDecoder(QMediaFormat::VideoCodec::H265);
+ const QMediaFormat::VideoCodec av1 = hasDecoder(QMediaFormat::VideoCodec::AV1);
+
+ decoders = {
+ { 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, vorbis}, {vp8, vp9, h264, h265, av1} },
+ { QMediaFormat::WebM, {opus, vorbis}, {vp8, vp9} }
+ };
+
+ removeUnspecifiedValues(decoders);
+ }
+
+ {
+ const QMediaFormat::AudioCodec aac = hasEncoder(QMediaFormat::AudioCodec::AAC);
+ const QMediaFormat::AudioCodec mp3 = hasEncoder(QMediaFormat::AudioCodec::MP3);
+ const QMediaFormat::AudioCodec opus = hasEncoder(QMediaFormat::AudioCodec::Opus);
+ const QMediaFormat::AudioCodec vorbis = hasEncoder(QMediaFormat::AudioCodec::Vorbis);
+
+ const QMediaFormat::VideoCodec vp8 = hasEncoder(QMediaFormat::VideoCodec::VP8);
+ const QMediaFormat::VideoCodec vp9 = hasEncoder(QMediaFormat::VideoCodec::VP9);
+ const QMediaFormat::VideoCodec h264 = hasEncoder(QMediaFormat::VideoCodec::H264);
+ const QMediaFormat::VideoCodec h265 = hasEncoder(QMediaFormat::VideoCodec::H265);
+ const QMediaFormat::VideoCodec av1 = hasEncoder(QMediaFormat::VideoCodec::AV1);
+
+ // MP3 and Vorbis encoders are not supported by the default Android SDK
+ // Opus encoder available only for Android 10+
+ encoders = {
+ { QMediaFormat::AAC, {aac}, {} },
+ { QMediaFormat::MP3, {mp3}, {} },
+ // 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} }
+ };
+
+ removeUnspecifiedValues(encoders);
+ }
+
+ imageFormats << QImageCapture::JPEG;
+ // NOTE: Add later if needed, the Camera API doens't seem to work with it.
+ // imageFormats << QImageCapture::PNG << QImageCapture::WebP;
+}
+
+QAndroidFormatInfo::~QAndroidFormatInfo()
+{
+}
+
+static QString getVideoCodecName(QMediaFormat::VideoCodec codec)
+{
+ QString codecString = QMediaFormat::videoCodecName(codec);
+ if (codec == QMediaFormat::VideoCodec::H265)
+ codecString = QLatin1String("HEVC");
+ return codecString;
+}
+
+QMediaFormat::AudioCodec QAndroidFormatInfo::hasEncoder(QMediaFormat::AudioCodec codec) const
+{
+ const QString codecString = QMediaFormat::audioCodecName(codec);
+ for (auto str : m_supportedEncoders) {
+ if (str.contains(codecString + QLatin1String(encoderFilter), Qt::CaseInsensitive))
+ return codec;
+ }
+ return QMediaFormat::AudioCodec::Unspecified;
+}
+
+QMediaFormat::VideoCodec QAndroidFormatInfo::hasEncoder(QMediaFormat::VideoCodec codec) const
+{
+ const QString codecString = getVideoCodecName(codec);
+ for (auto str : m_supportedEncoders) {
+ if (str.contains(codecString + QLatin1String(encoderFilter), Qt::CaseInsensitive))
+ return codec;
+ }
+ return QMediaFormat::VideoCodec::Unspecified;
+}
+
+QMediaFormat::AudioCodec QAndroidFormatInfo::hasDecoder(QMediaFormat::AudioCodec codec) const
+{
+ const QString codecString = QMediaFormat::audioCodecName(codec);
+ for (auto str : m_supportedDecoders) {
+ if (str.contains(codecString + QLatin1String(decoderFilter), Qt::CaseInsensitive))
+ return codec;
+ }
+ return QMediaFormat::AudioCodec::Unspecified;
+}
+
+QMediaFormat::VideoCodec QAndroidFormatInfo::hasDecoder(QMediaFormat::VideoCodec codec) const
+{
+ const QString codecString = getVideoCodecName(codec);
+ for (auto str : m_supportedDecoders) {
+ if (str.contains(codecString + QLatin1String(decoderFilter), Qt::CaseInsensitive))
+ return codec;
+ }
+ return QMediaFormat::VideoCodec::Unspecified;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/android/qandroidformatsinfo_p.h b/src/plugins/multimedia/android/qandroidformatsinfo_p.h
new file mode 100644
index 000000000..2d14ad181
--- /dev/null
+++ b/src/plugins/multimedia/android/qandroidformatsinfo_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 QANDROIDFORMATINFO_H
+#define QANDROIDFORMATINFO_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/qplatformmediaformatinfo_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidFormatInfo : public QPlatformMediaFormatInfo
+{
+public:
+ QAndroidFormatInfo();
+ ~QAndroidFormatInfo();
+
+private:
+ QMediaFormat::AudioCodec hasEncoder(QMediaFormat::AudioCodec codec) const;
+ QMediaFormat::VideoCodec hasEncoder(QMediaFormat::VideoCodec codec) const;
+ QMediaFormat::AudioCodec hasDecoder(QMediaFormat::AudioCodec codec) const;
+ QMediaFormat::VideoCodec hasDecoder(QMediaFormat::VideoCodec codec) const;
+
+ QStringList m_supportedDecoders;
+ QStringList m_supportedEncoders;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/android/qandroidintegration.cpp b/src/plugins/multimedia/android/qandroidintegration.cpp
new file mode 100644
index 000000000..c7077e49d
--- /dev/null
+++ b/src/plugins/multimedia/android/qandroidintegration.cpp
@@ -0,0 +1,136 @@
+// 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"
+#include "qandroidmediacapturesession_p.h"
+#include "androidmediaplayer_p.h"
+#include "qandroidcamerasession_p.h"
+#include "androidsurfacetexture_p.h"
+#include "androidsurfaceview_p.h"
+#include "androidcamera_p.h"
+#include "qandroidcamera_p.h"
+#include "qandroidimagecapture_p.h"
+#include "qandroidmediaencoder_p.h"
+#include "androidmediarecorder_p.h"
+#include "qandroidformatsinfo_p.h"
+#include "qandroidmediaplayer_p.h"
+#include "qandroidaudiooutput_p.h"
+#include "qandroidaudioinput_p.h"
+#include "qandroidvideosink_p.h"
+#include "qandroidaudiodecoder_p.h"
+#include <QtMultimedia/private/qplatformmediaplugin_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qtAndroidMediaPlugin, "qt.multimedia.android")
+
+class QAndroidMediaPlugin : public QPlatformMediaPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QPlatformMediaPlugin_iid FILE "android.json")
+
+public:
+ QAndroidMediaPlugin()
+ : QPlatformMediaPlugin()
+ {}
+
+ QPlatformMediaIntegration* create(const QString &name) override
+ {
+ if (name == u"android")
+ return new QAndroidIntegration;
+ return nullptr;
+ }
+};
+
+QAndroidIntegration::QAndroidIntegration() : QPlatformMediaIntegration(QLatin1String("android")) { }
+
+QMaybe<QPlatformAudioDecoder *> QAndroidIntegration::createAudioDecoder(QAudioDecoder *decoder)
+{
+ return new QAndroidAudioDecoder(decoder);
+}
+
+QPlatformMediaFormatInfo *QAndroidIntegration::createFormatInfo()
+{
+ return new QAndroidFormatInfo;
+}
+
+QMaybe<QPlatformMediaCaptureSession *> QAndroidIntegration::createCaptureSession()
+{
+ return new QAndroidMediaCaptureSession();
+}
+
+QMaybe<QPlatformMediaPlayer *> QAndroidIntegration::createPlayer(QMediaPlayer *player)
+{
+ return new QAndroidMediaPlayer(player);
+}
+
+QMaybe<QPlatformCamera *> QAndroidIntegration::createCamera(QCamera *camera)
+{
+ return new QAndroidCamera(camera);
+}
+
+QMaybe<QPlatformMediaRecorder *> QAndroidIntegration::createRecorder(QMediaRecorder *recorder)
+{
+ return new QAndroidMediaEncoder(recorder);
+}
+
+QMaybe<QPlatformImageCapture *> QAndroidIntegration::createImageCapture(QImageCapture *imageCapture)
+{
+ return new QAndroidImageCapture(imageCapture);
+}
+
+QMaybe<QPlatformAudioOutput *> QAndroidIntegration::createAudioOutput(QAudioOutput *q)
+{
+ return new QAndroidAudioOutput(q);
+}
+
+QMaybe<QPlatformAudioInput *> QAndroidIntegration::createAudioInput(QAudioInput *audioInput)
+{
+ return new QAndroidAudioInput(audioInput);
+}
+
+QMaybe<QPlatformVideoSink *> QAndroidIntegration::createVideoSink(QVideoSink *sink)
+{
+ return new QAndroidVideoSink(sink);
+}
+
+Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/)
+{
+ static bool initialized = false;
+ if (initialized)
+ return JNI_VERSION_1_6;
+ initialized = true;
+
+ QT_USE_NAMESPACE
+ typedef union {
+ JNIEnv *nativeEnvironment;
+ void *venv;
+ } UnionJNIEnvToVoid;
+
+ UnionJNIEnvToVoid uenv;
+ uenv.venv = NULL;
+
+ if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK)
+ return JNI_ERR;
+
+ if (!AndroidMediaPlayer::registerNativeMethods()
+ || !AndroidCamera::registerNativeMethods()
+ || !AndroidMediaRecorder::registerNativeMethods()
+ || !AndroidSurfaceHolder::registerNativeMethods()) {
+ return JNI_ERR;
+ }
+
+ AndroidSurfaceTexture::registerNativeMethods();
+
+ return JNI_VERSION_1_6;
+}
+
+QList<QCameraDevice> QAndroidIntegration::videoInputs()
+{
+ return QAndroidCameraSession::availableCameras();
+}
+
+QT_END_NAMESPACE
+
+#include "qandroidintegration.moc"
diff --git a/src/plugins/multimedia/android/qandroidintegration_p.h b/src/plugins/multimedia/android/qandroidintegration_p.h
new file mode 100644
index 000000000..9ef5a3267
--- /dev/null
+++ b/src/plugins/multimedia/android/qandroidintegration_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 QANDROIDINTEGRATION_H
+#define QANDROIDINTEGRATION_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 QAndroidMediaDevices;
+
+class QAndroidIntegration : public QPlatformMediaIntegration
+{
+public:
+ QAndroidIntegration();
+
+ 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;
+
+ QMaybe<QPlatformAudioOutput *> createAudioOutput(QAudioOutput *q) override;
+ QMaybe<QPlatformAudioInput *> createAudioInput(QAudioInput *audioInput) override;
+
+ QMaybe<QPlatformVideoSink *> createVideoSink(QVideoSink *) override;
+ QList<QCameraDevice> videoInputs() override;
+
+protected:
+ QPlatformMediaFormatInfo *createFormatInfo() override;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidcamera.cpp b/src/plugins/multimedia/android/wrappers/jni/androidcamera.cpp
new file mode 100644
index 000000000..cef36d7ad
--- /dev/null
+++ b/src/plugins/multimedia/android/wrappers/jni/androidcamera.cpp
@@ -0,0 +1,1797 @@
+// 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"
+#include "androidsurfaceview_p.h"
+#include "qandroidmultimediautils_p.h"
+#include "qandroidglobal_p.h"
+
+#include <private/qvideoframe_p.h>
+
+#include <qhash.h>
+#include <qstringlist.h>
+#include <qdebug.h>
+#include <QtCore/qthread.h>
+#include <QtCore/qreadwritelock.h>
+#include <QtCore/qmutex.h>
+#include <QtMultimedia/private/qmemoryvideobuffer_p.h>
+#include <QtCore/qcoreapplication.h>
+
+#include <mutex>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(lcAndroidCamera, "qt.multimedia.android.camera")
+
+static const char QtCameraListenerClassName[] = "org/qtproject/qt/android/multimedia/QtCameraListener";
+
+typedef QHash<int, AndroidCamera *> CameraMap;
+Q_GLOBAL_STATIC(CameraMap, cameras)
+Q_GLOBAL_STATIC(QReadWriteLock, rwLock)
+
+static QRect areaToRect(jobject areaObj)
+{
+ QJniObject area(areaObj);
+ QJniObject rect = area.getObjectField("rect", "Landroid/graphics/Rect;");
+
+ return QRect(rect.getField<jint>("left"),
+ rect.getField<jint>("top"),
+ rect.callMethod<jint>("width"),
+ rect.callMethod<jint>("height"));
+}
+
+static QJniObject rectToArea(const QRect &rect)
+{
+ QJniObject jrect("android/graphics/Rect",
+ "(IIII)V",
+ rect.left(), rect.top(), rect.right(), rect.bottom());
+
+ QJniObject area("android/hardware/Camera$Area",
+ "(Landroid/graphics/Rect;I)V",
+ jrect.object(), 500);
+
+ return area;
+}
+
+// native method for QtCameraLisener.java
+static void notifyAutoFocusComplete(JNIEnv* , jobject, int id, jboolean success)
+{
+ QReadLocker locker(rwLock);
+ const auto it = cameras->constFind(id);
+ if (Q_UNLIKELY(it == cameras->cend()))
+ return;
+
+ Q_EMIT (*it)->autoFocusComplete(success);
+}
+
+static void notifyPictureExposed(JNIEnv* , jobject, int id)
+{
+ QReadLocker locker(rwLock);
+ const auto it = cameras->constFind(id);
+ if (Q_UNLIKELY(it == cameras->cend()))
+ return;
+
+ Q_EMIT (*it)->pictureExposed();
+}
+
+static void notifyPictureCaptured(JNIEnv *env, jobject, int id, jbyteArray data)
+{
+ QReadLocker locker(rwLock);
+ const auto it = cameras->constFind(id);
+ if (Q_UNLIKELY(it == cameras->cend())) {
+ qCWarning(lcAndroidCamera) << "Could not obtain camera!";
+ return;
+ }
+
+ AndroidCamera *camera = (*it);
+
+ const int arrayLength = env->GetArrayLength(data);
+ QByteArray bytes(arrayLength, Qt::Uninitialized);
+ env->GetByteArrayRegion(data, 0, arrayLength, reinterpret_cast<jbyte *>(bytes.data()));
+
+ auto parameters = camera->getParametersObject();
+
+ QJniObject size =
+ parameters.callObjectMethod("getPictureSize", "()Landroid/hardware/Camera$Size;");
+
+ if (!size.isValid()) {
+ qCWarning(lcAndroidCamera) << "Picture Size is not valid!";
+ return;
+ }
+
+ QSize pictureSize(size.getField<jint>("width"), size.getField<jint>("height"));
+
+ auto format = AndroidCamera::ImageFormat(parameters.callMethod<jint>("getPictureFormat"));
+
+ if (format == AndroidCamera::ImageFormat::UnknownImageFormat) {
+ qCWarning(lcAndroidCamera) << "Android Camera Image Format is UnknownImageFormat!";
+ return;
+ }
+
+ int bytesPerLine = 0;
+
+ switch (format) {
+ case AndroidCamera::ImageFormat::YV12:
+ bytesPerLine = (pictureSize.width() + 15) & ~15;
+ break;
+ case AndroidCamera::ImageFormat::NV21:
+ bytesPerLine = pictureSize.width();
+ break;
+ case AndroidCamera::ImageFormat::RGB565:
+ case AndroidCamera::ImageFormat::YUY2:
+ bytesPerLine = pictureSize.width() * 2;
+ break;
+ default:
+ bytesPerLine = -1;
+ }
+
+ auto pictureFormat = qt_pixelFormatFromAndroidImageFormat(format);
+
+ emit camera->pictureCaptured(bytes, pictureFormat, pictureSize, bytesPerLine);
+}
+
+static void notifyNewPreviewFrame(JNIEnv *env, jobject, int id, jbyteArray data,
+ int width, int height, int format, int bpl)
+{
+ QReadLocker locker(rwLock);
+ const auto it = cameras->constFind(id);
+ if (Q_UNLIKELY(it == cameras->cend()))
+ return;
+
+ const int arrayLength = env->GetArrayLength(data);
+ if (arrayLength == 0)
+ return;
+
+ QByteArray bytes(arrayLength, Qt::Uninitialized);
+ env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data());
+
+ QVideoFrameFormat frameFormat(
+ QSize(width, height),
+ qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat(format)));
+
+ QVideoFrame frame = QVideoFramePrivate::createFrame(
+ std::make_unique<QMemoryVideoBuffer>(std::move(bytes), bpl), std::move(frameFormat));
+
+ Q_EMIT (*it)->newPreviewFrame(frame);
+}
+
+static void notifyFrameAvailable(JNIEnv *, jobject, int id)
+{
+ QReadLocker locker(rwLock);
+ const auto it = cameras->constFind(id);
+ if (Q_UNLIKELY(it == cameras->cend()))
+ return;
+
+ (*it)->fetchLastPreviewFrame();
+}
+
+class AndroidCameraPrivate : public QObject
+{
+ Q_OBJECT
+public:
+ AndroidCameraPrivate();
+ ~AndroidCameraPrivate();
+
+ Q_INVOKABLE bool init(int cameraId);
+
+ Q_INVOKABLE void release();
+ Q_INVOKABLE bool lock();
+ Q_INVOKABLE bool unlock();
+ Q_INVOKABLE bool reconnect();
+
+ Q_INVOKABLE AndroidCamera::CameraFacing getFacing();
+ Q_INVOKABLE int getNativeOrientation();
+
+ Q_INVOKABLE QSize getPreferredPreviewSizeForVideo();
+ Q_INVOKABLE QList<QSize> getSupportedPreviewSizes();
+ static QList<QSize> getSupportedPreviewSizes(QJniObject &parameters);
+
+ Q_INVOKABLE QList<AndroidCamera::FpsRange> getSupportedPreviewFpsRange();
+
+ Q_INVOKABLE AndroidCamera::FpsRange getPreviewFpsRange();
+ static AndroidCamera::FpsRange getPreviewFpsRange(QJniObject &parameters);
+ Q_INVOKABLE void setPreviewFpsRange(int min, int max);
+
+ Q_INVOKABLE AndroidCamera::ImageFormat getPreviewFormat();
+ Q_INVOKABLE void setPreviewFormat(AndroidCamera::ImageFormat fmt);
+ Q_INVOKABLE QList<AndroidCamera::ImageFormat> getSupportedPreviewFormats();
+ static QList<AndroidCamera::ImageFormat> getSupportedPreviewFormats(QJniObject &parameters);
+
+ Q_INVOKABLE QSize previewSize() const { return m_previewSize; }
+ Q_INVOKABLE QSize getPreviewSize();
+ Q_INVOKABLE void updatePreviewSize();
+ Q_INVOKABLE bool setPreviewTexture(void *surfaceTexture);
+ Q_INVOKABLE bool setPreviewDisplay(void *surfaceHolder);
+ Q_INVOKABLE void setDisplayOrientation(int degrees);
+
+ Q_INVOKABLE bool isZoomSupported();
+ Q_INVOKABLE int getMaxZoom();
+ Q_INVOKABLE QList<int> getZoomRatios();
+ Q_INVOKABLE int getZoom();
+ Q_INVOKABLE void setZoom(int value);
+
+ Q_INVOKABLE QString getFlashMode();
+ Q_INVOKABLE void setFlashMode(const QString &value);
+
+ Q_INVOKABLE QString getFocusMode();
+ Q_INVOKABLE void setFocusMode(const QString &value);
+
+ Q_INVOKABLE int getMaxNumFocusAreas();
+ Q_INVOKABLE QList<QRect> getFocusAreas();
+ Q_INVOKABLE void setFocusAreas(const QList<QRect> &areas);
+
+ Q_INVOKABLE void autoFocus();
+ Q_INVOKABLE void cancelAutoFocus();
+
+ Q_INVOKABLE bool isAutoExposureLockSupported();
+ Q_INVOKABLE bool getAutoExposureLock();
+ Q_INVOKABLE void setAutoExposureLock(bool toggle);
+
+ Q_INVOKABLE bool isAutoWhiteBalanceLockSupported();
+ Q_INVOKABLE bool getAutoWhiteBalanceLock();
+ Q_INVOKABLE void setAutoWhiteBalanceLock(bool toggle);
+
+ Q_INVOKABLE int getExposureCompensation();
+ Q_INVOKABLE void setExposureCompensation(int value);
+ Q_INVOKABLE float getExposureCompensationStep();
+ Q_INVOKABLE int getMinExposureCompensation();
+ Q_INVOKABLE int getMaxExposureCompensation();
+
+ Q_INVOKABLE QString getSceneMode();
+ Q_INVOKABLE void setSceneMode(const QString &value);
+
+ Q_INVOKABLE QString getWhiteBalance();
+ Q_INVOKABLE void setWhiteBalance(const QString &value);
+
+ Q_INVOKABLE void updateRotation();
+
+ Q_INVOKABLE QList<QSize> getSupportedPictureSizes();
+ Q_INVOKABLE QList<QSize> getSupportedVideoSizes();
+ Q_INVOKABLE void setPictureSize(const QSize &size);
+ Q_INVOKABLE void setJpegQuality(int quality);
+
+ Q_INVOKABLE void startPreview();
+ Q_INVOKABLE void stopPreview();
+
+ Q_INVOKABLE void takePicture();
+
+ Q_INVOKABLE void setupPreviewFrameCallback();
+ Q_INVOKABLE void notifyNewFrames(bool notify);
+ Q_INVOKABLE void fetchLastPreviewFrame();
+
+ Q_INVOKABLE void applyParameters();
+
+ Q_INVOKABLE QStringList callParametersStringListMethod(const QByteArray &methodName);
+
+ int m_cameraId;
+ QRecursiveMutex m_parametersMutex;
+ QSize m_previewSize;
+ int m_rotation;
+ QJniObject m_info;
+ QJniObject m_parameters;
+ QJniObject m_camera;
+ QJniObject m_cameraListener;
+
+Q_SIGNALS:
+ void previewSizeChanged();
+ void previewStarted();
+ void previewFailedToStart();
+ void previewStopped();
+
+ void autoFocusStarted();
+
+ void whiteBalanceChanged();
+
+ void takePictureFailed();
+
+ void lastPreviewFrameFetched(const QVideoFrame &frame);
+};
+
+AndroidCamera::AndroidCamera(AndroidCameraPrivate *d, QThread *worker)
+ : QObject(),
+ d_ptr(d),
+ m_worker(worker)
+
+{
+ connect(d, &AndroidCameraPrivate::previewSizeChanged, this, &AndroidCamera::previewSizeChanged);
+ connect(d, &AndroidCameraPrivate::previewStarted, this, &AndroidCamera::previewStarted);
+ connect(d, &AndroidCameraPrivate::previewFailedToStart, this, &AndroidCamera::previewFailedToStart);
+ connect(d, &AndroidCameraPrivate::previewStopped, this, &AndroidCamera::previewStopped);
+ connect(d, &AndroidCameraPrivate::autoFocusStarted, this, &AndroidCamera::autoFocusStarted);
+ connect(d, &AndroidCameraPrivate::whiteBalanceChanged, this, &AndroidCamera::whiteBalanceChanged);
+ connect(d, &AndroidCameraPrivate::takePictureFailed, this, &AndroidCamera::takePictureFailed);
+ connect(d, &AndroidCameraPrivate::lastPreviewFrameFetched, this, &AndroidCamera::lastPreviewFrameFetched);
+}
+
+AndroidCamera::~AndroidCamera()
+{
+ Q_D(AndroidCamera);
+ if (d->m_camera.isValid()) {
+ release();
+ QWriteLocker locker(rwLock);
+ cameras->remove(cameraId());
+ }
+
+ m_worker->exit();
+ m_worker->wait(5000);
+}
+
+AndroidCamera *AndroidCamera::open(int cameraId)
+{
+ if (!qt_androidCheckCameraPermission())
+ return nullptr;
+
+ AndroidCameraPrivate *d = new AndroidCameraPrivate();
+ QThread *worker = new QThread;
+ worker->start();
+ d->moveToThread(worker);
+ connect(worker, &QThread::finished, d, &AndroidCameraPrivate::deleteLater);
+ bool ok = true;
+ QMetaObject::invokeMethod(d, "init", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok), Q_ARG(int, cameraId));
+ if (!ok) {
+ worker->quit();
+ worker->wait(5000);
+ delete worker;
+ return 0;
+ }
+
+ AndroidCamera *q = new AndroidCamera(d, worker);
+ QWriteLocker locker(rwLock);
+ cameras->insert(cameraId, q);
+
+ return q;
+}
+
+int AndroidCamera::cameraId() const
+{
+ Q_D(const AndroidCamera);
+ return d->m_cameraId;
+}
+
+bool AndroidCamera::lock()
+{
+ Q_D(AndroidCamera);
+ bool ok = true;
+ QMetaObject::invokeMethod(d, "lock", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok));
+ return ok;
+}
+
+bool AndroidCamera::unlock()
+{
+ Q_D(AndroidCamera);
+ bool ok = true;
+ QMetaObject::invokeMethod(d, "unlock", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok));
+ return ok;
+}
+
+bool AndroidCamera::reconnect()
+{
+ Q_D(AndroidCamera);
+ bool ok = true;
+ QMetaObject::invokeMethod(d, "reconnect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok));
+ return ok;
+}
+
+void AndroidCamera::release()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "release", Qt::BlockingQueuedConnection);
+}
+
+AndroidCamera::CameraFacing AndroidCamera::getFacing()
+{
+ Q_D(AndroidCamera);
+ return d->getFacing();
+}
+
+int AndroidCamera::getNativeOrientation()
+{
+ Q_D(AndroidCamera);
+ return d->getNativeOrientation();
+}
+
+QSize AndroidCamera::getPreferredPreviewSizeForVideo()
+{
+ Q_D(AndroidCamera);
+ return d->getPreferredPreviewSizeForVideo();
+}
+
+QList<QSize> AndroidCamera::getSupportedPreviewSizes()
+{
+ Q_D(AndroidCamera);
+ return d->getSupportedPreviewSizes();
+}
+
+QList<AndroidCamera::FpsRange> AndroidCamera::getSupportedPreviewFpsRange()
+{
+ Q_D(AndroidCamera);
+ return d->getSupportedPreviewFpsRange();
+}
+
+AndroidCamera::FpsRange AndroidCamera::getPreviewFpsRange()
+{
+ Q_D(AndroidCamera);
+ return d->getPreviewFpsRange();
+}
+
+void AndroidCamera::setPreviewFpsRange(FpsRange range)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setPreviewFpsRange", Q_ARG(int, range.min), Q_ARG(int, range.max));
+}
+
+AndroidCamera::ImageFormat AndroidCamera::getPreviewFormat()
+{
+ Q_D(AndroidCamera);
+ return d->getPreviewFormat();
+}
+
+void AndroidCamera::setPreviewFormat(ImageFormat fmt)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setPreviewFormat", Q_ARG(AndroidCamera::ImageFormat, fmt));
+}
+
+QList<AndroidCamera::ImageFormat> AndroidCamera::getSupportedPreviewFormats()
+{
+ Q_D(AndroidCamera);
+ return d->getSupportedPreviewFormats();
+}
+
+QSize AndroidCamera::previewSize() const
+{
+ Q_D(const AndroidCamera);
+ return d->m_previewSize;
+}
+
+QSize AndroidCamera::actualPreviewSize()
+{
+ Q_D(AndroidCamera);
+ return d->getPreviewSize();
+}
+
+void AndroidCamera::setPreviewSize(const QSize &size)
+{
+ Q_D(AndroidCamera);
+ d->m_parametersMutex.lock();
+ bool areParametersValid = d->m_parameters.isValid();
+ d->m_parametersMutex.unlock();
+ if (!areParametersValid)
+ return;
+
+ d->m_previewSize = size;
+ QMetaObject::invokeMethod(d, "updatePreviewSize");
+}
+
+bool AndroidCamera::setPreviewTexture(AndroidSurfaceTexture *surfaceTexture)
+{
+ Q_D(AndroidCamera);
+ bool ok = true;
+ QMetaObject::invokeMethod(d,
+ "setPreviewTexture",
+ Qt::BlockingQueuedConnection,
+ Q_RETURN_ARG(bool, ok),
+ Q_ARG(void *, surfaceTexture ? surfaceTexture->surfaceTexture() : 0));
+ return ok;
+}
+
+bool AndroidCamera::setPreviewDisplay(AndroidSurfaceHolder *surfaceHolder)
+{
+ Q_D(AndroidCamera);
+ bool ok = true;
+ QMetaObject::invokeMethod(d,
+ "setPreviewDisplay",
+ Qt::BlockingQueuedConnection,
+ Q_RETURN_ARG(bool, ok),
+ Q_ARG(void *, surfaceHolder ? surfaceHolder->surfaceHolder() : 0));
+ return ok;
+}
+
+void AndroidCamera::setDisplayOrientation(int degrees)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setDisplayOrientation", Qt::QueuedConnection, Q_ARG(int, degrees));
+}
+
+bool AndroidCamera::isZoomSupported()
+{
+ Q_D(AndroidCamera);
+ return d->isZoomSupported();
+}
+
+int AndroidCamera::getMaxZoom()
+{
+ Q_D(AndroidCamera);
+ return d->getMaxZoom();
+}
+
+QList<int> AndroidCamera::getZoomRatios()
+{
+ Q_D(AndroidCamera);
+ return d->getZoomRatios();
+}
+
+int AndroidCamera::getZoom()
+{
+ Q_D(AndroidCamera);
+ return d->getZoom();
+}
+
+void AndroidCamera::setZoom(int value)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setZoom", Q_ARG(int, value));
+}
+
+QStringList AndroidCamera::getSupportedFlashModes()
+{
+ Q_D(AndroidCamera);
+ return d->callParametersStringListMethod("getSupportedFlashModes");
+}
+
+QString AndroidCamera::getFlashMode()
+{
+ Q_D(AndroidCamera);
+ return d->getFlashMode();
+}
+
+void AndroidCamera::setFlashMode(const QString &value)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setFlashMode", Q_ARG(QString, value));
+}
+
+QStringList AndroidCamera::getSupportedFocusModes()
+{
+ Q_D(AndroidCamera);
+ return d->callParametersStringListMethod("getSupportedFocusModes");
+}
+
+QString AndroidCamera::getFocusMode()
+{
+ Q_D(AndroidCamera);
+ return d->getFocusMode();
+}
+
+void AndroidCamera::setFocusMode(const QString &value)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setFocusMode", Q_ARG(QString, value));
+}
+
+int AndroidCamera::getMaxNumFocusAreas()
+{
+ Q_D(AndroidCamera);
+ return d->getMaxNumFocusAreas();
+}
+
+QList<QRect> AndroidCamera::getFocusAreas()
+{
+ Q_D(AndroidCamera);
+ return d->getFocusAreas();
+}
+
+void AndroidCamera::setFocusAreas(const QList<QRect> &areas)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setFocusAreas", Q_ARG(QList<QRect>, areas));
+}
+
+void AndroidCamera::autoFocus()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "autoFocus");
+}
+
+void AndroidCamera::cancelAutoFocus()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "cancelAutoFocus", Qt::QueuedConnection);
+}
+
+bool AndroidCamera::isAutoExposureLockSupported()
+{
+ Q_D(AndroidCamera);
+ return d->isAutoExposureLockSupported();
+}
+
+bool AndroidCamera::getAutoExposureLock()
+{
+ Q_D(AndroidCamera);
+ return d->getAutoExposureLock();
+}
+
+void AndroidCamera::setAutoExposureLock(bool toggle)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setAutoExposureLock", Q_ARG(bool, toggle));
+}
+
+bool AndroidCamera::isAutoWhiteBalanceLockSupported()
+{
+ Q_D(AndroidCamera);
+ return d->isAutoWhiteBalanceLockSupported();
+}
+
+bool AndroidCamera::getAutoWhiteBalanceLock()
+{
+ Q_D(AndroidCamera);
+ return d->getAutoWhiteBalanceLock();
+}
+
+void AndroidCamera::setAutoWhiteBalanceLock(bool toggle)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setAutoWhiteBalanceLock", Q_ARG(bool, toggle));
+}
+
+int AndroidCamera::getExposureCompensation()
+{
+ Q_D(AndroidCamera);
+ return d->getExposureCompensation();
+}
+
+void AndroidCamera::setExposureCompensation(int value)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setExposureCompensation", Q_ARG(int, value));
+}
+
+float AndroidCamera::getExposureCompensationStep()
+{
+ Q_D(AndroidCamera);
+ return d->getExposureCompensationStep();
+}
+
+int AndroidCamera::getMinExposureCompensation()
+{
+ Q_D(AndroidCamera);
+ return d->getMinExposureCompensation();
+}
+
+int AndroidCamera::getMaxExposureCompensation()
+{
+ Q_D(AndroidCamera);
+ return d->getMaxExposureCompensation();
+}
+
+QStringList AndroidCamera::getSupportedSceneModes()
+{
+ Q_D(AndroidCamera);
+ return d->callParametersStringListMethod("getSupportedSceneModes");
+}
+
+QString AndroidCamera::getSceneMode()
+{
+ Q_D(AndroidCamera);
+ return d->getSceneMode();
+}
+
+void AndroidCamera::setSceneMode(const QString &value)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setSceneMode", Q_ARG(QString, value));
+}
+
+QStringList AndroidCamera::getSupportedWhiteBalance()
+{
+ Q_D(AndroidCamera);
+ return d->callParametersStringListMethod("getSupportedWhiteBalance");
+}
+
+QString AndroidCamera::getWhiteBalance()
+{
+ Q_D(AndroidCamera);
+ return d->getWhiteBalance();
+}
+
+void AndroidCamera::setWhiteBalance(const QString &value)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setWhiteBalance", Q_ARG(QString, value));
+}
+
+void AndroidCamera::setRotation(int rotation)
+{
+ Q_D(AndroidCamera);
+ //We need to do it here and not in worker class because we cache rotation
+ d->m_parametersMutex.lock();
+ bool areParametersValid = d->m_parameters.isValid();
+ d->m_parametersMutex.unlock();
+ if (!areParametersValid)
+ return;
+
+ d->m_rotation = rotation;
+ QMetaObject::invokeMethod(d, "updateRotation");
+}
+
+int AndroidCamera::getRotation() const
+{
+ Q_D(const AndroidCamera);
+ return d->m_rotation;
+}
+
+QList<QSize> AndroidCamera::getSupportedPictureSizes()
+{
+ Q_D(AndroidCamera);
+ return d->getSupportedPictureSizes();
+}
+
+QList<QSize> AndroidCamera::getSupportedVideoSizes()
+{
+ Q_D(AndroidCamera);
+ return d->getSupportedVideoSizes();
+}
+
+void AndroidCamera::setPictureSize(const QSize &size)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setPictureSize", Q_ARG(QSize, size));
+}
+
+void AndroidCamera::setJpegQuality(int quality)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setJpegQuality", Q_ARG(int, quality));
+}
+
+void AndroidCamera::takePicture()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "takePicture", Qt::BlockingQueuedConnection);
+}
+
+void AndroidCamera::setupPreviewFrameCallback()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setupPreviewFrameCallback");
+}
+
+void AndroidCamera::notifyNewFrames(bool notify)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "notifyNewFrames", Q_ARG(bool, notify));
+}
+
+void AndroidCamera::fetchLastPreviewFrame()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "fetchLastPreviewFrame");
+}
+
+QJniObject AndroidCamera::getCameraObject()
+{
+ Q_D(AndroidCamera);
+ return d->m_camera;
+}
+
+int AndroidCamera::getNumberOfCameras()
+{
+ if (!qt_androidCheckCameraPermission())
+ return 0;
+
+ return QJniObject::callStaticMethod<jint>("android/hardware/Camera",
+ "getNumberOfCameras");
+}
+
+void AndroidCamera::getCameraInfo(int id, QCameraDevicePrivate *info)
+{
+ Q_ASSERT(info);
+
+ QJniObject cameraInfo("android/hardware/Camera$CameraInfo");
+ QJniObject::callStaticMethod<void>("android/hardware/Camera",
+ "getCameraInfo",
+ "(ILandroid/hardware/Camera$CameraInfo;)V",
+ id, cameraInfo.object());
+
+ AndroidCamera::CameraFacing facing = AndroidCamera::CameraFacing(cameraInfo.getField<jint>("facing"));
+ // The orientation provided by Android is counter-clockwise, we need it clockwise
+ info->orientation = (360 - cameraInfo.getField<jint>("orientation")) % 360;
+
+ switch (facing) {
+ case AndroidCamera::CameraFacingBack:
+ info->id = QByteArray("back");
+ info->description = QStringLiteral("Rear-facing camera");
+ info->position = QCameraDevice::BackFace;
+ info->isDefault = true;
+ break;
+ case AndroidCamera::CameraFacingFront:
+ info->id = QByteArray("front");
+ info->description = QStringLiteral("Front-facing camera");
+ info->position = QCameraDevice::FrontFace;
+ break;
+ 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)
+{
+ switch (format) {
+ case AndroidCamera::NV21:
+ return QVideoFrameFormat::Format_NV21;
+ case AndroidCamera::YUY2:
+ return QVideoFrameFormat::Format_YUYV;
+ case AndroidCamera::JPEG:
+ return QVideoFrameFormat::Format_Jpeg;
+ case AndroidCamera::YV12:
+ return QVideoFrameFormat::Format_YV12;
+ default:
+ return QVideoFrameFormat::Format_Invalid;
+ }
+}
+
+AndroidCamera::ImageFormat AndroidCamera::AndroidImageFormatFromQtPixelFormat(QVideoFrameFormat::PixelFormat format)
+{
+ switch (format) {
+ case QVideoFrameFormat::Format_NV21:
+ return AndroidCamera::NV21;
+ case QVideoFrameFormat::Format_YUYV:
+ return AndroidCamera::YUY2;
+ case QVideoFrameFormat::Format_Jpeg:
+ return AndroidCamera::JPEG;
+ case QVideoFrameFormat::Format_YV12:
+ return AndroidCamera::YV12;
+ default:
+ return AndroidCamera::UnknownImageFormat;
+ }
+}
+
+QList<QCameraFormat> AndroidCamera::getSupportedFormats()
+{
+ QList<QCameraFormat> formats;
+ AndroidCamera::FpsRange range = getPreviewFpsRange();
+ for (const auto &previewSize : getSupportedVideoSizes()) {
+ for (const auto &previewFormat : getSupportedPreviewFormats()) {
+ QCameraFormatPrivate * format = new QCameraFormatPrivate();
+ format->pixelFormat = QtPixelFormatFromAndroidImageFormat(previewFormat);
+ format->resolution = previewSize;
+ format->minFrameRate = range.min;
+ format->maxFrameRate = range.max;
+ formats.append(format->create());
+ }
+ }
+
+ return formats;
+}
+
+void AndroidCamera::startPreview()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "startPreview");
+}
+
+void AndroidCamera::stopPreview()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "stopPreview");
+}
+
+void AndroidCamera::stopPreviewSynchronous()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "stopPreview", Qt::BlockingQueuedConnection);
+}
+
+QJniObject AndroidCamera::getParametersObject()
+{
+ Q_D(AndroidCamera);
+ return d->m_parameters;
+}
+
+AndroidCameraPrivate::AndroidCameraPrivate()
+ : QObject()
+{
+}
+
+AndroidCameraPrivate::~AndroidCameraPrivate()
+{
+}
+
+static qint32 s_activeCameras = 0;
+
+bool AndroidCameraPrivate::init(int cameraId)
+{
+ m_cameraId = cameraId;
+ QJniEnvironment env;
+
+ const bool opened = s_activeCameras & (1 << cameraId);
+ if (opened)
+ return false;
+
+ m_camera = QJniObject::callStaticObjectMethod("android/hardware/Camera",
+ "open",
+ "(I)Landroid/hardware/Camera;",
+ cameraId);
+ if (!m_camera.isValid())
+ return false;
+
+ m_cameraListener = QJniObject(QtCameraListenerClassName, "(I)V", m_cameraId);
+ m_info = QJniObject("android/hardware/Camera$CameraInfo");
+ m_camera.callStaticMethod<void>("android/hardware/Camera",
+ "getCameraInfo",
+ "(ILandroid/hardware/Camera$CameraInfo;)V",
+ cameraId,
+ m_info.object());
+
+ QJniObject params = m_camera.callObjectMethod("getParameters",
+ "()Landroid/hardware/Camera$Parameters;");
+ m_parameters = QJniObject(params);
+ s_activeCameras |= 1 << cameraId;
+
+ return true;
+}
+
+void AndroidCameraPrivate::release()
+{
+ m_previewSize = QSize();
+ m_parametersMutex.lock();
+ m_parameters = QJniObject();
+ m_parametersMutex.unlock();
+ if (m_camera.isValid()) {
+ m_camera.callMethod<void>("release");
+ s_activeCameras &= ~(1 << m_cameraId);
+ }
+}
+
+bool AndroidCameraPrivate::lock()
+{
+ QJniEnvironment env;
+ auto methodId = env->GetMethodID(m_camera.objectClass(), "lock", "()V");
+ env->CallVoidMethod(m_camera.object(), methodId);
+
+ if (env.checkAndClearExceptions())
+ return false;
+ return true;
+}
+
+bool AndroidCameraPrivate::unlock()
+{
+ QJniEnvironment env;
+ auto methodId = env->GetMethodID(m_camera.objectClass(), "unlock", "()V");
+ env->CallVoidMethod(m_camera.object(), methodId);
+
+ if (env.checkAndClearExceptions())
+ return false;
+ return true;
+}
+
+bool AndroidCameraPrivate::reconnect()
+{
+ QJniEnvironment env;
+ auto methodId = env->GetMethodID(m_camera.objectClass(), "reconnect", "()V");
+ env->CallVoidMethod(m_camera.object(), methodId);
+
+ if (env.checkAndClearExceptions())
+ return false;
+ return true;
+}
+
+AndroidCamera::CameraFacing AndroidCameraPrivate::getFacing()
+{
+ return AndroidCamera::CameraFacing(m_info.getField<jint>("facing"));
+}
+
+int AndroidCameraPrivate::getNativeOrientation()
+{
+ return m_info.getField<jint>("orientation");
+}
+
+QSize AndroidCameraPrivate::getPreferredPreviewSizeForVideo()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return QSize();
+
+ QJniObject size = m_parameters.callObjectMethod("getPreferredPreviewSizeForVideo",
+ "()Landroid/hardware/Camera$Size;");
+
+ if (!size.isValid())
+ return QSize();
+
+ return QSize(size.getField<jint>("width"), size.getField<jint>("height"));
+}
+
+QList<QSize> AndroidCameraPrivate::getSupportedPreviewSizes()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+ return getSupportedPreviewSizes(m_parameters);
+}
+
+QList<QSize> AndroidCameraPrivate::getSupportedPreviewSizes(QJniObject &parameters)
+{
+ QList<QSize> list;
+
+ if (parameters.isValid()) {
+ QJniObject sizeList = parameters.callObjectMethod("getSupportedPreviewSizes",
+ "()Ljava/util/List;");
+ int count = sizeList.callMethod<jint>("size");
+ for (int i = 0; i < count; ++i) {
+ QJniObject size = sizeList.callObjectMethod("get",
+ "(I)Ljava/lang/Object;",
+ i);
+ list.append(QSize(size.getField<jint>("width"), size.getField<jint>("height")));
+ }
+
+ std::sort(list.begin(), list.end(), qt_sizeLessThan);
+ }
+
+ return list;
+}
+
+QList<AndroidCamera::FpsRange> AndroidCameraPrivate::getSupportedPreviewFpsRange()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ QJniEnvironment env;
+
+ QList<AndroidCamera::FpsRange> rangeList;
+
+ if (m_parameters.isValid()) {
+ QJniObject rangeListNative = m_parameters.callObjectMethod("getSupportedPreviewFpsRange",
+ "()Ljava/util/List;");
+ int count = rangeListNative.callMethod<jint>("size");
+
+ rangeList.reserve(count);
+
+ for (int i = 0; i < count; ++i) {
+ QJniObject range = rangeListNative.callObjectMethod("get",
+ "(I)Ljava/lang/Object;",
+ i);
+
+ jintArray jRange = static_cast<jintArray>(range.object());
+ jint* rangeArray = env->GetIntArrayElements(jRange, 0);
+
+ AndroidCamera::FpsRange fpsRange;
+
+ fpsRange.min = rangeArray[0];
+ fpsRange.max = rangeArray[1];
+
+ env->ReleaseIntArrayElements(jRange, rangeArray, 0);
+
+ rangeList << fpsRange;
+ }
+ }
+
+ return rangeList;
+}
+
+AndroidCamera::FpsRange AndroidCameraPrivate::getPreviewFpsRange()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+ return getPreviewFpsRange(m_parameters);
+}
+
+AndroidCamera::FpsRange AndroidCameraPrivate::getPreviewFpsRange(QJniObject &parameters)
+{
+ QJniEnvironment env;
+
+ AndroidCamera::FpsRange range;
+
+ if (!parameters.isValid())
+ return range;
+
+ jintArray jRangeArray = env->NewIntArray(2);
+ parameters.callMethod<void>("getPreviewFpsRange", "([I)V", jRangeArray);
+
+ jint* jRangeElements = env->GetIntArrayElements(jRangeArray, 0);
+
+ // Android Camera API returns values scaled by 1000, so divide here to report
+ // normal values for Qt
+ range.min = jRangeElements[0] / 1000;
+ range.max = jRangeElements[1] / 1000;
+
+ env->ReleaseIntArrayElements(jRangeArray, jRangeElements, 0);
+ env->DeleteLocalRef(jRangeArray);
+
+ return range;
+}
+
+void AndroidCameraPrivate::setPreviewFpsRange(int min, int max)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ // Android Camera API returns values scaled by 1000, so multiply here to
+ // give Android API the scale is expects
+ m_parameters.callMethod<void>("setPreviewFpsRange", "(II)V", min * 1000, max * 1000);
+}
+
+AndroidCamera::ImageFormat AndroidCameraPrivate::getPreviewFormat()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return AndroidCamera::UnknownImageFormat;
+
+ return AndroidCamera::ImageFormat(m_parameters.callMethod<jint>("getPreviewFormat"));
+}
+
+void AndroidCameraPrivate::setPreviewFormat(AndroidCamera::ImageFormat fmt)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setPreviewFormat", "(I)V", jint(fmt));
+ applyParameters();
+}
+
+QList<AndroidCamera::ImageFormat> AndroidCameraPrivate::getSupportedPreviewFormats()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+ return getSupportedPreviewFormats(m_parameters);
+}
+
+QList<AndroidCamera::ImageFormat> AndroidCameraPrivate::getSupportedPreviewFormats(QJniObject &parameters)
+{
+ QList<AndroidCamera::ImageFormat> list;
+
+ if (parameters.isValid()) {
+ QJniObject formatList = parameters.callObjectMethod("getSupportedPreviewFormats",
+ "()Ljava/util/List;");
+ int count = formatList.callMethod<jint>("size");
+ for (int i = 0; i < count; ++i) {
+ QJniObject format = formatList.callObjectMethod("get",
+ "(I)Ljava/lang/Object;",
+ i);
+ list.append(AndroidCamera::ImageFormat(format.callMethod<jint>("intValue")));
+ }
+ }
+
+ return list;
+}
+
+QSize AndroidCameraPrivate::getPreviewSize()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return QSize();
+
+ QJniObject size = m_parameters.callObjectMethod("getPreviewSize",
+ "()Landroid/hardware/Camera$Size;");
+
+ if (!size.isValid())
+ return QSize();
+
+ return QSize(size.getField<jint>("width"), size.getField<jint>("height"));
+}
+
+void AndroidCameraPrivate::updatePreviewSize()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (m_previewSize.isValid()) {
+ m_parameters.callMethod<void>("setPreviewSize", "(II)V", m_previewSize.width(), m_previewSize.height());
+ applyParameters();
+ }
+
+ emit previewSizeChanged();
+}
+
+bool AndroidCameraPrivate::setPreviewTexture(void *surfaceTexture)
+{
+ QJniEnvironment env;
+ auto methodId = env->GetMethodID(m_camera.objectClass(), "setPreviewTexture",
+ "(Landroid/graphics/SurfaceTexture;)V");
+ env->CallVoidMethod(m_camera.object(), methodId, static_cast<jobject>(surfaceTexture));
+
+ if (env.checkAndClearExceptions())
+ return false;
+ return true;
+}
+
+bool AndroidCameraPrivate::setPreviewDisplay(void *surfaceHolder)
+{
+ QJniEnvironment env;
+ auto methodId = env->GetMethodID(m_camera.objectClass(), "setPreviewDisplay",
+ "(Landroid/view/SurfaceHolder;)V");
+ env->CallVoidMethod(m_camera.object(), methodId, static_cast<jobject>(surfaceHolder));
+
+ if (env.checkAndClearExceptions())
+ return false;
+ return true;
+}
+
+void AndroidCameraPrivate::setDisplayOrientation(int degrees)
+{
+ m_camera.callMethod<void>("setDisplayOrientation", "(I)V", degrees);
+ m_cameraListener.callMethod<void>("setPhotoRotation", "(I)V", degrees);
+}
+
+bool AndroidCameraPrivate::isZoomSupported()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return false;
+
+ return m_parameters.callMethod<jboolean>("isZoomSupported");
+}
+
+int AndroidCameraPrivate::getMaxZoom()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return 0;
+
+ return m_parameters.callMethod<jint>("getMaxZoom");
+}
+
+QList<int> AndroidCameraPrivate::getZoomRatios()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ QList<int> ratios;
+
+ if (m_parameters.isValid()) {
+ QJniObject ratioList = m_parameters.callObjectMethod("getZoomRatios",
+ "()Ljava/util/List;");
+ int count = ratioList.callMethod<jint>("size");
+ for (int i = 0; i < count; ++i) {
+ QJniObject zoomRatio = ratioList.callObjectMethod("get",
+ "(I)Ljava/lang/Object;",
+ i);
+
+ ratios.append(zoomRatio.callMethod<jint>("intValue"));
+ }
+ }
+
+ return ratios;
+}
+
+int AndroidCameraPrivate::getZoom()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return 0;
+
+ return m_parameters.callMethod<jint>("getZoom");
+}
+
+void AndroidCameraPrivate::setZoom(int value)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setZoom", "(I)V", value);
+ applyParameters();
+}
+
+QString AndroidCameraPrivate::getFlashMode()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ QString value;
+
+ if (m_parameters.isValid()) {
+ QJniObject flashMode = m_parameters.callObjectMethod("getFlashMode",
+ "()Ljava/lang/String;");
+ if (flashMode.isValid())
+ value = flashMode.toString();
+ }
+
+ return value;
+}
+
+void AndroidCameraPrivate::setFlashMode(const QString &value)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setFlashMode",
+ "(Ljava/lang/String;)V",
+ QJniObject::fromString(value).object());
+ applyParameters();
+}
+
+QString AndroidCameraPrivate::getFocusMode()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ QString value;
+
+ if (m_parameters.isValid()) {
+ QJniObject focusMode = m_parameters.callObjectMethod("getFocusMode",
+ "()Ljava/lang/String;");
+ if (focusMode.isValid())
+ value = focusMode.toString();
+ }
+
+ return value;
+}
+
+void AndroidCameraPrivate::setFocusMode(const QString &value)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setFocusMode",
+ "(Ljava/lang/String;)V",
+ QJniObject::fromString(value).object());
+ applyParameters();
+}
+
+int AndroidCameraPrivate::getMaxNumFocusAreas()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return 0;
+
+ return m_parameters.callMethod<jint>("getMaxNumFocusAreas");
+}
+
+QList<QRect> AndroidCameraPrivate::getFocusAreas()
+{
+ QList<QRect> areas;
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (m_parameters.isValid()) {
+ QJniObject list = m_parameters.callObjectMethod("getFocusAreas",
+ "()Ljava/util/List;");
+
+ if (list.isValid()) {
+ int count = list.callMethod<jint>("size");
+ for (int i = 0; i < count; ++i) {
+ QJniObject area = list.callObjectMethod("get",
+ "(I)Ljava/lang/Object;",
+ i);
+
+ areas.append(areaToRect(area.object()));
+ }
+ }
+ }
+
+ return areas;
+}
+
+void AndroidCameraPrivate::setFocusAreas(const QList<QRect> &areas)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid() || areas.isEmpty())
+ return;
+
+ QJniObject list;
+
+ if (!areas.isEmpty()) {
+ QJniEnvironment env;
+ QJniObject arrayList("java/util/ArrayList", "(I)V", areas.size());
+ for (int i = 0; i < areas.size(); ++i) {
+ arrayList.callMethod<jboolean>("add",
+ "(Ljava/lang/Object;)Z",
+ rectToArea(areas.at(i)).object());
+ }
+ list = arrayList;
+ }
+
+ m_parameters.callMethod<void>("setFocusAreas", "(Ljava/util/List;)V", list.object());
+
+ applyParameters();
+}
+
+void AndroidCameraPrivate::autoFocus()
+{
+ QJniEnvironment env;
+ auto methodId = env->GetMethodID(m_camera.objectClass(), "autoFocus",
+ "(Landroid/hardware/Camera$AutoFocusCallback;)V");
+ env->CallVoidMethod(m_camera.object(), methodId, m_cameraListener.object());
+
+ if (!env.checkAndClearExceptions())
+ emit autoFocusStarted();
+}
+
+void AndroidCameraPrivate::cancelAutoFocus()
+{
+ QJniEnvironment env;
+ m_camera.callMethod<void>("cancelAutoFocus");
+}
+
+bool AndroidCameraPrivate::isAutoExposureLockSupported()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return false;
+
+ return m_parameters.callMethod<jboolean>("isAutoExposureLockSupported");
+}
+
+bool AndroidCameraPrivate::getAutoExposureLock()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return false;
+
+ return m_parameters.callMethod<jboolean>("getAutoExposureLock");
+}
+
+void AndroidCameraPrivate::setAutoExposureLock(bool toggle)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setAutoExposureLock", "(Z)V", toggle);
+ applyParameters();
+}
+
+bool AndroidCameraPrivate::isAutoWhiteBalanceLockSupported()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return false;
+
+ return m_parameters.callMethod<jboolean>("isAutoWhiteBalanceLockSupported");
+}
+
+bool AndroidCameraPrivate::getAutoWhiteBalanceLock()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return false;
+
+ return m_parameters.callMethod<jboolean>("getAutoWhiteBalanceLock");
+}
+
+void AndroidCameraPrivate::setAutoWhiteBalanceLock(bool toggle)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setAutoWhiteBalanceLock", "(Z)V", toggle);
+ applyParameters();
+}
+
+int AndroidCameraPrivate::getExposureCompensation()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return 0;
+
+ return m_parameters.callMethod<jint>("getExposureCompensation");
+}
+
+void AndroidCameraPrivate::setExposureCompensation(int value)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setExposureCompensation", "(I)V", value);
+ applyParameters();
+}
+
+float AndroidCameraPrivate::getExposureCompensationStep()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return 0;
+
+ return m_parameters.callMethod<jfloat>("getExposureCompensationStep");
+}
+
+int AndroidCameraPrivate::getMinExposureCompensation()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return 0;
+
+ return m_parameters.callMethod<jint>("getMinExposureCompensation");
+}
+
+int AndroidCameraPrivate::getMaxExposureCompensation()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return 0;
+
+ return m_parameters.callMethod<jint>("getMaxExposureCompensation");
+}
+
+QString AndroidCameraPrivate::getSceneMode()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ QString value;
+
+ if (m_parameters.isValid()) {
+ QJniObject sceneMode = m_parameters.callObjectMethod("getSceneMode",
+ "()Ljava/lang/String;");
+ if (sceneMode.isValid())
+ value = sceneMode.toString();
+ }
+
+ return value;
+}
+
+void AndroidCameraPrivate::setSceneMode(const QString &value)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setSceneMode",
+ "(Ljava/lang/String;)V",
+ QJniObject::fromString(value).object());
+ applyParameters();
+}
+
+QString AndroidCameraPrivate::getWhiteBalance()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ QString value;
+
+ if (m_parameters.isValid()) {
+ QJniObject wb = m_parameters.callObjectMethod("getWhiteBalance",
+ "()Ljava/lang/String;");
+ if (wb.isValid())
+ value = wb.toString();
+ }
+
+ return value;
+}
+
+void AndroidCameraPrivate::setWhiteBalance(const QString &value)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setWhiteBalance",
+ "(Ljava/lang/String;)V",
+ QJniObject::fromString(value).object());
+ applyParameters();
+
+ emit whiteBalanceChanged();
+}
+
+void AndroidCameraPrivate::updateRotation()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ m_parameters.callMethod<void>("setRotation", "(I)V", m_rotation);
+ applyParameters();
+}
+
+QList<QSize> AndroidCameraPrivate::getSupportedPictureSizes()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ QList<QSize> list;
+
+ if (m_parameters.isValid()) {
+ QJniObject sizeList = m_parameters.callObjectMethod("getSupportedPictureSizes",
+ "()Ljava/util/List;");
+ int count = sizeList.callMethod<jint>("size");
+ for (int i = 0; i < count; ++i) {
+ QJniObject size = sizeList.callObjectMethod("get",
+ "(I)Ljava/lang/Object;",
+ i);
+ list.append(QSize(size.getField<jint>("width"), size.getField<jint>("height")));
+ }
+
+ std::sort(list.begin(), list.end(), qt_sizeLessThan);
+ }
+
+ return list;
+}
+
+QList<QSize> AndroidCameraPrivate::getSupportedVideoSizes()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+ QList<QSize> list;
+
+ if (m_parameters.isValid()) {
+ QJniObject sizeList = m_parameters.callObjectMethod("getSupportedVideoSizes",
+ "()Ljava/util/List;");
+ if (!sizeList.isValid())
+ return list;
+
+ int count = sizeList.callMethod<jint>("size");
+ for (int i = 0; i < count; ++i) {
+ const QJniObject size = sizeList.callObjectMethod("get", "(I)Ljava/lang/Object;", i);
+ if (size.isValid())
+ list.append(QSize(size.getField<jint>("width"), size.getField<jint>("height")));
+ }
+ std::sort(list.begin(), list.end(), qt_sizeLessThan);
+ }
+
+ return list;
+}
+
+void AndroidCameraPrivate::setPictureSize(const QSize &size)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setPictureSize", "(II)V", size.width(), size.height());
+ applyParameters();
+}
+
+void AndroidCameraPrivate::setJpegQuality(int quality)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setJpegQuality", "(I)V", quality);
+ applyParameters();
+}
+
+void AndroidCameraPrivate::startPreview()
+{
+ setupPreviewFrameCallback();
+
+ QJniEnvironment env;
+ auto methodId = env->GetMethodID(m_camera.objectClass(), "startPreview", "()V");
+ env->CallVoidMethod(m_camera.object(), methodId);
+
+ if (env.checkAndClearExceptions())
+ emit previewFailedToStart();
+ else
+ emit previewStarted();
+}
+
+void AndroidCameraPrivate::stopPreview()
+{
+ // cancel any pending new frame notification
+ m_cameraListener.callMethod<void>("notifyWhenFrameAvailable", "(Z)V", false);
+ m_camera.callMethod<void>("stopPreview");
+ emit previewStopped();
+}
+
+void AndroidCameraPrivate::takePicture()
+{
+ // We must clear the preview callback before calling takePicture(), otherwise the call will
+ // block and the camera server will be frozen until the next device restart...
+ // That problem only happens on some devices and on the emulator
+ m_cameraListener.callMethod<void>("clearPreviewCallback", "(Landroid/hardware/Camera;)V", m_camera.object());
+
+ QJniEnvironment env;
+ auto methodId = env->GetMethodID(m_camera.objectClass(), "takePicture",
+ "(Landroid/hardware/Camera$ShutterCallback;"
+ "Landroid/hardware/Camera$PictureCallback;"
+ "Landroid/hardware/Camera$PictureCallback;)V");
+ env->CallVoidMethod(m_camera.object(), methodId, m_cameraListener.object(),
+ jobject(0), m_cameraListener.object());
+
+ if (env.checkAndClearExceptions())
+ emit takePictureFailed();
+}
+
+void AndroidCameraPrivate::setupPreviewFrameCallback()
+{
+ m_cameraListener.callMethod<void>("setupPreviewCallback", "(Landroid/hardware/Camera;)V", m_camera.object());
+}
+
+void AndroidCameraPrivate::notifyNewFrames(bool notify)
+{
+ m_cameraListener.callMethod<void>("notifyNewFrames", "(Z)V", notify);
+}
+
+void AndroidCameraPrivate::fetchLastPreviewFrame()
+{
+ QJniEnvironment env;
+ QJniObject data = m_cameraListener.callObjectMethod("lastPreviewBuffer", "()[B");
+
+ if (!data.isValid()) {
+ // If there's no buffer received yet, retry when the next one arrives
+ m_cameraListener.callMethod<void>("notifyWhenFrameAvailable", "(Z)V", true);
+ return;
+ }
+
+ const int arrayLength = env->GetArrayLength(static_cast<jbyteArray>(data.object()));
+ if (arrayLength == 0)
+ return;
+
+ QByteArray bytes(arrayLength, Qt::Uninitialized);
+ env->GetByteArrayRegion(static_cast<jbyteArray>(data.object()),
+ 0,
+ arrayLength,
+ reinterpret_cast<jbyte *>(bytes.data()));
+
+ const int width = m_cameraListener.callMethod<jint>("previewWidth");
+ const int height = m_cameraListener.callMethod<jint>("previewHeight");
+ const int format = m_cameraListener.callMethod<jint>("previewFormat");
+ const int bpl = m_cameraListener.callMethod<jint>("previewBytesPerLine");
+
+ QVideoFrameFormat frameFormat(
+ QSize(width, height),
+ qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat(format)));
+
+ QVideoFrame frame = QVideoFramePrivate::createFrame(
+ std::make_unique<QMemoryVideoBuffer>(std::move(bytes), bpl), std::move(frameFormat));
+
+ emit lastPreviewFrameFetched(frame);
+}
+
+void AndroidCameraPrivate::applyParameters()
+{
+ QJniEnvironment env;
+ m_camera.callMethod<void>("setParameters",
+ "(Landroid/hardware/Camera$Parameters;)V",
+ m_parameters.object());
+}
+
+QStringList AndroidCameraPrivate::callParametersStringListMethod(const QByteArray &methodName)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ QStringList stringList;
+
+ if (m_parameters.isValid()) {
+ QJniObject list = m_parameters.callObjectMethod(methodName.constData(),
+ "()Ljava/util/List;");
+
+ if (list.isValid()) {
+ int count = list.callMethod<jint>("size");
+ for (int i = 0; i < count; ++i) {
+ QJniObject string = list.callObjectMethod("get",
+ "(I)Ljava/lang/Object;",
+ i);
+ stringList.append(string.toString());
+ }
+ }
+ }
+
+ return stringList;
+}
+
+bool AndroidCamera::registerNativeMethods()
+{
+ static const JNINativeMethod methods[] = {
+ {"notifyAutoFocusComplete", "(IZ)V", (void *)notifyAutoFocusComplete},
+ {"notifyPictureExposed", "(I)V", (void *)notifyPictureExposed},
+ {"notifyPictureCaptured", "(I[B)V", (void *)notifyPictureCaptured},
+ {"notifyNewPreviewFrame", "(I[BIIII)V", (void *)notifyNewPreviewFrame},
+ {"notifyFrameAvailable", "(I)V", (void *)notifyFrameAvailable}
+ };
+
+ const int size = std::size(methods);
+ return QJniEnvironment().registerNativeMethods(QtCameraListenerClassName, methods, size);
+}
+
+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
new file mode 100644
index 000000000..8375cf3b1
--- /dev/null
+++ b/src/plugins/multimedia/android/wrappers/jni/androidcamera_p.h
@@ -0,0 +1,208 @@
+// 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
+
+//
+// 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 <qsize.h>
+#include <qrect.h>
+#include <QtMultimedia/qcamera.h>
+#include <QtCore/qjniobject.h>
+#include <private/qcameradevice_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QThread;
+
+class AndroidCameraPrivate;
+class AndroidSurfaceTexture;
+class AndroidSurfaceHolder;
+
+class AndroidCamera : public QObject
+{
+ Q_OBJECT
+public:
+ enum CameraFacing {
+ CameraFacingBack = 0,
+ CameraFacingFront = 1
+ };
+ Q_ENUM(CameraFacing)
+
+ enum ImageFormat { // same values as in android.graphics.ImageFormat Java class
+ UnknownImageFormat = 0,
+ RGB565 = 4,
+ NV16 = 16,
+ NV21 = 17,
+ YUY2 = 20,
+ JPEG = 256,
+ YV12 = 842094169
+ };
+ Q_ENUM(ImageFormat)
+
+ // http://developer.android.com/reference/android/hardware/Camera.Parameters.html#getSupportedPreviewFpsRange%28%29
+ // "The values are multiplied by 1000 and represented in integers"
+ struct FpsRange {
+ int min;
+ int max;
+
+ FpsRange(): min(0), max(0) {}
+
+ qreal getMinReal() const { return min / 1000.0; }
+ qreal getMaxReal() const { return max / 1000.0; }
+
+ static FpsRange makeFromQReal(qreal min, qreal max)
+ {
+ FpsRange range;
+ range.min = static_cast<int>(min * 1000.0);
+ range.max = static_cast<int>(max * 1000.0);
+ return range;
+ }
+ };
+
+ ~AndroidCamera();
+
+ static AndroidCamera *open(int cameraId);
+
+ int cameraId() const;
+
+ bool lock();
+ bool unlock();
+ bool reconnect();
+ void release();
+
+ CameraFacing getFacing();
+ int getNativeOrientation();
+
+ QSize getPreferredPreviewSizeForVideo();
+ QList<QSize> getSupportedPreviewSizes();
+
+ QList<FpsRange> getSupportedPreviewFpsRange();
+
+ FpsRange getPreviewFpsRange();
+ void setPreviewFpsRange(FpsRange);
+
+ ImageFormat getPreviewFormat();
+ void setPreviewFormat(ImageFormat fmt);
+ QList<ImageFormat> getSupportedPreviewFormats();
+
+ QSize previewSize() const;
+ QSize actualPreviewSize();
+ void setPreviewSize(const QSize &size);
+ bool setPreviewTexture(AndroidSurfaceTexture *surfaceTexture);
+ bool setPreviewDisplay(AndroidSurfaceHolder *surfaceHolder);
+ void setDisplayOrientation(int degrees);
+
+ bool isZoomSupported();
+ int getMaxZoom();
+ QList<int> getZoomRatios();
+ int getZoom();
+ void setZoom(int value);
+
+ QStringList getSupportedFlashModes();
+ QString getFlashMode();
+ void setFlashMode(const QString &value);
+
+ QStringList getSupportedFocusModes();
+ QString getFocusMode();
+ void setFocusMode(const QString &value);
+
+ int getMaxNumFocusAreas();
+ QList<QRect> getFocusAreas();
+ void setFocusAreas(const QList<QRect> &areas);
+
+ void autoFocus();
+ void cancelAutoFocus();
+
+ bool isAutoExposureLockSupported();
+ bool getAutoExposureLock();
+ void setAutoExposureLock(bool toggle);
+
+ bool isAutoWhiteBalanceLockSupported();
+ bool getAutoWhiteBalanceLock();
+ void setAutoWhiteBalanceLock(bool toggle);
+
+ int getExposureCompensation();
+ void setExposureCompensation(int value);
+ float getExposureCompensationStep();
+ int getMinExposureCompensation();
+ int getMaxExposureCompensation();
+
+ QStringList getSupportedSceneModes();
+ QString getSceneMode();
+ void setSceneMode(const QString &value);
+
+ QStringList getSupportedWhiteBalance();
+ QString getWhiteBalance();
+ void setWhiteBalance(const QString &value);
+
+ void setRotation(int rotation);
+ int getRotation() const;
+
+ QList<QCameraFormat> getSupportedFormats();
+ QList<QSize> getSupportedPictureSizes();
+ QList<QSize> getSupportedVideoSizes();
+ void setPictureSize(const QSize &size);
+ void setJpegQuality(int quality);
+
+ void startPreview();
+ void stopPreview();
+ void stopPreviewSynchronous();
+
+ void takePicture();
+
+ void setupPreviewFrameCallback();
+ void notifyNewFrames(bool notify);
+ void fetchLastPreviewFrame();
+ QJniObject getCameraObject();
+ QJniObject getParametersObject();
+
+ static int getNumberOfCameras();
+ static void getCameraInfo(int id, QCameraDevicePrivate *info);
+ static QVideoFrameFormat::PixelFormat QtPixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat);
+ static AndroidCamera::ImageFormat AndroidImageFormatFromQtPixelFormat(QVideoFrameFormat::PixelFormat);
+ static bool requestCameraPermission();
+
+ static bool registerNativeMethods();
+Q_SIGNALS:
+ void previewSizeChanged();
+ void previewStarted();
+ void previewFailedToStart();
+ void previewStopped();
+
+ void autoFocusStarted();
+ void autoFocusComplete(bool success);
+
+ void whiteBalanceChanged();
+
+ void takePictureFailed();
+ void pictureExposed();
+ void pictureCaptured(const QByteArray &frame, QVideoFrameFormat::PixelFormat format, QSize size, int bytesPerLine);
+ void lastPreviewFrameFetched(const QVideoFrame &frame);
+ void newPreviewFrame(const QVideoFrame &frame);
+
+private:
+ AndroidCamera(AndroidCameraPrivate *d, QThread *worker);
+
+ Q_DECLARE_PRIVATE(AndroidCamera)
+ AndroidCameraPrivate *d_ptr;
+ QScopedPointer<QThread> m_worker;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(AndroidCamera::ImageFormat)
+
+#endif // ANDROIDCAMERA_H
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidmediametadataretriever.cpp b/src/plugins/multimedia/android/wrappers/jni/androidmediametadataretriever.cpp
new file mode 100644
index 000000000..25e1efdb0
--- /dev/null
+++ b/src/plugins/multimedia/android/wrappers/jni/androidmediametadataretriever.cpp
@@ -0,0 +1,136 @@
+// 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"
+
+#include <QtCore/QUrl>
+#include <qdebug.h>
+#include <QtCore/qcoreapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+AndroidMediaMetadataRetriever::AndroidMediaMetadataRetriever()
+{
+ m_metadataRetriever = QJniObject("android/media/MediaMetadataRetriever");
+}
+
+AndroidMediaMetadataRetriever::~AndroidMediaMetadataRetriever()
+{
+ release();
+}
+
+QString AndroidMediaMetadataRetriever::extractMetadata(MetadataKey key)
+{
+ QString value;
+
+ QJniObject metadata = m_metadataRetriever.callObjectMethod("extractMetadata",
+ "(I)Ljava/lang/String;",
+ jint(key));
+ if (metadata.isValid())
+ value = metadata.toString();
+
+ return value;
+}
+
+void AndroidMediaMetadataRetriever::release()
+{
+ if (!m_metadataRetriever.isValid())
+ return;
+
+ m_metadataRetriever.callMethod<void>("release");
+}
+
+bool AndroidMediaMetadataRetriever::setDataSource(const QUrl &url)
+{
+ if (!m_metadataRetriever.isValid())
+ return false;
+
+ QJniEnvironment env;
+ if (url.isLocalFile()) { // also includes qrc files (copied to a temp file by QMediaPlayer)
+ QJniObject string = QJniObject::fromString(url.path());
+ QJniObject fileInputStream("java/io/FileInputStream",
+ "(Ljava/lang/String;)V",
+ string.object());
+
+ if (!fileInputStream.isValid())
+ return false;
+
+ QJniObject fd = fileInputStream.callObjectMethod("getFD",
+ "()Ljava/io/FileDescriptor;");
+ if (!fd.isValid()) {
+ fileInputStream.callMethod<void>("close");
+ return false;
+ }
+
+ auto methodId = env->GetMethodID(m_metadataRetriever.objectClass(), "setDataSource",
+ "(Ljava/io/FileDescriptor;)V");
+ env->CallVoidMethod(m_metadataRetriever.object(), methodId, fd.object());
+ bool ok = !env.checkAndClearExceptions();
+ fileInputStream.callMethod<void>("close");
+ if (!ok)
+ return false;
+ } else if (url.scheme() == QLatin1String("assets")) {
+ QJniObject string = QJniObject::fromString(url.path().mid(1)); // remove first '/'
+ QJniObject activity(QNativeInterface::QAndroidApplication::context());
+ QJniObject assetManager = activity.callObjectMethod("getAssets",
+ "()Landroid/content/res/AssetManager;");
+ QJniObject assetFd = assetManager.callObjectMethod("openFd",
+ "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;",
+ string.object());
+ if (!assetFd.isValid())
+ return false;
+
+ QJniObject fd = assetFd.callObjectMethod("getFileDescriptor",
+ "()Ljava/io/FileDescriptor;");
+ if (!fd.isValid()) {
+ assetFd.callMethod<void>("close");
+ return false;
+ }
+
+ auto methodId = env->GetMethodID(m_metadataRetriever.objectClass(), "setDataSource",
+ "(Ljava/io/FileDescriptor;JJ)V");
+ env->CallVoidMethod(m_metadataRetriever.object(), methodId,
+ fd.object(),
+ assetFd.callMethod<jlong>("getStartOffset"),
+ assetFd.callMethod<jlong>("getLength"));
+ bool ok = !env.checkAndClearExceptions();
+ assetFd.callMethod<void>("close");
+
+ if (!ok)
+ return false;
+ } else if (url.scheme() != QLatin1String("content")) {
+ // On API levels >= 14, only setDataSource(String, Map<String, String>) accepts remote media
+ QJniObject string = QJniObject::fromString(url.toString(QUrl::FullyEncoded));
+ QJniObject hash("java/util/HashMap");
+
+ auto methodId = env->GetMethodID(m_metadataRetriever.objectClass(), "setDataSource",
+ "(Ljava/lang/String;Ljava/util/Map;)V");
+ env->CallVoidMethod(m_metadataRetriever.object(), methodId,
+ string.object(), hash.object());
+ if (env.checkAndClearExceptions())
+ return false;
+ } else {
+ // While on API levels < 14, only setDataSource(Context, Uri) is available and works for
+ // remote media...
+ QJniObject string = QJniObject::fromString(url.toString(QUrl::FullyEncoded));
+ QJniObject uri = m_metadataRetriever.callStaticObjectMethod(
+ "android/net/Uri",
+ "parse",
+ "(Ljava/lang/String;)Landroid/net/Uri;",
+ string.object());
+ if (!uri.isValid())
+ return false;
+
+ auto methodId = env->GetMethodID(m_metadataRetriever.objectClass(), "setDataSource",
+ "(Landroid/content/Context;Landroid/net/Uri;)V");
+ env->CallVoidMethod(m_metadataRetriever.object(), methodId,
+ QNativeInterface::QAndroidApplication::context().object(),
+ uri.object());
+ if (env.checkAndClearExceptions())
+ return false;
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidmediametadataretriever_p.h b/src/plugins/multimedia/android/wrappers/jni/androidmediametadataretriever_p.h
new file mode 100644
index 000000000..68e346336
--- /dev/null
+++ b/src/plugins/multimedia/android/wrappers/jni/androidmediametadataretriever_p.h
@@ -0,0 +1,66 @@
+// 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
+
+//
+// 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 <QtCore/qurl.h>
+#include <QtCore/qjniobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class AndroidMediaMetadataRetriever
+{
+public:
+ enum MetadataKey {
+ Album = 1,
+ AlbumArtist = 13,
+ Artist = 2,
+ Author = 3,
+ Bitrate = 20,
+ CDTrackNumber = 0,
+ Compilation = 15,
+ Composer = 4,
+ Date = 5,
+ DiscNumber = 14,
+ Duration = 9,
+ Genre = 6,
+ HasAudio = 16,
+ HasVideo = 17,
+ Location = 23,
+ MimeType = 12,
+ NumTracks = 10,
+ Title = 7,
+ VideoHeight = 19,
+ VideoWidth = 18,
+ VideoRotation = 24,
+ Writer = 11,
+ Year = 8
+ };
+
+ AndroidMediaMetadataRetriever();
+ ~AndroidMediaMetadataRetriever();
+
+ QString extractMetadata(MetadataKey key);
+ bool setDataSource(const QUrl &url);
+
+private:
+ void release();
+ QJniObject m_metadataRetriever;
+};
+
+QT_END_NAMESPACE
+
+#endif // ANDROIDMEDIAMETADATARETRIEVER_H
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidmediaplayer.cpp b/src/plugins/multimedia/android/wrappers/jni/androidmediaplayer.cpp
new file mode 100644
index 000000000..91f489f9e
--- /dev/null
+++ b/src/plugins/multimedia/android/wrappers/jni/androidmediaplayer.cpp
@@ -0,0 +1,535 @@
+// 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"
+
+#include <QList>
+#include <QReadWriteLock>
+#include <QString>
+#include <QtCore/qcoreapplication.h>
+#include <qloggingcategory.h>
+
+static const char QtAndroidMediaPlayerClassName[] = "org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer";
+typedef QList<AndroidMediaPlayer *> MediaPlayerList;
+Q_GLOBAL_STATIC(MediaPlayerList, mediaPlayers)
+Q_GLOBAL_STATIC(QReadWriteLock, rwLock)
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(lcAudio, "qt.multimedia.audio")
+
+AndroidMediaPlayer::AndroidMediaPlayer()
+ : QObject()
+{
+ QWriteLocker locker(rwLock);
+ auto context = QNativeInterface::QAndroidApplication::context();
+ const jlong id = reinterpret_cast<jlong>(this);
+ mMediaPlayer = QJniObject(QtAndroidMediaPlayerClassName,
+ "(Landroid/content/Context;J)V",
+ context.object(),
+ id);
+ mediaPlayers->append(this);
+}
+
+AndroidMediaPlayer::~AndroidMediaPlayer()
+{
+ QWriteLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(this);
+ Q_ASSERT(i != -1);
+ mediaPlayers->remove(i);
+}
+
+void AndroidMediaPlayer::release()
+{
+ mMediaPlayer.callMethod<void>("release");
+}
+
+void AndroidMediaPlayer::reset()
+{
+ mMediaPlayer.callMethod<void>("reset");
+}
+
+int AndroidMediaPlayer::getCurrentPosition()
+{
+ return mMediaPlayer.callMethod<jint>("getCurrentPosition");
+}
+
+int AndroidMediaPlayer::getDuration()
+{
+ return mMediaPlayer.callMethod<jint>("getDuration");
+}
+
+bool AndroidMediaPlayer::isPlaying()
+{
+ return mMediaPlayer.callMethod<jboolean>("isPlaying");
+}
+
+int AndroidMediaPlayer::volume()
+{
+ return mMediaPlayer.callMethod<jint>("getVolume");
+}
+
+bool AndroidMediaPlayer::isMuted()
+{
+ return mMediaPlayer.callMethod<jboolean>("isMuted");
+}
+
+qreal AndroidMediaPlayer::playbackRate()
+{
+ qreal rate(1.0);
+
+ if (QNativeInterface::QAndroidApplication::sdkVersion() < 23)
+ return rate;
+
+ QJniObject player = mMediaPlayer.callObjectMethod("getMediaPlayerHandle",
+ "()Landroid/media/MediaPlayer;");
+ if (player.isValid()) {
+ QJniObject playbackParams = player.callObjectMethod("getPlaybackParams",
+ "()Landroid/media/PlaybackParams;");
+ if (playbackParams.isValid()) {
+ QJniEnvironment env;
+ auto methodId = env->GetMethodID(playbackParams.objectClass(), "getSpeed", "()F");
+ const qreal speed = env->CallFloatMethod(playbackParams.object(), methodId);
+ if (!env.checkAndClearExceptions())
+ rate = speed;
+ }
+ }
+
+ return rate;
+}
+
+jobject AndroidMediaPlayer::display()
+{
+ return mMediaPlayer.callObjectMethod("display", "()Landroid/view/SurfaceHolder;").object();
+}
+
+AndroidMediaPlayer::TrackInfo convertTrackInfo(int streamNumber, QJniObject androidTrackInfo)
+{
+ const QLatin1String unknownMimeType("application/octet-stream");
+ const QLatin1String undefinedLanguage("und");
+
+ if (!androidTrackInfo.isValid())
+ return { streamNumber, AndroidMediaPlayer::TrackType::Unknown, undefinedLanguage,
+ unknownMimeType };
+
+ QJniEnvironment env;
+ auto methodId = env->GetMethodID(androidTrackInfo.objectClass(), "getType", "()I");
+ const jint type = env->CallIntMethod(androidTrackInfo.object(), methodId);
+ if (env.checkAndClearExceptions())
+ return { streamNumber, AndroidMediaPlayer::TrackType::Unknown, undefinedLanguage,
+ unknownMimeType };
+
+ if (type < 0 || type > 5) {
+ return { streamNumber, AndroidMediaPlayer::TrackType::Unknown, undefinedLanguage,
+ unknownMimeType };
+ }
+
+ AndroidMediaPlayer::TrackType trackType = static_cast<AndroidMediaPlayer::TrackType>(type);
+
+ auto languageObject = androidTrackInfo.callObjectMethod("getLanguage", "()Ljava/lang/String;");
+ QString language = languageObject.isValid() ? languageObject.toString() : undefinedLanguage;
+
+ auto mimeTypeObject = androidTrackInfo.callObjectMethod("getMime", "()Ljava/lang/String;");
+ QString mimeType = mimeTypeObject.isValid() ? mimeTypeObject.toString() : unknownMimeType;
+
+ return { streamNumber, trackType, language, mimeType };
+}
+
+QList<AndroidMediaPlayer::TrackInfo> AndroidMediaPlayer::tracksInfo()
+{
+ auto androidTracksInfoObject = mMediaPlayer.callObjectMethod(
+ "getAllTrackInfo",
+ "()[Lorg/qtproject/qt/android/multimedia/QtAndroidMediaPlayer$TrackInfo;");
+
+ if (!androidTracksInfoObject.isValid())
+ return QList<AndroidMediaPlayer::TrackInfo>();
+
+ auto androidTracksInfo = androidTracksInfoObject.object<jobjectArray>();
+ if (!androidTracksInfo)
+ return QList<AndroidMediaPlayer::TrackInfo>();
+
+ QJniEnvironment environment;
+ auto numberofTracks = environment->GetArrayLength(androidTracksInfo);
+
+ QList<AndroidMediaPlayer::TrackInfo> tracksInformation;
+
+ for (int index = 0; index < numberofTracks; index++) {
+ auto androidTrackInformation = environment->GetObjectArrayElement(androidTracksInfo, index);
+
+ if (environment.checkAndClearExceptions()) {
+ continue;
+ }
+
+ auto trackInfo = convertTrackInfo(index, androidTrackInformation);
+ tracksInformation.insert(index, trackInfo);
+
+ environment->DeleteLocalRef(androidTrackInformation);
+ }
+
+ return tracksInformation;
+}
+
+int AndroidMediaPlayer::activeTrack(TrackType androidTrackType)
+{
+ int type = static_cast<int>(androidTrackType);
+ return mMediaPlayer.callMethod<jint>("getSelectedTrack", "(I)I", type);
+}
+
+void AndroidMediaPlayer::deselectTrack(int trackNumber)
+{
+ mMediaPlayer.callMethod<void>("deselectTrack", "(I)V", trackNumber);
+}
+
+void AndroidMediaPlayer::selectTrack(int trackNumber)
+{
+ mMediaPlayer.callMethod<void>("selectTrack", "(I)V", trackNumber);
+}
+
+void AndroidMediaPlayer::play()
+{
+ mMediaPlayer.callMethod<void>("start");
+}
+
+void AndroidMediaPlayer::pause()
+{
+ mMediaPlayer.callMethod<void>("pause");
+}
+
+void AndroidMediaPlayer::stop()
+{
+ mMediaPlayer.callMethod<void>("stop");
+}
+
+void AndroidMediaPlayer::seekTo(qint32 msec)
+{
+ mMediaPlayer.callMethod<void>("seekTo", "(I)V", jint(msec));
+}
+
+void AndroidMediaPlayer::setMuted(bool mute)
+{
+ if (mAudioBlocked)
+ return;
+
+ mMediaPlayer.callMethod<void>("mute", "(Z)V", jboolean(mute));
+}
+
+void AndroidMediaPlayer::setDataSource(const QNetworkRequest &request)
+{
+ QJniObject string = QJniObject::fromString(request.url().toString(QUrl::FullyEncoded));
+
+ mMediaPlayer.callMethod<void>("initHeaders", "()V");
+ for (auto &header : request.rawHeaderList()) {
+ auto value = request.rawHeader(header);
+ mMediaPlayer.callMethod<void>("setHeader", "(Ljava/lang/String;Ljava/lang/String;)V",
+ QJniObject::fromString(QLatin1String(header)).object(),
+ QJniObject::fromString(QLatin1String(value)).object());
+ }
+
+ mMediaPlayer.callMethod<void>("setDataSource", "(Ljava/lang/String;)V", string.object());
+}
+
+void AndroidMediaPlayer::prepareAsync()
+{
+ mMediaPlayer.callMethod<void>("prepareAsync");
+}
+
+void AndroidMediaPlayer::setVolume(int volume)
+{
+ if (mAudioBlocked)
+ return;
+
+ mMediaPlayer.callMethod<void>("setVolume", "(I)V", jint(volume));
+}
+
+void AndroidMediaPlayer::blockAudio()
+{
+ mAudioBlocked = true;
+}
+
+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) {
+ qWarning() << "Setting the playback rate on a media player requires"
+ << "Android 6.0 (API level 23) or later";
+ return false;
+ }
+
+ return mMediaPlayer.callMethod<jboolean>("setPlaybackRate", jfloat(rate));
+}
+
+void AndroidMediaPlayer::setDisplay(AndroidSurfaceTexture *surfaceTexture)
+{
+ mMediaPlayer.callMethod<void>("setDisplay",
+ "(Landroid/view/SurfaceHolder;)V",
+ surfaceTexture ? surfaceTexture->surfaceHolder() : 0);
+}
+
+bool AndroidMediaPlayer::setAudioOutput(const QByteArray &deviceId)
+{
+ const bool ret = QJniObject::callStaticMethod<jboolean>(
+ "org/qtproject/qt/android/multimedia/QtAudioDeviceManager",
+ "setAudioOutput",
+ "(I)Z",
+ deviceId.toInt());
+
+ if (!ret)
+ qCWarning(lcAudio) << "Output device not set";
+
+ return ret;
+}
+
+#if 0
+void AndroidMediaPlayer::setAudioRole(QAudio::Role role)
+{
+ QString r;
+ switch (role) {
+ case QAudio::MusicRole:
+ r = QLatin1String("CONTENT_TYPE_MUSIC");
+ break;
+ case QAudio::VideoRole:
+ r = QLatin1String("CONTENT_TYPE_MOVIE");
+ break;
+ case QAudio::VoiceCommunicationRole:
+ r = QLatin1String("USAGE_VOICE_COMMUNICATION");
+ break;
+ case QAudio::AlarmRole:
+ r = QLatin1String("USAGE_ALARM");
+ break;
+ case QAudio::NotificationRole:
+ r = QLatin1String("USAGE_NOTIFICATION");
+ break;
+ case QAudio::RingtoneRole:
+ r = QLatin1String("USAGE_NOTIFICATION_RINGTONE");
+ break;
+ case QAudio::AccessibilityRole:
+ r = QLatin1String("USAGE_ASSISTANCE_ACCESSIBILITY");
+ break;
+ case QAudio::SonificationRole:
+ r = QLatin1String("CONTENT_TYPE_SONIFICATION");
+ break;
+ case QAudio::GameRole:
+ r = QLatin1String("USAGE_GAME");
+ break;
+ default:
+ return;
+ }
+
+ int type = 0; // CONTENT_TYPE_UNKNOWN
+ int usage = 0; // USAGE_UNKNOWN
+
+ if (r == QLatin1String("CONTENT_TYPE_MOVIE"))
+ type = 3;
+ else if (r == QLatin1String("CONTENT_TYPE_MUSIC"))
+ type = 2;
+ else if (r == QLatin1String("CONTENT_TYPE_SONIFICATION"))
+ type = 4;
+ else if (r == QLatin1String("CONTENT_TYPE_SPEECH"))
+ type = 1;
+ else if (r == QLatin1String("USAGE_ALARM"))
+ usage = 4;
+ else if (r == QLatin1String("USAGE_ASSISTANCE_ACCESSIBILITY"))
+ usage = 11;
+ else if (r == QLatin1String("USAGE_ASSISTANCE_NAVIGATION_GUIDANCE"))
+ usage = 12;
+ else if (r == QLatin1String("USAGE_ASSISTANCE_SONIFICATION"))
+ usage = 13;
+ else if (r == QLatin1String("USAGE_ASSISTANT"))
+ usage = 16;
+ else if (r == QLatin1String("USAGE_GAME"))
+ usage = 14;
+ else if (r == QLatin1String("USAGE_MEDIA"))
+ usage = 1;
+ else if (r == QLatin1String("USAGE_NOTIFICATION"))
+ usage = 5;
+ else if (r == QLatin1String("USAGE_NOTIFICATION_COMMUNICATION_DELAYED"))
+ usage = 9;
+ else if (r == QLatin1String("USAGE_NOTIFICATION_COMMUNICATION_INSTANT"))
+ usage = 8;
+ else if (r == QLatin1String("USAGE_NOTIFICATION_COMMUNICATION_REQUEST"))
+ usage = 7;
+ else if (r == QLatin1String("USAGE_NOTIFICATION_EVENT"))
+ usage = 10;
+ else if (r == QLatin1String("USAGE_NOTIFICATION_RINGTONE"))
+ usage = 6;
+ else if (r == QLatin1String("USAGE_VOICE_COMMUNICATION"))
+ usage = 2;
+ else if (r == QLatin1String("USAGE_VOICE_COMMUNICATION_SIGNALLING"))
+ usage = 3;
+
+ mMediaPlayer.callMethod<void>("setAudioAttributes", "(II)V", jint(type), jint(usage));
+}
+#endif
+
+static void onErrorNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ Q_EMIT (*mediaPlayers)[i]->error(what, extra);
+}
+
+static void onBufferingUpdateNative(JNIEnv *env, jobject thiz, jint percent, jlong id)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ Q_EMIT (*mediaPlayers)[i]->bufferingChanged(percent);
+}
+
+static void onProgressUpdateNative(JNIEnv *env, jobject thiz, jint progress, jlong id)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ Q_EMIT (*mediaPlayers)[i]->progressChanged(progress);
+}
+
+static void onDurationChangedNative(JNIEnv *env, jobject thiz, jint duration, jlong id)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ Q_EMIT (*mediaPlayers)[i]->durationChanged(duration);
+}
+
+static void onInfoNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ Q_EMIT (*mediaPlayers)[i]->info(what, extra);
+}
+
+static void onStateChangedNative(JNIEnv *env, jobject thiz, jint state, jlong id)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ Q_EMIT (*mediaPlayers)[i]->stateChanged(state);
+}
+
+static void onVideoSizeChangedNative(JNIEnv *env,
+ jobject thiz,
+ jint width,
+ jint height,
+ jlong id)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ Q_EMIT (*mediaPlayers)[i]->videoSizeChanged(width, height);
+}
+
+static AndroidMediaPlayer *getMediaPlayer(jlong ptr)
+{
+ auto mediaplayer = reinterpret_cast<AndroidMediaPlayer *>(ptr);
+ if (!mediaplayer || !mediaPlayers->contains(mediaplayer))
+ return nullptr;
+
+ return mediaplayer;
+}
+
+static void onTrackInfoChangedNative(JNIEnv *env, jobject thiz, jlong ptr)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+
+ QReadLocker locker(rwLock);
+ auto mediaplayer = getMediaPlayer(ptr);
+ if (!mediaplayer)
+ return;
+
+ emit mediaplayer->tracksInfoChanged();
+}
+
+static void onTimedTextChangedNative(JNIEnv *env, jobject thiz, jstring timedText, jint time,
+ jlong ptr)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ Q_UNUSED(time);
+
+ QReadLocker locker(rwLock);
+
+ auto mediaplayer = getMediaPlayer(ptr);
+ if (!mediaplayer)
+ return;
+
+ QString subtitleText;
+ if (timedText != nullptr)
+ subtitleText = QString::fromUtf8(env->GetStringUTFChars(timedText, 0));
+
+ emit mediaplayer->timedTextChanged(subtitleText);
+}
+
+bool AndroidMediaPlayer::registerNativeMethods()
+{
+ static const JNINativeMethod methods[] = {
+ { "onErrorNative", "(IIJ)V", reinterpret_cast<void *>(onErrorNative) },
+ { "onBufferingUpdateNative", "(IJ)V", reinterpret_cast<void *>(onBufferingUpdateNative) },
+ { "onProgressUpdateNative", "(IJ)V", reinterpret_cast<void *>(onProgressUpdateNative) },
+ { "onDurationChangedNative", "(IJ)V", reinterpret_cast<void *>(onDurationChangedNative) },
+ { "onInfoNative", "(IIJ)V", reinterpret_cast<void *>(onInfoNative) },
+ { "onVideoSizeChangedNative", "(IIJ)V",
+ reinterpret_cast<void *>(onVideoSizeChangedNative) },
+ { "onStateChangedNative", "(IJ)V", reinterpret_cast<void *>(onStateChangedNative) },
+ { "onTrackInfoChangedNative", "(J)V", reinterpret_cast<void *>(onTrackInfoChangedNative) },
+ { "onTimedTextChangedNative", "(Ljava/lang/String;IJ)V",
+ reinterpret_cast<void *>(onTimedTextChangedNative) }
+ };
+
+ const int size = std::size(methods);
+ return QJniEnvironment().registerNativeMethods(QtAndroidMediaPlayerClassName, methods, size);
+}
+
+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
new file mode 100644
index 000000000..66095b114
--- /dev/null
+++ b/src/plugins/multimedia/android/wrappers/jni/androidmediaplayer_p.h
@@ -0,0 +1,135 @@
+// 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
+
+//
+// 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 <QNetworkRequest>
+#include <QtCore/qjniobject.h>
+#include <QAudio>
+
+QT_BEGIN_NAMESPACE
+
+class AndroidSurfaceTexture;
+
+class AndroidMediaPlayer : public QObject
+{
+ Q_OBJECT
+public:
+ AndroidMediaPlayer();
+ ~AndroidMediaPlayer();
+
+ enum MediaError
+ {
+ // What
+ MEDIA_ERROR_UNKNOWN = 1,
+ MEDIA_ERROR_SERVER_DIED = 100,
+ MEDIA_ERROR_INVALID_STATE = -38, // Undocumented
+ // Extra
+ MEDIA_ERROR_IO = -1004,
+ MEDIA_ERROR_MALFORMED = -1007,
+ MEDIA_ERROR_UNSUPPORTED = -1010,
+ MEDIA_ERROR_TIMED_OUT = -110,
+ MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200,
+ MEDIA_ERROR_BAD_THINGS_ARE_GOING_TO_HAPPEN = -2147483648 // Undocumented
+ };
+
+ enum MediaInfo
+ {
+ MEDIA_INFO_UNKNOWN = 1,
+ MEDIA_INFO_VIDEO_TRACK_LAGGING = 700,
+ MEDIA_INFO_VIDEO_RENDERING_START = 3,
+ MEDIA_INFO_BUFFERING_START = 701,
+ MEDIA_INFO_BUFFERING_END = 702,
+ MEDIA_INFO_BAD_INTERLEAVING = 800,
+ MEDIA_INFO_NOT_SEEKABLE = 801,
+ MEDIA_INFO_METADATA_UPDATE = 802
+ };
+
+ enum MediaPlayerState {
+ Uninitialized = 0x1, /* End */
+ Idle = 0x2,
+ Preparing = 0x4,
+ Prepared = 0x8,
+ Initialized = 0x10,
+ Started = 0x20,
+ Stopped = 0x40,
+ Paused = 0x80,
+ PlaybackCompleted = 0x100,
+ Error = 0x200
+ };
+
+ enum TrackType { Unknown = 0, Video, Audio, TimedText, Subtitle, Metadata };
+
+ struct TrackInfo
+ {
+ int trackNumber;
+ TrackType trackType;
+ QString language;
+ QString mimeType;
+ };
+
+ void release();
+ void reset();
+
+ int getCurrentPosition();
+ int getDuration();
+ bool isPlaying();
+ int volume();
+ bool isMuted();
+ qreal playbackRate();
+ jobject display();
+
+ void play();
+ void pause();
+ void stop();
+ void seekTo(qint32 msec);
+ void setMuted(bool mute);
+ 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);
+ QList<TrackInfo> tracksInfo();
+ int activeTrack(TrackType trackType);
+ void deselectTrack(int trackNumber);
+ void selectTrack(int trackNumber);
+
+ static bool registerNativeMethods();
+
+ void blockAudio();
+ void unblockAudio();
+Q_SIGNALS:
+ void error(qint32 what, qint32 extra);
+ void bufferingChanged(qint32 percent);
+ void durationChanged(qint64 duration);
+ void progressChanged(qint64 progress);
+ void stateChanged(qint32 state);
+ void info(qint32 what, qint32 extra);
+ void videoSizeChanged(qint32 width, qint32 height);
+ void timedTextChanged(QString text);
+ void tracksInfoChanged();
+
+private:
+ QJniObject mMediaPlayer;
+ bool mAudioBlocked = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // ANDROIDMEDIAPLAYER_H
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidmediarecorder.cpp b/src/plugins/multimedia/android/wrappers/jni/androidmediarecorder.cpp
new file mode 100644
index 000000000..a3c9f4556
--- /dev/null
+++ b/src/plugins/multimedia/android/wrappers/jni/androidmediarecorder.cpp
@@ -0,0 +1,337 @@
+// 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"
+#include "androidsurfacetexture_p.h"
+#include "androidsurfaceview_p.h"
+#include "qandroidglobal_p.h"
+#include "qandroidmultimediautils_p.h"
+
+#include <qmap.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qlogging.h>
+#include <QtCore/qurl.h>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(lcMediaRecorder, "qt.multimedia.mediarecorder.android")
+
+typedef QMap<QString, QJniObject> CamcorderProfiles;
+Q_GLOBAL_STATIC(CamcorderProfiles, g_camcorderProfiles)
+
+static QString profileKey()
+{
+ return QStringLiteral("%1-%2");
+}
+
+bool AndroidCamcorderProfile::hasProfile(jint cameraId, Quality quality)
+{
+ if (g_camcorderProfiles->contains(profileKey().arg(cameraId).arg(quality)))
+ return true;
+
+ return QJniObject::callStaticMethod<jboolean>("android/media/CamcorderProfile",
+ "hasProfile",
+ "(II)Z",
+ cameraId,
+ quality);
+}
+
+AndroidCamcorderProfile AndroidCamcorderProfile::get(jint cameraId, Quality quality)
+{
+ const QString key = profileKey().arg(cameraId).arg(quality);
+ QMap<QString, QJniObject>::const_iterator it = g_camcorderProfiles->constFind(key);
+
+ if (it != g_camcorderProfiles->constEnd())
+ return AndroidCamcorderProfile(*it);
+
+ QJniObject camProfile = QJniObject::callStaticObjectMethod("android/media/CamcorderProfile",
+ "get",
+ "(II)Landroid/media/CamcorderProfile;",
+ cameraId,
+ quality);
+
+ return AndroidCamcorderProfile((*g_camcorderProfiles)[key] = camProfile);
+}
+
+int AndroidCamcorderProfile::getValue(AndroidCamcorderProfile::Field field) const
+{
+ switch (field) {
+ case audioBitRate:
+ return m_camcorderProfile.getField<jint>("audioBitRate");
+ case audioChannels:
+ return m_camcorderProfile.getField<jint>("audioChannels");
+ case audioCodec:
+ return m_camcorderProfile.getField<jint>("audioCodec");
+ case audioSampleRate:
+ return m_camcorderProfile.getField<jint>("audioSampleRate");
+ case duration:
+ return m_camcorderProfile.getField<jint>("duration");
+ case fileFormat:
+ return m_camcorderProfile.getField<jint>("fileFormat");
+ case quality:
+ return m_camcorderProfile.getField<jint>("quality");
+ case videoBitRate:
+ return m_camcorderProfile.getField<jint>("videoBitRate");
+ case videoCodec:
+ return m_camcorderProfile.getField<jint>("videoCodec");
+ case videoFrameHeight:
+ return m_camcorderProfile.getField<jint>("videoFrameHeight");
+ case videoFrameRate:
+ return m_camcorderProfile.getField<jint>("videoFrameRate");
+ case videoFrameWidth:
+ return m_camcorderProfile.getField<jint>("videoFrameWidth");
+ }
+
+ return 0;
+}
+
+AndroidCamcorderProfile::AndroidCamcorderProfile(const QJniObject &camcorderProfile)
+{
+ m_camcorderProfile = camcorderProfile;
+}
+
+static const char QtMediaRecorderListenerClassName[] =
+ "org/qtproject/qt/android/multimedia/QtMediaRecorderListener";
+typedef QMap<jlong, AndroidMediaRecorder*> MediaRecorderMap;
+Q_GLOBAL_STATIC(MediaRecorderMap, mediaRecorders)
+
+static void notifyError(JNIEnv* , jobject, jlong id, jint what, jint extra)
+{
+ AndroidMediaRecorder *obj = mediaRecorders->value(id, 0);
+ if (obj)
+ emit obj->error(what, extra);
+}
+
+static void notifyInfo(JNIEnv* , jobject, jlong id, jint what, jint extra)
+{
+ AndroidMediaRecorder *obj = mediaRecorders->value(id, 0);
+ if (obj)
+ emit obj->info(what, extra);
+}
+
+AndroidMediaRecorder::AndroidMediaRecorder()
+ : QObject()
+ , m_id(reinterpret_cast<jlong>(this))
+{
+ m_mediaRecorder = QJniObject("android/media/MediaRecorder");
+ if (m_mediaRecorder.isValid()) {
+ QJniObject listener(QtMediaRecorderListenerClassName, "(J)V", m_id);
+ m_mediaRecorder.callMethod<void>("setOnErrorListener",
+ "(Landroid/media/MediaRecorder$OnErrorListener;)V",
+ listener.object());
+ m_mediaRecorder.callMethod<void>("setOnInfoListener",
+ "(Landroid/media/MediaRecorder$OnInfoListener;)V",
+ listener.object());
+ mediaRecorders->insert(m_id, this);
+ }
+}
+
+AndroidMediaRecorder::~AndroidMediaRecorder()
+{
+ if (m_isVideoSourceSet || m_isAudioSourceSet)
+ reset();
+
+ release();
+ mediaRecorders->remove(m_id);
+}
+
+void AndroidMediaRecorder::release()
+{
+ m_mediaRecorder.callMethod<void>("release");
+}
+
+bool AndroidMediaRecorder::prepare()
+{
+ QJniEnvironment env;
+ auto methodId = env->GetMethodID(m_mediaRecorder.objectClass(), "prepare", "()V");
+ env->CallVoidMethod(m_mediaRecorder.object(), methodId);
+
+ if (env.checkAndClearExceptions())
+ return false;
+ return true;
+}
+
+void AndroidMediaRecorder::reset()
+{
+ m_mediaRecorder.callMethod<void>("reset");
+ m_isAudioSourceSet = false; // Now setAudioSource can be used again.
+ m_isVideoSourceSet = false;
+}
+
+bool AndroidMediaRecorder::start()
+{
+ QJniEnvironment env;
+ auto methodId = env->GetMethodID(m_mediaRecorder.objectClass(), "start", "()V");
+ env->CallVoidMethod(m_mediaRecorder.object(), methodId);
+
+ if (env.checkAndClearExceptions())
+ return false;
+ return true;
+}
+
+void AndroidMediaRecorder::stop()
+{
+ m_mediaRecorder.callMethod<void>("stop");
+}
+
+void AndroidMediaRecorder::setAudioChannels(int numChannels)
+{
+ m_mediaRecorder.callMethod<void>("setAudioChannels", "(I)V", numChannels);
+}
+
+void AndroidMediaRecorder::setAudioEncoder(AudioEncoder encoder)
+{
+ QJniEnvironment env;
+ m_mediaRecorder.callMethod<void>("setAudioEncoder", "(I)V", int(encoder));
+}
+
+void AndroidMediaRecorder::setAudioEncodingBitRate(int bitRate)
+{
+ m_mediaRecorder.callMethod<void>("setAudioEncodingBitRate", "(I)V", bitRate);
+}
+
+void AndroidMediaRecorder::setAudioSamplingRate(int samplingRate)
+{
+ m_mediaRecorder.callMethod<void>("setAudioSamplingRate", "(I)V", samplingRate);
+}
+
+void AndroidMediaRecorder::setAudioSource(AudioSource source)
+{
+ if (!m_isAudioSourceSet) {
+ QJniEnvironment env;
+ auto methodId = env->GetMethodID(m_mediaRecorder.objectClass(), "setAudioSource", "(I)V");
+ env->CallVoidMethod(m_mediaRecorder.object(), methodId, source);
+ if (!env.checkAndClearExceptions())
+ m_isAudioSourceSet = true;
+ } else {
+ qCWarning(lcMediaRecorder) << "Audio source already set. Not setting a new source.";
+ }
+}
+
+bool AndroidMediaRecorder::isAudioSourceSet() const
+{
+ return m_isAudioSourceSet;
+}
+
+bool AndroidMediaRecorder::setAudioInput(const QByteArray &id)
+{
+ const bool ret = QJniObject::callStaticMethod<jboolean>(
+ "org/qtproject/qt/android/multimedia/QtAudioDeviceManager",
+ "setAudioInput",
+ "(Landroid/media/MediaRecorder;I)Z",
+ m_mediaRecorder.object(),
+ id.toInt());
+ if (!ret)
+ qCWarning(lcMediaRecorder) << "No default input device was set.";
+
+ return ret;
+}
+
+void AndroidMediaRecorder::setCamera(AndroidCamera *camera)
+{
+ QJniObject cam = camera->getCameraObject();
+ m_mediaRecorder.callMethod<void>("setCamera", "(Landroid/hardware/Camera;)V", cam.object());
+}
+
+void AndroidMediaRecorder::setVideoEncoder(VideoEncoder encoder)
+{
+ m_mediaRecorder.callMethod<void>("setVideoEncoder", "(I)V", int(encoder));
+}
+
+void AndroidMediaRecorder::setVideoEncodingBitRate(int bitRate)
+{
+ m_mediaRecorder.callMethod<void>("setVideoEncodingBitRate", "(I)V", bitRate);
+}
+
+void AndroidMediaRecorder::setVideoFrameRate(int rate)
+{
+ m_mediaRecorder.callMethod<void>("setVideoFrameRate", "(I)V", rate);
+}
+
+void AndroidMediaRecorder::setVideoSize(const QSize &size)
+{
+ m_mediaRecorder.callMethod<void>("setVideoSize", "(II)V", size.width(), size.height());
+}
+
+void AndroidMediaRecorder::setVideoSource(VideoSource source)
+{
+ QJniEnvironment env;
+
+ auto methodId = env->GetMethodID(m_mediaRecorder.objectClass(), "setVideoSource", "(I)V");
+ env->CallVoidMethod(m_mediaRecorder.object(), methodId, source);
+
+ if (!env.checkAndClearExceptions())
+ m_isVideoSourceSet = true;
+}
+
+void AndroidMediaRecorder::setOrientationHint(int degrees)
+{
+ m_mediaRecorder.callMethod<void>("setOrientationHint", "(I)V", degrees);
+}
+
+void AndroidMediaRecorder::setOutputFormat(OutputFormat format)
+{
+ QJniEnvironment env;
+ auto methodId = env->GetMethodID(m_mediaRecorder.objectClass(), "setOutputFormat", "(I)V");
+ env->CallVoidMethod(m_mediaRecorder.object(), methodId, format);
+ // setAudioSource cannot be set after outputFormat is set.
+ if (!env.checkAndClearExceptions())
+ m_isAudioSourceSet = true;
+}
+
+void AndroidMediaRecorder::setOutputFile(const QString &path)
+{
+ if (QUrl(path).scheme() == QLatin1String("content")) {
+ const QJniObject fileDescriptor = QJniObject::callStaticObjectMethod(
+ "org/qtproject/qt/android/QtNative",
+ "openFdObjectForContentUrl",
+ "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)Ljava/io/FileDescriptor;",
+ QNativeInterface::QAndroidApplication::context().object(),
+ QJniObject::fromString(path).object(),
+ QJniObject::fromString(QLatin1String("rw")).object());
+
+ m_mediaRecorder.callMethod<void>("setOutputFile",
+ "(Ljava/io/FileDescriptor;)V",
+ fileDescriptor.object());
+ } else {
+ m_mediaRecorder.callMethod<void>("setOutputFile",
+ "(Ljava/lang/String;)V",
+ QJniObject::fromString(path).object());
+ }
+}
+
+void AndroidMediaRecorder::setSurfaceTexture(AndroidSurfaceTexture *texture)
+{
+ m_mediaRecorder.callMethod<void>("setPreviewDisplay",
+ "(Landroid/view/Surface;)V",
+ texture->surface());
+}
+
+void AndroidMediaRecorder::setSurfaceHolder(AndroidSurfaceHolder *holder)
+{
+ QJniObject surfaceHolder(holder->surfaceHolder());
+ QJniObject surface = surfaceHolder.callObjectMethod("getSurface",
+ "()Landroid/view/Surface;");
+ if (!surface.isValid())
+ return;
+
+ m_mediaRecorder.callMethod<void>("setPreviewDisplay",
+ "(Landroid/view/Surface;)V",
+ surface.object());
+}
+
+bool AndroidMediaRecorder::registerNativeMethods()
+{
+ static const JNINativeMethod methods[] = {
+ {"notifyError", "(JII)V", (void *)notifyError},
+ {"notifyInfo", "(JII)V", (void *)notifyInfo}
+ };
+
+ const int size = std::size(methods);
+ return QJniEnvironment().registerNativeMethods(QtMediaRecorderListenerClassName, methods, size);
+}
+
+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
new file mode 100644
index 000000000..ffdbcc149
--- /dev/null
+++ b/src/plugins/multimedia/android/wrappers/jni/androidmediarecorder_p.h
@@ -0,0 +1,161 @@
+// 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
+
+//
+// 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 <QtCore/qjniobject.h>
+#include <qsize.h>
+
+QT_BEGIN_NAMESPACE
+
+class AndroidCamera;
+class AndroidSurfaceTexture;
+class AndroidSurfaceHolder;
+
+class AndroidCamcorderProfile
+{
+public:
+ enum Quality { // Needs to match CamcorderProfile
+ QUALITY_LOW,
+ QUALITY_HIGH,
+ QUALITY_QCIF,
+ QUALITY_CIF,
+ QUALITY_480P,
+ QUALITY_720P,
+ QUALITY_1080P,
+ QUALITY_QVGA
+ };
+
+ enum Field {
+ audioBitRate,
+ audioChannels,
+ audioCodec,
+ audioSampleRate,
+ duration,
+ fileFormat,
+ quality,
+ videoBitRate,
+ videoCodec,
+ videoFrameHeight,
+ videoFrameRate,
+ videoFrameWidth
+ };
+
+ static bool hasProfile(jint cameraId, Quality quality);
+ static AndroidCamcorderProfile get(jint cameraId, Quality quality);
+ int getValue(Field field) const;
+
+private:
+ AndroidCamcorderProfile(const QJniObject &camcorderProfile);
+ QJniObject m_camcorderProfile;
+};
+
+class AndroidMediaRecorder : public QObject
+{
+ Q_OBJECT
+public:
+ enum AudioEncoder {
+ DefaultAudioEncoder = 0,
+ AMR_NB_Encoder = 1,
+ AMR_WB_Encoder = 2,
+ AAC = 3,
+ OPUS = 7,
+ VORBIS = 6
+ };
+
+ enum AudioSource {
+ DefaultAudioSource = 0,
+ Mic = 1,
+ VoiceUplink = 2,
+ VoiceDownlink = 3,
+ VoiceCall = 4,
+ Camcorder = 5,
+ VoiceRecognition = 6
+ };
+
+ enum VideoEncoder {
+ DefaultVideoEncoder = 0,
+ H263 = 1,
+ H264 = 2,
+ MPEG_4_SP = 3,
+ HEVC = 5
+ };
+
+ enum VideoSource {
+ DefaultVideoSource = 0,
+ Camera = 1
+ };
+
+ enum OutputFormat {
+ DefaultOutputFormat = 0,
+ THREE_GPP = 1,
+ MPEG_4 = 2,
+ AMR_NB_Format = 3,
+ AMR_WB_Format = 4,
+ AAC_ADTS = 6,
+ OGG = 11,
+ WEBM = 9
+ };
+
+ AndroidMediaRecorder();
+ ~AndroidMediaRecorder();
+
+ void release();
+ bool prepare();
+ void reset();
+
+ bool start();
+ void stop();
+
+ void setAudioChannels(int numChannels);
+ void setAudioEncoder(AudioEncoder encoder);
+ void setAudioEncodingBitRate(int bitRate);
+ void setAudioSamplingRate(int samplingRate);
+ void setAudioSource(AudioSource source);
+ bool isAudioSourceSet() const;
+ bool setAudioInput(const QByteArray &id);
+
+ void setCamera(AndroidCamera *camera);
+ void setVideoEncoder(VideoEncoder encoder);
+ void setVideoEncodingBitRate(int bitRate);
+ void setVideoFrameRate(int rate);
+ void setVideoSize(const QSize &size);
+ void setVideoSource(VideoSource source);
+
+ void setOrientationHint(int degrees);
+
+ void setOutputFormat(OutputFormat format);
+ void setOutputFile(const QString &path);
+
+ void setSurfaceTexture(AndroidSurfaceTexture *texture);
+ void setSurfaceHolder(AndroidSurfaceHolder *holder);
+
+ static bool registerNativeMethods();
+
+Q_SIGNALS:
+ void error(int what, int extra);
+ void info(int what, int extra);
+
+private:
+ jlong m_id;
+ QJniObject m_mediaRecorder;
+ bool m_isAudioSourceSet = false;
+ bool m_isVideoSourceSet = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // ANDROIDMEDIARECORDER_H
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidmultimediautils.cpp b/src/plugins/multimedia/android/wrappers/jni/androidmultimediautils.cpp
new file mode 100644
index 000000000..9606bd6bb
--- /dev/null
+++ b/src/plugins/multimedia/android/wrappers/jni/androidmultimediautils.cpp
@@ -0,0 +1,43 @@
+// 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"
+
+#include <QtCore/qjniobject.h>
+
+QT_BEGIN_NAMESPACE
+
+
+void AndroidMultimediaUtils::enableOrientationListener(bool enable)
+{
+ QJniObject::callStaticMethod<void>("org/qtproject/qt/android/multimedia/QtMultimediaUtils",
+ "enableOrientationListener",
+ "(Z)V",
+ enable);
+}
+
+int AndroidMultimediaUtils::getDeviceOrientation()
+{
+ return QJniObject::callStaticMethod<jint>("org/qtproject/qt/android/multimedia/QtMultimediaUtils",
+ "getDeviceOrientation");
+}
+
+QString AndroidMultimediaUtils::getDefaultMediaDirectory(MediaType type)
+{
+ QJniObject path = QJniObject::callStaticObjectMethod(
+ "org/qtproject/qt/android/multimedia/QtMultimediaUtils",
+ "getDefaultMediaDirectory",
+ "(I)Ljava/lang/String;",
+ jint(type));
+ return path.toString();
+}
+
+void AndroidMultimediaUtils::registerMediaFile(const QString &file)
+{
+ QJniObject::callStaticMethod<void>("org/qtproject/qt/android/multimedia/QtMultimediaUtils",
+ "registerMediaFile",
+ "(Ljava/lang/String;)V",
+ QJniObject::fromString(file).object());
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidmultimediautils_p.h b/src/plugins/multimedia/android/wrappers/jni/androidmultimediautils_p.h
new file mode 100644
index 000000000..ee72c3c61
--- /dev/null
+++ b/src/plugins/multimedia/android/wrappers/jni/androidmultimediautils_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 ANDROIDMULTIMEDIAUTILS_H
+#define ANDROIDMULTIMEDIAUTILS_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>
+
+QT_BEGIN_NAMESPACE
+
+class AndroidMultimediaUtils
+{
+public:
+ enum MediaType {
+ Music = 0,
+ Movies = 1,
+ DCIM = 2,
+ Sounds = 3
+ };
+
+ static void enableOrientationListener(bool enable);
+ static int getDeviceOrientation();
+ static QString getDefaultMediaDirectory(MediaType type);
+ static void registerMediaFile(const QString &file);
+};
+
+QT_END_NAMESPACE
+
+#endif // ANDROIDMULTIMEDIAUTILS_H
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidsurfacetexture.cpp b/src/plugins/multimedia/android/wrappers/jni/androidsurfacetexture.cpp
new file mode 100644
index 000000000..c5860b265
--- /dev/null
+++ b/src/plugins/multimedia/android/wrappers/jni/androidsurfacetexture.cpp
@@ -0,0 +1,152 @@
+// 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>
+#include <QtCore/qcoreapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+static const char QtSurfaceTextureListenerClassName[] = "org/qtproject/qt/android/multimedia/QtSurfaceTextureListener";
+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)
+{
+ const QMutexLocker lock(g_textureMutex());
+ const int idx = g_surfaceTextures->indexOf(id);
+ if (idx == -1)
+ return;
+
+ AndroidSurfaceTexture *obj = reinterpret_cast<AndroidSurfaceTexture *>(g_surfaceTextures->at(idx));
+ if (obj)
+ Q_EMIT obj->frameAvailable();
+}
+
+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));
+
+ if (!m_surfaceTexture.isValid())
+ return;
+
+ const QMutexLocker lock(g_textureMutex());
+ g_surfaceTextures->append(jlong(this));
+ QJniObject listener(QtSurfaceTextureListenerClassName, "(J)V", jlong(this));
+ setOnFrameAvailableListener(listener);
+}
+
+AndroidSurfaceTexture::~AndroidSurfaceTexture()
+{
+ if (m_surface.isValid())
+ m_surface.callMethod<void>("release");
+
+ if (m_surfaceTexture.isValid()) {
+ release();
+ const QMutexLocker lock(g_textureMutex());
+ const int idx = g_surfaceTextures->indexOf(jlong(this));
+ if (idx != -1)
+ g_surfaceTextures->remove(idx);
+ }
+}
+
+QMatrix4x4 AndroidSurfaceTexture::getTransformMatrix()
+{
+ QMatrix4x4 matrix;
+ if (!m_surfaceTexture.isValid())
+ return matrix;
+
+ QJniEnvironment env;
+ jfloatArray array = env->NewFloatArray(16);
+ m_surfaceTexture.callMethod<void>("getTransformMatrix", "([F)V", array);
+ env->GetFloatArrayRegion(array, 0, 16, matrix.data());
+ env->DeleteLocalRef(array);
+
+ return matrix;
+}
+
+void AndroidSurfaceTexture::release()
+{
+ m_surfaceTexture.callMethod<void>("release");
+}
+
+void AndroidSurfaceTexture::updateTexImage()
+{
+ if (!m_surfaceTexture.isValid())
+ return;
+
+ m_surfaceTexture.callMethod<void>("updateTexImage");
+}
+
+jobject AndroidSurfaceTexture::surfaceTexture()
+{
+ return m_surfaceTexture.object();
+}
+
+jobject AndroidSurfaceTexture::surface()
+{
+ if (!m_surface.isValid()) {
+ m_surface = QJniObject("android/view/Surface",
+ "(Landroid/graphics/SurfaceTexture;)V",
+ m_surfaceTexture.object());
+ }
+
+ return m_surface.object();
+}
+
+jobject AndroidSurfaceTexture::surfaceHolder()
+{
+ if (!m_surfaceHolder.isValid()) {
+ m_surfaceHolder = QJniObject("org/qtproject/qt/android/multimedia/QtSurfaceTextureHolder",
+ "(Landroid/view/Surface;)V",
+ surface());
+ }
+
+ return m_surfaceHolder.object();
+}
+
+void AndroidSurfaceTexture::attachToGLContext(quint32 texName)
+{
+ if (!m_surfaceTexture.isValid())
+ return;
+
+ m_surfaceTexture.callMethod<void>("attachToGLContext", "(I)V", texName);
+}
+
+void AndroidSurfaceTexture::detachFromGLContext()
+{
+ if (!m_surfaceTexture.isValid())
+ return;
+
+ m_surfaceTexture.callMethod<void>("detachFromGLContext");
+}
+
+bool AndroidSurfaceTexture::registerNativeMethods()
+{
+ static const JNINativeMethod methods[] = {
+ {"notifyFrameAvailable", "(J)V", (void *)notifyFrameAvailable}
+ };
+ const int size = std::size(methods);
+ if (QJniEnvironment().registerNativeMethods(QtSurfaceTextureListenerClassName, methods, size))
+ return false;
+
+ return true;
+}
+
+void AndroidSurfaceTexture::setOnFrameAvailableListener(const QJniObject &listener)
+{
+ m_surfaceTexture.callMethod<void>("setOnFrameAvailableListener",
+ "(Landroid/graphics/SurfaceTexture$OnFrameAvailableListener;)V",
+ listener.object());
+}
+
+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
new file mode 100644
index 000000000..24581ca8d
--- /dev/null
+++ b/src/plugins/multimedia/android/wrappers/jni/androidsurfacetexture_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 ANDROIDSURFACETEXTURE_H
+#define ANDROIDSURFACETEXTURE_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 <QtCore/qjniobject.h>
+
+#include <QMatrix4x4>
+
+QT_BEGIN_NAMESPACE
+
+class AndroidSurfaceTexture : public QObject
+{
+ Q_OBJECT
+public:
+ explicit AndroidSurfaceTexture(quint32 texName);
+ ~AndroidSurfaceTexture();
+
+ jobject surfaceTexture();
+ jobject surface();
+ jobject surfaceHolder();
+ inline bool isValid() const { return m_surfaceTexture.isValid(); }
+
+ QMatrix4x4 getTransformMatrix();
+ void release(); // API level 14
+ void updateTexImage();
+
+ void attachToGLContext(quint32 texName); // API level 16
+ void detachFromGLContext(); // API level 16
+
+ static bool registerNativeMethods();
+
+ quint64 index() const { return m_index; }
+Q_SIGNALS:
+ void frameAvailable();
+
+private:
+ void setOnFrameAvailableListener(const QJniObject &listener);
+
+ QJniObject m_surfaceTexture;
+ QJniObject m_surface;
+ QJniObject m_surfaceHolder;
+ const quint64 m_index = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // ANDROIDSURFACETEXTURE_H
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidsurfaceview.cpp b/src/plugins/multimedia/android/wrappers/jni/androidsurfaceview.cpp
new file mode 100644
index 000000000..dae9516c3
--- /dev/null
+++ b/src/plugins/multimedia/android/wrappers/jni/androidsurfaceview.cpp
@@ -0,0 +1,152 @@
+// 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"
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmutex.h>
+#include <QtGui/qwindow.h>
+
+QT_BEGIN_NAMESPACE
+
+static const char QtSurfaceHolderCallbackClassName[] = "org/qtproject/qt/android/multimedia/QtSurfaceHolderCallback";
+typedef QList<AndroidSurfaceHolder *> SurfaceHolders;
+Q_GLOBAL_STATIC(SurfaceHolders, surfaceHolders)
+Q_GLOBAL_STATIC(QMutex, shLock)
+
+AndroidSurfaceHolder::AndroidSurfaceHolder(QJniObject object)
+ : m_surfaceHolder(object)
+ , m_surfaceCreated(false)
+{
+ if (!m_surfaceHolder.isValid())
+ return;
+
+ {
+ QMutexLocker locker(shLock());
+ surfaceHolders->append(this);
+ }
+
+ QJniObject callback(QtSurfaceHolderCallbackClassName, "(J)V", reinterpret_cast<jlong>(this));
+ m_surfaceHolder.callMethod<void>("addCallback",
+ "(Landroid/view/SurfaceHolder$Callback;)V",
+ callback.object());
+}
+
+AndroidSurfaceHolder::~AndroidSurfaceHolder()
+{
+ QMutexLocker locker(shLock());
+ const int i = surfaceHolders->indexOf(this);
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ surfaceHolders->remove(i);
+}
+
+jobject AndroidSurfaceHolder::surfaceHolder() const
+{
+ return m_surfaceHolder.object();
+}
+
+bool AndroidSurfaceHolder::isSurfaceCreated() const
+{
+ QMutexLocker locker(shLock());
+ return m_surfaceCreated;
+}
+
+void AndroidSurfaceHolder::handleSurfaceCreated(JNIEnv*, jobject, jlong id)
+{
+ QMutexLocker locker(shLock());
+ const int i = surfaceHolders->indexOf(reinterpret_cast<AndroidSurfaceHolder *>(id));
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ (*surfaceHolders)[i]->m_surfaceCreated = true;
+ Q_EMIT (*surfaceHolders)[i]->surfaceCreated();
+}
+
+void AndroidSurfaceHolder::handleSurfaceDestroyed(JNIEnv*, jobject, jlong id)
+{
+ QMutexLocker locker(shLock());
+ const int i = surfaceHolders->indexOf(reinterpret_cast<AndroidSurfaceHolder *>(id));
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ (*surfaceHolders)[i]->m_surfaceCreated = false;
+}
+
+bool AndroidSurfaceHolder::registerNativeMethods()
+{
+ static const JNINativeMethod methods[] = {
+ {"notifySurfaceCreated", "(J)V", (void *)AndroidSurfaceHolder::handleSurfaceCreated},
+ {"notifySurfaceDestroyed", "(J)V", (void *)AndroidSurfaceHolder::handleSurfaceDestroyed}
+ };
+
+ const int size = std::size(methods);
+ return QJniEnvironment().registerNativeMethods(QtSurfaceHolderCallbackClassName, methods, size);
+}
+
+AndroidSurfaceView::AndroidSurfaceView()
+ : m_window(0)
+ , m_surfaceHolder(0)
+ , m_pendingVisible(-1)
+{
+ QNativeInterface::QAndroidApplication::runOnAndroidMainThread([this] {
+ m_surfaceView = QJniObject("android/view/SurfaceView",
+ "(Landroid/content/Context;)V",
+ QNativeInterface::QAndroidApplication::context().object());
+ }).waitForFinished();
+
+ Q_ASSERT(m_surfaceView.isValid());
+
+ QJniObject holder = m_surfaceView.callObjectMethod("getHolder",
+ "()Landroid/view/SurfaceHolder;");
+ if (!holder.isValid()) {
+ m_surfaceView = QJniObject();
+ } else {
+ m_surfaceHolder = new AndroidSurfaceHolder(holder);
+ connect(m_surfaceHolder, &AndroidSurfaceHolder::surfaceCreated,
+ this, &AndroidSurfaceView::surfaceCreated);
+ { // Lock now to avoid a race with handleSurfaceCreated()
+ QMutexLocker locker(shLock());
+ m_window = QWindow::fromWinId(WId(m_surfaceView.object()));
+
+ if (m_pendingVisible != -1)
+ m_window->setVisible(m_pendingVisible);
+ if (m_pendingGeometry.isValid())
+ m_window->setGeometry(m_pendingGeometry);
+ }
+ }
+}
+
+AndroidSurfaceView::~AndroidSurfaceView()
+{
+ delete m_surfaceHolder;
+ delete m_window;
+}
+
+AndroidSurfaceHolder *AndroidSurfaceView::holder() const
+{
+ return m_surfaceHolder;
+}
+
+void AndroidSurfaceView::setVisible(bool v)
+{
+ if (m_window)
+ m_window->setVisible(v);
+ else
+ m_pendingVisible = int(v);
+}
+
+void AndroidSurfaceView::setGeometry(int x, int y, int width, int height)
+{
+ if (m_window)
+ m_window->setGeometry(x, y, width, height);
+ else
+ m_pendingGeometry = QRect(x, y, width, 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
new file mode 100644
index 000000000..e6be60ef1
--- /dev/null
+++ b/src/plugins/multimedia/android/wrappers/jni/androidsurfaceview_p.h
@@ -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
+
+#ifndef ANDROIDSURFACEVIEW_H
+#define ANDROIDSURFACEVIEW_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/qjniobject.h>
+#include <qrect.h>
+#include <QtCore/qrunnable.h>
+#include <QtCore/qobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindow;
+
+class AndroidSurfaceHolder : public QObject
+{
+ Q_OBJECT
+public:
+ ~AndroidSurfaceHolder();
+
+ jobject surfaceHolder() const;
+ bool isSurfaceCreated() const;
+
+ static bool registerNativeMethods();
+
+Q_SIGNALS:
+ void surfaceCreated();
+
+private:
+ AndroidSurfaceHolder(QJniObject object);
+
+ static void handleSurfaceCreated(JNIEnv*, jobject, jlong id);
+ static void handleSurfaceDestroyed(JNIEnv*, jobject, jlong id);
+
+ QJniObject m_surfaceHolder;
+ bool m_surfaceCreated;
+
+ friend class AndroidSurfaceView;
+};
+
+class AndroidSurfaceView : public QObject
+{
+ Q_OBJECT
+public:
+ AndroidSurfaceView();
+ ~AndroidSurfaceView();
+
+ AndroidSurfaceHolder *holder() const;
+
+ void setVisible(bool v);
+ void setGeometry(int x, int y, int width, int height);
+
+Q_SIGNALS:
+ void surfaceCreated();
+
+private:
+ QJniObject m_surfaceView;
+ QWindow *m_window;
+ AndroidSurfaceHolder *m_surfaceHolder;
+ int m_pendingVisible;
+ QRect m_pendingGeometry;
+};
+
+QT_END_NAMESPACE
+
+#endif // ANDROIDSURFACEVIEW_H
diff --git a/src/plugins/multimedia/darwin/CMakeLists.txt b/src/plugins/multimedia/darwin/CMakeLists.txt
new file mode 100644
index 000000000..0bbc054eb
--- /dev/null
+++ b/src/plugins/multimedia/darwin/CMakeLists.txt
@@ -0,0 +1,70 @@
+# 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
+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_add_plugin(QDarwinMediaPlugin
+ OUTPUT_NAME darwinmediaplugin
+ PLUGIN_TYPE multimedia
+ SOURCES
+ avfaudiodecoder.mm avfaudiodecoder_p.h
+ mediaplayer/avfdisplaylink.mm mediaplayer/avfdisplaylink_p.h
+ mediaplayer/avfmediaplayer.mm mediaplayer/avfmediaplayer_p.h
+ common/avfmetadata.mm common/avfmetadata_p.h
+ mediaplayer/avfvideorenderercontrol.mm mediaplayer/avfvideorenderercontrol_p.h
+ avfvideosink.mm avfvideosink_p.h
+ avfvideobuffer.mm avfvideobuffer_p.h
+ qavfhelpers.mm qavfhelpers_p.h
+ qdarwinformatsinfo.mm qdarwinformatsinfo_p.h
+ qdarwinintegration.mm qdarwinintegration_p.h
+ INCLUDE_DIRECTORIES
+ audio
+ camera
+ common
+ mediaplayer
+ LIBRARIES
+ Qt::MultimediaPrivate
+ Qt::CorePrivate
+ ${FWCoreMedia}
+ ${FWCoreAudio}
+ ${FWCoreVideo}
+ ${FWFoundation}
+ ${FWMetal}
+ ${FWQuartzCore}
+ ${FWAudioToolbox}
+ AVFoundation::AVFoundation
+)
+
+qt_internal_extend_target(QDarwinMediaPlugin CONDITION NOT TVOS
+ SOURCES
+ camera/qavfcamerabase.mm camera/qavfcamerabase_p.h
+ camera/avfcamera.mm camera/avfcamera_p.h
+ camera/avfcameradebug_p.h
+ camera/avfaudiopreviewdelegate.mm camera/avfaudiopreviewdelegate_p.h
+ camera/avfcamerarenderer.mm camera/avfcamerarenderer_p.h
+ camera/avfcameraservice.mm camera/avfcameraservice_p.h
+ camera/avfcamerasession.mm camera/avfcamerasession_p.h
+ camera/avfcamerautility.mm camera/avfcamerautility_p.h
+ camera/avfimagecapture.mm camera/avfimagecapture_p.h
+ camera/avfmediaassetwriter.mm camera/avfmediaassetwriter_p.h
+ camera/avfmediaencoder.mm camera/avfmediaencoder_p.h
+)
+
+qt_internal_extend_target(QDarwinMediaPlugin CONDITION MACOS
+ LIBRARIES
+ ${FWAppKit}
+ ${FWAudioUnit}
+ ${FWVideoToolbox}
+ ${FWApplicationServices}
+)
+
+qt_internal_extend_target(QDarwinMediaPlugin CONDITION IOS
+ LIBRARIES
+ ${FWCoreGraphics}
+ ${FWCoreVideo}
+)
diff --git a/src/plugins/multimedia/darwin/avfaudiodecoder.mm b/src/plugins/multimedia/darwin/avfaudiodecoder.mm
new file mode 100644
index 000000000..3191b7db0
--- /dev/null
+++ b/src/plugins/multimedia/darwin/avfaudiodecoder.mm
@@ -0,0 +1,544 @@
+// 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>
+
+QT_USE_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLcAVFAudioDecoder, "qt.multimedia.darwin.AVFAudioDecoder")
+constexpr static int MAX_BUFFERS_IN_QUEUE = 5;
+
+QAudioBuffer handleNextSampleBuffer(CMSampleBufferRef sampleBuffer)
+{
+ if (!sampleBuffer)
+ return {};
+
+ // Check format
+ CMFormatDescriptionRef formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer);
+ if (!formatDescription)
+ 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 {};
+
+ // Get the required size to allocate to audioBufferList
+ size_t audioBufferListSize = 0;
+ OSStatus err = CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer,
+ &audioBufferListSize,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
+ NULL);
+ if (err != noErr)
+ return {};
+
+ CMBlockBufferRef blockBuffer = NULL;
+ AudioBufferList* audioBufferList = (AudioBufferList*) malloc(audioBufferListSize);
+ // This ensures the buffers placed in audioBufferList are contiguous
+ err = CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer,
+ NULL,
+ audioBufferList,
+ audioBufferListSize,
+ NULL,
+ NULL,
+ kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
+ &blockBuffer);
+ if (err != noErr) {
+ free(audioBufferList);
+ return {};
+ }
+
+ QByteArray abuf;
+ for (UInt32 i = 0; i < audioBufferList->mNumberBuffers; i++)
+ {
+ AudioBuffer audioBuffer = audioBufferList->mBuffers[i];
+ abuf.push_back(QByteArray((const char*)audioBuffer.mData, audioBuffer.mDataByteSize));
+ }
+
+ free(audioBufferList);
+ CFRelease(blockBuffer);
+
+ CMTime sampleStartTime = (CMSampleBufferGetPresentationTimeStamp(sampleBuffer));
+ float sampleStartTimeSecs = CMTimeGetSeconds(sampleStartTime);
+
+ 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;
+
+ return self;
+}
+
+-(BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader
+ shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
+{
+ Q_UNUSED(resourceLoader);
+
+ if (![loadingRequest.request.URL.scheme isEqualToString:@"iodevice"])
+ return NO;
+
+ QMutexLocker locker(&m_mutex);
+
+ QIODevice *device = m_decoder->sourceDevice();
+ if (!device)
+ return NO;
+
+ device->seek(loadingRequest.dataRequest.requestedOffset);
+ if (loadingRequest.contentInformationRequest) {
+ loadingRequest.contentInformationRequest.contentLength = device->size();
+ loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
+ }
+
+ if (loadingRequest.dataRequest) {
+ NSInteger requestedLength = loadingRequest.dataRequest.requestedLength;
+ int maxBytes = qMin(32 * 1024, int(requestedLength));
+ char buffer[maxBytes];
+ NSInteger submitted = 0;
+ while (submitted < requestedLength) {
+ qint64 len = device->read(buffer, maxBytes);
+ if (len < 1)
+ break;
+
+ [loadingRequest.dataRequest respondWithData:[NSData dataWithBytes:buffer length:len]];
+ submitted += len;
+ }
+
+ // Finish loading even if not all bytes submitted.
+ [loadingRequest finishLoading];
+ }
+
+ return YES;
+}
+
+@end
+
+namespace {
+
+NSDictionary *av_audio_settings_for_format(const QAudioFormat &format)
+{
+ float sampleRate = format.sampleRate();
+ int nChannels = format.channelCount();
+ int sampleSize = format.bytesPerSample() * 8;
+ BOOL isFloat = format.sampleFormat() == QAudioFormat::Float;
+
+ NSDictionary *audioSettings = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey,
+ [NSNumber numberWithFloat:sampleRate], AVSampleRateKey,
+ [NSNumber numberWithInt:nChannels], AVNumberOfChannelsKey,
+ [NSNumber numberWithInt:sampleSize], AVLinearPCMBitDepthKey,
+ [NSNumber numberWithBool:isFloat], AVLinearPCMIsFloatKey,
+ [NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved,
+ [NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey,
+ nil];
+
+ return audioSettings;
+}
+
+QAudioFormat qt_format_for_audio_track(AVAssetTrack *track)
+{
+ QAudioFormat format;
+ CMFormatDescriptionRef desc = (__bridge CMFormatDescriptionRef)track.formatDescriptions[0];
+ const AudioStreamBasicDescription* const asbd =
+ CMAudioFormatDescriptionGetStreamBasicDescription(desc);
+ format = CoreAudioUtils::toQAudioFormat(*asbd);
+ // AudioStreamBasicDescription's mBitsPerChannel is 0 for compressed formats
+ // In this case set default Int16 sample format
+ if (asbd->mBitsPerChannel == 0)
+ format.setSampleFormat(QAudioFormat::Int16);
+ return format;
+}
+
+}
+
+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)
+{
+ m_readingQueue = dispatch_queue_create("reader_queue", DISPATCH_QUEUE_SERIAL);
+ m_decodingQueue = dispatch_queue_create("decoder_queue", DISPATCH_QUEUE_SERIAL);
+
+ m_readerDelegate = [[AVFResourceReaderDelegate alloc] initWithDecoder:this];
+}
+
+AVFAudioDecoder::~AVFAudioDecoder()
+{
+ stop();
+
+ [m_readerDelegate release];
+ [m_asset release];
+
+ dispatch_release(m_readingQueue);
+ dispatch_release(m_decodingQueue);
+}
+
+QUrl AVFAudioDecoder::source() const
+{
+ return m_source;
+}
+
+void AVFAudioDecoder::setSource(const QUrl &fileName)
+{
+ if (!m_device && m_source == fileName)
+ return;
+
+ stop();
+ m_device = nullptr;
+ [m_asset release];
+ m_asset = nil;
+
+ m_source = fileName;
+
+ if (!m_source.isEmpty()) {
+ NSURL *nsURL = m_source.toNSURL();
+ m_asset = [[AVURLAsset alloc] initWithURL:nsURL options:nil];
+ }
+
+ sourceChanged();
+}
+
+QIODevice *AVFAudioDecoder::sourceDevice() const
+{
+ return m_device;
+}
+
+void AVFAudioDecoder::setSourceDevice(QIODevice *device)
+{
+ if (m_device == device && m_source.isEmpty())
+ return;
+
+ stop();
+ m_source.clear();
+ [m_asset release];
+ m_asset = nil;
+
+ m_device = device;
+
+ if (m_device) {
+ const QString ext = QMimeDatabase().mimeTypeForData(m_device).preferredSuffix();
+ const QString url = "iodevice:///iodevice." + ext;
+ NSString *urlString = url.toNSString();
+ NSURL *nsURL = [NSURL URLWithString:urlString];
+
+ m_asset = [[AVURLAsset alloc] initWithURL:nsURL options:nil];
+
+ // 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];
+ }
+
+ sourceChanged();
+}
+
+void AVFAudioDecoder::start()
+{
+ if (m_decodingContext) {
+ qCDebug(qLcAVFAudioDecoder()) << "AVFAudioDecoder has been already started";
+ return;
+ }
+
+ positionChanged(-1);
+
+ if (m_device && (!m_device->isOpen() || !m_device->isReadable())) {
+ processInvalidMedia(QAudioDecoder::ResourceError, tr("Unable to read from specified device"));
+ return;
+ }
+
+ 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();
+ }
+ };
+
+ [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();
+
+ bufferAvailableChanged(false);
+ positionChanged(-1);
+ durationChanged(-1);
+
+ onFinished();
+}
+
+QAudioFormat AVFAudioDecoder::audioFormat() const
+{
+ return m_format;
+}
+
+void AVFAudioDecoder::setAudioFormat(const QAudioFormat &format)
+{
+ if (m_format != format) {
+ m_format = format;
+ formatChanged(m_format);
+ }
+}
+
+QAudioBuffer AVFAudioDecoder::read()
+{
+ if (m_cachedBuffers.empty())
+ return QAudioBuffer();
+
+ Q_ASSERT(m_cachedBuffers.size() > 0);
+ QAudioBuffer buffer = m_cachedBuffers.dequeue();
+ decBuffersCounter(1);
+
+ positionChanged(buffer.startTime() / 1000);
+ bufferAvailableChanged(!m_cachedBuffers.empty());
+ return buffer;
+}
+
+void AVFAudioDecoder::processInvalidMedia(QAudioDecoder::Error errorCode,
+ const QString &errorString)
+{
+ qCDebug(qLcAVFAudioDecoder()) << "Invalid media. Error code:" << errorCode
+ << "Description:" << errorString;
+
+ Q_ASSERT(QThread::currentThread() == thread());
+
+ 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::onFinished()
+{
+ m_decodingContext.reset();
+
+ if (isDecoding())
+ finished();
+}
+
+void AVFAudioDecoder::initAssetReader()
+{
+ 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];
+ QAudioFormat format = m_format.isValid() ? m_format : qt_format_for_audio_track(track);
+ if (!format.isValid()) {
+ processInvalidMedia(QAudioDecoder::FormatError, tr("Unsupported source format"));
+ return;
+ }
+
+ durationChanged(CMTimeGetSeconds(track.timeRange.duration) * 1000);
+
+ NSError *error = nil;
+ NSDictionary *audioSettings = av_audio_settings_for_format(format);
+
+ 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 (![reader canAddOutput:readerOutput]) {
+ processInvalidMedia(QAudioDecoder::ResourceError, tr("Failed to add asset reader output"));
+ return;
+ }
+
+ [reader addOutput:readerOutput];
+
+ Q_ASSERT(m_decodingContext);
+ m_decodingContext->m_reader = reader;
+ m_decodingContext->m_readerOutput = readerOutput;
+
+ startReading();
+}
+
+void AVFAudioDecoder::startReading()
+{
+ 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_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.
+ 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)
+{
+ m_cachedBuffers.enqueue(buffer);
+ ++m_buffersCounter;
+
+ Q_ASSERT(m_cachedBuffers.size() == m_buffersCounter);
+
+ bufferAvailableChanged(true);
+ 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
new file mode 100644
index 000000000..81ef3f49e
--- /dev/null
+++ b/src/plugins/multimedia/darwin/avfaudiodecoder_p.h
@@ -0,0 +1,99 @@
+// 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
+
+//
+// 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 <QObject>
+#include <QtCore/qurl.h>
+#include <QWaitCondition>
+#include <QMutex>
+#include <QQueue>
+
+#include "private/qplatformaudiodecoder_p.h"
+#include "qaudiodecoder.h"
+
+#include <dispatch/dispatch.h>
+
+Q_FORWARD_DECLARE_OBJC_CLASS(AVURLAsset);
+Q_FORWARD_DECLARE_OBJC_CLASS(AVAssetReader);
+Q_FORWARD_DECLARE_OBJC_CLASS(AVAssetReaderTrackOutput);
+Q_FORWARD_DECLARE_OBJC_CLASS(AVFResourceReaderDelegate);
+
+QT_BEGIN_NAMESPACE
+
+class AVFAudioDecoder : public QPlatformAudioDecoder
+{
+ Q_OBJECT
+
+ struct DecodingContext;
+
+public:
+ AVFAudioDecoder(QAudioDecoder *parent);
+ virtual ~AVFAudioDecoder();
+
+ QUrl source() const override;
+ void setSource(const QUrl &fileName) override;
+
+ QIODevice *sourceDevice() const override;
+ void setSourceDevice(QIODevice *device) override;
+
+ void start() override;
+ void stop() override;
+
+ QAudioFormat audioFormat() const override;
+ void setAudioFormat(const QAudioFormat &format) override;
+
+ QAudioBuffer read() override;
+
+private:
+ void handleNewAudioBuffer(QAudioBuffer);
+ void startReading();
+
+ 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;
+
+ // 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;
+
+ 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
+
+#endif // AVFAUDIODECODER_H
diff --git a/src/plugins/multimedia/darwin/avfvideobuffer.mm b/src/plugins/multimedia/darwin/avfvideobuffer.mm
new file mode 100644
index 000000000..57ec89ae7
--- /dev/null
+++ b/src/plugins/multimedia/darwin/avfvideobuffer.mm
@@ -0,0 +1,207 @@
+// 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 <rhi/qrhi.h>
+#include <CoreVideo/CVMetalTexture.h>
+#include <CoreVideo/CVMetalTextureCache.h>
+#include <QtGui/qopenglcontext.h>
+
+#include <private/qvideotexturehelper_p.h>
+#include "qavfhelpers_p.h"
+
+#import <AVFoundation/AVFoundation.h>
+#import <Metal/Metal.h>
+
+QT_USE_NAMESPACE
+
+AVFVideoBuffer::AVFVideoBuffer(AVFVideoSinkInterface *sink, CVImageBufferRef buffer)
+ : QHwVideoBuffer(sink->rhi() ? QVideoFrame::RhiTextureHandle : QVideoFrame::NoHandle,
+ sink->rhi()),
+ sink(sink),
+ m_buffer(buffer)
+{
+// m_type = QVideoFrame::NoHandle;
+// qDebug() << "RHI" << m_rhi;
+ CVPixelBufferRetain(m_buffer);
+ const bool rhiIsOpenGL = sink && sink->rhi() && sink->rhi()->backend() == QRhi::OpenGLES2;
+ m_format = QAVFHelpers::videoFormatForImageBuffer(m_buffer, rhiIsOpenGL);
+}
+
+AVFVideoBuffer::~AVFVideoBuffer()
+{
+ AVFVideoBuffer::unmap();
+ for (int i = 0; i < 3; ++i)
+ if (cvMetalTexture[i])
+ CFRelease(cvMetalTexture[i]);
+#if defined(Q_OS_MACOS)
+ if (cvOpenGLTexture)
+ CVOpenGLTextureRelease(cvOpenGLTexture);
+#elif defined(Q_OS_IOS)
+ if (cvOpenGLESTexture)
+ CFRelease(cvOpenGLESTexture);
+#endif
+ CVPixelBufferRelease(m_buffer);
+}
+
+AVFVideoBuffer::MapData AVFVideoBuffer::map(QtVideo::MapMode mode)
+{
+ MapData mapData;
+
+ if (m_mode == QtVideo::MapMode::NotMapped) {
+ CVPixelBufferLockBaseAddress(m_buffer, mode == QtVideo::MapMode::ReadOnly
+ ? kCVPixelBufferLock_ReadOnly
+ : 0);
+ m_mode = mode;
+ }
+
+ mapData.planeCount = CVPixelBufferGetPlaneCount(m_buffer);
+ Q_ASSERT(mapData.planeCount <= 3);
+
+ if (!mapData.planeCount) {
+ // single plane
+ mapData.bytesPerLine[0] = CVPixelBufferGetBytesPerRow(m_buffer);
+ mapData.data[0] = static_cast<uchar*>(CVPixelBufferGetBaseAddress(m_buffer));
+ mapData.dataSize[0] = CVPixelBufferGetDataSize(m_buffer);
+ mapData.planeCount = 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.planeCount; ++i) {
+ mapData.bytesPerLine[i] = CVPixelBufferGetBytesPerRowOfPlane(m_buffer, i);
+ mapData.dataSize[i] = mapData.bytesPerLine[i]*CVPixelBufferGetHeightOfPlane(m_buffer, i);
+ mapData.data[i] = static_cast<uchar*>(CVPixelBufferGetBaseAddressOfPlane(m_buffer, i));
+ }
+
+ return mapData;
+}
+
+void AVFVideoBuffer::unmap()
+{
+ if (m_mode != QtVideo::MapMode::NotMapped) {
+ CVPixelBufferUnlockBaseAddress(m_buffer, m_mode == QtVideo::MapMode::ReadOnly
+ ? kCVPixelBufferLock_ReadOnly
+ : 0);
+ m_mode = QtVideo::MapMode::NotMapped;
+ }
+}
+
+static MTLPixelFormat rhiTextureFormatToMetalFormat(QRhiTexture::Format f)
+{
+ switch (f) {
+ default:
+ case QRhiTexture::UnknownFormat:
+ return MTLPixelFormatInvalid;
+ case QRhiTexture::RGBA8:
+ return MTLPixelFormatRGBA8Unorm;
+ case QRhiTexture::BGRA8:
+ return MTLPixelFormatBGRA8Unorm;
+ case QRhiTexture::R8:
+ return MTLPixelFormatR8Unorm;
+ case QRhiTexture::RG8:
+ return MTLPixelFormatRG8Unorm;
+ case QRhiTexture::R16:
+ return MTLPixelFormatR16Unorm;
+ case QRhiTexture::RG16:
+ return MTLPixelFormatRG16Unorm;
+
+ case QRhiTexture::RGBA16F:
+ return MTLPixelFormatRGBA16Float;
+ case QRhiTexture::RGBA32F:
+ return MTLPixelFormatRGBA32Float;
+ case QRhiTexture::R16F:
+ return MTLPixelFormatR16Float;
+ case QRhiTexture::R32F:
+ return MTLPixelFormatR32Float;
+ }
+}
+
+
+quint64 AVFVideoBuffer::textureHandle(QRhi *, int plane) const
+{
+ auto *textureDescription = QVideoTextureHelper::textureDescription(m_format.pixelFormat());
+ int bufferPlanes = CVPixelBufferGetPlaneCount(m_buffer);
+// qDebug() << "texture handle" << plane << m_rhi << (m_rhi->backend() == QRhi::Metal) << bufferPlanes;
+ if (plane > 0 && plane >= bufferPlanes)
+ return 0;
+ if (!m_rhi)
+ return 0;
+ if (m_rhi->backend() == QRhi::Metal) {
+ if (!cvMetalTexture[plane]) {
+ size_t width = CVPixelBufferGetWidth(m_buffer);
+ size_t height = CVPixelBufferGetHeight(m_buffer);
+ width = textureDescription->widthForPlane(width, plane);
+ 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,
+ metalCache,
+ m_buffer, nil,
+ rhiTextureFormatToMetalFormat(textureDescription->textureFormat[plane]),
+ width, height,
+ plane,
+ &cvMetalTexture[plane]);
+
+ if (ret != kCVReturnSuccess)
+ qWarning() << "texture creation failed" << ret;
+// auto t = CVMetalTextureGetTexture(cvMetalTexture[plane]);
+// qDebug() << " metal texture is" << quint64(cvMetalTexture[plane]) << width << height;
+// qDebug() << " " << t.iosurfacePlane << t.pixelFormat << t.width << t.height;
+ }
+
+ // Get a Metal texture using the CoreVideo Metal texture reference.
+// qDebug() << " -> " << quint64(CVMetalTextureGetTexture(cvMetalTexture[plane]));
+ return cvMetalTexture[plane] ? quint64(CVMetalTextureGetTexture(cvMetalTexture[plane])) : 0;
+ } else if (m_rhi->backend() == QRhi::OpenGLES2) {
+#if QT_CONFIG(opengl)
+#ifdef Q_OS_MACOS
+ CVOpenGLTextureCacheFlush(sink->cvOpenGLTextureCache, 0);
+ // Create a CVPixelBuffer-backed OpenGL texture image from the texture cache.
+ const CVReturn cvret = CVOpenGLTextureCacheCreateTextureFromImage(
+ kCFAllocatorDefault,
+ sink->cvOpenGLTextureCache,
+ m_buffer,
+ nil,
+ &cvOpenGLTexture);
+ if (cvret != kCVReturnSuccess)
+ qWarning() << "OpenGL texture creation failed" << cvret;
+
+ Q_ASSERT(CVOpenGLTextureGetTarget(cvOpenGLTexture) == GL_TEXTURE_RECTANGLE);
+ // Get an OpenGL texture name from the CVPixelBuffer-backed OpenGL texture image.
+ return CVOpenGLTextureGetName(cvOpenGLTexture);
+#endif
+#ifdef Q_OS_IOS
+ CVOpenGLESTextureCacheFlush(sink->cvOpenGLESTextureCache, 0);
+ // Create a CVPixelBuffer-backed OpenGL texture image from the texture cache.
+ const CVReturn cvret = CVOpenGLESTextureCacheCreateTextureFromImage(
+ kCFAllocatorDefault,
+ sink->cvOpenGLESTextureCache,
+ m_buffer,
+ nil,
+ GL_TEXTURE_2D,
+ GL_RGBA,
+ CVPixelBufferGetWidth(m_buffer),
+ CVPixelBufferGetHeight(m_buffer),
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ 0,
+ &cvOpenGLESTexture);
+ if (cvret != kCVReturnSuccess)
+ qWarning() << "OpenGL ES texture creation failed" << cvret;
+
+ // Get an OpenGL texture name from the CVPixelBuffer-backed OpenGL texture image.
+ return CVOpenGLESTextureGetName(cvOpenGLESTexture);
+#endif
+#endif
+ }
+ return 0;
+}
diff --git a/src/plugins/multimedia/darwin/avfvideobuffer_p.h b/src/plugins/multimedia/darwin/avfvideobuffer_p.h
new file mode 100644
index 000000000..f70961c15
--- /dev/null
+++ b/src/plugins/multimedia/darwin/avfvideobuffer_p.h
@@ -0,0 +1,64 @@
+// 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
+
+//
+// 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/qhwvideobuffer_p.h>
+#include <private/qcore_mac_p.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qmutex.h>
+#include <avfvideosink_p.h>
+
+#include <CoreVideo/CVImageBuffer.h>
+
+#import "Metal/Metal.h"
+#import "MetalKit/MetalKit.h"
+
+QT_BEGIN_NAMESPACE
+
+struct AVFMetalTexture;
+class AVFVideoBuffer : public QHwVideoBuffer
+{
+public:
+ AVFVideoBuffer(AVFVideoSinkInterface *sink, CVImageBufferRef buffer);
+ ~AVFVideoBuffer();
+
+ MapData map(QtVideo::MapMode mode);
+ void unmap();
+
+ virtual quint64 textureHandle(QRhi *, int plane) const;
+
+ QVideoFrameFormat videoFormat() const { return m_format; }
+
+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)
+ mutable CVOpenGLESTextureRef cvOpenGLESTexture = nullptr;
+#endif
+
+ CVImageBufferRef m_buffer = nullptr;
+ QtVideo::MapMode m_mode = QtVideo::MapMode::NotMapped;
+ QVideoFrameFormat m_format;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/darwin/avfvideosink.mm b/src/plugins/multimedia/darwin/avfvideosink.mm
new file mode 100644
index 000000000..f4c8bdb2e
--- /dev/null
+++ b/src/plugins/multimedia/darwin/avfvideosink.mm
@@ -0,0 +1,228 @@
+// 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 <rhi/qrhi.h>
+#include <QtGui/qopenglcontext.h>
+
+#include <AVFoundation/AVFoundation.h>
+#import <QuartzCore/CATransaction.h>
+
+#if __has_include(<AppKit/AppKit.h>)
+#include <AppKit/AppKit.h>
+#endif
+
+#if __has_include(<UIKit/UIKit.h>)
+#include <UIKit/UIKit.h>
+#endif
+
+QT_USE_NAMESPACE
+
+AVFVideoSink::AVFVideoSink(QVideoSink *parent)
+ : QPlatformVideoSink(parent)
+{
+}
+
+AVFVideoSink::~AVFVideoSink()
+{
+}
+
+void AVFVideoSink::setRhi(QRhi *rhi)
+{
+ if (m_rhi == rhi)
+ return;
+ m_rhi = rhi;
+ if (m_interface)
+ m_interface->setRhi(rhi);
+}
+
+void AVFVideoSink::setNativeSize(QSize size)
+{
+ if (size == nativeSize())
+ return;
+ QPlatformVideoSink::setNativeSize(size);
+ if (m_interface)
+ m_interface->nativeSizeChanged();
+}
+
+void AVFVideoSink::setVideoSinkInterface(AVFVideoSinkInterface *interface)
+{
+ m_interface = interface;
+ if (m_interface)
+ m_interface->setRhi(m_rhi);
+}
+
+AVFVideoSinkInterface::~AVFVideoSinkInterface()
+{
+ if (m_layer)
+ [m_layer release];
+ if (m_outputSettings)
+ [m_outputSettings release];
+ freeTextureCaches();
+}
+
+void AVFVideoSinkInterface::freeTextureCaches()
+{
+ if (cvMetalTextureCache)
+ CFRelease(cvMetalTextureCache);
+ cvMetalTextureCache = nullptr;
+#if defined(Q_OS_MACOS)
+ if (cvOpenGLTextureCache)
+ CFRelease(cvOpenGLTextureCache);
+ cvOpenGLTextureCache = nullptr;
+#elif defined(Q_OS_IOS)
+ if (cvOpenGLESTextureCache)
+ CFRelease(cvOpenGLESTextureCache);
+ cvOpenGLESTextureCache = nullptr;
+#endif
+}
+
+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();
+ }
+}
+
+void AVFVideoSinkInterface::setRhi(QRhi *rhi)
+{
+ QMutexLocker locker(&m_textureCacheMutex);
+ if (m_rhi == rhi)
+ return;
+ freeTextureCaches();
+ m_rhi = rhi;
+
+ if (!rhi)
+ return;
+ if (rhi->backend() == QRhi::Metal) {
+ const auto *metal = static_cast<const QRhiMetalNativeHandles *>(rhi->nativeHandles());
+
+ // Create a Metal Core Video texture cache from the pixel buffer.
+ Q_ASSERT(!cvMetalTextureCache);
+ if (CVMetalTextureCacheCreate(
+ kCFAllocatorDefault,
+ nil,
+ (id<MTLDevice>)metal->dev,
+ nil,
+ &cvMetalTextureCache) != kCVReturnSuccess) {
+ qWarning() << "Metal texture cache creation failed";
+ m_rhi = nullptr;
+ }
+ } else if (rhi->backend() == QRhi::OpenGLES2) {
+#if QT_CONFIG(opengl)
+#ifdef Q_OS_MACOS
+ const auto *gl = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles());
+
+ auto nsGLContext = gl->context->nativeInterface<QNativeInterface::QCocoaGLContext>()->nativeContext();
+ auto nsGLPixelFormat = nsGLContext.pixelFormat.CGLPixelFormatObj;
+
+ // Create an OpenGL CoreVideo texture cache from the pixel buffer.
+ if (CVOpenGLTextureCacheCreate(
+ kCFAllocatorDefault,
+ nullptr,
+ reinterpret_cast<CGLContextObj>(nsGLContext.CGLContextObj),
+ nsGLPixelFormat,
+ nil,
+ &cvOpenGLTextureCache)) {
+ qWarning() << "OpenGL texture cache creation failed";
+ m_rhi = nullptr;
+ }
+#endif
+#ifdef Q_OS_IOS
+ // Create an OpenGL CoreVideo texture cache from the pixel buffer.
+ if (CVOpenGLESTextureCacheCreate(
+ kCFAllocatorDefault,
+ nullptr,
+ [EAGLContext currentContext],
+ nullptr,
+ &cvOpenGLESTextureCache)) {
+ qWarning() << "OpenGL texture cache creation failed";
+ m_rhi = nullptr;
+ }
+#endif
+#else
+ m_rhi = nullptr;
+#endif // QT_CONFIG(opengl)
+ }
+ setOutputSettings();
+}
+
+void AVFVideoSinkInterface::setLayer(CALayer *layer)
+{
+ if (layer == m_layer)
+ return;
+
+ if (m_layer)
+ [m_layer release];
+
+ m_layer = layer;
+ if (m_layer)
+ [m_layer retain];
+
+ reconfigure();
+}
+
+void AVFVideoSinkInterface::setOutputSettings()
+{
+ 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()
+{
+ if (!m_layer)
+ return;
+ [CATransaction begin];
+ [CATransaction setDisableActions: YES]; // disable animation/flicks
+ m_layer.frame = QRectF(0, 0, nativeSize().width(), nativeSize().height()).toCGRect();
+ m_layer.bounds = m_layer.frame;
+ [CATransaction commit];
+}
+
+#include "moc_avfvideosink_p.cpp"
diff --git a/src/plugins/multimedia/darwin/avfvideosink_p.h b/src/plugins/multimedia/darwin/avfvideosink_p.h
new file mode 100644
index 000000000..9b66e79f2
--- /dev/null
+++ b/src/plugins/multimedia/darwin/avfvideosink_p.h
@@ -0,0 +1,99 @@
+// 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
+
+//
+// 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>
+#include "private/qplatformvideosink_p.h"
+
+Q_FORWARD_DECLARE_OBJC_CLASS(CALayer);
+Q_FORWARD_DECLARE_OBJC_CLASS(AVPlayerLayer);
+Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureVideoPreviewLayer);
+
+#include <CoreVideo/CVBase.h>
+#include <CoreVideo/CVPixelBuffer.h>
+#include <CoreVideo/CVImageBuffer.h>
+
+#import "Metal/Metal.h"
+#import "MetalKit/MetalKit.h"
+
+QT_BEGIN_NAMESPACE
+
+class AVFVideoSinkInterface;
+
+class AVFVideoSink : public QPlatformVideoSink
+{
+ Q_OBJECT
+
+public:
+ AVFVideoSink(QVideoSink *parent = nullptr);
+ virtual ~AVFVideoSink();
+
+ // QPlatformVideoSink interface
+public:
+ void setRhi(QRhi *rhi) override;
+
+ void setNativeSize(QSize size);
+
+ void setVideoSinkInterface(AVFVideoSinkInterface *interface);
+
+private:
+ AVFVideoSinkInterface *m_interface = nullptr;
+ QRhi *m_rhi = nullptr;
+};
+
+class AVFVideoSinkInterface
+{
+public:
+ ~AVFVideoSinkInterface();
+
+ void setVideoSink(AVFVideoSink *sink);
+
+
+ virtual void reconfigure() = 0;
+ virtual void setRhi(QRhi *);
+ virtual void setLayer(CALayer *layer);
+ virtual void setOutputSettings();
+
+ QMutex *textureCacheMutex() { return &m_textureCacheMutex; }
+
+ QRhi *rhi() const { return m_rhi; }
+
+ void updateLayerBounds();
+ void nativeSizeChanged() { updateLayerBounds(); }
+ QSize nativeSize() const { return m_sink ? m_sink->nativeSize() : QSize(); }
+
+ CVMetalTextureCacheRef cvMetalTextureCache = nullptr;
+#if defined(Q_OS_MACOS)
+ CVOpenGLTextureCacheRef cvOpenGLTextureCache = nullptr;
+#elif defined(Q_OS_IOS)
+ CVOpenGLESTextureCacheRef cvOpenGLESTextureCache = nullptr;
+#endif
+private:
+ void freeTextureCaches();
+
+protected:
+
+ AVFVideoSink *m_sink = nullptr;
+ QRhi *m_rhi = nullptr;
+ CALayer *m_layer = nullptr;
+ NSDictionary *m_outputSettings = nullptr;
+ QMutex m_textureCacheMutex;
+};
+
+
+QT_END_NAMESPACE
+
+#endif // AVFVIDEOWINDOWCONTROL_H
diff --git a/src/plugins/multimedia/darwin/camera/avfaudiopreviewdelegate.mm b/src/plugins/multimedia/darwin/camera/avfaudiopreviewdelegate.mm
new file mode 100644
index 000000000..1b2d4b15d
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfaudiopreviewdelegate.mm
@@ -0,0 +1,98 @@
+// 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"
+
+QT_USE_NAMESPACE
+
+@implementation AVFAudioPreviewDelegate
+{
+@private
+ AVSampleBufferAudioRenderer *m_audioRenderer;
+ AVFCameraSession *m_session;
+ AVSampleBufferRenderSynchronizer *m_audioBufferSynchronizer;
+ dispatch_queue_t m_audioPreviewQueue;
+}
+
+- (id)init
+{
+ if (self = [super init]) {
+ m_session = nil;
+ m_audioBufferSynchronizer = [[AVSampleBufferRenderSynchronizer alloc] init];
+ m_audioRenderer = [[AVSampleBufferAudioRenderer alloc] init];
+ [m_audioBufferSynchronizer addRenderer:m_audioRenderer];
+ return self;
+ }
+ return nil;
+}
+
+- (void)captureOutput:(AVCaptureOutput *)captureOutput
+ didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
+ fromConnection:(AVCaptureConnection *)connection
+{
+ Q_UNUSED(connection);
+ Q_ASSERT(m_session);
+
+ if (!CMSampleBufferDataIsReady(sampleBuffer)) {
+ qWarning() << Q_FUNC_INFO << "sample buffer is not ready, skipping.";
+ return;
+ }
+
+ CFRetain(sampleBuffer);
+
+ dispatch_async(m_audioPreviewQueue, ^{
+ [self renderAudioSampleBuffer:sampleBuffer];
+ CFRelease(sampleBuffer);
+ });
+}
+
+- (void)renderAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer
+{
+ Q_ASSERT(sampleBuffer);
+ Q_ASSERT(m_session);
+
+ if (m_audioBufferSynchronizer && m_audioRenderer) {
+ [m_audioRenderer enqueueSampleBuffer:sampleBuffer];
+ if (m_audioBufferSynchronizer.rate == 0)
+ [m_audioBufferSynchronizer setRate:1 time:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)];
+ }
+}
+
+- (void)resetAudioPreviewDelegate
+{
+ [m_session->audioOutput() setSampleBufferDelegate:self queue:m_audioPreviewQueue];
+}
+
+- (void)setupWithCaptureSession: (AVFCameraSession*)session
+ audioOutputDevice: (NSString*)deviceId
+{
+ m_session = session;
+
+ m_audioPreviewQueue = dispatch_queue_create("audio-preview-queue", nullptr);
+ [m_session->audioOutput() setSampleBufferDelegate:self queue:m_audioPreviewQueue];
+#ifdef Q_OS_MACOS
+ m_audioRenderer.audioOutputDeviceUniqueID = deviceId;
+#endif
+}
+
+- (void)setVolume: (float)volume
+{
+ m_audioRenderer.volume = volume;
+}
+
+- (void)setMuted: (bool)muted
+{
+ m_audioRenderer.muted = muted;
+}
+
+-(void)dealloc {
+ m_session = nil;
+ [m_audioRenderer release];
+ [m_audioBufferSynchronizer release];
+ dispatch_release(m_audioPreviewQueue);
+
+ [super dealloc];
+}
+
+@end
diff --git a/src/plugins/multimedia/darwin/camera/avfaudiopreviewdelegate_p.h b/src/plugins/multimedia/darwin/camera/avfaudiopreviewdelegate_p.h
new file mode 100644
index 000000000..8fa06ef39
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfaudiopreviewdelegate_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 AVFAUDIOPREVIEWDELEGATE_P_H
+#define AVFAUDIOPREVIEWDELEGATE_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/qglobal.h>
+
+#include <AVFoundation/AVFoundation.h>
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraSession;
+
+QT_END_NAMESPACE
+
+@interface AVFAudioPreviewDelegate : NSObject<AVCaptureAudioDataOutputSampleBufferDelegate>
+
+- (id)init;
+- (void)setupWithCaptureSession: (AVFCameraSession*)session
+ audioOutputDevice: (NSString*)deviceId;
+- (void)renderAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer;
+- (void)resetAudioPreviewDelegate;
+- (void)setVolume: (float)volume;
+- (void)setMuted: (bool)muted;
+
+@end
+
+#endif // AVFAUDIOPREVIEWDELEGATE_P_H
diff --git a/src/plugins/multimedia/darwin/camera/avfcamera.mm b/src/plugins/multimedia/darwin/camera/avfcamera.mm
new file mode 100644
index 000000000..05cdbae17
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfcamera.mm
@@ -0,0 +1,89 @@
+// 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"
+#include "avfcamerasession_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcamerautility_p.h"
+#include "avfcamerarenderer_p.h"
+#include <qmediacapturesession.h>
+
+QT_USE_NAMESPACE
+
+AVFCamera::AVFCamera(QCamera *camera)
+ : QAVFCameraBase(camera)
+{
+ Q_ASSERT(camera);
+}
+
+AVFCamera::~AVFCamera()
+{
+}
+
+void AVFCamera::setActive(bool active)
+{
+ if (m_active == active)
+ return;
+ if (m_cameraDevice.isNull() && active)
+ return;
+
+ m_active = active;
+ if (m_session)
+ m_session->setActive(active);
+
+ if (active)
+ updateCameraConfiguration();
+ Q_EMIT activeChanged(m_active);
+}
+
+void AVFCamera::setCamera(const QCameraDevice &camera)
+{
+ if (m_cameraDevice == camera)
+ return;
+ m_cameraDevice = camera;
+ if (m_session)
+ m_session->setActiveCamera(camera);
+ setCameraFormat({});
+}
+
+bool AVFCamera::setCameraFormat(const QCameraFormat &format)
+{
+ if (!format.isNull() && !m_cameraDevice.videoFormats().contains(format))
+ return false;
+
+ m_cameraFormat = format.isNull() ? findBestCameraFormat(m_cameraDevice) : format;
+
+ if (m_session)
+ m_session->setCameraFormat(m_cameraFormat);
+
+ return true;
+}
+
+void AVFCamera::setCaptureSession(QPlatformMediaCaptureSession *session)
+{
+ AVFCameraService *captureSession = static_cast<AVFCameraService *>(session);
+ if (m_service == captureSession)
+ return;
+
+ if (m_session) {
+ m_session->disconnect(this);
+ m_session->setActiveCamera({});
+ m_session->setCameraFormat({});
+ }
+
+ m_service = captureSession;
+ if (!m_service) {
+ m_session = nullptr;
+ return;
+ }
+
+ m_session = m_service->session();
+ Q_ASSERT(m_session);
+
+ m_session->setActiveCamera(m_cameraDevice);
+ m_session->setCameraFormat(m_cameraFormat);
+ m_session->setActive(m_active);
+}
+
+#include "moc_avfcamera_p.cpp"
diff --git a/src/plugins/multimedia/darwin/camera/avfcamera_p.h b/src/plugins/multimedia/darwin/camera/avfcamera_p.h
new file mode 100644
index 000000000..3c3e6da09
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfcamera_p.h
@@ -0,0 +1,48 @@
+// 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
+
+//
+// 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 <qavfcamerabase_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraSession;
+class AVFCameraService;
+class AVFCameraSession;
+
+class AVFCamera : public QAVFCameraBase
+{
+Q_OBJECT
+public:
+ AVFCamera(QCamera *camera);
+ ~AVFCamera();
+
+ void setActive(bool activce) override;
+
+ void setCamera(const QCameraDevice &camera) override;
+ bool setCameraFormat(const QCameraFormat &format) override;
+
+ void setCaptureSession(QPlatformMediaCaptureSession *) override;
+
+private:
+ friend class AVFCameraSession;
+ AVFCameraService *m_service = nullptr;
+ AVFCameraSession *m_session = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/darwin/camera/avfcameradebug_p.h b/src/plugins/multimedia/darwin/camera/avfcameradebug_p.h
new file mode 100644
index 000000000..f93c85142
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfcameradebug_p.h
@@ -0,0 +1,26 @@
+// 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
+
+//
+// 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 <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(qLcCamera)
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/darwin/camera/avfcamerarenderer.mm b/src/plugins/multimedia/darwin/camera/avfcamerarenderer.mm
new file mode 100644
index 000000000..0c9ab3f2c
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfcamerarenderer.mm
@@ -0,0 +1,292 @@
+// 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 "qabstractvideobuffer.h"
+#include "private/qcameradevice_p.h"
+#include "private/qvideoframe_p.h"
+#include "avfcamerarenderer_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcameradebug_p.h"
+#include "avfcamera_p.h"
+#include <avfvideosink_p.h>
+#include <avfvideobuffer_p.h>
+#include "qvideosink.h"
+#include "qavfhelpers_p.h"
+
+#include <rhi/qrhi.h>
+
+#import <AVFoundation/AVFoundation.h>
+
+#ifdef Q_OS_IOS
+#include <QtGui/qopengl.h>
+#endif
+
+#include <QtMultimedia/qvideoframeformat.h>
+
+QT_USE_NAMESPACE
+
+@interface AVFCaptureFramesDelegate : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
+
+- (AVFCaptureFramesDelegate *) initWithRenderer:(AVFCameraRenderer*)renderer;
+
+- (void) captureOutput:(AVCaptureOutput *)captureOutput
+ didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
+ fromConnection:(AVCaptureConnection *)connection;
+
+@end
+
+@implementation AVFCaptureFramesDelegate
+{
+@private
+ AVFCameraRenderer *m_renderer;
+}
+
+- (AVFCaptureFramesDelegate *) initWithRenderer:(AVFCameraRenderer*)renderer
+{
+ if (!(self = [super init]))
+ return nil;
+
+ self->m_renderer = renderer;
+ 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);
+ auto buffer = std::make_unique<AVFVideoBuffer>(m_renderer, imageBuffer);
+ auto format = buffer->videoFormat();
+ if (!format.isValid()) {
+ return;
+ }
+
+ QVideoFrame frame = QVideoFramePrivate::createFrame(std::move(buffer), format);
+ m_renderer->syncHandleViewfinderFrame(frame);
+}
+
+@end
+
+AVFCameraRenderer::AVFCameraRenderer(QObject *parent)
+ : QObject(parent)
+{
+ m_viewfinderFramesDelegate = [[AVFCaptureFramesDelegate alloc] initWithRenderer:this];
+ connect(&m_orientationHandler, &QVideoOutputOrientationHandler::orientationChanged,
+ this, &AVFCameraRenderer::deviceOrientationChanged);
+}
+
+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
+ if (m_textureCache)
+ CFRelease(m_textureCache);
+#endif
+}
+
+void AVFCameraRenderer::reconfigure()
+{
+ QMutexLocker lock(&m_vfMutex);
+
+ // ### This is a hack, need to use a reliable way to determine the size and not use the preview layer
+ if (m_layer)
+ m_sink->setNativeSize(QSize(m_layer.bounds.size.width, m_layer.bounds.size.height));
+ nativeSizeChanged();
+ deviceOrientationChanged();
+}
+
+void AVFCameraRenderer::setOutputSettings()
+{
+ if (!m_videoDataOutput)
+ return;
+
+ 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)
+{
+ m_cameraSession = cameraSession;
+ connect(m_cameraSession, SIGNAL(readyToConfigureConnections()),
+ this, SLOT(updateCaptureConnection()));
+
+ m_needsHorizontalMirroring = false;
+
+ m_videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
+
+ // Configure video output
+ m_delegateQueue = dispatch_queue_create("vf_queue", nullptr);
+ [m_videoDataOutput
+ setSampleBufferDelegate:m_viewfinderFramesDelegate
+ queue:m_delegateQueue];
+
+ [m_cameraSession->captureSession() addOutput:m_videoDataOutput];
+}
+
+void AVFCameraRenderer::updateCaptureConnection()
+{
+ AVCaptureConnection *connection = [m_videoDataOutput connectionWithMediaType:AVMediaTypeVideo];
+ if (connection == nil || !m_cameraSession->videoCaptureDevice())
+ return;
+
+ // Frames of front-facing cameras should be mirrored horizontally (it's the default when using
+ // AVCaptureVideoPreviewLayer but not with AVCaptureVideoDataOutput)
+ if (connection.isVideoMirroringSupported)
+ connection.videoMirrored = m_cameraSession->videoCaptureDevice().position == AVCaptureDevicePositionFront;
+
+ // If the connection does't support mirroring, we'll have to do it ourselves
+ m_needsHorizontalMirroring = !connection.isVideoMirrored
+ && m_cameraSession->videoCaptureDevice().position == AVCaptureDevicePositionFront;
+
+ deviceOrientationChanged();
+}
+
+void AVFCameraRenderer::deviceOrientationChanged(int angle)
+{
+ AVCaptureConnection *connection = [m_videoDataOutput connectionWithMediaType:AVMediaTypeVideo];
+ if (connection == nil || !m_cameraSession->videoCaptureDevice())
+ return;
+
+ if (!connection.supportsVideoOrientation)
+ return;
+
+ if (angle < 0)
+ angle = m_orientationHandler.currentOrientation();
+
+ AVCaptureVideoOrientation orientation = AVCaptureVideoOrientationPortrait;
+ switch (angle) {
+ default:
+ break;
+ case 90:
+ orientation = AVCaptureVideoOrientationLandscapeRight;
+ break;
+ case 180:
+ // this keeps the last orientation, don't do anything
+ return;
+ case 270:
+ orientation = AVCaptureVideoOrientationLandscapeLeft;
+ break;
+ }
+
+ connection.videoOrientation = orientation;
+}
+
+//can be called from non main thread
+void AVFCameraRenderer::syncHandleViewfinderFrame(const QVideoFrame &frame)
+{
+ Q_EMIT newViewfinderFrame(frame);
+
+ QMutexLocker lock(&m_vfMutex);
+
+ if (!m_lastViewfinderFrame.isValid()) {
+ static QMetaMethod handleViewfinderFrameSlot = metaObject()->method(
+ metaObject()->indexOfMethod("handleViewfinderFrame()"));
+
+ handleViewfinderFrameSlot.invoke(this, Qt::QueuedConnection);
+ }
+
+ m_lastViewfinderFrame = frame;
+}
+
+AVCaptureVideoDataOutput *AVFCameraRenderer::videoDataOutput() const
+{
+ return m_videoDataOutput;
+}
+
+AVFCaptureFramesDelegate *AVFCameraRenderer::captureDelegate() const
+{
+ return m_viewfinderFramesDelegate;
+}
+
+void AVFCameraRenderer::resetCaptureDelegate() const
+{
+ [m_videoDataOutput setSampleBufferDelegate:m_viewfinderFramesDelegate queue:m_delegateQueue];
+}
+
+void AVFCameraRenderer::handleViewfinderFrame()
+{
+ QVideoFrame frame;
+ {
+ QMutexLocker lock(&m_vfMutex);
+ frame = m_lastViewfinderFrame;
+ m_lastViewfinderFrame = QVideoFrame();
+ }
+
+ if (m_sink && frame.isValid()) {
+ // frame.setMirroed(m_needsHorizontalMirroring) ?
+ m_sink->setVideoFrame(frame);
+ }
+}
+
+void AVFCameraRenderer::setPixelFormat(QVideoFrameFormat::PixelFormat pixelFormat,
+ QVideoFrameFormat::ColorRange colorRange)
+{
+ if (rhi() && rhi()->backend() == QRhi::OpenGLES2) {
+ if (pixelFormat != QVideoFrameFormat::Format_BGRA8888)
+ qWarning() << "OpenGL rhi backend only supports 32BGRA pixel format.";
+ return;
+ }
+
+ // 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).
+ 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] == cvPixelFormat) {
+ isSupported = true;
+ break;
+ }
+ }
+
+ if (isSupported) {
+ 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
+ };
+ 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?";
+ }
+}
+
+#include "moc_avfcamerarenderer_p.cpp"
+
diff --git a/src/plugins/multimedia/darwin/camera/avfcamerarenderer_p.h b/src/plugins/multimedia/darwin/camera/avfcamerarenderer_p.h
new file mode 100644
index 000000000..57f665cd6
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfcamerarenderer_p.h
@@ -0,0 +1,95 @@
+// 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
+
+//
+// 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 <QtMultimedia/qvideoframe.h>
+#include <QtCore/qmutex.h>
+#include <avfvideosink_p.h>
+#include <private/qvideooutputorientationhandler_p.h>
+
+#include <CoreVideo/CVBase.h>
+#include <CoreVideo/CVPixelBuffer.h>
+#include <CoreVideo/CVImageBuffer.h>
+#ifdef Q_OS_IOS
+#include <CoreVideo/CVOpenGLESTexture.h>
+#include <CoreVideo/CVOpenGLESTextureCache.h>
+#endif
+
+#include <dispatch/dispatch.h>
+
+Q_FORWARD_DECLARE_OBJC_CLASS(AVFCaptureFramesDelegate);
+Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureVideoDataOutput);
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraSession;
+class AVFCameraService;
+class AVFCameraRenderer;
+class AVFVideoSink;
+
+class AVFCameraRenderer : public QObject, public AVFVideoSinkInterface
+{
+Q_OBJECT
+public:
+ AVFCameraRenderer(QObject *parent = nullptr);
+ ~AVFCameraRenderer();
+
+ void reconfigure() override;
+ void setOutputSettings() override;
+
+ void configureAVCaptureSession(AVFCameraSession *cameraSession);
+ void syncHandleViewfinderFrame(const QVideoFrame &frame);
+
+ AVCaptureVideoDataOutput *videoDataOutput() const;
+
+ AVFCaptureFramesDelegate *captureDelegate() const;
+ void resetCaptureDelegate() const;
+
+ void setPixelFormat(QVideoFrameFormat::PixelFormat pixelFormat,
+ QVideoFrameFormat::ColorRange colorRange);
+
+Q_SIGNALS:
+ void newViewfinderFrame(const QVideoFrame &frame);
+
+private Q_SLOTS:
+ void handleViewfinderFrame();
+ void updateCaptureConnection();
+public Q_SLOTS:
+ void deviceOrientationChanged(int angle = -1);
+
+private:
+ AVFCaptureFramesDelegate *m_viewfinderFramesDelegate = nullptr;
+ AVFCameraSession *m_cameraSession = nullptr;
+ AVCaptureVideoDataOutput *m_videoDataOutput = nullptr;
+
+ bool m_needsHorizontalMirroring = false;
+
+#ifdef Q_OS_IOS
+ CVOpenGLESTextureCacheRef m_textureCache = nullptr;
+#endif
+
+ QVideoFrame m_lastViewfinderFrame;
+ QMutex m_vfMutex;
+ dispatch_queue_t m_delegateQueue;
+ QVideoOutputOrientationHandler m_orientationHandler;
+
+ friend class CVImageVideoBuffer;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/darwin/camera/avfcameraservice.mm b/src/plugins/multimedia/darwin/camera/avfcameraservice.mm
new file mode 100644
index 000000000..b25fb50a9
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfcameraservice.mm
@@ -0,0 +1,169 @@
+// 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>
+
+#include "avfcameraservice_p.h"
+#include "avfcamera_p.h"
+#include "avfcamerasession_p.h"
+#include "avfimagecapture_p.h"
+#include "avfcamerarenderer_p.h"
+#include "avfimagecapture_p.h"
+#include "avfmediaencoder_p.h"
+#include <qmediadevices.h>
+#include <private/qplatformaudioinput_p.h>
+#include <private/qplatformaudiooutput_p.h>
+#include <qaudioinput.h>
+#include <qaudiooutput.h>
+
+QT_USE_NAMESPACE
+
+AVFCameraService::AVFCameraService()
+{
+ m_session = new AVFCameraSession(this);
+}
+
+AVFCameraService::~AVFCameraService()
+{
+ if (m_session)
+ delete m_session;
+}
+
+QPlatformCamera *AVFCameraService::camera()
+{
+ return m_cameraControl;
+}
+
+void AVFCameraService::setCamera(QPlatformCamera *camera)
+{
+ AVFCamera *control = static_cast<AVFCamera *>(camera);
+ if (m_cameraControl == control)
+ return;
+
+ if (m_cameraControl) {
+ if (m_encoder)
+ m_cameraControl->disconnect(m_encoder);
+ m_cameraControl->setCaptureSession(nullptr);
+ }
+
+ m_cameraControl = control;
+
+ if (m_cameraControl)
+ m_cameraControl->setCaptureSession(this);
+
+ emit cameraChanged();
+}
+
+QPlatformImageCapture *AVFCameraService::imageCapture()
+{
+ return m_imageCaptureControl;
+}
+
+void AVFCameraService::setImageCapture(QPlatformImageCapture *imageCapture)
+{
+ AVFImageCapture *control = static_cast<AVFImageCapture *>(imageCapture);
+ if (m_imageCaptureControl == control)
+ return;
+
+ if (m_imageCaptureControl)
+ m_imageCaptureControl->setCaptureSession(nullptr);
+
+ m_imageCaptureControl = control;
+ if (m_imageCaptureControl)
+ m_imageCaptureControl->setCaptureSession(this);
+}
+
+QPlatformMediaRecorder *AVFCameraService::mediaRecorder()
+{
+ return m_encoder;
+}
+
+void AVFCameraService::setMediaRecorder(QPlatformMediaRecorder *recorder)
+{
+ AVFMediaEncoder *control = static_cast<AVFMediaEncoder *>(recorder);
+ if (m_encoder == control)
+ return;
+
+ if (m_encoder)
+ m_encoder->setCaptureSession(nullptr);
+
+ m_encoder = control;
+ if (m_encoder)
+ m_encoder->setCaptureSession(this);
+
+ emit encoderChanged();
+}
+
+void AVFCameraService::setAudioInput(QPlatformAudioInput *input)
+{
+ if (m_audioInput == input)
+ return;
+ if (m_audioInput)
+ m_audioInput->q->disconnect(this);
+
+ m_audioInput = input;
+
+ if (input) {
+ connect(m_audioInput->q, &QAudioInput::destroyed, this, &AVFCameraService::audioInputDestroyed);
+ connect(m_audioInput->q, &QAudioInput::deviceChanged, this, &AVFCameraService::audioInputChanged);
+ connect(m_audioInput->q, &QAudioInput::mutedChanged, this, &AVFCameraService::setAudioInputMuted);
+ connect(m_audioInput->q, &QAudioInput::volumeChanged, this, &AVFCameraService::setAudioInputVolume);
+ }
+ audioInputChanged();
+}
+
+void AVFCameraService::setAudioOutput(QPlatformAudioOutput *output)
+{
+ if (m_audioOutput == output)
+ return;
+ if (m_audioOutput)
+ m_audioOutput->q->disconnect(this);
+
+ m_audioOutput = output;
+
+ if (m_audioOutput) {
+ connect(m_audioOutput->q, &QAudioOutput::destroyed, this, &AVFCameraService::audioOutputDestroyed);
+ connect(m_audioOutput->q, &QAudioOutput::deviceChanged, this, &AVFCameraService::audioOutputChanged);
+ connect(m_audioOutput->q, &QAudioOutput::mutedChanged, this, &AVFCameraService::setAudioOutputMuted);
+ connect(m_audioOutput->q, &QAudioOutput::volumeChanged, this, &AVFCameraService::setAudioOutputVolume);
+ }
+ audioOutputChanged();
+}
+
+void AVFCameraService::audioInputChanged()
+{
+ m_session->updateAudioInput();
+}
+
+void AVFCameraService::audioOutputChanged()
+{
+ m_session->updateAudioOutput();
+}
+
+void AVFCameraService::setAudioInputMuted(bool muted)
+{
+ m_session->setAudioInputMuted(muted);
+}
+
+void AVFCameraService::setAudioInputVolume(float volume)
+{
+ m_session->setAudioInputVolume(volume);
+}
+
+void AVFCameraService::setAudioOutputMuted(bool muted)
+{
+ m_session->setAudioOutputMuted(muted);
+}
+
+void AVFCameraService::setAudioOutputVolume(float volume)
+{
+ m_session->setAudioOutputVolume(volume);
+}
+
+void AVFCameraService::setVideoPreview(QVideoSink *sink)
+{
+ m_session->setVideoSink(sink);
+}
+
+#include "moc_avfcameraservice_p.cpp"
diff --git a/src/plugins/multimedia/darwin/camera/avfcameraservice_p.h b/src/plugins/multimedia/darwin/camera/avfcameraservice_p.h
new file mode 100644
index 000000000..f3ef8d08e
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfcameraservice_p.h
@@ -0,0 +1,84 @@
+// 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
+
+//
+// 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/qset.h>
+#include <private/qplatformmediacapture_p.h>
+
+Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureDevice);
+
+QT_BEGIN_NAMESPACE
+class QPlatformCamera;
+class QPlatformMediaRecorder;
+class AVFCamera;
+class AVFImageCapture;
+class AVFCameraSession;
+class AVFMediaEncoder;
+
+class AVFCameraService : public QPlatformMediaCaptureSession
+{
+ Q_OBJECT
+public:
+ AVFCameraService();
+ ~AVFCameraService();
+
+ 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 *) override;
+ void setAudioOutput(QPlatformAudioOutput *) override;
+
+ void setVideoPreview(QVideoSink *sink) override;
+
+ AVFCameraSession *session() const { return m_session; }
+ AVFCamera *avfCameraControl() const { return m_cameraControl; }
+ AVFMediaEncoder *recorderControl() const { return m_encoder; }
+ AVFImageCapture *avfImageCaptureControl() const { return m_imageCaptureControl; }
+
+ QPlatformAudioInput *audioInput() { return m_audioInput; }
+ QPlatformAudioOutput *audioOutput() { return m_audioOutput; }
+
+public Q_SLOTS:
+ void audioInputDestroyed() { setAudioInput(nullptr); }
+ void audioInputChanged();
+ void audioOutputDestroyed() { setAudioOutput(nullptr); }
+ void audioOutputChanged();
+
+ void setAudioInputMuted(bool muted);
+ void setAudioInputVolume(float volume);
+ void setAudioOutputMuted(bool muted);
+ void setAudioOutputVolume(float volume);
+
+private:
+ QPlatformAudioInput *m_audioInput = nullptr;
+ QPlatformAudioOutput *m_audioOutput = nullptr;
+
+ AVFCameraSession *m_session = nullptr;
+ AVFCamera *m_cameraControl = nullptr;
+ AVFMediaEncoder *m_encoder = nullptr;
+ AVFImageCapture *m_imageCaptureControl = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/darwin/camera/avfcamerasession.mm b/src/plugins/multimedia/darwin/camera/avfcamerasession.mm
new file mode 100644
index 000000000..52e2eadfa
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfcamerasession.mm
@@ -0,0 +1,513 @@
+// 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"
+#include "avfcameraservice_p.h"
+#include "avfcamera_p.h"
+#include "avfcamerarenderer_p.h"
+#include "avfimagecapture_p.h"
+#include "avfmediaencoder_p.h"
+#include "avfcamerautility_p.h"
+#include <avfvideosink_p.h>
+
+#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>
+#include <private/qplatformaudiooutput_p.h>
+
+#include <QtCore/qdebug.h>
+
+QT_USE_NAMESPACE
+
+@interface AVFCameraSessionObserver : NSObject
+
+- (AVFCameraSessionObserver *) initWithCameraSession:(AVFCameraSession*)session;
+- (void) processRuntimeError:(NSNotification *)notification;
+- (void) processSessionStarted:(NSNotification *)notification;
+- (void) processSessionStopped:(NSNotification *)notification;
+
+@end
+
+@implementation AVFCameraSessionObserver
+{
+@private
+ AVFCameraSession *m_session;
+ AVCaptureSession *m_captureSession;
+}
+
+- (AVFCameraSessionObserver *) initWithCameraSession:(AVFCameraSession*)session
+{
+ if (!(self = [super init]))
+ return nil;
+
+ self->m_session = session;
+ self->m_captureSession = session->captureSession();
+
+ [m_captureSession retain];
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(processRuntimeError:)
+ name:AVCaptureSessionRuntimeErrorNotification
+ object:m_captureSession];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(processSessionStarted:)
+ name:AVCaptureSessionDidStartRunningNotification
+ object:m_captureSession];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(processSessionStopped:)
+ name:AVCaptureSessionDidStopRunningNotification
+ object:m_captureSession];
+
+ return self;
+}
+
+- (void) dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:AVCaptureSessionRuntimeErrorNotification
+ object:m_captureSession];
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:AVCaptureSessionDidStartRunningNotification
+ object:m_captureSession];
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:AVCaptureSessionDidStopRunningNotification
+ object:m_captureSession];
+ [m_captureSession release];
+ [super dealloc];
+}
+
+- (void) processRuntimeError:(NSNotification *)notification
+{
+ Q_UNUSED(notification);
+ QMetaObject::invokeMethod(m_session, "processRuntimeError", Qt::AutoConnection);
+}
+
+- (void) processSessionStarted:(NSNotification *)notification
+{
+ Q_UNUSED(notification);
+ QMetaObject::invokeMethod(m_session, "processSessionStarted", Qt::AutoConnection);
+}
+
+- (void) processSessionStopped:(NSNotification *)notification
+{
+ Q_UNUSED(notification);
+ QMetaObject::invokeMethod(m_session, "processSessionStopped", Qt::AutoConnection);
+}
+
+@end
+
+AVFCameraSession::AVFCameraSession(AVFCameraService *service, QObject *parent)
+ : QObject(parent)
+ , m_service(service)
+ , m_defaultCodec(0)
+{
+ m_captureSession = [[AVCaptureSession alloc] init];
+ m_observer = [[AVFCameraSessionObserver alloc] initWithCameraSession:this];
+}
+
+AVFCameraSession::~AVFCameraSession()
+{
+ if (m_videoInput) {
+ [m_captureSession removeInput:m_videoInput];
+ [m_videoInput release];
+ }
+
+ if (m_audioInput) {
+ [m_captureSession removeInput:m_audioInput];
+ [m_audioInput release];
+ }
+
+ if (m_audioOutput) {
+ [m_captureSession removeOutput:m_audioOutput];
+ [m_audioOutput release];
+ }
+
+ if (m_videoOutput)
+ delete m_videoOutput;
+
+ [m_observer release];
+ [m_captureSession release];
+}
+
+void AVFCameraSession::setActiveCamera(const QCameraDevice &info)
+{
+ if (m_activeCameraDevice != info) {
+ m_activeCameraDevice = info;
+
+ if (checkCameraPermission())
+ updateVideoInput();
+ }
+}
+
+void AVFCameraSession::setCameraFormat(const QCameraFormat &format)
+{
+ if (m_cameraFormat == format)
+ return;
+
+ updateCameraFormat(format);
+}
+
+QCameraFormat AVFCameraSession::cameraFormat() const
+{
+ return m_cameraFormat;
+}
+
+void AVFCameraSession::updateCameraFormat(const QCameraFormat &format)
+{
+ m_cameraFormat = format;
+
+ AVCaptureDevice *captureDevice = videoCaptureDevice();
+ if (!captureDevice)
+ return;
+
+ AVCaptureDeviceFormat *newFormat = qt_convert_to_capture_device_format(captureDevice, format);
+ if (newFormat)
+ qt_set_active_format(captureDevice, newFormat, false);
+}
+
+void AVFCameraSession::setVideoOutput(AVFCameraRenderer *output)
+{
+ if (m_videoOutput == output)
+ return;
+
+ delete m_videoOutput;
+ m_videoOutput = output;
+ if (output)
+ output->configureAVCaptureSession(this);
+}
+
+void AVFCameraSession::addAudioCapture()
+{
+ if (!m_audioOutput) {
+ m_audioOutput = [[AVCaptureAudioDataOutput alloc] init];
+ if (m_audioOutput && [m_captureSession canAddOutput:m_audioOutput]) {
+ [m_captureSession addOutput:m_audioOutput];
+ } else {
+ qWarning() << Q_FUNC_INFO << "failed to add audio output";
+ }
+ }
+}
+
+AVCaptureDevice *AVFCameraSession::videoCaptureDevice() const
+{
+ if (m_videoInput)
+ return m_videoInput.device;
+
+ return nullptr;
+}
+
+AVCaptureDevice *AVFCameraSession::audioCaptureDevice() const
+{
+ if (m_audioInput)
+ return m_audioInput.device;
+
+ return nullptr;
+}
+
+void AVFCameraSession::setAudioInputVolume(float volume)
+{
+ m_inputVolume = volume;
+
+ if (m_inputMuted)
+ volume = 0.0;
+
+#ifdef Q_OS_MACOS
+ AVCaptureConnection *audioInputConnection = [m_audioOutput connectionWithMediaType:AVMediaTypeAudio];
+ NSArray<AVCaptureAudioChannel *> *audioChannels = audioInputConnection.audioChannels;
+ if (audioChannels) {
+ for (AVCaptureAudioChannel *channel in audioChannels) {
+ channel.volume = volume;
+ }
+ }
+#endif
+}
+
+void AVFCameraSession::setAudioInputMuted(bool muted)
+{
+ m_inputMuted = muted;
+ setAudioInputVolume(m_inputVolume);
+}
+
+void AVFCameraSession::setAudioOutputVolume(float volume)
+{
+ if (m_audioPreviewDelegate)
+ [m_audioPreviewDelegate setVolume:volume];
+}
+
+void AVFCameraSession::setAudioOutputMuted(bool muted)
+{
+ if (m_audioPreviewDelegate)
+ [m_audioPreviewDelegate setMuted:muted];
+}
+
+bool AVFCameraSession::isActive() const
+{
+ return m_active;
+}
+
+void AVFCameraSession::setActive(bool active)
+{
+ if (m_active == active)
+ return;
+
+ m_active = active;
+
+ qCDebug(qLcCamera) << Q_FUNC_INFO << m_active << " -> " << active;
+
+ if (active) {
+ if (!m_activeCameraDevice.isNull()) {
+ Q_EMIT readyToConfigureConnections();
+ m_defaultCodec = 0;
+ defaultCodec();
+ }
+
+ applyImageEncoderSettings();
+
+ // According to the doc, the capture device must be locked before
+ // startRunning to prevent the format we set to be overridden by the
+ // session preset.
+ [videoCaptureDevice() lockForConfiguration:nil];
+ [m_captureSession startRunning];
+ [videoCaptureDevice() unlockForConfiguration];
+ } else {
+ [m_captureSession stopRunning];
+ }
+}
+
+void AVFCameraSession::processRuntimeError()
+{
+ qWarning() << tr("Runtime camera error");
+ m_active = false;
+ Q_EMIT error(QCamera::CameraError, tr("Runtime camera error"));
+}
+
+void AVFCameraSession::processSessionStarted()
+{
+ qCDebug(qLcCamera) << Q_FUNC_INFO;
+ if (!m_active) {
+ m_active = true;
+ Q_EMIT activeChanged(m_active);
+ }
+}
+
+void AVFCameraSession::processSessionStopped()
+{
+ qCDebug(qLcCamera) << Q_FUNC_INFO;
+ if (m_active) {
+ m_active = false;
+ Q_EMIT activeChanged(m_active);
+ }
+}
+
+AVCaptureDevice *AVFCameraSession::createVideoCaptureDevice()
+{
+ AVCaptureDevice *device = nullptr;
+
+ QByteArray deviceId = m_activeCameraDevice.id();
+ if (!deviceId.isEmpty()) {
+ device = [AVCaptureDevice deviceWithUniqueID:
+ [NSString stringWithUTF8String:
+ deviceId.constData()]];
+ }
+
+ return device;
+}
+
+AVCaptureDevice *AVFCameraSession::createAudioCaptureDevice()
+{
+ AVCaptureDevice *device = nullptr;
+
+ QByteArray deviceId = m_service->audioInput() ? m_service->audioInput()->device.id()
+ : QByteArray();
+ if (!deviceId.isEmpty())
+ device = [AVCaptureDevice deviceWithUniqueID: [NSString stringWithUTF8String:deviceId.constData()]];
+
+ return device;
+}
+
+void AVFCameraSession::attachVideoInputDevice()
+{
+ if (!checkCameraPermission())
+ return;
+
+ if (m_videoInput) {
+ [m_captureSession removeInput:m_videoInput];
+ [m_videoInput release];
+ m_videoInput = nullptr;
+ }
+
+ AVCaptureDevice *videoDevice = createVideoCaptureDevice();
+ if (!videoDevice)
+ return;
+
+ m_videoInput = [AVCaptureDeviceInput
+ deviceInputWithDevice:videoDevice
+ error:nil];
+ if (m_videoInput && [m_captureSession canAddInput:m_videoInput]) {
+ [m_videoInput retain];
+ [m_captureSession addInput:m_videoInput];
+ } else {
+ qWarning() << "Failed to create video device input";
+ }
+}
+
+void AVFCameraSession::attachAudioInputDevice()
+{
+ if (m_audioInput) {
+ [m_captureSession removeInput:m_audioInput];
+ [m_audioInput release];
+ m_audioInput = nullptr;
+ }
+
+ AVCaptureDevice *audioDevice = createAudioCaptureDevice();
+ if (!audioDevice)
+ return;
+
+ m_audioInput = [AVCaptureDeviceInput
+ deviceInputWithDevice:audioDevice
+ error:nil];
+
+ if (m_audioInput && [m_captureSession canAddInput:m_audioInput]) {
+ [m_audioInput retain];
+ [m_captureSession addInput:m_audioInput];
+ } else {
+ qWarning() << "Failed to create audio device input";
+ }
+}
+
+bool AVFCameraSession::applyImageEncoderSettings()
+{
+ if (AVFImageCapture *control = m_service->avfImageCaptureControl())
+ return control->applySettings();
+
+ return false;
+}
+
+FourCharCode AVFCameraSession::defaultCodec()
+{
+ if (!m_defaultCodec) {
+ if (AVCaptureDevice *device = videoCaptureDevice()) {
+ AVCaptureDeviceFormat *format = device.activeFormat;
+ if (!format || !format.formatDescription)
+ return m_defaultCodec;
+ m_defaultCodec = CMVideoFormatDescriptionGetCodecType(format.formatDescription);
+ }
+ }
+ return m_defaultCodec;
+}
+
+void AVFCameraSession::setVideoSink(QVideoSink *sink)
+{
+ auto *videoSink = sink ? static_cast<AVFVideoSink *>(sink->platformVideoSink()) : nullptr;
+
+ if (m_videoSink == videoSink)
+ return;
+
+ m_videoSink = videoSink;
+
+ updateVideoOutput();
+}
+
+void AVFCameraSession::updateVideoInput()
+{
+ auto recorder = m_service->recorderControl();
+ if (recorder && recorder->state() == QMediaRecorder::RecordingState)
+ recorder->toggleRecord(false);
+
+ [m_captureSession beginConfiguration];
+
+ attachVideoInputDevice();
+ if (!m_activeCameraDevice.isNull() && !m_videoOutput) {
+ setVideoOutput(new AVFCameraRenderer(this));
+ connect(m_videoOutput, &AVFCameraRenderer::newViewfinderFrame,
+ this, &AVFCameraSession::newViewfinderFrame);
+ updateVideoOutput();
+ }
+ if (m_videoOutput)
+ m_videoOutput->deviceOrientationChanged();
+
+ [m_captureSession commitConfiguration];
+
+ if (recorder && recorder->state() == QMediaRecorder::RecordingState)
+ recorder->toggleRecord(true);
+ Q_EMIT readyToConfigureConnections();
+}
+
+void AVFCameraSession::updateAudioInput()
+{
+ if (!checkMicrophonePermission())
+ return;
+
+ auto recorder = m_service->recorderControl();
+ if (recorder && recorder->state() == QMediaRecorder::RecordingState)
+ recorder->toggleRecord(false);
+
+ [m_captureSession beginConfiguration];
+ if (m_audioOutput) {
+ AVCaptureConnection *lastConnection = [m_audioOutput connectionWithMediaType:AVMediaTypeAudio];
+ [m_captureSession removeConnection:lastConnection];
+ }
+ attachAudioInputDevice();
+ if (m_audioInput)
+ addAudioCapture();
+ [m_captureSession commitConfiguration];
+
+ if (recorder && recorder->state() == QMediaRecorder::RecordingState)
+ recorder->toggleRecord(true);
+}
+
+void AVFCameraSession::updateAudioOutput()
+{
+ QByteArray deviceId = m_service->audioOutput()
+ ? m_service->audioOutput()->device.id()
+ : QByteArray();
+
+ [m_audioPreviewDelegate release];
+ m_audioPreviewDelegate = nil;
+ if (!deviceId.isEmpty()) {
+ m_audioPreviewDelegate = [[AVFAudioPreviewDelegate alloc] init];
+ [m_audioPreviewDelegate setupWithCaptureSession:this
+ audioOutputDevice:[NSString stringWithUTF8String:
+ deviceId.constData()]];
+ }
+}
+
+void AVFCameraSession::updateVideoOutput()
+{
+ if (m_videoOutput)
+ m_videoOutput->setVideoSink(m_videoSink);
+}
+
+bool AVFCameraSession::checkCameraPermission()
+{
+ const QCameraPermission permission;
+ const bool granted = qApp->checkPermission(permission) == Qt::PermissionStatus::Granted;
+ if (!granted)
+ qWarning() << "Access to camera not granted";
+
+ return granted;
+}
+
+bool AVFCameraSession::checkMicrophonePermission()
+{
+ const QMicrophonePermission permission;
+ const bool granted = qApp->checkPermission(permission) == Qt::PermissionStatus::Granted;
+ if (!granted)
+ qWarning() << "Access to microphone not granted";
+
+ 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
new file mode 100644
index 000000000..76e31ab48
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfcamerasession_p.h
@@ -0,0 +1,132 @@
+// 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
+
+//
+// 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 <QtMultimedia/qcamera.h>
+#include <QVideoFrame>
+#include <qcameradevice.h>
+#include "avfaudiopreviewdelegate_p.h"
+
+#import <AVFoundation/AVFoundation.h>
+
+@class AVFCameraSessionObserver;
+
+QT_BEGIN_NAMESPACE
+
+class AVFCamera;
+class AVFCameraService;
+class AVFCameraRenderer;
+class AVFVideoSink;
+class QVideoSink;
+
+class AVFCameraSession : public QObject
+{
+ Q_OBJECT
+public:
+ AVFCameraSession(AVFCameraService *service, QObject *parent = nullptr);
+ ~AVFCameraSession();
+
+ QCameraDevice activecameraDevice() const { return m_activeCameraDevice; }
+ 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; }
+ AVFAudioPreviewDelegate *audioPreviewDelegate() const { return m_audioPreviewDelegate; }
+
+ AVCaptureSession *captureSession() const { return m_captureSession; }
+ AVCaptureDevice *videoCaptureDevice() const;
+ AVCaptureDevice *audioCaptureDevice() const;
+
+ bool isActive() const;
+
+ FourCharCode defaultCodec();
+
+ AVCaptureDeviceInput *videoInput() const { return m_videoInput; }
+ AVCaptureDeviceInput *audioInput() const { return m_audioInput; }
+
+ void setVideoSink(QVideoSink *sink);
+
+ void updateVideoInput();
+
+ void updateAudioInput();
+ void updateAudioOutput();
+
+public Q_SLOTS:
+ void setActive(bool active);
+
+ void setAudioInputVolume(float volume);
+ void setAudioInputMuted(bool muted);
+ void setAudioOutputMuted(bool muted);
+ void setAudioOutputVolume(float volume);
+
+ void processRuntimeError();
+ void processSessionStarted();
+ void processSessionStopped();
+
+Q_SIGNALS:
+ void readyToConfigureConnections();
+ void activeChanged(bool);
+ void error(int error, const QString &errorString);
+ void newViewfinderFrame(const QVideoFrame &frame);
+
+private:
+ void updateCameraFormat(const QCameraFormat &format);
+
+ void setVideoOutput(AVFCameraRenderer *output);
+ void updateVideoOutput();
+
+ void addAudioCapture();
+
+ AVCaptureDevice *createVideoCaptureDevice();
+ AVCaptureDevice *createAudioCaptureDevice();
+ void attachVideoInputDevice();
+ void attachAudioInputDevice();
+ bool checkCameraPermission();
+ bool checkMicrophonePermission();
+
+ bool applyImageEncoderSettings();
+
+ QCameraDevice m_activeCameraDevice;
+ QCameraFormat m_cameraFormat;
+
+ AVFCameraService *m_service;
+ AVCaptureSession *m_captureSession;
+ AVFCameraSessionObserver *m_observer;
+
+ AVFCameraRenderer *m_videoOutput = nullptr;
+ AVFVideoSink *m_videoSink = nullptr;
+
+ AVCaptureDeviceInput *m_videoInput = nullptr;
+ AVCaptureDeviceInput *m_audioInput = nullptr;
+
+ AVCaptureAudioDataOutput *m_audioOutput = nullptr;
+ AVFAudioPreviewDelegate *m_audioPreviewDelegate = nullptr;
+
+ bool m_active = false;
+
+ float m_inputVolume = 1.0;
+ bool m_inputMuted = false;
+
+ FourCharCode m_defaultCodec;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/darwin/camera/avfcamerautility.mm b/src/plugins/multimedia/darwin/camera/avfcamerautility.mm
new file mode 100644
index 000000000..1864eb0e8
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfcamerautility.mm
@@ -0,0 +1,730 @@
+// 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"
+
+#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"
+
+#include <functional>
+#include <algorithm>
+#include <limits>
+#include <tuple>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qLcCamera, "qt.multimedia.camera")
+
+AVFPSRange qt_connection_framerates(AVCaptureConnection *videoConnection)
+{
+ Q_ASSERT(videoConnection);
+
+ AVFPSRange newRange;
+ // "The value in the videoMinFrameDuration is equivalent to the reciprocal
+ // of the maximum framerate, the value in the videoMaxFrameDuration is equivalent
+ // to the reciprocal of the minimum framerate."
+ if (videoConnection.supportsVideoMinFrameDuration) {
+ const CMTime cmMin = videoConnection.videoMinFrameDuration;
+ if (CMTimeCompare(cmMin, kCMTimeInvalid)) { // Has some non-default value:
+ if (const Float64 minSeconds = CMTimeGetSeconds(cmMin))
+ newRange.second = 1. / minSeconds;
+ }
+ }
+
+ if (videoConnection.supportsVideoMaxFrameDuration) {
+ const CMTime cmMax = videoConnection.videoMaxFrameDuration;
+ if (CMTimeCompare(cmMax, kCMTimeInvalid)) {
+ if (const Float64 maxSeconds = CMTimeGetSeconds(cmMax))
+ newRange.first = 1. / maxSeconds;
+ }
+ }
+
+ return newRange;
+}
+
+namespace {
+
+inline bool qt_area_sane(const QSize &size)
+{
+ return !size.isNull() && size.isValid()
+ && std::numeric_limits<int>::max() / size.width() >= size.height();
+}
+
+template <template <typename...> class Comp> // std::less or std::greater (or std::equal_to)
+struct ByResolution
+{
+ bool operator() (AVCaptureDeviceFormat *f1, AVCaptureDeviceFormat *f2)const
+ {
+ Q_ASSERT(f1 && f2);
+ const QSize r1(qt_device_format_resolution(f1));
+ const QSize r2(qt_device_format_resolution(f2));
+ // use std::tuple for lexicograpical sorting:
+ const Comp<std::tuple<int, int>> op = {};
+ return op(std::make_tuple(r1.width(), r1.height()),
+ std::make_tuple(r2.width(), r2.height()));
+ }
+};
+
+struct FormatHasNoFPSRange
+{
+ bool operator() (AVCaptureDeviceFormat *format) const
+ {
+ Q_ASSERT(format);
+ return !format.videoSupportedFrameRateRanges || !format.videoSupportedFrameRateRanges.count;
+ }
+};
+
+Float64 qt_find_min_framerate_distance(AVCaptureDeviceFormat *format, Float64 fps)
+{
+ Q_ASSERT(format && format.videoSupportedFrameRateRanges
+ && format.videoSupportedFrameRateRanges.count);
+
+ AVFrameRateRange *range = [format.videoSupportedFrameRateRanges objectAtIndex:0];
+ Float64 distance = qAbs(range.maxFrameRate - fps);
+ for (NSUInteger i = 1, e = format.videoSupportedFrameRateRanges.count; i < e; ++i) {
+ range = [format.videoSupportedFrameRateRanges objectAtIndex:i];
+ distance = qMin(distance, qAbs(range.maxFrameRate - fps));
+ }
+
+ return distance;
+}
+
+} // Unnamed namespace.
+
+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 cvPixFormat = CMVideoFormatDescriptionGetCodecType(formatDesc);
+
+ if (cvPixFormat != requiredCvPixFormat)
+ continue;
+
+ if (cameraFormatPrivate->resolution != QSize(dim.width, dim.height))
+ continue;
+
+ if (cvFormatValidator && !cvFormatValidator(cvPixFormat))
+ continue;
+
+ const float epsilon = 0.001f;
+ for (AVFrameRateRange *frameRateRange in format.videoSupportedFrameRateRanges) {
+ if (frameRateRange.minFrameRate >= cameraFormatPrivate->minFrameRate - epsilon
+ && frameRateRange.maxFrameRate <= cameraFormatPrivate->maxFrameRate + epsilon
+ && newFormatMaxFrameRate < frameRateRange.maxFrameRate) {
+ newFormat = format;
+ newFormatMaxFrameRate = frameRateRange.maxFrameRate;
+ }
+ }
+ }
+ return newFormat;
+}
+
+QVector<AVCaptureDeviceFormat *> qt_unique_device_formats(AVCaptureDevice *captureDevice, FourCharCode filter)
+{
+ // 'filter' is the format we prefer if we have duplicates.
+ Q_ASSERT(captureDevice);
+
+ QVector<AVCaptureDeviceFormat *> formats;
+
+ if (!captureDevice.formats || !captureDevice.formats.count)
+ return formats;
+
+ formats.reserve(captureDevice.formats.count);
+ for (AVCaptureDeviceFormat *format in captureDevice.formats) {
+ const QSize resolution(qt_device_format_resolution(format));
+ if (resolution.isNull() || !resolution.isValid())
+ continue;
+ formats << format;
+ }
+
+ if (!formats.size())
+ return formats;
+
+ std::sort(formats.begin(), formats.end(), ByResolution<std::less>());
+
+ QSize size(qt_device_format_resolution(formats[0]));
+ FourCharCode codec = CMVideoFormatDescriptionGetCodecType(formats[0].formatDescription);
+ int last = 0;
+ for (int i = 1; i < formats.size(); ++i) {
+ const QSize nextSize(qt_device_format_resolution(formats[i]));
+ if (nextSize == size) {
+ if (codec == filter)
+ continue;
+ formats[last] = formats[i];
+ } else {
+ ++last;
+ formats[last] = formats[i];
+ size = nextSize;
+ }
+ codec = CMVideoFormatDescriptionGetCodecType(formats[i].formatDescription);
+ }
+ formats.resize(last + 1);
+
+ return formats;
+}
+
+QSize qt_device_format_resolution(AVCaptureDeviceFormat *format)
+{
+ if (!format || !format.formatDescription)
+ return QSize();
+
+ const CMVideoDimensions res = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
+ return QSize(res.width, res.height);
+}
+
+QSize qt_device_format_high_resolution(AVCaptureDeviceFormat *format)
+{
+ Q_ASSERT(format);
+ QSize res;
+#if defined(Q_OS_IOS)
+ const CMVideoDimensions hrDim(format.highResolutionStillImageDimensions);
+ res.setWidth(hrDim.width);
+ res.setHeight(hrDim.height);
+#endif
+ return res;
+}
+
+QVector<AVFPSRange> qt_device_format_framerates(AVCaptureDeviceFormat *format)
+{
+ Q_ASSERT(format);
+
+ QVector<AVFPSRange> qtRanges;
+
+ if (!format.videoSupportedFrameRateRanges || !format.videoSupportedFrameRateRanges.count)
+ return qtRanges;
+
+ qtRanges.reserve(format.videoSupportedFrameRateRanges.count);
+ for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges)
+ qtRanges << AVFPSRange(range.minFrameRate, range.maxFrameRate);
+
+ return qtRanges;
+}
+
+QSize qt_device_format_pixel_aspect_ratio(AVCaptureDeviceFormat *format)
+{
+ Q_ASSERT(format);
+
+ if (!format.formatDescription) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "no format description found";
+ return QSize();
+ }
+
+ const CMVideoDimensions res = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
+ const CGSize resPAR = CMVideoFormatDescriptionGetPresentationDimensions(format.formatDescription, true, false);
+
+ if (qAbs(resPAR.width - res.width) < 1.) {
+ // "Pixel aspect ratio is used to adjust the width, leaving the height alone."
+ return QSize(1, 1);
+ }
+
+ if (!res.width || !resPAR.width)
+ return QSize();
+
+ auto frac = qRealToFraction(resPAR.width > res.width ? res.width / qreal(resPAR.width)
+ : resPAR.width / qreal(res.width));
+
+ return QSize(frac.numerator, frac.denominator);
+}
+
+AVCaptureDeviceFormat *qt_find_best_resolution_match(AVCaptureDevice *captureDevice,
+ const QSize &request,
+ FourCharCode filter,
+ bool stillImage)
+{
+ Q_ASSERT(captureDevice);
+ Q_ASSERT(!request.isNull() && request.isValid());
+
+ if (!captureDevice.formats || !captureDevice.formats.count)
+ return nullptr;
+
+ QVector<AVCaptureDeviceFormat *> formats(qt_unique_device_formats(captureDevice, filter));
+
+ for (int i = 0; i < formats.size(); ++i) {
+ AVCaptureDeviceFormat *format = formats[i];
+ if (qt_device_format_resolution(format) == request)
+ return format;
+ // iOS only (still images).
+ if (stillImage && qt_device_format_high_resolution(format) == request)
+ return format;
+ }
+
+ if (!qt_area_sane(request))
+ return nullptr;
+
+ typedef QPair<QSize, AVCaptureDeviceFormat *> FormatPair;
+
+ QVector<FormatPair> pairs; // default|HR sizes
+ pairs.reserve(formats.size());
+
+ for (int i = 0; i < formats.size(); ++i) {
+ AVCaptureDeviceFormat *format = formats[i];
+ const QSize res(qt_device_format_resolution(format));
+ if (!res.isNull() && res.isValid() && qt_area_sane(res))
+ pairs << FormatPair(res, format);
+ const QSize highRes(qt_device_format_high_resolution(format));
+ if (stillImage && !highRes.isNull() && highRes.isValid() && qt_area_sane(highRes))
+ pairs << FormatPair(highRes, format);
+ }
+
+ if (!pairs.size())
+ return nullptr;
+
+ AVCaptureDeviceFormat *best = pairs[0].second;
+ QSize next(pairs[0].first);
+ int wDiff = qAbs(request.width() - next.width());
+ int hDiff = qAbs(request.height() - next.height());
+ const int area = request.width() * request.height();
+ int areaDiff = qAbs(area - next.width() * next.height());
+ for (int i = 1; i < pairs.size(); ++i) {
+ next = pairs[i].first;
+ const int newWDiff = qAbs(next.width() - request.width());
+ const int newHDiff = qAbs(next.height() - request.height());
+ const int newAreaDiff = qAbs(area - next.width() * next.height());
+
+ if ((newWDiff < wDiff && newHDiff < hDiff)
+ || ((newWDiff <= wDiff || newHDiff <= hDiff) && newAreaDiff <= areaDiff)) {
+ wDiff = newWDiff;
+ hDiff = newHDiff;
+ best = pairs[i].second;
+ areaDiff = newAreaDiff;
+ }
+ }
+
+ return best;
+}
+
+AVCaptureDeviceFormat *qt_find_best_framerate_match(AVCaptureDevice *captureDevice,
+ FourCharCode filter,
+ Float64 fps)
+{
+ Q_ASSERT(captureDevice);
+ Q_ASSERT(fps > 0.);
+
+ const qreal epsilon = 0.1;
+
+ QVector<AVCaptureDeviceFormat *>sorted(qt_unique_device_formats(captureDevice, filter));
+ // Sort formats by their resolution in decreasing order:
+ std::sort(sorted.begin(), sorted.end(), ByResolution<std::greater>());
+ // We can use only formats with framerate ranges:
+ sorted.erase(std::remove_if(sorted.begin(), sorted.end(), FormatHasNoFPSRange()), sorted.end());
+
+ if (!sorted.size())
+ return nil;
+
+ for (int i = 0; i < sorted.size(); ++i) {
+ AVCaptureDeviceFormat *format = sorted[i];
+ for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) {
+ if (range.maxFrameRate - range.minFrameRate < epsilon) {
+ // On OS X ranges are points (built-in camera).
+ if (qAbs(fps - range.maxFrameRate) < epsilon)
+ return format;
+ }
+
+ if (fps >= range.minFrameRate && fps <= range.maxFrameRate)
+ return format;
+ }
+ }
+
+ Float64 distance = qt_find_min_framerate_distance(sorted[0], fps);
+ AVCaptureDeviceFormat *match = sorted[0];
+ for (int i = 1; i < sorted.size(); ++i) {
+ const Float64 newDistance = qt_find_min_framerate_distance(sorted[i], fps);
+ if (newDistance < distance) {
+ distance = newDistance;
+ match = sorted[i];
+ }
+ }
+
+ return match;
+}
+
+AVFrameRateRange *qt_find_supported_framerate_range(AVCaptureDeviceFormat *format, Float64 fps)
+{
+ Q_ASSERT(format && format.videoSupportedFrameRateRanges
+ && format.videoSupportedFrameRateRanges.count);
+
+ const qreal epsilon = 0.1;
+
+ for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) {
+ if (range.maxFrameRate - range.minFrameRate < epsilon) {
+ // On OS X ranges are points (built-in camera).
+ if (qAbs(fps - range.maxFrameRate) < epsilon)
+ return range;
+ }
+
+ if (fps >= range.minFrameRate && fps <= range.maxFrameRate)
+ return range;
+ }
+
+ AVFrameRateRange *match = [format.videoSupportedFrameRateRanges objectAtIndex:0];
+ Float64 distance = qAbs(match.maxFrameRate - fps);
+ for (NSUInteger i = 1, e = format.videoSupportedFrameRateRanges.count; i < e; ++i) {
+ AVFrameRateRange *range = [format.videoSupportedFrameRateRanges objectAtIndex:i];
+ const Float64 newDistance = qAbs(range.maxFrameRate - fps);
+ if (newDistance < distance) {
+ distance = newDistance;
+ match = range;
+ }
+ }
+
+ return match;
+}
+
+bool qt_format_supports_framerate(AVCaptureDeviceFormat *format, qreal fps)
+{
+ if (format && fps > qreal(0)) {
+ const qreal epsilon = 0.1;
+ for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) {
+ if (fps >= range.minFrameRate - epsilon && fps <= range.maxFrameRate + epsilon)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool qt_formats_are_equal(AVCaptureDeviceFormat *f1, AVCaptureDeviceFormat *f2)
+{
+ if (f1 == f2)
+ return true;
+
+ if (![f1.mediaType isEqualToString:f2.mediaType])
+ return false;
+
+ return CMFormatDescriptionEqual(f1.formatDescription, f2.formatDescription);
+}
+
+bool qt_set_active_format(AVCaptureDevice *captureDevice, AVCaptureDeviceFormat *format, bool preserveFps)
+{
+ static bool firstSet = true;
+
+ if (!captureDevice || !format)
+ return false;
+
+ if (qt_formats_are_equal(captureDevice.activeFormat, format)) {
+ if (firstSet) {
+ // The capture device format is persistent. The first time we set a format, report that
+ // it changed even if the formats are the same.
+ // This prevents the session from resetting the format to the default value.
+ firstSet = false;
+ return true;
+ }
+ return false;
+ }
+
+ firstSet = false;
+
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qWarning("Failed to set active format (lock failed)");
+ return false;
+ }
+
+ // Changing the activeFormat resets the frame rate.
+ AVFPSRange fps;
+ if (preserveFps)
+ fps = qt_current_framerates(captureDevice, nil);
+
+ captureDevice.activeFormat = format;
+
+ if (preserveFps)
+ qt_set_framerate_limits(captureDevice, nil, fps.first, fps.second);
+
+ return true;
+}
+
+void qt_set_framerate_limits(AVCaptureConnection *videoConnection, qreal minFPS, qreal maxFPS)
+{
+ Q_ASSERT(videoConnection);
+
+ if (minFPS < 0. || maxFPS < 0. || (maxFPS && maxFPS < minFPS)) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "invalid framerates (min, max):"
+ << minFPS << maxFPS;
+ return;
+ }
+
+ CMTime minDuration = kCMTimeInvalid;
+ if (maxFPS > 0.) {
+ if (!videoConnection.supportsVideoMinFrameDuration)
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "maximum framerate is not supported";
+ else
+ minDuration = CMTimeMake(1, maxFPS);
+ }
+ if (videoConnection.supportsVideoMinFrameDuration)
+ videoConnection.videoMinFrameDuration = minDuration;
+
+ CMTime maxDuration = kCMTimeInvalid;
+ if (minFPS > 0.) {
+ if (!videoConnection.supportsVideoMaxFrameDuration)
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "minimum framerate is not supported";
+ else
+ maxDuration = CMTimeMake(1, minFPS);
+ }
+ if (videoConnection.supportsVideoMaxFrameDuration)
+ videoConnection.videoMaxFrameDuration = maxDuration;
+}
+
+CMTime qt_adjusted_frame_duration(AVFrameRateRange *range, qreal fps)
+{
+ Q_ASSERT(range);
+ Q_ASSERT(fps > 0.);
+
+ if (range.maxFrameRate - range.minFrameRate < 0.1) {
+ // Can happen on OS X.
+ return range.minFrameDuration;
+ }
+
+ if (fps <= range.minFrameRate)
+ return range.maxFrameDuration;
+ if (fps >= range.maxFrameRate)
+ return range.minFrameDuration;
+
+ auto frac = qRealToFraction(1. / fps);
+ return CMTimeMake(frac.numerator, frac.denominator);
+}
+
+void qt_set_framerate_limits(AVCaptureDevice *captureDevice, qreal minFPS, qreal maxFPS)
+{
+ Q_ASSERT(captureDevice);
+ if (!captureDevice.activeFormat) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "no active capture device format";
+ return;
+ }
+
+ if (minFPS < 0. || maxFPS < 0. || (maxFPS && maxFPS < minFPS)) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "invalid framerates (min, max):"
+ << minFPS << maxFPS;
+ return;
+ }
+
+ CMTime minFrameDuration = kCMTimeInvalid;
+ CMTime maxFrameDuration = kCMTimeInvalid;
+ if (maxFPS || minFPS) {
+ AVFrameRateRange *range = qt_find_supported_framerate_range(captureDevice.activeFormat,
+ maxFPS ? maxFPS : minFPS);
+ if (!range) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "no framerate range found, (min, max):"
+ << minFPS << maxFPS;
+ return;
+ }
+
+ if (maxFPS)
+ minFrameDuration = qt_adjusted_frame_duration(range, maxFPS);
+ if (minFPS)
+ maxFrameDuration = qt_adjusted_frame_duration(range, minFPS);
+ }
+
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
+ return;
+ }
+
+ // While Apple's docs say kCMTimeInvalid will end in default
+ // settings for this format, kCMTimeInvalid on OS X ends with a runtime
+ // exception:
+ // "The activeVideoMinFrameDuration passed is not supported by the device."
+ // Instead, use the first item in the supported frame rates.
+#ifdef Q_OS_IOS
+ [captureDevice setActiveVideoMinFrameDuration:minFrameDuration];
+ [captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration];
+#elif defined(Q_OS_MACOS)
+ if (CMTimeCompare(minFrameDuration, kCMTimeInvalid) == 0
+ && CMTimeCompare(maxFrameDuration, kCMTimeInvalid) == 0) {
+ AVFrameRateRange *range = captureDevice.activeFormat.videoSupportedFrameRateRanges.firstObject;
+ minFrameDuration = range.minFrameDuration;
+ maxFrameDuration = range.maxFrameDuration;
+ }
+
+ if (CMTimeCompare(minFrameDuration, kCMTimeInvalid))
+ [captureDevice setActiveVideoMinFrameDuration:minFrameDuration];
+
+ if (CMTimeCompare(maxFrameDuration, kCMTimeInvalid))
+ [captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration];
+#endif // Q_OS_MACOS
+}
+
+void qt_set_framerate_limits(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection,
+ qreal minFPS, qreal maxFPS)
+{
+ Q_UNUSED(videoConnection);
+ Q_ASSERT(captureDevice);
+ qt_set_framerate_limits(captureDevice, minFPS, maxFPS);
+}
+
+AVFPSRange qt_current_framerates(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection)
+{
+ Q_UNUSED(videoConnection);
+ Q_ASSERT(captureDevice);
+
+ AVFPSRange fps;
+ const CMTime minDuration = captureDevice.activeVideoMinFrameDuration;
+ if (CMTimeCompare(minDuration, kCMTimeInvalid)) {
+ if (const Float64 minSeconds = CMTimeGetSeconds(minDuration))
+ fps.second = 1. / minSeconds; // Max FPS = 1 / MinDuration.
+ }
+
+ const CMTime maxDuration = captureDevice.activeVideoMaxFrameDuration;
+ if (CMTimeCompare(maxDuration, kCMTimeInvalid)) {
+ if (const Float64 maxSeconds = CMTimeGetSeconds(maxDuration))
+ fps.first = 1. / maxSeconds; // Min FPS = 1 / MaxDuration.
+ }
+
+ return fps;
+}
+
+QList<AudioValueRange> qt_supported_sample_rates_for_format(int codecId)
+{
+ QList<AudioValueRange> result;
+ UInt32 format = codecId;
+ UInt32 size;
+ OSStatus err = AudioFormatGetPropertyInfo(
+ kAudioFormatProperty_AvailableEncodeSampleRates,
+ sizeof(format),
+ &format,
+ &size);
+
+ if (err != noErr)
+ return result;
+
+ UInt32 numRanges = size / sizeof(AudioValueRange);
+ AudioValueRange sampleRanges[numRanges];
+
+ err = AudioFormatGetProperty(kAudioFormatProperty_AvailableEncodeSampleRates,
+ sizeof(format),
+ &format,
+ &size,
+ sampleRanges);
+ if (err != noErr)
+ return result;
+
+ for (UInt32 i = 0; i < numRanges; i++)
+ result << sampleRanges[i];
+
+ return result;
+}
+
+QList<AudioValueRange> qt_supported_bit_rates_for_format(int codecId)
+{
+ QList<AudioValueRange> result;
+ UInt32 format = codecId;
+ UInt32 size;
+ OSStatus err = AudioFormatGetPropertyInfo(
+ kAudioFormatProperty_AvailableEncodeBitRates,
+ sizeof(format),
+ &format,
+ &size);
+
+ if (err != noErr)
+ return result;
+
+ UInt32 numRanges = size / sizeof(AudioValueRange);
+ AudioValueRange bitRanges[numRanges];
+
+ err = AudioFormatGetProperty(kAudioFormatProperty_AvailableEncodeBitRates,
+ sizeof(format),
+ &format,
+ &size,
+ bitRanges);
+ if (err != noErr)
+ return result;
+
+ for (UInt32 i = 0; i < numRanges; i++)
+ result << bitRanges[i];
+
+ return result;
+}
+
+std::optional<QList<UInt32>> qt_supported_channel_counts_for_format(int codecId)
+{
+ QList<UInt32> result;
+ AudioStreamBasicDescription sf = {};
+ sf.mFormatID = codecId;
+ UInt32 size;
+ OSStatus err = AudioFormatGetPropertyInfo(
+ kAudioFormatProperty_AvailableEncodeNumberChannels,
+ sizeof(sf),
+ &sf,
+ &size);
+
+ if (err != noErr)
+ return result;
+
+ // From Apple's docs:
+ // A value of 0xFFFFFFFF indicates that any number of channels may be encoded.
+ if (int(size) == -1)
+ return std::nullopt;
+
+ UInt32 numCounts = size / sizeof(UInt32);
+ UInt32 channelCounts[numCounts];
+
+ err = AudioFormatGetProperty(kAudioFormatProperty_AvailableEncodeNumberChannels,
+ sizeof(sf),
+ &sf,
+ &size,
+ channelCounts);
+ if (err != noErr)
+ return result;
+
+ for (UInt32 i = 0; i < numCounts; i++)
+ result << channelCounts[i];
+
+ return result;
+}
+
+QList<UInt32> qt_supported_channel_layout_tags_for_format(int codecId, int noChannels)
+{
+ QList<UInt32> result;
+ AudioStreamBasicDescription sf = {};
+ sf.mFormatID = codecId;
+ sf.mChannelsPerFrame = noChannels;
+ UInt32 size;
+ OSStatus err = AudioFormatGetPropertyInfo(
+ kAudioFormatProperty_AvailableEncodeChannelLayoutTags,
+ sizeof(sf),
+ &sf,
+ &size);
+
+ if (err != noErr)
+ return result;
+
+ UInt32 noTags = (UInt32)size / sizeof(UInt32);
+ AudioChannelLayoutTag tagsArr[noTags];
+
+ err = AudioFormatGetProperty(kAudioFormatProperty_AvailableEncodeChannelLayoutTags,
+ sizeof(sf),
+ &sf,
+ &size,
+ tagsArr);
+ if (err != noErr)
+ return result;
+
+ for (UInt32 i = 0; i < noTags; i++)
+ result << tagsArr[i];
+
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/darwin/camera/avfcamerautility_p.h b/src/plugins/multimedia/darwin/camera/avfcamerautility_p.h
new file mode 100644
index 000000000..b5c9e9bda
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfcamerautility_p.h
@@ -0,0 +1,165 @@
+// 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
+
+//
+// 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/qglobal.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qsize.h>
+
+#include "qcameradevice.h"
+
+#include <AVFoundation/AVFoundation.h>
+
+// In case we have SDK below 10.7/7.0:
+@class AVCaptureDeviceFormat;
+
+QT_BEGIN_NAMESPACE
+
+class AVFConfigurationLock
+{
+public:
+ explicit AVFConfigurationLock(AVCaptureDevice *captureDevice)
+ : m_captureDevice(captureDevice),
+ m_locked(false)
+ {
+ Q_ASSERT(m_captureDevice);
+ NSError *error = nil;
+ m_locked = [m_captureDevice lockForConfiguration:&error];
+ }
+
+ ~AVFConfigurationLock()
+ {
+ if (m_locked)
+ [m_captureDevice unlockForConfiguration];
+ }
+
+ operator bool() const
+ {
+ return m_locked;
+ }
+
+private:
+ Q_DISABLE_COPY(AVFConfigurationLock)
+
+ AVCaptureDevice *m_captureDevice;
+ bool m_locked;
+};
+
+struct AVFObjectDeleter {
+ void operator()(NSObject *obj)
+ {
+ if (obj)
+ [obj release];
+ }
+};
+
+template<class T>
+class AVFScopedPointer : public std::unique_ptr<NSObject, AVFObjectDeleter>
+{
+public:
+ AVFScopedPointer() {}
+ explicit AVFScopedPointer(T *ptr) : std::unique_ptr<NSObject, AVFObjectDeleter>(ptr) {}
+ operator T*() const
+ {
+ // Quite handy operator to enable Obj-C messages: [ptr someMethod];
+ return data();
+ }
+
+ T *data() const
+ {
+ return static_cast<T *>(get());
+ }
+
+ T *take()
+ {
+ return static_cast<T *>(release());
+ }
+};
+
+template<>
+class AVFScopedPointer<dispatch_queue_t>
+{
+public:
+ AVFScopedPointer() : m_queue(nullptr) {}
+ explicit AVFScopedPointer(dispatch_queue_t q) : m_queue(q) {}
+
+ ~AVFScopedPointer()
+ {
+ if (m_queue)
+ dispatch_release(m_queue);
+ }
+
+ operator dispatch_queue_t() const
+ {
+ // Quite handy operator to enable Obj-C messages: [ptr someMethod];
+ return m_queue;
+ }
+
+ dispatch_queue_t data() const
+ {
+ return m_queue;
+ }
+
+ void reset(dispatch_queue_t q = nullptr)
+ {
+ if (m_queue)
+ dispatch_release(m_queue);
+ m_queue = q;
+ }
+
+private:
+ dispatch_queue_t m_queue;
+
+ Q_DISABLE_COPY(AVFScopedPointer)
+};
+
+typedef QPair<qreal, qreal> AVFPSRange;
+AVFPSRange qt_connection_framerates(AVCaptureConnection *videoConnection);
+
+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);
+QSize qt_device_format_high_resolution(AVCaptureDeviceFormat *format);
+QSize qt_device_format_pixel_aspect_ratio(AVCaptureDeviceFormat *format);
+QList<AVFPSRange> qt_device_format_framerates(AVCaptureDeviceFormat *format);
+AVCaptureDeviceFormat *qt_find_best_resolution_match(AVCaptureDevice *captureDevice, const QSize &res,
+ FourCharCode preferredFormat, bool stillImage = true);
+AVCaptureDeviceFormat *qt_find_best_framerate_match(AVCaptureDevice *captureDevice,
+ FourCharCode preferredFormat,
+ Float64 fps);
+AVFrameRateRange *qt_find_supported_framerate_range(AVCaptureDeviceFormat *format, Float64 fps);
+bool qt_format_supports_framerate(AVCaptureDeviceFormat *format, qreal fps);
+
+bool qt_formats_are_equal(AVCaptureDeviceFormat *f1, AVCaptureDeviceFormat *f2);
+bool qt_set_active_format(AVCaptureDevice *captureDevice, AVCaptureDeviceFormat *format, bool preserveFps);
+
+AVFPSRange qt_current_framerates(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection);
+void qt_set_framerate_limits(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection,
+ qreal minFPS, qreal maxFPS);
+
+QList<AudioValueRange> qt_supported_sample_rates_for_format(int codecId);
+QList<AudioValueRange> qt_supported_bit_rates_for_format(int codecId);
+std::optional<QList<UInt32>> qt_supported_channel_counts_for_format(int codecId);
+QList<UInt32> qt_supported_channel_layout_tags_for_format(int codecId, int noChannels);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/darwin/camera/avfimagecapture.mm b/src/plugins/multimedia/darwin/camera/avfimagecapture.mm
new file mode 100644
index 000000000..2ee7b0597
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfimagecapture.mm
@@ -0,0 +1,385 @@
+// 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"
+#include "avfcameraservice_p.h"
+#include "avfcamerautility_p.h"
+#include "avfcamera_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcamerarenderer_p.h"
+#include "private/qmediastoragelocation_p.h"
+#include <private/qplatformimagecapture_p.h>
+#include <private/qmemoryvideobuffer_p.h>
+#include <private/qvideoframe_p.h>
+
+#include <QtCore/qurl.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qbuffer.h>
+#include <QtConcurrent/qtconcurrentrun.h>
+#include <QtGui/qimagereader.h>
+
+#import <AVFoundation/AVFoundation.h>
+
+QT_USE_NAMESPACE
+
+AVFImageCapture::AVFImageCapture(QImageCapture *parent)
+ : QPlatformImageCapture(parent)
+{
+ m_stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
+
+ NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:
+ AVVideoCodecTypeJPEG, AVVideoCodecKey, nil];
+
+ [m_stillImageOutput setOutputSettings:outputSettings];
+ [outputSettings release];
+}
+
+AVFImageCapture::~AVFImageCapture()
+{
+ [m_stillImageOutput release];
+}
+
+bool AVFImageCapture::isReadyForCapture() const
+{
+ return m_cameraControl && m_videoConnection && m_cameraControl->isActive();
+}
+
+void AVFImageCapture::updateReadyStatus()
+{
+ if (m_ready != isReadyForCapture()) {
+ m_ready = !m_ready;
+ qCDebug(qLcCamera) << "ReadyToCapture status changed:" << m_ready;
+ Q_EMIT readyForCaptureChanged(m_ready);
+ }
+}
+
+int AVFImageCapture::doCapture(const QString &actualFileName)
+{
+ if (!m_session) {
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(int, m_lastCaptureId),
+ Q_ARG(int, QImageCapture::ResourceError),
+ Q_ARG(QString, QPlatformImageCapture::msgImageCaptureNotSet()));
+ return -1;
+ }
+ if (!isReadyForCapture()) {
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(int, m_lastCaptureId),
+ Q_ARG(int, QImageCapture::NotReadyError),
+ Q_ARG(QString, QPlatformImageCapture::msgCameraNotReady()));
+ return -1;
+ }
+ m_lastCaptureId++;
+
+ bool captureToBuffer = actualFileName.isEmpty();
+
+ CaptureRequest request = { m_lastCaptureId, QSharedPointer<QSemaphore>::create()};
+ m_requestsMutex.lock();
+ m_captureRequests.enqueue(request);
+ m_requestsMutex.unlock();
+
+ QString fileName(actualFileName);
+
+ [m_stillImageOutput captureStillImageAsynchronouslyFromConnection:m_videoConnection
+ completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
+
+ if (error) {
+ QStringList messageParts;
+ messageParts << QString::fromUtf8([[error localizedDescription] UTF8String]);
+ messageParts << QString::fromUtf8([[error localizedFailureReason] UTF8String]);
+ messageParts << QString::fromUtf8([[error localizedRecoverySuggestion] UTF8String]);
+
+ QString errorMessage = messageParts.join(QChar(u' '));
+ qCDebug(qLcCamera) << "Image capture failed:" << errorMessage;
+
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(int, request.captureId),
+ Q_ARG(int, QImageCapture::ResourceError),
+ Q_ARG(QString, errorMessage));
+ return;
+ }
+
+ // Wait for the preview to be generated before saving the JPEG (but only
+ // if we have AVFCameraRenderer attached).
+ // It is possible to stop camera immediately after trying to capture an
+ // image; this can result in a blocked callback's thread, waiting for a
+ // new viewfinder's frame to arrive/semaphore to be released. It is also
+ // unspecified on which thread this callback gets executed, (probably it's
+ // not the same thread that initiated a capture and stopped the camera),
+ // so we cannot reliably check the camera's status. Instead, we wait
+ // with a timeout and treat a failure to acquire a semaphore as an error.
+ if (!m_session->videoOutput() || request.previewReady->tryAcquire(1, 1000)) {
+ qCDebug(qLcCamera) << "Image capture completed";
+
+ NSData *nsJpgData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
+ QByteArray jpgData = QByteArray::fromRawData((const char *)[nsJpgData bytes], [nsJpgData length]);
+
+ if (captureToBuffer) {
+ QBuffer data(&jpgData);
+ QImageReader reader(&data, "JPEG");
+ QSize size = reader.size();
+ auto buffer = std::make_unique<QMemoryVideoBuffer>(
+ QByteArray(jpgData.constData(), jpgData.size()), -1);
+ QVideoFrame frame = QVideoFramePrivate::createFrame(
+ std::move(buffer), QVideoFrameFormat(size, QVideoFrameFormat::Format_Jpeg));
+ QMetaObject::invokeMethod(this, "imageAvailable", Qt::QueuedConnection,
+ Q_ARG(int, request.captureId),
+ Q_ARG(QVideoFrame, frame));
+ } else {
+ QFile f(fileName);
+ if (f.open(QFile::WriteOnly)) {
+ if (f.write(jpgData) != -1) {
+ QMetaObject::invokeMethod(this, "imageSaved", Qt::QueuedConnection,
+ Q_ARG(int, request.captureId),
+ Q_ARG(QString, fileName));
+ } else {
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(int, request.captureId),
+ Q_ARG(int, QImageCapture::OutOfSpaceError),
+ Q_ARG(QString, f.errorString()));
+ }
+ } else {
+ QString errorMessage = tr("Could not open destination file:\n%1").arg(fileName);
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(int, request.captureId),
+ Q_ARG(int, QImageCapture::ResourceError),
+ Q_ARG(QString, errorMessage));
+ }
+ }
+ } else {
+ const QLatin1String errorMessage("Image capture failed: timed out waiting"
+ " for a preview frame.");
+ qCDebug(qLcCamera) << errorMessage;
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(int, request.captureId),
+ Q_ARG(int, QImageCapture::ResourceError),
+ Q_ARG(QString, errorMessage));
+ }
+ }];
+
+ return request.captureId;
+}
+
+int AVFImageCapture::capture(const QString &fileName)
+{
+ auto actualFileName = QMediaStorageLocation::generateFileName(fileName, QStandardPaths::PicturesLocation, QLatin1String("jpg"));
+
+ qCDebug(qLcCamera) << "Capture image to" << actualFileName;
+ return doCapture(actualFileName);
+}
+
+int AVFImageCapture::captureToBuffer()
+{
+ return doCapture(QString());
+}
+
+void AVFImageCapture::onNewViewfinderFrame(const QVideoFrame &frame)
+{
+ QMutexLocker locker(&m_requestsMutex);
+
+ if (m_captureRequests.isEmpty())
+ return;
+
+ CaptureRequest request = m_captureRequests.dequeue();
+ Q_EMIT imageExposed(request.captureId);
+
+ (void) QtConcurrent::run(&AVFImageCapture::makeCapturePreview, this,
+ request,
+ frame,
+ 0 /* rotation */);
+}
+
+void AVFImageCapture::onCameraChanged()
+{
+ auto camera = m_service ? static_cast<AVFCamera *>(m_service->camera()) : nullptr;
+
+ if (camera == m_cameraControl)
+ return;
+
+ m_cameraControl = camera;
+
+ if (m_cameraControl)
+ connect(m_cameraControl, SIGNAL(activeChanged(bool)), this, SLOT(updateReadyStatus()));
+ updateReadyStatus();
+}
+
+void AVFImageCapture::makeCapturePreview(CaptureRequest request,
+ const QVideoFrame &frame,
+ int rotation)
+{
+ QTransform transform;
+ transform.rotate(rotation);
+
+ Q_EMIT imageCaptured(request.captureId, frame.toImage().transformed(transform));
+
+ request.previewReady->release();
+}
+
+void AVFImageCapture::updateCaptureConnection()
+{
+ if (m_session && m_session->videoCaptureDevice()) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO;
+ AVCaptureSession *captureSession = m_session->captureSession();
+
+ if (![captureSession.outputs containsObject:m_stillImageOutput]) {
+ if ([captureSession canAddOutput:m_stillImageOutput]) {
+ [captureSession beginConfiguration];
+ // Lock the video capture device to make sure the active format is not reset
+ const AVFConfigurationLock lock(m_session->videoCaptureDevice());
+ [captureSession addOutput:m_stillImageOutput];
+ m_videoConnection = [m_stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
+ [captureSession commitConfiguration];
+ updateReadyStatus();
+ }
+ } else {
+ m_videoConnection = [m_stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
+ }
+ }
+}
+
+
+QImageEncoderSettings AVFImageCapture::imageSettings() const
+{
+ QImageEncoderSettings settings;
+
+ if (!videoCaptureDeviceIsValid())
+ return settings;
+
+ AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
+ if (!captureDevice.activeFormat) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "no active format";
+ return settings;
+ }
+
+ QSize res(qt_device_format_resolution(captureDevice.activeFormat));
+#ifdef Q_OS_IOS
+ if (!m_service->avfImageCaptureControl() || !m_service->avfImageCaptureControl()->stillImageOutput()) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "no still image output";
+ return settings;
+ }
+
+ AVCaptureStillImageOutput *stillImageOutput = m_service->avfImageCaptureControl()->stillImageOutput();
+ if (stillImageOutput.highResolutionStillImageOutputEnabled)
+ res = qt_device_format_high_resolution(captureDevice.activeFormat);
+#endif
+ if (res.isNull() || !res.isValid()) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to exctract the image resolution";
+ return settings;
+ }
+
+ settings.setResolution(res);
+ settings.setFormat(QImageCapture::JPEG);
+
+ return settings;
+}
+
+void AVFImageCapture::setImageSettings(const QImageEncoderSettings &settings)
+{
+ if (m_settings == settings)
+ return;
+
+ m_settings = settings;
+ applySettings();
+}
+
+bool AVFImageCapture::applySettings()
+{
+ if (!videoCaptureDeviceIsValid())
+ return false;
+
+ AVFCameraSession *session = m_service->session();
+ if (!session)
+ return false;
+
+ if (!m_service->imageCapture()
+ || !m_service->avfImageCaptureControl()->stillImageOutput()) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "no still image output";
+ return false;
+ }
+
+ if (m_settings.format() != QImageCapture::UnspecifiedFormat && m_settings.format() != QImageCapture::JPEG) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "unsupported format:" << m_settings.format();
+ return false;
+ }
+
+ QSize res(m_settings.resolution());
+ if (res.isNull()) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "invalid resolution:" << res;
+ return false;
+ }
+
+ if (!res.isValid()) {
+ // Invalid == default value.
+ // Here we could choose the best format available, but
+ // activeFormat is already equal to 'preset high' by default,
+ // which is good enough, otherwise we can end in some format with low framerates.
+ return false;
+ }
+
+ bool activeFormatChanged = false;
+
+ AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
+ AVCaptureDeviceFormat *match = qt_find_best_resolution_match(captureDevice, res,
+ m_service->session()->defaultCodec());
+
+ if (!match) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "unsupported resolution:" << res;
+ return false;
+ }
+
+ activeFormatChanged = qt_set_active_format(captureDevice, match, true);
+
+#ifdef Q_OS_IOS
+ AVCaptureStillImageOutput *imageOutput = m_service->avfImageCaptureControl()->stillImageOutput();
+ if (res == qt_device_format_high_resolution(captureDevice.activeFormat))
+ imageOutput.highResolutionStillImageOutputEnabled = YES;
+ else
+ imageOutput.highResolutionStillImageOutputEnabled = NO;
+#endif
+
+ return activeFormatChanged;
+}
+
+void AVFImageCapture::setCaptureSession(QPlatformMediaCaptureSession *session)
+{
+ AVFCameraService *captureSession = static_cast<AVFCameraService *>(session);
+ if (m_service == captureSession)
+ return;
+
+ m_service = captureSession;
+ if (!m_service) {
+ m_session->disconnect(this);
+ if (m_cameraControl)
+ m_cameraControl->disconnect(this);
+ m_session = nullptr;
+ m_cameraControl = nullptr;
+ m_videoConnection = nil;
+ } else {
+ m_session = m_service->session();
+ Q_ASSERT(m_session);
+
+ connect(m_service, &AVFCameraService::cameraChanged, this, &AVFImageCapture::onCameraChanged);
+ connect(m_session, SIGNAL(readyToConfigureConnections()), SLOT(updateCaptureConnection()));
+ connect(m_session, &AVFCameraSession::newViewfinderFrame,
+ this, &AVFImageCapture::onNewViewfinderFrame);
+ }
+
+ updateCaptureConnection();
+ onCameraChanged();
+ updateReadyStatus();
+}
+
+bool AVFImageCapture::videoCaptureDeviceIsValid() const
+{
+ if (!m_service || !m_service->session() || !m_service->session()->videoCaptureDevice())
+ return false;
+
+ AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
+ if (!captureDevice.formats || !captureDevice.formats.count)
+ return false;
+
+ return true;
+}
+
+#include "moc_avfimagecapture_p.cpp"
diff --git a/src/plugins/multimedia/darwin/camera/avfimagecapture_p.h b/src/plugins/multimedia/darwin/camera/avfimagecapture_p.h
new file mode 100644
index 000000000..0714fa3cc
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfimagecapture_p.h
@@ -0,0 +1,81 @@
+// 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
+
+//
+// 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>
+
+#include <QtCore/qqueue.h>
+#include <QtCore/qsemaphore.h>
+#include <QtCore/qsharedpointer.h>
+#include <private/qplatformimagecapture_p.h>
+#include "avfcamerasession_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class AVFImageCapture : public QPlatformImageCapture
+{
+Q_OBJECT
+public:
+ struct CaptureRequest {
+ int captureId;
+ QSharedPointer<QSemaphore> previewReady;
+ };
+
+ AVFImageCapture(QImageCapture *parent = nullptr);
+ ~AVFImageCapture();
+
+ bool isReadyForCapture() const override;
+
+ AVCaptureStillImageOutput *stillImageOutput() const {return m_stillImageOutput;}
+
+ int doCapture(const QString &fileName);
+ int capture(const QString &fileName) override;
+ int captureToBuffer() override;
+
+ QImageEncoderSettings imageSettings() const override;
+ void setImageSettings(const QImageEncoderSettings &settings) override;
+ bool applySettings();
+
+ void setCaptureSession(QPlatformMediaCaptureSession *session);
+
+private Q_SLOTS:
+ void updateCaptureConnection();
+ void updateReadyStatus();
+ void onNewViewfinderFrame(const QVideoFrame &frame);
+ void onCameraChanged();
+
+private:
+ void makeCapturePreview(CaptureRequest request, const QVideoFrame &frame, int rotation);
+ bool videoCaptureDeviceIsValid() const;
+
+ AVFCameraService *m_service = nullptr;
+ AVFCameraSession *m_session = nullptr;
+ AVFCamera *m_cameraControl = nullptr;
+ bool m_ready = false;
+ int m_lastCaptureId = 0;
+ AVCaptureStillImageOutput *m_stillImageOutput;
+ AVCaptureConnection *m_videoConnection = nullptr;
+
+ QMutex m_requestsMutex;
+ QQueue<CaptureRequest> m_captureRequests;
+ QImageEncoderSettings m_settings;
+};
+
+Q_DECLARE_TYPEINFO(AVFImageCapture::CaptureRequest, Q_PRIMITIVE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/darwin/camera/avfmediaassetwriter.mm b/src/plugins/multimedia/darwin/camera/avfmediaassetwriter.mm
new file mode 100644
index 000000000..37fc69926
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfmediaassetwriter.mm
@@ -0,0 +1,556 @@
+// 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"
+#include "avfmediaassetwriter_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameradebug_p.h"
+#include <qdarwinformatsinfo_p.h>
+#include <avfmetadata_p.h>
+
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qatomic.h>
+
+QT_USE_NAMESPACE
+
+namespace {
+
+bool qt_capture_session_isValid(AVFCameraService *service)
+{
+ if (!service || !service->session())
+ return false;
+
+ AVFCameraSession *session = service->session();
+ if (!session->captureSession())
+ return false;
+
+ if (!session->videoInput() && !session->audioInput())
+ return false;
+
+ return true;
+}
+
+enum WriterState
+{
+ WriterStateIdle,
+ WriterStateActive,
+ WriterStatePaused,
+ WriterStateAborted
+};
+
+using AVFAtomicInt64 = QAtomicInteger<qint64>;
+
+} // unnamed namespace
+
+@interface QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) (PrivateAPI)
+- (bool)addWriterInputs;
+- (void)setQueues;
+- (void)updateDuration:(CMTime)newTimeStamp;
+- (CMSampleBufferRef)adjustTime:(CMSampleBufferRef)sample by:(CMTime)offset;
+@end
+
+@implementation QT_MANGLE_NAMESPACE(AVFMediaAssetWriter)
+{
+@private
+ AVFCameraService *m_service;
+
+ AVFScopedPointer<AVAssetWriterInput> m_cameraWriterInput;
+ AVFScopedPointer<AVAssetWriterInput> m_audioWriterInput;
+
+ // Queue to write sample buffers:
+ AVFScopedPointer<dispatch_queue_t> m_writerQueue;
+ // High priority serial queue for video output:
+ AVFScopedPointer<dispatch_queue_t> m_videoQueue;
+ // Serial queue for audio output:
+ AVFScopedPointer<dispatch_queue_t> m_audioQueue;
+
+ AVFScopedPointer<AVAssetWriter> m_assetWriter;
+
+ AVFMediaEncoder *m_delegate;
+
+ bool m_setStartTime;
+
+ QAtomicInt m_state;
+
+ bool m_writeFirstAudioBuffer;
+
+ CMTime m_startTime;
+ CMTime m_lastTimeStamp;
+ CMTime m_lastVideoTimestamp;
+ CMTime m_lastAudioTimestamp;
+ CMTime m_timeOffset;
+ bool m_adjustTime;
+
+ NSDictionary *m_audioSettings;
+ NSDictionary *m_videoSettings;
+
+ AVFAtomicInt64 m_durationInMs;
+}
+
+- (id)initWithDelegate:(AVFMediaEncoder *)delegate
+{
+ Q_ASSERT(delegate);
+
+ if (self = [super init]) {
+ m_delegate = delegate;
+ m_setStartTime = true;
+ m_state.storeRelaxed(WriterStateIdle);
+ m_startTime = kCMTimeInvalid;
+ m_lastTimeStamp = kCMTimeInvalid;
+ m_lastAudioTimestamp = kCMTimeInvalid;
+ m_lastVideoTimestamp = kCMTimeInvalid;
+ m_timeOffset = kCMTimeInvalid;
+ m_adjustTime = false;
+ m_durationInMs.storeRelaxed(0);
+ m_audioSettings = nil;
+ m_videoSettings = nil;
+ m_writeFirstAudioBuffer = false;
+ }
+
+ return self;
+}
+
+- (bool)setupWithFileURL:(NSURL *)fileURL
+ cameraService:(AVFCameraService *)service
+ audioSettings:(NSDictionary *)audioSettings
+ videoSettings:(NSDictionary *)videoSettings
+ fileFormat:(QMediaFormat::FileFormat)fileFormat
+ transform:(CGAffineTransform)transform
+{
+ Q_ASSERT(fileURL);
+
+ if (!qt_capture_session_isValid(service)) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "invalid capture session";
+ return false;
+ }
+
+ m_service = service;
+ m_audioSettings = audioSettings;
+ m_videoSettings = videoSettings;
+
+ AVFCameraSession *session = m_service->session();
+
+ m_writerQueue.reset(dispatch_queue_create("asset-writer-queue", DISPATCH_QUEUE_SERIAL));
+ if (!m_writerQueue) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to create an asset writer's queue";
+ return false;
+ }
+
+ m_videoQueue.reset();
+ if (session->videoInput() && session->videoOutput() && session->videoOutput()->videoDataOutput()) {
+ m_videoQueue.reset(dispatch_queue_create("video-output-queue", DISPATCH_QUEUE_SERIAL));
+ if (!m_videoQueue) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to create video queue";
+ return false;
+ }
+ dispatch_set_target_queue(m_videoQueue, dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0));
+ }
+
+ m_audioQueue.reset();
+ if (session->audioInput() && session->audioOutput()) {
+ m_audioQueue.reset(dispatch_queue_create("audio-output-queue", DISPATCH_QUEUE_SERIAL));
+ if (!m_audioQueue) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to create audio queue";
+ if (!m_videoQueue)
+ return false;
+ // But we still can write video!
+ }
+ }
+
+ auto fileType = QDarwinFormatInfo::avFileTypeForContainerFormat(fileFormat);
+ m_assetWriter.reset([[AVAssetWriter alloc] initWithURL:fileURL
+ fileType:fileType
+ error:nil]);
+ if (!m_assetWriter) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to create asset writer";
+ return false;
+ }
+
+ if (!m_videoQueue)
+ m_writeFirstAudioBuffer = true;
+
+ if (![self addWriterInputs]) {
+ m_assetWriter.reset();
+ return false;
+ }
+
+ if (m_cameraWriterInput)
+ m_cameraWriterInput.data().transform = transform;
+
+ [self setMetaData:fileType];
+
+ // Ready to start ...
+ return true;
+}
+
+- (void)setMetaData:(AVFileType)fileType
+{
+ m_assetWriter.data().metadata = AVFMetaData::toAVMetadataForFormat(m_delegate->metaData(), fileType);
+}
+
+- (void)start
+{
+ [self setQueues];
+
+ m_setStartTime = true;
+
+ m_state.storeRelease(WriterStateActive);
+
+ [m_assetWriter startWriting];
+ AVCaptureSession *session = m_service->session()->captureSession();
+ if (!session.running)
+ [session startRunning];
+}
+
+- (void)stop
+{
+ if (m_state.loadAcquire() != WriterStateActive && m_state.loadAcquire() != WriterStatePaused)
+ return;
+
+ if ([m_assetWriter status] != AVAssetWriterStatusWriting
+ && [m_assetWriter status] != AVAssetWriterStatusFailed)
+ return;
+
+ // Do this here so that -
+ // 1. '-abort' should not try calling finishWriting again and
+ // 2. async block (see below) will know if recorder control was deleted
+ // before the block's execution:
+ m_state.storeRelease(WriterStateIdle);
+ // Now, since we have to ensure no sample buffers are
+ // appended after a call to finishWriting, we must
+ // ensure writer's queue sees this change in m_state
+ // _before_ we call finishWriting:
+ dispatch_sync(m_writerQueue, ^{});
+ // Done, but now we also want to prevent video queue
+ // from updating our viewfinder:
+ if (m_videoQueue)
+ dispatch_sync(m_videoQueue, ^{});
+
+ // Now we're safe to stop:
+ [m_assetWriter finishWritingWithCompletionHandler:^{
+ // This block is async, so by the time it's executed,
+ // it's possible that render control was deleted already ...
+ if (m_state.loadAcquire() == WriterStateAborted)
+ return;
+
+ AVCaptureSession *session = m_service->session()->captureSession();
+ if (session.running)
+ [session stopRunning];
+ QMetaObject::invokeMethod(m_delegate, "assetWriterFinished", Qt::QueuedConnection);
+ }];
+}
+
+- (void)abort
+{
+ // -abort is to be called from recorder control's dtor.
+
+ if (m_state.fetchAndStoreRelease(WriterStateAborted) != WriterStateActive) {
+ // Not recording, nothing to stop.
+ return;
+ }
+
+ // From Apple's docs:
+ // "To guarantee that all sample buffers are successfully written,
+ // you must ensure that all calls to appendSampleBuffer: and
+ // appendPixelBuffer:withPresentationTime: have returned before
+ // invoking this method."
+ //
+ // The only way we can ensure this is:
+ dispatch_sync(m_writerQueue, ^{});
+ // At this point next block (if any) on the writer's queue
+ // will see m_state preventing it from any further processing.
+ if (m_videoQueue)
+ dispatch_sync(m_videoQueue, ^{});
+ // After this point video queue will not try to modify our
+ // viewfider, so we're safe to delete now.
+
+ [m_assetWriter finishWritingWithCompletionHandler:^{
+ }];
+}
+
+- (void)pause
+{
+ if (m_state.loadAcquire() != WriterStateActive)
+ return;
+ if ([m_assetWriter status] != AVAssetWriterStatusWriting)
+ return;
+
+ m_state.storeRelease(WriterStatePaused);
+ m_adjustTime = true;
+}
+
+- (void)resume
+{
+ if (m_state.loadAcquire() != WriterStatePaused)
+ return;
+ if ([m_assetWriter status] != AVAssetWriterStatusWriting)
+ return;
+
+ m_state.storeRelease(WriterStateActive);
+}
+
+- (void)setStartTimeFrom:(CMSampleBufferRef)sampleBuffer
+{
+ // Writer's queue only.
+ Q_ASSERT(m_setStartTime);
+ Q_ASSERT(sampleBuffer);
+
+ if (m_state.loadAcquire() != WriterStateActive)
+ return;
+
+ QMetaObject::invokeMethod(m_delegate, "assetWriterStarted", Qt::QueuedConnection);
+
+ m_durationInMs.storeRelease(0);
+ m_startTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
+ m_lastTimeStamp = m_startTime;
+ [m_assetWriter startSessionAtSourceTime:m_startTime];
+ m_setStartTime = false;
+}
+
+- (CMSampleBufferRef)adjustTime:(CMSampleBufferRef)sample by:(CMTime)offset
+{
+ CMItemCount count;
+ CMSampleBufferGetSampleTimingInfoArray(sample, 0, nil, &count);
+ CMSampleTimingInfo* timingInfo = (CMSampleTimingInfo*) malloc(sizeof(CMSampleTimingInfo) * count);
+ CMSampleBufferGetSampleTimingInfoArray(sample, count, timingInfo, &count);
+ for (CMItemCount i = 0; i < count; i++)
+ {
+ timingInfo[i].decodeTimeStamp = CMTimeSubtract(timingInfo[i].decodeTimeStamp, offset);
+ timingInfo[i].presentationTimeStamp = CMTimeSubtract(timingInfo[i].presentationTimeStamp, offset);
+ }
+ CMSampleBufferRef updatedBuffer;
+ CMSampleBufferCreateCopyWithNewTiming(kCFAllocatorDefault, sample, count, timingInfo, &updatedBuffer);
+ free(timingInfo);
+ return updatedBuffer;
+}
+
+- (void)writeVideoSampleBuffer:(CMSampleBufferRef)sampleBuffer
+{
+ // This code is executed only on a writer's queue.
+ Q_ASSERT(sampleBuffer);
+
+ if (m_state.loadAcquire() == WriterStateActive) {
+ if (m_setStartTime)
+ [self setStartTimeFrom:sampleBuffer];
+
+ if (m_cameraWriterInput.data().readyForMoreMediaData) {
+ [self updateDuration:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)];
+ [m_cameraWriterInput appendSampleBuffer:sampleBuffer];
+ }
+ }
+}
+
+- (void)writeAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer
+{
+ Q_ASSERT(sampleBuffer);
+
+ // This code is executed only on a writer's queue.
+ if (m_state.loadAcquire() == WriterStateActive) {
+ if (m_setStartTime)
+ [self setStartTimeFrom:sampleBuffer];
+
+ if (m_audioWriterInput.data().readyForMoreMediaData) {
+ [self updateDuration:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)];
+ [m_audioWriterInput appendSampleBuffer:sampleBuffer];
+ }
+ }
+}
+
+- (void)captureOutput:(AVCaptureOutput *)captureOutput
+ didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
+ fromConnection:(AVCaptureConnection *)connection
+{
+ Q_UNUSED(connection);
+ Q_ASSERT(m_service && m_service->session());
+
+ if (m_state.loadAcquire() != WriterStateActive && m_state.loadAcquire() != WriterStatePaused)
+ return;
+
+ if ([m_assetWriter status] != AVAssetWriterStatusWriting) {
+ if ([m_assetWriter status] == AVAssetWriterStatusFailed) {
+ NSError *error = [m_assetWriter error];
+ NSString *failureReason = error.localizedFailureReason;
+ NSString *suggestion = error.localizedRecoverySuggestion;
+ NSString *errorString = suggestion ? [failureReason stringByAppendingString:suggestion] : failureReason;
+ QMetaObject::invokeMethod(m_delegate, "assetWriterError",
+ Qt::QueuedConnection,
+ Q_ARG(QString, QString::fromNSString(errorString)));
+ }
+ return;
+ }
+
+ if (!CMSampleBufferDataIsReady(sampleBuffer)) {
+ qWarning() << Q_FUNC_INFO << "sample buffer is not ready, skipping.";
+ return;
+ }
+
+ CFRetain(sampleBuffer);
+
+ bool isVideoBuffer = true;
+ isVideoBuffer = (captureOutput != m_service->session()->audioOutput());
+ if (isVideoBuffer) {
+ // Find renderercontrol's delegate and invoke its method to
+ // show updated viewfinder's frame.
+ if (m_service->session()->videoOutput()) {
+ NSObject<AVCaptureVideoDataOutputSampleBufferDelegate> *vfDelegate =
+ (NSObject<AVCaptureVideoDataOutputSampleBufferDelegate> *)m_service->session()->videoOutput()->captureDelegate();
+ if (vfDelegate) {
+ AVCaptureOutput *output = nil;
+ AVCaptureConnection *connection = nil;
+ [vfDelegate captureOutput:output didOutputSampleBuffer:sampleBuffer fromConnection:connection];
+ }
+ }
+ } else {
+ if (m_service->session()->audioOutput()) {
+ NSObject<AVCaptureAudioDataOutputSampleBufferDelegate> *audioPreviewDelegate =
+ (NSObject<AVCaptureAudioDataOutputSampleBufferDelegate> *)m_service->session()->audioPreviewDelegate();
+ if (audioPreviewDelegate) {
+ AVCaptureOutput *output = nil;
+ AVCaptureConnection *connection = nil;
+ [audioPreviewDelegate captureOutput:output didOutputSampleBuffer:sampleBuffer fromConnection:connection];
+ }
+ }
+ }
+
+ if (m_state.loadAcquire() != WriterStateActive) {
+ CFRelease(sampleBuffer);
+ return;
+ }
+
+ if (m_adjustTime) {
+ CMTime currentTimestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
+ CMTime lastTimestamp = isVideoBuffer ? m_lastVideoTimestamp : m_lastAudioTimestamp;
+
+ if (!CMTIME_IS_INVALID(lastTimestamp)) {
+ if (!CMTIME_IS_INVALID(m_timeOffset))
+ currentTimestamp = CMTimeSubtract(currentTimestamp, m_timeOffset);
+
+ CMTime pauseDuration = CMTimeSubtract(currentTimestamp, lastTimestamp);
+
+ if (m_timeOffset.value == 0)
+ m_timeOffset = pauseDuration;
+ else
+ m_timeOffset = CMTimeAdd(m_timeOffset, pauseDuration);
+ }
+ m_lastVideoTimestamp = kCMTimeInvalid;
+ m_adjustTime = false;
+ }
+
+ if (m_timeOffset.value > 0) {
+ CFRelease(sampleBuffer);
+ sampleBuffer = [self adjustTime:sampleBuffer by:m_timeOffset];
+ }
+
+ CMTime currentTimestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
+ CMTime currentDuration = CMSampleBufferGetDuration(sampleBuffer);
+ if (currentDuration.value > 0)
+ currentTimestamp = CMTimeAdd(currentTimestamp, currentDuration);
+
+ if (isVideoBuffer)
+ {
+ m_lastVideoTimestamp = currentTimestamp;
+ dispatch_async(m_writerQueue, ^{
+ [self writeVideoSampleBuffer:sampleBuffer];
+ m_writeFirstAudioBuffer = true;
+ CFRelease(sampleBuffer);
+ });
+ } else if (m_writeFirstAudioBuffer) {
+ m_lastAudioTimestamp = currentTimestamp;
+ dispatch_async(m_writerQueue, ^{
+ [self writeAudioSampleBuffer:sampleBuffer];
+ CFRelease(sampleBuffer);
+ });
+ }
+}
+
+- (bool)addWriterInputs
+{
+ Q_ASSERT(m_service && m_service->session());
+ Q_ASSERT(m_assetWriter.data());
+
+ AVFCameraSession *session = m_service->session();
+
+ m_cameraWriterInput.reset();
+ if (m_videoQueue)
+ {
+ Q_ASSERT(session->videoCaptureDevice() && session->videoOutput() && session->videoOutput()->videoDataOutput());
+ m_cameraWriterInput.reset([[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo
+ outputSettings:m_videoSettings
+ sourceFormatHint:session->videoCaptureDevice().activeFormat.formatDescription]);
+
+ if (m_cameraWriterInput && [m_assetWriter canAddInput:m_cameraWriterInput]) {
+ [m_assetWriter addInput:m_cameraWriterInput];
+ } else {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to add camera writer input";
+ m_cameraWriterInput.reset();
+ return false;
+ }
+
+ m_cameraWriterInput.data().expectsMediaDataInRealTime = YES;
+ }
+
+ m_audioWriterInput.reset();
+ if (m_audioQueue) {
+ m_audioWriterInput.reset([[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio
+ outputSettings:m_audioSettings]);
+ if (!m_audioWriterInput) {
+ qWarning() << Q_FUNC_INFO << "failed to create audio writer input";
+ // But we still can record video.
+ if (!m_cameraWriterInput)
+ return false;
+ } else if ([m_assetWriter canAddInput:m_audioWriterInput]) {
+ [m_assetWriter addInput:m_audioWriterInput];
+ m_audioWriterInput.data().expectsMediaDataInRealTime = YES;
+ } else {
+ qWarning() << Q_FUNC_INFO << "failed to add audio writer input";
+ m_audioWriterInput.reset();
+ if (!m_cameraWriterInput)
+ return false;
+ // We can (still) write video though ...
+ }
+ }
+
+ return true;
+}
+
+- (void)setQueues
+{
+ Q_ASSERT(m_service && m_service->session());
+ AVFCameraSession *session = m_service->session();
+
+ if (m_videoQueue) {
+ Q_ASSERT(session->videoOutput() && session->videoOutput()->videoDataOutput());
+ [session->videoOutput()->videoDataOutput() setSampleBufferDelegate:self queue:m_videoQueue];
+ }
+
+ if (m_audioQueue) {
+ Q_ASSERT(session->audioOutput());
+ [session->audioOutput() setSampleBufferDelegate:self queue:m_audioQueue];
+ }
+}
+
+- (void)updateDuration:(CMTime)newTimeStamp
+{
+ Q_ASSERT(CMTimeCompare(m_startTime, kCMTimeInvalid));
+ Q_ASSERT(CMTimeCompare(m_lastTimeStamp, kCMTimeInvalid));
+ if (CMTimeCompare(newTimeStamp, m_lastTimeStamp) > 0) {
+
+ const CMTime duration = CMTimeSubtract(newTimeStamp, m_startTime);
+ if (!CMTimeCompare(duration, kCMTimeInvalid))
+ return;
+
+ m_durationInMs.storeRelease(CMTimeGetSeconds(duration) * 1000);
+ m_lastTimeStamp = newTimeStamp;
+
+ m_delegate->updateDuration([self durationInMs]);
+ }
+}
+
+- (qint64)durationInMs
+{
+ return m_durationInMs.loadAcquire();
+}
+
+@end
diff --git a/src/plugins/multimedia/darwin/camera/avfmediaassetwriter_p.h b/src/plugins/multimedia/darwin/camera/avfmediaassetwriter_p.h
new file mode 100644
index 000000000..8fe3e8522
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfmediaassetwriter_p.h
@@ -0,0 +1,54 @@
+// 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
+
+//
+// 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 "avfcamerautility_p.h"
+#include "qmediaformat.h"
+
+#include <QtCore/qglobal.h>
+
+#include <AVFoundation/AVFoundation.h>
+
+QT_BEGIN_NAMESPACE
+
+class AVFMediaEncoder;
+class AVFCameraService;
+
+QT_END_NAMESPACE
+
+@interface QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) : NSObject<AVCaptureVideoDataOutputSampleBufferDelegate,
+ AVCaptureAudioDataOutputSampleBufferDelegate>
+- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(AVFMediaEncoder) *)delegate;
+
+- (bool)setupWithFileURL:(NSURL *)fileURL
+ cameraService:(QT_PREPEND_NAMESPACE(AVFCameraService) *)service
+ audioSettings:(NSDictionary *)audioSettings
+ videoSettings:(NSDictionary *)videoSettings
+ fileFormat:(QMediaFormat::FileFormat)fileFormat
+ transform:(CGAffineTransform)transform;
+
+// This to be called from the recorder control's thread:
+- (void)start;
+- (void)stop;
+- (void)pause;
+- (void)resume;
+// This to be called from the recorder control's dtor:
+- (void)abort;
+- (qint64)durationInMs;
+
+@end
+
+#endif // AVFMEDIAASSETWRITER_H
diff --git a/src/plugins/multimedia/darwin/camera/avfmediaencoder.mm b/src/plugins/multimedia/darwin/camera/avfmediaencoder.mm
new file mode 100644
index 000000000..3fbc57995
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfmediaencoder.mm
@@ -0,0 +1,664 @@
+// 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"
+#include "avfcamerarenderer_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcamera_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcameradebug_p.h"
+#include "avfcamerautility_p.h"
+#include "qaudiodevice.h"
+
+#include "qmediadevices.h"
+#include "private/qmediastoragelocation_p.h"
+#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 {
+
+bool qt_is_writable_file_URL(NSURL *fileURL)
+{
+ Q_ASSERT(fileURL);
+
+ if (![fileURL isFileURL])
+ return false;
+
+ if (NSString *path = [[fileURL path] stringByExpandingTildeInPath]) {
+ return [[NSFileManager defaultManager]
+ isWritableFileAtPath:[path stringByDeletingLastPathComponent]];
+ }
+
+ return false;
+}
+
+bool qt_file_exists(NSURL *fileURL)
+{
+ Q_ASSERT(fileURL);
+
+ if (NSString *path = [[fileURL path] stringByExpandingTildeInPath])
+ return [[NSFileManager defaultManager] fileExistsAtPath:path];
+
+ return false;
+}
+
+}
+
+AVFMediaEncoder::AVFMediaEncoder(QMediaRecorder *parent)
+ : QObject(parent)
+ , QPlatformMediaRecorder(parent)
+ , m_state(QMediaRecorder::StoppedState)
+ , m_duration(0)
+ , m_audioSettings(nil)
+ , m_videoSettings(nil)
+ //, m_restoreFPS(-1, -1)
+{
+ m_writer.reset([[QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) alloc] initWithDelegate:this]);
+ if (!m_writer) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to create an asset writer";
+ return;
+ }
+}
+
+AVFMediaEncoder::~AVFMediaEncoder()
+{
+ [m_writer abort];
+
+ if (m_audioSettings)
+ [m_audioSettings release];
+ if (m_videoSettings)
+ [m_videoSettings release];
+}
+
+bool AVFMediaEncoder::isLocationWritable(const QUrl &location) const
+{
+ return location.scheme() == QLatin1String("file") || location.scheme().isEmpty();
+}
+
+QMediaRecorder::RecorderState AVFMediaEncoder::state() const
+{
+ return m_state;
+}
+
+qint64 AVFMediaEncoder::duration() const
+{
+ return m_duration;
+}
+
+void AVFMediaEncoder::updateDuration(qint64 duration)
+{
+ m_duration = duration;
+ durationChanged(m_duration);
+}
+
+static NSDictionary *avfAudioSettings(const QMediaEncoderSettings &encoderSettings, const QAudioFormat &format)
+{
+ NSMutableDictionary *settings = [NSMutableDictionary dictionary];
+
+ // Codec
+ int codecId = QDarwinFormatInfo::audioFormatForCodec(encoderSettings.mediaFormat().audioCodec());
+ [settings setObject:[NSNumber numberWithInt:codecId] forKey:AVFormatIDKey];
+
+ // Setting AVEncoderQualityKey is not allowed when format ID is alac or lpcm
+ if (codecId != kAudioFormatAppleLossless && codecId != kAudioFormatLinearPCM
+ && encoderSettings.encodingMode() == QMediaRecorder::ConstantQualityEncoding) {
+ // AudioQuality
+ int quality;
+ switch (encoderSettings.quality()) {
+ case QMediaRecorder::VeryLowQuality:
+ quality = AVAudioQualityMin;
+ break;
+ case QMediaRecorder::LowQuality:
+ quality = AVAudioQualityLow;
+ break;
+ case QMediaRecorder::HighQuality:
+ quality = AVAudioQualityHigh;
+ break;
+ case QMediaRecorder::VeryHighQuality:
+ quality = AVAudioQualityMax;
+ break;
+ case QMediaRecorder::NormalQuality:
+ default:
+ quality = AVAudioQualityMedium;
+ break;
+ }
+ [settings setObject:[NSNumber numberWithInt:quality] forKey:AVEncoderAudioQualityKey];
+ } else {
+ // BitRate
+ bool isBitRateSupported = false;
+ int bitRate = encoderSettings.audioBitRate();
+ if (bitRate > 0) {
+ QList<AudioValueRange> bitRates = qt_supported_bit_rates_for_format(codecId);
+ for (int i = 0; i < bitRates.count(); i++) {
+ if (bitRate >= bitRates[i].mMinimum &&
+ bitRate <= bitRates[i].mMaximum) {
+ isBitRateSupported = true;
+ break;
+ }
+ }
+ if (isBitRateSupported)
+ [settings setObject:[NSNumber numberWithInt:encoderSettings.audioBitRate()]
+ forKey:AVEncoderBitRateKey];
+ }
+ }
+
+ // SampleRate
+ int sampleRate = encoderSettings.audioSampleRate();
+ bool isSampleRateSupported = false;
+ if (sampleRate >= 8000 && sampleRate <= 192000) {
+ QList<AudioValueRange> sampleRates = qt_supported_sample_rates_for_format(codecId);
+ for (int i = 0; i < sampleRates.count(); i++) {
+ if (sampleRate >= sampleRates[i].mMinimum && sampleRate <= sampleRates[i].mMaximum) {
+ isSampleRateSupported = true;
+ break;
+ }
+ }
+ }
+ if (!isSampleRateSupported)
+ sampleRate = 44100;
+ [settings setObject:[NSNumber numberWithInt:sampleRate] forKey:AVSampleRateKey];
+
+ // Channels
+ int channelCount = encoderSettings.audioChannelCount();
+ bool isChannelCountSupported = false;
+ if (channelCount > 0) {
+ std::optional<QList<UInt32>> channelCounts = qt_supported_channel_counts_for_format(codecId);
+ // An std::nullopt result indicates that
+ // any number of channels can be encoded.
+ if (channelCounts == std::nullopt) {
+ isChannelCountSupported = true;
+ } else {
+ for (int i = 0; i < channelCounts.value().count(); i++) {
+ if ((UInt32)channelCount == channelCounts.value()[i]) {
+ isChannelCountSupported = true;
+ break;
+ }
+ }
+ }
+
+ // 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) {
+ // 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 {
+ // finally default to setting channel count to 1
+ [settings setObject:[NSNumber numberWithInt:1] forKey:AVNumberOfChannelsKey];
+ }
+ }
+
+ if (codecId == kAudioFormatAppleLossless)
+ [settings setObject:[NSNumber numberWithInt:24] forKey:AVEncoderBitDepthHintKey];
+
+ if (codecId == kAudioFormatLinearPCM) {
+ [settings setObject:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
+ [settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsBigEndianKey];
+ [settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsFloatKey];
+ [settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsNonInterleaved];
+ }
+
+ return settings;
+}
+
+NSDictionary *avfVideoSettings(QMediaEncoderSettings &encoderSettings, AVCaptureDevice *device, AVCaptureConnection *connection, QSize nativeSize)
+{
+ if (!device)
+ return nil;
+
+
+ // ### re-add needFpsChange
+// AVFPSRange currentFps = qt_current_framerates(device, connection);
+
+ NSMutableDictionary *videoSettings = [NSMutableDictionary dictionary];
+
+ // -- Codec
+
+ // AVVideoCodecKey is the only mandatory key
+ auto codec = encoderSettings.mediaFormat().videoCodec();
+ NSString *c = QDarwinFormatInfo::videoFormatForCodec(codec);
+ [videoSettings setObject:c forKey:AVVideoCodecKey];
+ [c release];
+
+ // -- Resolution
+
+ int w = encoderSettings.videoResolution().width();
+ int h = encoderSettings.videoResolution().height();
+
+ if (AVCaptureDeviceFormat *currentFormat = device.activeFormat) {
+ CMFormatDescriptionRef formatDesc = currentFormat.formatDescription;
+ CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(formatDesc);
+ FourCharCode formatCodec = CMVideoFormatDescriptionGetCodecType(formatDesc);
+
+ // We have to change the device's activeFormat in 3 cases:
+ // - the requested recording resolution is higher than the current device resolution
+ // - the requested recording resolution has a different aspect ratio than the current device aspect ratio
+ // - the requested frame rate is not available for the current device format
+ AVCaptureDeviceFormat *newFormat = nil;
+ if ((w <= 0 || h <= 0)
+ && encoderSettings.videoFrameRate() > 0
+ && !qt_format_supports_framerate(currentFormat, encoderSettings.videoFrameRate())) {
+
+ newFormat = qt_find_best_framerate_match(device,
+ formatCodec,
+ encoderSettings.videoFrameRate());
+
+ } else if (w > 0 && h > 0) {
+ AVCaptureDeviceFormat *f = qt_find_best_resolution_match(device,
+ encoderSettings.videoResolution(),
+ formatCodec);
+
+ if (f) {
+ CMVideoDimensions d = CMVideoFormatDescriptionGetDimensions(f.formatDescription);
+ qreal fAspectRatio = qreal(d.width) / d.height;
+
+ if (w > dim.width || h > dim.height
+ || qAbs((qreal(dim.width) / dim.height) - fAspectRatio) > 0.01) {
+ newFormat = f;
+ }
+ }
+ }
+
+ if (qt_set_active_format(device, newFormat, false /*### !needFpsChange*/)) {
+ formatDesc = newFormat.formatDescription;
+ dim = CMVideoFormatDescriptionGetDimensions(formatDesc);
+ }
+
+ if (w < 0 || h < 0) {
+ w = dim.width;
+ h = dim.height;
+ }
+
+
+ if (w > 0 && h > 0) {
+ // Make sure the recording resolution has the same aspect ratio as the device's
+ // current resolution
+ qreal deviceAspectRatio = qreal(dim.width) / dim.height;
+ qreal recAspectRatio = qreal(w) / h;
+ if (qAbs(deviceAspectRatio - recAspectRatio) > 0.01) {
+ if (recAspectRatio > deviceAspectRatio)
+ w = qRound(h * deviceAspectRatio);
+ else
+ h = qRound(w / deviceAspectRatio);
+ }
+
+ // recording resolution can't be higher than the device's active resolution
+ w = qMin(w, dim.width);
+ h = qMin(h, dim.height);
+ }
+ }
+
+ if (w > 0 && h > 0) {
+ // Width and height must be divisible by 2
+ w += w & 1;
+ h += h & 1;
+
+ bool isPortrait = nativeSize.width() < nativeSize.height();
+ // Make sure the video has the right aspect ratio
+ if (isPortrait && h < w)
+ qSwap(w, h);
+ else if (!isPortrait && w < h)
+ qSwap(w, h);
+
+ encoderSettings.setVideoResolution(QSize(w, h));
+ } else {
+ w = nativeSize.width();
+ h = nativeSize.height();
+ encoderSettings.setVideoResolution(nativeSize);
+ }
+ [videoSettings setObject:[NSNumber numberWithInt:w] forKey:AVVideoWidthKey];
+ [videoSettings setObject:[NSNumber numberWithInt:h] forKey:AVVideoHeightKey];
+
+ // -- FPS
+
+ if (true /*needFpsChange*/) {
+ const qreal fps = encoderSettings.videoFrameRate();
+ qt_set_framerate_limits(device, connection, fps, fps);
+ }
+ encoderSettings.setVideoFrameRate(qt_current_framerates(device, connection).second);
+
+ // -- Codec Settings
+
+ NSMutableDictionary *codecProperties = [NSMutableDictionary dictionary];
+ int bitrate = -1;
+ float quality = -1.f;
+
+ if (encoderSettings.encodingMode() == QMediaRecorder::ConstantQualityEncoding) {
+ if (encoderSettings.quality() != QMediaRecorder::NormalQuality) {
+ if (codec != QMediaFormat::VideoCodec::MotionJPEG) {
+ qWarning("ConstantQualityEncoding is not supported for MotionJPEG");
+ } else {
+ switch (encoderSettings.quality()) {
+ case QMediaRecorder::VeryLowQuality:
+ quality = 0.f;
+ break;
+ case QMediaRecorder::LowQuality:
+ quality = 0.25f;
+ break;
+ case QMediaRecorder::HighQuality:
+ quality = 0.75f;
+ break;
+ case QMediaRecorder::VeryHighQuality:
+ quality = 1.f;
+ break;
+ default:
+ quality = -1.f; // NormalQuality, let the system decide
+ break;
+ }
+ }
+ }
+ } else if (encoderSettings.encodingMode() == QMediaRecorder::AverageBitRateEncoding){
+ if (codec != QMediaFormat::VideoCodec::H264 && codec != QMediaFormat::VideoCodec::H265)
+ qWarning() << "AverageBitRateEncoding is not supported for codec" << QMediaFormat::videoCodecName(codec);
+ else
+ bitrate = encoderSettings.videoBitRate();
+ } else {
+ qWarning("Encoding mode is not supported");
+ }
+
+ if (bitrate != -1)
+ [codecProperties setObject:[NSNumber numberWithInt:bitrate] forKey:AVVideoAverageBitRateKey];
+ if (quality != -1.f)
+ [codecProperties setObject:[NSNumber numberWithFloat:quality] forKey:AVVideoQualityKey];
+
+ [videoSettings setObject:codecProperties forKey:AVVideoCompressionPropertiesKey];
+
+ return videoSettings;
+}
+
+void AVFMediaEncoder::applySettings(QMediaEncoderSettings &settings)
+{
+ unapplySettings();
+
+ AVFCameraSession *session = m_service->session();
+
+ // audio 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];
+
+ // video settings
+ AVCaptureDevice *device = session->videoCaptureDevice();
+ if (!device)
+ return;
+ const AVFConfigurationLock lock(device); // prevents activeFormat from being overridden
+ AVCaptureConnection *conn = [session->videoOutput()->videoDataOutput() connectionWithMediaType:AVMediaTypeVideo];
+ auto nativeSize = session->videoOutput()->nativeSize();
+ m_videoSettings = avfVideoSettings(settings, device, conn, nativeSize);
+ if (m_videoSettings)
+ [m_videoSettings retain];
+}
+
+void AVFMediaEncoder::unapplySettings()
+{
+ if (m_audioSettings) {
+ [m_audioSettings release];
+ m_audioSettings = nil;
+ }
+ if (m_videoSettings) {
+ [m_videoSettings release];
+ m_videoSettings = nil;
+ }
+}
+
+void AVFMediaEncoder::setMetaData(const QMediaMetaData &metaData)
+{
+ m_metaData = metaData;
+}
+
+QMediaMetaData AVFMediaEncoder::metaData() const
+{
+ return m_metaData;
+}
+
+void AVFMediaEncoder::setCaptureSession(QPlatformMediaCaptureSession *session)
+{
+ AVFCameraService *captureSession = static_cast<AVFCameraService *>(session);
+ if (m_service == captureSession)
+ return;
+
+ if (m_service)
+ stop();
+
+ m_service = captureSession;
+ if (!m_service)
+ return;
+
+ connect(m_service, &AVFCameraService::cameraChanged, this, &AVFMediaEncoder::onCameraChanged);
+ onCameraChanged();
+}
+
+void AVFMediaEncoder::record(QMediaEncoderSettings &settings)
+{
+ if (!m_service || !m_service->session()) {
+ qWarning() << Q_FUNC_INFO << "Encoder is not set to a capture session";
+ return;
+ }
+
+ if (!m_writer) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "Invalid recorder";
+ return;
+ }
+
+ if (QMediaRecorder::RecordingState == m_state)
+ return;
+
+ AVFCamera *cameraControl = m_service->avfCameraControl();
+ auto audioInput = m_service->audioInput();
+
+ if (!cameraControl && !audioInput) {
+ qWarning() << Q_FUNC_INFO << "Cannot record without any inputs";
+ updateError(QMediaRecorder::ResourceError, tr("No inputs specified"));
+ return;
+ }
+
+ m_service->session()->setActive(true);
+ const bool audioOnly = settings.videoCodec() == QMediaFormat::VideoCodec::Unspecified;
+ AVCaptureSession *session = m_service->session()->captureSession();
+ float rotation = 0;
+
+ if (!audioOnly) {
+ if (!cameraControl || !cameraControl->isActive()) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "can not start record while camera is not active";
+ updateError(QMediaRecorder::ResourceError,
+ QMediaRecorderPrivate::msgFailedStartRecording());
+ return;
+ }
+ }
+
+ const QString path(outputLocation().scheme() == QLatin1String("file") ?
+ outputLocation().path() : outputLocation().toString());
+ const QUrl fileURL(QUrl::fromLocalFile(QMediaStorageLocation::generateFileName(path,
+ audioOnly ? QStandardPaths::MusicLocation : QStandardPaths::MoviesLocation,
+ settings.mimeType().preferredSuffix())));
+
+ NSURL *nsFileURL = fileURL.toNSURL();
+ if (!nsFileURL) {
+ qWarning() << Q_FUNC_INFO << "invalid output URL:" << fileURL;
+ 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)";
+ updateError(QMediaRecorder::ResourceError, tr("Non-writeable file location"));
+ return;
+ }
+ if (qt_file_exists(nsFileURL)) {
+ // We test for/handle this error here since AWAssetWriter will raise an
+ // Objective-C exception, which is not good at all.
+ qWarning() << Q_FUNC_INFO << "invalid output URL:" << fileURL
+ << "(file already exists)";
+ updateError(QMediaRecorder::ResourceError, tr("File already exists"));
+ return;
+ }
+
+ applySettings(settings);
+
+ QVideoOutputOrientationHandler::setIsRecording(true);
+
+ // We stop session now so that no more frames for renderer's queue
+ // generated, will restart in assetWriterStarted.
+ [session stopRunning];
+
+ if ([m_writer setupWithFileURL:nsFileURL
+ cameraService:m_service
+ audioSettings:m_audioSettings
+ videoSettings:m_videoSettings
+ fileFormat:settings.fileFormat()
+ transform:CGAffineTransformMakeRotation(qDegreesToRadians(rotation))]) {
+
+ m_state = QMediaRecorder::RecordingState;
+
+ Q_EMIT actualLocationChanged(fileURL);
+ Q_EMIT stateChanged(m_state);
+
+ // Apple recommends to call startRunning and do all
+ // setup on a special queue, and that's what we had
+ // initially (dispatch_async to writerQueue). Unfortunately,
+ // writer's queue is not the only queue/thread that can
+ // access/modify the session, and as a result we have
+ // all possible data/race-conditions with Obj-C exceptions
+ // at best and something worse in general.
+ // Now we try to only modify session on the same thread.
+ [m_writer start];
+ } else {
+ [session startRunning];
+ updateError(QMediaRecorder::FormatError, QMediaRecorderPrivate::msgFailedStartRecording());
+ }
+}
+
+void AVFMediaEncoder::pause()
+{
+ if (!m_service || !m_service->session() || state() != QMediaRecorder::RecordingState)
+ return;
+
+ toggleRecord(false);
+ m_state = QMediaRecorder::PausedState;
+ stateChanged(m_state);
+}
+
+void AVFMediaEncoder::resume()
+{
+ if (!m_service || !m_service->session() || state() != QMediaRecorder::PausedState)
+ return;
+
+ toggleRecord(true);
+ m_state = QMediaRecorder::RecordingState;
+ stateChanged(m_state);
+}
+
+void AVFMediaEncoder::stop()
+{
+ if (m_state != QMediaRecorder::StoppedState) {
+ // Do not check the camera status, we can stop if we started.
+ stopWriter();
+ }
+ QVideoOutputOrientationHandler::setIsRecording(false);
+}
+
+
+void AVFMediaEncoder::toggleRecord(bool enable)
+{
+ if (!m_service || !m_service->session())
+ return;
+
+ if (!enable)
+ [m_writer pause];
+ else
+ [m_writer resume];
+}
+
+void AVFMediaEncoder::assetWriterStarted()
+{
+}
+
+void AVFMediaEncoder::assetWriterFinished()
+{
+
+ const QMediaRecorder::RecorderState lastState = m_state;
+
+ unapplySettings();
+
+ if (m_service) {
+ AVFCameraSession *session = m_service->session();
+
+ if (session->videoOutput()) {
+ session->videoOutput()->resetCaptureDelegate();
+ }
+ if (session->audioPreviewDelegate()) {
+ [session->audioPreviewDelegate() resetAudioPreviewDelegate];
+ }
+ if (session->videoOutput() || session->audioPreviewDelegate())
+ [session->captureSession() startRunning];
+ }
+
+ m_state = QMediaRecorder::StoppedState;
+ if (m_state != lastState)
+ Q_EMIT stateChanged(m_state);
+}
+
+void AVFMediaEncoder::assetWriterError(QString err)
+{
+ updateError(QMediaRecorder::FormatError, err);
+ if (m_state != QMediaRecorder::StoppedState)
+ stopWriter();
+}
+
+void AVFMediaEncoder::onCameraChanged()
+{
+ if (m_service && m_service->avfCameraControl()) {
+ AVFCamera *cameraControl = m_service->avfCameraControl();
+ connect(cameraControl, SIGNAL(activeChanged(bool)),
+ SLOT(cameraActiveChanged(bool)));
+ }
+}
+
+void AVFMediaEncoder::cameraActiveChanged(bool active)
+{
+ Q_ASSERT(m_service);
+ AVFCamera *cameraControl = m_service->avfCameraControl();
+ Q_ASSERT(cameraControl);
+
+ if (!active) {
+ return stopWriter();
+ }
+}
+
+void AVFMediaEncoder::stopWriter()
+{
+ [m_writer stop];
+}
+
+#include "moc_avfmediaencoder_p.cpp"
diff --git a/src/plugins/multimedia/darwin/camera/avfmediaencoder_p.h b/src/plugins/multimedia/darwin/camera/avfmediaencoder_p.h
new file mode 100644
index 000000000..23aced325
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/avfmediaencoder_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 AVFMEDIAENCODER_H
+#define AVFMEDIAENCODER_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 "avfmediaassetwriter_p.h"
+#include "avfcamerautility_p.h"
+#include "qaudiodevice.h"
+
+#include <private/qplatformmediarecorder_p.h>
+#include <private/qplatformmediacapture_p.h>
+#include <QtMultimedia/qmediametadata.h>
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qurl.h>
+
+#include <AVFoundation/AVFoundation.h>
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraService;
+class QString;
+class QUrl;
+
+class AVFMediaEncoder : public QObject, public QPlatformMediaRecorder
+{
+ Q_OBJECT
+public:
+ AVFMediaEncoder(QMediaRecorder *parent);
+ ~AVFMediaEncoder() override;
+
+ 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 setMetaData(const QMediaMetaData &) override;
+ QMediaMetaData metaData() const override;
+
+ AVFCameraService *cameraService() const { return m_service; }
+
+ void setCaptureSession(QPlatformMediaCaptureSession *session);
+
+ void updateDuration(qint64 duration);
+
+ void toggleRecord(bool enable);
+
+private:
+ void applySettings(QMediaEncoderSettings &settings);
+ void unapplySettings();
+
+ Q_INVOKABLE void assetWriterStarted();
+ Q_INVOKABLE void assetWriterFinished();
+ Q_INVOKABLE void assetWriterError(QString error);
+
+private Q_SLOTS:
+ void onCameraChanged();
+ void cameraActiveChanged(bool);
+
+private:
+ void stopWriter();
+
+ AVFCameraService *m_service = nullptr;
+ AVFScopedPointer<QT_MANGLE_NAMESPACE(AVFMediaAssetWriter)> m_writer;
+
+ QMediaRecorder::RecorderState m_state;
+
+ QMediaMetaData m_metaData;
+
+ qint64 m_duration;
+
+ NSDictionary *m_audioSettings;
+ NSDictionary *m_videoSettings;
+};
+
+QT_END_NAMESPACE
+
+#endif // AVFMEDIAENCODER_H
diff --git a/src/plugins/multimedia/darwin/camera/qavfcamerabase.mm b/src/plugins/multimedia/darwin/camera/qavfcamerabase.mm
new file mode 100644
index 000000000..9d99de0b9
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/qavfcamerabase.mm
@@ -0,0 +1,1084 @@
+// 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"
+#include "avfcamerautility_p.h"
+#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
+
+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.
+
+
+QAVFVideoDevices::QAVFVideoDevices(QPlatformMediaIntegration *integration)
+ : QPlatformVideoDevices(integration)
+{
+ NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
+ m_deviceConnectedObserver = [notificationCenter addObserverForName:AVCaptureDeviceWasConnectedNotification
+ object:nil
+ queue:[NSOperationQueue mainQueue]
+ usingBlock:^(NSNotification *) {
+ this->updateCameraDevices();
+ }];
+
+ m_deviceDisconnectedObserver = [notificationCenter addObserverForName:AVCaptureDeviceWasDisconnectedNotification
+ object:nil
+ queue:[NSOperationQueue mainQueue]
+ usingBlock:^(NSNotification *) {
+ this->updateCameraDevices();
+ }];
+ updateCameraDevices();
+}
+
+QAVFVideoDevices::~QAVFVideoDevices()
+{
+ NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
+ [notificationCenter removeObserver:(id)m_deviceConnectedObserver];
+ [notificationCenter removeObserver:(id)m_deviceDisconnectedObserver];
+}
+
+QList<QCameraDevice> QAVFVideoDevices::videoDevices() const
+{
+ return m_cameraDevices;
+}
+
+void QAVFVideoDevices::updateCameraDevices()
+{
+#ifdef Q_OS_IOS
+ // Cameras can't change dynamically on iOS. Update only once.
+ if (!m_cameraDevices.isEmpty())
+ return;
+#endif
+
+ QList<QCameraDevice> cameras;
+
+ // 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) {
+ 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;
+
+ for (AVCaptureDeviceFormat *format in device.formats) {
+ if (![format.mediaType isEqualToString:AVMediaTypeVideo])
+ continue;
+
+ auto dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
+ QSize resolution(dimensions.width, dimensions.height);
+ photoResolutions.insert(resolution);
+
+ float maxFrameRate = 0;
+ float minFrameRate = 1.e6;
+
+ 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) {
+ qCDebug(qLcCamera) << "ignore camera CV format" << encoding
+ << "as no matching video format found";
+ continue;
+ }
+
+ for (AVFrameRateRange *frameRateRange in format.videoSupportedFrameRateRanges) {
+ if (frameRateRange.minFrameRate < minFrameRate)
+ minFrameRate = frameRateRange.minFrameRate;
+ if (frameRateRange.maxFrameRate > maxFrameRate)
+ maxFrameRate = frameRateRange.maxFrameRate;
+ }
+
+#ifdef Q_OS_IOS
+ // From Apple's docs (iOS):
+ // By default, AVCaptureStillImageOutput emits images with the same dimensions as
+ // its source AVCaptureDevice instance’s activeFormat.formatDescription. However,
+ // if you set this property to YES, the receiver emits still images at the capture
+ // device’s highResolutionStillImageDimensions value.
+ const QSize hrRes(qt_device_format_high_resolution(format));
+ if (!hrRes.isNull() && hrRes.isValid())
+ photoResolutions.insert(hrRes);
+#endif
+
+ 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
+ qCWarning(qLcCamera())
+ << "Skip camera" << info->description << "without supported formats";
+ continue;
+ }
+ info->videoFormats = videoFormats;
+ info->photoResolutions = photoResolutions.values();
+
+ cameras.append(info.release()->create());
+ }
+
+ if (cameras != m_cameraDevices) {
+ m_cameraDevices = cameras;
+ emit videoInputsChanged();
+ }
+}
+
+
+QAVFCameraBase::QAVFCameraBase(QCamera *camera)
+ : QPlatformCamera(camera)
+{
+ Q_ASSERT(camera);
+}
+
+QAVFCameraBase::~QAVFCameraBase()
+{
+}
+
+bool QAVFCameraBase::isActive() const
+{
+ return m_active;
+}
+
+void QAVFCameraBase::setActive(bool active)
+{
+ if (m_active == active)
+ return;
+ if (m_cameraDevice.isNull() && active)
+ return;
+
+ m_active = active;
+
+ if (active)
+ updateCameraConfiguration();
+ Q_EMIT activeChanged(m_active);
+}
+
+void QAVFCameraBase::setCamera(const QCameraDevice &camera)
+{
+ if (m_cameraDevice == camera)
+ return;
+ m_cameraDevice = camera;
+ setCameraFormat({});
+}
+
+bool QAVFCameraBase::setCameraFormat(const QCameraFormat &format)
+{
+ if (!format.isNull() && !m_cameraDevice.videoFormats().contains(format))
+ return false;
+
+ m_cameraFormat = format.isNull() ? findBestCameraFormat(m_cameraDevice) : format;
+
+ return true;
+}
+
+AVCaptureDevice *QAVFCameraBase::device() const
+{
+ AVCaptureDevice *device = nullptr;
+ QByteArray deviceId = m_cameraDevice.id();
+ if (!deviceId.isEmpty()) {
+ device = [AVCaptureDevice deviceWithUniqueID:
+ [NSString stringWithUTF8String:
+ deviceId.constData()]];
+ }
+ return device;
+}
+
+#ifdef Q_OS_IOS
+namespace
+{
+
+bool qt_focus_mode_supported(QCamera::FocusMode mode)
+{
+ // Check if QCamera::FocusMode has counterpart in AVFoundation.
+
+ // AVFoundation has 'Manual', 'Auto' and 'Continuous',
+ // where 'Manual' is actually 'Locked' + writable property 'lensPosition'.
+ return mode == QCamera::FocusModeAuto
+ || mode == QCamera::FocusModeManual;
+}
+
+AVCaptureFocusMode avf_focus_mode(QCamera::FocusMode requestedMode)
+{
+ switch (requestedMode) {
+ case QCamera::FocusModeHyperfocal:
+ case QCamera::FocusModeInfinity:
+ case QCamera::FocusModeManual:
+ return AVCaptureFocusModeLocked;
+ default:
+ return AVCaptureFocusModeContinuousAutoFocus;
+ }
+
+}
+
+}
+#endif
+
+void QAVFCameraBase::setFocusMode(QCamera::FocusMode mode)
+{
+#ifdef Q_OS_IOS
+ if (focusMode() == mode)
+ return;
+
+ AVCaptureDevice *captureDevice = device();
+ if (!captureDevice) {
+ if (qt_focus_mode_supported(mode)) {
+ focusModeChanged(mode);
+ } else {
+ qCDebug(qLcCamera) << Q_FUNC_INFO
+ << "focus mode not supported";
+ }
+ return;
+ }
+
+ if (isFocusModeSupported(mode)) {
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO
+ << "failed to lock for configuration";
+ return;
+ }
+
+ captureDevice.focusMode = avf_focus_mode(mode);
+ } else {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "focus mode not supported";
+ return;
+ }
+
+ Q_EMIT focusModeChanged(mode);
+#else
+ Q_UNUSED(mode);
+#endif
+}
+
+bool QAVFCameraBase::isFocusModeSupported(QCamera::FocusMode mode) const
+{
+#ifdef Q_OS_IOS
+ AVCaptureDevice *captureDevice = device();
+ if (captureDevice) {
+ AVCaptureFocusMode avMode = avf_focus_mode(mode);
+ switch (mode) {
+ case QCamera::FocusModeAuto:
+ case QCamera::FocusModeHyperfocal:
+ case QCamera::FocusModeInfinity:
+ case QCamera::FocusModeManual:
+ return [captureDevice isFocusModeSupported:avMode];
+ case QCamera::FocusModeAutoNear:
+ Q_FALLTHROUGH();
+ case QCamera::FocusModeAutoFar:
+ return captureDevice.autoFocusRangeRestrictionSupported
+ && [captureDevice isFocusModeSupported:avMode];
+ }
+ }
+#endif
+ return mode == QCamera::FocusModeAuto; // stupid builtin webcam doesn't do any focus handling, but hey it's usually focused :)
+}
+
+void QAVFCameraBase::setCustomFocusPoint(const QPointF &point)
+{
+ if (customFocusPoint() == point)
+ return;
+
+ if (!QRectF(0.f, 0.f, 1.f, 1.f).contains(point)) {
+ // ### release custom focus point, tell the camera to focus where it wants...
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "invalid focus point (out of range)";
+ return;
+ }
+
+ AVCaptureDevice *captureDevice = device();
+ if (!captureDevice)
+ return;
+
+ if ([captureDevice isFocusPointOfInterestSupported]) {
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
+ return;
+ }
+
+ const CGPoint focusPOI = CGPointMake(point.x(), point.y());
+ [captureDevice setFocusPointOfInterest:focusPOI];
+ if (focusMode() != QCamera::FocusModeAuto)
+ [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
+
+ customFocusPointChanged(point);
+ }
+}
+
+void QAVFCameraBase::setFocusDistance(float d)
+{
+#ifdef Q_OS_IOS
+ AVCaptureDevice *captureDevice = device();
+ if (!captureDevice)
+ return;
+
+ if (captureDevice.lockingFocusWithCustomLensPositionSupported) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "Setting custom focus distance not supported\n";
+ return;
+ }
+
+ {
+ AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
+ return;
+ }
+ [captureDevice setFocusModeLockedWithLensPosition:d completionHandler:nil];
+ }
+ focusDistanceChanged(d);
+#else
+ Q_UNUSED(d);
+#endif
+}
+
+void QAVFCameraBase::updateCameraConfiguration()
+{
+ AVCaptureDevice *captureDevice = device();
+ if (!captureDevice) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "capture device is nil in 'active' state";
+ return;
+ }
+
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
+ return;
+ }
+
+ if ([captureDevice isFocusPointOfInterestSupported]) {
+ auto point = customFocusPoint();
+ const CGPoint focusPOI = CGPointMake(point.x(), point.y());
+ [captureDevice setFocusPointOfInterest:focusPOI];
+ }
+
+#ifdef Q_OS_IOS
+ if (focusMode() != QCamera::FocusModeAuto) {
+ const AVCaptureFocusMode avMode = avf_focus_mode(focusMode());
+ if (captureDevice.focusMode != avMode) {
+ if ([captureDevice isFocusModeSupported:avMode]) {
+ [captureDevice setFocusMode:avMode];
+ } else {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "focus mode not supported";
+ }
+ }
+ }
+
+ if (!captureDevice.activeFormat) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "camera state is active, but active format is nil";
+ return;
+ }
+
+ minimumZoomFactorChanged(captureDevice.minAvailableVideoZoomFactor);
+ maximumZoomFactorChanged(captureDevice.activeFormat.videoMaxZoomFactor);
+
+ captureDevice.videoZoomFactor = zoomFactor();
+
+ CMTime newDuration = AVCaptureExposureDurationCurrent;
+ bool setCustomMode = false;
+
+ float exposureTime = manualExposureTime();
+ if (exposureTime > 0
+ && !qt_exposure_duration_equal(captureDevice, exposureTime)) {
+ newDuration = CMTimeMakeWithSeconds(exposureTime, captureDevice.exposureDuration.timescale);
+ if (!qt_check_exposure_duration(captureDevice, newDuration)) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "requested exposure duration is out of range";
+ return;
+ }
+ setCustomMode = true;
+ }
+
+ float newISO = AVCaptureISOCurrent;
+ int iso = manualIsoSensitivity();
+ if (iso > 0 && !qt_iso_equal(captureDevice, iso)) {
+ newISO = iso;
+ if (!qt_check_ISO_value(captureDevice, newISO)) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "requested ISO value is out of range";
+ return;
+ }
+ setCustomMode = true;
+ }
+
+ float bias = exposureCompensation();
+ if (bias != 0 && !qt_exposure_bias_equal(captureDevice, bias)) {
+ // TODO: mixed fpns.
+ if (bias < captureDevice.minExposureTargetBias || bias > captureDevice.maxExposureTargetBias) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "exposure compensation value is"
+ << "out of range";
+ return;
+ }
+ [captureDevice setExposureTargetBias:bias completionHandler:nil];
+ }
+
+ // Setting shutter speed (exposure duration) or ISO values
+ // also reset exposure mode into Custom. With this settings
+ // we ignore any attempts to set exposure mode.
+
+ if (setCustomMode) {
+ [captureDevice setExposureModeCustomWithDuration:newDuration
+ ISO:newISO
+ completionHandler:nil];
+ return;
+ }
+
+ QCamera::ExposureMode qtMode = exposureMode();
+ AVCaptureExposureMode avMode = AVCaptureExposureModeContinuousAutoExposure;
+ if (!qt_convert_exposure_mode(captureDevice, qtMode, avMode)) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "requested exposure mode is not supported";
+ return;
+ }
+
+ captureDevice.exposureMode = avMode;
+#endif
+
+ isFlashSupported = isFlashAutoSupported = false;
+ isTorchSupported = isTorchAutoSupported = false;
+
+ if (captureDevice.hasFlash) {
+ if ([captureDevice isFlashModeSupported:AVCaptureFlashModeOn])
+ isFlashSupported = true;
+ if ([captureDevice isFlashModeSupported:AVCaptureFlashModeAuto])
+ isFlashAutoSupported = true;
+ }
+
+ if (captureDevice.hasTorch) {
+ if ([captureDevice isTorchModeSupported:AVCaptureTorchModeOn])
+ isTorchSupported = true;
+ if ([captureDevice isTorchModeSupported:AVCaptureTorchModeAuto])
+ isTorchAutoSupported = true;
+ }
+
+ applyFlashSettings();
+ flashReadyChanged(isFlashSupported);
+}
+
+void QAVFCameraBase::updateCameraProperties()
+{
+ QCamera::Features features;
+ AVCaptureDevice *captureDevice = device();
+
+#ifdef Q_OS_IOS
+ features = QCamera::Feature::ColorTemperature | QCamera::Feature::ExposureCompensation |
+ QCamera::Feature::IsoSensitivity | QCamera::Feature::ManualExposureTime;
+
+ if (captureDevice && [captureDevice isLockingFocusWithCustomLensPositionSupported])
+ features |= QCamera::Feature::FocusDistance;
+#endif
+
+ if (captureDevice && [captureDevice isFocusPointOfInterestSupported])
+ features |= QCamera::Feature::CustomFocusPoint;
+
+ supportedFeaturesChanged(features);
+}
+
+void QAVFCameraBase::zoomTo(float factor, float rate)
+{
+ Q_UNUSED(factor);
+ Q_UNUSED(rate);
+
+#ifdef Q_OS_IOS
+ if (zoomFactor() == factor)
+ return;
+
+ AVCaptureDevice *captureDevice = device();
+ if (!captureDevice || !captureDevice.activeFormat)
+ return;
+
+ factor = qBound(captureDevice.minAvailableVideoZoomFactor, factor,
+ captureDevice.activeFormat.videoMaxZoomFactor);
+
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
+ return;
+ }
+
+ if (rate <= 0)
+ captureDevice.videoZoomFactor = factor;
+ else
+ [captureDevice rampToVideoZoomFactor:factor withRate:rate];
+#endif
+}
+
+void QAVFCameraBase::setFlashMode(QCamera::FlashMode mode)
+{
+ if (flashMode() == mode)
+ return;
+
+ if (isActive() && !isFlashModeSupported(mode)) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "unsupported mode" << mode;
+ return;
+ }
+
+ flashModeChanged(mode);
+
+ if (!isActive())
+ return;
+
+ applyFlashSettings();
+}
+
+bool QAVFCameraBase::isFlashModeSupported(QCamera::FlashMode mode) const
+{
+ if (mode == QCamera::FlashOff)
+ return true;
+ else if (mode == QCamera::FlashOn)
+ return isFlashSupported;
+ else //if (mode == QCamera::FlashAuto)
+ return isFlashAutoSupported;
+}
+
+bool QAVFCameraBase::isFlashReady() const
+{
+ if (!isActive())
+ return false;
+
+ AVCaptureDevice *captureDevice = device();
+ if (!captureDevice)
+ return false;
+
+ if (!captureDevice.hasFlash)
+ return false;
+
+ if (!isFlashModeSupported(flashMode()))
+ return false;
+
+ // AVCaptureDevice's docs:
+ // "The flash may become unavailable if, for example,
+ // the device overheats and needs to cool off."
+ return [captureDevice isFlashAvailable];
+}
+
+void QAVFCameraBase::setTorchMode(QCamera::TorchMode mode)
+{
+ if (torchMode() == mode)
+ return;
+
+ if (isActive() && !isTorchModeSupported(mode)) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "unsupported torch mode" << mode;
+ return;
+ }
+
+ torchModeChanged(mode);
+
+ if (!isActive())
+ return;
+
+ applyFlashSettings();
+}
+
+bool QAVFCameraBase::isTorchModeSupported(QCamera::TorchMode mode) const
+{
+ if (mode == QCamera::TorchOff)
+ return true;
+ else if (mode == QCamera::TorchOn)
+ return isTorchSupported;
+ else //if (mode == QCamera::TorchAuto)
+ return isTorchAutoSupported;
+}
+
+void QAVFCameraBase::setExposureMode(QCamera::ExposureMode qtMode)
+{
+#ifdef Q_OS_IOS
+ if (qtMode != QCamera::ExposureAuto && qtMode != QCamera::ExposureManual) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "exposure mode not supported";
+ return;
+ }
+
+ AVCaptureDevice *captureDevice = device();
+ if (!captureDevice) {
+ exposureModeChanged(qtMode);
+ return;
+ }
+
+ AVCaptureExposureMode avMode = AVCaptureExposureModeContinuousAutoExposure;
+ if (!qt_convert_exposure_mode(captureDevice, qtMode, avMode)) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "exposure mode not supported";
+ return;
+ }
+
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock a capture device"
+ << "for configuration";
+ return;
+ }
+
+ [captureDevice setExposureMode:avMode];
+ exposureModeChanged(qtMode);
+#else
+ Q_UNUSED(qtMode);
+#endif
+}
+
+bool QAVFCameraBase::isExposureModeSupported(QCamera::ExposureMode mode) const
+{
+ if (mode == QCamera::ExposureAuto)
+ return true;
+ if (mode != QCamera::ExposureManual)
+ return false;
+
+ if (@available(macOS 10.15, *)) {
+ AVCaptureDevice *captureDevice = device();
+ return captureDevice && [captureDevice isExposureModeSupported:AVCaptureExposureModeCustom];
+ }
+
+ return false;
+}
+
+void QAVFCameraBase::applyFlashSettings()
+{
+ Q_ASSERT(isActive());
+
+ AVCaptureDevice *captureDevice = device();
+ if (!captureDevice) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "no capture device found";
+ return;
+ }
+
+ const AVFConfigurationLock lock(captureDevice);
+
+ if (captureDevice.hasFlash) {
+ 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) {
+ setAvFlashModeSafe(AVCaptureFlashModeOff);
+ } else {
+ 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";
+ }
+ }
+ }
+
+ if (captureDevice.hasTorch) {
+ 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) {
+ setAvTorchModeSafe(AVCaptureTorchModeOff);
+ } else {
+ 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";
+ }
+ }
+ }
+}
+
+
+void QAVFCameraBase::setExposureCompensation(float bias)
+{
+#ifdef Q_OS_IOS
+ AVCaptureDevice *captureDevice = device();
+ if (!captureDevice) {
+ exposureCompensationChanged(bias);
+ return;
+ }
+
+ bias = qBound(captureDevice.minExposureTargetBias, bias, captureDevice.maxExposureTargetBias);
+
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
+ return;
+ }
+
+ [captureDevice setExposureTargetBias:bias completionHandler:nil];
+ exposureCompensationChanged(bias);
+#else
+ Q_UNUSED(bias);
+#endif
+}
+
+void QAVFCameraBase::setManualExposureTime(float value)
+{
+#ifdef Q_OS_IOS
+ if (value < 0) {
+ setExposureMode(QCamera::ExposureAuto);
+ return;
+ }
+
+ AVCaptureDevice *captureDevice = device();
+ if (!captureDevice) {
+ exposureTimeChanged(value);
+ return;
+ }
+
+ const CMTime newDuration = CMTimeMakeWithSeconds(value, captureDevice.exposureDuration.timescale);
+ if (!qt_check_exposure_duration(captureDevice, newDuration)) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "shutter speed value is out of range";
+ return;
+ }
+
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock for configuration";
+ return;
+ }
+
+ // Setting the shutter speed (exposure duration in Apple's terms,
+ // since there is no shutter actually) will also reset
+ // exposure mode into custom mode.
+ [captureDevice setExposureModeCustomWithDuration:newDuration
+ ISO:AVCaptureISOCurrent
+ completionHandler:nil];
+
+ exposureTimeChanged(value);
+
+#else
+ Q_UNUSED(value);
+#endif
+}
+
+float QAVFCameraBase::exposureTime() const
+{
+#ifdef Q_OS_IOS
+ AVCaptureDevice *captureDevice = device();
+ if (!captureDevice)
+ return -1.;
+ auto duration = captureDevice.exposureDuration;
+ return CMTimeGetSeconds(duration);
+#else
+ return -1;
+#endif
+}
+
+#ifdef Q_OS_IOS
+namespace {
+
+void avf_convert_white_balance_mode(QCamera::WhiteBalanceMode qtMode,
+ AVCaptureWhiteBalanceMode &avMode)
+{
+ if (qtMode == QCamera::WhiteBalanceAuto)
+ avMode = AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance;
+ else
+ avMode = AVCaptureWhiteBalanceModeLocked;
+}
+
+bool avf_set_white_balance_mode(AVCaptureDevice *captureDevice,
+ AVCaptureWhiteBalanceMode avMode)
+{
+ Q_ASSERT(captureDevice);
+
+ const bool lock = [captureDevice lockForConfiguration:nil];
+ if (!lock) {
+ qDebug() << "Failed to lock a capture device for configuration\n";
+ return false;
+ }
+
+ captureDevice.whiteBalanceMode = avMode;
+ [captureDevice unlockForConfiguration];
+ return true;
+}
+
+bool avf_convert_temp_and_tint_to_wb_gains(AVCaptureDevice *captureDevice,
+ float temp, float tint, AVCaptureWhiteBalanceGains &wbGains)
+{
+ Q_ASSERT(captureDevice);
+
+ AVCaptureWhiteBalanceTemperatureAndTintValues wbTTValues = {
+ .temperature = temp,
+ .tint = tint
+ };
+ wbGains = [captureDevice deviceWhiteBalanceGainsForTemperatureAndTintValues:wbTTValues];
+
+ if (wbGains.redGain >= 1.0 && wbGains.redGain <= captureDevice.maxWhiteBalanceGain
+ && wbGains.greenGain >= 1.0 && wbGains.greenGain <= captureDevice.maxWhiteBalanceGain
+ && wbGains.blueGain >= 1.0 && wbGains.blueGain <= captureDevice.maxWhiteBalanceGain)
+ return true;
+
+ return false;
+}
+
+bool avf_set_white_balance_gains(AVCaptureDevice *captureDevice,
+ AVCaptureWhiteBalanceGains wbGains)
+{
+ const bool lock = [captureDevice lockForConfiguration:nil];
+ if (!lock) {
+ qDebug() << "Failed to lock a capture device for configuration\n";
+ return false;
+ }
+
+ [captureDevice setWhiteBalanceModeLockedWithDeviceWhiteBalanceGains:wbGains
+ completionHandler:nil];
+ [captureDevice unlockForConfiguration];
+ return true;
+}
+
+}
+
+bool QAVFCameraBase::isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const
+{
+ if (mode == QCamera::WhiteBalanceAuto)
+ return true;
+ AVCaptureDevice *captureDevice = device();
+ if (!captureDevice)
+ return false;
+ return [captureDevice isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked];
+}
+
+void QAVFCameraBase::setWhiteBalanceMode(QCamera::WhiteBalanceMode mode)
+{
+ if (!isWhiteBalanceModeSupported(mode))
+ return;
+
+ AVCaptureDevice *captureDevice = device();
+ Q_ASSERT(captureDevice);
+
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock a capture device"
+ << "for configuration";
+ return;
+ }
+
+ AVCaptureWhiteBalanceMode avMode;
+ avf_convert_white_balance_mode(mode, avMode);
+ avf_set_white_balance_mode(captureDevice, avMode);
+
+ if (mode == QCamera::WhiteBalanceAuto || mode == QCamera::WhiteBalanceManual) {
+ whiteBalanceModeChanged(mode);
+ return;
+ }
+
+ const int colorTemp = colorTemperatureForWhiteBalance(mode);
+ AVCaptureWhiteBalanceGains wbGains;
+ if (avf_convert_temp_and_tint_to_wb_gains(captureDevice, colorTemp, 0., wbGains)
+ && avf_set_white_balance_gains(captureDevice, wbGains))
+ whiteBalanceModeChanged(mode);
+}
+
+void QAVFCameraBase::setColorTemperature(int colorTemp)
+{
+ if (colorTemp == 0) {
+ colorTemperatureChanged(colorTemp);
+ return;
+ }
+
+ AVCaptureDevice *captureDevice = device();
+ if (!captureDevice || ![captureDevice isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked])
+ return;
+
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock a capture device"
+ << "for configuration";
+ return;
+ }
+
+ AVCaptureWhiteBalanceGains wbGains;
+ if (avf_convert_temp_and_tint_to_wb_gains(captureDevice, colorTemp, 0., wbGains)
+ && avf_set_white_balance_gains(captureDevice, wbGains))
+ colorTemperatureChanged(colorTemp);
+}
+#endif
+
+void QAVFCameraBase::setManualIsoSensitivity(int value)
+{
+#ifdef Q_OS_IOS
+ if (value < 0) {
+ setExposureMode(QCamera::ExposureAuto);
+ return;
+ }
+
+ AVCaptureDevice *captureDevice = device();
+ if (!captureDevice) {
+ isoSensitivityChanged(value);
+ return;
+ }
+
+ if (!qt_check_ISO_value(captureDevice, value)) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "ISO value is out of range";
+ return;
+ }
+
+ const AVFConfigurationLock lock(captureDevice);
+ if (!lock) {
+ qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to lock a capture device"
+ << "for configuration";
+ return;
+ }
+
+ // Setting the ISO will also reset
+ // exposure mode to the custom mode.
+ [captureDevice setExposureModeCustomWithDuration:AVCaptureExposureDurationCurrent
+ ISO:value
+ completionHandler:nil];
+
+ isoSensitivityChanged(value);
+#else
+ Q_UNUSED(value);
+#endif
+}
+
+int QAVFCameraBase::isoSensitivity() const
+{
+ return manualIsoSensitivity();
+}
+
+
+#include "moc_qavfcamerabase_p.cpp"
diff --git a/src/plugins/multimedia/darwin/camera/qavfcamerabase_p.h b/src/plugins/multimedia/darwin/camera/qavfcamerabase_p.h
new file mode 100644
index 000000000..1ad3ba250
--- /dev/null
+++ b/src/plugins/multimedia/darwin/camera/qavfcamerabase_p.h
@@ -0,0 +1,110 @@
+// 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
+
+//
+// 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 <private/qplatformcamera_p.h>
+#include <private/qplatformvideodevices_p.h>
+
+Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureDeviceFormat);
+Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureConnection);
+Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureDevice);
+
+QT_BEGIN_NAMESPACE
+class QPlatformMediaIntegration;
+
+class QAVFVideoDevices : public QPlatformVideoDevices
+{
+public:
+ QAVFVideoDevices(QPlatformMediaIntegration *integration);
+ ~QAVFVideoDevices();
+
+ QList<QCameraDevice> videoDevices() const override;
+
+private:
+ void updateCameraDevices();
+
+ NSObject *m_deviceConnectedObserver;
+ NSObject *m_deviceDisconnectedObserver;
+
+ QList<QCameraDevice> m_cameraDevices;
+};
+
+
+class QAVFCameraBase : public QPlatformCamera
+{;
+Q_OBJECT
+public:
+ QAVFCameraBase(QCamera *camera);
+ ~QAVFCameraBase();
+
+ bool isActive() const override;
+ void setActive(bool activce) override;
+
+ 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 factor, float rate) override;
+
+ void setFlashMode(QCamera::FlashMode mode) override;
+ bool isFlashModeSupported(QCamera::FlashMode mode) const override;
+ bool isFlashReady() const override;
+
+ void setTorchMode(QCamera::TorchMode mode) override;
+ bool isTorchModeSupported(QCamera::TorchMode mode) const override;
+
+ void setExposureMode(QCamera::ExposureMode) override;
+ bool isExposureModeSupported(QCamera::ExposureMode mode) const override;
+
+ void setExposureCompensation(float bias) override;
+ void setManualIsoSensitivity(int value) override;
+ virtual int isoSensitivity() const override;
+ void setManualExposureTime(float value) override;
+ virtual float exposureTime() const override;
+
+#ifdef Q_OS_IOS
+ // not supported on macOS
+ bool isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const override;
+ void setWhiteBalanceMode(QCamera::WhiteBalanceMode /*mode*/) override;
+ void setColorTemperature(int /*temperature*/) override;
+#endif
+
+ AVCaptureDevice *device() const;
+
+protected:
+ void updateCameraConfiguration();
+ void updateCameraProperties();
+ void applyFlashSettings();
+
+ QCameraDevice m_cameraDevice;
+ bool m_active = false;
+private:
+ bool isFlashSupported = false;
+ bool isFlashAutoSupported = false;
+ bool isTorchSupported = false;
+ bool isTorchAutoSupported = false;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/darwin/common/avfmetadata.mm b/src/plugins/multimedia/darwin/common/avfmetadata.mm
new file mode 100644
index 000000000..da07f69c6
--- /dev/null
+++ b/src/plugins/multimedia/darwin/common/avfmetadata.mm
@@ -0,0 +1,382 @@
+// 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>
+#include <avfmediaplayer_p.h>
+
+#include <QtCore/qbuffer.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qlocale.h>
+#include <QtCore/qurl.h>
+#include <QImage>
+#include <QtMultimedia/qvideoframe.h>
+
+#if __has_include(<AppKit/AppKit.h>)
+#include <AppKit/AppKit.h>
+#endif
+
+#include <CoreFoundation/CoreFoundation.h>
+
+QT_USE_NAMESPACE
+
+struct AVMetadataIDs {
+ AVMetadataIdentifier common;
+ AVMetadataIdentifier iTunes;
+ AVMetadataIdentifier quickTime;
+ AVMetadataIdentifier ID3;
+ AVMetadataIdentifier quickTimeUserData;
+ AVMetadataIdentifier isoUserData;
+};
+
+const AVMetadataIDs keyToAVMetaDataID[] = {
+ // Title
+ { AVMetadataCommonIdentifierTitle, AVMetadataIdentifieriTunesMetadataSongName,
+ AVMetadataIdentifierQuickTimeMetadataTitle,
+ AVMetadataIdentifierID3MetadataTitleDescription,
+ nil, AVMetadata3GPUserDataKeyTitle },
+ // Author
+ { AVMetadataCommonIdentifierAuthor,AVMetadataIdentifieriTunesMetadataAuthor,
+ AVMetadataIdentifierQuickTimeMetadataAuthor, nil,
+ AVMetadataQuickTimeUserDataKeyAuthor, AVMetadata3GPUserDataKeyAuthor },
+ // Comment
+ { nil, AVMetadataIdentifieriTunesMetadataUserComment,
+ AVMetadataIdentifierQuickTimeMetadataComment, AVMetadataIdentifierID3MetadataComments,
+ AVMetadataQuickTimeUserDataKeyComment, nil },
+ // Description
+ { AVMetadataCommonIdentifierDescription,AVMetadataIdentifieriTunesMetadataDescription,
+ AVMetadataIdentifierQuickTimeMetadataDescription, nil,
+ AVMetadataQuickTimeUserDataKeyDescription, AVMetadata3GPUserDataKeyDescription },
+ // Genre
+ { nil, AVMetadataIdentifieriTunesMetadataUserGenre,
+ AVMetadataIdentifierQuickTimeMetadataGenre, nil,
+ AVMetadataQuickTimeUserDataKeyGenre, AVMetadata3GPUserDataKeyGenre },
+ // Date
+ { AVMetadataCommonIdentifierCreationDate, AVMetadataIdentifieriTunesMetadataReleaseDate,
+ AVMetadataIdentifierQuickTimeMetadataCreationDate, AVMetadataIdentifierID3MetadataDate,
+ AVMetadataQuickTimeUserDataKeyCreationDate, AVMetadataISOUserDataKeyDate },
+ // Language
+ { AVMetadataCommonIdentifierLanguage, nil, nil, AVMetadataIdentifierID3MetadataLanguage, nil, nil },
+ // Publisher
+ { AVMetadataCommonIdentifierPublisher, AVMetadataIdentifieriTunesMetadataPublisher,
+ AVMetadataIdentifierQuickTimeMetadataPublisher, AVMetadataIdentifierID3MetadataPublisher, nil, nil },
+ // Copyright
+ { AVMetadataCommonIdentifierCopyrights, AVMetadataIdentifieriTunesMetadataCopyright,
+ AVMetadataIdentifierQuickTimeMetadataCopyright, AVMetadataIdentifierID3MetadataCopyright,
+ AVMetadataQuickTimeUserDataKeyCopyright, AVMetadataISOUserDataKeyCopyright },
+ // Url
+ { nil, nil, nil, AVMetadataIdentifierID3MetadataOfficialAudioSourceWebpage, nil, nil },
+ // Duration
+ { nil, nil, nil, AVMetadataIdentifierID3MetadataLength, nil, nil },
+ // MediaType
+ { AVMetadataCommonIdentifierType, nil, nil, AVMetadataIdentifierID3MetadataContentType, nil, nil },
+ // FileFormat
+ { nil, nil, nil, AVMetadataIdentifierID3MetadataFileType, nil, nil },
+ // AudioBitRate
+ { nil, nil, nil, nil, nil, nil },
+ // AudioCodec
+ { nil, nil, nil, nil, nil, nil },
+ // VideoBitRate
+ { nil, nil, nil, nil, nil, nil },
+ // VideoCodec
+ { nil, nil, nil, nil, nil, nil },
+ // VideoFrameRate
+ { nil, nil, AVMetadataIdentifierQuickTimeMetadataCameraFrameReadoutTime, nil, nil, nil },
+ // AlbumTitle
+ { AVMetadataCommonIdentifierAlbumName, AVMetadataIdentifieriTunesMetadataAlbum,
+ AVMetadataIdentifierQuickTimeMetadataAlbum, AVMetadataIdentifierID3MetadataAlbumTitle,
+ AVMetadataQuickTimeUserDataKeyAlbum, AVMetadata3GPUserDataKeyAlbumAndTrack },
+ // AlbumArtist
+ { nil, AVMetadataIdentifieriTunesMetadataAlbumArtist, nil, nil,
+ AVMetadataQuickTimeUserDataKeyArtist, AVMetadata3GPUserDataKeyPerformer },
+ // ContributingArtist
+ { AVMetadataCommonIdentifierArtist, AVMetadataIdentifieriTunesMetadataArtist,
+ AVMetadataIdentifierQuickTimeMetadataArtist, nil, nil, nil },
+ // TrackNumber
+ { nil, AVMetadataIdentifieriTunesMetadataTrackNumber,
+ nil, AVMetadataIdentifierID3MetadataTrackNumber, nil, nil },
+ // Composer
+ { nil, AVMetadataIdentifieriTunesMetadataComposer,
+ AVMetadataIdentifierQuickTimeMetadataComposer, AVMetadataIdentifierID3MetadataComposer, nil, nil },
+ // LeadPerformer
+ { nil, AVMetadataIdentifieriTunesMetadataPerformer,
+ AVMetadataIdentifierQuickTimeMetadataPerformer, AVMetadataIdentifierID3MetadataLeadPerformer, nil, nil },
+ // ThumbnailImage
+ { nil, nil, nil, AVMetadataIdentifierID3MetadataAttachedPicture, nil, nil },
+ // CoverArtImage
+ { AVMetadataCommonIdentifierArtwork, AVMetadataIdentifieriTunesMetadataCoverArt,
+ AVMetadataIdentifierQuickTimeMetadataArtwork, nil, nil, nil },
+ // Orientation
+ { nil, nil, AVMetadataIdentifierQuickTimeMetadataVideoOrientation, nil, nil, nil },
+ // Resolution
+ { nil, nil, nil, nil, nil, nil }
+};
+
+static AVMetadataIdentifier toIdentifier(QMediaMetaData::Key key, AVMetadataKeySpace keySpace)
+{
+ static_assert(sizeof(keyToAVMetaDataID)/sizeof(AVMetadataIDs) == QMediaMetaData::Key::Resolution + 1);
+
+ AVMetadataIdentifier identifier = nil;
+ if ([keySpace isEqualToString:AVMetadataKeySpaceiTunes]) {
+ identifier = keyToAVMetaDataID[key].iTunes;
+ } else if ([keySpace isEqualToString:AVMetadataKeySpaceID3]) {
+ identifier = keyToAVMetaDataID[key].ID3;
+ } else if ([keySpace isEqualToString:AVMetadataKeySpaceQuickTimeMetadata]) {
+ identifier = keyToAVMetaDataID[key].quickTime;
+ } else {
+ identifier = keyToAVMetaDataID[key].common;
+ }
+ return identifier;
+}
+
+static std::optional<QMediaMetaData::Key> toKey(AVMetadataItem *item)
+{
+ static_assert(sizeof(keyToAVMetaDataID)/sizeof(AVMetadataIDs) == QMediaMetaData::Key::Resolution + 1);
+
+ // The item identifier may be different than the ones we support,
+ // so check by common key first, as it will get the metadata
+ // irrespective of the format.
+ AVMetadataKey commonKey = item.commonKey;
+ if (commonKey.length != 0) {
+ if ([commonKey isEqualToString:AVMetadataCommonKeyTitle]) {
+ return QMediaMetaData::Title;
+ } else if ([commonKey isEqualToString:AVMetadataCommonKeyDescription]) {
+ return QMediaMetaData::Description;
+ } else if ([commonKey isEqualToString:AVMetadataCommonKeyPublisher]) {
+ return QMediaMetaData::Publisher;
+ } else if ([commonKey isEqualToString:AVMetadataCommonKeyCreationDate]) {
+ return QMediaMetaData::Date;
+ } else if ([commonKey isEqualToString:AVMetadataCommonKeyType]) {
+ return QMediaMetaData::MediaType;
+ } else if ([commonKey isEqualToString:AVMetadataCommonKeyLanguage]) {
+ return QMediaMetaData::Language;
+ } else if ([commonKey isEqualToString:AVMetadataCommonKeyCopyrights]) {
+ return QMediaMetaData::Copyright;
+ } else if ([commonKey isEqualToString:AVMetadataCommonKeyAlbumName]) {
+ return QMediaMetaData::AlbumTitle;
+ } else if ([commonKey isEqualToString:AVMetadataCommonKeyAuthor]) {
+ return QMediaMetaData::Author;
+ } else if ([commonKey isEqualToString:AVMetadataCommonKeyArtist]) {
+ return QMediaMetaData::ContributingArtist;
+ }
+ }
+
+ // Check by identifier if no common key found
+ // No need to check for the common keySpace since there's no common key
+ enum keySpaces { iTunes, QuickTime, QuickTimeUserData, IsoUserData, ID3, Other } itemKeySpace;
+ itemKeySpace = Other;
+ AVMetadataKeySpace keySpace = [item keySpace];
+ AVMetadataIdentifier identifier = [item identifier];
+
+ if ([keySpace isEqualToString:AVMetadataKeySpaceiTunes]) {
+ itemKeySpace = iTunes;
+ } else if ([keySpace isEqualToString:AVMetadataKeySpaceQuickTimeMetadata]) {
+ itemKeySpace = QuickTime;
+ } else if ([keySpace isEqualToString:AVMetadataKeySpaceQuickTimeUserData]) {
+ itemKeySpace = QuickTimeUserData;
+ } else if ([keySpace isEqualToString:AVMetadataKeySpaceISOUserData]) {
+ itemKeySpace = IsoUserData;
+ } else if (([keySpace isEqualToString:AVMetadataKeySpaceID3])) {
+ itemKeySpace = ID3;
+ }
+
+ for (int key = 0; key < QMediaMetaData::Resolution + 1; key++) {
+ AVMetadataIdentifier idForKey = nil;
+ switch (itemKeySpace) {
+ case iTunes:
+ idForKey = keyToAVMetaDataID[key].iTunes;
+ break;
+ case QuickTime:
+ idForKey = keyToAVMetaDataID[key].quickTime;
+ break;
+ case ID3:
+ idForKey = keyToAVMetaDataID[key].ID3;
+ break;
+ case QuickTimeUserData:
+ idForKey = keyToAVMetaDataID[key].quickTimeUserData;
+ break;
+ case IsoUserData:
+ idForKey = keyToAVMetaDataID[key].isoUserData;
+ break;
+ default:
+ break;
+ }
+
+ if ([identifier isEqualToString:idForKey])
+ return QMediaMetaData::Key(key);
+ }
+
+ return std::nullopt;
+}
+
+static QMediaMetaData fromAVMetadata(NSArray *metadataItems)
+{
+ QMediaMetaData metadata;
+
+ for (AVMetadataItem* item in metadataItems) {
+ auto key = toKey(item);
+ if (!key)
+ continue;
+
+ const QString value = QString::fromNSString([item stringValue]);
+ if (!value.isNull())
+ metadata.insert(*key, value);
+ }
+ return metadata;
+}
+
+QMediaMetaData AVFMetaData::fromAsset(AVAsset *asset)
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO;
+#endif
+ QMediaMetaData metadata = fromAVMetadata([asset metadata]);
+
+ // add duration
+ const CMTime time = [asset duration];
+ const qint64 duration = static_cast<qint64>(float(time.value) / float(time.timescale) * 1000.0f);
+ metadata.insert(QMediaMetaData::Duration, duration);
+
+ return metadata;
+}
+
+QMediaMetaData AVFMetaData::fromAssetTrack(AVAssetTrack *asset)
+{
+ QMediaMetaData metadata = fromAVMetadata([asset metadata]);
+ if ([asset.mediaType isEqualToString:AVMediaTypeAudio]) {
+ if (metadata.value(QMediaMetaData::Language).isNull()) {
+ auto *languageCode = asset.languageCode;
+ if (languageCode) {
+ // languageCode is encoded as ISO 639-2, which QLocale does not handle.
+ // Convert it to 639-1 first.
+ auto id = CFLocaleCreateCanonicalLanguageIdentifierFromString(kCFAllocatorDefault,
+ (__bridge CFStringRef)languageCode);
+ QString lang = QString::fromCFString(id);
+ CFRelease(id);
+ metadata.insert(QMediaMetaData::Language, QLocale::codeToLanguage(lang));
+ }
+ }
+ }
+ if ([asset.mediaType isEqualToString:AVMediaTypeVideo]) {
+ // add orientation
+ if (metadata.value(QMediaMetaData::Orientation).isNull()) {
+ QtVideo::Rotation angle = QtVideo::Rotation::None;
+ bool mirrored;
+ AVFMediaPlayer::videoOrientationForAssetTrack(asset, angle, mirrored);
+ Q_UNUSED(mirrored);
+ metadata.insert(QMediaMetaData::Orientation, int(angle));
+ }
+ }
+ return metadata;
+}
+
+static AVMutableMetadataItem *setAVMetadataItemForKey(QMediaMetaData::Key key, const QVariant &value,
+ AVMetadataKeySpace keySpace = AVMetadataKeySpaceCommon)
+{
+ AVMetadataIdentifier identifier = toIdentifier(key, keySpace);
+ if (!identifier.length)
+ return nil;
+
+ AVMutableMetadataItem *item = [AVMutableMetadataItem metadataItem];
+ item.keySpace = keySpace;
+ item.identifier = identifier;
+
+ switch (key) {
+ case QMediaMetaData::ThumbnailImage:
+ case QMediaMetaData::CoverArtImage: {
+#if defined(Q_OS_MACOS)
+ QImage img = value.value<QImage>();
+ if (!img.isNull()) {
+ QByteArray arr;
+ QBuffer buffer(&arr);
+ buffer.open(QIODevice::WriteOnly);
+ img.save(&buffer);
+ NSData *data = arr.toNSData();
+ NSImage *nsImg = [[NSImage alloc] initWithData:data];
+ item.value = nsImg;
+ [nsImg release];
+ }
+#endif
+ break;
+ }
+ case QMediaMetaData::FileFormat: {
+ QMediaFormat::FileFormat qtFormat = value.value<QMediaFormat::FileFormat>();
+ AVFileType avFormat = QDarwinFormatInfo::avFileTypeForContainerFormat(qtFormat);
+ item.value = avFormat;
+ break;
+ }
+ case QMediaMetaData::Language: {
+ QString lang = QLocale::languageToCode(value.value<QLocale::Language>());
+ if (!lang.isEmpty())
+ item.value = lang.toNSString();
+ break;
+ }
+ case QMediaMetaData::Orientation: {
+ bool ok;
+ int rotation = value.toInt(&ok);
+ if (ok)
+ item.value = [NSNumber numberWithInt:rotation];
+ }
+ default: {
+ switch (value.typeId()) {
+ case QMetaType::QString: {
+ item.value = value.toString().toNSString();
+ break;
+ }
+ case QMetaType::Int: {
+ item.value = [NSNumber numberWithInt:value.toInt()];
+ break;
+ }
+ case QMetaType::LongLong: {
+ item.value = [NSNumber numberWithLongLong:value.toLongLong()];
+ break;
+ }
+ case QMetaType::Double: {
+ item.value = [NSNumber numberWithDouble:value.toDouble()];
+ break;
+ }
+ case QMetaType::QDate:
+ case QMetaType::QDateTime: {
+ item.value = value.toDateTime().toNSDate();
+ break;
+ }
+ case QMetaType::QUrl: {
+ item.value = value.toUrl().toNSURL();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ return item;
+}
+
+NSMutableArray<AVMetadataItem *> *AVFMetaData::toAVMetadataForFormat(QMediaMetaData metadata, AVFileType format)
+{
+ NSMutableArray<AVMetadataKeySpace> *keySpaces = [NSMutableArray<AVMetadataKeySpace> array];
+ if (format == AVFileTypeAppleM4A) {
+ [keySpaces addObject:AVMetadataKeySpaceiTunes];
+ } else if (format == AVFileTypeMPEGLayer3) {
+ [keySpaces addObject:AVMetadataKeySpaceID3];
+ [keySpaces addObject:AVMetadataKeySpaceiTunes];
+ } else if (format == AVFileTypeQuickTimeMovie) {
+ [keySpaces addObject:AVMetadataKeySpaceQuickTimeMetadata];
+ } else {
+ [keySpaces addObject:AVMetadataKeySpaceCommon];
+ }
+ NSMutableArray<AVMetadataItem *> *avMetaDataArr = [NSMutableArray array];
+ for (const auto &key : metadata.keys()) {
+ for (NSUInteger i = 0; i < [keySpaces count]; i++) {
+ const QVariant &value = metadata.value(key);
+ // set format-specific metadata
+ AVMetadataItem *item = setAVMetadataItemForKey(key, value, keySpaces[i]);
+ if (item)
+ [avMetaDataArr addObject:item];
+ }
+ }
+ return avMetaDataArr;
+}
+
diff --git a/src/plugins/multimedia/darwin/common/avfmetadata_p.h b/src/plugins/multimedia/darwin/common/avfmetadata_p.h
new file mode 100644
index 000000000..d1cb2e7e8
--- /dev/null
+++ b/src/plugins/multimedia/darwin/common/avfmetadata_p.h
@@ -0,0 +1,37 @@
+// 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
+
+//
+// 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/QMediaMetaData>
+#include <QtCore/qvariant.h>
+
+#import <AVFoundation/AVFoundation.h>
+
+QT_BEGIN_NAMESPACE
+
+class AVFMediaPlayer;
+
+class AVFMetaData
+{
+public:
+ static QMediaMetaData fromAsset(AVAsset *asset);
+ static QMediaMetaData fromAssetTrack(AVAssetTrack *asset);
+ static NSMutableArray<AVMetadataItem *> *toAVMetadataForFormat(QMediaMetaData metaData, AVFileType format);
+};
+
+QT_END_NAMESPACE
+
+#endif // AVFMEDIAPLAYERMETADATACONTROL_H
diff --git a/src/plugins/multimedia/darwin/darwin.json b/src/plugins/multimedia/darwin/darwin.json
new file mode 100644
index 000000000..f72350b17
--- /dev/null
+++ b/src/plugins/multimedia/darwin/darwin.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "darwin" ]
+}
diff --git a/src/plugins/multimedia/darwin/mediaplayer/avfdisplaylink.mm b/src/plugins/multimedia/darwin/mediaplayer/avfdisplaylink.mm
new file mode 100644
index 000000000..8c6561f37
--- /dev/null
+++ b/src/plugins/multimedia/darwin/mediaplayer/avfdisplaylink.mm
@@ -0,0 +1,207 @@
+// 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>
+
+#ifdef QT_DEBUG_AVF
+#include <QtCore/qdebug.h>
+#endif
+
+#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
+#import <QuartzCore/CADisplayLink.h>
+#import <Foundation/NSRunLoop.h>
+#define _m_displayLink static_cast<DisplayLinkObserver*>(m_displayLink)
+#else
+#endif
+
+QT_USE_NAMESPACE
+
+#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
+@interface DisplayLinkObserver : NSObject
+
+- (void)start;
+- (void)stop;
+- (void)displayLinkNotification:(CADisplayLink *)sender;
+
+@end
+
+@implementation DisplayLinkObserver
+{
+ AVFDisplayLink *m_avfDisplayLink;
+ CADisplayLink *m_displayLink;
+}
+
+- (id)initWithAVFDisplayLink:(AVFDisplayLink *)link
+{
+ self = [super init];
+
+ if (self) {
+ m_avfDisplayLink = link;
+ m_displayLink = [[CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkNotification:)] retain];
+ }
+
+ return self;
+}
+
+- (void) dealloc
+{
+ if (m_displayLink) {
+ [m_displayLink release];
+ m_displayLink = nullptr;
+ }
+
+ [super dealloc];
+}
+
+- (void)start
+{
+ [m_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+}
+
+- (void)stop
+{
+ [m_displayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+}
+
+- (void)displayLinkNotification:(CADisplayLink *)sender
+{
+ Q_UNUSED(sender);
+ m_avfDisplayLink->displayLinkEvent(nullptr);
+}
+
+@end
+#else
+static CVReturn CVDisplayLinkCallback(CVDisplayLinkRef displayLink,
+ const CVTimeStamp *inNow,
+ const CVTimeStamp *inOutputTime,
+ CVOptionFlags flagsIn,
+ CVOptionFlags *flagsOut,
+ void *displayLinkContext)
+{
+ Q_UNUSED(displayLink);
+ Q_UNUSED(inNow);
+ Q_UNUSED(flagsIn);
+ Q_UNUSED(flagsOut);
+
+ AVFDisplayLink *link = (AVFDisplayLink *)displayLinkContext;
+
+ link->displayLinkEvent(inOutputTime);
+ return kCVReturnSuccess;
+}
+#endif
+
+AVFDisplayLink::AVFDisplayLink(QObject *parent)
+ : QObject(parent)
+ , m_displayLink(nullptr)
+ , m_pendingDisplayLinkEvent(false)
+ , m_isActive(false)
+{
+#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
+ m_displayLink = [[DisplayLinkObserver alloc] initWithAVFDisplayLink:this];
+#else
+ // create display link for the main display
+ CVDisplayLinkCreateWithCGDisplay(kCGDirectMainDisplay, &m_displayLink);
+ if (m_displayLink) {
+ // set the current display of a display link.
+ CVDisplayLinkSetCurrentCGDisplay(m_displayLink, kCGDirectMainDisplay);
+
+ // set the renderer output callback function
+ CVDisplayLinkSetOutputCallback(m_displayLink, &CVDisplayLinkCallback, this);
+ }
+#endif
+}
+
+AVFDisplayLink::~AVFDisplayLink()
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO;
+#endif
+
+ if (m_displayLink) {
+ stop();
+#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
+ [_m_displayLink release];
+#else
+ CVDisplayLinkRelease(m_displayLink);
+#endif
+ m_displayLink = nullptr;
+ }
+}
+
+bool AVFDisplayLink::isValid() const
+{
+ return m_displayLink != nullptr;
+}
+
+bool AVFDisplayLink::isActive() const
+{
+ return m_isActive;
+}
+
+void AVFDisplayLink::start()
+{
+ if (m_displayLink && !m_isActive) {
+#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
+ [_m_displayLink start];
+#else
+ CVDisplayLinkStart(m_displayLink);
+#endif
+ m_isActive = true;
+ }
+}
+
+void AVFDisplayLink::stop()
+{
+ if (m_displayLink && m_isActive) {
+#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
+ [_m_displayLink stop];
+#else
+ CVDisplayLinkStop(m_displayLink);
+#endif
+ m_isActive = false;
+ }
+}
+
+void AVFDisplayLink::displayLinkEvent(const CVTimeStamp *ts)
+{
+ // This function is called from a
+ // thread != gui thread. So we post the event.
+ // But we need to make sure that we don't post faster
+ // than the event loop can eat:
+ m_displayLinkMutex.lock();
+ bool pending = m_pendingDisplayLinkEvent;
+ m_pendingDisplayLinkEvent = true;
+#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
+ Q_UNUSED(ts);
+ memset(&m_frameTimeStamp, 0, sizeof(CVTimeStamp));
+#else
+ m_frameTimeStamp = *ts;
+#endif
+ m_displayLinkMutex.unlock();
+
+ if (!pending)
+ qApp->postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority);
+}
+
+bool AVFDisplayLink::event(QEvent *event)
+{
+ switch (event->type()){
+ case QEvent::User: {
+ m_displayLinkMutex.lock();
+ m_pendingDisplayLinkEvent = false;
+ CVTimeStamp ts = m_frameTimeStamp;
+ m_displayLinkMutex.unlock();
+
+ Q_EMIT tick(ts);
+
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+ 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
new file mode 100644
index 000000000..c4eb504a5
--- /dev/null
+++ b/src/plugins/multimedia/darwin/mediaplayer/avfdisplaylink_p.h
@@ -0,0 +1,65 @@
+// 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
+
+//
+// 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/qmutex.h>
+
+#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
+#include <CoreVideo/CVBase.h>
+#else
+#include <QuartzCore/CVDisplayLink.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class AVFDisplayLink : public QObject
+{
+ Q_OBJECT
+public:
+ explicit AVFDisplayLink(QObject *parent = nullptr);
+ virtual ~AVFDisplayLink();
+ bool isValid() const;
+ bool isActive() const;
+
+public Q_SLOTS:
+ void start();
+ void stop();
+
+Q_SIGNALS:
+ void tick(const CVTimeStamp &ts);
+
+public:
+ void displayLinkEvent(const CVTimeStamp *);
+
+protected:
+ virtual bool event(QEvent *) override;
+
+private:
+#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
+ void *m_displayLink;
+#else
+ CVDisplayLinkRef m_displayLink;
+#endif
+ QMutex m_displayLinkMutex;
+ bool m_pendingDisplayLinkEvent;
+ bool m_isActive;
+ CVTimeStamp m_frameTimeStamp;
+};
+
+QT_END_NAMESPACE
+
+#endif // AVFDISPLAYLINK_H
diff --git a/src/plugins/multimedia/darwin/mediaplayer/avfmediaplayer.mm b/src/plugins/multimedia/darwin/mediaplayer/avfmediaplayer.mm
new file mode 100644
index 000000000..964964a8e
--- /dev/null
+++ b/src/plugins/multimedia/darwin/mediaplayer/avfmediaplayer.mm
@@ -0,0 +1,1270 @@
+// 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"
+#include <avfvideosink_p.h>
+#include <avfmetadata_p.h>
+
+#include "qaudiooutput.h"
+#include "private/qplatformaudiooutput_p.h"
+
+#include <qpointer.h>
+#include <QFileInfo>
+#include <QtCore/qmath.h>
+#include <QtCore/qmutex.h>
+
+#import <AVFoundation/AVFoundation.h>
+
+QT_USE_NAMESPACE
+
+//AVAsset Keys
+static NSString* const AVF_TRACKS_KEY = @"tracks";
+static NSString* const AVF_PLAYABLE_KEY = @"playable";
+
+//AVPlayerItem keys
+static NSString* const AVF_STATUS_KEY = @"status";
+static NSString* const AVF_BUFFER_LIKELY_KEEP_UP_KEY = @"playbackLikelyToKeepUp";
+
+//AVPlayer keys
+static NSString* const AVF_RATE_KEY = @"rate";
+static NSString* const AVF_CURRENT_ITEM_KEY = @"currentItem";
+static NSString* const AVF_CURRENT_ITEM_DURATION_KEY = @"currentItem.duration";
+
+static void *AVFMediaPlayerObserverRateObservationContext = &AVFMediaPlayerObserverRateObservationContext;
+static void *AVFMediaPlayerObserverStatusObservationContext = &AVFMediaPlayerObserverStatusObservationContext;
+static void *AVFMediaPlayerObserverPresentationSizeContext = &AVFMediaPlayerObserverPresentationSizeContext;
+static void *AVFMediaPlayerObserverBufferLikelyToKeepUpContext = &AVFMediaPlayerObserverBufferLikelyToKeepUpContext;
+static void *AVFMediaPlayerObserverTracksContext = &AVFMediaPlayerObserverTracksContext;
+static void *AVFMediaPlayerObserverCurrentItemObservationContext = &AVFMediaPlayerObserverCurrentItemObservationContext;
+static void *AVFMediaPlayerObserverCurrentItemDurationObservationContext = &AVFMediaPlayerObserverCurrentItemDurationObservationContext;
+
+@interface AVFMediaPlayerObserver : NSObject<AVAssetResourceLoaderDelegate>
+
+@property (readonly, getter=player) AVPlayer* m_player;
+@property (readonly, getter=playerItem) AVPlayerItem* m_playerItem;
+@property (readonly, getter=playerLayer) AVPlayerLayer* m_playerLayer;
+@property (readonly, getter=session) AVFMediaPlayer* m_session;
+@property (retain) AVPlayerItemTrack *videoTrack;
+
+- (AVFMediaPlayerObserver *) initWithMediaPlayerSession:(AVFMediaPlayer *)session;
+- (void) setURL:(NSURL *)url mimeType:(NSString *)mimeType;
+- (void) unloadMedia;
+- (void) prepareToPlayAsset:(AVURLAsset *)asset withKeys:(NSArray *)requestedKeys;
+- (void) assetFailedToPrepareForPlayback:(NSError *)error;
+- (void) playerItemDidReachEnd:(NSNotification *)notification;
+- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
+ change:(NSDictionary *)change context:(void *)context;
+- (void) detatchSession;
+- (void) dealloc;
+- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest;
+@end
+
+#ifdef Q_OS_IOS
+// Alas, no such thing as 'class variable', hence globals:
+static unsigned sessionActivationCount;
+static QMutex sessionMutex;
+#endif // Q_OS_IOS
+
+@implementation AVFMediaPlayerObserver
+{
+@private
+ AVFMediaPlayer *m_session;
+ AVPlayer *m_player;
+ AVPlayerItem *m_playerItem;
+ AVPlayerLayer *m_playerLayer;
+ NSURL *m_URL;
+ BOOL m_bufferIsLikelyToKeepUp;
+ NSData *m_data;
+ NSString *m_mimeType;
+#ifdef Q_OS_IOS
+ BOOL m_activated;
+#endif
+}
+
+@synthesize m_player, m_playerItem, m_playerLayer, m_session;
+
+#ifdef Q_OS_IOS
+- (void)setSessionActive:(BOOL)active
+{
+ const QMutexLocker lock(&sessionMutex);
+ if (active) {
+ // Don't count the same player twice if already activated,
+ // unless it tried to deactivate first:
+ if (m_activated)
+ return;
+ if (!sessionActivationCount)
+ [AVAudioSession.sharedInstance setActive:YES error:nil];
+ ++sessionActivationCount;
+ m_activated = YES;
+ } else {
+ if (!sessionActivationCount || !m_activated) {
+ qWarning("Unbalanced audio session deactivation, ignoring.");
+ return;
+ }
+ --sessionActivationCount;
+ m_activated = NO;
+ if (!sessionActivationCount)
+ [AVAudioSession.sharedInstance setActive:NO error:nil];
+ }
+}
+#endif // Q_OS_IOS
+
+- (AVFMediaPlayerObserver *) initWithMediaPlayerSession:(AVFMediaPlayer *)session
+{
+ if (!(self = [super init]))
+ return nil;
+
+ m_session = session;
+ m_bufferIsLikelyToKeepUp = FALSE;
+
+ m_playerLayer = [AVPlayerLayer playerLayerWithPlayer:nil];
+ [m_playerLayer retain];
+ m_playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
+ m_playerLayer.anchorPoint = CGPointMake(0.0f, 0.0f);
+ return self;
+}
+
+- (void) setURL:(NSURL *)url mimeType:(NSString *)mimeType
+{
+ if (!m_session)
+ return;
+
+ [m_mimeType release];
+ m_mimeType = [mimeType retain];
+
+ if (m_URL != url)
+ {
+ [m_URL release];
+ m_URL = [url copy];
+
+ //Create an asset for inspection of a resource referenced by a given URL.
+ //Load the values for the asset keys "tracks", "playable".
+
+ // 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 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 defined(Q_OS_IOS)
+ if (isAccessing)
+ [m_URL stopAccessingSecurityScopedResource];
+#endif
+ [blockSelf prepareToPlayAsset:asset withKeys:requestedKeys];
+ [asset release];
+ [requestedKeys release];
+ [blockSelf release];
+ });
+ }];
+ }
+}
+
+- (void) unloadMedia
+{
+ if (m_playerItem) {
+ [m_playerItem removeObserver:self forKeyPath:@"presentationSize"];
+ [m_playerItem removeObserver:self forKeyPath:AVF_STATUS_KEY];
+ [m_playerItem removeObserver:self forKeyPath:AVF_BUFFER_LIKELY_KEEP_UP_KEY];
+ [m_playerItem removeObserver:self forKeyPath:AVF_TRACKS_KEY];
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:AVPlayerItemDidPlayToEndTimeNotification
+ object:m_playerItem];
+ m_playerItem = 0;
+ }
+ if (m_player) {
+ [m_player setRate:0.0];
+ [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_DURATION_KEY];
+ [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_KEY];
+ [m_player removeObserver:self forKeyPath:AVF_RATE_KEY];
+ [m_player release];
+ m_player = 0;
+ }
+ if (m_playerLayer)
+ m_playerLayer.player = nil;
+#if defined(Q_OS_IOS)
+ [self setSessionActive:NO];
+#endif
+}
+
+- (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)
+ {
+ NSError *error = nil;
+ AVKeyValueStatus keyStatus = [asset statusOfValueForKey:thisKey error:&error];
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO << [thisKey UTF8String] << " status: " << keyStatus;
+#endif
+ if (keyStatus == AVKeyValueStatusFailed)
+ {
+ [self assetFailedToPrepareForPlayback:error];
+ return;
+ }
+ }
+
+ //Use the AVAsset playable property to detect whether the asset can be played.
+#ifdef QT_DEBUG_AVF
+ 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 couldn't create player item.", @"Item cannot be played failure reason");
+ NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys:
+ localizedDescription, NSLocalizedDescriptionKey,
+ localizedFailureReason, NSLocalizedFailureReasonErrorKey,
+ nil];
+ NSError *assetCannotBePlayedError = [NSError errorWithDomain:@"StitchedStreamPlayer" code:0 userInfo:errorDict];
+
+ [self assetFailedToPrepareForPlayback:assetCannotBePlayedError];
+ return;
+ }
+
+ //Observe the player item "status" key to determine when it is ready to play.
+ [m_playerItem addObserver:self
+ forKeyPath:AVF_STATUS_KEY
+ options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
+ context:AVFMediaPlayerObserverStatusObservationContext];
+
+ [m_playerItem addObserver:self
+ forKeyPath:@"presentationSize"
+ options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
+ context:AVFMediaPlayerObserverPresentationSizeContext];
+
+ [m_playerItem addObserver:self
+ forKeyPath:AVF_BUFFER_LIKELY_KEEP_UP_KEY
+ options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
+ context:AVFMediaPlayerObserverBufferLikelyToKeepUpContext];
+
+ [m_playerItem addObserver:self
+ forKeyPath:AVF_TRACKS_KEY
+ options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
+ context:AVFMediaPlayerObserverTracksContext];
+
+ //When the player item has played to its end time we'll toggle
+ //the movie controller Pause button to be the Play button
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(playerItemDidReachEnd:)
+ name:AVPlayerItemDidPlayToEndTimeNotification
+ object:m_playerItem];
+
+ //Get a new AVPlayer initialized to play the specified player item.
+ m_player = [AVPlayer playerWithPlayerItem:m_playerItem];
+ [m_player retain];
+
+ //Set the initial volume on new player object
+ if (self.session) {
+ auto *audioOutput = m_session->m_audioOutput;
+ m_player.volume = (audioOutput ? audioOutput->volume : 1.);
+ m_player.muted = (audioOutput ? audioOutput->muted : true);
+ }
+
+ //Assign the output layer to the new player
+ m_playerLayer.player = m_player;
+
+ //Observe the AVPlayer "currentItem" property to find out when any
+ //AVPlayer replaceCurrentItemWithPlayerItem: replacement will/did
+ //occur.
+ [m_player addObserver:self
+ forKeyPath:AVF_CURRENT_ITEM_KEY
+ options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
+ context:AVFMediaPlayerObserverCurrentItemObservationContext];
+
+ //Observe the AVPlayer "rate" property to update the scrubber control.
+ [m_player addObserver:self
+ forKeyPath:AVF_RATE_KEY
+ options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
+ context:AVFMediaPlayerObserverRateObservationContext];
+
+ //Observe the duration for getting the buffer state
+ [m_player addObserver:self
+ forKeyPath:AVF_CURRENT_ITEM_DURATION_KEY
+ options:0
+ context:AVFMediaPlayerObserverCurrentItemDurationObservationContext];
+#if defined(Q_OS_IOS)
+ [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
+ [self setSessionActive:YES];
+#endif
+}
+
+-(void) assetFailedToPrepareForPlayback:(NSError *)error
+{
+ Q_UNUSED(error);
+ QMetaObject::invokeMethod(m_session, "processMediaLoadError", Qt::AutoConnection);
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO;
+ qDebug() << [[error localizedDescription] UTF8String];
+ qDebug() << [[error localizedFailureReason] UTF8String];
+ qDebug() << [[error localizedRecoverySuggestion] UTF8String];
+#endif
+}
+
+- (void) playerItemDidReachEnd:(NSNotification *)notification
+{
+ Q_UNUSED(notification);
+ if (self.session)
+ QMetaObject::invokeMethod(m_session, "processEOS", Qt::AutoConnection);
+}
+
+- (void) observeValueForKeyPath:(NSString*) path
+ ofObject:(id)object
+ change:(NSDictionary*)change
+ context:(void*)context
+{
+ //AVPlayerItem "status" property value observer.
+ if (context == AVFMediaPlayerObserverStatusObservationContext)
+ {
+ AVPlayerStatus status = (AVPlayerStatus)[[change objectForKey:NSKeyValueChangeNewKey] integerValue];
+ switch (status)
+ {
+ //Indicates that the status of the player is not yet known because
+ //it has not tried to load new media resources for playback
+ case AVPlayerStatusUnknown:
+ {
+ //QMetaObject::invokeMethod(m_session, "processLoadStateChange", Qt::AutoConnection);
+ }
+ break;
+
+ case AVPlayerStatusReadyToPlay:
+ {
+ //Once the AVPlayerItem becomes ready to play, i.e.
+ //[playerItem status] == AVPlayerItemStatusReadyToPlay,
+ //its duration can be fetched from the item.
+ if (self.session)
+ QMetaObject::invokeMethod(m_session, "processLoadStateChange", Qt::AutoConnection);
+ }
+ break;
+
+ case AVPlayerStatusFailed:
+ {
+ AVPlayerItem *playerItem = static_cast<AVPlayerItem*>(object);
+ [self assetFailedToPrepareForPlayback:playerItem.error];
+
+ if (self.session)
+ QMetaObject::invokeMethod(m_session, "processLoadStateFailure", Qt::AutoConnection);
+ }
+ break;
+ }
+ } else if (context == AVFMediaPlayerObserverPresentationSizeContext) {
+ QSize size(m_playerItem.presentationSize.width, m_playerItem.presentationSize.height);
+ QMetaObject::invokeMethod(m_session, "nativeSizeChanged", Qt::AutoConnection, Q_ARG(QSize, size));
+ } else if (context == AVFMediaPlayerObserverBufferLikelyToKeepUpContext)
+ {
+ const bool isPlaybackLikelyToKeepUp = [m_playerItem isPlaybackLikelyToKeepUp];
+ if (isPlaybackLikelyToKeepUp != m_bufferIsLikelyToKeepUp) {
+ m_bufferIsLikelyToKeepUp = isPlaybackLikelyToKeepUp;
+ QMetaObject::invokeMethod(m_session, "processBufferStateChange", Qt::AutoConnection,
+ Q_ARG(int, isPlaybackLikelyToKeepUp ? 100 : 0));
+ }
+ }
+ else if (context == AVFMediaPlayerObserverTracksContext)
+ {
+ QMetaObject::invokeMethod(m_session, "updateTracks", Qt::AutoConnection);
+ }
+ //AVPlayer "rate" property value observer.
+ else if (context == AVFMediaPlayerObserverRateObservationContext)
+ {
+ //QMetaObject::invokeMethod(m_session, "setPlaybackRate", Qt::AutoConnection, Q_ARG(qreal, [m_player rate]));
+ }
+ //AVPlayer "currentItem" property observer.
+ //Called when the AVPlayer replaceCurrentItemWithPlayerItem:
+ //replacement will/did occur.
+ else if (context == AVFMediaPlayerObserverCurrentItemObservationContext)
+ {
+ AVPlayerItem *newPlayerItem = [change objectForKey:NSKeyValueChangeNewKey];
+ if (m_playerItem != newPlayerItem)
+ m_playerItem = newPlayerItem;
+ }
+ else if (context == AVFMediaPlayerObserverCurrentItemDurationObservationContext)
+ {
+ const CMTime time = [m_playerItem duration];
+ const qint64 duration = static_cast<qint64>(float(time.value) / float(time.timescale) * 1000.0f);
+ if (self.session)
+ QMetaObject::invokeMethod(m_session, "processDurationChange", Qt::AutoConnection, Q_ARG(qint64, duration));
+ }
+ else
+ {
+ [super observeValueForKeyPath:path ofObject:object change:change context:context];
+ }
+}
+
+- (void) detatchSession
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO;
+#endif
+ m_session = 0;
+}
+
+- (void) dealloc
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO;
+#endif
+ [self unloadMedia];
+
+ if (m_URL) {
+ [m_URL release];
+ }
+
+ [m_mimeType release];
+ [m_playerLayer release];
+ [super dealloc];
+}
+
+- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
+{
+ Q_UNUSED(resourceLoader);
+
+ if (![loadingRequest.request.URL.scheme isEqualToString:@"iodevice"])
+ return NO;
+
+ QIODevice *device = m_session->mediaStream();
+ if (!device)
+ return NO;
+
+ device->seek(loadingRequest.dataRequest.requestedOffset);
+ if (loadingRequest.contentInformationRequest) {
+ loadingRequest.contentInformationRequest.contentType = m_mimeType;
+ loadingRequest.contentInformationRequest.contentLength = device->size();
+ loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
+ }
+
+ if (loadingRequest.dataRequest) {
+ NSInteger requestedLength = loadingRequest.dataRequest.requestedLength;
+ int maxBytes = qMin(32 * 1064, int(requestedLength));
+ char buffer[maxBytes];
+ NSInteger submitted = 0;
+ while (submitted < requestedLength) {
+ qint64 len = device->read(buffer, maxBytes);
+ if (len < 1)
+ break;
+
+ [loadingRequest.dataRequest respondWithData:[NSData dataWithBytes:buffer length:len]];
+ submitted += len;
+ }
+
+ // Finish loading even if not all bytes submitted.
+ [loadingRequest finishLoading];
+ }
+
+ return YES;
+}
+@end
+
+AVFMediaPlayer::AVFMediaPlayer(QMediaPlayer *player)
+ : 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);
+ setVideoOutput(new AVFVideoRendererControl(this));
+}
+
+AVFMediaPlayer::~AVFMediaPlayer()
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO;
+#endif
+ //Detatch the session from the sessionObserver (which could still be alive trying to communicate with this session).
+ [m_observer detatchSession];
+ [static_cast<AVFMediaPlayerObserver*>(m_observer) release];
+}
+
+void AVFMediaPlayer::setVideoSink(QVideoSink *sink)
+{
+ m_videoSink = sink ? static_cast<AVFVideoSink *>(sink->platformVideoSink()): nullptr;
+ m_videoOutput->setVideoSink(m_videoSink);
+}
+
+void AVFMediaPlayer::setVideoOutput(AVFVideoRendererControl *output)
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO << output;
+#endif
+
+ if (m_videoOutput == output)
+ return;
+
+ //Set the current output layer to null to stop rendering
+ if (m_videoOutput) {
+ m_videoOutput->setLayer(nullptr);
+ }
+
+ m_videoOutput = output;
+
+ if (m_videoOutput && m_state != QMediaPlayer::StoppedState)
+ m_videoOutput->setLayer([static_cast<AVFMediaPlayerObserver*>(m_observer) playerLayer]);
+}
+
+AVAsset *AVFMediaPlayer::currentAssetHandle()
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO;
+#endif
+ AVAsset *currentAsset = [[static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem] asset];
+ return currentAsset;
+}
+
+QMediaPlayer::PlaybackState AVFMediaPlayer::state() const
+{
+ return m_state;
+}
+
+QMediaPlayer::MediaStatus AVFMediaPlayer::mediaStatus() const
+{
+ return m_mediaStatus;
+}
+
+QUrl AVFMediaPlayer::media() const
+{
+ return m_resources;
+}
+
+QIODevice *AVFMediaPlayer::mediaStream() const
+{
+ return m_mediaStream;
+}
+
+static void setURL(AVFMediaPlayerObserver *observer, const QByteArray &url, const QString &mimeType = QString())
+{
+ NSString *urlString = [NSString stringWithUTF8String:url.constData()];
+ NSURL *nsurl = [NSURL URLWithString:urlString];
+ [observer setURL:nsurl mimeType:[NSString stringWithUTF8String:mimeType.toLatin1().constData()]];
+}
+
+static void setStreamURL(AVFMediaPlayerObserver *observer, const QByteArray &url)
+{
+ setURL(observer, QByteArrayLiteral("iodevice://") + url, QFileInfo(QString::fromUtf8(url)).suffix());
+}
+
+void AVFMediaPlayer::setMedia(const QUrl &content, QIODevice *stream)
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO << content.request().url();
+#endif
+
+ [static_cast<AVFMediaPlayerObserver*>(m_observer) unloadMedia];
+
+ m_resources = content;
+ resetStream(stream);
+
+ setAudioAvailable(false);
+ setVideoAvailable(false);
+ setSeekable(false);
+ m_requestedPosition = -1;
+ orientationChanged(QtVideo::Rotation::None, false);
+ Q_EMIT positionChanged(position());
+ if (m_duration != 0) {
+ m_duration = 0;
+ Q_EMIT durationChanged(0);
+ }
+ if (!m_metaData.isEmpty()) {
+ m_metaData.clear();
+ metaDataChanged();
+ }
+ for (int i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
+ tracks[i].clear();
+ nativeTracks[i].clear();
+ }
+ tracksChanged();
+
+ const QMediaPlayer::MediaStatus oldMediaStatus = m_mediaStatus;
+ const QMediaPlayer::PlaybackState oldState = m_state;
+
+ if (!m_mediaStream && content.isEmpty()) {
+ m_mediaStatus = QMediaPlayer::NoMedia;
+ if (m_mediaStatus != oldMediaStatus)
+ Q_EMIT mediaStatusChanged(m_mediaStatus);
+
+ m_state = QMediaPlayer::StoppedState;
+ if (m_state != oldState)
+ Q_EMIT stateChanged(m_state);
+
+ return;
+ }
+
+ m_mediaStatus = QMediaPlayer::LoadingMedia;
+ if (m_mediaStatus != oldMediaStatus)
+ Q_EMIT mediaStatusChanged(m_mediaStatus);
+
+ if (m_mediaStream) {
+ // If there is a data, try to load it,
+ // otherwise wait for readyRead.
+ if (m_mediaStream->size())
+ setStreamURL(m_observer, m_resources.toEncoded());
+ } else {
+ //Load AVURLAsset
+ //initialize asset using content's URL
+ setURL(m_observer, m_resources.toEncoded());
+ }
+
+ m_state = QMediaPlayer::StoppedState;
+ if (m_state != oldState)
+ Q_EMIT stateChanged(m_state);
+}
+
+qint64 AVFMediaPlayer::position() const
+{
+ AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem];
+
+ if (m_requestedPosition != -1)
+ return m_requestedPosition;
+
+ if (!playerItem)
+ return 0;
+
+ CMTime time = [playerItem currentTime];
+ return static_cast<quint64>(float(time.value) / float(time.timescale) * 1000.0f);
+}
+
+qint64 AVFMediaPlayer::duration() const
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO;
+#endif
+ return m_duration;
+}
+
+float AVFMediaPlayer::bufferProgress() const
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO;
+#endif
+ return m_bufferProgress/100.;
+}
+
+void AVFMediaPlayer::setAudioAvailable(bool available)
+{
+ if (m_audioAvailable == available)
+ return;
+
+ m_audioAvailable = available;
+ Q_EMIT audioAvailableChanged(available);
+}
+
+bool AVFMediaPlayer::isAudioAvailable() const
+{
+ return m_audioAvailable;
+}
+
+void AVFMediaPlayer::setVideoAvailable(bool available)
+{
+ if (m_videoAvailable == available)
+ return;
+
+ m_videoAvailable = available;
+ Q_EMIT videoAvailableChanged(available);
+}
+
+bool AVFMediaPlayer::isVideoAvailable() const
+{
+ return m_videoAvailable;
+}
+
+bool AVFMediaPlayer::isSeekable() const
+{
+ return m_seekable;
+}
+
+void AVFMediaPlayer::setSeekable(bool seekable)
+{
+ if (m_seekable == seekable)
+ return;
+
+ m_seekable = seekable;
+ Q_EMIT seekableChanged(seekable);
+}
+
+QMediaTimeRange AVFMediaPlayer::availablePlaybackRanges() const
+{
+ AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem];
+
+ if (playerItem) {
+ QMediaTimeRange timeRanges;
+
+ NSArray *ranges = [playerItem loadedTimeRanges];
+ for (NSValue *timeRange in ranges) {
+ CMTimeRange currentTimeRange = [timeRange CMTimeRangeValue];
+ qint64 startTime = qint64(float(currentTimeRange.start.value) / currentTimeRange.start.timescale * 1000.0);
+ timeRanges.addInterval(startTime, startTime + qint64(float(currentTimeRange.duration.value) / currentTimeRange.duration.timescale * 1000.0));
+ }
+ if (!timeRanges.isEmpty())
+ return timeRanges;
+ }
+ return QMediaTimeRange(0, duration());
+}
+
+qreal AVFMediaPlayer::playbackRate() const
+{
+ return m_rate;
+}
+
+void AVFMediaPlayer::setAudioOutput(QPlatformAudioOutput *output)
+{
+ if (m_audioOutput == output)
+ return;
+ if (m_audioOutput)
+ m_audioOutput->q->disconnect(this);
+ m_audioOutput = output;
+ if (m_audioOutput) {
+ connect(m_audioOutput->q, &QAudioOutput::deviceChanged, this, &AVFMediaPlayer::audioOutputChanged);
+ connect(m_audioOutput->q, &QAudioOutput::volumeChanged, this, &AVFMediaPlayer::setVolume);
+ connect(m_audioOutput->q, &QAudioOutput::mutedChanged, this, &AVFMediaPlayer::setMuted);
+ //connect(m_audioOutput->q, &QAudioOutput::audioRoleChanged, this, &AVFMediaPlayer::setAudioRole);
+ }
+ audioOutputChanged();
+ setMuted(m_audioOutput ? m_audioOutput->muted : true);
+ setVolume(m_audioOutput ? m_audioOutput->volume : 1.);
+}
+
+QMediaMetaData AVFMediaPlayer::metaData() const
+{
+ return m_metaData;
+}
+
+void AVFMediaPlayer::setPlaybackRate(qreal rate)
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO << rate;
+#endif
+
+ if (qFuzzyCompare(m_rate, rate))
+ return;
+
+ m_rate = rate;
+
+ AVPlayer *player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
+ if (player && m_state == QMediaPlayer::PlayingState)
+ [player setRate:m_rate];
+
+ Q_EMIT playbackRateChanged(m_rate);
+}
+
+void AVFMediaPlayer::setPosition(qint64 pos)
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO << pos;
+#endif
+
+ if (pos == position())
+ return;
+
+ AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem];
+ if (!playerItem) {
+ m_requestedPosition = pos;
+ Q_EMIT positionChanged(m_requestedPosition);
+ return;
+ }
+
+ if (!isSeekable()) {
+ if (m_requestedPosition != -1) {
+ m_requestedPosition = -1;
+ Q_EMIT positionChanged(position());
+ }
+ return;
+ }
+
+ pos = qMax(qint64(0), pos);
+ if (duration() > 0)
+ pos = qMin(pos, duration());
+ m_requestedPosition = pos;
+
+ CMTime newTime = [playerItem currentTime];
+ newTime.value = (pos / 1000.0f) * newTime.timescale;
+ [playerItem seekToTime:newTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero
+ completionHandler:^(BOOL finished) {
+ if (finished)
+ m_requestedPosition = -1;
+ }];
+
+ Q_EMIT positionChanged(pos);
+
+ // Reset media status if the current status is EndOfMedia
+ if (m_mediaStatus == QMediaPlayer::EndOfMedia) {
+ QMediaPlayer::MediaStatus newMediaStatus = (m_state == QMediaPlayer::PausedState) ? QMediaPlayer::BufferedMedia
+ : QMediaPlayer::LoadedMedia;
+ Q_EMIT mediaStatusChanged((m_mediaStatus = newMediaStatus));
+ }
+}
+
+void AVFMediaPlayer::play()
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO << "currently: " << m_state;
+#endif
+
+ if (m_mediaStatus == QMediaPlayer::NoMedia || m_mediaStatus == QMediaPlayer::InvalidMedia)
+ return;
+
+ if (m_state == QMediaPlayer::PlayingState)
+ return;
+
+ resetCurrentLoop();
+
+ if (m_videoOutput && m_videoSink)
+ m_videoOutput->setLayer([static_cast<AVFMediaPlayerObserver*>(m_observer) playerLayer]);
+
+ // Reset media status if the current status is EndOfMedia
+ if (m_mediaStatus == QMediaPlayer::EndOfMedia)
+ setPosition(0);
+
+ if (m_mediaStatus == QMediaPlayer::LoadedMedia || m_mediaStatus == QMediaPlayer::BufferedMedia) {
+ // Setting the rate starts playback
+ [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
+ }
+
+ m_state = QMediaPlayer::PlayingState;
+ processLoadStateChange();
+
+ Q_EMIT stateChanged(m_state);
+ m_playbackTimer.start(100);
+}
+
+void AVFMediaPlayer::pause()
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO << "currently: " << m_state;
+#endif
+
+ if (m_mediaStatus == QMediaPlayer::NoMedia)
+ return;
+
+ if (m_state == QMediaPlayer::PausedState)
+ return;
+
+ m_state = QMediaPlayer::PausedState;
+
+ if (m_videoOutput && m_videoSink)
+ m_videoOutput->setLayer([static_cast<AVFMediaPlayerObserver*>(m_observer) playerLayer]);
+
+ [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] pause];
+
+ // Reset media status if the current status is EndOfMedia
+ if (m_mediaStatus == QMediaPlayer::EndOfMedia)
+ setPosition(0);
+
+ Q_EMIT positionChanged(position());
+ Q_EMIT stateChanged(m_state);
+ m_playbackTimer.stop();
+}
+
+void AVFMediaPlayer::stop()
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO << "currently: " << m_state;
+#endif
+
+ if (m_state == QMediaPlayer::StoppedState)
+ return;
+
+ // AVPlayer doesn't have stop(), only pause() and play().
+ [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] pause];
+ setPosition(0);
+
+ if (m_videoOutput)
+ m_videoOutput->setLayer(nullptr);
+
+ if (m_mediaStatus == QMediaPlayer::BufferedMedia)
+ Q_EMIT mediaStatusChanged((m_mediaStatus = QMediaPlayer::LoadedMedia));
+
+ Q_EMIT stateChanged((m_state = QMediaPlayer::StoppedState));
+ m_playbackTimer.stop();
+}
+
+void AVFMediaPlayer::setVolume(float volume)
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO << volume;
+#endif
+
+ AVPlayer *player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
+ if (player)
+ player.volume = volume;
+}
+
+void AVFMediaPlayer::setMuted(bool muted)
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO << muted;
+#endif
+
+ AVPlayer *player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
+ if (player)
+ player.muted = muted;
+}
+
+void AVFMediaPlayer::audioOutputChanged()
+{
+#ifdef Q_OS_MACOS
+ AVPlayer *player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
+ if (!m_audioOutput || m_audioOutput->device.id().isEmpty()) {
+ player.audioOutputDeviceUniqueID = nil;
+ if (!m_audioOutput)
+ player.muted = true;
+ } else {
+ NSString *str = QString::fromUtf8(m_audioOutput->device.id()).toNSString();
+ player.audioOutputDeviceUniqueID = str;
+ }
+#endif
+}
+
+void AVFMediaPlayer::processEOS()
+{
+ if (doLoop()) {
+ setPosition(0);
+ [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
+ return;
+ }
+
+ //AVPlayerItem has reached end of track/stream
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO;
+#endif
+ Q_EMIT positionChanged(position());
+ m_mediaStatus = QMediaPlayer::EndOfMedia;
+ m_state = QMediaPlayer::StoppedState;
+
+ if (m_videoOutput)
+ m_videoOutput->setLayer(nullptr);
+
+ Q_EMIT mediaStatusChanged(m_mediaStatus);
+ Q_EMIT stateChanged(m_state);
+}
+
+void AVFMediaPlayer::processLoadStateChange(QMediaPlayer::PlaybackState newState)
+{
+ AVPlayerStatus currentStatus = [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] status];
+
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO << currentStatus << ", " << m_mediaStatus << ", " << newState;
+#endif
+
+ if (m_mediaStatus == QMediaPlayer::NoMedia)
+ return;
+
+ if (currentStatus == AVPlayerStatusReadyToPlay) {
+
+ QMediaPlayer::MediaStatus newStatus = m_mediaStatus;
+
+ AVPlayerItem *playerItem = [m_observer playerItem];
+
+ // get the meta data
+ m_metaData = AVFMetaData::fromAsset(playerItem.asset);
+ Q_EMIT metaDataChanged();
+ updateTracks();
+
+ if (playerItem) {
+ setSeekable([[playerItem seekableTimeRanges] count] > 0);
+
+ // Get the native size of the video, and reset the bounds of the player layer
+ AVPlayerLayer *playerLayer = [m_observer playerLayer];
+ if (m_observer.videoTrack && playerLayer) {
+ if (!playerLayer.bounds.size.width || !playerLayer.bounds.size.height) {
+ playerLayer.bounds = CGRectMake(0.0f, 0.0f,
+ m_observer.videoTrack.assetTrack.naturalSize.width,
+ m_observer.videoTrack.assetTrack.naturalSize.height);
+ }
+ }
+
+ if (m_requestedPosition != -1) {
+ setPosition(m_requestedPosition);
+ m_requestedPosition = -1;
+ }
+ }
+
+ newStatus = (newState != QMediaPlayer::StoppedState) ? QMediaPlayer::BufferedMedia
+ : QMediaPlayer::LoadedMedia;
+
+ if (newStatus != m_mediaStatus)
+ Q_EMIT mediaStatusChanged((m_mediaStatus = newStatus));
+
+ }
+
+ if (newState == QMediaPlayer::PlayingState && [static_cast<AVFMediaPlayerObserver*>(m_observer) player]) {
+ // Setting the rate is enough to start playback, no need to call play()
+ [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
+ m_playbackTimer.start();
+ }
+}
+
+
+void AVFMediaPlayer::processLoadStateChange()
+{
+ processLoadStateChange(m_state);
+}
+
+
+void AVFMediaPlayer::processLoadStateFailure()
+{
+ Q_EMIT stateChanged((m_state = QMediaPlayer::StoppedState));
+}
+
+void AVFMediaPlayer::processBufferStateChange(int bufferProgress)
+{
+ if (bufferProgress == m_bufferProgress)
+ return;
+
+ auto status = m_mediaStatus;
+ // Buffered -> unbuffered.
+ if (!bufferProgress) {
+ status = QMediaPlayer::StalledMedia;
+ } else if (status == QMediaPlayer::StalledMedia) {
+ status = QMediaPlayer::BufferedMedia;
+ // Resume playback.
+ if (m_state == QMediaPlayer::PlayingState) {
+ [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
+ m_playbackTimer.start();
+ }
+ }
+
+ if (m_mediaStatus != status)
+ Q_EMIT mediaStatusChanged(m_mediaStatus = status);
+
+ m_bufferProgress = bufferProgress;
+ Q_EMIT bufferProgressChanged(bufferProgress/100.);
+}
+
+void AVFMediaPlayer::processDurationChange(qint64 duration)
+{
+ if (duration == m_duration)
+ return;
+
+ m_duration = duration;
+ Q_EMIT durationChanged(duration);
+}
+
+void AVFMediaPlayer::processPositionChange()
+{
+ if (m_state == QMediaPlayer::StoppedState)
+ return;
+
+ Q_EMIT positionChanged(position());
+}
+
+void AVFMediaPlayer::processMediaLoadError()
+{
+ if (m_requestedPosition != -1) {
+ m_requestedPosition = -1;
+ Q_EMIT positionChanged(position());
+ }
+
+ Q_EMIT mediaStatusChanged((m_mediaStatus = QMediaPlayer::InvalidMedia));
+
+ Q_EMIT error(QMediaPlayer::FormatError, tr("Failed to load media"));
+}
+
+void AVFMediaPlayer::streamReady()
+{
+ setStreamURL(m_observer, m_resources.toEncoded());
+}
+
+void AVFMediaPlayer::streamDestroyed()
+{
+ resetStream(nullptr);
+}
+
+void AVFMediaPlayer::updateTracks()
+{
+ bool firstLoad = true;
+ for (int i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
+ if (tracks[i].count())
+ firstLoad = false;
+ tracks[i].clear();
+ nativeTracks[i].clear();
+ }
+ AVPlayerItem *playerItem = [m_observer playerItem];
+ if (playerItem) {
+ // Check each track for audio and video content
+ NSArray *tracks = playerItem.tracks;
+ for (AVPlayerItemTrack *track in tracks) {
+ AVAssetTrack *assetTrack = track.assetTrack;
+ if (assetTrack) {
+ int qtTrack = -1;
+ if ([assetTrack.mediaType isEqualToString:AVMediaTypeAudio]) {
+ qtTrack = QPlatformMediaPlayer::AudioStream;
+ setAudioAvailable(true);
+ } else if ([assetTrack.mediaType isEqualToString:AVMediaTypeVideo]) {
+ qtTrack = QPlatformMediaPlayer::VideoStream;
+ setVideoAvailable(true);
+ if (m_observer.videoTrack != track) {
+ m_observer.videoTrack = track;
+ bool isMirrored = false;
+ QtVideo::Rotation orientation = QtVideo::Rotation::None;
+ videoOrientationForAssetTrack(assetTrack, orientation, isMirrored);
+ orientationChanged(orientation, isMirrored);
+ }
+ }
+ else if ([assetTrack.mediaType isEqualToString:AVMediaTypeSubtitle]) {
+ qtTrack = QPlatformMediaPlayer::SubtitleStream;
+ }
+ if (qtTrack != -1) {
+ QMediaMetaData metaData = AVFMetaData::fromAssetTrack(assetTrack);
+ this->tracks[qtTrack].append(metaData);
+ nativeTracks[qtTrack].append(track);
+ }
+ }
+ }
+ // subtitles are disabled by default
+ if (firstLoad)
+ setActiveTrack(SubtitleStream, -1);
+ }
+ Q_EMIT tracksChanged();
+}
+
+void AVFMediaPlayer::setActiveTrack(QPlatformMediaPlayer::TrackType type, int index)
+{
+ const auto &t = nativeTracks[type];
+ if (type == QPlatformMediaPlayer::SubtitleStream) {
+ // subtitle streams are not always automatically enabled on macOS/iOS.
+ // this hack ensures they get enables and we actually get the text
+ AVPlayerItem *playerItem = m_observer.m_playerItem;
+ if (playerItem) {
+ AVAsset *asset = playerItem.asset;
+ if (!asset)
+ return;
+ AVMediaSelectionGroup *group = [asset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
+ if (!group)
+ return;
+ auto *options = group.options;
+ if (options.count)
+ [playerItem selectMediaOption:options.firstObject inMediaSelectionGroup:group];
+ }
+ }
+ for (int i = 0; i < t.count(); ++i)
+ t.at(i).enabled = (i == index);
+ emit activeTracksChanged();
+}
+
+int AVFMediaPlayer::activeTrack(QPlatformMediaPlayer::TrackType type)
+{
+ const auto &t = nativeTracks[type];
+ for (int i = 0; i < t.count(); ++i)
+ if (t.at(i).enabled)
+ return i;
+ return -1;
+}
+
+int AVFMediaPlayer::trackCount(QPlatformMediaPlayer::TrackType type)
+{
+ return nativeTracks[type].count();
+}
+
+QMediaMetaData AVFMediaPlayer::trackMetaData(QPlatformMediaPlayer::TrackType type, int trackNumber)
+{
+ const auto &t = tracks[type];
+ if (trackNumber < 0 || trackNumber >= t.count())
+ return QMediaMetaData();
+ return t.at(trackNumber);
+}
+
+void AVFMediaPlayer::resetStream(QIODevice *stream)
+{
+ if (m_mediaStream) {
+ disconnect(m_mediaStream, &QIODevice::readyRead, this, &AVFMediaPlayer::streamReady);
+ disconnect(m_mediaStream, &QIODevice::destroyed, this, &AVFMediaPlayer::streamDestroyed);
+ }
+
+ m_mediaStream = stream;
+
+ if (m_mediaStream) {
+ connect(m_mediaStream, &QIODevice::readyRead, this, &AVFMediaPlayer::streamReady);
+ connect(m_mediaStream, &QIODevice::destroyed, this, &AVFMediaPlayer::streamDestroyed);
+ }
+}
+
+void AVFMediaPlayer::nativeSizeChanged(QSize size)
+{
+ if (!m_videoSink)
+ return;
+ m_videoSink->setNativeSize(size);
+}
+
+void AVFMediaPlayer::orientationChanged(QtVideo::Rotation rotation, bool mirrored)
+{
+ if (!m_videoOutput)
+ return;
+
+ m_videoOutput->setVideoRotation(rotation);
+ m_videoOutput->setVideoMirrored(mirrored);
+}
+
+void AVFMediaPlayer::videoOrientationForAssetTrack(AVAssetTrack *videoTrack,
+ QtVideo::Rotation &angle,
+ bool &mirrored)
+{
+ angle = QtVideo::Rotation::None;
+ if (videoTrack) {
+ CGAffineTransform transform = videoTrack.preferredTransform;
+ if (CGAffineTransformIsIdentity(transform))
+ return;
+ qreal delta = transform.a * transform.d - transform.b * transform.c;
+ qreal radians = qAtan2(transform.b, transform.a);
+ qreal degrees = qRadiansToDegrees(radians);
+ qreal scaleX = (transform.a/qAbs(transform.a)) * qSqrt(qPow(transform.a, 2) + qPow(transform.c, 2));
+ qreal scaleY = (transform.d/abs(transform.d)) * qSqrt(qPow(transform.b, 2) + qPow(transform.d, 2));
+
+ if (delta < 0.0) { // flipped
+ if (scaleX < 0.0) {
+ // vertical flip
+ degrees = -degrees;
+ } else if (scaleY < 0.0) {
+ // horizontal flip
+ degrees = (180 + (int)degrees) % 360;
+ }
+ mirrored = true;
+ }
+
+ if (qFuzzyCompare(degrees, qreal(90)) || qFuzzyCompare(degrees, qreal(-270))) {
+ angle = QtVideo::Rotation::Clockwise90;
+ } else if (qFuzzyCompare(degrees, qreal(-90)) || qFuzzyCompare(degrees, qreal(270))) {
+ angle = QtVideo::Rotation::Clockwise270;
+ } else if (qFuzzyCompare(degrees, qreal(180)) || qFuzzyCompare(degrees, qreal(-180))) {
+ 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
new file mode 100644
index 000000000..d04ab0818
--- /dev/null
+++ b/src/plugins/multimedia/darwin/mediaplayer/avfmediaplayer_p.h
@@ -0,0 +1,151 @@
+// 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
+
+//
+// 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>
+#include <QtCore/QByteArray>
+#include <QtCore/QSet>
+#include <QtCore/QResource>
+#include <QtCore/QUrl>
+#include <QtCore/QTimer>
+
+#include <private/qplatformmediaplayer_p.h>
+#include <QtMultimedia/QMediaPlayer>
+#include <QtMultimedia/QVideoFrame>
+
+Q_FORWARD_DECLARE_OBJC_CLASS(AVAsset);
+Q_FORWARD_DECLARE_OBJC_CLASS(AVPlayerItemTrack);
+Q_FORWARD_DECLARE_OBJC_CLASS(AVFMediaPlayerObserver);
+Q_FORWARD_DECLARE_OBJC_CLASS(AVAssetTrack);
+
+QT_BEGIN_NAMESPACE
+
+class AVFMediaPlayer;
+class AVFVideoRendererControl;
+class AVFVideoSink;
+
+class AVFMediaPlayer : public QObject, public QPlatformMediaPlayer
+{
+ Q_OBJECT
+public:
+ AVFMediaPlayer(QMediaPlayer *parent);
+ virtual ~AVFMediaPlayer();
+
+ void setVideoSink(QVideoSink *sink) override;
+ void setVideoOutput(AVFVideoRendererControl *output);
+ AVAsset *currentAssetHandle();
+
+ QMediaPlayer::PlaybackState state() const override;
+ QMediaPlayer::MediaStatus mediaStatus() const override;
+
+ QUrl media() const override;
+ QIODevice *mediaStream() const override;
+ void setMedia(const QUrl &content, QIODevice *stream) override;
+
+ qint64 position() const override;
+ qint64 duration() const override;
+
+ float bufferProgress() const override;
+
+ bool isAudioAvailable() const override;
+ bool isVideoAvailable() const override;
+
+ bool isSeekable() const override;
+ QMediaTimeRange availablePlaybackRanges() const override;
+
+ qreal playbackRate() const override;
+
+ void setAudioOutput(QPlatformAudioOutput *output) override;
+ QPlatformAudioOutput *m_audioOutput = nullptr;
+
+ QMediaMetaData metaData() const override;
+
+ static void videoOrientationForAssetTrack(AVAssetTrack *track,
+ QtVideo::Rotation &angle,
+ bool &mirrored);
+
+public Q_SLOTS:
+ void setPlaybackRate(qreal rate) override;
+ void nativeSizeChanged(QSize size);
+
+ void setPosition(qint64 pos) override;
+
+ void play() override;
+ void pause() override;
+ void stop() override;
+
+ void setVolume(float volume);
+ void setMuted(bool muted);
+ void audioOutputChanged();
+
+ void processEOS();
+ void processLoadStateChange(QMediaPlayer::PlaybackState newState);
+ void processPositionChange();
+ void processMediaLoadError();
+
+ void processLoadStateChange();
+ void processLoadStateFailure();
+
+ void processBufferStateChange(int bufferProgress);
+
+ void processDurationChange(qint64 duration);
+
+ void streamReady();
+ void streamDestroyed();
+ void updateTracks();
+ void setActiveTrack(QPlatformMediaPlayer::TrackType type, int index) override;
+ int activeTrack(QPlatformMediaPlayer::TrackType type) override;
+ int trackCount(TrackType) override;
+ QMediaMetaData trackMetaData(TrackType type, int trackNumber) override;
+
+public:
+ QList<QMediaMetaData> tracks[QPlatformMediaPlayer::NTrackTypes];
+ QList<AVPlayerItemTrack *> nativeTracks[QPlatformMediaPlayer::NTrackTypes];
+
+private:
+ void setAudioAvailable(bool available);
+ void setVideoAvailable(bool available);
+ void setSeekable(bool seekable);
+ void resetStream(QIODevice *stream = nullptr);
+
+ void orientationChanged(QtVideo::Rotation rotation, bool mirrored);
+
+ AVFVideoRendererControl *m_videoOutput = nullptr;
+ AVFVideoSink *m_videoSink = nullptr;
+
+ QMediaPlayer::PlaybackState m_state;
+ QMediaPlayer::MediaStatus m_mediaStatus;
+ QIODevice *m_mediaStream;
+ QUrl m_resources;
+ QMediaMetaData m_metaData;
+
+ qreal m_rate;
+ qint64 m_requestedPosition;
+
+ qint64 m_duration;
+ int m_bufferProgress;
+ bool m_videoAvailable;
+ bool m_audioAvailable;
+ bool m_seekable;
+
+ AVFMediaPlayerObserver *m_observer;
+
+ QTimer m_playbackTimer;
+};
+
+QT_END_NAMESPACE
+
+#endif // AVFMEDIAPLAYER_H
diff --git a/src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol.mm b/src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol.mm
new file mode 100644
index 000000000..66687c931
--- /dev/null
+++ b/src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol.mm
@@ -0,0 +1,222 @@
+// 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"
+#include <avfvideobuffer_p.h>
+#include "qavfhelpers_p.h"
+#include "private/qvideoframe_p.h"
+
+#include <QtMultimedia/qvideoframeformat.h>
+
+#include <avfvideosink_p.h>
+#include <rhi/qrhi.h>
+
+#include <QtCore/qdebug.h>
+
+#import <AVFoundation/AVFoundation.h>
+#include <CoreVideo/CVPixelBuffer.h>
+#include <CoreVideo/CVImageBuffer.h>
+
+QT_USE_NAMESPACE
+
+@interface SubtitleDelegate : NSObject <AVPlayerItemLegibleOutputPushDelegate>
+{
+ AVFVideoRendererControl *m_renderer;
+}
+
+- (void)legibleOutput:(AVPlayerItemLegibleOutput *)output
+ didOutputAttributedStrings:(NSArray<NSAttributedString *> *)strings
+ nativeSampleBuffers:(NSArray *)nativeSamples
+ forItemTime:(CMTime)itemTime;
+
+@end
+
+@implementation SubtitleDelegate
+
+-(id)initWithRenderer: (AVFVideoRendererControl *)renderer
+{
+ if (!(self = [super init]))
+ return nil;
+
+ m_renderer = renderer;
+
+ return self;
+}
+
+- (void)legibleOutput:(AVPlayerItemLegibleOutput *)output
+ didOutputAttributedStrings:(NSArray<NSAttributedString *> *)strings
+ nativeSampleBuffers:(NSArray *)nativeSamples
+ forItemTime:(CMTime)itemTime
+{
+ QString text;
+ for (NSAttributedString *s : strings) {
+ if (!text.isEmpty())
+ text += QChar::LineSeparator;
+ text += QString::fromNSString(s.string);
+ }
+ m_renderer->setSubtitleText(text);
+}
+
+@end
+
+
+AVFVideoRendererControl::AVFVideoRendererControl(QObject *parent)
+ : QObject(parent)
+{
+ m_displayLink = new AVFDisplayLink(this);
+ connect(m_displayLink, SIGNAL(tick(CVTimeStamp)), SLOT(updateVideoFrame(CVTimeStamp)));
+}
+
+AVFVideoRendererControl::~AVFVideoRendererControl()
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << Q_FUNC_INFO;
+#endif
+ m_displayLink->stop();
+ if (m_videoOutput)
+ [m_videoOutput release];
+ if (m_subtitleOutput)
+ [m_subtitleOutput release];
+ if (m_subtitleDelegate)
+ [m_subtitleDelegate release];
+}
+
+void AVFVideoRendererControl::reconfigure()
+{
+#ifdef QT_DEBUG_AVF
+ qDebug() << "reconfigure";
+#endif
+ if (!m_layer) {
+ m_displayLink->stop();
+ return;
+ }
+
+ QMutexLocker locker(&m_mutex);
+
+ m_displayLink->start();
+
+ nativeSizeChanged();
+}
+
+void AVFVideoRendererControl::setLayer(CALayer *layer)
+{
+ if (m_layer == layer)
+ return;
+
+ AVPlayerLayer *plLayer = playerLayer();
+ if (plLayer) {
+ if (m_videoOutput)
+ [[[plLayer player] currentItem] removeOutput:m_videoOutput];
+
+ if (m_subtitleOutput)
+ [[[plLayer player] currentItem] removeOutput:m_subtitleOutput];
+ }
+
+ if (!layer && m_sink)
+ m_sink->setVideoFrame(QVideoFrame());
+
+ AVFVideoSinkInterface::setLayer(layer);
+}
+
+void AVFVideoRendererControl::setVideoRotation(QtVideo::Rotation rotation)
+{
+ m_rotation = rotation;
+}
+
+void AVFVideoRendererControl::setVideoMirrored(bool mirrored)
+{
+ m_mirrored = mirrored;
+}
+
+void AVFVideoRendererControl::updateVideoFrame(const CVTimeStamp &ts)
+{
+ Q_UNUSED(ts);
+
+ if (!m_sink)
+ return;
+
+ if (!m_layer)
+ return;
+
+ auto *layer = playerLayer();
+ if (!layer.readyForDisplay)
+ return;
+ nativeSizeChanged();
+
+ QVideoFrame frame;
+ size_t width, height;
+ CVPixelBufferRef pixelBuffer = copyPixelBufferFromLayer(width, height);
+ if (!pixelBuffer)
+ return;
+ auto buffer = std::make_unique<AVFVideoBuffer>(this, pixelBuffer);
+ // qDebug() << "Got pixelbuffer with format" << fmt << Qt::hex <<
+ // CVPixelBufferGetPixelFormatType(pixelBuffer);
+ CVPixelBufferRelease(pixelBuffer);
+
+ frame = QVideoFramePrivate::createFrame(std::move(buffer), buffer->videoFormat());
+ frame.setRotation(m_rotation);
+ frame.setMirrored(m_mirrored);
+ m_sink->setVideoFrame(frame);
+}
+
+CVPixelBufferRef AVFVideoRendererControl::copyPixelBufferFromLayer(size_t& width, size_t& height)
+{
+ AVPlayerLayer *layer = playerLayer();
+ //Is layer valid
+ if (!layer) {
+#ifdef QT_DEBUG_AVF
+ qWarning("copyPixelBufferFromLayer: invalid layer");
+#endif
+ return nullptr;
+ }
+
+ AVPlayerItem * item = [[layer player] currentItem];
+
+ if (!m_videoOutput) {
+ if (!m_outputSettings)
+ setOutputSettings();
+ m_videoOutput = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:m_outputSettings];
+ [m_videoOutput setDelegate:nil queue:nil];
+ }
+ if (!m_subtitleOutput) {
+ m_subtitleOutput = [[AVPlayerItemLegibleOutput alloc] init];
+ m_subtitleDelegate = [[SubtitleDelegate alloc] initWithRenderer:this];
+ [m_subtitleOutput setDelegate:m_subtitleDelegate queue:dispatch_get_main_queue()];
+ }
+ if (![item.outputs containsObject:m_videoOutput])
+ [item addOutput:m_videoOutput];
+ if (![item.outputs containsObject:m_subtitleOutput])
+ [item addOutput:m_subtitleOutput];
+
+ CFTimeInterval currentCAFrameTime = CACurrentMediaTime();
+ CMTime currentCMFrameTime = [m_videoOutput itemTimeForHostTime:currentCAFrameTime];
+ // happens when buffering / loading
+ if (CMTimeCompare(currentCMFrameTime, kCMTimeZero) < 0) {
+ return nullptr;
+ }
+
+ if (![m_videoOutput hasNewPixelBufferForItemTime:currentCMFrameTime])
+ return nullptr;
+
+ CVPixelBufferRef pixelBuffer = [m_videoOutput copyPixelBufferForItemTime:currentCMFrameTime
+ itemTimeForDisplay:nil];
+ if (!pixelBuffer) {
+#ifdef QT_DEBUG_AVF
+ qWarning("copyPixelBufferForItemTime returned nil");
+ CMTimeShow(currentCMFrameTime);
+#endif
+ return nullptr;
+ }
+
+ width = CVPixelBufferGetWidth(pixelBuffer);
+ height = CVPixelBufferGetHeight(pixelBuffer);
+// auto f = CVPixelBufferGetPixelFormatType(pixelBuffer);
+// char fmt[5];
+// memcpy(fmt, &f, 4);
+// fmt[4] = 0;
+// qDebug() << "copyPixelBuffer" << f << fmt << width << height;
+ return pixelBuffer;
+}
+
+#include "moc_avfvideorenderercontrol_p.cpp"
diff --git a/src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol_p.h b/src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol_p.h
new file mode 100644
index 000000000..177114127
--- /dev/null
+++ b/src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol_p.h
@@ -0,0 +1,72 @@
+// 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
+
+//
+// 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>
+#include <QtCore/QMutex>
+#include <QtCore/QSize>
+
+#include <avfvideosink_p.h>
+
+#include <CoreVideo/CVBase.h>
+#include <CoreVideo/CVPixelBuffer.h>
+
+Q_FORWARD_DECLARE_OBJC_CLASS(CALayer);
+Q_FORWARD_DECLARE_OBJC_CLASS(AVPlayerItemVideoOutput);
+Q_FORWARD_DECLARE_OBJC_CLASS(AVPlayerItemLegibleOutput);
+Q_FORWARD_DECLARE_OBJC_CLASS(SubtitleDelegate);
+
+QT_BEGIN_NAMESPACE
+
+class AVFDisplayLink;
+
+class AVFVideoRendererControl : public QObject, public AVFVideoSinkInterface
+{
+ Q_OBJECT
+public:
+ explicit AVFVideoRendererControl(QObject *parent = nullptr);
+ virtual ~AVFVideoRendererControl();
+
+ // AVFVideoSinkInterface
+ void reconfigure() override;
+ void setLayer(CALayer *layer) override;
+
+ void setVideoRotation(QtVideo::Rotation);
+ void setVideoMirrored(bool mirrored);
+
+ void setSubtitleText(const QString &subtitle)
+ {
+ m_sink->setSubtitleText(subtitle);
+ }
+private Q_SLOTS:
+ void updateVideoFrame(const CVTimeStamp &ts);
+
+private:
+ AVPlayerLayer *playerLayer() const { return static_cast<AVPlayerLayer *>(m_layer); }
+ CVPixelBufferRef copyPixelBufferFromLayer(size_t& width, size_t& height);
+
+ QMutex m_mutex;
+ AVFDisplayLink *m_displayLink = nullptr;
+ AVPlayerItemVideoOutput *m_videoOutput = nullptr;
+ AVPlayerItemLegibleOutput *m_subtitleOutput = nullptr;
+ SubtitleDelegate *m_subtitleDelegate = nullptr;
+ QtVideo::Rotation m_rotation = QtVideo::Rotation::None;
+ bool m_mirrored = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // AVFVIDEORENDERERCONTROL_H
diff --git a/src/plugins/multimedia/darwin/qavfhelpers.mm b/src/plugins/multimedia/darwin/qavfhelpers.mm
new file mode 100644
index 000000000..51ae9eedc
--- /dev/null
+++ b/src/plugins/multimedia/darwin/qavfhelpers.mm
@@ -0,0 +1,143 @@
+// 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>
+#include <qdebug.h>
+
+#import <CoreVideo/CoreVideo.h>
+
+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)
+{
+ 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);
+}
+
+}
+
+ColorRange QAVFHelpers::colorRangeForCVPixelFormat(CvPixelFormat cvPixelFormat)
+{
+ return findInPixelFormatMap(ColorRange::ColorRange_Unknown, cvPixelFormat);
+}
+
+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 cvPixelFormat = CVPixelBufferGetPixelFormatType(buffer);
+ auto pixelFormat = fromCVPixelFormat(cvPixelFormat);
+ if (openGL) {
+ if (cvPixelFormat == kCVPixelFormatType_32BGRA)
+ pixelFormat = QVideoFrameFormat::Format_SamplerRect;
+ else
+ qWarning() << "Accelerated macOS OpenGL video supports BGRA only, got CV pixel format"
+ << cvPixelFormat;
+ }
+
+ size_t width = CVPixelBufferGetWidth(buffer);
+ size_t height = CVPixelBufferGetHeight(buffer);
+
+ QVideoFrameFormat format(QSize(width, height), pixelFormat);
+
+ auto colorSpace = QVideoFrameFormat::ColorSpace_Undefined;
+ auto colorTransfer = QVideoFrameFormat::ColorTransfer_Unknown;
+
+ 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;
+ }
+ }
+ }
+
+ if (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)
+ 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
new file mode 100644
index 000000000..8133d5500
--- /dev/null
+++ b/src/plugins/multimedia/darwin/qavfhelpers_p.h
@@ -0,0 +1,41 @@
+// 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
+
+//
+// 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/qvideoframe.h>
+#include <qvideoframeformat.h>
+
+#include <CoreVideo/CVBase.h>
+#include <CoreVideo/CVPixelBuffer.h>
+#include <CoreVideo/CVImageBuffer.h>
+
+QT_BEGIN_NAMESPACE
+
+using CvPixelFormat = unsigned;
+constexpr CvPixelFormat CvPixelFormatInvalid = 0;
+
+namespace QAVFHelpers
+{
+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);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/darwin/qdarwinformatsinfo.mm b/src/plugins/multimedia/darwin/qdarwinformatsinfo.mm
new file mode 100644
index 000000000..582060a6c
--- /dev/null
+++ b/src/plugins/multimedia/darwin/qdarwinformatsinfo.mm
@@ -0,0 +1,211 @@
+// 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>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+static struct {
+ const char *name;
+ QMediaFormat::FileFormat value;
+} mediaContainerMap[] = {
+ { "video/x-ms-asf", QMediaFormat::WMV },
+ { "video/avi", QMediaFormat::AVI },
+ { "video/x-matroska", QMediaFormat::Matroska },
+ { "video/mp4", QMediaFormat::MPEG4 },
+ { "video/quicktime", QMediaFormat::QuickTime },
+ { "video/ogg", QMediaFormat::Ogg },
+ { "audio/mp3", QMediaFormat::MP3 },
+ { "audio/flac", QMediaFormat::FLAC },
+ { nullptr, QMediaFormat::UnspecifiedFormat }
+};
+
+static struct {
+ const char *name;
+ QMediaFormat::VideoCodec value;
+} videoCodecMap[] = {
+ // See CMVideoCodecType for the four character code names of codecs
+ { "; codecs=\"mp1v\"", QMediaFormat::VideoCodec::MPEG1 },
+ { "; codecs=\"mp2v\"", QMediaFormat::VideoCodec::MPEG2 },
+ { "; codecs=\"mp4v\"", QMediaFormat::VideoCodec::MPEG4 },
+ { "; codecs=\"avc1\"", QMediaFormat::VideoCodec::H264 },
+ { "; codecs=\"hvc1\"", QMediaFormat::VideoCodec::H265 },
+ { "; codecs=\"vp09\"", QMediaFormat::VideoCodec::VP9 },
+ { "; codecs=\"av01\"", QMediaFormat::VideoCodec::AV1 }, // ### ????
+ { "; codecs=\"jpeg\"", QMediaFormat::VideoCodec::MotionJPEG },
+ { nullptr, QMediaFormat::VideoCodec::Unspecified }
+};
+
+static struct {
+ const char *name;
+ QMediaFormat::AudioCodec value;
+} audioCodecMap[] = {
+ // AudioFile.h
+ // ### The next two entries do not work, probably because they contain non a space and period and AVFoundation doesn't like that
+ // We know they are supported on all Apple platforms, so we'll add them manually below
+// { "; codecs=\".mp3\"", QMediaFormat::AudioCodec::MP3 },
+// { "; codecs=\"aac \"", QMediaFormat::AudioCodec::AAC },
+ { "; codecs=\"ac-3\"", QMediaFormat::AudioCodec::AC3 },
+ { "; codecs=\"ec-3\"", QMediaFormat::AudioCodec::EAC3 },
+ { "; codecs=\"flac\"", QMediaFormat::AudioCodec::FLAC },
+ { "; codecs=\"alac\"", QMediaFormat::AudioCodec::ALAC },
+ { "; codecs=\"opus\"", QMediaFormat::AudioCodec::Opus },
+ { nullptr, QMediaFormat::AudioCodec::Unspecified },
+};
+
+QDarwinFormatInfo::QDarwinFormatInfo()
+{
+ auto avtypes = [AVURLAsset audiovisualMIMETypes];
+ for (AVFileType filetype in avtypes) {
+ auto *m = mediaContainerMap;
+ while (m->name) {
+ if (strcmp(filetype.UTF8String, m->name)) {
+ ++m;
+ continue;
+ }
+
+ QList<QMediaFormat::VideoCodec> video;
+ QList<QMediaFormat::AudioCodec> audio;
+
+ auto *v = videoCodecMap;
+ while (v->name) {
+ QByteArray extendedMimetype = m->name;
+ extendedMimetype += v->name;
+ if ([AVURLAsset isPlayableExtendedMIMEType:[NSString stringWithUTF8String:extendedMimetype.constData()]])
+ video << v->value;
+ ++v;
+ }
+
+ auto *a = audioCodecMap;
+ while (a->name) {
+ QByteArray extendedMimetype = m->name;
+ extendedMimetype += a->name;
+ if ([AVURLAsset isPlayableExtendedMIMEType:[NSString stringWithUTF8String:extendedMimetype.constData()]])
+ audio << a->value;
+ ++a;
+ }
+ // Added manually, see comment in the list above
+ if (m->value <= QMediaFormat::AAC)
+ audio << QMediaFormat::AudioCodec::AAC;
+ if (m->value < QMediaFormat::AAC || m->value == QMediaFormat::MP3)
+ audio << QMediaFormat::AudioCodec::MP3;
+
+ decoders << CodecMap{ m->value, audio, video };
+ ++m;
+ }
+ }
+
+ // seems AVFoundation only supports those for encoding
+ encoders = {
+ { QMediaFormat::MPEG4,
+ { QMediaFormat::AudioCodec::AAC, QMediaFormat::AudioCodec::ALAC },
+ { QMediaFormat::VideoCodec::H264, QMediaFormat::VideoCodec::H265, QMediaFormat::VideoCodec::MotionJPEG } },
+ { QMediaFormat::QuickTime,
+ { QMediaFormat::AudioCodec::AAC, QMediaFormat::AudioCodec::ALAC },
+ { QMediaFormat::VideoCodec::H264, QMediaFormat::VideoCodec::H265, QMediaFormat::VideoCodec::MotionJPEG } },
+ { QMediaFormat::Mpeg4Audio,
+ { QMediaFormat::AudioCodec::AAC },
+ {} },
+ { QMediaFormat::Wave,
+ { QMediaFormat::AudioCodec::Wave },
+ {} },
+ };
+
+ // ###
+ imageFormats << QImageCapture::JPEG;
+}
+
+QDarwinFormatInfo::~QDarwinFormatInfo()
+{
+}
+
+int QDarwinFormatInfo::audioFormatForCodec(QMediaFormat::AudioCodec codec)
+{
+ int codecId = kAudioFormatMPEG4AAC;
+ switch (codec) {
+ case QMediaFormat::AudioCodec::Unspecified:
+ case QMediaFormat::AudioCodec::DolbyTrueHD:
+ case QMediaFormat::AudioCodec::Vorbis:
+ case QMediaFormat::AudioCodec::WMA:
+ // Unsupported, shouldn't happen. Fall back to AAC
+ case QMediaFormat::AudioCodec::AAC:
+ codecId = kAudioFormatMPEG4AAC;
+ break;
+ case QMediaFormat::AudioCodec::MP3:
+ codecId = kAudioFormatMPEGLayer3;
+ break;
+ case QMediaFormat::AudioCodec::AC3:
+ codecId = kAudioFormatAC3;
+ break;
+ case QMediaFormat::AudioCodec::EAC3:
+ codecId = kAudioFormatEnhancedAC3;
+ break;
+ case QMediaFormat::AudioCodec::FLAC:
+ codecId = kAudioFormatFLAC;
+ break;
+ case QMediaFormat::AudioCodec::ALAC:
+ codecId = kAudioFormatAppleLossless;
+ break;
+ case QMediaFormat::AudioCodec::Opus:
+ codecId = kAudioFormatOpus;
+ break;
+ case QMediaFormat::AudioCodec::Wave:
+ codecId = kAudioFormatLinearPCM;
+ }
+ return codecId;
+}
+
+NSString *QDarwinFormatInfo::videoFormatForCodec(QMediaFormat::VideoCodec codec)
+{
+ const char *c = "hvc1"; // fallback is H265
+ switch (codec) {
+ case QMediaFormat::VideoCodec::Unspecified:
+ case QMediaFormat::VideoCodec::VP8:
+ case QMediaFormat::VideoCodec::H265:
+ case QMediaFormat::VideoCodec::AV1:
+ case QMediaFormat::VideoCodec::Theora:
+ case QMediaFormat::VideoCodec::WMV:
+ break;
+
+ case QMediaFormat::VideoCodec::MPEG1:
+ c = "mp1v";
+ break;
+ case QMediaFormat::VideoCodec::MPEG2:
+ c = "mp2v";
+ break;
+ case QMediaFormat::VideoCodec::MPEG4:
+ c = "mp4v";
+ break;
+ case QMediaFormat::VideoCodec::H264:
+ c = "avc1";
+ break;
+ case QMediaFormat::VideoCodec::VP9:
+ c = "vp09";
+ break;
+ case QMediaFormat::VideoCodec::MotionJPEG:
+ c = "jpeg";
+ }
+ return [NSString stringWithUTF8String:c];
+}
+
+NSString *QDarwinFormatInfo::avFileTypeForContainerFormat(QMediaFormat::FileFormat container)
+{
+ switch (container) {
+ case QMediaFormat::MPEG4:
+ return AVFileTypeMPEG4;
+ case QMediaFormat::QuickTime:
+ return AVFileTypeQuickTimeMovie;
+ case QMediaFormat::MP3:
+ return AVFileTypeMPEGLayer3;
+ case QMediaFormat::Mpeg4Audio:
+ return AVFileTypeAppleM4A;
+ case QMediaFormat::Wave:
+ return AVFileTypeWAVE;
+ default:
+ return AVFileTypeQuickTimeMovie;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/darwin/qdarwinformatsinfo_p.h b/src/plugins/multimedia/darwin/qdarwinformatsinfo_p.h
new file mode 100644
index 000000000..e01486286
--- /dev/null
+++ b/src/plugins/multimedia/darwin/qdarwinformatsinfo_p.h
@@ -0,0 +1,38 @@
+// 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
+
+//
+// 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/qplatformmediaformatinfo_p.h>
+#include <qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDarwinMediaDevices;
+
+class QDarwinFormatInfo : public QPlatformMediaFormatInfo
+{
+public:
+ QDarwinFormatInfo();
+ ~QDarwinFormatInfo();
+
+ static int audioFormatForCodec(QMediaFormat::AudioCodec codec);
+ static NSString *videoFormatForCodec(QMediaFormat::VideoCodec codec);
+ static NSString *avFileTypeForContainerFormat(QMediaFormat::FileFormat fileType);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/darwin/qdarwinintegration.mm b/src/plugins/multimedia/darwin/qdarwinintegration.mm
new file mode 100644
index 000000000..0e880447e
--- /dev/null
+++ b/src/plugins/multimedia/darwin/qdarwinintegration.mm
@@ -0,0 +1,93 @@
+// 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>
+#include <avfcameraservice_p.h>
+#include <avfcamera_p.h>
+#include <avfimagecapture_p.h>
+#include <avfmediaencoder_p.h>
+#include <qdarwinformatsinfo_p.h>
+#include <avfvideosink_p.h>
+#include <avfaudiodecoder_p.h>
+#include <VideoToolbox/VideoToolbox.h>
+#include <qdebug.h>
+#include <private/qplatformmediaplugin_p.h>
+#include <qavfcamerabase_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDarwinMediaPlugin : public QPlatformMediaPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QPlatformMediaPlugin_iid FILE "darwin.json")
+
+public:
+ QDarwinMediaPlugin()
+ : QPlatformMediaPlugin()
+ {}
+
+ QPlatformMediaIntegration* create(const QString &name) override
+ {
+ if (name == u"darwin")
+ return new QDarwinIntegration;
+ return nullptr;
+ }
+};
+
+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
+}
+
+QPlatformMediaFormatInfo *QDarwinIntegration::createFormatInfo()
+{
+ return new QDarwinFormatInfo();
+}
+
+QPlatformVideoDevices *QDarwinIntegration::createVideoDevices()
+{
+ return new QAVFVideoDevices(this);
+}
+
+QMaybe<QPlatformAudioDecoder *> QDarwinIntegration::createAudioDecoder(QAudioDecoder *decoder)
+{
+ return new AVFAudioDecoder(decoder);
+}
+
+QMaybe<QPlatformMediaCaptureSession *> QDarwinIntegration::createCaptureSession()
+{
+ return new AVFCameraService;
+}
+
+QMaybe<QPlatformMediaPlayer *> QDarwinIntegration::createPlayer(QMediaPlayer *player)
+{
+ return new AVFMediaPlayer(player);
+}
+
+QMaybe<QPlatformCamera *> QDarwinIntegration::createCamera(QCamera *camera)
+{
+ return new AVFCamera(camera);
+}
+
+QMaybe<QPlatformMediaRecorder *> QDarwinIntegration::createRecorder(QMediaRecorder *recorder)
+{
+ return new AVFMediaEncoder(recorder);
+}
+
+QMaybe<QPlatformImageCapture *> QDarwinIntegration::createImageCapture(QImageCapture *imageCapture)
+{
+ return new AVFImageCapture(imageCapture);
+}
+
+QMaybe<QPlatformVideoSink *> QDarwinIntegration::createVideoSink(QVideoSink *sink)
+{
+ return new AVFVideoSink(sink);
+}
+
+QT_END_NAMESPACE
+
+#include "qdarwinintegration.moc"
diff --git a/src/plugins/multimedia/darwin/qdarwinintegration_p.h b/src/plugins/multimedia/darwin/qdarwinintegration_p.h
new file mode 100644
index 000000000..8333de4ec
--- /dev/null
+++ b/src/plugins/multimedia/darwin/qdarwinintegration_p.h
@@ -0,0 +1,45 @@
+// 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
+
+//
+// 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>
+
+Q_FORWARD_DECLARE_OBJC_CLASS(NSObject);
+
+QT_BEGIN_NAMESPACE
+
+class QDarwinIntegration : public QPlatformMediaIntegration
+{
+public:
+ QDarwinIntegration();
+
+ 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;
+
+ QMaybe<QPlatformVideoSink *> createVideoSink(QVideoSink *) override;
+
+protected:
+ QPlatformMediaFormatInfo *createFormatInfo() override;
+ QPlatformVideoDevices *createVideoDevices() override;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/CMakeLists.txt b/src/plugins/multimedia/ffmpeg/CMakeLists.txt
new file mode 100644
index 000000000..c6ab93273
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/CMakeLists.txt
@@ -0,0 +1,260 @@
+# 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::VAAPI MODULE_NAME multimedia QMAKE_LIB vaapi)
+
+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 ffmpegmediaplugin
+ PLUGIN_TYPE multimedia
+ SOURCES
+ 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
+ qffmpegconverter.cpp qffmpegconverter_p.h
+ qffmpeghwaccel.cpp qffmpeghwaccel_p.h
+ qffmpegmediametadata.cpp qffmpegmediametadata_p.h
+ qffmpegmediaplayer.cpp qffmpegmediaplayer_p.h
+ qffmpegvideosink.cpp qffmpegvideosink_p.h
+ qffmpegmediaformatinfo.cpp qffmpegmediaformatinfo_p.h
+ qffmpegmediaintegration.cpp qffmpegmediaintegration_p.h
+ qffmpegvideobuffer.cpp qffmpegvideobuffer_p.h
+ qffmpegimagecapture.cpp qffmpegimagecapture_p.h
+ qffmpegmediacapturesession.cpp qffmpegmediacapturesession_p.h
+ qffmpegmediarecorder.cpp qffmpegmediarecorder_p.h
+ qffmpegthread.cpp qffmpegthread_p.h
+ qffmpegresampler.cpp qffmpegresampler_p.h
+ qffmpegencodingformatcontext.cpp qffmpegencodingformatcontext_p.h
+ qgrabwindowsurfacecapture.cpp qgrabwindowsurfacecapture_p.h
+ qffmpegsurfacecapturegrabber.cpp qffmpegsurfacecapturegrabber_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/qffmpegencodinginitializer_p.h
+ recordingengine/qffmpegencodinginitializer.cpp
+ recordingengine/qffmpegrecordingengineutils_p.h
+ recordingengine/qffmpegrecordingengineutils.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
+)
+
+if (LINUX OR ANDROID)
+ # We have 2 options: link shared stubs to QFFmpegMediaPlugin vs
+ # static compilation of the needed stubs to the FFmpeg plugin.
+ # Currently, we chose the second option so that user could trivially
+ # remove the FFmpeg libs we ship.
+ # Set QT_LINK_STUBS_TO_FFMPEG_PLUGIN = TRUE to change the behavior.
+
+ # set(QT_LINK_STUBS_TO_FFMPEG_PLUGIN TRUE)
+
+ include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/QtAddFFmpegStubs.cmake")
+ qt_internal_multimedia_add_ffmpeg_stubs()
+endif()
+
+
+if (QT_FEATURE_vaapi)
+ qt_internal_extend_target(QFFmpegMediaPlugin
+ 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
+ EGL::EGL
+ )
+
+ list(FIND FFMPEG_STUBS "va" va_stub_index)
+ if (NOT QT_LINK_STUBS_TO_FFMPEG_PLUGIN AND (FFMPEG_SHARED_LIBRARIES OR ${va_stub_index} EQUAL -1))
+ target_compile_definitions(QFFmpegMediaPlugin PRIVATE Q_FFMPEG_PLUGIN_STUBS_ONLY)
+ qt_internal_multimedia_find_vaapi_soversion()
+ qt_internal_multimedia_add_private_stub_to_plugin("va")
+ endif()
+endif()
+
+
+qt_internal_extend_target(QFFmpegMediaPlugin CONDITION APPLE
+ SOURCES
+ ../darwin/qavfhelpers.mm ../darwin/qavfhelpers_p.h
+ ../darwin/camera/qavfcamerabase_p.h ../darwin/camera/qavfcamerabase.mm
+ ../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
+ LIBRARIES
+ ${FWAudioToolbox}
+ ${FWCoreAudio}
+ ${FWCoreFoundation}
+ ${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
+ ${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.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 AND NOT UIKIT)
+ 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 OR UIKIT))
+ 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/QtAddFFmpegStubs.cmake b/src/plugins/multimedia/ffmpeg/cmake/QtAddFFmpegStubs.cmake
new file mode 100644
index 000000000..5778ae4d2
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/cmake/QtAddFFmpegStubs.cmake
@@ -0,0 +1,199 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+# Utilities
+
+function(qt_internal_multimedia_find_ffmpeg_stubs)
+ foreach (stub ${FFMPEG_STUBS})
+ if (${stub} MATCHES ${vaapi_regex})
+ set(ffmpeg_has_vaapi TRUE PARENT_SCOPE)
+ elseif (${stub} MATCHES ${openssl_regex})
+ set(ffmpeg_has_openssl TRUE PARENT_SCOPE)
+ else()
+ set(unknown_ffmpeg_stubs
+ ${unknown_ffmpeg_stubs} ${stub} PARENT_SCOPE)
+ endif()
+ endforeach()
+endfunction()
+
+function(qt_internal_multimedia_check_ffmpeg_stubs_configuration)
+ if (NOT LINUX AND NOT ANDROID)
+ message(FATAL_ERROR "Currently, stubs are supported on Linux and Android")
+ endif()
+
+ if (unknown_ffmpeg_stubs)
+ message(FATAL_ERROR "Unknown ffmpeg stubs: ${unknown_ffmpeg_stubs}")
+ endif()
+
+ if (BUILD_SHARED_LIBS AND FFMPEG_SHARED_LIBRARIES AND FFMPEG_STUBS AND NOT QT_DEPLOY_FFMPEG)
+ message(FATAL_ERROR
+ "FFmpeg stubs have been found but QT_DEPLOY_FFMPEG is not specified. "
+ "Set -DQT_DEPLOY_FFMPEG=TRUE to continue.")
+ endif()
+
+ if (ffmpeg_has_vaapi AND NOT QT_FEATURE_vaapi)
+ message(FATAL_ERROR
+ "QT_FEATURE_vaapi is OFF but FFmpeg includes VAAPI.")
+ elseif (NOT ffmpeg_has_vaapi AND QT_FEATURE_vaapi)
+ message(WARNING
+ "QT_FEATURE_vaapi is ON "
+ "but FFmpeg includes VAAPI and dynamic symbols resolve is enabled.")
+ elseif(ffmpeg_has_vaapi AND NOT VAAPI_SUFFIX)
+ message(FATAL_ERROR "Cannot find VAAPI_SUFFIX, fix FindVAAPI.cmake")
+ elseif (ffmpeg_has_vaapi AND "${VAAPI_SUFFIX}" MATCHES "^1\\.32.*")
+ # drop the ancient vaapi version to avoid ABI problems
+ message(FATAL_ERROR "VAAPI ${VAAPI_SUFFIX} is not supported")
+ endif()
+
+ if (ffmpeg_has_openssl AND NOT QT_FEATURE_openssl)
+ message(FATAL_ERROR
+ "QT_FEATURE_openssl is OFF but FFmpeg includes OpenSSL.")
+ endif()
+endfunction()
+
+macro(qt_internal_multimedia_find_vaapi_soversion)
+ string(REGEX MATCH "^[0-9]+" va_soversion "${VAAPI_SUFFIX}")
+
+ set(va-drm_soversion "${va_soversion}")
+ set(va-x11_soversion "${va_soversion}")
+endmacro()
+
+macro(qt_internal_multimedia_find_openssl_soversion)
+ # Update OpenSSL variables since OPENSSL_SSL_LIBRARY is not propagated to this place in some cases.
+ qt_find_package(OpenSSL)
+
+ if (NOT OPENSSL_INCLUDE_DIR AND OPENSSL_ROOT_DIR)
+ set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/include")
+ endif()
+
+ if (LINUX)
+ if (NOT OPENSSL_SSL_LIBRARY)
+ message(FATAL_ERROR "OPENSSL_SSL_LIBRARY is not found")
+ endif()
+
+ get_filename_component(ssl_lib_realpath "${OPENSSL_SSL_LIBRARY}" REALPATH)
+ string(REGEX MATCH "[0-9]+(\\.[0-9]+)*$" ssl_soversion "${ssl_lib_realpath}")
+ string(REGEX REPLACE "^3(\\..*|$)" "3" ssl_soversion "${ssl_soversion}")
+ endif()
+
+ #TODO: enhance finding openssl version and throw an error if it's not found.
+
+ set(crypto_soversion "${ssl_soversion}")
+endmacro()
+
+function(qt_internal_multimedia_set_stub_version_script stub stub_target)
+ if ("${stub}" MATCHES "${openssl_regex}")
+ if ("${ssl_soversion}" STREQUAL "3" OR
+ (NOT ssl_soversion AND "${OPENSSL_VERSION}" MATCHES "^3\\..*"))
+ # Symbols in OpenSSL 1.* are not versioned.
+ set(file_name "openssl3.ver")
+ endif()
+ elseif("${stub}" STREQUAL "va")
+ set(file_name "va.ver")
+ endif()
+
+ if (file_name)
+ set(version_script "${CMAKE_CURRENT_SOURCE_DIR}/symbolstubs/${file_name}")
+ set_property(TARGET ${stub_target} APPEND_STRING
+ PROPERTY LINK_FLAGS " -Wl,--version-script=${version_script}")
+ set_target_properties(${stub_target} PROPERTIES LINK_DEPENDS ${version_script})
+ source_group("Stubs Version Scripts" FILES ${version_script})
+ endif()
+endfunction()
+
+function(qt_internal_multimedia_set_stub_output stub stub_target)
+ set(output_dir "${QT_BUILD_DIR}/${INSTALL_LIBDIR}")
+
+ set_target_properties(${stub_target} PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${output_dir}"
+ LIBRARY_OUTPUT_DIRECTORY "${output_dir}"
+ )
+
+ if (${stub}_soversion)
+ set_target_properties(${stub_target} PROPERTIES
+ VERSION "${${stub}_soversion}"
+ SOVERSION "${${stub}_soversion}")
+ endif()
+
+ qt_apply_rpaths(TARGET ${stub_target} INSTALL_PATH "${INSTALL_LIBDIR}" RELATIVE_RPATH)
+endfunction()
+
+function(qt_internal_multimedia_set_stub_include_directories stub target)
+ qt_internal_extend_target(${target}
+ CONDITION ${stub} MATCHES "${openssl_regex}"
+ INCLUDE_DIRECTORIES "${OPENSSL_INCLUDE_DIR}")
+
+ qt_internal_extend_target(${target}
+ CONDITION ${stub} MATCHES "${vaapi_regex}"
+ INCLUDE_DIRECTORIES "${VAAPI_INCLUDE_DIR}")
+endfunction()
+
+function(qt_internal_multimedia_set_stub_symbols_visibility stub stub_target)
+ set_target_properties(${stub_target} PROPERTIES
+ C_VISIBILITY_PRESET hidden
+ CXX_VISIBILITY_PRESET hidden)
+ target_compile_definitions(${stub_target} PRIVATE Q_EXPORT_STUB_SYMBOLS)
+endfunction()
+
+function(qt_internal_multimedia_set_stub_libraries stub stub_target)
+ qt_internal_extend_target(${stub_target} LIBRARIES Qt::Core Qt::MultimediaPrivate)
+
+ if (LINK_STUBS_TO_FFMPEG_PLUGIN AND ${stub} STREQUAL "va")
+ qt_internal_extend_target(QFFmpegMediaPlugin LIBRARIES ${stub_target})
+ endif()
+endfunction()
+
+function(qt_internal_multimedia_define_stub_needed_version stub target)
+ string(TOUPPER ${stub} prefix)
+ string(REPLACE "-" "_" prefix ${prefix})
+
+ target_compile_definitions(${target} PRIVATE
+ "${prefix}_NEEDED_SOVERSION=\"${${stub}_soversion}\"")
+endfunction()
+
+function(qt_internal_multimedia_add_shared_stub stub)
+ set(stub_target "Qt${PROJECT_VERSION_MAJOR}FFmpegStub-${stub}")
+
+ qt_add_library(${stub_target} SHARED "symbolstubs/qffmpegsymbols-${stub}.cpp")
+
+ qt_internal_multimedia_set_stub_include_directories(${stub} ${stub_target})
+ qt_internal_multimedia_set_stub_output(${stub} ${stub_target})
+ qt_internal_multimedia_set_stub_symbols_visibility(${stub} ${stub_target})
+ qt_internal_multimedia_set_stub_version_script(${stub} ${stub_target})
+ qt_internal_multimedia_define_stub_needed_version(${stub} ${stub_target})
+ qt_internal_multimedia_set_stub_libraries(${stub} ${stub_target})
+
+ qt_install(TARGETS ${stub_target} LIBRARY NAMELINK_SKIP)
+endfunction()
+
+function(qt_internal_multimedia_add_private_stub_to_plugin stub)
+ qt_internal_multimedia_set_stub_include_directories(${stub} QFFmpegMediaPlugin)
+ qt_internal_multimedia_define_stub_needed_version(${stub} QFFmpegMediaPlugin)
+ qt_internal_extend_target(QFFmpegMediaPlugin SOURCES "symbolstubs/qffmpegsymbols-${stub}.cpp")
+endfunction()
+
+# Main function
+
+set(vaapi_regex "^(va|va-drm|va-x11)$")
+set(openssl_regex "^(ssl|crypto)$")
+
+function(qt_internal_multimedia_add_ffmpeg_stubs)
+ qt_internal_multimedia_find_ffmpeg_stubs()
+ qt_internal_multimedia_check_ffmpeg_stubs_configuration()
+
+ if (ffmpeg_has_vaapi)
+ qt_internal_multimedia_find_vaapi_soversion()
+ endif()
+
+ if (ffmpeg_has_openssl)
+ qt_internal_multimedia_find_openssl_soversion()
+ endif()
+
+ foreach (stub ${FFMPEG_STUBS})
+ if (FFMPEG_SHARED_LIBRARIES)
+ qt_internal_multimedia_add_shared_stub("${stub}")
+ else()
+ qt_internal_multimedia_add_private_stub_to_plugin("${stub}")
+ endif()
+ endforeach()
+endfunction()
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/ffmpeg.json b/src/plugins/multimedia/ffmpeg/ffmpeg.json
new file mode 100644
index 000000000..d8e7e4456
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/ffmpeg.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "ffmpeg" ]
+}
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegaudiorenderer.cpp b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegaudiorenderer.cpp
new file mode 100644
index 000000000..64bd82dc0
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegaudiorenderer.cpp
@@ -0,0 +1,407 @@
+// 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 "qaudiobufferoutput.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;
+}
+
+QAudioFormat audioFormatFromFrame(const Frame &frame)
+{
+ return QFFmpegMediaFormatInfo::audioFormatFromCodecParameters(
+ frame.codec()->stream()->codecpar);
+}
+
+std::unique_ptr<QFFmpegResampler> createResampler(const Frame &frame,
+ const QAudioFormat &outputFormat)
+{
+ return std::make_unique<QFFmpegResampler>(frame.codec(), outputFormat, frame.pts());
+}
+
+} // namespace
+
+AudioRenderer::AudioRenderer(const TimeController &tc, QAudioOutput *output,
+ QAudioBufferOutput *bufferOutput)
+ : Renderer(tc), m_output(output), m_bufferOutput(bufferOutput)
+{
+ 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(); });
+}
+
+void AudioRenderer::setOutput(QAudioBufferOutput *bufferOutput)
+{
+ setOutputInternal(m_bufferOutput, bufferOutput,
+ [this](QAudioBufferOutput *) { m_bufferOutputChanged = true; });
+}
+
+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())
+ updateOutputs(frame);
+
+ // push to sink first in order not to waste time on resampling
+ // for QAudioBufferOutput
+ const RenderingResult result = pushFrameToOutput(frame);
+
+ if (m_lastFramePushDone)
+ pushFrameToBufferOutput(frame);
+ // else // skip pushing the same data to QAudioBufferOutput
+
+ m_lastFramePushDone = result.done;
+
+ return result;
+}
+
+AudioRenderer::RenderingResult AudioRenderer::pushFrameToOutput(const Frame &frame)
+{
+ if (!m_ioDevice || !m_resampler)
+ return {};
+
+ Q_ASSERT(m_sink);
+
+ auto firstFrameFlagGuard = qScopeGuard([&]() { m_firstFrameToSink = 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::pushFrameToBufferOutput(const Frame &frame)
+{
+ if (!m_bufferOutput)
+ return;
+
+ Q_ASSERT(m_bufferOutputResampler);
+
+ if (frame.isValid()) {
+ // TODO: get buffer from m_bufferedData if resample formats are equal
+ QAudioBuffer buffer = m_resampler->resample(frame.avFrame());
+ emit m_bufferOutput->audioBufferReceived(buffer);
+ } else {
+ emit m_bufferOutput->audioBufferReceived({});
+ }
+}
+
+void AudioRenderer::onPlaybackRateChanged()
+{
+ m_resampler.reset();
+}
+
+int AudioRenderer::timerInterval() const
+{
+ constexpr auto MaxFixableInterval = 50; // ms
+
+ const auto interval = Renderer::timerInterval();
+
+ if (m_firstFrameToSink || !m_sink || m_sink->state() != QAudio::IdleState
+ || interval > MaxFixableInterval)
+ return interval;
+
+ return 0;
+}
+
+void AudioRenderer::onPauseChanged()
+{
+ m_firstFrameToSink = true;
+ Renderer::onPauseChanged();
+}
+
+void AudioRenderer::initResempler(const Frame &frame)
+{
+ // We recreate resampler whenever format is changed
+
+ auto resamplerFormat = m_sinkFormat;
+ resamplerFormat.setSampleRate(
+ qRound(m_sinkFormat.sampleRate() / playbackRate() * sampleRateFactor()));
+ m_resampler = createResampler(frame, 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_sinkFormat = {};
+ m_timings = {};
+ m_bufferLoadingInfo = {};
+}
+
+void AudioRenderer::updateOutputs(const Frame &frame)
+{
+ if (m_deviceChanged) {
+ freeOutput();
+ m_resampler.reset();
+ }
+
+ if (m_bufferOutput) {
+ if (m_bufferOutputChanged) {
+ m_bufferOutputChanged = false;
+ m_bufferOutputResampler.reset();
+ }
+
+ if (!m_bufferOutputResampler) {
+ QAudioFormat outputFormat = m_bufferOutput->format();
+ if (!outputFormat.isValid())
+ outputFormat = audioFormatFromFrame(frame);
+ m_bufferOutputResampler = createResampler(frame, outputFormat);
+ }
+ }
+
+ if (!m_output)
+ return;
+
+ if (!m_sinkFormat.isValid()) {
+ m_sinkFormat = audioFormatFromFrame(frame);
+ m_sinkFormat.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_sinkFormat);
+ updateVolume();
+ m_sink->setBufferSize(m_sinkFormat.bytesForDuration(DesiredBufferTime.count()));
+ m_ioDevice = m_sink->start();
+ m_firstFrameToSink = 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(frame);
+}
+
+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_firstFrameToSink
+ << "\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_firstFrameToSink && 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_firstFrameToSink)
+ scheduleNextStep();
+}
+
+microseconds AudioRenderer::durationForBytes(qsizetype bytes) const
+{
+ return microseconds(m_sinkFormat.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..9a22a8a48
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegaudiorenderer_p.h
@@ -0,0 +1,132 @@
+// 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 QAudioBufferOutput;
+class QAudioSink;
+class QFFmpegResampler;
+
+namespace QFFmpeg {
+
+class AudioRenderer : public Renderer
+{
+ Q_OBJECT
+public:
+ AudioRenderer(const TimeController &tc, QAudioOutput *output, QAudioBufferOutput *bufferOutput);
+
+ void setOutput(QAudioOutput *output);
+
+ void setOutput(QAudioBufferOutput *bufferOutput);
+
+ ~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;
+
+ RenderingResult pushFrameToOutput(const Frame &frame);
+
+ void pushFrameToBufferOutput(const Frame &frame);
+
+ void onPlaybackRateChanged() override;
+
+ int timerInterval() const override;
+
+ void onPauseChanged() override;
+
+ void freeOutput();
+
+ void updateOutputs(const Frame &frame);
+
+ void initResempler(const Frame &frame);
+
+ 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;
+ QPointer<QAudioBufferOutput> m_bufferOutput;
+ std::unique_ptr<QAudioSink> m_sink;
+ AudioTimings m_timings;
+ BufferLoadingInfo m_bufferLoadingInfo;
+ std::unique_ptr<QFFmpegResampler> m_resampler;
+ std::unique_ptr<QFFmpegResampler> m_bufferOutputResampler;
+ QAudioFormat m_sinkFormat;
+
+ BufferedDataWithOffset m_bufferedData;
+ QIODevice *m_ioDevice = nullptr;
+
+ bool m_lastFramePushDone = true;
+
+ bool m_deviceChanged = false;
+ bool m_bufferOutputChanged = false;
+ bool m_drained = false;
+ bool m_firstFrameToSink = 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..f92f93ddb
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegmediadataholder.cpp
@@ -0,0 +1,390 @@
+// 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;
+}
+
+
+static bool colorTransferSupportsHdr(const AVStream *stream)
+{
+ if (!stream)
+ return false;
+
+ const AVCodecParameters *codecPar = stream->codecpar;
+ if (!codecPar)
+ return false;
+
+ const QVideoFrameFormat::ColorTransfer colorTransfer = fromAvColorTransfer(codecPar->color_trc);
+
+ // Assume that content is using HDR if the color transfer supports high
+ // dynamic range. The video may still not utilize the extended range,
+ // but we can't determine the actual range without decoding frames.
+ return colorTransfer == QVideoFrameFormat::ColorTransfer_ST2084
+ || colorTransfer == QVideoFrameFormat::ColorTransfer_STD_B67;
+}
+
+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)));
+ metaData.insert(QMediaMetaData::HasHdrContent, colorTransferSupportsHdr(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);
+
+ const QByteArray protocolWhitelist = qgetenv("QT_FFMPEG_PROTOCOL_WHITELIST");
+ if (!protocolWhitelist.isNull())
+ av_dict_set(dict, "protocol_whitelist", protocolWhitelist.data(), 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..dceb00f83
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegvideorenderer.cpp
@@ -0,0 +1,79 @@
+// 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"
+#include "private/qvideoframe_p.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 = QVideoFramePrivate::createFrame(std::move(buffer), 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..56725b2bb
--- /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()) {
+ updateError(QCamera::CameraError, QStringLiteral("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);
+ updateError(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;
+
+ updateError(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)
+{
+ updateError(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);
+
+ updateError(QCamera::CameraError,
+ QStringLiteral("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..28d02b20e
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qandroidcameraframe.cpp
@@ -0,0 +1,224 @@
+// 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")
+Q_DECLARE_JNI_CLASS(QtVideoDeviceManager,
+ "org/qtproject/qt/android/multimedia/QtVideoDeviceManager");
+
+QT_BEGIN_NAMESPACE
+static Q_LOGGING_CATEGORY(qLCAndroidCameraFrame, "qt.multimedia.ffmpeg.android.camera.frame");
+
+namespace {
+bool isWorkaroundForEmulatorNeeded() {
+ const static bool workaroundForEmulator
+ = QtJniTypes::QtVideoDeviceManager::callStaticMethod<jboolean>("isEmulator");
+ return workaroundForEmulator;
+}
+}
+
+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];
+ };
+
+ int width = frame.callMethod<jint>("getWidth");
+ int height = frame.callMethod<jint>("getHeight");
+ m_size = QSize(width, height);
+
+ switch (calculedPixelFormat) {
+ case QVideoFrameFormat::Format_YUV420P:
+ m_numberPlanes = 3;
+ copyPlane(0, 0);
+ copyPlane(1, 1);
+ copyPlane(2, 2);
+
+ if (isWorkaroundForEmulatorNeeded()) {
+ for (int i = 0; i < 3; ++i) {
+ const int dataSize = (i == 0) ? width * height : width * height / 4;
+ m_planes[i].data = new uint8_t[dataSize];
+ memcpy(m_planes[i].data, buffer[i], dataSize);
+ }
+ }
+
+ 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;
+
+ 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);
+
+ if (isWorkaroundForEmulatorNeeded()) {
+ if (m_pixelFormat == QVideoFrameFormat::Format_YUV420P) {
+ for (int i = 0; i < 3; ++i)
+ delete[] m_planes[i].data;
+ }
+ }
+}
+
+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
new file mode 100644
index 000000000..891c4b376
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qavfcamera.mm
@@ -0,0 +1,349 @@
+// 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 <rhi/qrhi.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qpermissions.h>
+#define AVMediaType XAVMediaType
+#include "qffmpegvideobuffer_p.h"
+#include "qffmpegvideosink_p.h"
+extern "C" {
+#include <libavutil/hwcontext_videotoolbox.h>
+#include <libavutil/hwcontext.h>
+}
+#undef AVMediaType
+
+QT_BEGIN_NAMESPACE
+
+using namespace QFFmpeg;
+
+QAVFCamera::QAVFCamera(QCamera *parent)
+ : QAVFCameraBase(parent)
+{
+ m_captureSession = [[AVCaptureSession alloc] init];
+ m_sampleBufferDelegate = [[QAVFSampleBufferDelegate alloc]
+ initWithFrameHandler:[this](const QVideoFrame &frame) { syncHandleFrame(frame); }];
+}
+
+QAVFCamera::~QAVFCamera()
+{
+ [m_sampleBufferDelegate release];
+ [m_videoInput release];
+ [m_videoDataOutput release];
+ [m_captureSession release];
+}
+
+bool QAVFCamera::checkCameraPermission()
+{
+ const QCameraPermission permission;
+ const bool granted = qApp->checkPermission(permission) == Qt::PermissionStatus::Granted;
+ if (!granted)
+ qWarning() << "Access to camera not granted";
+
+ return granted;
+}
+
+void QAVFCamera::updateVideoInput()
+{
+ if (!checkCameraPermission())
+ return;
+
+ [m_captureSession beginConfiguration];
+
+ attachVideoInputDevice();
+
+ if (!m_videoDataOutput) {
+ m_videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
+
+ // Configure video output
+ m_delegateQueue = dispatch_queue_create("vf_queue", nullptr);
+ [m_videoDataOutput
+ setSampleBufferDelegate:m_sampleBufferDelegate
+ queue:m_delegateQueue];
+
+ [m_captureSession addOutput:m_videoDataOutput];
+ }
+ [m_captureSession commitConfiguration];
+ deviceOrientationChanged();
+}
+
+void QAVFCamera::deviceOrientationChanged(int angle)
+{
+ AVCaptureConnection *connection = [m_videoDataOutput connectionWithMediaType:AVMediaTypeVideo];
+ if (connection == nil || !m_videoDataOutput)
+ return;
+
+ if (!connection.supportsVideoOrientation)
+ return;
+
+ if (angle < 0)
+ angle = m_orientationHandler.currentOrientation();
+
+ AVCaptureVideoOrientation orientation = AVCaptureVideoOrientationPortrait;
+ switch (angle) {
+ default:
+ break;
+ case 90:
+ orientation = AVCaptureVideoOrientationLandscapeRight;
+ break;
+ case 180:
+ // this keeps the last orientation, don't do anything
+ return;
+ case 270:
+ orientation = AVCaptureVideoOrientationLandscapeLeft;
+ break;
+ }
+
+ connection.videoOrientation = orientation;
+}
+
+void QAVFCamera::attachVideoInputDevice()
+{
+ if (m_videoInput) {
+ [m_captureSession removeInput:m_videoInput];
+ [m_videoInput release];
+ m_videoInput = nullptr;
+ }
+
+ QByteArray deviceId = m_cameraDevice.id();
+ if (deviceId.isEmpty())
+ return;
+
+ AVCaptureDevice *videoDevice = [AVCaptureDevice deviceWithUniqueID:
+ [NSString stringWithUTF8String: deviceId.constData()]];
+
+ if (!videoDevice)
+ return;
+
+ m_videoInput = [AVCaptureDeviceInput
+ deviceInputWithDevice:videoDevice
+ error:nil];
+ if (m_videoInput && [m_captureSession canAddInput:m_videoInput]) {
+ [m_videoInput retain];
+ [m_captureSession addInput:m_videoInput];
+ } else {
+ qWarning() << "Failed to create video device input";
+ }
+}
+
+AVCaptureDevice *QAVFCamera::device() const
+{
+ return m_videoInput ? m_videoInput.device : nullptr;
+}
+
+bool QAVFCamera::isActive() const
+{
+ return m_active;
+}
+
+void QAVFCamera::setActive(bool active)
+{
+ if (m_active == active)
+ return;
+ if (!checkCameraPermission())
+ return;
+
+ m_active = active;
+
+ if (active) {
+ // According to the doc, the capture device must be locked before
+ // startRunning to prevent the format we set to be overridden by the
+ // session preset.
+ [m_videoInput.device lockForConfiguration:nil];
+ [m_captureSession startRunning];
+ [m_videoInput.device unlockForConfiguration];
+ } else {
+ [m_captureSession stopRunning];
+ }
+
+ emit activeChanged(active);
+}
+
+void QAVFCamera::setCaptureSession(QPlatformMediaCaptureSession *session)
+{
+ m_session = session ? session->captureSession() : nullptr;
+}
+
+void QAVFCamera::setCamera(const QCameraDevice &camera)
+{
+ if (m_cameraDevice == camera)
+ return;
+
+ m_cameraDevice = camera;
+
+ if (checkCameraPermission())
+ updateVideoInput();
+ setCameraFormat({});
+}
+
+bool QAVFCamera::setCameraFormat(const QCameraFormat &format)
+{
+ if (m_cameraFormat == format && !format.isNull())
+ return true;
+
+ if (!QAVFCameraBase::setCameraFormat(format))
+ return false;
+
+ updateCameraFormat();
+ return true;
+}
+
+void QAVFCamera::updateCameraFormat()
+{
+ m_framePixelFormat = QVideoFrameFormat::Format_Invalid;
+
+ AVCaptureDevice *captureDevice = device();
+ if (!captureDevice)
+ return;
+
+ 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);
+ 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?";
+ }
+
+ 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()];
+}
+
+uint32_t QAVFCamera::setPixelFormat(QVideoFrameFormat::PixelFormat cameraPixelFormat,
+ uint32_t inputCvPixFormat)
+{
+ 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 (!bestFormat) {
+ qWarning() << "QCamera::setCameraFormat: availableVideoCVPixelFormatTypes empty";
+ return 0;
+ }
+
+ 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
+{
+#ifdef Q_OS_MACOS
+ return m_cameraFormat.resolution();
+#else
+ // 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;
+#endif // Q_OS_MACOS
+}
+
+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
new file mode 100644
index 000000000..0c88c520c
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qavfcamera_p.h
@@ -0,0 +1,90 @@
+// 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
+
+//
+// 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 "qavfcamerabase_p.h"
+#include <private/qplatformmediaintegration_p.h>
+#include <private/qvideooutputorientationhandler_p.h>
+#define AVMediaType XAVMediaType
+#include "qffmpeghwaccel_p.h"
+#undef AVMediaType
+
+#include <qfilesystemwatcher.h>
+#include <qsocketnotifier.h>
+#include <qmutex.h>
+
+#include <dispatch/dispatch.h>
+
+Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureSession);
+Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureDeviceInput);
+Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureVideoDataOutput);
+Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureDevice);
+Q_FORWARD_DECLARE_OBJC_CLASS(QAVFSampleBufferDelegate);
+
+QT_BEGIN_NAMESPACE
+
+class QFFmpegVideoSink;
+
+class QAVFCamera : public QAVFCameraBase
+{
+ Q_OBJECT
+
+public:
+ explicit QAVFCamera(QCamera *parent);
+ ~QAVFCamera();
+
+ bool isActive() const override;
+ void setActive(bool active) override;
+
+ void setCaptureSession(QPlatformMediaCaptureSession *) override;
+
+ void setCamera(const QCameraDevice &camera) override;
+ bool setCameraFormat(const QCameraFormat &format) override;
+
+ void syncHandleFrame(const QVideoFrame &frame);
+
+ void deviceOrientationChanged(int angle = -1);
+
+ std::optional<int> ffmpegHWPixelFormat() const override;
+
+ int cameraPixelFormatScore(QVideoFrameFormat::PixelFormat pixelFmt,
+ QVideoFrameFormat::ColorRange colorRange) const override;
+
+private:
+ bool checkCameraPermission();
+ void updateCameraFormat();
+ void updateVideoInput();
+ void attachVideoInputDevice();
+ uint32_t setPixelFormat(QVideoFrameFormat::PixelFormat pixelFormat, uint32_t inputCvPixFormat);
+ QSize adjustedResolution() const;
+
+ AVCaptureDevice *device() const;
+
+ QMediaCaptureSession *m_session = nullptr;
+ AVCaptureSession *m_captureSession = nullptr;
+ AVCaptureDeviceInput *m_videoInput = nullptr;
+ AVCaptureVideoDataOutput *m_videoDataOutput = nullptr;
+ QAVFSampleBufferDelegate *m_sampleBufferDelegate = nullptr;
+ dispatch_queue_t m_delegateQueue;
+ QVideoOutputOrientationHandler m_orientationHandler;
+ AVPixelFormat m_hwPixelFormat = AV_PIX_FMT_NONE;
+};
+
+QT_END_NAMESPACE
+
+
+#endif // QFFMPEGCAMERA_H
+
diff --git a/src/plugins/multimedia/ffmpeg/qavfsamplebufferdelegate.mm b/src/plugins/multimedia/ffmpeg/qavfsamplebufferdelegate.mm
new file mode 100644
index 000000000..8a4c77a9e
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qavfsamplebufferdelegate.mm
@@ -0,0 +1,224 @@
+// 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"
+#include "private/qvideoframe_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) : m_buffer(imageBuffer)
+ {
+ CVPixelBufferRetain(imageBuffer);
+ }
+
+ ~CVImageVideoBuffer()
+ {
+ CVImageVideoBuffer::unmap();
+ CVPixelBufferRelease(m_buffer);
+ }
+
+ CVImageVideoBuffer::MapData map(QtVideo::MapMode mode) override
+ {
+ MapData mapData;
+
+ if (m_mode == QtVideo::MapMode::NotMapped) {
+ CVPixelBufferLockBaseAddress(
+ m_buffer, mode == QtVideo::MapMode::ReadOnly ? kCVPixelBufferLock_ReadOnly : 0);
+ m_mode = mode;
+ }
+
+ mapData.planeCount = CVPixelBufferGetPlaneCount(m_buffer);
+ Q_ASSERT(mapData.planeCount <= 3);
+
+ if (!mapData.planeCount) {
+ // single plane
+ mapData.bytesPerLine[0] = CVPixelBufferGetBytesPerRow(m_buffer);
+ mapData.data[0] = static_cast<uchar *>(CVPixelBufferGetBaseAddress(m_buffer));
+ mapData.dataSize[0] = CVPixelBufferGetDataSize(m_buffer);
+ mapData.planeCount = 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.planeCount; ++i) {
+ mapData.bytesPerLine[i] = CVPixelBufferGetBytesPerRowOfPlane(m_buffer, i);
+ mapData.dataSize[i] = mapData.bytesPerLine[i] * CVPixelBufferGetHeightOfPlane(m_buffer, i);
+ mapData.data[i] = static_cast<uchar *>(CVPixelBufferGetBaseAddressOfPlane(m_buffer, i));
+ }
+
+ return mapData;
+ }
+
+ void unmap() override
+ {
+ if (m_mode != QtVideo::MapMode::NotMapped) {
+ CVPixelBufferUnlockBaseAddress(
+ m_buffer, m_mode == QtVideo::MapMode::ReadOnly ? kCVPixelBufferLock_ReadOnly : 0);
+ m_mode = QtVideo::MapMode::NotMapped;
+ }
+ }
+
+ QVideoFrameFormat format() const override { return {}; }
+
+private:
+ CVImageBufferRef m_buffer;
+ QtVideo::MapMode m_mode = QtVideo::MapMode::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 QVideoFramePrivate::createFrame(std::make_unique<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 = QVideoFramePrivate::createFrame(std::make_unique<CVImageVideoBuffer>(imageBuffer),
+ std::move(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..6fa2f620f
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qcgwindowcapture.mm
@@ -0,0 +1,203 @@
+// 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 "qabstractvideobuffer.h"
+
+#include "qcgwindowcapture_p.h"
+#include "private/qcapturablewindow_p.h"
+#include "qffmpegsurfacecapturegrabber_p.h"
+#include "private/qvideoframe_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)
+ {
+ auto provider = CGImageGetDataProvider(image);
+ m_data = CGDataProviderCopyData(provider);
+ m_bytesPerLine = CGImageGetBytesPerRow(image);
+ }
+
+ ~QCGImageVideoBuffer() override { CFRelease(m_data); }
+
+ MapData map(QtVideo::MapMode mode) override
+ {
+ MapData mapData;
+ if (m_mapMode == QtVideo::MapMode::NotMapped) {
+ m_mapMode = mode;
+
+ mapData.planeCount = 1;
+ mapData.bytesPerLine[0] = static_cast<int>(m_bytesPerLine);
+ mapData.data[0] = (uchar *)CFDataGetBytePtr(m_data);
+ mapData.dataSize[0] = static_cast<int>(CFDataGetLength(m_data));
+ }
+
+ return mapData;
+ }
+
+ void unmap() override { m_mapMode = QtVideo::MapMode::NotMapped; }
+
+ QVideoFrameFormat format() const override { return {}; }
+
+private:
+ QtVideo::MapMode m_mapMode = QtVideo::MapMode::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 QVideoFramePrivate::createFrame(std::make_unique<QCGImageVideoBuffer>(imageRef),
+ std::move(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..871cafd4f
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qeglfsscreencapture.cpp
@@ -0,0 +1,180 @@
+// 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 "private/qvideoframe_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 QVideoFramePrivate::createFrame(std::move(videoBuffer), 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 QVideoFramePrivate::createFrame(
+ std::make_unique<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..ce7dfc682
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpeg.cpp
@@ -0,0 +1,645 @@
+// 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;
+
+ if (!codec->pix_fmts) {
+#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 (findAVPixelFormat(codec, &isHwPixelFormat) == AV_PIX_FMT_NONE)
+ return true;
+
+ if ((codec->capabilities & AV_CODEC_CAP_HARDWARE) == 0)
+ return true;
+
+ auto checkDeviceType = [codec](AVHWDeviceType type) {
+ return isAVFormatSupported(codec, 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)
+{
+ // TODO: remove deviceType and use only isAVFormatSupported to check the 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
+ // To be removed: only isAVFormatSupported should be used.
+ 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) {
+ auto checkFormat = [format](AVPixelFormat f) { return f == format; };
+ return findAVPixelFormat(codec, checkFormat) != AV_PIX_FMT_NONE;
+ }
+
+ 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);
+}
+
+QVideoFrameFormat::ColorTransfer fromAvColorTransfer(AVColorTransferCharacteristic colorTrc) {
+ switch (colorTrc) {
+ case AVCOL_TRC_BT709:
+ // The following three cases have transfer characteristics identical to BT709
+ case AVCOL_TRC_BT1361_ECG:
+ case AVCOL_TRC_BT2020_10:
+ case AVCOL_TRC_BT2020_12:
+ case AVCOL_TRC_SMPTE240M: // almost identical to bt709
+ return QVideoFrameFormat::ColorTransfer_BT709;
+ case AVCOL_TRC_GAMMA22:
+ case AVCOL_TRC_SMPTE428: // No idea, let's hope for the best...
+ case AVCOL_TRC_IEC61966_2_1: // sRGB, close enough to 2.2...
+ case AVCOL_TRC_IEC61966_2_4: // not quite, but probably close enough
+ return QVideoFrameFormat::ColorTransfer_Gamma22;
+ case AVCOL_TRC_GAMMA28:
+ return QVideoFrameFormat::ColorTransfer_Gamma28;
+ case AVCOL_TRC_SMPTE170M:
+ return QVideoFrameFormat::ColorTransfer_BT601;
+ case AVCOL_TRC_LINEAR:
+ return QVideoFrameFormat::ColorTransfer_Linear;
+ case AVCOL_TRC_SMPTE2084:
+ return QVideoFrameFormat::ColorTransfer_ST2084;
+ case AVCOL_TRC_ARIB_STD_B67:
+ return QVideoFrameFormat::ColorTransfer_STD_B67;
+ default:
+ break;
+ }
+ return QVideoFrameFormat::ColorTransfer_Unknown;
+}
+
+#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
new file mode 100644
index 000000000..09bf7e4f4
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpeg_p.h
@@ -0,0 +1,280 @@
+// 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
+
+//
+// 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 <QtMultimedia/qvideoframeformat.h>
+
+#include <qstring.h>
+#include <optional>
+
+inline bool operator==(const AVRational &lhs, const AVRational &rhs)
+{
+ return lhs.num == rhs.num && lhs.den == rhs.den;
+}
+
+inline bool operator!=(const AVRational &lhs, const AVRational &rhs)
+{
+ return !(lhs == rhs);
+}
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg
+{
+
+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 mul(1'000 * ts, base);
+}
+
+inline std::optional<qint64> timeStampUs(qint64 ts, AVRational base)
+{
+ return mul(1'000'000 * ts, base);
+}
+
+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)
+{
+ char buffer[AV_ERROR_MAX_STRING_SIZE + 1] = {};
+ av_make_error_string(buffer, AV_ERROR_MAX_STRING_SIZE, errnum);
+ return QString::fromLocal8Bit(buffer);
+}
+
+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 Predicate>
+const AVCodecHWConfig *findHwConfig(const AVCodec *codec, const Predicate &predicate)
+{
+ for (int i = 0; const auto hwConfig = avcodec_get_hw_config(codec, i); ++i) {
+ if (predicate(hwConfig))
+ return hwConfig;
+ }
+
+ return nullptr;
+}
+
+template <typename Predicate>
+AVPixelFormat findAVPixelFormat(const AVCodec *codec, const Predicate &predicate)
+{
+ const AVPixelFormat format = findAVFormat(codec->pix_fmts, predicate);
+ if (format != AV_PIX_FMT_NONE)
+ return format;
+
+ auto checkHwConfig = [&predicate](const AVCodecHWConfig *config) {
+ return config->pix_fmt != AV_PIX_FMT_NONE && predicate(config->pix_fmt);
+ };
+
+ if (auto hwConfig = findHwConfig(codec, checkHwConfig))
+ return hwConfig->pix_fmt;
+
+ return AV_PIX_FMT_NONE;
+}
+
+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);
+
+QVideoFrameFormat::ColorTransfer fromAvColorTransfer(AVColorTransferCharacteristic colorTrc);
+
+#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
new file mode 100644
index 000000000..69820cc79
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder.cpp
@@ -0,0 +1,247 @@
+// 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 "qffmpegresampler_p.h"
+#include "qaudiobuffer.h"
+
+#include "qffmpegplaybackengine_p.h"
+#include "playbackengine/qffmpegrenderer_p.h"
+
+#include <qloggingcategory.h>
+
+static Q_LOGGING_CATEGORY(qLcAudioDecoder, "qt.multimedia.ffmpeg.audioDecoder")
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg
+{
+
+class SteppingAudioRenderer : public Renderer
+{
+ Q_OBJECT
+public:
+ 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 {};
+ }
+
+signals:
+ void newAudioBuffer(QAudioBuffer);
+
+private:
+ QAudioFormat m_format;
+ std::unique_ptr<QFFmpegResampler> m_resampler;
+};
+
+class AudioDecoder : public PlaybackEngine
+{
+ Q_OBJECT
+public:
+ explicit AudioDecoder(const QAudioFormat &format) : m_format(format) { }
+
+ RendererPtr createRenderer(QPlatformMediaPlayer::TrackType trackType) override
+ {
+ 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()
+ {
+ Q_ASSERT(m_audioRenderer);
+ Q_ASSERT(!m_audioRenderer->isStepForced());
+
+ m_audioRenderer->doForceStep();
+ // updateObjectsPausedState();
+ }
+
+signals:
+ void newAudioBuffer(QAudioBuffer);
+
+private:
+ QPointer<Renderer> m_audioRenderer;
+ QAudioFormat m_format;
+};
+}
+
+
+QFFmpegAudioDecoder::QFFmpegAudioDecoder(QAudioDecoder *parent)
+ : QPlatformAudioDecoder(parent)
+{
+}
+
+QFFmpegAudioDecoder::~QFFmpegAudioDecoder() = default;
+
+QUrl QFFmpegAudioDecoder::source() const
+{
+ return m_url;
+}
+
+void QFFmpegAudioDecoder::setSource(const QUrl &fileName)
+{
+ stop();
+ m_sourceDevice = nullptr;
+
+ if (std::exchange(m_url, fileName) != fileName)
+ sourceChanged();
+}
+
+QIODevice *QFFmpegAudioDecoder::sourceDevice() const
+{
+ return m_sourceDevice;
+}
+
+void QFFmpegAudioDecoder::setSourceDevice(QIODevice *device)
+{
+ stop();
+ m_url.clear();
+ if (std::exchange(m_sourceDevice, device) != device)
+ sourceChanged();
+}
+
+void QFFmpegAudioDecoder::start()
+{
+ qCDebug(qLcAudioDecoder) << "start";
+ 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);
+ }
+
+ if (!checkNoError())
+ return;
+
+ m_decoder->setState(QMediaPlayer::PausedState);
+ if (!checkNoError())
+ return;
+
+ m_decoder->nextBuffer();
+ if (!checkNoError())
+ return;
+
+ durationChanged(m_decoder->duration() / 1000);
+ setIsDecoding(true);
+}
+
+void QFFmpegAudioDecoder::stop()
+{
+ qCDebug(qLcAudioDecoder) << ">>>>> stop";
+ if (m_decoder) {
+ m_decoder.reset();
+ done();
+ }
+}
+
+QAudioFormat QFFmpegAudioDecoder::audioFormat() const
+{
+ return m_audioFormat;
+}
+
+void QFFmpegAudioDecoder::setAudioFormat(const QAudioFormat &format)
+{
+ if (std::exchange(m_audioFormat, format) != format)
+ formatChanged(m_audioFormat);
+}
+
+QAudioBuffer QFFmpegAudioDecoder::read()
+{
+ auto buffer = std::exchange(m_audioBuffer, QAudioBuffer{});
+ if (!buffer.isValid())
+ return buffer;
+ qCDebug(qLcAudioDecoder) << "reading buffer" << buffer.startTime();
+ bufferAvailableChanged(false);
+ 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();
+ positionChanged(pos/1000);
+ bufferAvailableChanged(b.isValid());
+ bufferReady();
+}
+
+void QFFmpegAudioDecoder::done()
+{
+ qCDebug(qLcAudioDecoder) << ">>>>> 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
new file mode 100644
index 000000000..11816cd6f
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder_p.h
@@ -0,0 +1,68 @@
+// 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
+
+//
+// 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/qplatformaudiodecoder_p.h"
+#include <qffmpeg_p.h>
+#include <qurl.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+class AudioDecoder;
+}
+
+class QFFmpegAudioDecoder : public QPlatformAudioDecoder
+{
+ Q_OBJECT
+
+public:
+ QFFmpegAudioDecoder(QAudioDecoder *parent);
+ virtual ~QFFmpegAudioDecoder();
+
+ QUrl source() const override;
+ void setSource(const QUrl &fileName) override;
+
+ QIODevice *sourceDevice() const override;
+ void setSourceDevice(QIODevice *device) override;
+
+ void start() override;
+ void stop() override;
+
+ QAudioFormat audioFormat() const override;
+ void setAudioFormat(const QAudioFormat &format) override;
+
+ QAudioBuffer read() override;
+
+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;
+ std::unique_ptr<AudioDecoder> m_decoder;
+ QAudioFormat m_audioFormat;
+
+ QAudioBuffer m_audioBuffer;
+};
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGAUDIODECODER_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegaudioinput.cpp b/src/plugins/multimedia/ffmpeg/qffmpegaudioinput.cpp
new file mode 100644
index 000000000..014c53ca4
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegaudioinput.cpp
@@ -0,0 +1,195 @@
+// 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
+
+namespace QFFmpeg {
+
+class AudioSourceIO : public QIODevice
+{
+ Q_OBJECT
+public:
+ AudioSourceIO(QFFmpegAudioInput *audioInput) : QIODevice(), m_input(audioInput)
+ {
+ m_muted = m_input->muted;
+ m_volume = m_input->volume;
+ updateVolume();
+ open(QIODevice::WriteOnly);
+ }
+
+ ~AudioSourceIO() override = default;
+
+ void setDevice(const QAudioDevice &device)
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_device == device)
+ return;
+ m_device = device;
+ QMetaObject::invokeMethod(this, "updateSource");
+ }
+ void setFrameSize(int frameSize)
+ {
+ m_bufferSize.storeRelease((frameSize > 0 && m_format.isValid())
+ ? m_format.bytesForFrames(frameSize)
+ : DefaultAudioInputBufferSize);
+ }
+ void setRunning(bool r) {
+ QMutexLocker locker(&m_mutex);
+ if (m_running == r)
+ return;
+ m_running = r;
+ QMetaObject::invokeMethod(this, "updateRunning");
+ }
+
+ void setVolume(float vol) {
+ QMutexLocker locker(&m_mutex);
+ m_volume = vol;
+ QMetaObject::invokeMethod(this, "updateVolume");
+ }
+ void setMuted(bool muted) {
+ QMutexLocker locker(&m_mutex);
+ m_muted = muted;
+ QMetaObject::invokeMethod(this, "updateVolume");
+ }
+
+ int bufferSize() const { return m_bufferSize.loadAcquire(); }
+
+protected:
+ qint64 readData(char *, qint64) override
+ {
+ return 0;
+ }
+ qint64 writeData(const char *data, qint64 len) override
+ {
+ int l = len;
+ while (len > 0) {
+ const auto bufferSize = m_bufferSize.loadAcquire();
+ int toAppend = qMin(len, bufferSize - m_pcm.size());
+ m_pcm.append(data, toAppend);
+ data += toAppend;
+ len -= toAppend;
+ if (m_pcm.size() == bufferSize)
+ sendBuffer();
+ }
+
+ return l;
+ }
+
+private Q_SLOTS:
+ void updateSource() {
+ QMutexLocker locker(&m_mutex);
+ m_format = m_device.preferredFormat();
+ 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);
+ }
+ void updateVolume()
+ {
+ if (m_src)
+ m_src->setVolume(m_muted ? 0. : m_volume);
+ }
+ void updateRunning()
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_running) {
+ if (!m_src)
+ updateSource();
+ m_src->start(this);
+ } else {
+ m_src->stop();
+ }
+ }
+
+private:
+
+ void sendBuffer()
+ {
+ QAudioFormat fmt = m_src->format();
+ 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 m_mutex;
+ QAudioDevice m_device;
+ float m_volume = 1.;
+ bool m_muted = false;
+ bool m_running = false;
+
+ QFFmpegAudioInput *m_input = nullptr;
+ std::unique_ptr<QAudioSource> m_src;
+ QAudioFormat m_format;
+ QAtomicInt m_bufferSize = DefaultAudioInputBufferSize;
+ qint64 m_processed = 0;
+ QByteArray m_pcm;
+};
+
+}
+
+QFFmpegAudioInput::QFFmpegAudioInput(QAudioInput *qq)
+ : QPlatformAudioInput(qq)
+{
+ qRegisterMetaType<QAudioBuffer>();
+
+ inputThread = std::make_unique<QThread>();
+ audioIO = new QFFmpeg::AudioSourceIO(this);
+ 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();
+}
+
+void QFFmpegAudioInput::setAudioDevice(const QAudioDevice &device)
+{
+ audioIO->setDevice(device);
+}
+
+void QFFmpegAudioInput::setMuted(bool muted)
+{
+ audioIO->setMuted(muted);
+}
+
+void QFFmpegAudioInput::setVolume(float volume)
+{
+ audioIO->setVolume(volume);
+}
+
+void QFFmpegAudioInput::setFrameSize(int s)
+{
+ audioIO->setFrameSize(s);
+}
+
+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
new file mode 100644
index 000000000..288b3f432
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegaudioinput_p.h
@@ -0,0 +1,57 @@
+// 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
+
+//
+// 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/qplatformaudioinput_p.h>
+#include <private/qplatformaudiobufferinput_p.h>
+#include "qffmpegthread_p.h"
+#include <qaudioinput.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioSource;
+class QAudioBuffer;
+namespace QFFmpeg {
+class AudioSourceIO;
+}
+
+constexpr int DefaultAudioInputBufferSize = 4096;
+
+class QFFmpegAudioInput : public QPlatformAudioBufferInputBase, public QPlatformAudioInput
+{
+ // for qobject_cast
+ Q_OBJECT
+public:
+ QFFmpegAudioInput(QAudioInput *qq);
+ ~QFFmpegAudioInput();
+
+ void setAudioDevice(const QAudioDevice &/*device*/) override;
+ void setMuted(bool /*muted*/) override;
+ void setVolume(float /*volume*/) override;
+
+ void setFrameSize(int frameSize);
+ void setRunning(bool b);
+
+ int bufferSize() const;
+
+private:
+ QFFmpeg::AudioSourceIO *audioIO = nullptr;
+ std::unique_ptr<QThread> inputThread;
+};
+
+QT_END_NAMESPACE
+
+
+#endif // QPLATFORMAUDIOINPUT_H
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/qffmpegconverter.cpp b/src/plugins/multimedia/ffmpeg/qffmpegconverter.cpp
new file mode 100644
index 000000000..ba87ce3ed
--- /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(QtVideo::MapMode::ReadOnly))
+ return false;
+
+ QScopeGuard unmapSrc{[&] {
+ src.unmap();
+ }};
+
+ if (!dst.map(QtVideo::MapMode::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/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/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
new file mode 100644
index 000000000..5b140f0ca
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel.cpp
@@ -0,0 +1,504 @@
+// 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)
+#include "qffmpeghwaccel_vaapi_p.h"
+#endif
+#ifdef Q_OS_DARWIN
+#include "qffmpeghwaccel_videotoolbox_p.h"
+#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 "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
+
+static Q_LOGGING_CATEGORY(qLHWAccel, "qt.multimedia.ffmpeg.hwaccel");
+extern bool thread_local FFmpegLogsEnabledInThread;
+
+namespace QFFmpeg {
+
+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,
+
+ // 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,
+#endif
+};
+
+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);
+
+ if (ret == 0) {
+ qCDebug(qLHWAccel) << " Using above hw context.";
+ return AVBufferUPtr(hwContext);
+ }
+ qCDebug(qLHWAccel) << " Could not create hw context:" << ret << strerror(-ret);
+ return nullptr;
+}
+
+// FFmpeg might crash on loading non-existing hw devices.
+// Let's roughly precheck drivers/libraries.
+static bool precheckDriver(AVHWDeviceType type)
+{
+ // 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();
+
+#if QT_FFMPEG_HAS_D3D12VA
+ if (type == AV_HWDEVICE_TYPE_D3D12VA)
+ return QSystemLibrary(QLatin1String("d3d12.dll")).load();
+#endif
+
+ 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;
+}
+
+static bool checkHwType(AVHWDeviceType type)
+{
+ const auto deviceName = av_hwdevice_get_type_name(type);
+ if (!deviceName) {
+ qWarning() << "Internal FFmpeg error, unknow hw type:" << type;
+ return false;
+ }
+
+ if (!precheckDriver(type)) {
+ qCDebug(qLHWAccel) << "Drivers for hw device" << deviceName << "is not installed";
+ return false;
+ }
+
+ 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;
+}
+
+static const std::vector<AVHWDeviceType> &deviceTypes()
+{
+ 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)) {
+ findAVPixelFormat(codec, [&](AVPixelFormat format) {
+ if (isHwPixelFormat(format))
+ hwPixFormats.insert(format);
+ return false;
+ });
+ }
+
+ // 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;
+}
+
+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)
+{
+ 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;
+};
+
+namespace {
+
+bool hwTextureConversionEnabled()
+{
+
+ // 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;
+}
+
+void setupDecoder(const AVPixelFormat format, AVCodecContext *const codecContext)
+{
+ if (!hwTextureConversionEnabled())
+ return;
+
+#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) {
+ // check in supported codec->pix_fmts;
+ // no reason to use findAVPixelFormat as we're already in the hw_config loop
+ if (shouldCheckCodecFormats && !hasAVFormat(codecContext->codec->pix_fmts, 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)
+ return AVPixelFormat(frame->format);
+
+ auto *hwFramesContext = (AVHWFramesContext *)frame->hw_frames_ctx->data;
+ Q_ASSERT(hwFramesContext);
+ return AVPixelFormat(hwFramesContext->sw_format);
+}
+
+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()
+{
+ static const auto &result = deviceTypes("QT_FFMPEG_DECODING_HW_DEVICE_TYPES");
+ return result;
+}
+
+AVHWDeviceContext *HWAccel::hwDeviceContext() const
+{
+ return m_hwDeviceContext ? (AVHWDeviceContext *)m_hwDeviceContext->data : nullptr;
+}
+
+AVPixelFormat HWAccel::hwFormat() const
+{
+ return pixelFormatForHwDevice(deviceType());
+}
+
+const AVHWFramesConstraints *HWAccel::constraints() const
+{
+ std::call_once(m_constraintsOnceFlag, [this]() {
+ if (auto context = hwDeviceContextAsBuffer())
+ m_constraints.reset(av_hwdevice_get_hwframe_constraints(context, nullptr));
+ });
+
+ return m_constraints.get();
+}
+
+std::pair<const AVCodec *, std::unique_ptr<HWAccel>>
+HWAccel::findEncoderWithHwAccel(AVCodecID id, const std::function<bool(const HWAccel &)>& hwAccelPredicate)
+{
+ 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
+{
+ return m_hwDeviceContext ? hwDeviceContext()->type : AV_HWDEVICE_TYPE_NONE;
+}
+
+void HWAccel::createFramesContext(AVPixelFormat swFormat, const QSize &size)
+{
+ if (m_hwFramesContext) {
+ qWarning() << "Frames context has been already created!";
+ return;
+ }
+
+ if (!m_hwDeviceContext)
+ return;
+
+ 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();
+ 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
+ qCDebug(qLHWAccel) << "Initialized frames context" << size << c->format << c->sw_format;
+}
+
+AVHWFramesContext *HWAccel::hwFramesContext() const
+{
+ return m_hwFramesContext ? (AVHWFramesContext *)m_hwFramesContext->data : nullptr;
+}
+
+
+TextureConverter::TextureConverter(QRhi *rhi)
+ : d(new Data)
+{
+ d->rhi = rhi;
+}
+
+TextureSet *TextureConverter::getTextures(AVFrame *frame)
+{
+ if (!frame || isNull())
+ return nullptr;
+
+ Q_ASSERT(frame->format == d->format);
+ return d->backend->getTextures(frame);
+}
+
+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 = std::make_unique<VAAPITextureConverter>(d->rhi);
+ break;
+#endif
+#ifdef Q_OS_DARWIN
+ case AV_PIX_FMT_VIDEOTOOLBOX:
+ d->backend = std::make_unique<VideoToolBoxTextureConverter>(d->rhi);
+ break;
+#endif
+#if QT_CONFIG(wmf)
+ case AV_PIX_FMT_D3D11:
+ 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:
+ break;
+ }
+ d->format = fmt;
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11.cpp b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11.cpp
new file mode 100644
index 000000000..a2533a132
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11.cpp
@@ -0,0 +1,309 @@
+// 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/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
+
+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 {
+
+bool TextureBridge::copyToSharedTex(ID3D11Device *dev, ID3D11DeviceContext *ctx,
+ const ComPtr<ID3D11Texture2D> &tex, UINT index,
+ const QSize &frameSize)
+{
+ 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;
+ }
+
+ if (m_destTex)
+ return true;
+
+ if (m_destDevice->OpenSharedResource1(m_sharedHandle.get(), IID_PPV_ARGS(&m_destTex)) != S_OK)
+ return false;
+
+ CD3D11_TEXTURE2D_DESC desc{};
+ m_destTex->GetDesc(&desc);
+
+ 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_owner = nullptr;
+ ComPtr<ID3D11Texture2D> m_tex;
+};
+
+D3D11TextureConverter::D3D11TextureConverter(QRhi *rhi)
+ : TextureConverterBackend(rhi), m_rhiDevice{ GetD3DDevice(rhi) }
+{
+ if (!m_rhiDevice)
+ return;
+
+ m_rhiDevice->GetImmediateContext(m_rhiCtx.GetAddressOf());
+}
+
+TextureSet *D3D11TextureConverter::getTextures(AVFrame *frame)
+{
+ if (!m_rhiDevice)
+ return nullptr;
+
+ if (!frame || !frame->hw_frames_ctx || frame->format != AV_PIX_FMT_D3D11)
+ return nullptr;
+
+ const auto *fCtx = reinterpret_cast<AVHWFramesContext *>(frame->hw_frames_ctx->data);
+ const auto *ctx = fCtx->device_ctx;
+
+ if (!ctx || ctx->type != AV_HWDEVICE_TYPE_D3D11VA)
+ 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) {
+ {
+ 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;
+ }
+ }
+
+ // 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;
+}
+
+void D3D11TextureConverter::SetupDecoderTextures(AVCodecContext *s)
+{
+ // 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;
+ }
+
+ 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);
+ if (ret < 0) {
+ qCDebug(qLcMediaFFmpegHWAccel) << "Failed to initialize HW frames context" << ret;
+ av_buffer_unref(&s->hw_frames_ctx);
+ }
+}
+
+} // 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
new file mode 100644
index 000000000..bfcc1f10c
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11_p.h
@@ -0,0 +1,104 @@
+// 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
+
+//
+// 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/quniquehandle_p.h>
+#include <private/qcomptr_p.h>
+#include <qt_windows.h>
+
+#include <d3d11.h>
+#include <d3d11_1.h>
+
+#if QT_CONFIG(wmf)
+
+QT_BEGIN_NAMESPACE
+
+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:
+ D3D11TextureConverter(QRhi *rhi);
+
+ 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
+
+#endif
+
+#endif
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
new file mode 100644
index 000000000..bc6547f12
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_p.h
@@ -0,0 +1,130 @@
+// 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
+
+//
+// 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 "qvideoframeformat.h"
+#include "qabstractvideobuffer.h"
+
+#include <qshareddata.h>
+#include <memory>
+#include <functional>
+#include <mutex>
+
+QT_BEGIN_NAMESPACE
+
+class QRhi;
+class QRhiTexture;
+class QFFmpegVideoBuffer;
+
+namespace QFFmpeg {
+
+// used for the get_format callback for the decoder
+enum AVPixelFormat getFormat(struct AVCodecContext *s, const enum AVPixelFormat * fmt);
+
+class HWAccel;
+
+class TextureSet {
+public:
+ // ### Should add QVideoFrameFormat::PixelFormat here
+ virtual ~TextureSet() {}
+ virtual qint64 textureHandle(QRhi *, int /*plane*/) { return 0; }
+};
+
+class TextureConverterBackend
+{
+public:
+ TextureConverterBackend(QRhi *rhi)
+ : rhi(rhi)
+ {}
+ virtual ~TextureConverterBackend() {}
+ virtual TextureSet *getTextures(AVFrame * /*frame*/) { return nullptr; }
+
+ QRhi *rhi = nullptr;
+};
+
+class TextureConverter
+{
+ class Data final
+ {
+ public:
+ QAtomicInt ref = 0;
+ QRhi *rhi = nullptr;
+ AVPixelFormat format = AV_PIX_FMT_NONE;
+ std::unique_ptr<TextureConverterBackend> backend;
+ };
+public:
+ TextureConverter(QRhi *rhi = nullptr);
+
+ void init(AVFrame *frame) {
+ AVPixelFormat fmt = frame ? AVPixelFormat(frame->format) : AV_PIX_FMT_NONE;
+ if (fmt != d->format)
+ updateBackend(fmt);
+ }
+ TextureSet *getTextures(AVFrame *frame);
+ bool isNull() const { return !d->backend || !d->backend->rhi; }
+
+private:
+ void updateBackend(AVPixelFormat format);
+
+ QExplicitlySharedDataPointer<Data> d;
+};
+
+class HWAccel
+{
+ AVBufferUPtr m_hwDeviceContext;
+ AVBufferUPtr m_hwFramesContext;
+
+ mutable std::once_flag m_constraintsOnceFlag;
+ mutable AVHWFramesConstraintsUPtr m_constraints;
+
+public:
+ ~HWAccel();
+
+ 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 m_hwDeviceContext.get(); }
+ AVHWDeviceContext *hwDeviceContext() const;
+ AVPixelFormat hwFormat() const;
+ const AVHWFramesConstraints *constraints() const;
+
+ void createFramesContext(AVPixelFormat swFormat, const QSize &size);
+ AVBufferRef *hwFramesContextAsBuffer() const { return m_hwFramesContext.get(); }
+ AVHWFramesContext *hwFramesContext() const;
+
+ static AVPixelFormat format(AVFrame *frame);
+ static const std::vector<AVHWDeviceType> &encodingDeviceTypes();
+
+ static const std::vector<AVHWDeviceType> &decodingDeviceTypes();
+
+private:
+ HWAccel(AVBufferUPtr hwDeviceContext) : m_hwDeviceContext(std::move(hwDeviceContext)) { }
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi.cpp b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi.cpp
new file mode 100644
index 000000000..7e46e3537
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi.cpp
@@ -0,0 +1,364 @@
+// 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"
+
+#if !QT_CONFIG(vaapi)
+#error "Configuration error"
+#endif
+
+#include <va/va.h>
+
+#include <qvideoframeformat.h>
+#include "qffmpegvideobuffer_p.h"
+#include "private/qvideotexturehelper_p.h"
+
+#include <rhi/qrhi.h>
+
+#include <qguiapplication.h>
+#include <qpa/qplatformnativeinterface.h>
+
+#include <qopenglfunctions.h>
+
+//#define VA_EXPORT_USE_LAYERS
+
+#if __has_include("drm/drm_fourcc.h")
+#include <drm/drm_fourcc.h>
+#elif __has_include("libdrm/drm_fourcc.h")
+#include <libdrm/drm_fourcc.h>
+#else
+// keep things building without drm_fourcc.h
+#define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \
+ ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
+
+#define DRM_FORMAT_RGBA8888 fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */
+#define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */
+#define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */
+#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */
+#define DRM_FORMAT_BGR888 fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */
+#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */
+#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */
+#define DRM_FORMAT_R16 fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */
+#define DRM_FORMAT_RGB565 fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */
+#define DRM_FORMAT_RG1616 fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */
+#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 */
+#endif
+
+extern "C" {
+#include <libavutil/hwcontext_vaapi.h>
+}
+
+#include <va/va_drm.h>
+#include <va/va_drmcommon.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <unistd.h>
+
+#include <qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLHWAccelVAAPI, "qt.multimedia.ffmpeg.hwaccelvaapi");
+
+namespace QFFmpeg {
+
+static const quint32 *fourccFromPixelFormat(const QVideoFrameFormat::PixelFormat format)
+{
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ const quint32 rgba_fourcc = DRM_FORMAT_ABGR8888;
+ const quint32 rg_fourcc = DRM_FORMAT_GR88;
+ const quint32 rg16_fourcc = DRM_FORMAT_GR1616;
+#else
+ const quint32 rgba_fourcc = DRM_FORMAT_RGBA8888;
+ const quint32 rg_fourcc = DRM_FORMAT_RG88;
+ const quint32 rg16_fourcc = DRM_FORMAT_RG1616;
+#endif
+
+// qCDebug(qLHWAccelVAAPI) << "Getting DRM fourcc for pixel format" << format;
+
+ switch (format) {
+ case QVideoFrameFormat::Format_Invalid:
+ case QVideoFrameFormat::Format_IMC1:
+ case QVideoFrameFormat::Format_IMC2:
+ case QVideoFrameFormat::Format_IMC3:
+ case QVideoFrameFormat::Format_IMC4:
+ case QVideoFrameFormat::Format_SamplerExternalOES:
+ case QVideoFrameFormat::Format_Jpeg:
+ case QVideoFrameFormat::Format_SamplerRect:
+ return nullptr;
+
+ case QVideoFrameFormat::Format_ARGB8888:
+ case QVideoFrameFormat::Format_ARGB8888_Premultiplied:
+ case QVideoFrameFormat::Format_XRGB8888:
+ case QVideoFrameFormat::Format_BGRA8888:
+ case QVideoFrameFormat::Format_BGRA8888_Premultiplied:
+ case QVideoFrameFormat::Format_BGRX8888:
+ case QVideoFrameFormat::Format_ABGR8888:
+ case QVideoFrameFormat::Format_XBGR8888:
+ case QVideoFrameFormat::Format_RGBA8888:
+ case QVideoFrameFormat::Format_RGBX8888:
+ case QVideoFrameFormat::Format_AYUV:
+ case QVideoFrameFormat::Format_AYUV_Premultiplied:
+ case QVideoFrameFormat::Format_UYVY:
+ case QVideoFrameFormat::Format_YUYV:
+ {
+ static constexpr quint32 format[] = { rgba_fourcc, 0, 0, 0 };
+ return format;
+ }
+
+ case QVideoFrameFormat::Format_Y8:
+ {
+ static constexpr quint32 format[] = { DRM_FORMAT_R8, 0, 0, 0 };
+ return format;
+ }
+ case QVideoFrameFormat::Format_Y16:
+ {
+ static constexpr quint32 format[] = { DRM_FORMAT_R16, 0, 0, 0 };
+ return format;
+ }
+
+ case QVideoFrameFormat::Format_YUV420P:
+ case QVideoFrameFormat::Format_YUV422P:
+ case QVideoFrameFormat::Format_YV12:
+ {
+ static constexpr quint32 format[] = { DRM_FORMAT_R8, DRM_FORMAT_R8, DRM_FORMAT_R8, 0 };
+ return format;
+ }
+ case QVideoFrameFormat::Format_YUV420P10:
+ {
+ static constexpr quint32 format[] = { DRM_FORMAT_R16, DRM_FORMAT_R16, DRM_FORMAT_R16, 0 };
+ return format;
+ }
+
+ case QVideoFrameFormat::Format_NV12:
+ case QVideoFrameFormat::Format_NV21:
+ {
+ static constexpr quint32 format[] = { DRM_FORMAT_R8, rg_fourcc, 0, 0 };
+ return format;
+ }
+
+ case QVideoFrameFormat::Format_P010:
+ case QVideoFrameFormat::Format_P016:
+ {
+ static constexpr quint32 format[] = { DRM_FORMAT_R16, rg16_fourcc, 0, 0 };
+ return format;
+ }
+ }
+ return nullptr;
+}
+
+class VAAPITextureSet : public TextureSet
+{
+public:
+ ~VAAPITextureSet();
+ qint64 textureHandle(QRhi *, int plane) override {
+ return textures[plane];
+ }
+
+ QRhi *rhi = nullptr;
+ QOpenGLContext *glContext = nullptr;
+ int nPlanes = 0;
+ GLuint textures[4] = {};
+};
+
+
+VAAPITextureConverter::VAAPITextureConverter(QRhi *rhi)
+ : TextureConverterBackend(nullptr)
+{
+ qCDebug(qLHWAccelVAAPI) << ">>>> Creating VAAPI HW accelerator";
+
+ if (!rhi || rhi->backend() != QRhi::OpenGLES2) {
+ qWarning() << "VAAPITextureConverter: No rhi or non openGL based RHI";
+ this->rhi = nullptr;
+ return;
+ }
+
+ auto *nativeHandles = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles());
+ glContext = nativeHandles->context;
+ if (!glContext) {
+ qCDebug(qLHWAccelVAAPI) << " no GL context, disabling";
+ return;
+ }
+ const QString platform = QGuiApplication::platformName();
+ QPlatformNativeInterface *pni = QGuiApplication::platformNativeInterface();
+ eglDisplay = pni->nativeResourceForIntegration(QByteArrayLiteral("egldisplay"));
+ qCDebug(qLHWAccelVAAPI) << " platform is" << platform << eglDisplay;
+
+ if (!eglDisplay) {
+ qCDebug(qLHWAccelVAAPI) << " no egl display, disabling";
+ return;
+ }
+ eglImageTargetTexture2D = eglGetProcAddress("glEGLImageTargetTexture2DOES");
+ if (!eglDisplay) {
+ qCDebug(qLHWAccelVAAPI) << " no eglImageTargetTexture2D, disabling";
+ return;
+ }
+
+ // everything ok, indicate that we can do zero copy
+ this->rhi = rhi;
+}
+
+VAAPITextureConverter::~VAAPITextureConverter()
+{
+}
+
+//#define VA_EXPORT_USE_LAYERS
+TextureSet *VAAPITextureConverter::getTextures(AVFrame *frame)
+{
+// qCDebug(qLHWAccelVAAPI) << "VAAPIAccel::getTextures";
+ if (frame->format != AV_PIX_FMT_VAAPI || !eglDisplay) {
+ qCDebug(qLHWAccelVAAPI) << "format/egl error" << frame->format << eglDisplay;
+ return nullptr;
+ }
+
+ if (!frame->hw_frames_ctx)
+ return nullptr;
+
+ auto *fCtx = (AVHWFramesContext *)frame->hw_frames_ctx->data;
+ auto *ctx = fCtx->device_ctx;
+ if (!ctx)
+ return nullptr;
+
+ auto *vaCtx = (AVVAAPIDeviceContext *)ctx->hwctx;
+ auto vaDisplay = vaCtx->display;
+ if (!vaDisplay) {
+ qCDebug(qLHWAccelVAAPI) << " no VADisplay, disabling";
+ return nullptr;
+ }
+
+ VASurfaceID vaSurface = (uintptr_t)frame->data[3];
+
+ VADRMPRIMESurfaceDescriptor prime = {};
+ if (vaExportSurfaceHandle(vaDisplay, vaSurface,
+ VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
+ VA_EXPORT_SURFACE_READ_ONLY |
+#ifdef VA_EXPORT_USE_LAYERS
+ VA_EXPORT_SURFACE_SEPARATE_LAYERS,
+#else
+ VA_EXPORT_SURFACE_COMPOSED_LAYERS,
+#endif
+ &prime) != VA_STATUS_SUCCESS)
+ {
+ 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);
+
+// qCDebug(qLHWAccelVAAPI) << "VAAPIAccel: vaSufraceDesc: width/height" << prime.width << prime.height << "num objects"
+// << prime.num_objects << "num layers" << prime.num_layers;
+
+ QOpenGLFunctions functions(glContext);
+
+ AVPixelFormat fmt = HWAccel::format(frame);
+ bool needsConversion;
+ auto qtFormat = QFFmpegVideoBuffer::toQtPixelFormat(fmt, &needsConversion);
+ auto *drm_formats = fourccFromPixelFormat(qtFormat);
+ if (!drm_formats || needsConversion) {
+ qWarning() << "can't use DMA transfer for pixel format" << fmt << qtFormat;
+ return nullptr;
+ }
+
+ auto *desc = QVideoTextureHelper::textureDescription(qtFormat);
+ int nPlanes = 0;
+ for (; nPlanes < 5; ++nPlanes) {
+ if (drm_formats[nPlanes] == 0)
+ break;
+ }
+ Q_ASSERT(nPlanes == desc->nplanes);
+ nPlanes = desc->nplanes;
+// qCDebug(qLHWAccelVAAPI) << "VAAPIAccel: nPlanes" << nPlanes;
+
+ rhi->makeThreadLocalNativeContextCurrent();
+
+ EGLImage images[4];
+ GLuint glTextures[4] = {};
+ functions.glGenTextures(nPlanes, glTextures);
+ for (int i = 0; i < nPlanes; ++i) {
+#ifdef VA_EXPORT_USE_LAYERS
+#define LAYER i
+#define PLANE 0
+ if (prime.layers[i].drm_format != drm_formats[i]) {
+ qWarning() << "expected DRM format check failed expected"
+ << Qt::hex << drm_formats[i] << "got" << prime.layers[i].drm_format;
+ }
+#else
+#define LAYER 0
+#define PLANE i
+#endif
+
+ EGLAttrib img_attr[] = {
+ EGL_LINUX_DRM_FOURCC_EXT, (EGLint)drm_formats[i],
+ EGL_WIDTH, desc->widthForPlane(frame->width, i),
+ EGL_HEIGHT, desc->heightForPlane(frame->height, i),
+ EGL_DMA_BUF_PLANE0_FD_EXT, prime.objects[prime.layers[LAYER].object_index[PLANE]].fd,
+ EGL_DMA_BUF_PLANE0_OFFSET_EXT, (EGLint)prime.layers[LAYER].offset[PLANE],
+ EGL_DMA_BUF_PLANE0_PITCH_EXT, (EGLint)prime.layers[LAYER].pitch[PLANE],
+ EGL_NONE
+ };
+ images[i] = eglCreateImage(eglDisplay, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, img_attr);
+ if (!images[i]) {
+ 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]);
+ GLenum error = glGetError();
+ if (error)
+ qWarning() << "eglImageTargetTexture2D failed with error code" << error;
+ }
+
+ for (int i = 0; i < nPlanes; ++i) {
+ functions.glActiveTexture(GL_TEXTURE0 + i);
+ functions.glBindTexture(GL_TEXTURE_2D, 0);
+ eglDestroyImage(eglDisplay, images[i]);
+ }
+
+ VAAPITextureSet *textureSet = new VAAPITextureSet;
+ textureSet->nPlanes = nPlanes;
+ textureSet->rhi = rhi;
+ textureSet->glContext = glContext;
+
+ for (int i = 0; i < 4; ++i)
+ textureSet->textures[i] = glTextures[i];
+// qCDebug(qLHWAccelVAAPI) << "VAAPIAccel: got textures" << textures[0] << textures[1] << textures[2] << textures[3];
+
+ return textureSet;
+}
+
+VAAPITextureSet::~VAAPITextureSet()
+{
+ if (rhi) {
+ rhi->makeThreadLocalNativeContextCurrent();
+ QOpenGLFunctions functions(glContext);
+ functions.glDeleteTextures(nPlanes, textures);
+ }
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi_p.h b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi_p.h
new file mode 100644
index 000000000..03084cc72
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi_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 QFFMPEGHWACCEL_VAAPI_P_H
+#define QFFMPEGHWACCEL_VAAPI_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"
+
+#if QT_CONFIG(vaapi)
+
+#include <qshareddata.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRhi;
+class QOpenGLContext;
+
+namespace QFFmpeg {
+
+class VAAPITextureConverter : public TextureConverterBackend
+{
+public:
+ VAAPITextureConverter(QRhi *rhi);
+ ~VAAPITextureConverter();
+
+ TextureSet *getTextures(AVFrame *frame) override;
+
+ Qt::HANDLE eglDisplay = nullptr;
+ QOpenGLContext *glContext = nullptr;
+ QFunctionPointer eglImageTargetTexture2D = nullptr;
+};
+}
+
+QT_END_NAMESPACE
+
+#endif
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox.mm b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox.mm
new file mode 100644
index 000000000..948f7fc23
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox.mm
@@ -0,0 +1,288 @@
+// 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"
+
+#if !defined(Q_OS_DARWIN)
+#error "Configuration error"
+#endif
+
+#include <qvideoframeformat.h>
+#include <qffmpegvideobuffer_p.h>
+#include <qloggingcategory.h>
+#include "private/qvideotexturehelper_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
+{
+
+static CVMetalTextureCacheRef &mtc(void *&cache) { return reinterpret_cast<CVMetalTextureCacheRef &>(cache); }
+
+class VideoToolBoxTextureSet : public TextureSet
+{
+public:
+ ~VideoToolBoxTextureSet();
+ qint64 textureHandle(QRhi *, int plane) override;
+
+ QRhi *rhi = nullptr;
+ CVMetalTextureRef cvMetalTexture[3] = {};
+
+#if defined(Q_OS_MACOS)
+ CVOpenGLTextureRef cvOpenGLTexture = nullptr;
+#elif defined(Q_OS_IOS)
+ CVOpenGLESTextureRef cvOpenGLESTexture = nullptr;
+#endif
+
+ CVImageBufferRef m_buffer = nullptr;
+};
+
+VideoToolBoxTextureConverter::VideoToolBoxTextureConverter(QRhi *rhi)
+ : TextureConverterBackend(rhi)
+{
+ if (!rhi)
+ return;
+
+ if (rhi->backend() == QRhi::Metal) {
+ const auto *metal = static_cast<const QRhiMetalNativeHandles *>(rhi->nativeHandles());
+
+ // Create a Metal Core Video texture cache from the pixel buffer.
+ Q_ASSERT(!cvMetalTextureCache);
+ if (CVMetalTextureCacheCreate(
+ kCFAllocatorDefault,
+ nil,
+ (id<MTLDevice>)metal->dev,
+ nil,
+ &mtc(cvMetalTextureCache)) != kCVReturnSuccess) {
+ qWarning() << "Metal texture cache creation failed";
+ rhi = nullptr;
+ }
+ } else if (rhi->backend() == QRhi::OpenGLES2) {
+#if QT_CONFIG(opengl)
+#ifdef Q_OS_MACOS
+ const auto *gl = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles());
+
+ auto nsGLContext = gl->context->nativeInterface<QNativeInterface::QCocoaGLContext>()->nativeContext();
+ auto nsGLPixelFormat = nsGLContext.pixelFormat.CGLPixelFormatObj;
+
+ // Create an OpenGL CoreVideo texture cache from the pixel buffer.
+ if (CVOpenGLTextureCacheCreate(
+ kCFAllocatorDefault,
+ nullptr,
+ reinterpret_cast<CGLContextObj>(nsGLContext.CGLContextObj),
+ nsGLPixelFormat,
+ nil,
+ &cvOpenGLTextureCache)) {
+ qWarning() << "OpenGL texture cache creation failed";
+ rhi = nullptr;
+ }
+#endif
+#ifdef Q_OS_IOS
+ // Create an OpenGL CoreVideo texture cache from the pixel buffer.
+ if (CVOpenGLESTextureCacheCreate(
+ kCFAllocatorDefault,
+ nullptr,
+ [EAGLContext currentContext],
+ nullptr,
+ &cvOpenGLESTextureCache)) {
+ qWarning() << "OpenGL texture cache creation failed";
+ rhi = nullptr;
+ }
+#endif
+#else
+ rhi = nullptr;
+#endif // QT_CONFIG(opengl)
+ }
+}
+
+VideoToolBoxTextureConverter::~VideoToolBoxTextureConverter()
+{
+ freeTextureCaches();
+}
+
+void VideoToolBoxTextureConverter::freeTextureCaches()
+{
+ if (cvMetalTextureCache)
+ CFRelease(cvMetalTextureCache);
+ cvMetalTextureCache = nullptr;
+#if defined(Q_OS_MACOS)
+ if (cvOpenGLTextureCache)
+ CFRelease(cvOpenGLTextureCache);
+ cvOpenGLTextureCache = nullptr;
+#elif defined(Q_OS_IOS)
+ if (cvOpenGLESTextureCache)
+ CFRelease(cvOpenGLESTextureCache);
+ cvOpenGLESTextureCache = nullptr;
+#endif
+}
+
+static MTLPixelFormat rhiTextureFormatToMetalFormat(QRhiTexture::Format f)
+{
+ switch (f) {
+ default:
+ case QRhiTexture::UnknownFormat:
+ return MTLPixelFormatInvalid;
+ case QRhiTexture::RGBA8:
+ return MTLPixelFormatRGBA8Unorm;
+ case QRhiTexture::BGRA8:
+ return MTLPixelFormatBGRA8Unorm;
+ case QRhiTexture::R8:
+ return MTLPixelFormatR8Unorm;
+ case QRhiTexture::RG8:
+ return MTLPixelFormatRG8Unorm;
+ case QRhiTexture::R16:
+ return MTLPixelFormatR16Unorm;
+ case QRhiTexture::RG16:
+ return MTLPixelFormatRG16Unorm;
+
+ case QRhiTexture::RGBA16F:
+ return MTLPixelFormatRGBA16Float;
+ case QRhiTexture::RGBA32F:
+ return MTLPixelFormatRGBA32Float;
+ case QRhiTexture::R16F:
+ return MTLPixelFormatR16Float;
+ case QRhiTexture::R32F:
+ return MTLPixelFormatR32Float;
+ }
+}
+
+TextureSet *VideoToolBoxTextureConverter::getTextures(AVFrame *frame)
+{
+ if (!rhi)
+ return nullptr;
+
+ bool needsConversion = false;
+ QVideoFrameFormat::PixelFormat pixelFormat = QFFmpegVideoBuffer::toQtPixelFormat(HWAccel::format(frame), &needsConversion);
+ if (needsConversion) {
+ // qDebug() << "XXXXXXXXXXXX pixel format needs conversion" << pixelFormat << HWAccel::format(frame);
+ return nullptr;
+ }
+
+ CVPixelBufferRef buffer = (CVPixelBufferRef)frame->data[3];
+
+ auto textureSet = std::make_unique<VideoToolBoxTextureSet>();
+ textureSet->m_buffer = buffer;
+ textureSet->rhi = rhi;
+ CVPixelBufferRetain(buffer);
+
+ auto *textureDescription = QVideoTextureHelper::textureDescription(pixelFormat);
+ int bufferPlanes = CVPixelBufferGetPlaneCount(buffer);
+// qDebug() << "XXXXX getTextures" << pixelFormat << bufferPlanes << buffer;
+
+ if (rhi->backend() == QRhi::Metal) {
+ for (int plane = 0; plane < bufferPlanes; ++plane) {
+ size_t width = CVPixelBufferGetWidth(buffer);
+ size_t height = CVPixelBufferGetHeight(buffer);
+ width = textureDescription->widthForPlane(width, plane);
+ height = textureDescription->heightForPlane(height, plane);
+
+ // Create a CoreVideo pixel buffer backed Metal texture image from the texture cache.
+ auto ret = CVMetalTextureCacheCreateTextureFromImage(
+ kCFAllocatorDefault,
+ mtc(cvMetalTextureCache),
+ buffer, nil,
+ rhiTextureFormatToMetalFormat(textureDescription->textureFormat[plane]),
+ width, height,
+ plane,
+ &textureSet->cvMetalTexture[plane]);
+
+ if (ret != kCVReturnSuccess)
+ qWarning() << "texture creation failed" << ret;
+// auto t = CVMetalTextureGetTexture(textureSet->cvMetalTexture[plane]);
+// qDebug() << " metal texture for plane" << plane << "is" << quint64(textureSet->cvMetalTexture[plane]) << width << height;
+// qDebug() << " " << t.iosurfacePlane << t.pixelFormat << t.width << t.height;
+ }
+ } else if (rhi->backend() == QRhi::OpenGLES2) {
+#if QT_CONFIG(opengl)
+#ifdef Q_OS_MACOS
+ CVOpenGLTextureCacheFlush(cvOpenGLTextureCache, 0);
+ // Create a CVPixelBuffer-backed OpenGL texture image from the texture cache.
+ const CVReturn cvret = CVOpenGLTextureCacheCreateTextureFromImage(
+ kCFAllocatorDefault,
+ cvOpenGLTextureCache,
+ buffer,
+ nil,
+ &textureSet->cvOpenGLTexture);
+ if (cvret != kCVReturnSuccess) {
+ qCWarning(qLcVideotoolbox) << "OpenGL texture creation failed" << cvret;
+ return nullptr;
+ }
+
+ Q_ASSERT(CVOpenGLTextureGetTarget(textureSet->cvOpenGLTexture) == GL_TEXTURE_RECTANGLE);
+#endif
+#ifdef Q_OS_IOS
+ CVOpenGLESTextureCacheFlush(cvOpenGLESTextureCache, 0);
+ // Create a CVPixelBuffer-backed OpenGL texture image from the texture cache.
+ const CVReturn cvret = CVOpenGLESTextureCacheCreateTextureFromImage(
+ kCFAllocatorDefault,
+ cvOpenGLESTextureCache,
+ buffer,
+ nil,
+ GL_TEXTURE_2D,
+ GL_RGBA,
+ CVPixelBufferGetWidth(buffer),
+ CVPixelBufferGetHeight(buffer),
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ 0,
+ &textureSet->cvOpenGLESTexture);
+ if (cvret != kCVReturnSuccess) {
+ qCWarning(qLcVideotoolbox) << "OpenGL ES texture creation failed" << cvret;
+ return nullptr;
+ }
+#endif
+#endif
+ }
+
+ return textureSet.release();
+}
+
+VideoToolBoxTextureSet::~VideoToolBoxTextureSet()
+{
+ for (int i = 0; i < 4; ++i)
+ if (cvMetalTexture[i])
+ CFRelease(cvMetalTexture[i]);
+#if defined(Q_OS_MACOS)
+ if (cvOpenGLTexture)
+ CVOpenGLTextureRelease(cvOpenGLTexture);
+#elif defined(Q_OS_IOS)
+ if (cvOpenGLESTexture)
+ CFRelease(cvOpenGLESTexture);
+#endif
+ CVPixelBufferRelease(m_buffer);
+}
+
+qint64 VideoToolBoxTextureSet::textureHandle(QRhi *, int plane)
+{
+ if (rhi->backend() == QRhi::Metal)
+ return cvMetalTexture[plane] ? qint64(CVMetalTextureGetTexture(cvMetalTexture[plane])) : 0;
+#if QT_CONFIG(opengl)
+ Q_ASSERT(plane == 0);
+#ifdef Q_OS_MACOS
+ return CVOpenGLTextureGetName(cvOpenGLTexture);
+#endif
+#ifdef Q_OS_IOS
+ return CVOpenGLESTextureGetName(cvOpenGLESTexture);
+#endif
+#endif
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox_p.h b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox_p.h
new file mode 100644
index 000000000..44fa32dd2
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox_p.h
@@ -0,0 +1,63 @@
+// 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
+
+//
+// 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"
+
+#ifdef Q_OS_DARWIN
+
+#include <CoreVideo/CVBase.h>
+#include <CoreVideo/CVPixelBuffer.h>
+#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
+
+class QRhi;
+
+namespace QFFmpeg {
+
+class VideoToolBoxTextureConverter : public TextureConverterBackend
+{
+public:
+ VideoToolBoxTextureConverter(QRhi *rhi);
+ ~VideoToolBoxTextureConverter();
+ TextureSet *getTextures(AVFrame *frame) override;
+
+private:
+ void freeTextureCaches();
+
+ // can not forward declare that type from C++ :/
+ void *cvMetalTextureCache = nullptr;
+#if defined(Q_OS_MACOS)
+ CVOpenGLTextureCacheRef cvOpenGLTextureCache = nullptr;
+#elif defined(Q_OS_IOS)
+ CVOpenGLESTextureCacheRef cvOpenGLESTextureCache = nullptr;
+#endif
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp b/src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp
new file mode 100644
index 000000000..2fb878784
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp
@@ -0,0 +1,271 @@
+// 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>
+#include <private/qplatformcamera_p.h>
+#include <private/qplatformimagecapture_p.h>
+#include <qvideoframeformat.h>
+#include <private/qmediastoragelocation_p.h>
+#include <qimagewriter.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <qstandardpaths.h>
+
+#include <qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+// 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()
+{
+}
+
+bool QFFmpegImageCapture::isReadyForCapture() const
+{
+ return m_isReadyForCapture;
+}
+
+static const char *extensionForFormat(QImageCapture::FileFormat format)
+{
+ const char *fmt = "jpg";
+ switch (format) {
+ case QImageCapture::UnspecifiedFormat:
+ case QImageCapture::JPEG:
+ fmt = "jpg";
+ break;
+ case QImageCapture::PNG:
+ fmt = "png";
+ break;
+ case QImageCapture::WebP:
+ fmt = "webp";
+ break;
+ case QImageCapture::Tiff:
+ fmt = "tiff";
+ break;
+ }
+ return fmt;
+}
+
+int QFFmpegImageCapture::capture(const QString &fileName)
+{
+ QString path = QMediaStorageLocation::generateFileName(fileName, QStandardPaths::PicturesLocation, QLatin1String(extensionForFormat(m_settings.format())));
+ return doCapture(path);
+}
+
+int QFFmpegImageCapture::captureToBuffer()
+{
+ return doCapture(QString());
+}
+
+int QFFmpegImageCapture::doCapture(const QString &fileName)
+{
+ qCDebug(qLcImageCapture) << "do capture";
+ 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";
+ return -1;
+ }
+ 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,
+ Q_ARG(int, -1),
+ Q_ARG(int, QImageCapture::ResourceError),
+ Q_ARG(QString,tr("No camera available.")));
+
+ qCDebug(qLcImageCapture) << "error 2";
+ return -1;
+ }
+ 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,
+ Q_ARG(int, -1),
+ Q_ARG(int, QImageCapture::NotReadyError),
+ Q_ARG(QString, QPlatformImageCapture::msgCameraNotReady()));
+
+ qCDebug(qLcImageCapture) << "error 3";
+ return -1;
+ }
+
+ m_lastId++;
+
+ m_pendingImages.enqueue({ m_lastId, fileName, QMediaMetaData{} });
+ updateReadyForCapture();
+
+ return m_lastId;
+}
+
+void QFFmpegImageCapture::setCaptureSession(QPlatformMediaCaptureSession *session)
+{
+ auto *captureSession = static_cast<QFFmpegMediaCaptureSession *>(session);
+ if (m_session == captureSession)
+ return;
+
+ if (m_session) {
+ m_session->disconnect(this);
+ m_lastId = 0;
+ m_pendingImages.clear();
+ }
+
+ m_session = captureSession;
+
+ if (m_session)
+ connect(m_session, &QFFmpegMediaCaptureSession::primaryActiveVideoSourceChanged, this,
+ &QFFmpegImageCapture::onVideoSourceChanged);
+
+ onVideoSourceChanged();
+}
+
+void QFFmpegImageCapture::updateReadyForCapture()
+{
+ const bool ready = m_session && m_pendingImages.size() < MaxPendingImagesCount && m_videoSource
+ && m_videoSource->isActive();
+
+ qCDebug(qLcImageCapture) << "updateReadyForCapture" << ready;
+
+ if (std::exchange(m_isReadyForCapture, ready) != ready)
+ emit readyForCaptureChanged(ready);
+}
+
+void QFFmpegImageCapture::newVideoFrame(const QVideoFrame &frame)
+{
+ if (m_pendingImages.empty())
+ return;
+
+ auto pending = m_pendingImages.dequeue();
+
+ qCDebug(qLcImageCapture) << "Taking image" << pending.id;
+
+ emit imageExposed(pending.id);
+ // ### Add metadata from the AVFrame
+ emit imageMetadataAvailable(pending.id, pending.metaData);
+ emit imageAvailable(pending.id, frame);
+ QImage image = frame.toImage();
+ if (m_settings.resolution().isValid() && m_settings.resolution() != image.size())
+ image = image.scaled(m_settings.resolution());
+
+ emit imageCaptured(pending.id, image);
+ if (!pending.filename.isEmpty()) {
+ const char *fmt = nullptr;
+ switch (m_settings.format()) {
+ case QImageCapture::UnspecifiedFormat:
+ case QImageCapture::JPEG:
+ fmt = "jpeg";
+ break;
+ case QImageCapture::PNG:
+ fmt = "png";
+ break;
+ case QImageCapture::WebP:
+ fmt = "webp";
+ break;
+ case QImageCapture::Tiff:
+ fmt = "tiff";
+ break;
+ }
+ int quality = -1;
+ switch (m_settings.quality()) {
+ case QImageCapture::VeryLowQuality:
+ quality = 25;
+ break;
+ case QImageCapture::LowQuality:
+ quality = 50;
+ break;
+ case QImageCapture::NormalQuality:
+ break;
+ case QImageCapture::HighQuality:
+ quality = 75;
+ break;
+ case QImageCapture::VeryHighQuality:
+ quality = 99;
+ break;
+ }
+
+ QImageWriter writer(pending.filename, fmt);
+ writer.setQuality(quality);
+
+ if (writer.write(image)) {
+ emit imageSaved(pending.id, pending.filename);
+ } else {
+ QImageCapture::Error err = QImageCapture::ResourceError;
+ if (writer.error() == QImageWriter::UnsupportedFormatError)
+ err = QImageCapture::FormatError;
+ emit error(pending.id, err, writer.errorString());
+ }
+ }
+
+ updateReadyForCapture();
+}
+
+void QFFmpegImageCapture::setupVideoSourceConnections()
+{
+ connect(m_videoSource, &QPlatformCamera::newVideoFrame, this,
+ &QFFmpegImageCapture::newVideoFrame);
+}
+
+QPlatformVideoSource *QFFmpegImageCapture::videoSource() const
+{
+ return m_videoSource;
+}
+
+void QFFmpegImageCapture::onVideoSourceChanged()
+{
+ if (m_videoSource)
+ m_videoSource->disconnect(this);
+
+ m_videoSource = m_session ? m_session->primaryActiveVideoSource() : nullptr;
+
+ // TODO: optimize, setup the connection only when the capture is ready
+ if (m_videoSource)
+ setupVideoSourceConnections();
+
+ updateReadyForCapture();
+}
+
+QImageEncoderSettings QFFmpegImageCapture::imageSettings() const
+{
+ return m_settings;
+}
+
+void QFFmpegImageCapture::setImageSettings(const QImageEncoderSettings &settings)
+{
+ auto s = settings;
+ const auto supportedFormats = QPlatformMediaIntegration::instance()->formatInfo()->imageFormats;
+ if (supportedFormats.isEmpty()) {
+ emit error(-1, QImageCapture::FormatError, "No image formats supported, can't capture.");
+ return;
+ }
+ if (s.format() == QImageCapture::UnspecifiedFormat) {
+ auto f = QImageCapture::JPEG;
+ if (!supportedFormats.contains(f))
+ f = supportedFormats.first();
+ s.setFormat(f);
+ } else if (!supportedFormats.contains(settings.format())) {
+ emit error(-1, QImageCapture::FormatError, "Image format not supported.");
+ return;
+ }
+
+ m_settings = 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
new file mode 100644
index 000000000..d8174ae05
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegimagecapture_p.h
@@ -0,0 +1,71 @@
+// 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
+#define QFFMPEGIMAGECAPTURE_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/qplatformimagecapture_p.h>
+#include "qffmpegmediacapturesession_p.h"
+
+#include <QtCore/qpointer.h>
+#include <qqueue.h>
+
+QT_BEGIN_NAMESPACE
+
+class QFFmpegImageCapture : public QPlatformImageCapture
+{
+ Q_OBJECT
+public:
+ QFFmpegImageCapture(QImageCapture *parent);
+ virtual ~QFFmpegImageCapture();
+
+ bool isReadyForCapture() const override;
+ int capture(const QString &fileName) override;
+ int captureToBuffer() override;
+
+ QImageEncoderSettings imageSettings() const override;
+ void setImageSettings(const QImageEncoderSettings &settings) override;
+
+ void setCaptureSession(QPlatformMediaCaptureSession *session);
+
+protected:
+ virtual int doCapture(const QString &fileName);
+ virtual void setupVideoSourceConnections();
+ QPlatformVideoSource *videoSource() const;
+ void updateReadyForCapture();
+
+protected Q_SLOTS:
+ void newVideoFrame(const QVideoFrame &frame);
+ void onVideoSourceChanged();
+
+private:
+ QFFmpegMediaCaptureSession *m_session = nullptr;
+ QPointer<QPlatformVideoSource> m_videoSource;
+ int m_lastId = 0;
+ QImageEncoderSettings m_settings;
+
+ struct PendingImage {
+ int id;
+ QString filename;
+ QMediaMetaData metaData;
+ };
+
+ QQueue<PendingImage> m_pendingImages;
+ bool m_isReadyForCapture = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGSTREAMERCAPTURECORNTROL_H
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
new file mode 100644
index 000000000..1b6db5813
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession.cpp
@@ -0,0 +1,318 @@
+// 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 "private/qplatformaudiobufferinput_p.h"
+#include "private/qplatformvideoframeinput_p.h"
+#include "private/qplatformcamera_p.h"
+
+#include "qffmpegimagecapture_p.h"
+#include "qffmpegmediarecorder_p.h"
+#include "qvideosink.h"
+#include "qffmpegaudioinput_p.h"
+#include "qaudiosink.h"
+#include "qaudiobuffer.h"
+#include "qaudiooutput.h"
+
+#include <qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+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() = default;
+
+QPlatformCamera *QFFmpegMediaCaptureSession::camera()
+{
+ return m_camera;
+}
+
+void QFFmpegMediaCaptureSession::setCamera(QPlatformCamera *camera)
+{
+ if (setVideoSource(m_camera, camera))
+ emit cameraChanged();
+}
+
+QPlatformSurfaceCapture *QFFmpegMediaCaptureSession::screenCapture()
+{
+ return m_screenCapture;
+}
+
+void QFFmpegMediaCaptureSession::setScreenCapture(QPlatformSurfaceCapture *screenCapture)
+{
+ if (setVideoSource(m_screenCapture, screenCapture))
+ emit screenCaptureChanged();
+}
+
+QPlatformSurfaceCapture *QFFmpegMediaCaptureSession::windowCapture()
+{
+ return m_windowCapture;
+}
+
+void QFFmpegMediaCaptureSession::setWindowCapture(QPlatformSurfaceCapture *windowCapture)
+{
+ if (setVideoSource(m_windowCapture, windowCapture))
+ emit windowCaptureChanged();
+}
+
+QPlatformVideoFrameInput *QFFmpegMediaCaptureSession::videoFrameInput()
+{
+ return m_videoFrameInput;
+}
+
+void QFFmpegMediaCaptureSession::setVideoFrameInput(QPlatformVideoFrameInput *input)
+{
+ if (setVideoSource(m_videoFrameInput, input))
+ emit videoFrameInputChanged();
+}
+
+QPlatformImageCapture *QFFmpegMediaCaptureSession::imageCapture()
+{
+ return m_imageCapture;
+}
+
+void QFFmpegMediaCaptureSession::setImageCapture(QPlatformImageCapture *imageCapture)
+{
+ if (m_imageCapture == imageCapture)
+ return;
+
+ if (m_imageCapture)
+ m_imageCapture->setCaptureSession(nullptr);
+
+ m_imageCapture = static_cast<QFFmpegImageCapture *>(imageCapture);
+
+ if (m_imageCapture)
+ m_imageCapture->setCaptureSession(this);
+
+ emit imageCaptureChanged();
+}
+
+void QFFmpegMediaCaptureSession::setMediaRecorder(QPlatformMediaRecorder *recorder)
+{
+ auto *r = static_cast<QFFmpegMediaRecorder *>(recorder);
+ if (m_mediaRecorder == r)
+ return;
+
+ if (m_mediaRecorder)
+ m_mediaRecorder->setCaptureSession(nullptr);
+ m_mediaRecorder = r;
+ if (m_mediaRecorder)
+ m_mediaRecorder->setCaptureSession(this);
+
+ emit encoderChanged();
+}
+
+QPlatformMediaRecorder *QFFmpegMediaCaptureSession::mediaRecorder()
+{
+ return m_mediaRecorder;
+}
+
+void QFFmpegMediaCaptureSession::setAudioInput(QPlatformAudioInput *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;
+
+ 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::setAudioBufferInput(QPlatformAudioBufferInput *input)
+{
+ // TODO: implement binding to audio sink like setAudioInput does
+ m_audioBufferInput = input;
+}
+
+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() const
+{
+ return m_audioInput;
+}
+
+void QFFmpegMediaCaptureSession::setVideoPreview(QVideoSink *sink)
+{
+ if (std::exchange(m_videoSink, sink) == sink)
+ return;
+
+ 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::updateVideoFrameConnection()
+{
+ 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;
+}
+
+std::vector<QPlatformAudioBufferInputBase *> QFFmpegMediaCaptureSession::activeAudioInputs() const
+{
+ std::vector<QPlatformAudioBufferInputBase *> result;
+ if (m_audioInput)
+ result.push_back(m_audioInput);
+
+ if (m_audioBufferInput)
+ result.push_back(m_audioBufferInput);
+
+ return result;
+}
+
+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
new file mode 100644
index 000000000..25340dad5
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession_p.h
@@ -0,0 +1,112 @@
+// 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
+
+//
+// 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/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 QPlatformAudioBufferInput;
+class QPlatformAudioBufferInputBase;
+
+class QFFmpegMediaCaptureSession : public QPlatformMediaCaptureSession
+{
+ Q_OBJECT
+
+public:
+ using VideoSources = std::vector<QPointer<QPlatformVideoSource>>;
+
+ 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;
+
+ QPlatformVideoFrameInput *videoFrameInput() override;
+ void setVideoFrameInput(QPlatformVideoFrameInput *) 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;
+
+ void setAudioBufferInput(QPlatformAudioBufferInput *input) override;
+
+ void setVideoPreview(QVideoSink *sink) override;
+ void setAudioOutput(QPlatformAudioOutput *output) override;
+
+ QPlatformVideoSource *primaryActiveVideoSource();
+
+ // it might be moved to the base class, but it needs QPlatformAudioInput
+ // to be QPlatformAudioBufferInputBase, which might not make sense
+ std::vector<QPlatformAudioBufferInputBase *> activeAudioInputs() const;
+
+private Q_SLOTS:
+ void updateAudioSink();
+ void updateVolume();
+ void updateVideoFrameConnection();
+ void updatePrimaryActiveVideoSource();
+
+Q_SIGNALS:
+ void primaryActiveVideoSourceChanged();
+
+private:
+ template<typename VideoSource>
+ bool setVideoSource(QPointer<VideoSource> &source, VideoSource *newSource);
+
+ QPointer<QPlatformCamera> m_camera;
+ QPointer<QPlatformSurfaceCapture> m_screenCapture;
+ QPointer<QPlatformSurfaceCapture> m_windowCapture;
+ QPointer<QPlatformVideoFrameInput> m_videoFrameInput;
+ QPointer<QPlatformVideoSource> m_primaryActiveVideoSource;
+
+ QPointer<QFFmpegAudioInput> m_audioInput;
+ QPointer<QPlatformAudioBufferInput> m_audioBufferInput;
+
+ 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
+
+#endif // QGSTREAMERCAPTURESERVICE_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo.cpp b/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo.cpp
new file mode 100644
index 000000000..6389b4eed
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo.cpp
@@ -0,0 +1,517 @@
+// 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 "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;
+} videoCodecMap [] = {
+ { AV_CODEC_ID_MPEG1VIDEO, QMediaFormat::VideoCodec::MPEG1 },
+ { AV_CODEC_ID_MPEG2VIDEO, QMediaFormat::VideoCodec::MPEG2 },
+ { AV_CODEC_ID_MPEG4, QMediaFormat::VideoCodec::MPEG4 },
+ { AV_CODEC_ID_H264, QMediaFormat::VideoCodec::H264 },
+ { AV_CODEC_ID_HEVC, QMediaFormat::VideoCodec::H265 },
+ { AV_CODEC_ID_VP8, QMediaFormat::VideoCodec::VP8 },
+ { AV_CODEC_ID_VP9, QMediaFormat::VideoCodec::VP9 },
+ { AV_CODEC_ID_AV1, QMediaFormat::VideoCodec::AV1 },
+ { AV_CODEC_ID_THEORA, QMediaFormat::VideoCodec::Theora },
+ { AV_CODEC_ID_WMV3, QMediaFormat::VideoCodec::WMV },
+ { AV_CODEC_ID_MJPEG, QMediaFormat::VideoCodec::MotionJPEG }
+};
+
+static AVCodecID codecId(QMediaFormat::VideoCodec codec)
+{
+ for (const auto &c : videoCodecMap) {
+ if (c.codec == codec)
+ return c.id;
+ }
+ return AV_CODEC_ID_NONE;
+}
+
+static struct {
+ AVCodecID id;
+ QMediaFormat::AudioCodec codec;
+} audioCodecMap [] = {
+ { AV_CODEC_ID_MP3, QMediaFormat::AudioCodec::MP3 },
+ { AV_CODEC_ID_AAC, QMediaFormat::AudioCodec::AAC },
+ { AV_CODEC_ID_AC3, QMediaFormat::AudioCodec::AC3 },
+ { AV_CODEC_ID_EAC3, QMediaFormat::AudioCodec::EAC3 },
+ { AV_CODEC_ID_FLAC, QMediaFormat::AudioCodec::FLAC },
+ { AV_CODEC_ID_TRUEHD, QMediaFormat::AudioCodec::DolbyTrueHD },
+ { AV_CODEC_ID_OPUS, QMediaFormat::AudioCodec::Opus },
+ { AV_CODEC_ID_VORBIS, QMediaFormat::AudioCodec::Vorbis },
+ { AV_CODEC_ID_PCM_S16LE, QMediaFormat::AudioCodec::Wave },
+ { AV_CODEC_ID_WMAPRO, QMediaFormat::AudioCodec::WMA },
+ { AV_CODEC_ID_ALAC, QMediaFormat::AudioCodec::ALAC }
+};
+
+static AVCodecID codecId(QMediaFormat::AudioCodec codec)
+{
+ for (const auto &c : audioCodecMap) {
+ if (c.codec == codec)
+ return c.id;
+ }
+ return AV_CODEC_ID_NONE;
+}
+
+// mimetypes are mostly copied from qmediaformat.cpp. Unfortunately, FFmpeg uses
+// in some cases slightly different mimetypes
+static const struct
+{
+ QMediaFormat::FileFormat fileFormat;
+ const char *mimeType;
+ const char *name; // disambiguate if we have several muxers/demuxers
+} map[QMediaFormat::LastFileFormat + 1] = {
+ { QMediaFormat::WMV, "video/x-ms-asf", "asf" },
+ { QMediaFormat::AVI, "video/x-msvideo", nullptr },
+ { QMediaFormat::Matroska, "video/x-matroska", nullptr },
+ { QMediaFormat::MPEG4, "video/mp4", "mp4" },
+ { QMediaFormat::Ogg, "video/ogg", nullptr },
+ // QuickTime is the same as MP4
+ { QMediaFormat::WebM, "video/webm", "webm" },
+ // Audio Formats
+ // Mpeg4Audio is the same as MP4 without the video codecs
+ { QMediaFormat::AAC, "audio/aac", nullptr },
+ // WMA is the same as WMV
+ { QMediaFormat::FLAC, "audio/x-flac", nullptr },
+ { QMediaFormat::MP3, "audio/mpeg", "mp3" },
+ { QMediaFormat::Wave, "audio/x-wav", nullptr },
+ { QMediaFormat::UnspecifiedFormat, nullptr, nullptr }
+};
+
+template <typename AVFormat>
+static QMediaFormat::FileFormat formatForAVFormat(AVFormat *format)
+{
+
+ if (!format->mime_type || !*format->mime_type)
+ return QMediaFormat::UnspecifiedFormat;
+
+ auto *m = map;
+ while (m->fileFormat != QMediaFormat::UnspecifiedFormat) {
+ if (m->mimeType && !strcmp(m->mimeType, format->mime_type)) {
+ // check if the name matches. This is used to disambiguate where FFmpeg provides
+ // multiple muxers or demuxers
+ if (!m->name || !strcmp(m->name, format->name))
+ return m->fileFormat;
+ }
+ ++m;
+ }
+
+ return QMediaFormat::UnspecifiedFormat;
+}
+
+static const AVOutputFormat *avFormatForFormat(QMediaFormat::FileFormat format)
+{
+ if (format == QMediaFormat::QuickTime || format == QMediaFormat::Mpeg4Audio)
+ format = QMediaFormat::MPEG4;
+ if (format == QMediaFormat::WMA)
+ format = QMediaFormat::WMV;
+
+ auto *m = map;
+ while (m->fileFormat != QMediaFormat::UnspecifiedFormat) {
+ if (m->fileFormat == format)
+ return av_guess_format(m->name, nullptr, m->mimeType);
+ ++m;
+ }
+
+ return nullptr;
+}
+
+
+QFFmpegMediaFormatInfo::QFFmpegMediaFormatInfo()
+{
+ qCDebug(qLcMediaFormatInfo) << ">>>> listing codecs";
+
+ QList<QMediaFormat::AudioCodec> audioEncoders;
+ QList<QMediaFormat::AudioCodec> extraAudioDecoders;
+ QList<QMediaFormat::VideoCodec> videoEncoders;
+ QList<QMediaFormat::VideoCodec> extraVideoDecoders;
+
+ const AVCodecDescriptor *descriptor = nullptr;
+ while ((descriptor = avcodec_descriptor_next(descriptor))) {
+ 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) {
+ if (canEncode) {
+ if (!videoEncoders.contains(videoCodec))
+ videoEncoders.append(videoCodec);
+ } else if (canDecode) {
+ if (!extraVideoDecoders.contains(videoCodec))
+ extraVideoDecoders.append(videoCodec);
+ }
+ } else if (descriptor->type == AVMEDIA_TYPE_AUDIO
+ && audioCodec != QMediaFormat::AudioCodec::Unspecified) {
+ if (canEncode) {
+ if (!audioEncoders.contains(audioCodec))
+ audioEncoders.append(audioCodec);
+ } else if (canDecode) {
+ if (!extraAudioDecoders.contains(audioCodec))
+ extraAudioDecoders.append(audioCodec);
+ }
+ }
+ }
+
+ // get demuxers
+// 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;
+// qCDebug(qLcMediaFormatInfo) << " mux:" << outputFormat->name << outputFormat->long_name << outputFormat->mime_type << outputFormat->extensions << mediaFormat;
+
+ CodecMap encoder;
+ encoder.format = mediaFormat;
+
+ for (auto codec : audioEncoders) {
+ auto id = codecId(codec);
+ // 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
+// qCDebug(qLcMediaFormatInfo) << " " << codec << Qt::hex << av_codec_get_tag(outputFormat->codec_tag, id);
+ encoder.audio.append(codec);
+ }
+ }
+ for (auto codec : videoEncoders) {
+ auto id = codecId(codec);
+ // 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
+// qCDebug(qLcMediaFormatInfo) << " " << codec << Qt::hex << av_codec_get_tag(outputFormat->codec_tag, id);
+ encoder.video.append(codec);
+ }
+ }
+
+ // sanity checks and handling special cases
+ if (encoder.audio.isEmpty() && encoder.video.isEmpty())
+ continue;
+ switch (encoder.format) {
+ case QMediaFormat::WMV:
+ // add WMA
+ encoders.append({ QMediaFormat::WMA, encoder.audio, {} });
+ break;
+ case QMediaFormat::MPEG4:
+ // add Mpeg4Audio and QuickTime
+ encoders.append({ QMediaFormat::QuickTime, encoder.audio, encoder.video });
+ encoders.append({ QMediaFormat::Mpeg4Audio, encoder.audio, {} });
+ break;
+ case QMediaFormat::Wave:
+ // FFmpeg allows other encoded formats in WAV containers, but we do not want that
+ if (!encoder.audio.contains(QMediaFormat::AudioCodec::Wave))
+ continue;
+ encoder.audio = { QMediaFormat::AudioCodec::Wave };
+ break;
+ default:
+ break;
+ }
+ encoders.append(encoder);
+ }
+
+ // FFmpeg doesn't allow querying supported codecs for decoders
+ // we take a simple approximation stating that we can decode what we
+ // can encode. That's a safe subset.
+ decoders = encoders;
+
+#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);
+ decoders[QMediaFormat::WMV].audio.append(QMediaFormat::AudioCodec::WMA);
+ }
+ if (extraVideoDecoders.contains(QMediaFormat::VideoCodec::WMV)) {
+ decoders[QMediaFormat::WMV].video.append(QMediaFormat::VideoCodec::WMV);
+ }
+
+ // Add image formats we support. We currently simply use Qt's built-in image write
+ // to save images. That doesn't give us HDR support or support for larger bit depths,
+ // but most cameras can currently not generate those anyway.
+ const auto imgFormats = QImageWriter::supportedImageFormats();
+ for (const auto &f : imgFormats) {
+ if (f == "png")
+ imageFormats.append(QImageCapture::PNG);
+ else if (f == "jpeg")
+ imageFormats.append(QImageCapture::JPEG);
+ else if (f == "tiff")
+ imageFormats.append(QImageCapture::Tiff);
+ else if (f == "webp")
+ imageFormats.append(QImageCapture::WebP);
+ }
+
+}
+
+QFFmpegMediaFormatInfo::~QFFmpegMediaFormatInfo() = default;
+
+QMediaFormat::AudioCodec QFFmpegMediaFormatInfo::audioCodecForAVCodecId(AVCodecID id)
+{
+ for (const auto &c : audioCodecMap) {
+ if (c.id == id)
+ return c.codec;
+ }
+ return QMediaFormat::AudioCodec::Unspecified;
+}
+
+QMediaFormat::VideoCodec QFFmpegMediaFormatInfo::videoCodecForAVCodecId(AVCodecID id)
+{
+ for (const auto &c : videoCodecMap) {
+ if (c.id == id)
+ return c.codec;
+ }
+ return QMediaFormat::VideoCodec::Unspecified;
+}
+
+QMediaFormat::FileFormat
+QFFmpegMediaFormatInfo::fileFormatForAVInputFormat(const AVInputFormat *format)
+{
+ // Seems like FFmpeg uses different names for muxers and demuxers of the same format.
+ // that makes it somewhat cumbersome to detect things correctly.
+ // The input formats have a comma separated list of short names. We check the first one of those
+ // as the docs specify that you only append to the list
+ static const struct
+ {
+ QMediaFormat::FileFormat fileFormat;
+ const char *name;
+ } map[QMediaFormat::LastFileFormat + 1] = {
+ { QMediaFormat::WMV, "asf" },
+ { QMediaFormat::AVI, "avi" },
+ { QMediaFormat::Matroska, "matroska" },
+ { QMediaFormat::MPEG4, "mov" },
+ { QMediaFormat::Ogg, "ogg" },
+ { QMediaFormat::WebM, "webm" },
+ // Audio Formats
+ // Mpeg4Audio is the same as MP4 without the video codecs
+ { QMediaFormat::AAC, "aac"},
+ // WMA is the same as WMV
+ { QMediaFormat::FLAC, "flac" },
+ { QMediaFormat::MP3, "mp3" },
+ { QMediaFormat::Wave, "wav" },
+ { QMediaFormat::UnspecifiedFormat, nullptr }
+ };
+
+ if (!format->name)
+ return QMediaFormat::UnspecifiedFormat;
+
+ auto *m = map;
+ while (m->fileFormat != QMediaFormat::UnspecifiedFormat) {
+ if (!strncmp(m->name, format->name, strlen(m->name)))
+ return m->fileFormat;
+ ++m;
+ }
+
+ return QMediaFormat::UnspecifiedFormat;
+}
+
+const AVOutputFormat *
+QFFmpegMediaFormatInfo::outputFormatForFileFormat(QMediaFormat::FileFormat format)
+{
+ return avFormatForFormat(format);
+}
+
+AVCodecID QFFmpegMediaFormatInfo::codecIdForVideoCodec(QMediaFormat::VideoCodec codec)
+{
+ return codecId(codec);
+}
+
+AVCodecID QFFmpegMediaFormatInfo::codecIdForAudioCodec(QMediaFormat::AudioCodec codec)
+{
+ return codecId(codec);
+}
+
+QAudioFormat::SampleFormat QFFmpegMediaFormatInfo::sampleFormat(AVSampleFormat format)
+{
+ switch (format) {
+ case AV_SAMPLE_FMT_NONE:
+ default:
+ return QAudioFormat::Unknown;
+ case AV_SAMPLE_FMT_U8: ///< unsigned 8 bits
+ case AV_SAMPLE_FMT_U8P: ///< unsigned 8 bits: planar
+ return QAudioFormat::UInt8;
+ case AV_SAMPLE_FMT_S16: ///< signed 16 bits
+ case AV_SAMPLE_FMT_S16P: ///< signed 16 bits: planar
+ return QAudioFormat::Int16;
+ case AV_SAMPLE_FMT_S32: ///< signed 32 bits
+ case AV_SAMPLE_FMT_S32P: ///< signed 32 bits: planar
+ return QAudioFormat::Int32;
+ case AV_SAMPLE_FMT_FLT: ///< float
+ case AV_SAMPLE_FMT_FLTP: ///< float: planar
+ return QAudioFormat::Float;
+ case AV_SAMPLE_FMT_DBL: ///< double
+ case AV_SAMPLE_FMT_DBLP: ///< double: planar
+ case AV_SAMPLE_FMT_S64: ///< signed 64 bits
+ case AV_SAMPLE_FMT_S64P: ///< signed 64 bits, planar
+ // let's use float
+ return QAudioFormat::Float;
+ }
+}
+
+AVSampleFormat QFFmpegMediaFormatInfo::avSampleFormat(QAudioFormat::SampleFormat format)
+{
+ switch (format) {
+ case QAudioFormat::UInt8:
+ return AV_SAMPLE_FMT_U8;
+ case QAudioFormat::Int16:
+ return AV_SAMPLE_FMT_S16;
+ case QAudioFormat::Int32:
+ return AV_SAMPLE_FMT_S32;
+ case QAudioFormat::Float:
+ return AV_SAMPLE_FMT_FLT;
+ default:
+ return AV_SAMPLE_FMT_NONE;
+ }
+}
+
+int64_t QFFmpegMediaFormatInfo::avChannelLayout(QAudioFormat::ChannelConfig channelConfig)
+{
+ int64_t avChannelLayout = 0;
+ if (channelConfig & (1 << QAudioFormat::FrontLeft))
+ avChannelLayout |= AV_CH_FRONT_LEFT;
+ if (channelConfig & (1 << QAudioFormat::FrontRight))
+ avChannelLayout |= AV_CH_FRONT_RIGHT;
+ if (channelConfig & (1 << QAudioFormat::FrontCenter))
+ avChannelLayout |= AV_CH_FRONT_CENTER;
+ if (channelConfig & (1 << QAudioFormat::LFE))
+ avChannelLayout |= AV_CH_LOW_FREQUENCY;
+ if (channelConfig & (1 << QAudioFormat::BackLeft))
+ avChannelLayout |= AV_CH_BACK_LEFT;
+ if (channelConfig & (1 << QAudioFormat::BackRight))
+ avChannelLayout |= AV_CH_BACK_RIGHT;
+ if (channelConfig & (1 << QAudioFormat::FrontLeftOfCenter))
+ avChannelLayout |= AV_CH_FRONT_LEFT_OF_CENTER;
+ if (channelConfig & (1 << QAudioFormat::FrontRightOfCenter))
+ avChannelLayout |= AV_CH_FRONT_RIGHT_OF_CENTER;
+ if (channelConfig & (1 << QAudioFormat::BackCenter))
+ avChannelLayout |= AV_CH_BACK_CENTER;
+ if (channelConfig & (1 << QAudioFormat::LFE2))
+ avChannelLayout |= AV_CH_LOW_FREQUENCY_2;
+ if (channelConfig & (1 << QAudioFormat::SideLeft))
+ avChannelLayout |= AV_CH_SIDE_LEFT;
+ if (channelConfig & (1 << QAudioFormat::SideRight))
+ avChannelLayout |= AV_CH_SIDE_RIGHT;
+ if (channelConfig & (1 << QAudioFormat::TopFrontLeft))
+ avChannelLayout |= AV_CH_TOP_FRONT_LEFT;
+ if (channelConfig & (1 << QAudioFormat::TopFrontRight))
+ avChannelLayout |= AV_CH_TOP_FRONT_RIGHT;
+ if (channelConfig & (1 << QAudioFormat::TopFrontCenter))
+ avChannelLayout |= AV_CH_TOP_FRONT_CENTER;
+ if (channelConfig & (1 << QAudioFormat::TopCenter))
+ avChannelLayout |= AV_CH_TOP_CENTER;
+ if (channelConfig & (1 << QAudioFormat::TopBackLeft))
+ avChannelLayout |= AV_CH_TOP_BACK_LEFT;
+ if (channelConfig & (1 << QAudioFormat::TopBackRight))
+ avChannelLayout |= AV_CH_TOP_BACK_RIGHT;
+ if (channelConfig & (1 << QAudioFormat::TopBackCenter))
+ avChannelLayout |= AV_CH_TOP_BACK_CENTER;
+ // The defines used below got added together for FFmpeg 4.4
+#ifdef AV_CH_TOP_SIDE_LEFT
+ if (channelConfig & (1 << QAudioFormat::TopSideLeft))
+ avChannelLayout |= AV_CH_TOP_SIDE_LEFT;
+ if (channelConfig & (1 << QAudioFormat::TopSideRight))
+ avChannelLayout |= AV_CH_TOP_SIDE_RIGHT;
+ if (channelConfig & (1 << QAudioFormat::BottomFrontCenter))
+ avChannelLayout |= AV_CH_BOTTOM_FRONT_CENTER;
+ if (channelConfig & (1 << QAudioFormat::BottomFrontLeft))
+ avChannelLayout |= AV_CH_BOTTOM_FRONT_LEFT;
+ if (channelConfig & (1 << QAudioFormat::BottomFrontRight))
+ avChannelLayout |= AV_CH_BOTTOM_FRONT_RIGHT;
+#endif
+ return avChannelLayout;
+}
+
+QAudioFormat::ChannelConfig QFFmpegMediaFormatInfo::channelConfigForAVLayout(int64_t avChannelLayout)
+{
+ quint32 channelConfig = 0;
+ if (avChannelLayout & AV_CH_FRONT_LEFT)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::FrontLeft);
+ if (avChannelLayout & AV_CH_FRONT_RIGHT)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::FrontRight);
+ if (avChannelLayout & AV_CH_FRONT_CENTER)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::FrontCenter);
+ if (avChannelLayout & AV_CH_LOW_FREQUENCY)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::LFE);
+ if (avChannelLayout & AV_CH_BACK_LEFT)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::BackLeft);
+ if (avChannelLayout & AV_CH_BACK_RIGHT)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::BackRight);
+ if (avChannelLayout & AV_CH_FRONT_LEFT_OF_CENTER)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::FrontLeftOfCenter);
+ if (avChannelLayout & AV_CH_FRONT_RIGHT_OF_CENTER)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::FrontRightOfCenter);
+ if (avChannelLayout & AV_CH_BACK_CENTER)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::BackCenter);
+ if (avChannelLayout & AV_CH_LOW_FREQUENCY_2)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::LFE2);
+ if (avChannelLayout & AV_CH_SIDE_LEFT)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::SideLeft);
+ if (avChannelLayout & AV_CH_SIDE_RIGHT)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::SideRight);
+ if (avChannelLayout & AV_CH_TOP_FRONT_LEFT)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopFrontLeft);
+ if (avChannelLayout & AV_CH_TOP_FRONT_RIGHT)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopFrontRight);
+ if (avChannelLayout & AV_CH_TOP_FRONT_CENTER)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopFrontCenter);
+ if (avChannelLayout & AV_CH_TOP_CENTER)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopCenter);
+ if (avChannelLayout & AV_CH_TOP_BACK_LEFT)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopBackLeft);
+ if (avChannelLayout & AV_CH_TOP_BACK_RIGHT)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopBackRight);
+ if (avChannelLayout & AV_CH_TOP_BACK_CENTER)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopBackCenter);
+ // The defines used below got added together for FFmpeg 4.4
+#ifdef AV_CH_TOP_SIDE_LEFT
+ if (avChannelLayout & AV_CH_TOP_SIDE_LEFT)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopSideLeft);
+ if (avChannelLayout & AV_CH_TOP_SIDE_RIGHT)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopSideRight);
+ if (avChannelLayout & AV_CH_BOTTOM_FRONT_CENTER)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::BottomFrontCenter);
+ if (avChannelLayout & AV_CH_BOTTOM_FRONT_LEFT)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::BottomFrontLeft);
+ if (avChannelLayout & AV_CH_BOTTOM_FRONT_RIGHT)
+ channelConfig |= QAudioFormat::channelConfig(QAudioFormat::BottomFrontRight);
+#endif
+ return QAudioFormat::ChannelConfig(channelConfig);
+}
+
+QAudioFormat QFFmpegMediaFormatInfo::audioFormatFromCodecParameters(AVCodecParameters *codecpar)
+{
+ QAudioFormat format;
+ format.setSampleFormat(sampleFormat(AVSampleFormat(codecpar->format)));
+ format.setSampleRate(codecpar->sample_rate);
+#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;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo_p.h b/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo_p.h
new file mode 100644
index 000000000..52fcf6f72
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo_p.h
@@ -0,0 +1,52 @@
+// 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
+
+//
+// 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/qplatformmediaformatinfo_p.h>
+#include <qhash.h>
+#include <qlist.h>
+#include <qaudioformat.h>
+#include "qffmpeg_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QFFmpegMediaFormatInfo : public QPlatformMediaFormatInfo
+{
+public:
+ QFFmpegMediaFormatInfo();
+ ~QFFmpegMediaFormatInfo();
+
+ static QMediaFormat::VideoCodec videoCodecForAVCodecId(AVCodecID id);
+ static QMediaFormat::AudioCodec audioCodecForAVCodecId(AVCodecID id);
+ static QMediaFormat::FileFormat fileFormatForAVInputFormat(const AVInputFormat *format);
+
+ static const AVOutputFormat *outputFormatForFileFormat(QMediaFormat::FileFormat format);
+
+ static AVCodecID codecIdForVideoCodec(QMediaFormat::VideoCodec codec);
+ static AVCodecID codecIdForAudioCodec(QMediaFormat::AudioCodec codec);
+
+ static QAudioFormat::SampleFormat sampleFormat(AVSampleFormat format);
+ static AVSampleFormat avSampleFormat(QAudioFormat::SampleFormat format);
+
+ static int64_t avChannelLayout(QAudioFormat::ChannelConfig channelConfig);
+ static QAudioFormat::ChannelConfig channelConfigForAVLayout(int64_t avChannelLayout);
+
+ static QAudioFormat audioFormatFromCodecParameters(AVCodecParameters *codecPar);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration.cpp b/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration.cpp
new file mode 100644
index 000000000..ba1fff3b3
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration.cpp
@@ -0,0 +1,375 @@
+// 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>
+#include "qffmpegmediaintegration_p.h"
+#include "qffmpegmediaformatinfo_p.h"
+#include "qffmpegmediaplayer_p.h"
+#include "qffmpegvideosink_p.h"
+#include "qffmpegmediacapturesession_p.h"
+#include "qffmpegmediarecorder_p.h"
+#include "qffmpegimagecapture_p.h"
+#include "qffmpegaudioinput_p.h"
+#include "qffmpegaudiodecoder_p.h"
+#include "qffmpegresampler_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 <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
+
+class QFFmpegMediaPlugin : public QPlatformMediaPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QPlatformMediaPlugin_iid FILE "ffmpeg.json")
+
+public:
+ QFFmpegMediaPlugin()
+ : QPlatformMediaPlugin()
+ {}
+
+ QPlatformMediaIntegration* create(const QString &name) override
+ {
+ if (name == u"ffmpeg")
+ return new QFFmpegMediaIntegration;
+ return nullptr;
+ }
+};
+
+bool thread_local FFmpegLogsEnabledInThread = true;
+static bool UseCustomFFmpegLogger = false;
+
+static void qffmpegLogCallback(void *ptr, int level, const char *fmt, va_list vl)
+{
+ if (!FFmpegLogsEnabledInThread)
+ return;
+
+ 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
+
+#if QT_CONFIG(xlib)
+ if (backend == u"x11")
+ return new QX11SurfaceCapture(QPlatformSurfaceCapture::ScreenSource{});
+#elif defined(Q_OS_WINDOWS)
+ if (backend == u"dxgi")
+ return new QFFmpegScreenCaptureDxgi;
+#elif defined(Q_OS_MACOS)
+ if (backend == u"avf")
+ return new QAVFScreenCapture;
+#endif
+ return nullptr;
+}
+
+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()
+ : QPlatformMediaIntegration(QLatin1String("ffmpeg"))
+{
+ 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
+}
+
+QMaybe<QPlatformAudioDecoder *> QFFmpegMediaIntegration::createAudioDecoder(QAudioDecoder *decoder)
+{
+ return new QFFmpegAudioDecoder(decoder);
+}
+
+QMaybe<std::unique_ptr<QPlatformAudioResampler>>
+QFFmpegMediaIntegration::createAudioResampler(const QAudioFormat &inputFormat,
+ const QAudioFormat &outputFormat)
+{
+ return { std::make_unique<QFFmpegResampler>(inputFormat, outputFormat) };
+}
+
+QMaybe<QPlatformMediaCaptureSession *> QFFmpegMediaIntegration::createCaptureSession()
+{
+ return new QFFmpegMediaCaptureSession();
+}
+
+QMaybe<QPlatformMediaPlayer *> QFFmpegMediaIntegration::createPlayer(QMediaPlayer *player)
+{
+ return new QFFmpegMediaPlayer(player);
+}
+
+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)
+ return new QWindowsCamera(camera);
+#else
+ Q_UNUSED(camera);
+ return nullptr;//new QFFmpegCamera(camera);
+#endif
+}
+
+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);
+}
+
+QMaybe<QPlatformImageCapture *> QFFmpegMediaIntegration::createImageCapture(QImageCapture *imageCapture)
+{
+#if defined(Q_OS_ANDROID)
+ return new QAndroidImageCapture(imageCapture);
+#else
+ return new QFFmpegImageCapture(imageCapture);
+#endif
+}
+
+QMaybe<QPlatformVideoSink *> QFFmpegMediaIntegration::createVideoSink(QVideoSink *sink)
+{
+ return new QFFmpegVideoSink(sink);
+}
+
+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;
+ if (initialized)
+ return JNI_VERSION_1_6;
+ initialized = true;
+
+ QT_USE_NAMESPACE
+ void *environment;
+ if (vm->GetEnv(&environment, JNI_VERSION_1_6))
+ return JNI_ERR;
+
+ // setting our javavm into ffmpeg.
+ if (av_jni_set_java_vm(vm, nullptr))
+ return JNI_ERR;
+
+ if (!QAndroidCamera::registerNativeMethods())
+ return JNI_ERR;
+
+ return JNI_VERSION_1_6;
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "qffmpegmediaintegration.moc"
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration_p.h b/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration_p.h
new file mode 100644
index 000000000..473a5f044
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration_p.h
@@ -0,0 +1,57 @@
+// 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
+
+//
+// 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 QFFmpegMediaFormatInfo;
+
+class QFFmpegMediaIntegration : public QPlatformMediaIntegration
+{
+public:
+ QFFmpegMediaIntegration();
+
+ 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;
+
+ QMaybe<QPlatformVideoSink *> createVideoSink(QVideoSink *sink) override;
+
+ QMaybe<QPlatformAudioInput *> createAudioInput(QAudioInput *input) override;
+// QPlatformAudioOutput *createAudioOutput(QAudioOutput *) override;
+
+ QVideoFrame convertVideoFrame(QVideoFrame &srcFrame,
+ const QVideoFrameFormat &destFormat) override;
+
+protected:
+ QPlatformMediaFormatInfo *createFormatInfo() override;
+
+ QPlatformVideoDevices *createVideoDevices() override;
+
+ QPlatformCapturableWindows *createCapturableWindows() override;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediametadata.cpp b/src/plugins/multimedia/ffmpeg/qffmpegmediametadata.cpp
new file mode 100644
index 000000000..465e380db
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediametadata.cpp
@@ -0,0 +1,182 @@
+// 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>
+#include <QtCore/qdatetime.h>
+#include <qstringlist.h>
+#include <qurl.h>
+#include <qlocale.h>
+
+#include <qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLcMetaData, "qt.multimedia.ffmpeg.metadata")
+
+namespace {
+
+struct ffmpegTagToMetaDataKey
+{
+ const char *tag;
+ QMediaMetaData::Key key;
+};
+
+constexpr ffmpegTagToMetaDataKey ffmpegTagToMetaDataKey[] = {
+ { "title", QMediaMetaData::Title },
+ { "comment", QMediaMetaData::Comment },
+ { "description", QMediaMetaData::Description },
+ { "genre", QMediaMetaData::Genre },
+ { "date", QMediaMetaData::Date },
+ { "year", QMediaMetaData::Date },
+ { "creation_time", QMediaMetaData::Date },
+
+ { "language", QMediaMetaData::Language },
+
+ { "copyright", QMediaMetaData::Copyright },
+
+ // Music
+ { "album", QMediaMetaData::AlbumTitle },
+ { "album_artist", QMediaMetaData::AlbumArtist },
+ { "artist", QMediaMetaData::ContributingArtist },
+ { "track", QMediaMetaData::TrackNumber },
+
+ // Movie
+ { "performer", QMediaMetaData::LeadPerformer },
+
+ { nullptr, QMediaMetaData::Title }
+};
+
+}
+
+static QMediaMetaData::Key tagToKey(const char *tag)
+{
+ const auto *map = ffmpegTagToMetaDataKey;
+ while (map->tag) {
+ if (!strcmp(map->tag, tag))
+ return map->key;
+ ++map;
+ }
+ return QMediaMetaData::Key(-1);
+}
+
+static const char *keyToTag(QMediaMetaData::Key key)
+{
+ const auto *map = ffmpegTagToMetaDataKey;
+ while (map->tag) {
+ if (map->key == key)
+ return map->tag;
+ ++map;
+ }
+ return nullptr;
+}
+
+//internal
+void QFFmpegMetaData::addEntry(QMediaMetaData &metaData, AVDictionaryEntry *entry)
+{
+ qCDebug(qLcMetaData) << " checking:" << entry->key << entry->value;
+ QByteArray tag(entry->key);
+ QMediaMetaData::Key key = tagToKey(tag.toLower());
+ if (key == QMediaMetaData::Key(-1))
+ return;
+ qCDebug(qLcMetaData) << " adding" << key;
+
+ auto *map = &metaData;
+
+ int metaTypeId = keyType(key).id();
+ switch (metaTypeId) {
+ case qMetaTypeId<QString>():
+ map->insert(key, QString::fromUtf8(entry->value));
+ return;
+ case qMetaTypeId<QStringList>():
+ map->insert(key, QString::fromUtf8(entry->value).split(QLatin1Char(',')));
+ return;
+ case qMetaTypeId<QDateTime>(): {
+ QDateTime date;
+ if (!qstrcmp(entry->key, "year")) {
+ if (map->keys().contains(QMediaMetaData::Date))
+ return;
+ date = QDateTime(QDate(QByteArray(entry->value).toInt(), 1, 1), QTime(0, 0, 0));
+ } else {
+ date = QDateTime::fromString(QString::fromUtf8(entry->value), Qt::ISODate);
+ }
+ map->insert(key, date);
+ return;
+ }
+ case qMetaTypeId<QUrl>():
+ map->insert(key, QUrl::fromEncoded(entry->value));
+ return;
+ case qMetaTypeId<qint64>():
+ map->insert(key, (qint64)QByteArray(entry->value).toLongLong());
+ return;
+ case qMetaTypeId<int>():
+ map->insert(key, QByteArray(entry->value).toInt());
+ return;
+ case qMetaTypeId<qreal>():
+ map->insert(key, (qreal)QByteArray(entry->value).toDouble());
+ return;
+ default:
+ break;
+ }
+ if (metaTypeId == qMetaTypeId<QLocale::Language>()) {
+ map->insert(key, QVariant::fromValue(QLocale::codeToLanguage(QString::fromUtf8(entry->value), QLocale::ISO639Part2)));
+ }
+}
+
+
+QMediaMetaData QFFmpegMetaData::fromAVMetaData(const AVDictionary *tags)
+{
+ QMediaMetaData metaData;
+ AVDictionaryEntry *entry = nullptr;
+ while ((entry = av_dict_get(tags, "", entry, AV_DICT_IGNORE_SUFFIX)))
+ addEntry(metaData, entry);
+
+ return metaData;
+}
+
+QByteArray QFFmpegMetaData::value(const QMediaMetaData &metaData, QMediaMetaData::Key key)
+{
+ const int metaTypeId = keyType(key).id();
+ const QVariant val = metaData.value(key);
+ switch (metaTypeId) {
+ case qMetaTypeId<QString>():
+ return val.toString().toUtf8();
+ case qMetaTypeId<QStringList>():
+ return val.toStringList().join(u",").toUtf8();
+ case qMetaTypeId<QDateTime>():
+ return val.toDateTime().toString(Qt::ISODate).toUtf8();
+ case qMetaTypeId<QUrl>():
+ return val.toUrl().toEncoded();
+ case qMetaTypeId<qint64>():
+ case qMetaTypeId<int>():
+ return QByteArray::number(val.toLongLong());
+ case qMetaTypeId<qreal>():
+ return QByteArray::number(val.toDouble());
+ default:
+ break;
+ }
+ if (metaTypeId == qMetaTypeId<QLocale::Language>())
+ return QLocale::languageToCode(val.value<QLocale::Language>(), QLocale::ISO639Part2).toUtf8();
+ return {};
+}
+
+
+AVDictionary *QFFmpegMetaData::toAVMetaData(const QMediaMetaData &metaData)
+{
+ const QList<Key> keys = metaData.keys();
+ AVDictionary *dict = nullptr;
+ for (const auto &k : keys) {
+ const char *key = ::keyToTag(k);
+ if (!key)
+ continue;
+ QByteArray val = value(metaData, k);
+ if (val.isEmpty())
+ continue;
+ av_dict_set(&dict, key, val.constData(), 0);
+ }
+ return dict;
+}
+
+
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediametadata_p.h b/src/plugins/multimedia/ffmpeg/qffmpegmediametadata_p.h
new file mode 100644
index 000000000..201287495
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediametadata_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 QFFMPEGMEDIAMETADATA_H
+#define QFFMPEGMEDIAMETADATA_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 <qffmpeg_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QFFmpegMetaData : public QMediaMetaData
+{
+public:
+ static void addEntry(QMediaMetaData &metaData, AVDictionaryEntry *entry);
+ static QMediaMetaData fromAVMetaData(const AVDictionary *tags);
+
+ static QByteArray value(const QMediaMetaData &metaData, QMediaMetaData::Key key);
+ static AVDictionary *toAVMetaData(const QMediaMetaData &metaData);
+};
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGMEDIAMETADATA_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediaplayer.cpp b/src/plugins/multimedia/ffmpeg/qffmpegmediaplayer.cpp
new file mode 100644
index 000000000..951144692
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediaplayer.cpp
@@ -0,0 +1,411 @@
+// 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 "private/qplatformaudiooutput_p.h"
+#include "qvideosink.h"
+#include "qaudiooutput.h"
+#include "qaudiobufferoutput.h"
+
+#include "qffmpegplaybackengine_p.h"
+#include <qiodevice.h>
+#include <qvideosink.h>
+#include <qtimer.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()
+{
+ if (m_cancelToken)
+ m_cancelToken->cancel();
+
+ m_loadMedia.waitForFinished();
+};
+
+qint64 QFFmpegMediaPlayer::duration() const
+{
+ return m_playbackEngine ? m_playbackEngine->duration() / 1000 : 0;
+}
+
+void QFFmpegMediaPlayer::setPosition(qint64 position)
+{
+ 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()
+{
+ // stop update timer and report end position anyway
+ m_positionUpdateTimer.stop();
+ QPointer currentPlaybackEngine(m_playbackEngine.get());
+ positionChanged(duration());
+
+ // skip changing state and mediaStatus if playbackEngine has been recreated,
+ // e.g. if new media has been loaded as a response to positionChanged signal
+ if (currentPlaybackEngine)
+ stateChanged(QMediaPlayer::StoppedState);
+ if (currentPlaybackEngine)
+ 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 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
+{
+ return {};
+}
+
+qreal QFFmpegMediaPlayer::playbackRate() const
+{
+ return m_playbackRate;
+}
+
+void QFFmpegMediaPlayer::setPlaybackRate(qreal rate)
+{
+ const float effectiveRate = std::max(static_cast<float>(rate), 0.0f);
+
+ if (qFuzzyCompare(m_playbackRate, effectiveRate))
+ return;
+
+ m_playbackRate = effectiveRate;
+
+ if (m_playbackEngine)
+ m_playbackEngine->setPlaybackRate(effectiveRate);
+
+ playbackRateChanged(effectiveRate);
+}
+
+QUrl QFFmpegMediaPlayer::media() const
+{
+ return m_url;
+}
+
+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;
+ m_playbackEngine = nullptr;
+
+ if (media.isEmpty() && !stream) {
+ handleIncorrectMedia(QMediaPlayer::NoMedia);
+ return;
+ }
+
+ mediaStatusChanged(QMediaPlayer::LoadingMedia);
+
+ 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->setAudioBufferOutput(m_audioBufferOutput);
+ m_playbackEngine->setAudioSink(m_audioOutput);
+ m_playbackEngine->setVideoSink(m_videoSink);
+
+ m_playbackEngine->setLoops(loops());
+ m_playbackEngine->setPlaybackRate(m_playbackRate);
+
+ durationChanged(duration());
+ tracksChanged();
+ metaDataChanged();
+ seekableChanged(m_playbackEngine->isSeekable());
+
+ audioAvailableChanged(
+ !m_playbackEngine->streamInfo(QPlatformMediaPlayer::AudioStream).isEmpty());
+ videoAvailableChanged(
+ !m_playbackEngine->streamInfo(QPlatformMediaPlayer::VideoStream).isEmpty());
+
+ 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 (mediaStatus() == QMediaPlayer::LoadingMedia) {
+ m_requestedStatus = QMediaPlayer::PlayingState;
+ return;
+ }
+
+ if (!m_playbackEngine)
+ return;
+
+ 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);
+
+ if (mediaStatus() == QMediaPlayer::LoadedMedia || mediaStatus() == QMediaPlayer::EndOfMedia)
+ mediaStatusChanged(QMediaPlayer::BufferingMedia);
+}
+
+void QFFmpegMediaPlayer::pause()
+{
+ if (mediaStatus() == QMediaPlayer::LoadingMedia) {
+ m_requestedStatus = QMediaPlayer::PausedState;
+ return;
+ }
+
+ if (!m_playbackEngine)
+ return;
+
+ if (mediaStatus() == QMediaPlayer::EndOfMedia && state() == QMediaPlayer::StoppedState) {
+ m_playbackEngine->seek(0);
+ positionChanged(0);
+ }
+ m_playbackEngine->pause();
+ m_positionUpdateTimer.stop();
+ stateChanged(QMediaPlayer::PausedState);
+
+ if (mediaStatus() == QMediaPlayer::LoadedMedia || mediaStatus() == QMediaPlayer::EndOfMedia)
+ mediaStatusChanged(QMediaPlayer::BufferingMedia);
+}
+
+void QFFmpegMediaPlayer::stop()
+{
+ if (mediaStatus() == QMediaPlayer::LoadingMedia) {
+ m_requestedStatus = QMediaPlayer::StoppedState;
+ return;
+ }
+
+ if (!m_playbackEngine)
+ return;
+
+ m_playbackEngine->stop();
+ m_positionUpdateTimer.stop();
+ m_playbackEngine->seek(0);
+ positionChanged(0);
+ stateChanged(QMediaPlayer::StoppedState);
+ mediaStatusChanged(QMediaPlayer::LoadedMedia);
+}
+
+void QFFmpegMediaPlayer::setAudioOutput(QPlatformAudioOutput *output)
+{
+ m_audioOutput = output;
+ if (m_playbackEngine)
+ m_playbackEngine->setAudioSink(output);
+}
+
+void QFFmpegMediaPlayer::setAudioBufferOutput(QAudioBufferOutput *output) {
+ m_audioBufferOutput = output;
+ if (m_playbackEngine)
+ m_playbackEngine->setAudioBufferOutput(output);
+}
+
+QMediaMetaData QFFmpegMediaPlayer::metaData() const
+{
+ return m_playbackEngine ? m_playbackEngine->metaData() : QMediaMetaData{};
+}
+
+void QFFmpegMediaPlayer::setVideoSink(QVideoSink *sink)
+{
+ m_videoSink = sink;
+ if (m_playbackEngine)
+ m_playbackEngine->setVideoSink(sink);
+}
+
+QVideoSink *QFFmpegMediaPlayer::videoSink() const
+{
+ return m_videoSink;
+}
+
+int QFFmpegMediaPlayer::trackCount(TrackType type)
+{
+ return m_playbackEngine ? m_playbackEngine->streamInfo(type).count() : 0;
+}
+
+QMediaMetaData QFFmpegMediaPlayer::trackMetaData(TrackType type, int streamNumber)
+{
+ if (!m_playbackEngine || streamNumber < 0
+ || streamNumber >= m_playbackEngine->streamInfo(type).count())
+ return {};
+ return m_playbackEngine->streamInfo(type).at(streamNumber).metaData;
+}
+
+int QFFmpegMediaPlayer::activeTrack(TrackType type)
+{
+ return m_playbackEngine ? m_playbackEngine->activeTrack(type) : -1;
+}
+
+void QFFmpegMediaPlayer::setActiveTrack(TrackType type, int 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
new file mode 100644
index 000000000..4ab5701da
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediaplayer_p.h
@@ -0,0 +1,119 @@
+// 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
+// -------------
+//
+// 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 QFFMPEGMEDIAPLAYER_H
+#define QFFMPEGMEDIAPLAYER_H
+
+#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 CancelToken;
+
+class PlaybackEngine;
+}
+
+class QPlatformAudioOutput;
+
+class QFFmpegMediaPlayer : public QObject, public QPlatformMediaPlayer
+{
+ Q_OBJECT
+public:
+ QFFmpegMediaPlayer(QMediaPlayer *player);
+ ~QFFmpegMediaPlayer();
+
+ qint64 duration() const override;
+
+ void setPosition(qint64 position) override;
+
+ float bufferProgress() 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 &media, QIODevice *stream) override;
+
+ void play() override;
+ void pause() override;
+ void stop() override;
+
+ void setAudioOutput(QPlatformAudioOutput *) override;
+
+ void setAudioBufferOutput(QAudioBufferOutput *) override;
+
+ QMediaMetaData metaData() const override;
+
+ void setVideoSink(QVideoSink *sink) override;
+ QVideoSink *videoSink() const;
+
+ int trackCount(TrackType) override;
+ QMediaMetaData trackMetaData(TrackType type, int streamNumber) override;
+ int activeTrack(TrackType) override;
+ void setActiveTrack(TrackType, int streamNumber) override;
+ void setLoops(int loops) override;
+
+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:
+ QTimer m_positionUpdateTimer;
+ QMediaPlayer::PlaybackState m_requestedStatus = QMediaPlayer::StoppedState;
+
+ using PlaybackEngine = QFFmpeg::PlaybackEngine;
+
+ std::unique_ptr<PlaybackEngine> m_playbackEngine;
+ QPlatformAudioOutput *m_audioOutput = nullptr;
+ QPointer<QAudioBufferOutput> m_audioBufferOutput;
+ QPointer<QVideoSink> m_videoSink;
+
+ QUrl m_url;
+ 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
+
+
+#endif // QMEDIAPLAYERCONTROL_H
+
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder.cpp b/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder.cpp
new file mode 100644
index 000000000..2f9580581
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder.cpp
@@ -0,0 +1,200 @@
+// 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 "recordingengine/qffmpegrecordingengine_p.h"
+#include "qffmpegmediacapturesession_p.h"
+
+#include <qdebug.h>
+#include <qloggingcategory.h>
+
+static Q_LOGGING_CATEGORY(qLcMediaEncoder, "qt.multimedia.ffmpeg.encoder");
+
+QT_BEGIN_NAMESPACE
+
+QFFmpegMediaRecorder::QFFmpegMediaRecorder(QMediaRecorder *parent) : QPlatformMediaRecorder(parent)
+{
+}
+
+QFFmpegMediaRecorder::~QFFmpegMediaRecorder() = default;
+
+bool QFFmpegMediaRecorder::isLocationWritable(const QUrl &) const
+{
+ return true;
+}
+
+void QFFmpegMediaRecorder::handleSessionError(QMediaRecorder::Error code, const QString &description)
+{
+ updateError(code, description);
+ stop();
+}
+
+void QFFmpegMediaRecorder::record(QMediaEncoderSettings &settings)
+{
+ if (!m_session || state() != QMediaRecorder::StoppedState)
+ return;
+
+ auto videoSources = m_session->activeVideoSources();
+ auto audioInputs = m_session->activeAudioInputs();
+ const auto hasVideo = !videoSources.empty();
+ const auto hasAudio = !audioInputs.empty();
+
+ if (!hasVideo && !hasAudio) {
+ updateError(QMediaRecorder::ResourceError, QMediaRecorder::tr("No video or audio input"));
+ return;
+ }
+
+ 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());
+
+ if (outputDevice() && outputDevice()->isWritable()) {
+ formatContext->openAVIO(outputDevice());
+ } else {
+ actualLocation = findActualLocation(settings);
+ qCDebug(qLcMediaEncoder) << "recording new media to" << actualLocation;
+ formatContext->openAVIO(actualLocation);
+ }
+
+ qCDebug(qLcMediaEncoder) << "requested format:" << settings.fileFormat()
+ << settings.audioCodec();
+
+ if (!formatContext->isAVIOOpen()) {
+ updateError(QMediaRecorder::LocationNotWritable,
+ QMediaRecorder::tr("Cannot open the output location for writing"));
+ return;
+ }
+
+ 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::sessionError, this,
+ &QFFmpegMediaRecorder::handleSessionError);
+
+ updateAutoStop();
+
+ auto handleStreamInitializationError = [this](QMediaRecorder::Error code,
+ const QString &description) {
+ qCWarning(qLcMediaEncoder) << "Stream initialization error:" << description;
+ updateError(code, description);
+ };
+
+ connect(m_recordingEngine.get(), &QFFmpeg::RecordingEngine::streamInitializationError, this,
+ handleStreamInitializationError);
+
+ durationChanged(0);
+ stateChanged(QMediaRecorder::RecordingState);
+ actualLocationChanged(QUrl::fromLocalFile(actualLocation));
+
+ m_recordingEngine->initialize(audioInputs, videoSources);
+}
+
+void QFFmpegMediaRecorder::pause()
+{
+ if (!m_session || state() != QMediaRecorder::RecordingState)
+ return;
+
+ Q_ASSERT(m_recordingEngine);
+ m_recordingEngine->setPaused(true);
+
+ stateChanged(QMediaRecorder::PausedState);
+}
+
+void QFFmpegMediaRecorder::resume()
+{
+ if (!m_session || state() != QMediaRecorder::PausedState)
+ return;
+
+ Q_ASSERT(m_recordingEngine);
+ m_recordingEngine->setPaused(false);
+
+ stateChanged(QMediaRecorder::RecordingState);
+}
+
+void QFFmpegMediaRecorder::stop()
+{
+ if (!m_session || state() == QMediaRecorder::StoppedState)
+ return;
+ auto * input = m_session ? m_session->audioInput() : nullptr;
+ if (input)
+ static_cast<QFFmpegAudioInput *>(input)->setRunning(false);
+ qCDebug(qLcMediaEncoder) << "stop";
+
+ m_recordingEngine.reset();
+}
+
+void QFFmpegMediaRecorder::finalizationDone()
+{
+ stateChanged(QMediaRecorder::StoppedState);
+}
+
+void QFFmpegMediaRecorder::setMetaData(const QMediaMetaData &metaData)
+{
+ if (!m_session)
+ return;
+ m_metaData = metaData;
+}
+
+QMediaMetaData QFFmpegMediaRecorder::metaData() const
+{
+ return m_metaData;
+}
+
+void QFFmpegMediaRecorder::setCaptureSession(QFFmpegMediaCaptureSession *session)
+{
+ auto *captureSession = session;
+ if (m_session == captureSession)
+ return;
+
+ if (m_session)
+ stop();
+
+ m_session = captureSession;
+ if (!m_session)
+ return;
+}
+
+void QFFmpegMediaRecorder::updateAutoStop()
+{
+ const bool autoStop = mediaRecorder()->autoStop();
+ if (!m_recordingEngine || m_recordingEngine->autoStop() == autoStop)
+ return;
+
+ if (autoStop)
+ connect(m_recordingEngine.get(), &QFFmpeg::RecordingEngine::autoStopped, this,
+ &QFFmpegMediaRecorder::stop);
+ else
+ disconnect(m_recordingEngine.get(), &QFFmpeg::RecordingEngine::autoStopped, this,
+ &QFFmpegMediaRecorder::stop);
+
+ m_recordingEngine->setAutoStop(autoStop);
+}
+
+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
new file mode 100644
index 000000000..af3ee1509
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder_p.h
@@ -0,0 +1,73 @@
+// 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
+
+//
+// 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>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioSource;
+class QAudioSourceIO;
+class QAudioBuffer;
+class QMediaMetaData;
+class QFFmpegMediaCaptureSession;
+
+namespace QFFmpeg {
+class RecordingEngine;
+}
+
+class QFFmpegMediaRecorder : public QObject, public QPlatformMediaRecorder
+{
+ Q_OBJECT
+public:
+ QFFmpegMediaRecorder(QMediaRecorder *parent);
+ virtual ~QFFmpegMediaRecorder();
+
+ bool isLocationWritable(const QUrl &sink) const override;
+
+ void record(QMediaEncoderSettings &settings) override;
+ void pause() override;
+ void resume() override;
+ void stop() override;
+
+ void setMetaData(const QMediaMetaData &) override;
+ QMediaMetaData metaData() const override;
+
+ void setCaptureSession(QFFmpegMediaCaptureSession *session);
+
+ void updateAutoStop() override;
+
+private Q_SLOTS:
+ void newDuration(qint64 d) { durationChanged(d); }
+ void finalizationDone();
+ 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;
+
+ std::unique_ptr<RecordingEngine, RecordingEngineDeleter> m_recordingEngine;
+};
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGMEDIARECODER_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegplaybackengine.cpp b/src/plugins/multimedia/ffmpeg/qffmpegplaybackengine.cpp
new file mode 100644
index 000000000..11dccb149
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegplaybackengine.cpp
@@ -0,0 +1,649 @@
+// 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 "private/qaudiobufferoutput_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 || m_audioBufferOutput
+ ? createPlaybackEngineObject<AudioRenderer>(m_timeController, m_audioOutput, m_audioBufferOutput)
+ : 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)
+{
+ QAudioOutput *prev = std::exchange(m_audioOutput, output);
+ if (prev == output)
+ return;
+
+ updateActiveAudioOutput(output);
+
+ if (!output || !prev) {
+ // might need some improvements
+ forceUpdate();
+ }
+}
+
+void PlaybackEngine::setAudioBufferOutput(QAudioBufferOutput *output)
+{
+ QAudioBufferOutput *prev = std::exchange(m_audioBufferOutput, output);
+ if (prev == output)
+ return;
+ updateActiveAudioOutput(output);
+}
+
+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()
+{
+ if (m_audioBufferOutput)
+ updateActiveAudioOutput(static_cast<QAudioBufferOutput *>(nullptr));
+ if (m_audioOutput)
+ updateActiveAudioOutput(static_cast<QAudioOutput *>(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; });
+}
+
+template <typename AudioOutput>
+void PlaybackEngine::updateActiveAudioOutput(AudioOutput *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..50c94c955
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegplaybackengine_p.h
@@ -0,0 +1,234 @@
+// 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 QAudioBufferOutput;
+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 setAudioBufferOutput(QAudioBufferOutput *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);
+
+ template <typename AudioOutput>
+ void updateActiveAudioOutput(AudioOutput *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;
+ QPointer<QAudioBufferOutput> m_audioBufferOutput;
+
+ 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
new file mode 100644
index 000000000..141a6ade2
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp
@@ -0,0 +1,112 @@
+// 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 "playbackengine/qffmpegcodec_p.h"
+#include "qffmpegmediaformatinfo_p.h"
+#include <qloggingcategory.h>
+
+static Q_LOGGING_CATEGORY(qLcResampler, "qt.multimedia.ffmpeg.resampler")
+
+QT_BEGIN_NAMESPACE
+
+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));
+}
+
+QFFmpegResampler::QFFmpegResampler(const Codec *codec, const QAudioFormat &outputFormat,
+ qint64 startTime)
+ : m_outputFormat(outputFormat), m_startTime(startTime)
+{
+ Q_ASSERT(codec);
+
+ qCDebug(qLcResampler) << "createResampler";
+ const AVStream *audioStream = codec->stream();
+
+ if (!m_outputFormat.isValid())
+ // want the native format
+ m_outputFormat = QFFmpegMediaFormatInfo::audioFormatFromCodecParameters(audioStream->codecpar);
+
+ m_resampler = createResampleContext(AVAudioFormat(audioStream->codecpar),
+ AVAudioFormat(m_outputFormat));
+}
+
+QFFmpegResampler::~QFFmpegResampler() = default;
+
+QAudioBuffer QFFmpegResampler::resample(const char* data, size_t size)
+{
+ if (!m_inputFormat.isValid())
+ return {};
+
+ return resample(reinterpret_cast<const uint8_t **>(&data),
+ m_inputFormat.framesForBytes(static_cast<qint32>(size)));
+}
+
+QAudioBuffer QFFmpegResampler::resample(const AVFrame *frame)
+{
+ 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 outSamples =
+ swr_convert(m_resampler.get(), &out, maxOutSamples, inputData, inputSamplesCount);
+
+ samples.resize(m_outputFormat.bytesForFrames(outSamples));
+
+ const qint64 startTime = m_outputFormat.durationForFrames(m_samplesProcessed) + m_startTime;
+ m_samplesProcessed += outSamples;
+
+ 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
new file mode 100644
index 000000000..530f40aa2
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegresampler_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-2.0-only OR GPL-3.0-only
+#ifndef QFFMPEGRESAMPLER_P_H
+#define QFFMPEGRESAMPLER_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 "qaudiobuffer.h"
+#include "qffmpeg_p.h"
+#include "private/qplatformaudioresampler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg
+{
+class Codec;
+}
+
+class QFFmpegResampler : public QPlatformAudioResampler
+{
+public:
+ QFFmpegResampler(const QAudioFormat &inputFormat, const QAudioFormat &outputFormat);
+ QFFmpegResampler(const QFFmpeg::Codec *codec, const QAudioFormat &outputFormat,
+ qint64 startTime = 0);
+
+ ~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;
+ qint64 m_startTime = 0;
+ 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..feab39697
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi.cpp
@@ -0,0 +1,467 @@
+// 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 "qabstractvideobuffer.h"
+#include <private/qmultimediautils_p.h>
+#include <private/qwindowsmultimediautils_p.h>
+#include <private/qvideoframe_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)
+ : m_device(device), m_texture(texture), m_ctxMutex(mutex), m_size(size)
+ {}
+
+ ~QD3D11TextureVideoBuffer()
+ {
+ QD3D11TextureVideoBuffer::unmap();
+ }
+
+ MapData map(QtVideo::MapMode mode) override
+ {
+ MapData mapData;
+ if (!m_ctx && mode == QtVideo::MapMode::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.planeCount = 1;
+ mapData.bytesPerLine[0] = int(resource.RowPitch);
+ mapData.data[0] = reinterpret_cast<uchar*>(resource.pData);
+ mapData.dataSize[0] = m_size.height() * int(resource.RowPitch);
+ }
+
+ return mapData;
+ }
+
+ void unmap() override
+ {
+ if (m_mapMode == QtVideo::MapMode::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 = QtVideo::MapMode::NotMapped;
+ }
+
+ QVideoFrameFormat format() const override { return {}; }
+
+ 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;
+ QtVideo::MapMode m_mapMode = QtVideo::MapMode::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 = QVideoFramePrivate::createFrame(std::move(buffer), 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/qffmpegthread.cpp b/src/plugins/multimedia/ffmpeg/qffmpegthread.cpp
new file mode 100644
index 000000000..fb14ced54
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegthread.cpp
@@ -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
+
+#include "qffmpegthread_p.h"
+
+
+QT_BEGIN_NAMESPACE
+
+using namespace QFFmpeg;
+
+void ConsumerThread::stopAndDelete()
+{
+ {
+ QMutexLocker locker(&m_loopDataMutex);
+ m_exit = true;
+ }
+ dataReady();
+ wait();
+ delete this;
+}
+
+void ConsumerThread::dataReady()
+{
+ m_condition.wakeAll();
+}
+
+void ConsumerThread::run()
+{
+ init();
+
+ 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
new file mode 100644
index 000000000..a7c5b0927
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegthread_p.h
@@ -0,0 +1,96 @@
+// 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
+
+//
+// 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 <qmutex.h>
+#include <qwaitcondition.h>
+#include <qthread.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioSink;
+
+namespace QFFmpeg
+{
+
+/*!
+ 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:
+ /*!
+ Stops the thread and deletes this object
+ */
+ void stopAndDelete();
+
+protected:
+
+ /*!
+ 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 run() final;
+
+ mutable QMutex m_loopDataMutex;
+ QWaitCondition m_condition;
+ bool m_exit = false;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp b/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp
new file mode 100644
index 000000000..0edf355d8
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp
@@ -0,0 +1,363 @@
+// 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>
+#include <libavutil/hdr_dynamic_metadata.h>
+#include <libavutil/mastering_display_metadata.h>
+}
+
+QT_BEGIN_NAMESPACE
+
+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)
+ : QHwVideoBuffer(QVideoFrame::NoHandle),
+ m_frame(frame.get()),
+ m_size(qCalculateFrameSize({ frame->width, frame->height },
+ { pixelAspectRatio.num, pixelAspectRatio.den }))
+{
+ if (frame->hw_frames_ctx) {
+ m_hwFrame = std::move(frame);
+ m_pixelFormat = toQtPixelFormat(QFFmpeg::HWAccel::format(m_hwFrame.get()));
+ return;
+ }
+
+ m_swFrame = std::move(frame);
+ m_pixelFormat = toQtPixelFormat(AVPixelFormat(m_swFrame->format));
+
+ convertSWFrame();
+}
+
+QFFmpegVideoBuffer::~QFFmpegVideoBuffer() = default;
+
+void QFFmpegVideoBuffer::convertSWFrame()
+{
+ Q_ASSERT(m_swFrame);
+
+ const auto actualAVPixelFormat = AVPixelFormat(m_swFrame->format);
+ const auto targetAVPixelFormat = 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(m_swFrame->width, m_swFrame->height, actualAVPixelFormat,
+ m_size.width(), m_size.height(), targetAVPixelFormat,
+ SWS_BICUBIC, nullptr, nullptr, nullptr);
+
+ 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, 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)
+{
+ m_textureConverter = converter;
+ m_textureConverter.init(m_hwFrame.get());
+ m_type = converter.isNull() ? QVideoFrame::NoHandle : QVideoFrame::RhiTextureHandle;
+}
+
+QVideoFrameFormat::ColorSpace QFFmpegVideoBuffer::colorSpace() const
+{
+ switch (m_frame->colorspace) {
+ default:
+ case AVCOL_SPC_UNSPECIFIED:
+ case AVCOL_SPC_RESERVED:
+ case AVCOL_SPC_FCC:
+ case AVCOL_SPC_SMPTE240M:
+ case AVCOL_SPC_YCGCO:
+ case AVCOL_SPC_SMPTE2085:
+ case AVCOL_SPC_CHROMA_DERIVED_NCL:
+ case AVCOL_SPC_CHROMA_DERIVED_CL:
+ case AVCOL_SPC_ICTCP: // BT.2100 ICtCp
+ return QVideoFrameFormat::ColorSpace_Undefined;
+ case AVCOL_SPC_RGB:
+ return QVideoFrameFormat::ColorSpace_AdobeRgb;
+ case AVCOL_SPC_BT709:
+ return QVideoFrameFormat::ColorSpace_BT709;
+ case AVCOL_SPC_BT470BG: // BT601
+ case AVCOL_SPC_SMPTE170M: // Also BT601
+ return QVideoFrameFormat::ColorSpace_BT601;
+ case AVCOL_SPC_BT2020_NCL: // Non constant luminence
+ case AVCOL_SPC_BT2020_CL: // Constant luminence
+ return QVideoFrameFormat::ColorSpace_BT2020;
+ }
+}
+
+QVideoFrameFormat::ColorTransfer QFFmpegVideoBuffer::colorTransfer() const
+{
+ return QFFmpeg::fromAvColorTransfer(m_frame->color_trc);
+}
+
+QVideoFrameFormat::ColorRange QFFmpegVideoBuffer::colorRange() const
+{
+ switch (m_frame->color_range) {
+ case AVCOL_RANGE_MPEG:
+ return QVideoFrameFormat::ColorRange_Video;
+ case AVCOL_RANGE_JPEG:
+ return QVideoFrameFormat::ColorRange_Full;
+ default:
+ return QVideoFrameFormat::ColorRange_Unknown;
+ }
+}
+
+float QFFmpegVideoBuffer::maxNits()
+{
+ float maxNits = -1;
+ 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);
+ auto maybeLum = QFFmpeg::mul(10'000., data->max_luminance);
+ if (maybeLum)
+ maxNits = float(maybeLum.value());
+ }
+ }
+ return maxNits;
+}
+
+QAbstractVideoBuffer::MapData QFFmpegVideoBuffer::map(QtVideo::MapMode mode)
+{
+ 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(m_swFrame.get(), m_hwFrame.get(), 0);
+ if (ret < 0) {
+ qWarning() << "Error transferring the data to system memory:" << ret;
+ return {};
+ }
+ convertSWFrame();
+ }
+
+ m_mode = mode;
+
+ MapData mapData;
+ auto *desc = QVideoTextureHelper::textureDescription(pixelFormat());
+ mapData.planeCount = desc->nplanes;
+ for (int i = 0; i < mapData.planeCount; ++i) {
+ Q_ASSERT(m_swFrame->linesize[i] >= 0);
+
+ mapData.data[i] = m_swFrame->data[i];
+ mapData.bytesPerLine[i] = m_swFrame->linesize[i];
+ mapData.dataSize[i] = mapData.bytesPerLine[i]*desc->heightForPlane(m_swFrame->height, i);
+ }
+
+ if ((mode & QtVideo::MapMode::WriteOnly) != QtVideo::MapMode::NotMapped && 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.
+ // Set NotMapped mode to ensure map/unmap/mapMode consisteny.
+ m_mode = QtVideo::MapMode::NotMapped;
+}
+
+std::unique_ptr<QVideoFrameTextures> QFFmpegVideoBuffer::mapTextures(QRhi *)
+{
+ if (m_textures)
+ return {};
+ if (!m_hwFrame)
+ return {};
+ if (m_textureConverter.isNull()) {
+ m_textures = nullptr;
+ return {};
+ }
+
+ 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 {};
+}
+
+quint64 QFFmpegVideoBuffer::textureHandle(QRhi *rhi, int plane) const
+{
+ return m_textures ? m_textures->textureHandle(rhi, plane) : 0;
+}
+
+QVideoFrameFormat::PixelFormat QFFmpegVideoBuffer::pixelFormat() const
+{
+ return m_pixelFormat;
+}
+
+QSize QFFmpegVideoBuffer::size() const
+{
+ return m_size;
+}
+
+QVideoFrameFormat::PixelFormat QFFmpegVideoBuffer::toQtPixelFormat(AVPixelFormat avPixelFormat, bool *needsConversion)
+{
+ if (needsConversion)
+ *needsConversion = false;
+
+ 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:
+ return QVideoFrameFormat::Format_XRGB8888;
+ case AV_PIX_FMT_BGRA:
+ return QVideoFrameFormat::Format_BGRA8888;
+ case AV_PIX_FMT_BGR0:
+ return QVideoFrameFormat::Format_BGRX8888;
+ case AV_PIX_FMT_ABGR:
+ return QVideoFrameFormat::Format_ABGR8888;
+ case AV_PIX_FMT_0BGR:
+ return QVideoFrameFormat::Format_XBGR8888;
+ case AV_PIX_FMT_RGBA:
+ return QVideoFrameFormat::Format_RGBA8888;
+ case AV_PIX_FMT_RGB0:
+ return QVideoFrameFormat::Format_RGBX8888;
+
+ case AV_PIX_FMT_YUV422P:
+ return QVideoFrameFormat::Format_YUV422P;
+ case AV_PIX_FMT_YUV420P:
+ return QVideoFrameFormat::Format_YUV420P;
+ case AV_PIX_FMT_YUV420P10:
+ return QVideoFrameFormat::Format_YUV420P10;
+ case AV_PIX_FMT_UYVY422:
+ return QVideoFrameFormat::Format_UYVY;
+ case AV_PIX_FMT_YUYV422:
+ return QVideoFrameFormat::Format_YUYV;
+ case AV_PIX_FMT_NV12:
+ return QVideoFrameFormat::Format_NV12;
+ case AV_PIX_FMT_NV21:
+ return QVideoFrameFormat::Format_NV21;
+ case AV_PIX_FMT_GRAY8:
+ return QVideoFrameFormat::Format_Y8;
+ case AV_PIX_FMT_GRAY16:
+ return QVideoFrameFormat::Format_Y16;
+
+ case AV_PIX_FMT_P010:
+ return QVideoFrameFormat::Format_P010;
+ case AV_PIX_FMT_P016:
+ return QVideoFrameFormat::Format_P016;
+ case AV_PIX_FMT_MEDIACODEC:
+ return QVideoFrameFormat::Format_SamplerExternalOES;
+ }
+
+ if (needsConversion)
+ *needsConversion = true;
+
+ const AVPixFmtDescriptor *descriptor = av_pix_fmt_desc_get(avPixelFormat);
+
+ if (descriptor->flags & AV_PIX_FMT_FLAG_RGB)
+ return QVideoFrameFormat::Format_RGBA8888;
+
+ if (descriptor->comp[0].depth > 8)
+ return QVideoFrameFormat::Format_P016;
+ return QVideoFrameFormat::Format_YUV420P;
+}
+
+AVPixelFormat QFFmpegVideoBuffer::toAVPixelFormat(QVideoFrameFormat::PixelFormat pixelFormat)
+{
+ switch (pixelFormat) {
+ default:
+ case QVideoFrameFormat::Format_Invalid:
+ case QVideoFrameFormat::Format_AYUV:
+ case QVideoFrameFormat::Format_AYUV_Premultiplied:
+ case QVideoFrameFormat::Format_YV12:
+ case QVideoFrameFormat::Format_IMC1:
+ case QVideoFrameFormat::Format_IMC2:
+ case QVideoFrameFormat::Format_IMC3:
+ case QVideoFrameFormat::Format_IMC4:
+ return AV_PIX_FMT_NONE;
+ case QVideoFrameFormat::Format_Jpeg:
+ // We're using the data from the converted QImage here, which is in BGRA.
+ 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;
+ // to be added in 6.8:
+ // case QVideoFrameFormat::Format_RGBA8888_Premultiplied:
+ 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;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer_p.h b/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer_p.h
new file mode 100644
index 000000000..c61c3f5ff
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer_p.h
@@ -0,0 +1,71 @@
+// 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
+
+//
+// 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/qhwvideobuffer_p.h>
+#include <QtCore/qvariant.h>
+
+#include "qffmpeg_p.h"
+#include "qffmpeghwaccel_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QFFmpegVideoBuffer : public QHwVideoBuffer
+{
+public:
+ using AVFrameUPtr = QFFmpeg::AVFrameUPtr;
+
+ QFFmpegVideoBuffer(AVFrameUPtr frame, AVRational pixelAspectRatio = { 1, 1 });
+ ~QFFmpegVideoBuffer() override;
+
+ MapData map(QtVideo::MapMode mode) override;
+ void unmap() 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;
+
+ static QVideoFrameFormat::PixelFormat toQtPixelFormat(AVPixelFormat avPixelFormat, bool *needsConversion = nullptr);
+ static AVPixelFormat toAVPixelFormat(QVideoFrameFormat::PixelFormat pixelFormat);
+
+ void convertSWFrame();
+
+ AVFrame *getHWFrame() const { return m_hwFrame.get(); }
+
+ void setTextureConverter(const QFFmpeg::TextureConverter &converter);
+
+ QVideoFrameFormat::ColorSpace colorSpace() const;
+ QVideoFrameFormat::ColorTransfer colorTransfer() const;
+ QVideoFrameFormat::ColorRange colorRange() const;
+
+ float maxNits();
+
+private:
+ QVideoFrameFormat::PixelFormat m_pixelFormat;
+ AVFrame *m_frame = nullptr;
+ AVFrameUPtr m_hwFrame;
+ AVFrameUPtr m_swFrame;
+ QSize m_size;
+ QFFmpeg::TextureConverter m_textureConverter;
+ QtVideo::MapMode m_mode = QtVideo::MapMode::NotMapped;
+ std::unique_ptr<QFFmpeg::TextureSet> m_textures;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvideosink.cpp b/src/plugins/multimedia/ffmpeg/qffmpegvideosink.cpp
new file mode 100644
index 000000000..2f02f09c1
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegvideosink.cpp
@@ -0,0 +1,34 @@
+// 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>
+#include <private/qvideoframe_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QFFmpegVideoSink::QFFmpegVideoSink(QVideoSink *sink)
+ : QPlatformVideoSink(sink)
+{
+}
+
+void QFFmpegVideoSink::setRhi(QRhi *rhi)
+{
+ if (m_rhi == rhi)
+ return;
+ m_rhi = rhi;
+ textureConverter = QFFmpeg::TextureConverter(rhi);
+ emit rhiChanged(rhi);
+}
+
+void QFFmpegVideoSink::setVideoFrame(const QVideoFrame &frame)
+{
+ auto *buffer = dynamic_cast<QFFmpegVideoBuffer *>(QVideoFramePrivate::hwBuffer(frame));
+ if (buffer)
+ buffer->setTextureConverter(textureConverter);
+
+ QPlatformVideoSink::setVideoFrame(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
new file mode 100644
index 000000000..92b537ee3
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegvideosink_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 QFFMPEGVIDEOSINK_H
+#define QFFMPEGVIDEOSINK_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/qplatformvideosink_p.h>
+#include <qffmpeghwaccel_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// Required for QDoc workaround
+class QString;
+
+class QFFmpegVideoSink : public QPlatformVideoSink
+{
+ Q_OBJECT
+
+public:
+ QFFmpegVideoSink(QVideoSink *sink);
+ void setRhi(QRhi *rhi) override;
+
+ void setVideoFrame(const QVideoFrame &frame) override;
+
+private:
+ QFFmpeg::TextureConverter textureConverter;
+ QRhi *m_rhi = nullptr;
+};
+
+QT_END_NAMESPACE
+
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegwindowcapture_uwp.cpp b/src/plugins/multimedia/ffmpeg/qffmpegwindowcapture_uwp.cpp
new file mode 100644
index 000000000..b36279cc3
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegwindowcapture_uwp.cpp
@@ -0,0 +1,508 @@
+// 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 "qabstractvideobuffer.h"
+#include <private/qvideoframe_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) : m_surface(surface) { }
+
+ ~QUwpTextureVideoBuffer() override { QUwpTextureVideoBuffer::unmap(); }
+
+ MapData map(QtVideo::MapMode mode) override
+ {
+ if (m_mapMode != QtVideo::MapMode::NotMapped)
+ return {};
+
+ if (mode == QtVideo::MapMode::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.planeCount = 1;
+ md.bytesPerLine[0] = rect.Pitch;
+ md.data[0] = rect.pBits;
+ md.dataSize[0] = rect.Pitch * desc.Height;
+
+ m_mapMode = QtVideo::MapMode::ReadOnly;
+
+ return md;
+ } else {
+ qCDebug(qLcWindowCaptureUwp) << "Failed to map DXGI surface" << errorString(hr);
+ return {};
+ }
+ }
+
+ return {};
+ }
+
+ void unmap() override
+ {
+ if (m_mapMode == QtVideo::MapMode::NotMapped)
+ return;
+
+ const HRESULT hr = m_surface->Unmap();
+ if (FAILED(hr))
+ qCDebug(qLcWindowCaptureUwp) << "Failed to unmap surface" << errorString(hr);
+
+ m_mapMode = QtVideo::MapMode::NotMapped;
+ }
+
+ QVideoFrameFormat format() const override { return {}; }
+
+private:
+ QtVideo::MapMode m_mapMode = QtVideo::MapMode::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 QVideoFramePrivate::createFrame(
+ std::make_unique<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..97742043c
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qgdiwindowcapture.cpp
@@ -0,0 +1,197 @@
+// 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 "private/qvideoframe_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 QVideoFramePrivate::createFrame(
+ std::make_unique<QMemoryVideoBuffer>(std::move(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..4bd1f6a65
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qgrabwindowsurfacecapture.cpp
@@ -0,0 +1,221 @@
+// 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 "private/qvideoframe_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 QVideoFramePrivate::createFrame(std::move(buffer), std::move(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..4ac08fd24
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qopenglvideobuffer.cpp
@@ -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
+
+#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)
+ : QHwVideoBuffer(QVideoFrame::RhiTextureHandle), m_fbo(std::move(fbo))
+{
+ Q_ASSERT(m_fbo);
+}
+
+QOpenGLVideoBuffer::~QOpenGLVideoBuffer() { }
+
+QAbstractVideoBuffer::MapData QOpenGLVideoBuffer::map(QtVideo::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..6e62625d0
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qopenglvideobuffer_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 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/qhwvideobuffer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QImageVideoBuffer;
+class QOpenGLFramebufferObject;
+
+class QOpenGLVideoBuffer : public QHwVideoBuffer
+{
+public:
+ QOpenGLVideoBuffer(std::unique_ptr<QOpenGLFramebufferObject> fbo);
+ ~QOpenGLVideoBuffer();
+
+ MapData map(QtVideo::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
new file mode 100644
index 000000000..800460f14
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qv4l2camera.cpp
@@ -0,0 +1,708 @@
+// 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 <private/qcameradevice_p.h>
+#include <private/qmultimediautils_p.h>
+#include <private/qmemoryvideobuffer_p.h>
+#include <private/qvideoframe_p.h>
+#include <private/qcore_unix_p.h>
+
+#include <qsocketnotifier.h>
+#include <qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLcV4L2Camera, "qt.multimedia.ffmpeg.v4l2camera");
+
+static const struct {
+ QVideoFrameFormat::PixelFormat fmt;
+ uint32_t v4l2Format;
+} formatMap[] = {
+ // ### How do we handle V4L2_PIX_FMT_H264 and V4L2_PIX_FMT_MPEG4?
+ { QVideoFrameFormat::Format_YUV420P, V4L2_PIX_FMT_YUV420 },
+ { QVideoFrameFormat::Format_YUV422P, V4L2_PIX_FMT_YUV422P },
+ { QVideoFrameFormat::Format_YUYV, V4L2_PIX_FMT_YUYV },
+ { QVideoFrameFormat::Format_UYVY, V4L2_PIX_FMT_UYVY },
+ { QVideoFrameFormat::Format_XBGR8888, V4L2_PIX_FMT_XBGR32 },
+ { QVideoFrameFormat::Format_XRGB8888, V4L2_PIX_FMT_XRGB32 },
+ { QVideoFrameFormat::Format_ABGR8888, V4L2_PIX_FMT_ABGR32 },
+ { QVideoFrameFormat::Format_ARGB8888, V4L2_PIX_FMT_ARGB32 },
+ { QVideoFrameFormat::Format_BGRX8888, V4L2_PIX_FMT_BGR32 },
+ { QVideoFrameFormat::Format_RGBX8888, V4L2_PIX_FMT_RGB32 },
+ { QVideoFrameFormat::Format_BGRA8888, V4L2_PIX_FMT_BGRA32 },
+ { QVideoFrameFormat::Format_RGBA8888, V4L2_PIX_FMT_RGBA32 },
+ { QVideoFrameFormat::Format_Y8, V4L2_PIX_FMT_GREY },
+ { QVideoFrameFormat::Format_Y16, V4L2_PIX_FMT_Y16 },
+ { QVideoFrameFormat::Format_NV12, V4L2_PIX_FMT_NV12 },
+ { QVideoFrameFormat::Format_NV21, V4L2_PIX_FMT_NV21 },
+ { QVideoFrameFormat::Format_Jpeg, V4L2_PIX_FMT_MJPEG },
+ { QVideoFrameFormat::Format_Jpeg, V4L2_PIX_FMT_JPEG },
+ { QVideoFrameFormat::Format_Invalid, 0 },
+};
+
+QVideoFrameFormat::PixelFormat formatForV4L2Format(uint32_t v4l2Format)
+{
+ auto *f = formatMap;
+ while (f->v4l2Format) {
+ if (f->v4l2Format == v4l2Format)
+ return f->fmt;
+ ++f;
+ }
+ return QVideoFrameFormat::Format_Invalid;
+}
+
+uint32_t v4l2FormatForPixelFormat(QVideoFrameFormat::PixelFormat format)
+{
+ auto *f = formatMap;
+ while (f->v4l2Format) {
+ if (f->fmt == format)
+ return f->v4l2Format;
+ ++f;
+ }
+ return 0;
+}
+
+QV4L2Camera::QV4L2Camera(QCamera *camera)
+ : QPlatformCamera(camera)
+{
+}
+
+QV4L2Camera::~QV4L2Camera()
+{
+ stopCapturing();
+ closeV4L2Fd();
+}
+
+bool QV4L2Camera::isActive() const
+{
+ return m_active;
+}
+
+void QV4L2Camera::setActive(bool active)
+{
+ if (m_active == active)
+ return;
+ if (m_cameraDevice.isNull() && active)
+ return;
+
+ if (m_cameraFormat.isNull())
+ resolveCameraFormat({});
+
+ m_active = active;
+ if (m_active)
+ startCapturing();
+ else
+ stopCapturing();
+
+ emit newVideoFrame({});
+
+ emit activeChanged(active);
+}
+
+void QV4L2Camera::setCamera(const QCameraDevice &camera)
+{
+ if (m_cameraDevice == camera)
+ return;
+
+ stopCapturing();
+ closeV4L2Fd();
+
+ m_cameraDevice = camera;
+ resolveCameraFormat({});
+
+ initV4L2Controls();
+
+ if (m_active)
+ startCapturing();
+}
+
+bool QV4L2Camera::setCameraFormat(const QCameraFormat &format)
+{
+ if (!format.isNull() && !m_cameraDevice.videoFormats().contains(format))
+ return false;
+
+ if (!resolveCameraFormat(format))
+ return true;
+
+ if (m_active) {
+ stopCapturing();
+ closeV4L2Fd();
+
+ initV4L2Controls();
+ startCapturing();
+ }
+
+ return true;
+}
+
+bool QV4L2Camera::resolveCameraFormat(const QCameraFormat &format)
+{
+ auto fmt = format;
+ if (fmt.isNull())
+ fmt = findBestCameraFormat(m_cameraDevice);
+
+ if (fmt == m_cameraFormat)
+ return false;
+
+ m_cameraFormat = fmt;
+ return true;
+}
+
+void QV4L2Camera::setFocusMode(QCamera::FocusMode mode)
+{
+ if (mode == focusMode())
+ return;
+
+ bool focusDist = supportedFeatures() & QCamera::Feature::FocusDistance;
+ if (!focusDist && !m_v4l2Info.rangedFocus)
+ return;
+
+ switch (mode) {
+ default:
+ case QCamera::FocusModeAuto:
+ setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 1);
+ 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 (m_v4l2Info.rangedFocus)
+ setV4L2Parameter(V4L2_CID_AUTO_FOCUS_RANGE, V4L2_AUTO_FOCUS_RANGE_MACRO);
+ else if (focusDist)
+ setV4L2Parameter(V4L2_CID_FOCUS_ABSOLUTE, m_v4l2Info.minFocus);
+ break;
+ case QCamera::FocusModeAutoFar:
+ setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 1);
+ 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, m_v4l2Info.maxFocus);
+ break;
+ case QCamera::FocusModeManual:
+ setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 0);
+ setFocusDistance(focusDistance());
+ break;
+ }
+ focusModeChanged(mode);
+}
+
+void QV4L2Camera::setFocusDistance(float 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 (m_v4l2Info.maxZoom == m_v4l2Info.minZoom)
+ return;
+ factor = qBound(1., factor, 2.);
+ int zoom = m_v4l2Info.minZoom + (factor - 1.) * (m_v4l2Info.maxZoom - m_v4l2Info.minZoom);
+ setV4L2Parameter(V4L2_CID_ZOOM_ABSOLUTE, zoom);
+ zoomFactorChanged(factor);
+}
+
+bool QV4L2Camera::isFocusModeSupported(QCamera::FocusMode mode) const
+{
+ if (supportedFeatures() & QCamera::Feature::FocusDistance &&
+ (mode == QCamera::FocusModeManual || mode == QCamera::FocusModeAutoNear || mode == QCamera::FocusModeInfinity))
+ return true;
+
+ return mode == QCamera::FocusModeAuto;
+}
+
+void QV4L2Camera::setFlashMode(QCamera::FlashMode mode)
+{
+ 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);
+}
+
+bool QV4L2Camera::isFlashModeSupported(QCamera::FlashMode mode) const
+{
+ if (m_v4l2Info.flashSupported && mode == QCamera::FlashAuto)
+ return true;
+ return mode == QCamera::FlashOff;
+}
+
+bool QV4L2Camera::isFlashReady() const
+{
+ struct v4l2_queryctrl queryControl;
+ ::memset(&queryControl, 0, sizeof(queryControl));
+ queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE;
+
+ return m_v4l2FileDescriptor && m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl);
+}
+
+void QV4L2Camera::setTorchMode(QCamera::TorchMode mode)
+{
+ 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);
+}
+
+bool QV4L2Camera::isTorchModeSupported(QCamera::TorchMode mode) const
+{
+ if (mode == QCamera::TorchOn)
+ return m_v4l2Info.torchSupported;
+ return mode == QCamera::TorchOff;
+}
+
+void QV4L2Camera::setExposureMode(QCamera::ExposureMode mode)
+{
+ 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;
+ setV4L2Parameter(V4L2_CID_EXPOSURE_AUTO, value);
+ exposureModeChanged(mode);
+ return;
+ }
+}
+
+bool QV4L2Camera::isExposureModeSupported(QCamera::ExposureMode mode) const
+{
+ if (mode == QCamera::ExposureAuto)
+ return true;
+ if (m_v4l2Info.manualExposureSupported && m_v4l2Info.autoExposureSupported)
+ return mode == QCamera::ExposureManual;
+ return false;
+}
+
+void QV4L2Camera::setExposureCompensation(float compensation)
+{
+ 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;
+ }
+}
+
+void QV4L2Camera::setManualIsoSensitivity(int iso)
+{
+ if (!(supportedFeatures() & QCamera::Feature::IsoSensitivity))
+ return;
+ setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY_AUTO, iso <= 0 ? V4L2_ISO_SENSITIVITY_AUTO : V4L2_ISO_SENSITIVITY_MANUAL);
+ if (iso > 0) {
+ iso = qBound(minIso(), iso, maxIso());
+ setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY, iso);
+ }
+ return;
+}
+
+int QV4L2Camera::isoSensitivity() const
+{
+ if (!(supportedFeatures() & QCamera::Feature::IsoSensitivity))
+ return -1;
+ return getV4L2Parameter(V4L2_CID_ISO_SENSITIVITY);
+}
+
+void QV4L2Camera::setManualExposureTime(float secs)
+{
+ 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;
+ }
+}
+
+float QV4L2Camera::exposureTime() const
+{
+ return getV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE)/10000.;
+}
+
+bool QV4L2Camera::isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const
+{
+ if (m_v4l2Info.autoWhiteBalanceSupported && m_v4l2Info.colorTemperatureSupported)
+ return true;
+
+ return mode == QCamera::WhiteBalanceAuto;
+}
+
+void QV4L2Camera::setWhiteBalanceMode(QCamera::WhiteBalanceMode mode)
+{
+ Q_ASSERT(isWhiteBalanceModeSupported(mode));
+
+ int temperature = colorTemperatureForWhiteBalance(mode);
+ int t = setV4L2ColorTemperature(temperature);
+ if (t == 0)
+ mode = QCamera::WhiteBalanceAuto;
+ whiteBalanceModeChanged(mode);
+}
+
+void QV4L2Camera::setColorTemperature(int temperature)
+{
+ if (temperature == 0) {
+ setWhiteBalanceMode(QCamera::WhiteBalanceAuto);
+ return;
+ }
+
+ Q_ASSERT(isWhiteBalanceModeSupported(QCamera::WhiteBalanceManual));
+
+ int t = setV4L2ColorTemperature(temperature);
+ if (t)
+ colorTemperatureChanged(t);
+}
+
+void QV4L2Camera::readFrame()
+{
+ Q_ASSERT(m_memoryTransfer);
+
+ auto buffer = m_memoryTransfer->dequeueBuffer();
+ if (!buffer) {
+ qCWarning(qLcV4L2Camera) << "Cannot take buffer";
+
+ if (errno == ENODEV) {
+ // camera got removed while being active
+ stopCapturing();
+ closeV4L2Fd();
+ }
+
+ return;
+ }
+
+ auto videoBuffer = std::make_unique<QMemoryVideoBuffer>(buffer->data, m_bytesPerLine);
+ QVideoFrame frame = QVideoFramePrivate::createFrame(std::move(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() + m_frameDuration);
+
+ emit newVideoFrame(frame);
+
+ if (!m_memoryTransfer->enqueueBuffer(v4l2Buffer.index))
+ qCWarning(qLcV4L2Camera) << "Cannot add buffer";
+}
+
+void QV4L2Camera::setCameraBusy()
+{
+ m_cameraBusy = true;
+ updateError(QCamera::CameraError, QLatin1String("Camera is in use"));
+}
+
+void QV4L2Camera::initV4L2Controls()
+{
+ m_v4l2Info = {};
+ QCamera::Features features;
+
+ const QByteArray deviceName = m_cameraDevice.id();
+ Q_ASSERT(!deviceName.isEmpty());
+
+ closeV4L2Fd();
+
+ 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);
+ updateError(QCamera::CameraError, QLatin1String("Cannot open camera"));
+ return;
+ }
+
+ 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 (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 (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 (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
+ m_v4l2Info.autoExposureSupported = true;
+ }
+
+ ::memset(&queryControl, 0, sizeof(queryControl));
+ queryControl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
+ 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 (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 (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
+ queryControl.id = V4L2_CID_ISO_SENSITIVITY;
+ if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
+ features |= QCamera::Feature::IsoSensitivity;
+ minIsoChanged(queryControl.minimum);
+ maxIsoChanged(queryControl.minimum);
+ }
+ }
+
+ ::memset(&queryControl, 0, sizeof(queryControl));
+ queryControl.id = V4L2_CID_FOCUS_ABSOLUTE;
+ 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 (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
+ m_v4l2Info.rangedFocus = true;
+ }
+
+ ::memset(&queryControl, 0, sizeof(queryControl));
+ queryControl.id = V4L2_CID_FLASH_LED_MODE;
+ 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;
+ }
+
+ ::memset(&queryControl, 0, sizeof(queryControl));
+ queryControl.id = V4L2_CID_ZOOM_ABSOLUTE;
+ 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(m_v4l2Info.minZoom != m_v4l2Info.maxZoom ? 2 : 1);
+
+ supportedFeaturesChanged(features);
+}
+
+void QV4L2Camera::closeV4L2Fd()
+{
+ Q_ASSERT(!m_memoryTransfer);
+
+ m_v4l2Info = {};
+ m_cameraBusy = false;
+ m_v4l2FileDescriptor = nullptr;
+}
+
+int QV4L2Camera::setV4L2ColorTemperature(int temperature)
+{
+ struct v4l2_control control;
+ ::memset(&control, 0, sizeof(control));
+
+ if (m_v4l2Info.autoWhiteBalanceSupported) {
+ setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, temperature == 0 ? true : false);
+ } else if (temperature == 0) {
+ temperature = 5600;
+ }
+
+ 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;
+ }
+
+ return temperature;
+}
+
+bool QV4L2Camera::setV4L2Parameter(quint32 id, qint32 value)
+{
+ 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;
+ }
+ return true;
+}
+
+int QV4L2Camera::getV4L2Parameter(quint32 id) const
+{
+ struct v4l2_control control{id, 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;
+ }
+ return control.value;
+}
+
+void QV4L2Camera::setV4L2CameraFormat()
+{
+ if (m_v4l2Info.formatInitialized || !m_v4l2FileDescriptor)
+ return;
+
+ Q_ASSERT(!m_cameraFormat.isNull());
+ qCDebug(qLcV4L2Camera) << "XXXXX" << this << m_cameraDevice.id() << m_cameraFormat.pixelFormat()
+ << m_cameraFormat.resolution();
+
+ v4l2_format fmt = {};
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ auto size = m_cameraFormat.resolution();
+ fmt.fmt.pix.width = size.width();
+ fmt.fmt.pix.height = size.height();
+ fmt.fmt.pix.pixelformat = v4l2FormatForPixelFormat(m_cameraFormat.pixelFormat());
+ fmt.fmt.pix.field = V4L2_FIELD_ANY;
+
+ qCDebug(qLcV4L2Camera) << "setting camera format to" << size << fmt.fmt.pix.pixelformat;
+
+ if (!m_v4l2FileDescriptor->call(VIDIOC_S_FMT, &fmt)) {
+ if (errno == EBUSY) {
+ setCameraBusy();
+ return;
+ }
+ qWarning() << "Couldn't set video format on v4l2 camera" << strerror(errno);
+ }
+
+ 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:
+ m_colorSpace = QVideoFrameFormat::ColorSpace_Undefined;
+ break;
+ case V4L2_COLORSPACE_REC709:
+ m_colorSpace = QVideoFrameFormat::ColorSpace_BT709;
+ break;
+ case V4L2_COLORSPACE_JPEG:
+ m_colorSpace = QVideoFrameFormat::ColorSpace_AdobeRgb;
+ break;
+ case V4L2_COLORSPACE_SRGB:
+ // ##### is this correct???
+ m_colorSpace = QVideoFrameFormat::ColorSpace_BT601;
+ break;
+ case V4L2_COLORSPACE_BT2020:
+ m_colorSpace = QVideoFrameFormat::ColorSpace_BT2020;
+ break;
+ }
+
+ v4l2_streamparm streamParam = {};
+ streamParam.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ streamParam.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ auto [num, den] = qRealToFraction(1./m_cameraFormat.maxFrameRate());
+ streamParam.parm.capture.timeperframe = { (uint)num, (uint)den };
+ m_v4l2FileDescriptor->call(VIDIOC_S_PARM, &streamParam);
+
+ m_frameDuration = 1000000 * streamParam.parm.capture.timeperframe.numerator
+ / streamParam.parm.capture.timeperframe.denominator;
+}
+
+void QV4L2Camera::initV4L2MemoryTransfer()
+{
+ if (m_cameraBusy)
+ return;
+
+ Q_ASSERT(!m_memoryTransfer);
+
+ m_memoryTransfer = makeUserPtrMemoryTransfer(m_v4l2FileDescriptor, m_imageSize);
+
+ if (m_memoryTransfer)
+ return;
+
+ if (errno == EBUSY) {
+ setCameraBusy();
+ return;
+ }
+
+ qCDebug(qLcV4L2Camera) << "Cannot init V4L2_MEMORY_USERPTR; trying V4L2_MEMORY_MMAP";
+
+ m_memoryTransfer = makeMMapMemoryTransfer(m_v4l2FileDescriptor);
+
+ if (!m_memoryTransfer) {
+ qCWarning(qLcV4L2Camera) << "Cannot init v4l2 memory transfer," << qt_error_string(errno);
+ updateError(QCamera::CameraError, QLatin1String("Cannot init V4L2 memory transfer"));
+ }
+}
+
+void QV4L2Camera::stopCapturing()
+{
+ if (!m_memoryTransfer || !m_v4l2FileDescriptor)
+ return;
+
+ m_notifier = nullptr;
+
+ if (!m_v4l2FileDescriptor->stopStream()) {
+ // TODO: handle the case carefully to avoid possible memory corruption
+ if (errno != ENODEV)
+ qWarning() << "failed to stop capture";
+ }
+
+ m_memoryTransfer = nullptr;
+ m_cameraBusy = false;
+}
+
+void QV4L2Camera::startCapturing()
+{
+ if (!m_v4l2FileDescriptor)
+ return;
+
+ setV4L2CameraFormat();
+ initV4L2MemoryTransfer();
+
+ if (m_cameraBusy || !m_memoryTransfer)
+ return;
+
+ if (!m_v4l2FileDescriptor->startStream()) {
+ qWarning() << "Couldn't start v4l2 camera stream";
+ return;
+ }
+
+ m_notifier =
+ std::make_unique<QSocketNotifier>(m_v4l2FileDescriptor->get(), QSocketNotifier::Read);
+ connect(m_notifier.get(), &QSocketNotifier::activated, this, &QV4L2Camera::readFrame);
+
+ 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
new file mode 100644
index 000000000..3033f5ff9
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qv4l2camera_p.h
@@ -0,0 +1,133 @@
+// 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
+// -------------
+//
+// 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 <sys/time.h>
+
+QT_BEGIN_NAMESPACE
+
+class QV4L2FileDescriptor;
+class QV4L2MemoryTransfer;
+class QSocketNotifier;
+
+struct V4L2CameraInfo
+{
+ 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;
+};
+
+QVideoFrameFormat::PixelFormat formatForV4L2Format(uint32_t v4l2Format);
+uint32_t v4l2FormatForPixelFormat(QVideoFrameFormat::PixelFormat format);
+
+class QV4L2Camera : public QPlatformCamera
+{
+ Q_OBJECT
+
+public:
+ explicit QV4L2Camera(QCamera *parent);
+ ~QV4L2Camera();
+
+ bool isActive() const override;
+ void setActive(bool active) override;
+
+ void setCamera(const QCameraDevice &camera) override;
+ bool setCameraFormat(const QCameraFormat &format) override;
+ bool resolveCameraFormat(const QCameraFormat &format);
+
+ bool isFocusModeSupported(QCamera::FocusMode mode) const override;
+ void setFocusMode(QCamera::FocusMode /*mode*/) override;
+
+// void setCustomFocusPoint(const QPointF &/*point*/) override;
+ void setFocusDistance(float) override;
+ void zoomTo(float /*newZoomFactor*/, float /*rate*/ = -1.) override;
+
+ void setFlashMode(QCamera::FlashMode /*mode*/) override;
+ bool isFlashModeSupported(QCamera::FlashMode mode) const override;
+ bool isFlashReady() const override;
+
+ void setTorchMode(QCamera::TorchMode /*mode*/) override;
+ bool isTorchModeSupported(QCamera::TorchMode mode) const override;
+
+ void setExposureMode(QCamera::ExposureMode) override;
+ bool isExposureModeSupported(QCamera::ExposureMode mode) const override;
+ void setExposureCompensation(float) override;
+ int isoSensitivity() const override;
+ void setManualIsoSensitivity(int) override;
+ void setManualExposureTime(float) override;
+ float exposureTime() const override;
+
+ bool isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const override;
+ void setWhiteBalanceMode(QCamera::WhiteBalanceMode /*mode*/) override;
+ void setColorTemperature(int /*temperature*/) override;
+
+ QVideoFrameFormat frameFormat() const override;
+
+private Q_SLOTS:
+ void readFrame();
+
+private:
+ void setCameraBusy();
+ void initV4L2Controls();
+ void closeV4L2Fd();
+ int setV4L2ColorTemperature(int temperature);
+ bool setV4L2Parameter(quint32 id, qint32 value);
+ int getV4L2Parameter(quint32 id) const;
+
+ void setV4L2CameraFormat();
+ void initV4L2MemoryTransfer();
+ void startCapturing();
+ void stopCapturing();
+
+private:
+ bool m_active = false;
+ QCameraDevice m_cameraDevice;
+
+ std::unique_ptr<QSocketNotifier> m_notifier;
+ std::unique_ptr<QV4L2MemoryTransfer> m_memoryTransfer;
+ std::shared_ptr<QV4L2FileDescriptor> m_v4l2FileDescriptor;
+
+ V4L2CameraInfo m_v4l2Info;
+
+ 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
new file mode 100644
index 000000000..61a4ebe52
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qwindowscamera.cpp
@@ -0,0 +1,330 @@
+// 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"
+#include "qmutex.h"
+
+#include <private/qmemoryvideobuffer_p.h>
+#include <private/qwindowsmfdefs_p.h>
+#include <private/qwindowsmultimediautils_p.h>
+#include <private/qvideoframe_p.h>
+#include <private/qcomobject_p.h>
+
+#include <mfapi.h>
+#include <mfidl.h>
+#include <mferror.h>
+#include <mfreadwrite.h>
+
+#include <system_error>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QWindowsMultimediaUtils;
+
+class CameraReaderCallback : public QComObject<IMFSourceReaderCallback>
+{
+public:
+ //from IMFSourceReaderCallback
+ STDMETHODIMP OnReadSample(HRESULT status, DWORD, DWORD, LONGLONG timestamp, IMFSample *sample) override;
+ STDMETHODIMP OnFlush(DWORD) override;
+ STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) override { return S_OK; }
+
+ void setActiveCamera(ActiveCamera *activeCamera)
+ {
+ QMutexLocker locker(&m_mutex);
+ m_activeCamera = activeCamera;
+ }
+private:
+ // Destructor is not public. Caller should call Release.
+ ~CameraReaderCallback() override = default;
+
+ ActiveCamera *m_activeCamera = nullptr;
+ QMutex m_mutex;
+};
+
+static ComPtr<IMFSourceReader> createCameraReader(IMFMediaSource *mediaSource,
+ const ComPtr<CameraReaderCallback> &callback)
+{
+ ComPtr<IMFSourceReader> sourceReader;
+ ComPtr<IMFAttributes> readerAttributes;
+
+ HRESULT hr = MFCreateAttributes(readerAttributes.GetAddressOf(), 1);
+ if (SUCCEEDED(hr)) {
+ hr = readerAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, callback.Get());
+ if (SUCCEEDED(hr)) {
+ hr = MFCreateSourceReaderFromMediaSource(mediaSource, readerAttributes.Get(), sourceReader.GetAddressOf());
+ if (SUCCEEDED(hr))
+ return sourceReader;
+ }
+ }
+
+ qWarning() << "Failed to create camera IMFSourceReader" << hr;
+ return sourceReader;
+}
+
+static ComPtr<IMFMediaSource> createCameraSource(const QString &deviceId)
+{
+ 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.GetAddressOf());
+ if (SUCCEEDED(hr))
+ return mediaSource;
+ }
+ }
+ }
+ qWarning() << "Failed to create camera IMFMediaSource" << hr;
+ return mediaSource;
+}
+
+static int calculateVideoFrameStride(IMFMediaType *videoType, int width)
+{
+ Q_ASSERT(videoType);
+
+ GUID subtype = GUID_NULL;
+ HRESULT hr = videoType->GetGUID(MF_MT_SUBTYPE, &subtype);
+ if (SUCCEEDED(hr)) {
+ LONG stride = 0;
+ hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &stride);
+ if (SUCCEEDED(hr))
+ return int(qAbs(stride));
+ }
+
+ qWarning() << "Failed to calculate video stride" << errorString(hr);
+ return 0;
+}
+
+static bool setCameraReaderFormat(IMFSourceReader *sourceReader, IMFMediaType *videoType)
+{
+ Q_ASSERT(sourceReader);
+ Q_ASSERT(videoType);
+
+ HRESULT hr = sourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, nullptr,
+ videoType);
+ if (FAILED(hr))
+ qWarning() << "Failed to set video format" << errorString(hr);
+
+ 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:
+ static std::unique_ptr<ActiveCamera> create(QWindowsCamera &wc, const QCameraDevice &device, const QCameraFormat &format)
+ {
+ auto ac = std::unique_ptr<ActiveCamera>(new ActiveCamera(wc));
+ ac->m_source = createCameraSource(device.id());
+ if (!ac->m_source)
+ return {};
+
+ ac->m_readerCallback = makeComObject<CameraReaderCallback>();
+ ac->m_readerCallback->setActiveCamera(ac.get());
+ ac->m_reader = createCameraReader(ac->m_source.Get(), ac->m_readerCallback);
+ if (!ac->m_reader)
+ return {};
+
+ if (!ac->setFormat(format))
+ return {};
+
+ return ac;
+ }
+
+ bool setFormat(const QCameraFormat &format)
+ {
+ 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);
+ return true;
+ }
+
+ void onReadSample(HRESULT status, LONGLONG timestamp, IMFSample *sample)
+ {
+ if (FAILED(status)) {
+ const std::string msg{ std::system_category().message(status) };
+ m_windowsCamera.updateError(QCamera::CameraError, QString::fromStdString(msg));
+ return;
+ }
+
+ if (sample) {
+ ComPtr<IMFMediaBuffer> mediaBuffer;
+ if (SUCCEEDED(sample->ConvertToContiguousBuffer(mediaBuffer.GetAddressOf()))) {
+
+ DWORD bufLen = 0;
+ BYTE *buffer = nullptr;
+ if (SUCCEEDED(mediaBuffer->Lock(&buffer, nullptr, &bufLen))) {
+ QByteArray bytes(reinterpret_cast<char*>(buffer), qsizetype(bufLen));
+ auto buffer = std::make_unique<QMemoryVideoBuffer>(std::move(bytes),
+ m_videoFrameStride);
+ QVideoFrame frame =
+ QVideoFramePrivate::createFrame(std::move(buffer), m_frameFormat);
+
+ // WMF uses 100-nanosecond units, Qt uses microseconds
+ frame.setStartTime(timestamp / 10);
+
+ LONGLONG duration = -1;
+ if (SUCCEEDED(sample->GetSampleDuration(&duration)))
+ frame.setEndTime((timestamp + duration) / 10);
+
+ emit m_windowsCamera.newVideoFrame(frame);
+ mediaBuffer->Unlock();
+ }
+ }
+ }
+
+ m_reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, nullptr,
+ nullptr, nullptr, nullptr);
+ }
+
+ void onFlush()
+ {
+ m_flushWait.release();
+ }
+
+ ~ActiveCamera()
+ {
+ 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;
+
+ ComPtr<IMFMediaSource> m_source;
+ ComPtr<IMFSourceReader> m_reader;
+ ComPtr<CameraReaderCallback> m_readerCallback;
+
+ QVideoFrameFormat m_frameFormat;
+ int m_videoFrameStride = 0;
+};
+
+STDMETHODIMP CameraReaderCallback::OnReadSample(HRESULT status, DWORD, DWORD, LONGLONG timestamp, IMFSample *sample)
+{
+ QMutexLocker locker(&m_mutex);
+ if (m_activeCamera)
+ m_activeCamera->onReadSample(status, timestamp, sample);
+
+ return status;
+}
+
+STDMETHODIMP CameraReaderCallback::OnFlush(DWORD)
+{
+ QMutexLocker locker(&m_mutex);
+ if (m_activeCamera)
+ m_activeCamera->onFlush();
+ return S_OK;
+}
+
+QWindowsCamera::QWindowsCamera(QCamera *camera)
+ : QPlatformCamera(camera)
+{
+ m_cameraDevice = camera ? camera->cameraDevice() : QCameraDevice{};
+}
+
+QWindowsCamera::~QWindowsCamera()
+{
+ QWindowsCamera::setActive(false);
+}
+
+void QWindowsCamera::setActive(bool active)
+{
+ if (bool(m_active) == active)
+ return;
+
+ if (active) {
+ if (m_cameraDevice.isNull())
+ return;
+
+ if (m_cameraFormat.isNull())
+ m_cameraFormat = findBestCameraFormat(m_cameraDevice);
+
+ m_active = ActiveCamera::create(*this, m_cameraDevice, m_cameraFormat);
+ if (m_active)
+ activeChanged(true);
+
+ } else {
+ m_active.reset();
+ emit activeChanged(false);
+ }
+}
+
+void QWindowsCamera::setCamera(const QCameraDevice &camera)
+{
+ bool active = bool(m_active);
+ if (active)
+ setActive(false);
+ m_cameraDevice = camera;
+ m_cameraFormat = {};
+ if (active)
+ setActive(true);
+}
+
+bool QWindowsCamera::setCameraFormat(const QCameraFormat &format)
+{
+ if (format.isNull())
+ return false;
+
+ bool ok = m_active ? m_active->setFormat(format) : true;
+ if (ok)
+ m_cameraFormat = format;
+
+ return ok;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qwindowscamera_p.h b/src/plugins/multimedia/ffmpeg/qwindowscamera_p.h
new file mode 100644
index 000000000..80c05ff59
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qwindowscamera_p.h
@@ -0,0 +1,45 @@
+// 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
+
+//
+// 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/qcomptr_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class ActiveCamera;
+
+class QWindowsCamera : public QPlatformCamera
+{
+ Q_OBJECT
+
+public:
+ explicit QWindowsCamera(QCamera *parent);
+ ~QWindowsCamera() override;
+
+ bool isActive() const override { return bool(m_active); }
+ void setActive(bool active) override;
+ void setCamera(const QCameraDevice &camera) override;
+ bool setCameraFormat(const QCameraFormat &/*format*/) override;
+
+private:
+ QCameraDevice m_cameraDevice;
+ std::unique_ptr<ActiveCamera> m_active;
+};
+
+QT_END_NAMESPACE
+
+#endif //QWINDOWSCAMERA_H
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..d9343cdfe
--- /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/qcapturablewindow_p.h"
+#include "private/qmemoryvideobuffer_p.h"
+#include "private/qvideoframeconversionhelper_p.h"
+#include "private/qvideoframe_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 = std::make_unique<QMemoryVideoBuffer>(data, m_xImage->bytes_per_line);
+ return QVideoFramePrivate::createFrame(std::move(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..bcd8a39c8
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder.cpp
@@ -0,0 +1,343 @@
+// 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 "qffmpegrecordingengineutils_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, const QAudioFormat &sourceFormat,
+ const QMediaEncoderSettings &settings)
+ : EncoderThread(recordingEngine), m_format(sourceFormat), m_settings(settings)
+{
+ setObjectName(QLatin1String("AudioEncoder"));
+ qCDebug(qLcFFmpegAudioEncoder) << "AudioEncoder" << settings.audioCodec();
+
+ 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);
+
+ const 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;
+
+ updateResampler();
+}
+
+void AudioEncoder::addBuffer(const QAudioBuffer &buffer)
+{
+ if (!buffer.isValid()) {
+ setEndOfSourceStream();
+ return;
+ }
+
+ {
+ const std::chrono::microseconds bufferDuration(buffer.duration());
+ auto guard = lockLoopData();
+
+ resetEndOfSourceStream();
+
+ if (m_paused)
+ return;
+
+ // TODO: apply logic with canPushFrame
+
+ m_audioBufferQueue.push(buffer);
+ m_queueDuration += bufferDuration;
+ }
+
+ dataReady();
+}
+
+QAudioBuffer AudioEncoder::takeBuffer()
+{
+ auto locker = lockLoopData();
+ QAudioBuffer result = dequeueIfPossible(m_audioBufferQueue);
+ m_queueDuration -= std::chrono::microseconds(result.duration());
+ return result;
+}
+
+void AudioEncoder::init()
+{
+ open();
+
+ // TODO: try to address this dependency here.
+ if (auto input = qobject_cast<QFFmpegAudioInput *>(source()))
+ input->setFrameSize(m_codecContext->frame_size);
+
+ qCDebug(qLcFFmpegAudioEncoder) << "AudioEncoder::init started audio device thread.";
+}
+
+void AudioEncoder::cleanup()
+{
+ while (!m_audioBufferQueue.empty())
+ processOne();
+
+ if (m_avFrameSamplesOffset) {
+ // the size of the last frame can be less than m_codecContext->frame_size
+
+ retrievePackets();
+ sendPendingFrameToAVCodec();
+ }
+
+ 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();
+ Q_ASSERT(buffer.isValid());
+
+ // qCDebug(qLcFFmpegEncoder) << "new audio buffer" << buffer.byteCount() << buffer.format()
+ // << buffer.frameCount() << codec->frame_size;
+
+ if (buffer.format() != m_format) {
+ m_format = buffer.format();
+ updateResampler();
+ }
+
+ int samplesOffset = 0;
+ const int bufferSamplesCount = static_cast<int>(buffer.frameCount());
+
+ while (samplesOffset < bufferSamplesCount)
+ handleAudioData(buffer.constData<uint8_t>(), samplesOffset, bufferSamplesCount);
+
+ Q_ASSERT(samplesOffset == bufferSamplesCount);
+}
+
+bool AudioEncoder::checkIfCanPushFrame() const
+{
+ if (isRunning())
+ return m_audioBufferQueue.size() <= 1 || m_queueDuration < m_maxQueueDuration;
+ if (!isFinished())
+ return m_audioBufferQueue.empty();
+
+ return false;
+}
+
+void AudioEncoder::updateResampler()
+{
+ m_resampler.reset();
+
+ const AVAudioFormat requestedAudioFormat(m_format);
+ const AVAudioFormat codecAudioFormat(m_codecContext.get());
+
+ if (requestedAudioFormat != codecAudioFormat)
+ m_resampler = createResampleContext(requestedAudioFormat, codecAudioFormat);
+
+ qCDebug(qLcFFmpegAudioEncoder)
+ << "Resampler updated. Input format:" << m_format << "Resampler:" << m_resampler.get();
+}
+
+void AudioEncoder::ensurePendingFrame(int availableSamplesCount)
+{
+ Q_ASSERT(availableSamplesCount >= 0);
+
+ if (m_avFrame)
+ return;
+
+ m_avFrame = makeAVFrame();
+
+ m_avFrame->format = m_codecContext->sample_fmt;
+#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
+ m_avFrame->channel_layout = m_codecContext->channel_layout;
+ m_avFrame->channels = m_codecContext->channels;
+#else
+ m_avFrame->ch_layout = m_codecContext->ch_layout;
+#endif
+ m_avFrame->sample_rate = m_codecContext->sample_rate;
+
+ const bool isFixedFrameSize = !(m_avCodec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)
+ && m_codecContext->frame_size;
+ m_avFrame->nb_samples = isFixedFrameSize ? m_codecContext->frame_size : availableSamplesCount;
+ if (m_avFrame->nb_samples)
+ av_frame_get_buffer(m_avFrame.get(), 0);
+
+ 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(*m_avFrame, pts, timeBase);
+}
+
+void AudioEncoder::writeDataToPendingFrame(const uchar *data, int &samplesOffset, int samplesCount)
+{
+ Q_ASSERT(m_avFrame);
+ Q_ASSERT(m_avFrameSamplesOffset <= m_avFrame->nb_samples);
+
+ const int bytesPerSample = av_get_bytes_per_sample(m_codecContext->sample_fmt);
+ const bool isPlanar = av_sample_fmt_is_planar(m_codecContext->sample_fmt);
+
+#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
+ const int channelsCount = m_codecContext->channels;
+#else
+ const int channelsCount = m_codecContext->ch_layout.nb_channels;
+#endif
+
+ const int audioDataOffset = isPlanar ? bytesPerSample * m_avFrameSamplesOffset
+ : bytesPerSample * m_avFrameSamplesOffset * channelsCount;
+
+ const int planesCount = isPlanar ? channelsCount : 1;
+ m_avFramePlanesData.resize(planesCount);
+ for (int plane = 0; plane < planesCount; ++plane)
+ m_avFramePlanesData[plane] = m_avFrame->extended_data[plane] + audioDataOffset;
+
+ const int samplesToRead =
+ std::min(m_avFrame->nb_samples - m_avFrameSamplesOffset, samplesCount - samplesOffset);
+
+ data += m_format.bytesForFrames(samplesOffset);
+
+ if (m_resampler) {
+ m_avFrameSamplesOffset += swr_convert(m_resampler.get(), m_avFramePlanesData.data(),
+ samplesToRead, &data, samplesToRead);
+ } else {
+ Q_ASSERT(planesCount == 1);
+ m_avFrameSamplesOffset += samplesToRead;
+ memcpy(m_avFramePlanesData[0], data, m_format.bytesForFrames(samplesToRead));
+ }
+
+ samplesOffset += samplesToRead;
+}
+
+void AudioEncoder::sendPendingFrameToAVCodec()
+{
+ Q_ASSERT(m_avFrame);
+ Q_ASSERT(m_avFrameSamplesOffset <= m_avFrame->nb_samples);
+
+ m_avFrame->nb_samples = m_avFrameSamplesOffset;
+
+ m_samplesWritten += m_avFrameSamplesOffset;
+
+ const 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(), m_avFrame.get());
+ if (ret < 0) {
+ char errStr[AV_ERROR_MAX_STRING_SIZE];
+ av_strerror(ret, errStr, AV_ERROR_MAX_STRING_SIZE);
+ qCDebug(qLcFFmpegAudioEncoder) << "error sending frame" << ret << errStr;
+ }
+
+ m_avFrame = nullptr;
+ m_avFrameSamplesOffset = 0;
+ std::fill(m_avFramePlanesData.begin(), m_avFramePlanesData.end(), nullptr);
+}
+
+void AudioEncoder::handleAudioData(const uchar *data, int &samplesOffset, int samplesCount)
+{
+ ensurePendingFrame(samplesCount - samplesOffset);
+
+ writeDataToPendingFrame(data, samplesOffset, samplesCount);
+
+ // The frame is not ready yet
+ if (m_avFrameSamplesOffset < m_avFrame->nb_samples)
+ return;
+
+ retrievePackets();
+
+ sendPendingFrameToAVCodec();
+}
+
+} // 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..4408ff54f
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder_p.h
@@ -0,0 +1,77 @@
+// 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>
+#include <chrono>
+
+QT_BEGIN_NAMESPACE
+
+class QMediaEncoderSettings;
+
+namespace QFFmpeg {
+
+class AudioEncoder : public EncoderThread
+{
+public:
+ AudioEncoder(RecordingEngine &recordingEngine, const QAudioFormat &sourceFormat,
+ const QMediaEncoderSettings &settings);
+
+ void addBuffer(const QAudioBuffer &buffer);
+
+protected:
+ bool checkIfCanPushFrame() const override;
+
+private:
+ void open();
+
+ QAudioBuffer takeBuffer();
+ void retrievePackets();
+ void updateResampler();
+
+ void init() override;
+ void cleanup() override;
+ bool hasData() const override;
+ void processOne() override;
+
+ void handleAudioData(const uchar *data, int &samplesOffset, int samplesCount);
+
+ void ensurePendingFrame(int availableSamplesCount);
+
+ void writeDataToPendingFrame(const uchar *data, int &samplesOffset, int samplesCount);
+
+ void sendPendingFrameToAVCodec();
+
+private:
+ std::queue<QAudioBuffer> m_audioBufferQueue;
+
+ // Arbitrarily chosen to limit audio queue duration
+ const std::chrono::microseconds m_maxQueueDuration = std::chrono::seconds(5);
+
+ std::chrono::microseconds m_queueDuration{ 0 };
+
+ AVStream *m_stream = nullptr;
+ AVCodecContextUPtr m_codecContext;
+ QAudioFormat m_format;
+
+ SwrContextUPtr m_resampler;
+ qint64 m_samplesWritten = 0;
+ const AVCodec *m_avCodec = nullptr;
+ QMediaEncoderSettings m_settings;
+
+ AVFrameUPtr m_avFrame;
+ int m_avFrameSamplesOffset = 0;
+ std::vector<uint8_t *> m_avFramePlanesData;
+};
+
+
+} // 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/recordingengine/qffmpegencoderoptions.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderoptions.cpp
new file mode 100644
index 000000000..bd6a8e09b
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderoptions.cpp
@@ -0,0 +1,362 @@
+// 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)
+#include <va/va.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// unfortunately there is no common way to specify options for the encoders. The code here tries to map our settings sensibly
+// to options available in different encoders
+
+// For constant quality options, we're trying to map things to approx those bit rates for 1080p@30fps (in Mbps):
+// VeryLow Low Normal High VeryHigh
+// H264: 0.8M 1.5M 3.5M 6M 10M
+// H265: 0.5M 1.0M 2.5M 4M 7M
+
+[[maybe_unused]]
+static int bitrateForSettings(const QMediaEncoderSettings &settings, bool hdr = false)
+{
+ // calculate an acceptable bitrate depending on video codec, resolution, framerate and requested quality
+ // The calculations are rather heuristic here, trying to take into account how well codecs compress using
+ // the tables above.
+
+ // The table here is for 30FPS
+ const double bitsPerPixel[int(QMediaFormat::VideoCodec::LastVideoCodec)+1][QMediaRecorder::VeryHighQuality+1] = {
+ { 1.2, 2.25, 5, 9, 15 }, // MPEG1,
+ { 0.8, 1.5, 3.5, 6, 10 }, // MPEG2
+ { 0.4, 0.75, 1.75, 3, 5 }, // MPEG4
+ { 0.4, 0.75, 1.75, 3, 5 }, // H264
+ { 0.3, 0.5, 0.2, 2, 3 }, // H265
+ { 0.4, 0.75, 1.75, 3, 5 }, // VP8
+ { 0.3, 0.5, 0.2, 2, 3 }, // VP9
+ { 0.2, 0.4, 0.9, 1.5, 2.5 }, // AV1
+ { 0.4, 0.75, 1.75, 3, 5 }, // Theora
+ { 0.8, 1.5, 3.5, 6, 10 }, // WMV
+ { 16, 24, 32, 40, 48 }, // MotionJPEG
+ };
+
+ QSize s = settings.videoResolution();
+ double bitrate = bitsPerPixel[int(settings.videoCodec())][settings.quality()]*s.width()*s.height();
+
+ if (settings.videoCodec() != QMediaFormat::VideoCodec::MotionJPEG) {
+ // We assume that doubling the framerate requires 1.5 times the amount of data (not twice, as intraframe
+ // differences will be smaller). 4 times the frame rate uses thus 2.25 times the data, etc.
+ float rateMultiplier = log2(settings.videoFrameRate()/30.);
+ bitrate *= pow(1.5, rateMultiplier);
+ } else {
+ // MotionJPEG doesn't optimize between frames, so we have a linear dependency on framerate
+ bitrate *= settings.videoFrameRate()/30.;
+ }
+
+ // HDR requires 10bits per pixel instead of 8, so apply a factor of 1.25.
+ if (hdr)
+ bitrate *= 1.25;
+ 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) {
+ codec->bit_rate = settings.videoBitRate();
+ } else {
+ const char *scales[] = {
+ "29", "26", "23", "21", "19"
+ };
+ av_dict_set(opts, "crf", scales[settings.quality()], 0);
+ }
+}
+
+static void apply_x265(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
+{
+ if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
+ codec->bit_rate = settings.videoBitRate();
+ } else {
+ const char *scales[QMediaRecorder::VeryHighQuality+1] = {
+ "40", "34", "28", "26", "24",
+ };
+ av_dict_set(opts, "crf", scales[settings.quality()], 0);
+ }
+}
+
+static void apply_libvpx(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
+{
+ if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
+ codec->bit_rate = settings.videoBitRate();
+ } else {
+ const char *scales[QMediaRecorder::VeryHighQuality+1] = {
+ "38", "34", "31", "28", "25",
+ };
+ av_dict_set(opts, "crf", scales[settings.quality()], 0);
+ av_dict_set(opts, "b", 0, 0);
+ }
+ av_dict_set(opts, "row-mt", "1", 0); // better multithreading
+}
+
+#ifdef Q_OS_DARWIN
+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();
+ } else {
+ // only use quality on macOS/ARM, as FFmpeg doesn't support it on the other platforms and would throw
+ // an error when initializing the codec
+#if defined(Q_OS_MACOS) && defined(Q_PROCESSOR_ARM_64)
+ // Videotoolbox describes quality as a number from 0 to 1, with low == 0.25, normal 0.5, high 0.75 and lossless = 1
+ // ffmpeg uses a different scale going from 0 to 11800.
+ // Values here are adjusted to agree approximately with the target bit rates listed above
+ const int scales[] = {
+ 3000, 4800, 5900, 6900, 7700,
+ };
+ codec->global_quality = scales[settings.quality()];
+ codec->flags |= AV_CODEC_FLAG_QSCALE;
+#else
+ 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
+
+#if QT_CONFIG(vaapi)
+static void apply_vaapi(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **/*opts*/)
+{
+ // See also vaapi_encode_init_rate_control() in libavcodec
+ if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding) {
+ codec->bit_rate = settings.videoBitRate();
+ codec->rc_max_rate = settings.videoBitRate();
+ } else if (settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
+ codec->bit_rate = settings.videoBitRate();
+ } else {
+ const int *quality = nullptr;
+ // unfortunately, all VA codecs use different quality scales :/
+ switch (settings.videoCodec()) {
+ case QMediaFormat::VideoCodec::MPEG2: {
+ static const int q[] = { 20, 15, 10, 8, 6 };
+ quality = q;
+ break;
+ }
+ case QMediaFormat::VideoCodec::MPEG4:
+ case QMediaFormat::VideoCodec::H264: {
+ static const int q[] = { 29, 26, 23, 21, 19 };
+ quality = q;
+ break;
+ }
+ case QMediaFormat::VideoCodec::H265: {
+ static const int q[] = { 40, 34, 28, 26, 24 };
+ quality = q;
+ break;
+ }
+ case QMediaFormat::VideoCodec::VP8: {
+ static const int q[] = { 56, 48, 40, 34, 28 };
+ quality = q;
+ break;
+ }
+ case QMediaFormat::VideoCodec::VP9: {
+ static const int q[] = { 124, 112, 100, 88, 76 };
+ quality = q;
+ break;
+ }
+ case QMediaFormat::VideoCodec::MotionJPEG: {
+ static const int q[] = { 40, 60, 80, 90, 95 };
+ quality = q;
+ break;
+ }
+ case QMediaFormat::VideoCodec::AV1:
+ case QMediaFormat::VideoCodec::Theora:
+ case QMediaFormat::VideoCodec::WMV:
+ default:
+ break;
+ }
+
+ 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)
+{
+ if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
+ codec->bit_rate = settings.videoBitRate();
+ av_dict_set(opts, "rate_control", "cbr", 0);
+ } else {
+ av_dict_set(opts, "rate_control", "quality", 0);
+ const char *scales[] = {
+ "25", "50", "75", "90", "100"
+ };
+ av_dict_set(opts, "quality", scales[settings.quality()], 0);
+ }
+}
+#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);
+
+const struct {
+ const char *name;
+ ApplyOptions apply;
+} 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 },
+#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 },
+#endif
+#ifdef Q_OS_WINDOWS
+ { "hevc_mf", apply_mf },
+ { "h264_mf", apply_mf },
+#endif
+#ifdef Q_OS_ANDROID
+ { "hevc_mediacodec", apply_mediacodec },
+ { "h264_mediacodec", apply_mediacodec },
+#endif
+ { nullptr, nullptr } };
+
+const struct {
+ const char *name;
+ ApplyOptions apply;
+} audioCodecOptionTable[] = {
+ { nullptr, nullptr }
+};
+
+void applyVideoEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts)
+{
+ av_dict_set(opts, "threads", "auto", 0); // we always want automatic threading
+
+ auto *table = videoCodecOptionTable;
+ while (table->name) {
+ if (codecName == table->name) {
+ table->apply(settings, codec, opts);
+ return;
+ }
+
+ ++table;
+ }
+}
+
+void applyAudioEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts)
+{
+ codec->thread_count = -1; // we always want automatic threading
+ if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding)
+ codec->bit_rate = settings.audioBitRate();
+
+ auto *table = audioCodecOptionTable;
+ while (table->name) {
+ if (codecName == table->name) {
+ table->apply(settings, codec, opts);
+ return;
+ }
+
+ ++table;
+ }
+
+}
+
+}
+
+QT_END_NAMESPACE
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..61fe954c8
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread.cpp
@@ -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
+#include "qffmpegencoderthread_p.h"
+#include "qmetaobject.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+EncoderThread::EncoderThread(RecordingEngine &recordingEngine) : m_recordingEngine(recordingEngine)
+{
+}
+
+void EncoderThread::setPaused(bool paused)
+{
+ auto guard = lockLoopData();
+ m_paused = paused;
+}
+
+void EncoderThread::setAutoStop(bool autoStop)
+{
+ auto guard = lockLoopData();
+ m_autoStop = autoStop;
+}
+
+void EncoderThread::setEndOfSourceStream()
+{
+ {
+ auto guard = lockLoopData();
+ m_endOfSourceStream = true;
+ }
+
+ emit endOfSourceStream();
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#include "moc_qffmpegencoderthread_p.cpp"
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..f1f6b610a
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread_p.h
@@ -0,0 +1,72 @@
+// 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"
+#include "qpointer.h"
+
+#include "private/qmediainputencoderinterface_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+class RecordingEngine;
+
+class EncoderThread : public ConsumerThread, public QMediaInputEncoderInterface
+{
+ Q_OBJECT
+public:
+ EncoderThread(RecordingEngine &recordingEngine);
+
+ void setPaused(bool paused);
+
+ void setAutoStop(bool autoStop);
+
+ void setSource(QObject *source) { m_source = source; }
+
+ QObject *source() const { return m_source; }
+
+ bool canPushFrame() const override { return m_canPushFrame.load(std::memory_order_relaxed); }
+
+ void setEndOfSourceStream();
+
+ bool isEndOfSourceStream() const { return m_endOfSourceStream; }
+
+protected:
+ void updateCanPushFrame();
+
+ virtual bool checkIfCanPushFrame() const = 0;
+
+ void resetEndOfSourceStream() { m_endOfSourceStream = false; }
+
+ auto lockLoopData()
+ {
+ return QScopeGuard([this, locker = ConsumerThread::lockLoopData()]() mutable {
+ const bool autoStopActivated = m_endOfSourceStream && m_autoStop;
+ const bool canPush = !autoStopActivated && !m_paused && checkIfCanPushFrame();
+ locker.unlock();
+ if (m_canPushFrame.exchange(canPush, std::memory_order_relaxed) != canPush)
+ emit canPushFrameChanged();
+ });
+ }
+
+Q_SIGNALS:
+ void canPushFrameChanged();
+ void endOfSourceStream();
+
+protected:
+ bool m_paused = false;
+ bool m_endOfSourceStream = false;
+ bool m_autoStop = false;
+ std::atomic_bool m_canPushFrame = false;
+ RecordingEngine &m_recordingEngine;
+ QPointer<QObject> m_source;
+};
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencodinginitializer.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencodinginitializer.cpp
new file mode 100644
index 000000000..4f8c21bd5
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencodinginitializer.cpp
@@ -0,0 +1,165 @@
+// 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 "qffmpegencodinginitializer_p.h"
+#include "qffmpegrecordingengineutils_p.h"
+#include "qffmpegrecordingengine_p.h"
+#include "qffmpegaudioinput_p.h"
+#include "qvideoframe.h"
+
+#include "private/qplatformvideoframeinput_p.h"
+#include "private/qplatformaudiobufferinput_p.h"
+#include "private/qplatformaudiobufferinput_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+EncodingInitializer::EncodingInitializer(RecordingEngine &engine) : m_recordingEngine(engine) { }
+
+EncodingInitializer::~EncodingInitializer()
+{
+ for (QObject *source : m_pendingSources)
+ setEncoderInterface(source, nullptr);
+}
+
+void EncodingInitializer::start(const std::vector<QPlatformAudioBufferInputBase *> &audioSources,
+ const std::vector<QPlatformVideoSource *> &videoSources)
+{
+ for (auto source : audioSources) {
+ if (auto audioInput = qobject_cast<QFFmpegAudioInput *>(source))
+ m_recordingEngine.addAudioInput(audioInput);
+ else if (auto audioBufferInput = qobject_cast<QPlatformAudioBufferInput *>(source))
+ addAudioBufferInput(audioBufferInput);
+ else
+ Q_ASSERT(!"Undefined source type");
+ }
+
+ for (auto source : videoSources)
+ addVideoSource(source);
+
+ tryStartRecordingEngine();
+}
+
+void EncodingInitializer::addAudioBufferInput(QPlatformAudioBufferInput *input)
+{
+ Q_ASSERT(input);
+
+ if (input->audioFormat().isValid())
+ m_recordingEngine.addAudioBufferInput(input, {});
+ else
+ addPendingAudioBufferInput(input);
+}
+
+void EncodingInitializer::addPendingAudioBufferInput(QPlatformAudioBufferInput *input)
+{
+ addPendingSource(input);
+
+ connect(input, &QPlatformAudioBufferInput::destroyed, this, [this, input]() {
+ erasePendingSource(input, QStringLiteral("Audio source deleted"), true);
+ });
+
+ connect(input, &QPlatformAudioBufferInput::newAudioBuffer, this,
+ [this, input](const QAudioBuffer &buffer) {
+ if (buffer.isValid())
+ erasePendingSource(
+ input, [&]() { m_recordingEngine.addAudioBufferInput(input, buffer); });
+ else
+ erasePendingSource(input,
+ QStringLiteral("Audio source has sent the end frame"));
+ });
+}
+
+void EncodingInitializer::addVideoSource(QPlatformVideoSource *source)
+{
+ Q_ASSERT(source);
+ Q_ASSERT(source->isActive());
+
+ if (source->frameFormat().isValid())
+ m_recordingEngine.addVideoSource(source, {});
+ else if (source->hasError())
+ emitStreamInitializationError(QStringLiteral("Video source error: ")
+ + source->errorString());
+ else
+ addPendingVideoSource(source);
+}
+
+void EncodingInitializer::addPendingVideoSource(QPlatformVideoSource *source)
+{
+ addPendingSource(source);
+
+ connect(source, &QPlatformVideoSource::errorChanged, this, [this, source]() {
+ if (source->hasError())
+ erasePendingSource(source,
+ QStringLiteral("Videio source error: ") + source->errorString());
+ });
+
+ connect(source, &QPlatformVideoSource::destroyed, this, [this, source]() {
+ erasePendingSource(source, QStringLiteral("Source deleted"), true);
+ });
+
+ connect(source, &QPlatformVideoSource::activeChanged, this, [this, source]() {
+ if (!source->isActive())
+ erasePendingSource(source, QStringLiteral("Video source deactivated"));
+ });
+
+ connect(source, &QPlatformVideoSource::newVideoFrame, this,
+ [this, source](const QVideoFrame &frame) {
+ if (frame.isValid())
+ erasePendingSource(source,
+ [&]() { m_recordingEngine.addVideoSource(source, frame); });
+ else
+ erasePendingSource(source,
+ QStringLiteral("Video source has sent the end frame"));
+ });
+}
+
+void EncodingInitializer::tryStartRecordingEngine()
+{
+ if (m_pendingSources.empty())
+ m_recordingEngine.start();
+}
+
+void EncodingInitializer::emitStreamInitializationError(QString error)
+{
+ emit m_recordingEngine.streamInitializationError(
+ QMediaRecorder::ResourceError,
+ QStringLiteral("Video steam initialization error. ") + error);
+}
+
+void EncodingInitializer::addPendingSource(QObject *source)
+{
+ Q_ASSERT(m_pendingSources.count(source) == 0);
+
+ setEncoderInterface(source, this);
+ m_pendingSources.emplace(source);
+}
+
+template <typename F>
+void EncodingInitializer::erasePendingSource(QObject *source, F &&functionOrError, bool destroyed)
+{
+ const auto erasedCount = m_pendingSources.erase(source);
+ if (erasedCount == 0)
+ return; // got a queued event, just ignore it.
+
+ if (!destroyed) {
+ setEncoderInterface(source, nullptr);
+ disconnect(source, nullptr, this, nullptr);
+ }
+
+ if constexpr (std::is_invocable_v<F>)
+ functionOrError();
+ else
+ emitStreamInitializationError(functionOrError);
+
+ tryStartRecordingEngine();
+}
+
+bool EncodingInitializer::canPushFrame() const
+{
+ return true;
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencodinginitializer_p.h b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencodinginitializer_p.h
new file mode 100644
index 000000000..e3bcb3428
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencodinginitializer_p.h
@@ -0,0 +1,77 @@
+// 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 QENCODINGINITIALIZER_P_H
+#define QENCODINGINITIALIZER_P_H
+
+#include "qobject.h"
+#include "private/qmediainputencoderinterface_p.h"
+#include <unordered_set>
+#include <vector>
+
+//
+// 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 QFFmpegAudioInput;
+class QPlatformVideoSource;
+class QPlatformAudioBufferInput;
+class QPlatformAudioBufferInputBase;
+class QMediaInputEncoderInterface;
+
+namespace QFFmpeg {
+
+class RecordingEngine;
+
+// Initializes RecordingEngine with audio and video sources, potentially lazily
+// upon first frame arrival if video frame format is not pre-determined.
+class EncodingInitializer : public QObject, private QMediaInputEncoderInterface
+{
+public:
+ EncodingInitializer(RecordingEngine &engine);
+
+ ~EncodingInitializer() override;
+
+ void start(const std::vector<QPlatformAudioBufferInputBase *> &audioSources,
+ const std::vector<QPlatformVideoSource *> &videoSources);
+
+private:
+ void addAudioBufferInput(QPlatformAudioBufferInput *input);
+
+ void addPendingAudioBufferInput(QPlatformAudioBufferInput *input);
+
+ void addVideoSource(QPlatformVideoSource *source);
+
+ void addPendingVideoSource(QPlatformVideoSource *source);
+
+ void addPendingSource(QObject *source);
+
+ void tryStartRecordingEngine();
+
+private:
+ void emitStreamInitializationError(QString error);
+
+ template <typename F>
+ void erasePendingSource(QObject *source, F &&functionOrError, bool destroyed = false);
+
+ bool canPushFrame() const override;
+
+private:
+ RecordingEngine &m_recordingEngine;
+ std::unordered_set<QObject *> m_pendingSources;
+};
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif // QENCODINGINITIALIZER_P_H
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer.cpp
new file mode 100644
index 000000000..6a33e79dd
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer.cpp
@@ -0,0 +1,64 @@
+// 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 "qffmpegrecordingengineutils_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..2c0931780
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine.cpp
@@ -0,0 +1,278 @@
+// 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 "qffmpegencodinginitializer_p.h"
+#include "qffmpegaudioencoder_p.h"
+#include "qffmpegaudioinput_p.h"
+#include "qffmpegrecordingengineutils_p.h"
+
+#include "private/qmultimediautils_p.h"
+#include "private/qplatformaudiobufferinput_p.h"
+#include "private/qplatformvideosource_p.h"
+#include "private/qplatformvideoframeinput_p.h"
+
+#include "qdebug.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)
+{
+ Q_ASSERT(input);
+
+ if (input->device.isNull()) {
+ emit streamInitializationError(QMediaRecorder::ResourceError,
+ QLatin1StringView("Audio device is null"));
+ return;
+ }
+
+ const QAudioFormat format = input->device.preferredFormat();
+
+ if (!format.isValid()) {
+ emit streamInitializationError(
+ QMediaRecorder::FormatError,
+ QLatin1StringView("Audio device has invalid preferred format"));
+ return;
+ }
+
+ AudioEncoder *audioEncoder = createAudioEncoder(format);
+ connectEncoderToSource(audioEncoder, input);
+
+ input->setRunning(true);
+}
+
+void RecordingEngine::addAudioBufferInput(QPlatformAudioBufferInput *input,
+ const QAudioBuffer &firstBuffer)
+{
+ Q_ASSERT(input);
+ const QAudioFormat format = firstBuffer.isValid() ? firstBuffer.format() : input->audioFormat();
+
+ AudioEncoder *audioEncoder = createAudioEncoder(format);
+
+ // set the buffer before connecting to avoid potential races
+ if (firstBuffer.isValid())
+ audioEncoder->addBuffer(firstBuffer);
+
+ connectEncoderToSource(audioEncoder, input);
+}
+
+AudioEncoder *RecordingEngine::createAudioEncoder(const QAudioFormat &format)
+{
+ Q_ASSERT(format.isValid());
+
+ auto audioEncoder = new AudioEncoder(*this, format, m_settings);
+ m_audioEncoders.push_back(audioEncoder);
+ connect(audioEncoder, &EncoderThread::endOfSourceStream, this,
+ &RecordingEngine::handleSourceEndOfStream);
+ if (m_autoStop)
+ audioEncoder->setAutoStop(true);
+
+ return audioEncoder;
+}
+
+void RecordingEngine::addVideoSource(QPlatformVideoSource *source, const QVideoFrame &firstFrame)
+{
+ QVideoFrameFormat frameFormat =
+ firstFrame.isValid() ? firstFrame.surfaceFormat() : source->frameFormat();
+
+ Q_ASSERT(frameFormat.isValid());
+
+ if (firstFrame.isValid() && frameFormat.streamFrameRate() <= 0.f) {
+ const qint64 startTime = firstFrame.startTime();
+ const qint64 endTime = firstFrame.endTime();
+ if (startTime != -1 && endTime > startTime)
+ frameFormat.setStreamFrameRate(static_cast<qreal>(VideoFrameTimeBase)
+ / (endTime - startTime));
+ }
+
+ 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 streamInitializationError(QMediaRecorder::FormatError,
+ QLatin1StringView("Cannot initialize encoder"));
+ return;
+ }
+
+ auto videoEncoder = veUPtr.release();
+ m_videoEncoders.append(videoEncoder);
+ if (m_autoStop)
+ videoEncoder->setAutoStop(true);
+
+ connect(videoEncoder, &EncoderThread::endOfSourceStream, this,
+ &RecordingEngine::handleSourceEndOfStream);
+
+ // set the frame before connecting to avoid potential races
+ if (firstFrame.isValid())
+ videoEncoder->addFrame(firstFrame);
+
+ connectEncoderToSource(videoEncoder, source);
+}
+
+void RecordingEngine::start()
+{
+ Q_ASSERT(m_initializer);
+ m_initializer.reset();
+
+ if (m_audioEncoders.empty() && m_videoEncoders.empty()) {
+ emit sessionError(QMediaRecorder::ResourceError,
+ QLatin1StringView("No valid stream found for encoding"));
+ return;
+ }
+
+ 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 sessionError(QMediaRecorder::ResourceError,
+ QLatin1StringView("Cannot start writing the stream"));
+ return;
+ }
+
+ m_isHeaderWritten = true;
+
+ qCDebug(qLcFFmpegEncoder) << "stream header is successfully written";
+
+ m_muxer->start();
+
+ forEachEncoder([](QThread *thread) { thread->start(); });
+}
+
+void RecordingEngine::initialize(const std::vector<QPlatformAudioBufferInputBase *> &audioSources,
+ const std::vector<QPlatformVideoSource *> &videoSources)
+{
+ qCDebug(qLcFFmpegEncoder) << ">>>>>>>>>>>>>>> initialize";
+
+ m_initializer = std::make_unique<EncodingInitializer>(*this);
+ m_initializer->start(audioSources, videoSources);
+}
+
+RecordingEngine::EncodingFinalizer::EncodingFinalizer(RecordingEngine &recordingEngine)
+ : m_recordingEngine(recordingEngine)
+{
+ connect(this, &QThread::finished, this, &QObject::deleteLater);
+}
+
+void RecordingEngine::EncodingFinalizer::run()
+{
+ m_recordingEngine.forEachEncoder(&EncoderThread::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.sessionError(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";
+
+ m_initializer.reset();
+
+ forEachEncoder(&disconnectEncoderFromSource);
+
+ auto *finalizer = new EncodingFinalizer(*this);
+ finalizer->start();
+}
+
+void RecordingEngine::setPaused(bool paused)
+{
+ forEachEncoder(&EncoderThread::setPaused, paused);
+}
+
+void RecordingEngine::setAutoStop(bool autoStop)
+{
+ m_autoStop = autoStop;
+ forEachEncoder(&EncoderThread::setAutoStop, autoStop);
+ handleSourceEndOfStream();
+}
+
+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);
+ }
+}
+
+bool RecordingEngine::isEndOfSourceStreams() const
+{
+ auto isAtEnd = [](EncoderThread *encoder) { return encoder->isEndOfSourceStream(); };
+ return std::all_of(m_videoEncoders.cbegin(), m_videoEncoders.cend(), isAtEnd)
+ && std::all_of(m_audioEncoders.cbegin(), m_audioEncoders.cend(), isAtEnd);
+}
+
+void RecordingEngine::handleSourceEndOfStream()
+{
+ if (m_autoStop && isEndOfSourceStreams())
+ emit autoStopped();
+}
+
+template <typename F, typename... Args>
+void RecordingEngine::forEachEncoder(F &&f, Args &&...args)
+{
+ for (AudioEncoder *audioEncoder : m_audioEncoders)
+ std::invoke(f, audioEncoder, args...);
+ for (VideoEncoder *videoEncoder : m_videoEncoders)
+ std::invoke(f, videoEncoder, args...);
+}
+}
+
+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..ce3aaa6bb
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine_p.h
@@ -0,0 +1,121 @@
+// 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>
+
+QT_BEGIN_NAMESPACE
+
+class QFFmpegAudioInput;
+class QPlatformAudioBufferInput;
+class QPlatformAudioBufferInputBase;
+class QVideoFrame;
+class QAudioBuffer;
+class QPlatformVideoSource;
+
+namespace QFFmpeg
+{
+
+class RecordingEngine;
+class Muxer;
+class AudioEncoder;
+class VideoEncoder;
+class VideoFrameEncoder;
+class EncodingInitializer;
+
+class RecordingEngine : public QObject
+{
+ Q_OBJECT
+public:
+ RecordingEngine(const QMediaEncoderSettings &settings, std::unique_ptr<EncodingFormatContext> context);
+ ~RecordingEngine();
+
+ void initialize(const std::vector<QPlatformAudioBufferInputBase *> &audioSources,
+ const std::vector<QPlatformVideoSource *> &videoSources);
+ void finalize();
+
+ void setPaused(bool p);
+
+ void setAutoStop(bool autoStop);
+
+ bool autoStop() const { return m_autoStop; }
+
+ void setMetaData(const QMediaMetaData &metaData);
+ AVFormatContext *avFormatContext() { return m_formatContext->avFormatContext(); }
+ Muxer *getMuxer() { return m_muxer; }
+
+ bool isEndOfSourceStreams() const;
+
+public Q_SLOTS:
+ void newTimeStamp(qint64 time);
+
+Q_SIGNALS:
+ void durationChanged(qint64 duration);
+ void sessionError(QMediaRecorder::Error code, const QString &description);
+ void streamInitializationError(QMediaRecorder::Error code, const QString &description);
+ void finalizationDone();
+ void autoStopped();
+
+private:
+ class EncodingFinalizer : public QThread
+ {
+ public:
+ EncodingFinalizer(RecordingEngine &recordingEngine);
+
+ void run() override;
+
+ private:
+ RecordingEngine &m_recordingEngine;
+ };
+
+ friend class EncodingInitializer;
+ void addAudioInput(QFFmpegAudioInput *input);
+ void addAudioBufferInput(QPlatformAudioBufferInput *input, const QAudioBuffer &firstBuffer);
+ AudioEncoder *createAudioEncoder(const QAudioFormat &format);
+
+ void addVideoSource(QPlatformVideoSource *source, const QVideoFrame &firstFrame);
+ void handleSourceEndOfStream();
+
+ void start();
+
+ template <typename F, typename... Args>
+ void forEachEncoder(F &&f, Args &&...args);
+
+private:
+ QMediaEncoderSettings m_settings;
+ QMediaMetaData m_metaData;
+ std::unique_ptr<EncodingFormatContext> m_formatContext;
+ Muxer *m_muxer = nullptr;
+
+ QList<AudioEncoder *> m_audioEncoders;
+ QList<VideoEncoder *> m_videoEncoders;
+ std::unique_ptr<EncodingInitializer> m_initializer;
+
+ QMutex m_timeMutex;
+ qint64 m_timeRecorded = 0;
+
+ bool m_isHeaderWritten = false;
+ bool m_autoStop = false;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengineutils.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengineutils.cpp
new file mode 100644
index 000000000..6c2ba8b15
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengineutils.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 "recordingengine/qffmpegrecordingengineutils_p.h"
+#include "recordingengine/qffmpegencoderthread_p.h"
+#include "private/qplatformaudiobufferinput_p.h"
+#include "private/qplatformvideoframeinput_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+template <typename F>
+void doWithMediaFrameInput(QObject *source, F &&f)
+{
+ if (auto videoFrameInput = qobject_cast<QPlatformVideoFrameInput *>(source))
+ f(videoFrameInput);
+ else if (auto audioBufferInput = qobject_cast<QPlatformAudioBufferInput *>(source))
+ f(audioBufferInput);
+}
+
+void setEncoderInterface(QObject *source, QMediaInputEncoderInterface *interface)
+{
+ doWithMediaFrameInput(source, [&](auto source) {
+ using Source = std::remove_pointer_t<decltype(source)>;
+
+ source->setEncoderInterface(interface);
+ if (interface)
+ // Postpone emit 'encoderUpdated' as the encoding pipeline may be not
+ // completely ready at the moment. The case is calling QMediaRecorder::stop
+ // upon handling 'readyToSendFrame'
+ QMetaObject::invokeMethod(source, &Source::encoderUpdated, Qt::QueuedConnection);
+ else
+ emit source->encoderUpdated();
+ });
+}
+
+void setEncoderUpdateConnection(QObject *source, EncoderThread *encoder)
+{
+ doWithMediaFrameInput(source, [&](auto source) {
+ using Source = std::remove_pointer_t<decltype(source)>;
+ QObject::connect(encoder, &EncoderThread::canPushFrameChanged, source,
+ &Source::encoderUpdated);
+ });
+}
+
+void disconnectEncoderFromSource(EncoderThread *encoder)
+{
+ QObject *source = encoder->source();
+ if (!source)
+ return;
+
+ // We should address the dependency AudioEncoder from QFFmpegAudioInput to
+ // set null source here.
+ // encoder->setSource(nullptr);
+
+ QObject::disconnect(source, nullptr, encoder, nullptr);
+ setEncoderInterface(source, nullptr);
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengineutils_p.h b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengineutils_p.h
new file mode 100644
index 000000000..a60f81696
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengineutils_p.h
@@ -0,0 +1,81 @@
+// 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 QFFMPEGRECORDINGENGINEUTILS_P_H
+#define QFFMPEGRECORDINGENGINEUTILS_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 <queue>
+
+QT_BEGIN_NAMESPACE
+
+class QMediaInputEncoderInterface;
+class QPlatformVideoSource;
+
+namespace QFFmpeg {
+
+constexpr qint64 VideoFrameTimeBase = 1000000; // us in sec
+
+class EncoderThread;
+
+template <typename T>
+T dequeueIfPossible(std::queue<T> &queue)
+{
+ if (queue.empty())
+ return T{};
+
+ auto result = std::move(queue.front());
+ queue.pop();
+ return result;
+}
+
+void setEncoderInterface(QObject *source, QMediaInputEncoderInterface *interface);
+
+void setEncoderUpdateConnection(QObject *source, EncoderThread *encoder);
+
+template <typename Encoder, typename Source>
+void connectEncoderToSource(Encoder *encoder, Source *source)
+{
+ Q_ASSERT(!encoder->source());
+ encoder->setSource(source);
+
+ if constexpr (std::is_same_v<Source, QPlatformVideoSource>) {
+ QObject::connect(source, &Source::newVideoFrame, encoder, &Encoder::addFrame,
+ Qt::DirectConnection);
+
+ QObject::connect(source, &Source::activeChanged, encoder, [=]() {
+ if (!source->isActive())
+ encoder->setEndOfSourceStream();
+ });
+ } else {
+ QObject::connect(source, &Source::newAudioBuffer, encoder, &Encoder::addBuffer,
+ Qt::DirectConnection);
+ }
+
+ // TODO:
+ // QObject::connect(source, &Source::disconnectedFromSession, encoder, [=]() {
+ // encoder->setSourceEndOfStream();
+ // });
+
+ setEncoderUpdateConnection(source, encoder);
+ setEncoderInterface(source, encoder);
+}
+
+void disconnectEncoderFromSource(EncoderThread *encoder);
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGRECORDINGENGINEUTILS_P_H
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder.cpp
new file mode 100644
index 000000000..7c7d4a55f
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder.cpp
@@ -0,0 +1,245 @@
+// 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 "qffmpegrecordingengineutils_p.h"
+#include "private/qvideoframe_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)
+{
+ if (!frame.isValid()) {
+ setEndOfSourceStream();
+ return;
+ }
+
+ {
+ auto guard = lockLoopData();
+
+ resetEndOfSourceStream();
+
+ if (m_paused) {
+ m_shouldAdjustTimeBaseForNextFrame = true;
+ return;
+ }
+
+ // Drop frames if encoder can not keep up with the video source data rate;
+ // canPushFrame might be used instead
+ const bool queueFull = m_videoFrameQueue.size() >= m_maxQueueSize;
+
+ if (queueFull) {
+ qCDebug(qLcFFmpegVideoEncoder) << "RecordingEngine frame queue full. Frame lost.";
+ return;
+ }
+
+ m_videoFrameQueue.push({ frame, m_shouldAdjustTimeBaseForNextFrame });
+ m_shouldAdjustTimeBaseForNextFrame = false;
+ }
+
+ dataReady();
+}
+
+VideoEncoder::FrameInfo VideoEncoder::takeFrame()
+{
+ auto guard = 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()
+{
+ Q_ASSERT(isValid());
+
+ qCDebug(qLcFFmpegVideoEncoder) << "VideoEncoder::init started video device thread.";
+ bool ok = m_frameEncoder->open();
+ if (!ok)
+ emit m_recordingEngine.sessionError(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();
+
+ FrameInfo frameInfo = takeFrame();
+ QVideoFrame &frame = frameInfo.frame;
+ Q_ASSERT(frame.isValid());
+
+ if (!isValid())
+ return;
+
+ // qCDebug(qLcFFmpegEncoder) << "new video buffer" << frame.startTime();
+
+ AVFrameUPtr avFrame;
+
+ auto *videoBuffer = dynamic_cast<QFFmpegVideoBuffer *>(QVideoFramePrivate::hwBuffer(frame));
+ 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(QtVideo::MapMode::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);
+ }
+
+ const auto [startTime, endTime] = frameTimeStamps(frame);
+
+ if (frameInfo.shouldAdjustTimeBase) {
+ m_baseTime += startTime - m_lastFrameTime;
+ qCDebug(qLcFFmpegVideoEncoder)
+ << ">>>> adjusting base time to" << m_baseTime << startTime << m_lastFrameTime;
+ }
+
+ const qint64 time = startTime - m_baseTime;
+ m_lastFrameTime = endTime;
+
+ 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.sessionError(QMediaRecorder::ResourceError, err2str(ret));
+ }
+}
+
+bool VideoEncoder::checkIfCanPushFrame() const
+{
+ if (isRunning())
+ return m_videoFrameQueue.size() < m_maxQueueSize;
+ if (!isFinished())
+ return m_videoFrameQueue.empty();
+
+ return false;
+}
+
+std::pair<qint64, qint64> VideoEncoder::frameTimeStamps(const QVideoFrame &frame) const
+{
+ qint64 startTime = frame.startTime();
+ qint64 endTime = frame.endTime();
+
+ if (startTime == -1) {
+ startTime = m_lastFrameTime;
+ endTime = -1;
+ }
+
+ if (endTime == -1) {
+ qreal frameRate = frame.streamFrameRate();
+ if (frameRate <= 0.)
+ frameRate = m_frameEncoder->settings().videoFrameRate();
+
+ Q_ASSERT(frameRate > 0.f);
+ endTime = startTime + static_cast<qint64>(std::round(VideoFrameTimeBase / frameRate));
+ }
+
+ return { startTime, endTime };
+}
+
+} // 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..ff6a74fc8
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder_p.h
@@ -0,0 +1,64 @@
+// 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);
+
+protected:
+ bool checkIfCanPushFrame() const override;
+
+private:
+ struct FrameInfo
+ {
+ QVideoFrame frame;
+ bool shouldAdjustTimeBase = false;
+ };
+
+ FrameInfo takeFrame();
+ void retrievePackets();
+
+ void init() override;
+ void cleanup() override;
+ bool hasData() const override;
+ void processOne() override;
+
+ std::pair<qint64, qint64> frameTimeStamps(const QVideoFrame &frame) const;
+
+private:
+ std::queue<FrameInfo> m_videoFrameQueue;
+ const size_t m_maxQueueSize = 10; // Arbitrarily chosen to limit memory usage (332 MB @ 4K)
+
+ std::unique_ptr<VideoFrameEncoder> m_frameEncoder;
+ qint64 m_baseTime = 0;
+ bool m_shouldAdjustTimeBaseForNextFrame = true;
+ 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..eef2a64bf
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoderutils.cpp
@@ -0,0 +1,214 @@
+// 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 and hw_config
+ if (isAVFormatSupported(codec, 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..6f03c75a6
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoframeencoder.cpp
@@ -0,0 +1,477 @@
+// 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 (!updateSourceFormatAndSize(frame.get()))
+ return AVERROR(EINVAL);
+
+ 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());
+}
+
+qint64 VideoFrameEncoder::estimateDuration(const AVPacket &packet, bool isFirstPacket)
+{
+ qint64 duration = 0; // In stream units, multiply by time_base to get seconds
+
+ if (isFirstPacket) {
+ // First packet - Estimate duration from frame rate. Duration must
+ // be set for single-frame videos, otherwise they won't open in
+ // media player.
+ const AVRational frameDuration = av_inv_q(m_codecContext->framerate);
+ duration = av_rescale_q(1, frameDuration, m_stream->time_base);
+ } else {
+ // Duration is calculated from actual packet times. TODO: Handle discontinuities
+ duration = packet.pts - m_lastPacketTime;
+ }
+
+ return duration;
+}
+
+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;
+
+ if (packet->duration == 0) {
+ const bool firstFrame = m_lastPacketTime == AV_NOPTS_VALUE;
+ packet->duration = estimateDuration(*packet, firstFrame);
+ }
+
+ m_lastPacketTime = packet->pts;
+
+ 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;
+}
+
+bool VideoFrameEncoder::updateSourceFormatAndSize(const AVFrame *frame)
+{
+ Q_ASSERT(frame);
+
+ const QSize frameSize(frame->width, frame->height);
+ const AVPixelFormat frameFormat = static_cast<AVPixelFormat>(frame->format);
+
+ if (frameSize == m_sourceSize && frameFormat == m_sourceFormat)
+ return true;
+
+ auto applySourceFormatAndSize = [&](AVPixelFormat swFormat) {
+ m_sourceSize = frameSize;
+ m_sourceFormat = frameFormat;
+ m_sourceSWFormat = swFormat;
+ updateConversions();
+ return true;
+ };
+
+ if (frameFormat == m_sourceFormat)
+ return applySourceFormatAndSize(m_sourceSWFormat);
+
+ if (frameFormat == AV_PIX_FMT_NONE) {
+ qWarning() << "Got a frame with invalid pixel format";
+ return false;
+ }
+
+ if (isSwPixelFormat(frameFormat))
+ return applySourceFormatAndSize(frameFormat);
+
+ auto framesCtx = reinterpret_cast<const AVHWFramesContext *>(frame->hw_frames_ctx->data);
+ if (!framesCtx || framesCtx->sw_format == AV_PIX_FMT_NONE) {
+ qWarning() << "Cannot update conversions as hw frame has invalid framesCtx" << framesCtx;
+ return false;
+ }
+
+ return applySourceFormatAndSize(framesCtx->sw_format);
+}
+
+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..193590a64
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoframeencoder_p.h
@@ -0,0 +1,96 @@
+// 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();
+
+ const QMediaEncoderSettings &settings() { return m_settings; }
+
+private:
+ VideoFrameEncoder() = default;
+
+ bool updateSourceFormatAndSize(const AVFrame *frame);
+
+ void updateConversions();
+
+ bool initCodec();
+
+ bool initTargetFormats();
+
+ bool initCodecContext(AVFormatContext *formatContext);
+
+ qint64 estimateDuration(const AVPacket &packet, bool isFirstPacket);
+
+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;
+ qint64 m_lastPacketTime = AV_NOPTS_VALUE;
+ 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/ffmpeg/symbolstubs/openssl3.ver b/src/plugins/multimedia/ffmpeg/symbolstubs/openssl3.ver
new file mode 100644
index 000000000..88235a94c
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/symbolstubs/openssl3.ver
@@ -0,0 +1,7 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+OPENSSL_3.0.0 {
+ global:
+ *;
+};
diff --git a/src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-crypto.cpp b/src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-crypto.cpp
new file mode 100644
index 000000000..fbf3b783c
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-crypto.cpp
@@ -0,0 +1,6 @@
+// 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/qsymbolsresolveutils_p.h>
+
+// No stub functions are needed for ffmpeg
diff --git a/src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-ssl.cpp b/src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-ssl.cpp
new file mode 100644
index 000000000..3e38e398c
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-ssl.cpp
@@ -0,0 +1,300 @@
+// 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/qsymbolsresolveutils_p.h>
+
+#include <qstringliteral.h>
+
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+using namespace Qt::StringLiterals;
+
+[[maybe_unused]] static constexpr auto SHLIB_VERSION =
+#if defined(OPENSSL_SHLIB_VERSION)
+ OPENSSL_SHLIB_VERSION;
+#elif defined(SHLIB_VERSION_NUMBER)
+ SHLIB_VERSION_NUMBER;
+#endif
+
+
+#if !defined(Q_OS_ANDROID)
+CHECK_VERSIONS("ssl", SSL_NEEDED_SOVERSION, SHLIB_VERSION);
+#endif
+
+static std::unique_ptr<QLibrary> loadLib()
+{
+ auto lib = std::make_unique<QLibrary>();
+
+ auto tryLoad = [&](QString sslName, auto version) {
+ lib->setFileNameAndVersion(sslName, version);
+ return lib->load();
+ };
+
+// openssl on Android has specific suffixes
+#if defined(Q_OS_ANDROID)
+ {
+ auto suffix = qEnvironmentVariable("ANDROID_OPENSSL_SUFFIX");
+ if (suffix.isEmpty()) {
+#if (OPENSSL_VERSION_NUMBER >> 28) < 3 // major version < 3
+ suffix = "_1_1"_L1;
+#elif OPENSSL_VERSION_MAJOR == 3
+ suffix = "_3"_L1;
+#else
+ static_assert(false, "Unexpected openssl version");
+#endif
+ }
+
+ if (tryLoad("ssl"_L1 + suffix, -1))
+ return lib;
+ }
+#endif
+
+ if (tryLoad("ssl"_L1, SSL_NEEDED_SOVERSION ""_L1))
+ return lib;
+
+ return {};
+};
+
+
+BEGIN_INIT_FUNCS("ssl", loadLib)
+
+// BN functions
+
+INIT_FUNC(BN_value_one);
+INIT_FUNC(BN_mod_word);
+
+INIT_FUNC(BN_div_word)
+INIT_FUNC(BN_mul_word)
+INIT_FUNC(BN_add_word)
+INIT_FUNC(BN_sub_word)
+INIT_FUNC(BN_set_word)
+INIT_FUNC(BN_new)
+INIT_FUNC(BN_cmp)
+
+INIT_FUNC(BN_free);
+
+INIT_FUNC(BN_copy);
+
+INIT_FUNC(BN_CTX_new);
+
+INIT_FUNC(BN_CTX_free);
+INIT_FUNC(BN_CTX_start);
+
+INIT_FUNC(BN_CTX_get);
+INIT_FUNC(BN_CTX_end);
+
+INIT_FUNC(BN_rand);
+INIT_FUNC(BN_mod_exp);
+
+INIT_FUNC(BN_num_bits);
+INIT_FUNC(BN_num_bits_word);
+
+INIT_FUNC(BN_bn2hex);
+INIT_FUNC(BN_bn2dec);
+
+INIT_FUNC(BN_hex2bn);
+INIT_FUNC(BN_dec2bn);
+INIT_FUNC(BN_asc2bn);
+
+INIT_FUNC(BN_bn2bin);
+INIT_FUNC(BN_bin2bn);
+
+// BIO-related functions
+
+INIT_FUNC(BIO_new);
+INIT_FUNC(BIO_free);
+
+INIT_FUNC(BIO_read);
+INIT_FUNC(BIO_write);
+INIT_FUNC(BIO_s_mem);
+
+INIT_FUNC(BIO_set_data);
+
+INIT_FUNC(BIO_get_data);
+INIT_FUNC(BIO_set_init);
+
+INIT_FUNC(BIO_set_flags);
+INIT_FUNC(BIO_test_flags);
+INIT_FUNC(BIO_clear_flags);
+
+INIT_FUNC(BIO_meth_new);
+INIT_FUNC(BIO_meth_free);
+
+INIT_FUNC(BIO_meth_set_write);
+INIT_FUNC(BIO_meth_set_read);
+INIT_FUNC(BIO_meth_set_puts);
+INIT_FUNC(BIO_meth_set_gets);
+INIT_FUNC(BIO_meth_set_ctrl);
+INIT_FUNC(BIO_meth_set_create);
+INIT_FUNC(BIO_meth_set_destroy);
+INIT_FUNC(BIO_meth_set_callback_ctrl);
+
+// SSL functions
+
+INIT_FUNC(SSL_CTX_new);
+INIT_FUNC(SSL_CTX_up_ref);
+INIT_FUNC(SSL_CTX_free);
+
+INIT_FUNC(SSL_new);
+INIT_FUNC(SSL_up_ref);
+INIT_FUNC(SSL_free);
+
+INIT_FUNC(SSL_accept);
+INIT_FUNC(SSL_stateless);
+INIT_FUNC(SSL_connect);
+INIT_FUNC(SSL_read);
+INIT_FUNC(SSL_peek);
+INIT_FUNC(SSL_write);
+INIT_FUNC(SSL_ctrl);
+INIT_FUNC(SSL_shutdown);
+INIT_FUNC(SSL_set_bio);
+
+// options are unsigned long in openssl 1.1.1, and uint64 in 3.x.x
+INIT_FUNC(SSL_CTX_set_options);
+
+INIT_FUNC(SSL_get_error);
+INIT_FUNC(SSL_CTX_load_verify_locations);
+
+INIT_FUNC(SSL_CTX_set_verify);
+INIT_FUNC(SSL_CTX_use_PrivateKey);
+
+INIT_FUNC(SSL_CTX_use_PrivateKey_file);
+INIT_FUNC(SSL_CTX_use_certificate_chain_file);
+
+INIT_FUNC(ERR_get_error);
+
+INIT_FUNC(ERR_error_string);
+
+// TLS functions
+
+INIT_FUNC(TLS_client_method);
+INIT_FUNC(TLS_server_method);
+
+// RAND functions
+
+INIT_FUNC(RAND_bytes);
+
+END_INIT_FUNCS()
+
+//////////// Define
+
+// 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/symbolstubs/qffmpegsymbols-va-drm.cpp b/src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-va-drm.cpp
new file mode 100644
index 000000000..655a6b2b6
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-va-drm.cpp
@@ -0,0 +1,14 @@
+// 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/qsymbolsresolveutils_p.h>
+
+#include <va/va_drm.h>
+
+CHECK_VERSIONS("va-drm", VA_DRM_NEEDED_SOVERSION, VA_MAJOR_VERSION + 1);
+
+BEGIN_INIT_FUNCS("va-drm", VA_DRM_NEEDED_SOVERSION)
+INIT_FUNC(vaGetDisplayDRM)
+END_INIT_FUNCS()
+
+DEFINE_FUNC(vaGetDisplayDRM, 1);
diff --git a/src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-va-x11.cpp b/src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-va-x11.cpp
new file mode 100644
index 000000000..3bada9e69
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-va-x11.cpp
@@ -0,0 +1,14 @@
+// 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/qsymbolsresolveutils_p.h>
+
+#include <va/va_x11.h>
+
+CHECK_VERSIONS("va-x11", VA_X11_NEEDED_SOVERSION, VA_MAJOR_VERSION + 1);
+
+BEGIN_INIT_FUNCS("va-x11", VA_X11_NEEDED_SOVERSION)
+INIT_FUNC(vaGetDisplay)
+END_INIT_FUNCS()
+
+DEFINE_FUNC(vaGetDisplay, 1);
diff --git a/src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-va.cpp b/src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-va.cpp
new file mode 100644
index 000000000..cfd2e5686
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/symbolstubs/qffmpegsymbols-va.cpp
@@ -0,0 +1,150 @@
+// 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/qsymbolsresolveutils_p.h>
+
+#include <va/va.h>
+#include <va/va_str.h>
+
+// VAAPI generated the actual *.so name due to the rule:
+// https://github.com/intel/libva/blob/master/configure.ac
+//
+// The library name is generated libva.<x>.<y>.0 where
+// <x> = VA-API major version + 1
+// <y> = 100 * VA-API minor version + VA-API micro version
+CHECK_VERSIONS("va", VA_NEEDED_SOVERSION, VA_MAJOR_VERSION + 1);
+
+#ifdef Q_FFMPEG_PLUGIN_STUBS_ONLY
+constexpr const char *loggingName = "va(in plugin)";
+#else
+constexpr const char *loggingName = nullptr;
+#endif
+
+BEGIN_INIT_FUNCS("va", VA_NEEDED_SOVERSION, loggingName)
+
+
+INIT_FUNC(vaExportSurfaceHandle);
+INIT_FUNC(vaSyncSurface);
+INIT_FUNC(vaQueryVendorString);
+
+#ifndef Q_FFMPEG_PLUGIN_STUBS_ONLY
+
+INIT_FUNC(vaInitialize);
+INIT_FUNC(vaTerminate);
+INIT_FUNC(vaErrorStr);
+INIT_FUNC(vaSetErrorCallback);
+INIT_FUNC(vaSetInfoCallback);
+
+INIT_FUNC(vaCreateImage);
+INIT_FUNC(vaGetImage);
+INIT_FUNC(vaPutImage);
+INIT_FUNC(vaDeriveImage);
+INIT_FUNC(vaDestroyImage);
+INIT_FUNC(vaQueryImageFormats);
+
+INIT_FUNC(vaBeginPicture);
+INIT_FUNC(vaRenderPicture);
+INIT_FUNC(vaEndPicture);
+
+INIT_FUNC(vaCreateBuffer);
+INIT_FUNC(vaMapBuffer);
+INIT_FUNC(vaUnmapBuffer);
+#if VA_CHECK_VERSION(1, 9, 0)
+INIT_FUNC(vaSyncBuffer);
+#endif
+INIT_FUNC(vaDestroyBuffer);
+
+INIT_FUNC(vaCreateSurfaces);
+INIT_FUNC(vaDestroySurfaces);
+
+INIT_FUNC(vaCreateConfig);
+INIT_FUNC(vaGetConfigAttributes);
+INIT_FUNC(vaMaxNumProfiles);
+INIT_FUNC(vaMaxNumImageFormats);
+INIT_FUNC(vaMaxNumEntrypoints);
+INIT_FUNC(vaQueryConfigProfiles);
+INIT_FUNC(vaQueryConfigEntrypoints);
+INIT_FUNC(vaQuerySurfaceAttributes);
+INIT_FUNC(vaDestroyConfig);
+
+INIT_FUNC(vaCreateContext);
+INIT_FUNC(vaDestroyContext);
+
+INIT_FUNC(vaProfileStr);
+INIT_FUNC(vaEntrypointStr);
+
+INIT_FUNC(vaGetDisplayAttributes);
+
+INIT_FUNC(vaSetDriverName);
+
+INIT_FUNC(vaAcquireBufferHandle);
+INIT_FUNC(vaReleaseBufferHandle);
+
+#endif
+
+END_INIT_FUNCS()
+
+constexpr auto emptyString = "";
+
+DEFINE_FUNC(vaExportSurfaceHandle, 5, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaSyncSurface, 2, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaQueryVendorString, 1, emptyString);
+
+#ifndef Q_FFMPEG_PLUGIN_STUBS_ONLY
+
+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(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);
+
+
+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);
+
+#endif
+
diff --git a/src/plugins/multimedia/ffmpeg/symbolstubs/va.ver b/src/plugins/multimedia/ffmpeg/symbolstubs/va.ver
new file mode 100644
index 000000000..80c9a6dc0
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/symbolstubs/va.ver
@@ -0,0 +1,7 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+VA_API_0.33.0 {
+ global:
+ vaCreateSurfaces;
+};
diff --git a/src/plugins/multimedia/gstreamer/CMakeLists.txt b/src/plugins/multimedia/gstreamer/CMakeLists.txt
new file mode 100644
index 000000000..1ef1f9a36
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/CMakeLists.txt
@@ -0,0 +1,73 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_find_package(EGL)
+
+qt_internal_add_module(QGstreamerMediaPluginPrivate
+ STATIC
+ INTERNAL_MODULE
+ SOURCES
+ audio/qgstreameraudiodevice.cpp audio/qgstreameraudiodevice_p.h
+ audio/qgstreameraudiodecoder.cpp audio/qgstreameraudiodecoder_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_p.h
+ common/qgstreamermediaplayer.cpp common/qgstreamermediaplayer_p.h
+ common/qgstreamervideooutput.cpp common/qgstreamervideooutput_p.h
+ common/qgstreamervideooverlay.cpp common/qgstreamervideooverlay_p.h
+ common/qgstreamervideosink.cpp common/qgstreamervideosink_p.h
+ common/qgstpipeline.cpp common/qgstpipeline_p.h
+ common/qgstutils.cpp common/qgstutils_p.h
+ common/qgstvideobuffer.cpp common/qgstvideobuffer_p.h
+ common/qgstvideorenderersink.cpp common/qgstvideorenderersink_p.h
+ common/qgstsubtitlesink.cpp common/qgstsubtitlesink_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
+ PUBLIC_LIBRARIES
+ Qt::MultimediaPrivate
+ Qt::CorePrivate
+ GStreamer::GStreamer
+ GStreamer::App
+)
+
+qt_internal_extend_target(QGstreamerMediaPluginPrivate CONDITION QT_FEATURE_gstreamer_photography
+ PUBLIC_LIBRARIES
+ GStreamer::Photography
+)
+
+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
new file mode 100644
index 000000000..ba1582877
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder.cpp
@@ -0,0 +1,535 @@
+// 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 <audio/qgstreameraudiodecoder_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>
+
+#include <QtCore/qdatetime.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qsize.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qstandardpaths.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLcGstreamerAudioDecoder, "qt.multimedia.gstreameraudiodecoder");
+
+typedef enum {
+ GST_PLAY_FLAG_VIDEO = 0x00000001,
+ GST_PLAY_FLAG_AUDIO = 0x00000002,
+ GST_PLAY_FLAG_TEXT = 0x00000004,
+ GST_PLAY_FLAG_VIS = 0x00000008,
+ GST_PLAY_FLAG_SOFT_VOLUME = 0x00000010,
+ GST_PLAY_FLAG_NATIVE_AUDIO = 0x00000020,
+ GST_PLAY_FLAG_NATIVE_VIDEO = 0x00000040,
+ GST_PLAY_FLAG_DOWNLOAD = 0x00000080,
+ GST_PLAY_FLAG_BUFFERING = 0x000000100
+} GstPlayFlags;
+
+
+QMaybe<QPlatformAudioDecoder *> QGstreamerAudioDecoder::create(QAudioDecoder *parent)
+{
+ static const auto error = qGstErrorMessageIfElementsNotAvailable("audioconvert", "playbin");
+ if (error)
+ return *error;
+
+ return new QGstreamerAudioDecoder(parent);
+}
+
+QGstreamerAudioDecoder::QGstreamerAudioDecoder(QAudioDecoder *parent)
+ : QPlatformAudioDecoder(parent),
+ m_playbin{
+ QGstPipeline::adopt(GST_PIPELINE_CAST(
+ QGstElement::createFromFactory("playbin", "playbin").element())),
+ },
+ m_audioConvert{
+ QGstElement::createFromFactory("audioconvert", "audioconvert"),
+ }
+{
+ // Sort out messages
+ m_playbin.installMessageFilter(this);
+
+ // Set the rest of the pipeline up
+ setAudioFlags(true);
+
+ 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);
+
+ m_deepNotifySourceConnection = m_playbin.connect(
+ "deep-notify::source", (GCallback)&configureAppSrcElement, (gpointer)this);
+
+ // Set volume to 100%
+ gdouble volume = 1.0;
+ m_playbin.set("volume", volume);
+}
+
+QGstreamerAudioDecoder::~QGstreamerAudioDecoder()
+{
+ stop();
+
+ m_playbin.removeMessageFilter(this);
+
+#if QT_CONFIG(gstreamer_app)
+ delete m_appSrc;
+#endif
+}
+
+#if QT_CONFIG(gstreamer_app)
+void QGstreamerAudioDecoder::configureAppSrcElement([[maybe_unused]] GObject *object, GObject *orig,
+ [[maybe_unused]] GParamSpec *pspec,
+ QGstreamerAudioDecoder *self)
+{
+ // In case we switch from appsrc to not
+ if (!self->m_appSrc)
+ return;
+
+ QGstElementHandle appsrc;
+ g_object_get(orig, "source", &appsrc, NULL);
+
+ auto *qAppSrc = self->m_appSrc;
+ qAppSrc->setExternalAppSrc(QGstAppSrc{
+ qGstSafeCast<GstAppSrc>(appsrc.get()),
+ QGstAppSrc::NeedsRef, // CHECK: can we `release()`?
+ });
+ qAppSrc->setup(self->mDevice);
+}
+#endif
+
+bool QGstreamerAudioDecoder::processBusMessage(const QGstreamerMessage &message)
+{
+ qCDebug(qLcGstreamerAudioDecoder) << "received bus message:" << message;
+
+ 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.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.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.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;
+}
+
+QUrl QGstreamerAudioDecoder::source() const
+{
+ return mSource;
+}
+
+void QGstreamerAudioDecoder::setSource(const QUrl &fileName)
+{
+ stop();
+ mDevice = nullptr;
+ delete m_appSrc;
+ m_appSrc = nullptr;
+
+ bool isSignalRequired = (mSource != fileName);
+ mSource = fileName;
+ if (isSignalRequired)
+ sourceChanged();
+}
+
+QIODevice *QGstreamerAudioDecoder::sourceDevice() const
+{
+ return mDevice;
+}
+
+void QGstreamerAudioDecoder::setSourceDevice(QIODevice *device)
+{
+ stop();
+ mSource.clear();
+ bool isSignalRequired = (mDevice != device);
+ mDevice = device;
+ if (isSignalRequired)
+ sourceChanged();
+}
+
+void QGstreamerAudioDecoder::start()
+{
+ addAppSink();
+
+ if (!mSource.isEmpty()) {
+ m_playbin.set("uri", mSource.toEncoded().constData());
+ } else if (mDevice) {
+ // make sure we can read from device
+ if (!mDevice->isOpen() || !mDevice->isReadable()) {
+ processInvalidMedia(QAudioDecoder::ResourceError, QLatin1String("Unable to read from specified device"));
+ return;
+ }
+
+ 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 {
+ return;
+ }
+
+ // Set audio format
+ if (m_appSink) {
+ if (mFormat.isValid()) {
+ setAudioFlags(false);
+ auto caps = QGstUtils::capsForAudioFormat(mFormat);
+ m_appSink.setCaps(caps);
+ } else {
+ // We want whatever the native audio format is
+ setAudioFlags(true);
+ m_appSink.setCaps({});
+ }
+ }
+
+ if (m_playbin.setState(GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
+ qWarning() << "GStreamer; Unable to start decoding process";
+ m_playbin.dumpGraph("failed");
+ return;
+ }
+}
+
+void QGstreamerAudioDecoder::stop()
+{
+ 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;
+ bufferAvailableChanged(false);
+ }
+
+ if (m_position != invalidPosition) {
+ m_position = invalidPosition;
+ positionChanged(m_position.count());
+ }
+
+ if (m_duration != invalidDuration) {
+ m_duration = invalidDuration;
+ durationChanged(m_duration.count());
+ }
+
+ setIsDecoding(false);
+}
+
+QAudioFormat QGstreamerAudioDecoder::audioFormat() const
+{
+ return mFormat;
+}
+
+void QGstreamerAudioDecoder::setAudioFormat(const QAudioFormat &format)
+{
+ if (mFormat != format) {
+ mFormat = format;
+ formatChanged(mFormat);
+ }
+}
+
+QAudioBuffer QGstreamerAudioDecoder::read()
+{
+ using namespace std::chrono;
+
+ QAudioBuffer audioBuffer;
+
+ 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.
+ nanoseconds position = getPositionFromBuffer(buffer);
+ audioBuffer = QAudioBuffer{
+ QByteArray(bufferData, bufferSize),
+ format,
+ round<microseconds>(position).count(),
+ };
+ milliseconds positionInMs = round<milliseconds>(position);
+ if (position != m_position) {
+ m_position = positionInMs;
+ positionChanged(m_position.count());
+ }
+ }
+ gst_buffer_unmap(buffer, &mapInfo);
+
+ return audioBuffer;
+}
+
+qint64 QGstreamerAudioDecoder::position() const
+{
+ return m_position.count();
+}
+
+qint64 QGstreamerAudioDecoder::duration() const
+{
+ return m_duration.count();
+}
+
+void QGstreamerAudioDecoder::processInvalidMedia(QAudioDecoder::Error errorCode, const QString& errorString)
+{
+ stop();
+ error(int(errorCode), errorString);
+}
+
+GstFlowReturn QGstreamerAudioDecoder::newSample(GstAppSink *)
+{
+ // "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();
+ });
+
+ return GST_FLOW_OK;
+}
+
+GstFlowReturn QGstreamerAudioDecoder::new_sample(GstAppSink *sink, gpointer user_data)
+{
+ 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
+ flags &= ~(GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_TEXT | GST_PLAY_FLAG_VIS | GST_PLAY_FLAG_NATIVE_AUDIO);
+ flags |= GST_PLAY_FLAG_AUDIO;
+ if (wantNativeAudio)
+ flags |= GST_PLAY_FLAG_NATIVE_AUDIO;
+ m_playbin.set("flags", flags);
+}
+
+void QGstreamerAudioDecoder::addAppSink()
+{
+ using namespace std::chrono_literals;
+
+ if (m_appSink)
+ return;
+
+ qCDebug(qLcGstreamerAudioDecoder) << "QGstreamerAudioDecoder::addAppSink";
+ m_appSink = QGstAppSink::create("decoderAppSink");
+ GstAppSinkCallbacks callbacks{};
+ callbacks.new_sample = new_sample;
+ m_appSink.setCallbacks(callbacks, this, nullptr);
+
+#if GST_CHECK_VERSION(1, 24, 0)
+ static constexpr auto maxBufferTime = 500ms;
+ m_appSink.setMaxBufferTime(maxBufferTime);
+#else
+ static constexpr int maxBuffers = 16;
+ m_appSink.setMaxBuffers(maxBuffers);
+#endif
+
+ static constexpr bool sync = false;
+ m_appSink.setSync(sync);
+
+ QGstPipeline::modifyPipelineWhileNotRunning(m_playbin.getPipeline(), [&] {
+ m_outputBin.add(m_appSink);
+ qLinkGstElements(m_audioConvert, m_appSink);
+ });
+}
+
+void QGstreamerAudioDecoder::removeAppSink()
+{
+ if (!m_appSink)
+ return;
+
+ qCDebug(qLcGstreamerAudioDecoder) << "QGstreamerAudioDecoder::removeAppSink";
+
+ QGstPipeline::modifyPipelineWhileNotRunning(m_playbin.getPipeline(), [&] {
+ qUnlinkGstElements(m_audioConvert, m_appSink);
+ m_outputBin.stopAndRemoveElements(m_appSink);
+ });
+ m_appSink = {};
+}
+
+void QGstreamerAudioDecoder::updateDuration()
+{
+ std::optional<std::chrono::milliseconds> duration = m_playbin.durationInMs();
+ if (!duration)
+ duration = invalidDuration;
+
+ if (m_duration != duration) {
+ m_duration = *duration;
+ durationChanged(m_duration.count());
+ }
+
+ if (m_duration.count() > 0)
+ m_durationQueries = 0;
+
+ if (m_durationQueries > 0) {
+ //increase delay between duration requests
+ int delay = 25 << (5 - m_durationQueries);
+ QTimer::singleShot(delay, this, &QGstreamerAudioDecoder::updateDuration);
+ m_durationQueries--;
+ }
+}
+
+std::chrono::nanoseconds QGstreamerAudioDecoder::getPositionFromBuffer(GstBuffer *buffer)
+{
+ using namespace std::chrono;
+ using namespace std::chrono_literals;
+ nanoseconds position{ GST_BUFFER_TIMESTAMP(buffer) };
+ if (position >= 0ns)
+ return position;
+ else
+ return invalidPosition;
+}
+
+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
new file mode 100644
index 000000000..a5e192a38
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder_p.h
@@ -0,0 +1,116 @@
+// 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
+
+//
+// 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/qmultimediautils_p.h>
+#include <QtMultimedia/private/qplatformaudiodecoder_p.h>
+#include <QtMultimedia/private/qtmultimediaglobal_p.h>
+#include <QtMultimedia/qaudiodecoder.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qurl.h>
+
+#include <common/qgstpipeline_p.h>
+#include <common/qgst_p.h>
+
+#if QT_CONFIG(gstreamer_app)
+# include <common/qgstappsource_p.h>
+#endif
+
+#include <gst/app/gstappsink.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGstreamerMessage;
+
+class QGstreamerAudioDecoder final : public QPlatformAudioDecoder, public QGstreamerBusMessageFilter
+{
+ Q_OBJECT
+
+public:
+ static QMaybe<QPlatformAudioDecoder *> create(QAudioDecoder *parent);
+ virtual ~QGstreamerAudioDecoder();
+
+ QUrl source() const override;
+ void setSource(const QUrl &fileName) override;
+
+ QIODevice *sourceDevice() const override;
+ void setSourceDevice(QIODevice *device) override;
+
+ void start() override;
+ void stop() override;
+
+ QAudioFormat audioFormat() const override;
+ void setAudioFormat(const QAudioFormat &format) override;
+
+ QAudioBuffer read() override;
+
+ qint64 position() const override;
+ qint64 duration() const override;
+
+ // GStreamerBusMessageFilter interface
+ bool processBusMessage(const QGstreamerMessage &message) override;
+
+private slots:
+ void updateDuration();
+
+private:
+ explicit QGstreamerAudioDecoder(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();
+
+ bool handlePlaybinMessage(const QGstreamerMessage &);
+
+ void processInvalidMedia(QAudioDecoder::Error errorCode, const QString &errorString);
+ static std::chrono::nanoseconds getPositionFromBuffer(GstBuffer *buffer);
+
+ QGstPipeline m_playbin;
+ QGstBin m_outputBin;
+ QGstElement m_audioConvert;
+ QGstAppSink m_appSink;
+ QGstAppSource *m_appSrc = nullptr;
+
+ QUrl mSource;
+ QIODevice *mDevice = nullptr;
+ QAudioFormat mFormat;
+
+ int m_buffersAvailable = 0;
+
+ static constexpr auto invalidDuration = std::chrono::milliseconds{ -1 };
+ static constexpr auto invalidPosition = std::chrono::milliseconds{ -1 };
+ std::chrono::milliseconds m_position{ invalidPosition };
+ std::chrono::milliseconds m_duration{ invalidDuration };
+
+ int m_durationQueries = 0;
+
+ qint32 m_currentSessionId{};
+
+ QGObjectHandlerScopedConnection m_deepNotifySourceConnection;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGSTREAMERPLAYERSESSION_H
diff --git a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodevice.cpp b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodevice.cpp
new file mode 100644
index 000000000..dc6975030
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodevice.cpp
@@ -0,0 +1,77 @@
+// 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 <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)
+ : QAudioDevicePrivate(device, mode),
+ gstDevice{
+ d,
+ QGstDeviceHandle::NeedsRef,
+ }
+{
+ QGString name{
+ gst_device_get_display_name(gstDevice.get()),
+ };
+ description = name.toQString();
+
+ 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);
+ if (c.name() == "audio/x-raw") {
+ auto rate = c["rate"].toIntRange();
+ if (rate) {
+ minimumSampleRate = rate->min;
+ maximumSampleRate = rate->max;
+ }
+ auto channels = c["channels"].toIntRange();
+ if (channels) {
+ minimumChannelCount = channels->min;
+ maximumChannelCount = channels->max;
+ }
+ supportedSampleFormats = c["format"].getSampleFormats();
+ }
+ }
+
+ preferredFormat.setChannelCount(qBound(minimumChannelCount, 2, maximumChannelCount));
+ preferredFormat.setSampleRate(qBound(minimumSampleRate, 48000, maximumSampleRate));
+ QAudioFormat::SampleFormat f = QAudioFormat::Int16;
+ if (!supportedSampleFormats.contains(f))
+ f = supportedSampleFormats.value(0, QAudioFormat::Unknown);
+ preferredFormat.setSampleFormat(f);
+}
+
+QGStreamerCustomAudioDeviceInfo::QGStreamerCustomAudioDeviceInfo(
+ const QByteArray &gstreamerPipeline, QAudioDevice::Mode mode)
+ : QAudioDevicePrivate{
+ gstreamerPipeline,
+ mode,
+ }
+{
+}
+
+QAudioDevice qMakeCustomGStreamerAudioInput(const QByteArray &gstreamerPipeline)
+{
+ auto deviceInfo = std::make_unique<QGStreamerCustomAudioDeviceInfo>(gstreamerPipeline,
+ QAudioDevice::Mode::Input);
+
+ return deviceInfo.release()->create();
+}
+
+QAudioDevice qMakeCustomGStreamerAudioOutput(const QByteArray &gstreamerPipeline)
+{
+ auto deviceInfo = std::make_unique<QGStreamerCustomAudioDeviceInfo>(gstreamerPipeline,
+ QAudioDevice::Mode::Output);
+
+ return deviceInfo.release()->create();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodevice_p.h b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodevice_p.h
new file mode 100644
index 000000000..34d25bceb
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodevice_p.h
@@ -0,0 +1,52 @@
+// 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
+
+//
+// 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/qbytearray.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qlist.h>
+
+#include <QtMultimedia/qaudio.h>
+#include <QtMultimedia/qaudiodevice.h>
+#include <QtMultimedia/private/qaudiodevice_p.h>
+
+#include <QtQGstreamerMediaPlugin/private/qgst_handle_types_p.h>
+
+#include <gst/gst.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGStreamerAudioDeviceInfo : public QAudioDevicePrivate
+{
+public:
+ QGStreamerAudioDeviceInfo(GstDevice *gstDevice, const QByteArray &device, QAudioDevice::Mode mode);
+
+ QGstDeviceHandle gstDevice;
+};
+
+class QGStreamerCustomAudioDeviceInfo : public QAudioDevicePrivate
+{
+public:
+ QGStreamerCustomAudioDeviceInfo(const QByteArray &gstreamerPipeline, QAudioDevice::Mode mode);
+};
+
+QAudioDevice qMakeCustomGStreamerAudioInput(const QByteArray &gstreamerPipeline);
+QAudioDevice qMakeCustomGStreamerAudioOutput(const QByteArray &gstreamerPipeline);
+
+QT_END_NAMESPACE
+
+#endif
+
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..6cf133d6c
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgst.cpp
@@ -0,0 +1,1392 @@
+// 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) };
+}
+
+QGstStructureView QGValue::toStructure() const
+{
+ if (!value || !GST_VALUE_HOLDS_STRUCTURE(value))
+ return QGstStructureView(nullptr);
+ return QGstStructureView(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) };
+}
+
+// QGstStructureView
+
+QGstStructureView::QGstStructureView(const GstStructure *s) : structure(s) { }
+
+QGstStructureView::QGstStructureView(const QUniqueGstStructureHandle &handle)
+ : QGstStructureView{ handle.get() }
+{
+}
+
+QUniqueGstStructureHandle QGstStructureView::clone() const
+{
+ return QUniqueGstStructureHandle{ gst_structure_copy(structure) };
+}
+
+bool QGstStructureView::isNull() const
+{
+ return !structure;
+}
+
+QByteArrayView QGstStructureView::name() const
+{
+ return gst_structure_get_name(structure);
+}
+
+QGValue QGstStructureView::operator[](const char *fieldname) const
+{
+ return QGValue{ gst_structure_get_value(structure, fieldname) };
+}
+
+QGstCaps QGstStructureView::caps() const
+{
+ return operator[]("caps").toCaps();
+}
+
+QGstTagListHandle QGstStructureView::tags() const
+{
+ QGValue tags = operator[]("tags");
+ if (tags.isNull())
+ return {};
+
+ QGstTagListHandle tagList;
+ gst_structure_get(structure, "tags", GST_TYPE_TAG_LIST, &tagList, nullptr);
+ return tagList;
+}
+
+QSize QGstStructureView::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 QGstStructureView::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> QGstStructureView::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 QGstStructureView::getMessage()
+{
+ GstMessage *message = nullptr;
+ gst_structure_get(structure, "message", GST_TYPE_MESSAGE, &message, nullptr);
+ return QGstreamerMessage(message, QGstreamerMessage::HasRef);
+}
+
+std::optional<Fraction> QGstStructureView::pixelAspectRatio() const
+{
+ gint numerator;
+ gint denominator;
+ if (gst_structure_get_fraction(structure, "pixel-aspect-ratio", &numerator, &denominator)) {
+ return Fraction{
+ numerator,
+ denominator,
+ };
+ }
+
+ return std::nullopt;
+}
+
+// QTBUG-125249: gstreamer tries "to keep the input height (because of interlacing)". Can we align
+// the behavior between gstreamer and ffmpeg?
+static QSize qCalculateFrameSizeGStreamer(QSize resolution, Fraction par)
+{
+ if (par.numerator == par.denominator || par.numerator < 1 || par.denominator < 1)
+ return resolution;
+
+ return QSize{
+ resolution.width() * par.numerator / par.denominator,
+ resolution.height(),
+ };
+}
+
+QSize QGstStructureView::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 = qCalculateFrameSizeGStreamer(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()));
+}
+
+QGstStructureView QGstCaps::at(int index) const
+{
+ return QGstStructureView{
+ 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);
+}
+
+QGstStructureView QGstObject::getStructure(const char *property) const
+{
+ GstStructure *s = nullptr;
+ g_object_get(get(), property, &s, nullptr);
+ return QGstStructureView(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());
+}
+
+QLatin1StringView QGstObject::typeName() const
+{
+ return QLatin1StringView{
+ g_type_name(type()),
+ };
+}
+
+GstObject *QGstObject::object() const
+{
+ return get();
+}
+
+QLatin1StringView QGstObject::name() const
+{
+ using namespace Qt::StringLiterals;
+
+ return get() ? QLatin1StringView{ GST_OBJECT_NAME(get()) } : "(null)"_L1;
+}
+
+// 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);
+}
+
+QGstTagListHandle QGstPad::tags() const
+{
+ QGstTagListHandle tagList;
+ g_object_get(object(), "tags", &tagList, nullptr);
+ return tagList;
+}
+
+std::optional<QPlatformMediaPlayer::TrackType> QGstPad::inferTrackTypeFromName() const
+{
+ using namespace Qt::Literals;
+ QLatin1StringView padName = name();
+
+ if (padName.startsWith("video_"_L1))
+ return QPlatformMediaPlayer::TrackType::VideoStream;
+ if (padName.startsWith("audio_"_L1))
+ return QPlatformMediaPlayer::TrackType::AudioStream;
+ if (padName.startsWith("text_"_L1))
+ return QPlatformMediaPlayer::TrackType::SubtitleStream;
+
+ return std::nullopt;
+}
+
+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());
+}
+
+QGstElementFactoryHandle QGstElement::findFactory(const char *name)
+{
+ return QGstElementFactoryHandle{
+ gst_element_factory_find(name),
+ QGstElementFactoryHandle::HasRef,
+ };
+}
+
+QGstElementFactoryHandle QGstElement::findFactory(const QByteArray &name)
+{
+ return findFactory(name.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)
+{
+ if (state == GST_STATE_NULL) {
+ // QTBUG-125251: when changing pipeline state too quickly between NULL->PAUSED->NULL there
+ // may be a pending task to activate pads while we try to switch to NULL. This can cause an
+ // assertion failure in gstreamer. we therefore finish the state change when called on a bin
+ // or pipeline.
+ if (qIsGstObjectOfType<GstBin>(element()))
+ finishStateChange();
+ }
+
+ GstStateChangeReturn change = gst_element_set_state(element(), state);
+ if (change == GST_STATE_CHANGE_ASYNC)
+ change = gst_element_get_state(element(), nullptr, &state, timeout.count());
+
+ if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) {
+ qWarning() << "Could not change state of" << name() << "to" << state << change;
+ dumpPipelineGraph("setStatSyncFailure");
+ }
+ 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());
+
+ if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) {
+ qWarning() << "Could not finish change state of" << name() << change << state << pending;
+ dumpPipelineGraph("finishStateChangeFailure");
+ }
+ 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());
+}
+
+std::optional<std::chrono::nanoseconds> QGstElement::duration() const
+{
+ gint64 d;
+ if (!gst_element_query_duration(element(), GST_FORMAT_TIME, &d)) {
+ qDebug() << "QGstElement: failed to query duration";
+ return std::nullopt;
+ }
+ return std::chrono::nanoseconds{ d };
+}
+
+std::optional<std::chrono::milliseconds> QGstElement::durationInMs() const
+{
+ using namespace std::chrono;
+ auto dur = duration();
+ if (dur)
+ return round<milliseconds>(*dur);
+ return std::nullopt;
+}
+
+std::optional<std::chrono::nanoseconds> QGstElement::position() const
+{
+ QGstQueryHandle &query = positionQuery();
+
+ gint64 pos;
+ if (gst_element_query(element(), query.get())) {
+ gst_query_parse_position(query.get(), nullptr, &pos);
+ return std::chrono::nanoseconds{ pos };
+ }
+
+ qDebug() << "QGstElement: failed to query position";
+ return std::nullopt;
+}
+
+std::optional<std::chrono::milliseconds> QGstElement::positionInMs() const
+{
+ using namespace std::chrono;
+ auto pos = position();
+ if (pos)
+ return round<milliseconds>(*pos);
+ return std::nullopt;
+}
+
+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,
+ };
+ }
+}
+
+void QGstElement::dumpPipelineGraph(const char *filename) const
+{
+ static const bool dumpEnabled = qEnvironmentVariableIsSet("GST_DEBUG_DUMP_DOT_DIR");
+ if (dumpEnabled) {
+ QGstPipeline pipeline = getPipeline();
+ if (pipeline)
+ pipeline.dumpGraph(filename);
+ }
+}
+
+QGstQueryHandle &QGstElement::positionQuery() const
+{
+ if (Q_UNLIKELY(!m_positionQuery))
+ m_positionQuery = QGstQueryHandle{
+ gst_query_new_position(GST_FORMAT_TIME),
+ QGstQueryHandle::HasRef,
+ };
+
+ return m_positionQuery;
+}
+
+// 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(), GST_DEBUG_GRAPH_SHOW_VERBOSE, 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,
+ }
+{
+}
+
+void QGstBaseSink::setSync(bool arg)
+{
+ gst_base_sink_set_sync(baseSink(), arg ? TRUE : FALSE);
+}
+
+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());
+}
+
+# if GST_CHECK_VERSION(1, 24, 0)
+void QGstAppSink::setMaxBufferTime(std::chrono::nanoseconds ns)
+{
+ gst_app_sink_set_max_time(appSink(), qGstClockTimeFromChrono(ns));
+}
+# endif
+
+void QGstAppSink::setMaxBuffers(int n)
+{
+ gst_app_sink_set_max_buffers(appSink(), n);
+}
+
+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
+
+QString qGstErrorMessageCannotFindElement(std::string_view element)
+{
+ return QStringLiteral("Could not find the %1 GStreamer element")
+ .arg(QLatin1StringView(element));
+}
+
+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..413b02f44
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgst_debug.cpp
@@ -0,0 +1,565 @@
+// 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 QGstStructureView &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 QGstStreamCollectionHandle &handle)
+{
+ return dbg << handle.get();
+}
+
+QDebug operator<<(QDebug dbg, const QGstStreamHandle &handle)
+{
+ return dbg << handle.get();
+}
+
+QDebug operator<<(QDebug dbg, const QGstTagListHandle &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_TAG: {
+ QGstTagListHandle tagList;
+ gst_message_parse_tag(const_cast<GstMessage *>(msg), &tagList);
+
+ dbg << ", Tags: " << tagList;
+ 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;
+ }
+
+ case GST_MESSAGE_STREAM_COLLECTION: {
+ QGstStreamCollectionHandle collection;
+ gst_message_parse_stream_collection(const_cast<GstMessage *>(msg), &collection);
+
+ dbg << ", " << collection;
+ break;
+ }
+
+ case GST_MESSAGE_STREAMS_SELECTED: {
+ QGstStreamCollectionHandle collection;
+ gst_message_parse_streams_selected(const_cast<GstMessage *>(msg), &collection);
+
+ dbg << ", " << collection;
+ break;
+ }
+
+ case GST_MESSAGE_STREAM_STATUS: {
+ GstStreamStatusType streamStatus;
+ gst_message_parse_stream_status(const_cast<GstMessage *>(msg), &streamStatus, nullptr);
+
+ dbg << ", Stream Status: " << streamStatus;
+ 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, const GstStreamCollection *streamCollection)
+{
+ GstStreamCollection *collection = const_cast<GstStreamCollection *>(streamCollection);
+ guint size = gst_stream_collection_get_size(collection);
+
+ dbg << "Stream Collection: {";
+ for (guint index = 0; index != size; ++index) {
+ dbg << gst_stream_collection_get_stream(collection, index);
+ if (index + 1 != size)
+ dbg << ", ";
+ }
+
+ dbg << "}";
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const GstStream *cstream)
+{
+ GstStream *stream = const_cast<GstStream *>(cstream);
+
+ dbg << "GstStream { ";
+ dbg << "Type: " << gst_stream_type_get_name(gst_stream_get_stream_type(stream));
+
+ QGstTagListHandle tagList{
+ gst_stream_get_tags(stream),
+ QGstTagListHandle::HasRef,
+ };
+
+ if (tagList)
+ dbg << ", Tags: " << tagList;
+
+ QGstCaps caps{
+ gst_stream_get_caps(stream),
+ QGstCaps::HasRef,
+ };
+
+ if (caps)
+ dbg << ", Caps: " << caps;
+
+ dbg << "}";
+
+ 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);
+}
+
+#define ADD_ENUM_SWITCH(value) \
+ case value: \
+ return dbg << #value; \
+ static_assert(true, "enforce semicolon")
+
+QDebug operator<<(QDebug dbg, GstPadDirection direction)
+{
+ switch (direction) {
+ ADD_ENUM_SWITCH(GST_PAD_UNKNOWN);
+ ADD_ENUM_SWITCH(GST_PAD_SRC);
+ ADD_ENUM_SWITCH(GST_PAD_SINK);
+ default:
+ Q_UNREACHABLE_RETURN(dbg);
+ }
+}
+
+QDebug operator<<(QDebug dbg, GstStreamStatusType type)
+{
+ switch (type) {
+ ADD_ENUM_SWITCH(GST_STREAM_STATUS_TYPE_CREATE);
+ ADD_ENUM_SWITCH(GST_STREAM_STATUS_TYPE_ENTER);
+ ADD_ENUM_SWITCH(GST_STREAM_STATUS_TYPE_LEAVE);
+ ADD_ENUM_SWITCH(GST_STREAM_STATUS_TYPE_DESTROY);
+ ADD_ENUM_SWITCH(GST_STREAM_STATUS_TYPE_START);
+ ADD_ENUM_SWITCH(GST_STREAM_STATUS_TYPE_PAUSE);
+ ADD_ENUM_SWITCH(GST_STREAM_STATUS_TYPE_STOP);
+ default:
+ Q_UNREACHABLE_RETURN(dbg);
+ }
+ return dbg;
+}
+
+#undef ADD_ENUM_SWITCH
+
+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..df13c6c13
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgst_debug_p.h
@@ -0,0 +1,74 @@
+// 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 QGstStructureView &);
+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 QGstStreamCollectionHandle &);
+QDebug operator<<(QDebug, const QGstStreamHandle &);
+QDebug operator<<(QDebug, const QGstTagListHandle &);
+
+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, const GstStreamCollection *);
+QDebug operator<<(QDebug, const GstStream *);
+
+QDebug operator<<(QDebug, GstState);
+QDebug operator<<(QDebug, GstStateChange);
+QDebug operator<<(QDebug, GstStateChangeReturn);
+QDebug operator<<(QDebug, GstMessageType);
+QDebug operator<<(QDebug, GstPadDirection);
+QDebug operator<<(QDebug, GstStreamStatusType);
+
+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..e813f4181
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgst_handle_types_p.h
@@ -0,0 +1,270 @@
+// 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
+ {
+ if (GST_MINI_OBJECT_CAST(handle))
+ 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>::SharedHandle;
+using QGstDeviceHandle = QGstImpl::QGstHandleHelper<GstDevice>::SharedHandle;
+using QGstDeviceMonitorHandle = QGstImpl::QGstHandleHelper<GstDeviceMonitor>::UniqueHandle;
+using QGstBusHandle = QGstImpl::QGstHandleHelper<GstBus>::UniqueHandle;
+using QGstStreamCollectionHandle = QGstImpl::QGstHandleHelper<GstStreamCollection>::SharedHandle;
+using QGstStreamHandle = QGstImpl::QGstHandleHelper<GstStream>::SharedHandle;
+
+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
new file mode 100644
index 000000000..865b5895d
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgst_p.h
@@ -0,0 +1,853 @@
+// 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
+
+//
+// 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/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 <QtMultimedia/private/qplatformmediaplayer_p.h>
+
+#include <gst/gst.h>
+#include <gst/video/video-info.h>
+
+#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
+#endif
+
+#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 QGstStructureView;
+class QGstCaps;
+class QGstPipelinePrivate;
+class QCameraFormat;
+
+template <typename T> struct QGRange
+{
+ T min;
+ T max;
+};
+
+struct QGString : QUniqueGStringHandle
+{
+ using QUniqueGStringHandle::QUniqueGStringHandle;
+
+ QLatin1StringView asStringView() const { return QLatin1StringView{ get() }; }
+ QString toQString() const { return QString::fromUtf8(get()); }
+};
+
+class QGValue
+{
+public:
+ explicit QGValue(const GValue *v);
+ const GValue *value;
+
+ bool isNull() const;
+
+ std::optional<bool> toBool() const;
+ std::optional<int> toInt() const;
+ std::optional<int> toInt64() const;
+ template<typename T>
+ T *getPointer() const
+ {
+ return value ? static_cast<T *>(g_value_get_pointer(value)) : nullptr;
+ }
+
+ const char *toString() const;
+ std::optional<float> getFraction() const;
+ std::optional<QGRange<float>> getFractionRange() const;
+ std::optional<QGRange<int>> toIntRange() const;
+
+ QGstStructureView 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 (m_object && mode == NeedsRef)
+ Adaptor::ref(m_object);
+ }
+
+ QGstObjectWrapper(const QGstObjectWrapper &other) : m_object(other.m_object)
+ {
+ if (m_object)
+ Adaptor::ref(m_object);
+ }
+
+ ~QGstObjectWrapper()
+ {
+ if (m_object)
+ Adaptor::unref(m_object);
+ }
+
+ QGstObjectWrapper(QGstObjectWrapper &&other) noexcept
+ : m_object(std::exchange(other.m_object, nullptr))
+ {
+ }
+
+ QGstObjectWrapper &
+ operator=(const QGstObjectWrapper &other) // NOLINT: bugprone-unhandled-self-assign
+ {
+ 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;
+ }
+
+ QGstObjectWrapper &operator=(QGstObjectWrapper &&other) noexcept
+ {
+ if (this != &other) {
+ GstType *originalObject = m_object;
+ m_object = std::exchange(other.m_object, nullptr);
+
+ if (originalObject)
+ Adaptor::unref(originalObject);
+ }
+ return *this;
+ }
+
+ friend bool operator==(const QGstObjectWrapper &a, const QGstObjectWrapper &b)
+ {
+ return a.m_object == b.m_object;
+ }
+ friend bool operator!=(const QGstObjectWrapper &a, const QGstObjectWrapper &b)
+ {
+ return a.m_object != b.m_object;
+ }
+
+ explicit operator bool() const { return bool(m_object); }
+ bool isNull() const { return !m_object; }
+ GstType *release() { return std::exchange(m_object, nullptr); }
+
+protected:
+ GstType *get() const { return m_object; }
+};
+
+} // namespace QGstPointerImpl
+
+class QGstreamerMessage;
+
+class QGstStructureView
+{
+public:
+ const GstStructure *structure = nullptr;
+ explicit QGstStructureView(const GstStructure *);
+ explicit QGstStructureView(const QUniqueGstStructureHandle &);
+
+ QUniqueGstStructureHandle clone() const;
+
+ bool isNull() const;
+ QByteArrayView name() const;
+ QGValue operator[](const char *fieldname) const;
+
+ QGstCaps caps() const;
+ QGstTagListHandle tags() const;
+
+ QSize resolution() const;
+ QVideoFrameFormat::PixelFormat pixelFormat() const;
+ QGRange<float> frameRateRange() const;
+ QGstreamerMessage getMessage();
+ std::optional<Fraction> pixelAspectRatio() const;
+ QSize nativeSize() const;
+};
+
+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 : public QGstPointerImpl::QGstObjectWrapper<GstCaps>
+{
+ using BaseClass = QGstPointerImpl::QGstObjectWrapper<GstCaps>;
+
+public:
+ using BaseClass::BaseClass;
+ QGstCaps(const QGstCaps &) = default;
+ QGstCaps(QGstCaps &&) noexcept = default;
+ QGstCaps &operator=(const QGstCaps &) = default;
+ QGstCaps &operator=(QGstCaps &&) noexcept = default;
+
+ enum MemoryFormat { CpuMemory, GLTexture, DMABuf };
+
+ int size() const;
+ QGstStructureView at(int index) const;
+ GstCaps *caps() 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;
+};
+
+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:
+ 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;
+ QGstStructureView 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;
+ QLatin1StringView typeName() const;
+ GstObject *object() const;
+ QLatin1StringView name() const;
+};
+
+class QGObjectHandlerConnection
+{
+public:
+ QGObjectHandlerConnection(QGstObject object, gulong handler);
+
+ QGObjectHandlerConnection() = default;
+ QGObjectHandlerConnection(const QGObjectHandlerConnection &) = default;
+ QGObjectHandlerConnection(QGObjectHandlerConnection &&) = default;
+ QGObjectHandlerConnection &operator=(const QGObjectHandlerConnection &) = default;
+ QGObjectHandlerConnection &operator=(QGObjectHandlerConnection &&) = default;
+
+ void disconnect();
+
+private:
+ static constexpr gulong invalidHandlerId = std::numeric_limits<gulong>::max();
+
+ QGstObject object;
+ gulong handlerId = invalidHandlerId;
+};
+
+// disconnects in dtor
+class QGObjectHandlerScopedConnection
+{
+public:
+ QGObjectHandlerScopedConnection(QGObjectHandlerConnection connection);
+
+ QGObjectHandlerScopedConnection() = default;
+ QGObjectHandlerScopedConnection(const QGObjectHandlerScopedConnection &) = delete;
+ QGObjectHandlerScopedConnection &operator=(const QGObjectHandlerScopedConnection &) = delete;
+ QGObjectHandlerScopedConnection(QGObjectHandlerScopedConnection &&) = default;
+ QGObjectHandlerScopedConnection &operator=(QGObjectHandlerScopedConnection &&) = default;
+
+ ~QGObjectHandlerScopedConnection();
+
+ void disconnect();
+
+private:
+ QGObjectHandlerConnection connection;
+};
+
+class QGstElement;
+
+class QGstPad : public QGstObject
+{
+public:
+ 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;
+
+ QGstTagListHandle tags() const;
+
+ std::optional<QPlatformMediaPlayer::TrackType>
+ inferTrackTypeFromName() const; // for decodebin3 etc
+
+ 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) {
+ auto callback = [](GstPad *pad, GstPadProbeInfo *info, gpointer userData) {
+ return (static_cast<T *>(userData)->*Member)(QGstPad(pad, NeedsRef), info);
+ };
+
+ gst_pad_add_probe(pad(), type, callback, instance, nullptr);
+ }
+
+ template <typename Functor>
+ void doInIdleProbe(Functor &&work)
+ {
+ struct CallbackData {
+ QSemaphore waitDone;
+ Functor work;
+ };
+
+ CallbackData cd{
+ .waitDone = QSemaphore{},
+ .work = std::forward<Functor>(work),
+ };
+
+ auto callback= [](GstPad *, GstPadProbeInfo *, gpointer p) {
+ auto cd = reinterpret_cast<CallbackData*>(p);
+ cd->work();
+ cd->waitDone.release();
+ return GST_PAD_PROBE_REMOVE;
+ };
+
+ gst_pad_add_probe(pad(), GST_PAD_PROBE_TYPE_IDLE, callback, &cd, nullptr);
+ cd.waitDone.acquire();
+ }
+
+ template<auto Member, typename T>
+ void addEosProbe(T *instance) {
+ 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, callback, instance, nullptr);
+ }
+};
+
+class QGstClock : public QGstObject
+{
+public:
+ QGstClock() = default;
+ explicit QGstClock(const QGstObject &o);
+ explicit QGstClock(GstClock *clock, RefMode mode);
+
+ GstClock *clock() const;
+ GstClockTime time() const;
+};
+
+class QGstPipeline;
+
+class QGstElement : public QGstObject
+{
+public:
+ 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 &);
+
+ static QGstElementFactoryHandle findFactory(const char *);
+ static QGstElementFactoryHandle findFactory(const QByteArray &name);
+
+ 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;
+
+ std::optional<std::chrono::nanoseconds> duration() const;
+ std::optional<std::chrono::milliseconds> durationInMs() const;
+ std::optional<std::chrono::nanoseconds> position() const;
+ std::optional<std::chrono::milliseconds> positionInMs() const;
+
+ template <auto Member, typename T>
+ QGObjectHandlerConnection onPadAdded(T *instance)
+ {
+ struct Impl
+ {
+ static void callback(GstElement *e, GstPad *pad, gpointer userData)
+ {
+ (static_cast<T *>(userData)->*Member)(QGstElement(e, NeedsRef),
+ QGstPad(pad, NeedsRef));
+ };
+ };
+
+ return connect("pad-added", G_CALLBACK(Impl::callback), instance);
+ }
+ 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));
+ };
+ };
+
+ return connect("pad-removed", G_CALLBACK(Impl::callback), instance);
+ }
+ 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));
+ };
+ };
+
+ return connect("no-more-pads", G_CALLBACK(Impl::callback), instance);
+ }
+
+ GstClockTime baseTime() const;
+ void setBaseTime(GstClockTime time) const;
+
+ GstElement *element() const;
+
+ QGstElement getParent() const;
+ QGstPipeline getPipeline() const;
+ void dumpPipelineGraph(const char *filename) const;
+
+private:
+ QGstQueryHandle &positionQuery() const;
+ mutable QGstQueryHandle m_positionQuery;
+};
+
+template <typename... Ts>
+std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void>
+qLinkGstElements(const Ts &...ts)
+{
+ 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:
+ 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);
+ }
+
+ template <typename... Ts>
+ std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void> remove(const Ts &...ts)
+ {
+ if constexpr (sizeof...(Ts) == 1)
+ gst_bin_remove(bin(), ts.element()...);
+ else
+ gst_bin_remove_many(bin(), ts.element()..., nullptr);
+ }
+
+ template <typename... Ts>
+ std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void>
+ stopAndRemoveElements(Ts... ts)
+ {
+ 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 *);
+};
+
+class QGstBaseSink : public QGstElement
+{
+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;
+
+ void setSync(bool);
+
+ 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 setMaxBuffers(int);
+# if GST_CHECK_VERSION(1, 24, 0)
+ void setMaxBufferTime(std::chrono::nanoseconds);
+# endif
+
+ 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 GstClockTime qGstClockTimeFromChrono(std::chrono::nanoseconds ns)
+{
+ return ns.count();
+}
+
+QString qGstErrorMessageCannotFindElement(std::string_view element);
+
+template <typename Arg, typename... Args>
+std::optional<QString> qGstErrorMessageIfElementsNotAvailable(const Arg &arg, Args... args)
+{
+ QGstElementFactoryHandle factory = QGstElement::findFactory(arg);
+ if (!factory)
+ return qGstErrorMessageCannotFindElement(arg);
+
+ if constexpr (sizeof...(args) != 0)
+ return qGstErrorMessageIfElementsNotAvailable(args...);
+ else
+ return std::nullopt;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/gstreamer/common/qgstappsource.cpp b/src/plugins/multimedia/gstreamer/common/qgstappsource.cpp
new file mode 100644
index 000000000..3c345de82
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstappsource.cpp
@@ -0,0 +1,319 @@
+// 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 "qgstappsource_p.h"
+#include <common/qgstutils_p.h>
+#include "qnetworkreply.h"
+#include "qloggingcategory.h"
+
+static Q_LOGGING_CATEGORY(qLcAppSrc, "qt.multimedia.appsrc")
+
+QT_BEGIN_NAMESPACE
+
+QMaybe<QGstAppSource *> QGstAppSource::create(QObject *parent)
+{
+ QGstAppSrc appsrc = QGstAppSrc::create("appsrc");
+ if (!appsrc)
+ return qGstErrorMessageCannotFindElement("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);
+}
+
+QGstAppSource::~QGstAppSource()
+{
+ m_appSrc.setStateSync(GST_STATE_NULL);
+ streamDestroyed();
+ qCDebug(qLcAppSrc) << "~QGstAppSrc";
+}
+
+bool QGstAppSource::setup(QIODevice *stream, qint64 offset)
+{
+ QMutexLocker locker(&m_mutex);
+
+ if (m_appSrc.isNull())
+ return false;
+
+ if (!setStream(stream, offset))
+ return false;
+
+ 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;
+
+ if (m_sequential)
+ m_streamType = GST_APP_STREAM_TYPE_STREAM;
+ else
+ m_streamType = GST_APP_STREAM_TYPE_RANDOM_ACCESS;
+ gst_app_src_set_stream_type(appSrc, m_streamType);
+ gst_app_src_set_size(appSrc, m_sequential ? -1 : m_stream->size() - m_offset);
+
+ m_noMoreData = true;
+
+ return true;
+}
+
+void QGstAppSource::setAudioFormat(const QAudioFormat &f)
+{
+ QMutexLocker locker(&m_mutex);
+
+ m_format = f;
+ if (!m_format.isValid())
+ return;
+
+ auto caps = QGstUtils::capsForAudioFormat(m_format);
+ Q_ASSERT(!caps.isNull());
+ m_appSrc.set("caps", caps);
+ m_appSrc.set("format", GST_FORMAT_TIME);
+}
+
+void QGstAppSource::setExternalAppSrc(QGstAppSrc appsrc)
+{
+ QMutexLocker locker(&m_mutex);
+ m_appSrc = std::move(appsrc);
+}
+
+bool QGstAppSource::setStream(QIODevice *stream, qint64 offset)
+{
+ if (m_stream) {
+ disconnect(m_stream, &QIODevice::readyRead, this, &QGstAppSource::onDataReady);
+ disconnect(m_stream, &QIODevice::destroyed, this, &QGstAppSource::streamDestroyed);
+ m_stream = nullptr;
+ }
+
+ m_dataRequestSize = 0;
+ m_sequential = true;
+ m_maxBytes = 0;
+ streamedSamples = 0;
+
+ if (stream) {
+ if (!stream->isOpen() && !stream->open(QIODevice::ReadOnly))
+ return false;
+ m_stream = stream;
+ 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;
+}
+
+bool QGstAppSource::isStreamValid() const
+{
+ return m_stream != nullptr && m_stream->isOpen();
+}
+
+QGstElement QGstAppSource::element() const
+{
+ return m_appSrc;
+}
+
+void QGstAppSource::write(const char *data, qsizetype size)
+{
+ QMutexLocker locker(&m_mutex);
+
+ qCDebug(qLcAppSrc) << "write" << size << m_noMoreData << m_dataRequestSize;
+ if (!size)
+ return;
+ Q_ASSERT(!m_stream);
+ m_buffer.append(data, size);
+ m_noMoreData = false;
+ pushData();
+}
+
+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 QGstAppSource::streamDestroyed()
+{
+ qCDebug(qLcAppSrc) << "stream destroyed";
+ m_stream = nullptr;
+ m_dataRequestSize = 0;
+ streamedSamples = 0;
+ sendEOS();
+}
+
+void QGstAppSource::pushData()
+{
+ if (m_appSrc.isNull() || !m_dataRequestSize || m_suspended) {
+ qCDebug(qLcAppSrc) << "push data: return immediately" << m_appSrc.isNull() << m_dataRequestSize << m_suspended;
+ return;
+ }
+
+ qCDebug(qLcAppSrc) << "pushData" << (m_stream ? m_stream : nullptr) << m_buffer.size();
+ if ((m_stream && m_stream->atEnd())) {
+ eosOrIdle();
+ qCDebug(qLcAppSrc) << "end pushData" << (m_stream ? m_stream : nullptr) << m_buffer.size();
+ return;
+ }
+
+ qint64 size;
+ if (m_stream)
+ size = m_stream->bytesAvailable();
+ else
+ size = m_buffer.size();
+
+ if (!m_dataRequestSize)
+ m_dataRequestSize = m_maxBytes;
+ size = qMin(size, (qint64)m_dataRequestSize);
+ qCDebug(qLcAppSrc) << " reading" << size << "bytes" << size << m_dataRequestSize;
+
+ GstBuffer* buffer = gst_buffer_new_and_alloc(size);
+
+ if (m_sequential || !m_stream)
+ buffer->offset = bytesReadSoFar;
+ else
+ buffer->offset = m_stream->pos();
+
+ if (m_format.isValid()) {
+ // timestamp raw audio data
+ uint nSamples = size/m_format.bytesPerFrame();
+
+ GST_BUFFER_TIMESTAMP(buffer) = gst_util_uint64_scale(streamedSamples, GST_SECOND, m_format.sampleRate());
+ GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale(nSamples, GST_SECOND, m_format.sampleRate());
+ streamedSamples += nSamples;
+ }
+
+ GstMapInfo mapInfo;
+ gst_buffer_map(buffer, &mapInfo, GST_MAP_WRITE);
+ void* bufferData = mapInfo.data;
+
+ qint64 bytesRead;
+ if (m_stream)
+ bytesRead = m_stream->read((char*)bufferData, size);
+ else
+ bytesRead = m_buffer.read((char*)bufferData, size);
+ buffer->offset_end = buffer->offset + bytesRead - 1;
+ bytesReadSoFar += bytesRead;
+
+ gst_buffer_unmap(buffer, &mapInfo);
+ qCDebug(qLcAppSrc) << "pushing bytes into gstreamer" << buffer->offset << bytesRead;
+ if (bytesRead == 0) {
+ gst_buffer_unref(buffer);
+ eosOrIdle();
+ qCDebug(qLcAppSrc) << "end pushData" << (m_stream ? m_stream : nullptr) << m_buffer.size();
+ return;
+ }
+ m_noMoreData = false;
+ emit bytesProcessed(bytesRead);
+
+ GstFlowReturn ret = m_appSrc.pushBuffer(buffer);
+ if (ret == GST_FLOW_ERROR) {
+ qWarning() << "QGstAppSrc: push buffer error";
+ } else if (ret == GST_FLOW_FLUSHING) {
+ qWarning() << "QGstAppSrc: push buffer wrong state";
+ }
+ qCDebug(qLcAppSrc) << "end pushData" << (m_stream ? m_stream : nullptr) << m_buffer.size();
+
+}
+
+bool QGstAppSource::doSeek(qint64 value)
+{
+ if (isStreamValid())
+ return m_stream->seek(value + m_offset);
+ return false;
+}
+
+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;
+
+ QGstAppSource *self = reinterpret_cast<QGstAppSource *>(userdata);
+ Q_ASSERT(self);
+
+ QMutexLocker locker(&self->m_mutex);
+
+ if (self->m_sequential)
+ return false;
+
+ self->doSeek(arg0);
+ return true;
+}
+
+void QGstAppSource::on_enough_data(GstAppSrc *, gpointer userdata)
+{
+ qCDebug(qLcAppSrc) << "on_enough_data";
+ QGstAppSource *self = static_cast<QGstAppSource *>(userdata);
+ Q_ASSERT(self);
+ QMutexLocker locker(&self->m_mutex);
+ self->m_dataRequestSize = 0;
+}
+
+void QGstAppSource::on_need_data(GstAppSrc *, guint arg0, gpointer userdata)
+{
+ qCDebug(qLcAppSrc) << "on_need_data requesting bytes" << arg0;
+ QGstAppSource *self = static_cast<QGstAppSource *>(userdata);
+ Q_ASSERT(self);
+ QMutexLocker locker(&self->m_mutex);
+ self->m_dataRequestSize = arg0;
+ self->pushData();
+ qCDebug(qLcAppSrc) << "done on_need_data";
+}
+
+void QGstAppSource::sendEOS()
+{
+ qCDebug(qLcAppSrc) << "sending EOS";
+ if (m_appSrc.isNull())
+ return;
+
+ gst_app_src_end_of_stream(GST_APP_SRC(m_appSrc.element()));
+}
+
+void QGstAppSource::eosOrIdle()
+{
+ qCDebug(qLcAppSrc) << "eosOrIdle";
+ if (m_appSrc.isNull())
+ return;
+
+ if (!m_sequential) {
+ sendEOS();
+ return;
+ }
+ if (m_noMoreData)
+ return;
+ qCDebug(qLcAppSrc) << " idle!";
+ 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/qgstpipeline.cpp b/src/plugins/multimedia/gstreamer/common/qgstpipeline.cpp
new file mode 100644
index 000000000..c92a12764
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstpipeline.cpp
@@ -0,0 +1,414 @@
+// 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>
+#include <QtCore/qmutex.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qabstracteventdispatcher.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qproperty.h>
+
+#include "qgstpipeline_p.h"
+#include "qgstreamermessage_p.h"
+
+QT_BEGIN_NAMESPACE
+
+static constexpr GstSeekFlags rateChangeSeekFlags =
+#if GST_CHECK_VERSION(1, 18, 0)
+ GST_SEEK_FLAG_INSTANT_RATE_CHANGE;
+#else
+ GST_SEEK_FLAG_FLUSH;
+#endif
+
+class QGstPipelinePrivate : public QObject
+{
+public:
+ int m_ref = 0;
+ guint m_tag = 0;
+ GstBus *m_bus = nullptr;
+ QTimer *m_intervalTimer = nullptr;
+ QMutex filterMutex;
+ QList<QGstreamerSyncMessageFilter*> syncFilters;
+ QList<QGstreamerBusMessageFilter*> busFilters;
+ bool inStoppedState = true;
+ mutable std::chrono::nanoseconds m_position{};
+ double m_rate = 1.;
+ bool m_flushOnConfigChanges = false;
+ bool m_pendingFlush = false;
+
+ int m_configCounter = 0;
+ GstState m_savedState = GST_STATE_NULL;
+
+ explicit QGstPipelinePrivate(GstBus *bus, QObject *parent = nullptr);
+ ~QGstPipelinePrivate();
+
+ void ref() { ++ m_ref; }
+ void deref() { if (!--m_ref) delete this; }
+
+ void installMessageFilter(QGstreamerSyncMessageFilter *filter);
+ void removeMessageFilter(QGstreamerSyncMessageFilter *filter);
+ void installMessageFilter(QGstreamerBusMessageFilter *filter);
+ void removeMessageFilter(QGstreamerBusMessageFilter *filter);
+
+ 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 : std::as_const(d->syncFilters)) {
+ if (filter->processSyncMessage(
+ QGstreamerMessage{ message, QGstreamerMessage::NeedsRef })) {
+ gst_message_unref(message);
+ return GST_BUS_DROP;
+ }
+ }
+
+ return GST_BUS_PASS;
+ }
+
+ void processMessage(GstMessage *message)
+ {
+ if (!message)
+ return;
+
+ QGstreamerMessage msg{
+ message,
+ QGstreamerMessage::NeedsRef,
+ };
+
+ processMessage(msg);
+ }
+
+ static gboolean busCallback(GstBus *, GstMessage *message, gpointer data)
+ {
+ static_cast<QGstPipelinePrivate *>(data)->processMessage(message);
+ return TRUE;
+ }
+};
+
+QGstPipelinePrivate::QGstPipelinePrivate(GstBus* bus, QObject* parent)
+ : QObject(parent),
+ m_bus(bus)
+{
+ // glib event loop can be disabled either by env variable or QT_NO_GLIB define, so check the dispacher
+ QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher();
+ const bool hasGlib = dispatcher && dispatcher->inherits("QEventDispatcherGlib");
+ if (!hasGlib) {
+ m_intervalTimer = new QTimer(this);
+ m_intervalTimer->setInterval(250);
+ 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);
+ }
+
+ gst_bus_set_sync_handler(bus, (GstBusSyncHandler)syncGstBusFilter, this, nullptr);
+}
+
+QGstPipelinePrivate::~QGstPipelinePrivate()
+{
+ delete m_intervalTimer;
+
+ if (m_tag)
+ gst_bus_remove_watch(m_bus);
+
+ gst_bus_set_sync_handler(m_bus, nullptr, nullptr, nullptr);
+ gst_object_unref(GST_OBJECT(m_bus));
+}
+
+void QGstPipelinePrivate::installMessageFilter(QGstreamerSyncMessageFilter *filter)
+{
+ if (filter) {
+ QMutexLocker lock(&filterMutex);
+ if (!syncFilters.contains(filter))
+ syncFilters.append(filter);
+ }
+}
+
+void QGstPipelinePrivate::removeMessageFilter(QGstreamerSyncMessageFilter *filter)
+{
+ if (filter) {
+ QMutexLocker lock(&filterMutex);
+ syncFilters.removeAll(filter);
+ }
+}
+
+void QGstPipelinePrivate::installMessageFilter(QGstreamerBusMessageFilter *filter)
+{
+ if (filter && !busFilters.contains(filter))
+ busFilters.append(filter);
+}
+
+void QGstPipelinePrivate::removeMessageFilter(QGstreamerBusMessageFilter *filter)
+{
+ if (filter)
+ busFilters.removeAll(filter);
+}
+
+QGstPipeline QGstPipeline::create(const char *name)
+{
+ GstPipeline *pipeline = qGstCheckedCast<GstPipeline>(gst_pipeline_new(name));
+ return adopt(pipeline);
+}
+
+QGstPipeline QGstPipeline::adopt(GstPipeline *pipeline)
+{
+ 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, RefMode mode) : QGstBin(qGstCheckedCast<GstBin>(p), mode)
+{
+}
+
+QGstPipeline::~QGstPipeline() = default;
+
+bool QGstPipeline::inStoppedState() const
+{
+ QGstPipelinePrivate *d = getPrivate();
+ return d->inStoppedState;
+}
+
+void QGstPipeline::setInStoppedState(bool stopped)
+{
+ QGstPipelinePrivate *d = getPrivate();
+ d->inStoppedState = stopped;
+}
+
+void QGstPipeline::setFlushOnConfigChanges(bool flush)
+{
+ QGstPipelinePrivate *d = getPrivate();
+ d->m_flushOnConfigChanges = flush;
+}
+
+void QGstPipeline::installMessageFilter(QGstreamerSyncMessageFilter *filter)
+{
+ QGstPipelinePrivate *d = getPrivate();
+ d->installMessageFilter(filter);
+}
+
+void QGstPipeline::removeMessageFilter(QGstreamerSyncMessageFilter *filter)
+{
+ QGstPipelinePrivate *d = getPrivate();
+ d->removeMessageFilter(filter);
+}
+
+void QGstPipeline::installMessageFilter(QGstreamerBusMessageFilter *filter)
+{
+ QGstPipelinePrivate *d = getPrivate();
+ d->installMessageFilter(filter);
+}
+
+void QGstPipeline::removeMessageFilter(QGstreamerBusMessageFilter *filter)
+{
+ 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;
+ flush();
+ }
+ return retval;
+}
+
+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 (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;
+
+ 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()
+{
+ QGstPipelinePrivate *d = getPrivate();
+ Q_ASSERT(!isNull());
+
+ --d->m_configCounter;
+ if (d->m_configCounter)
+ return;
+
+ if (d->m_flushOnConfigChanges)
+ d->m_pendingFlush = true;
+ if (d->m_savedState == GST_STATE_PLAYING)
+ setState(GST_STATE_PLAYING);
+ d->m_savedState = GST_STATE_NULL;
+}
+
+void QGstPipeline::flush()
+{
+ seek(position());
+}
+
+void QGstPipeline::seek(std::chrono::nanoseconds pos, double rate)
+{
+ using namespace std::chrono_literals;
+
+ 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
+
+ bool success = (rate > 0)
+ ? gst_element_seek(element(), d->m_rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_SET, pos.count(), GST_SEEK_TYPE_END, 0)
+ : gst_element_seek(element(), d->m_rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, pos.count());
+
+ if (!success) {
+ qDebug() << "seek: gst_element_seek failed" << pos;
+ return;
+ }
+
+ d->m_position = pos;
+}
+
+void QGstPipeline::seek(std::chrono::nanoseconds pos)
+{
+ seek(pos, getPrivate()->m_rate);
+}
+
+void QGstPipeline::setPlaybackRate(double rate)
+{
+ QGstPipelinePrivate *d = getPrivate();
+ if (rate == d->m_rate)
+ return;
+
+ d->m_rate = rate;
+
+ applyPlaybackRate(/*instantRateChange =*/true);
+}
+
+double QGstPipeline::playbackRate() const
+{
+ QGstPipelinePrivate *d = getPrivate();
+ return d->m_rate;
+}
+
+void QGstPipeline::applyPlaybackRate(bool instantRateChange)
+{
+ QGstPipelinePrivate *d = getPrivate();
+
+ bool success = gst_element_seek(element(), d->m_rate, GST_FORMAT_UNDEFINED,
+ instantRateChange ? rateChangeSeekFlags : GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE, GST_SEEK_TYPE_NONE,
+ GST_CLOCK_TIME_NONE);
+ if (!success)
+ qDebug() << "setPlaybackRate: gst_element_seek failed";
+}
+
+void QGstPipeline::setPosition(std::chrono::nanoseconds pos)
+{
+ seek(pos);
+}
+
+std::chrono::nanoseconds QGstPipeline::position() const
+{
+ QGstPipelinePrivate *d = getPrivate();
+ std::optional<std::chrono::nanoseconds> pos = QGstElement::position();
+ if (pos)
+ d->m_position = *pos;
+ else
+ qDebug() << "QGstPipeline: failed to query position, using previous position";
+
+ return d->m_position;
+}
+
+std::chrono::milliseconds QGstPipeline::positionInMs() const
+{
+ using namespace std::chrono;
+ return round<milliseconds>(position());
+}
+
+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
new file mode 100644
index 000000000..ef08bfaaa
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstpipeline_p.h
@@ -0,0 +1,119 @@
+// 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
+
+//
+// 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 <QtCore/qobject.h>
+
+#include "qgst_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QGstreamerMessage;
+
+class QGstreamerSyncMessageFilter {
+public:
+ //returns true if message was processed and should be dropped, false otherwise
+ virtual bool processSyncMessage(const QGstreamerMessage &message) = 0;
+};
+
+
+class QGstreamerBusMessageFilter {
+public:
+ //returns true if message was processed and should be dropped, false otherwise
+ virtual bool processBusMessage(const QGstreamerMessage &message) = 0;
+};
+
+class QGstPipelinePrivate;
+
+class QGstPipeline : public QGstBin
+{
+public:
+ constexpr QGstPipeline() = default;
+ 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.
+ // QMediaPlayer is still in a stopped state, while we put the gstreamer pipeline into a
+ // Paused state so that we can get the required metadata of the stream and also have a fast
+ // transition to play.
+ bool inStoppedState() const;
+ void setInStoppedState(bool stopped);
+
+ void setFlushOnConfigChanges(bool flush);
+
+ void installMessageFilter(QGstreamerSyncMessageFilter *filter);
+ void removeMessageFilter(QGstreamerSyncMessageFilter *filter);
+ void installMessageFilter(QGstreamerBusMessageFilter *filter);
+ void removeMessageFilter(QGstreamerBusMessageFilter *filter);
+
+ GstStateChangeReturn setState(GstState state);
+
+ GstPipeline *pipeline() const { return GST_PIPELINE_CAST(get()); }
+
+ void processMessages(GstMessageType = GST_MESSAGE_ANY);
+
+ void dumpGraph(const char *fileName);
+
+ template <typename Functor>
+ void modifyPipelineWhileNotRunning(Functor &&fn)
+ {
+ beginConfig();
+ fn();
+ endConfig();
+ }
+
+ template <typename Functor>
+ static void modifyPipelineWhileNotRunning(QGstPipeline &&pipeline, Functor &&fn)
+ {
+ if (pipeline)
+ pipeline.modifyPipelineWhileNotRunning(fn);
+ else
+ fn();
+ }
+
+ void flush();
+
+ void setPlaybackRate(double rate);
+ double playbackRate() const;
+ void applyPlaybackRate(bool instantRateChange);
+
+ void setPosition(std::chrono::nanoseconds pos);
+ std::chrono::nanoseconds position() const;
+ std::chrono::milliseconds positionInMs() const;
+
+private:
+ void seek(std::chrono::nanoseconds pos, double rate);
+ void seek(std::chrono::nanoseconds pos);
+
+ QGstPipelinePrivate *getPrivate() const;
+
+ void beginConfig();
+ void endConfig();
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput.cpp b/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput.cpp
new file mode 100644
index 000000000..7c620da39
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput.cpp
@@ -0,0 +1,137 @@
+// 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 <audio/qgstreameraudiodevice_p.h>
+#include <common/qgstreameraudioinput_p.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <utility>
+
+static Q_LOGGING_CATEGORY(qLcMediaAudioInput, "qt.multimedia.audioInput")
+
+QT_BEGIN_NAMESPACE
+
+QMaybe<QPlatformAudioInput *> QGstreamerAudioInput::create(QAudioInput *parent)
+{
+ static const auto error = qGstErrorMessageIfElementsNotAvailable("autoaudiosrc", "volume");
+ if (error)
+ return *error;
+
+ return new QGstreamerAudioInput(parent);
+}
+
+QGstreamerAudioInput::QGstreamerAudioInput(QAudioInput *parent)
+ : QObject(parent),
+ QPlatformAudioInput(parent),
+ gstAudioInput(QGstBin::create("audioInput")),
+ audioSrc{
+ QGstElement::createFromFactory("autoaudiosrc", "autoaudiosrc"),
+ },
+ audioVolume{
+ QGstElement::createFromFactory("volume", "volume"),
+ }
+{
+ gstAudioInput.add(audioSrc, audioVolume);
+ qLinkGstElements(audioSrc, audioVolume);
+
+ gstAudioInput.addGhostPad(audioVolume, "src");
+}
+
+QGstElement QGstreamerAudioInput::createGstElement()
+{
+ const auto *customDeviceInfo =
+ dynamic_cast<const QGStreamerCustomAudioDeviceInfo *>(m_audioDevice.handle());
+
+ if (customDeviceInfo) {
+ qCDebug(qLcMediaAudioInput)
+ << "requesting custom audio src element: " << customDeviceInfo->id;
+
+ QGstElement element = QGstBin::createFromPipelineDescription(customDeviceInfo->id,
+ /*name=*/nullptr,
+ /*ghostUnlinkedPads=*/true);
+ if (element)
+ return element;
+
+ qCWarning(qLcMediaAudioInput)
+ << "Cannot create audio source element:" << customDeviceInfo->id;
+ }
+
+ const QByteArray &id = m_audioDevice.id();
+ if constexpr (QT_CONFIG(pulseaudio)) {
+ QGstElement newSrc = QGstElement::createFromFactory("pulsesrc", "audiosrc");
+ if (newSrc) {
+ newSrc.set("device", id.constData());
+ return newSrc;
+ } else {
+ qWarning() << "Cannot create pulsesrc";
+ }
+ } else if constexpr (QT_CONFIG(alsa)) {
+ QGstElement newSrc = QGstElement::createFromFactory("alsasrc", "audiosrc");
+ if (newSrc) {
+ newSrc.set("device", id.constData());
+ return newSrc;
+ } else {
+ qWarning() << "Cannot create alsasrc";
+ }
+ } else {
+ auto *deviceInfo = dynamic_cast<const QGStreamerAudioDeviceInfo *>(m_audioDevice.handle());
+ if (deviceInfo && deviceInfo->gstDevice) {
+ QGstElement element = QGstElement::createFromDevice(deviceInfo->gstDevice, "audiosrc");
+ if (element)
+ return element;
+ }
+ }
+ qCWarning(qLcMediaAudioInput) << "Invalid audio device";
+ qCWarning(qLcMediaAudioInput)
+ << "Failed to create a gst element for the audio device, using a default audio source";
+ return QGstElement::createFromFactory("autoaudiosrc", "audiosrc");
+}
+
+QGstreamerAudioInput::~QGstreamerAudioInput()
+{
+ gstAudioInput.setStateSync(GST_STATE_NULL);
+}
+
+void QGstreamerAudioInput::setVolume(float volume)
+{
+ audioVolume.set("volume", volume);
+}
+
+void QGstreamerAudioInput::setMuted(bool muted)
+{
+ audioVolume.set("mute", muted);
+}
+
+void QGstreamerAudioInput::setAudioDevice(const QAudioDevice &device)
+{
+ if (device == m_audioDevice)
+ return;
+ qCDebug(qLcMediaAudioInput) << "setAudioInput" << device.description() << device.isNull();
+ m_audioDevice = device;
+
+ QGstElement newSrc = createGstElement();
+
+ QGstPipeline::modifyPipelineWhileNotRunning(gstAudioInput.getPipeline(), [&] {
+ qUnlinkGstElements(audioSrc, audioVolume);
+ gstAudioInput.stopAndRemoveElements(audioSrc);
+ audioSrc = std::move(newSrc);
+ gstAudioInput.add(audioSrc);
+ qLinkGstElements(audioSrc, audioVolume);
+ audioSrc.syncStateWithParent();
+ });
+}
+
+QAudioDevice QGstreamerAudioInput::audioInput() const
+{
+ return m_audioDevice;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput_p.h b/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput_p.h
new file mode 100644
index 000000000..5ca0e1a49
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput_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 QGSTREAMERAUDIOINPUT_P_H
+#define QGSTREAMERAUDIOINPUT_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/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 <common/qgst_p.h>
+#include <common/qgstpipeline_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioDevice;
+
+class QGstreamerAudioInput : public QObject, public QPlatformAudioInput
+{
+public:
+ static QMaybe<QPlatformAudioInput *> create(QAudioInput *parent);
+ ~QGstreamerAudioInput();
+
+ bool setAudioInput(const QAudioDevice &);
+ QAudioDevice audioInput() const;
+
+ void setAudioDevice(const QAudioDevice &) override;
+ void setVolume(float) override;
+ void setMuted(bool) override;
+
+ QGstElement gstElement() const { return gstAudioInput; }
+
+private:
+ explicit QGstreamerAudioInput(QAudioInput *parent);
+
+ QGstElement createGstElement();
+
+ QAudioDevice m_audioDevice;
+
+ // Gst elements
+ QGstBin gstAudioInput;
+
+ QGstElement audioSrc;
+ QGstElement audioVolume;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp b/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp
new file mode 100644
index 000000000..9cea7fb62
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp
@@ -0,0 +1,138 @@
+// 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 <utility>
+
+static Q_LOGGING_CATEGORY(qLcMediaAudioOutput, "qt.multimedia.audiooutput")
+
+QT_BEGIN_NAMESPACE
+
+QMaybe<QPlatformAudioOutput *> QGstreamerAudioOutput::create(QAudioOutput *parent)
+{
+ static const auto error = qGstErrorMessageIfElementsNotAvailable(
+ "audioconvert", "audioresample", "volume", "autoaudiosink");
+ if (error)
+ return *error;
+
+ return new QGstreamerAudioOutput(parent);
+}
+
+QGstreamerAudioOutput::QGstreamerAudioOutput(QAudioOutput *parent)
+ : QObject(parent),
+ QPlatformAudioOutput(parent),
+ gstAudioOutput(QGstBin::create("audioOutput")),
+ audioQueue{
+ QGstElement::createFromFactory("queue", "audioQueue"),
+ },
+ audioConvert{
+ QGstElement::createFromFactory("audioconvert", "audioConvert"),
+ },
+ audioResample{
+ QGstElement::createFromFactory("audioresample", "audioResample"),
+ },
+ audioVolume{
+ QGstElement::createFromFactory("volume", "volume"),
+ },
+ audioSink{
+ QGstElement::createFromFactory("autoaudiosink", "autoAudioSink"),
+ }
+{
+ gstAudioOutput.add(audioQueue, audioConvert, audioResample, audioVolume, audioSink);
+ qLinkGstElements(audioQueue, audioConvert, audioResample, audioVolume, audioSink);
+
+ gstAudioOutput.addGhostPad(audioQueue, "sink");
+}
+
+QGstElement QGstreamerAudioOutput::createGstElement()
+{
+ const auto *customDeviceInfo =
+ dynamic_cast<const QGStreamerCustomAudioDeviceInfo *>(m_audioOutput.handle());
+
+ if (customDeviceInfo) {
+ qCDebug(qLcMediaAudioOutput)
+ << "requesting custom audio sink element: " << customDeviceInfo->id;
+
+ QGstElement element =
+ QGstBin::createFromPipelineDescription(customDeviceInfo->id, /*name=*/nullptr,
+ /*ghostUnlinkedPads=*/true);
+ if (element)
+ return element;
+
+ qCWarning(qLcMediaAudioOutput)
+ << "Cannot create audio sink element:" << customDeviceInfo->id;
+ }
+
+ const QByteArray &id = m_audioOutput.id();
+ if constexpr (QT_CONFIG(pulseaudio)) {
+ QGstElement newSink = QGstElement::createFromFactory("pulsesink", "audiosink");
+ if (newSink) {
+ newSink.set("device", id.constData());
+ return newSink;
+ } else {
+ qWarning() << "Cannot create pulsesink";
+ }
+ } else if constexpr (QT_CONFIG(alsa)) {
+ QGstElement newSink = QGstElement::createFromFactory("alsasink", "audiosink");
+ if (newSink) {
+ newSink.set("device", id.constData());
+ return newSink;
+ } else {
+ qWarning() << "Cannot create alsasink";
+ }
+ } else {
+ auto *deviceInfo = dynamic_cast<const QGStreamerAudioDeviceInfo *>(m_audioOutput.handle());
+ if (deviceInfo && deviceInfo->gstDevice) {
+ QGstElement element = QGstElement::createFromDevice(deviceInfo->gstDevice, "audiosink");
+ if (element)
+ return element;
+ }
+ }
+ qCWarning(qLcMediaAudioOutput) << "Invalid audio device:" << m_audioOutput.id();
+ qCWarning(qLcMediaAudioOutput)
+ << "Failed to create a gst element for the audio device, using a default audio sink";
+ return QGstElement::createFromFactory("autoaudiosink", "audiosink");
+}
+
+QGstreamerAudioOutput::~QGstreamerAudioOutput()
+{
+ gstAudioOutput.setStateSync(GST_STATE_NULL);
+}
+
+void QGstreamerAudioOutput::setVolume(float volume)
+{
+ audioVolume.set("volume", volume);
+}
+
+void QGstreamerAudioOutput::setMuted(bool muted)
+{
+ audioVolume.set("mute", muted);
+}
+
+void QGstreamerAudioOutput::setAudioDevice(const QAudioDevice &info)
+{
+ if (info == m_audioOutput)
+ return;
+ qCDebug(qLcMediaAudioOutput) << "setAudioOutput" << info.description() << info.isNull();
+
+ m_audioOutput = info;
+
+ QGstElement newSink = createGstElement();
+
+ QGstPipeline::modifyPipelineWhileNotRunning(gstAudioOutput.getPipeline(), [&] {
+ qUnlinkGstElements(audioVolume, audioSink);
+ gstAudioOutput.stopAndRemoveElements(audioSink);
+ audioSink = std::move(newSink);
+ gstAudioOutput.add(audioSink);
+ audioSink.syncStateWithParent();
+ qLinkGstElements(audioVolume, audioSink);
+ });
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput_p.h b/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput_p.h
new file mode 100644
index 000000000..dea53e5c4
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput_p.h
@@ -0,0 +1,63 @@
+// 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
+
+//
+// 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 <QtMultimedia/private/qmultimediautils_p.h>
+#include <QtMultimedia/private/qplatformaudiooutput_p.h>
+#include <QtMultimedia/private/qtmultimediaglobal_p.h>
+#include <QtMultimedia/qaudiodevice.h>
+
+#include <common/qgst_p.h>
+#include <common/qgstpipeline_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioDevice;
+
+class QGstreamerAudioOutput : public QObject, public QPlatformAudioOutput
+{
+public:
+ static QMaybe<QPlatformAudioOutput *> create(QAudioOutput *parent);
+ ~QGstreamerAudioOutput();
+
+ void setAudioDevice(const QAudioDevice &) override;
+ void setVolume(float) override;
+ void setMuted(bool) override;
+
+ QGstElement gstElement() const { return gstAudioOutput; }
+
+private:
+ explicit QGstreamerAudioOutput(QAudioOutput *parent);
+
+ QGstElement createGstElement();
+
+ QAudioDevice m_audioOutput;
+
+ // Gst elements
+ QGstPipeline gstPipeline;
+ QGstBin gstAudioOutput;
+
+ QGstElement audioQueue;
+ QGstElement audioConvert;
+ QGstElement audioResample;
+ QGstElement audioVolume;
+ QGstElement audioSink;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamerbufferprobe.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamerbufferprobe.cpp
new file mode 100644
index 000000000..9cba810db
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamerbufferprobe.cpp
@@ -0,0 +1,88 @@
+// 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
+
+QGstreamerBufferProbe::QGstreamerBufferProbe(Flags flags)
+ : m_flags(flags)
+{
+}
+
+QGstreamerBufferProbe::~QGstreamerBufferProbe() = default;
+
+void QGstreamerBufferProbe::addProbeToPad(GstPad *pad, bool downstream)
+{
+ 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,
+ downstream
+ ? GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM
+ : GST_PAD_PROBE_TYPE_EVENT_UPSTREAM,
+ capsProbe,
+ this,
+ nullptr);
+ }
+ if (m_flags & ProbeBuffers) {
+ m_bufferProbeId = gst_pad_add_probe(
+ pad, GST_PAD_PROBE_TYPE_BUFFER, bufferProbe, this, nullptr);
+ }
+}
+
+void QGstreamerBufferProbe::removeProbeFromPad(GstPad *pad)
+{
+ if (m_capsProbeId != -1) {
+ gst_pad_remove_probe(pad, m_capsProbeId);
+ m_capsProbeId = -1;
+ }
+ if (m_bufferProbeId != -1) {
+ gst_pad_remove_probe(pad, m_bufferProbeId);
+ m_bufferProbeId = -1;
+ }
+}
+
+void QGstreamerBufferProbe::probeCaps(GstCaps *)
+{
+}
+
+bool QGstreamerBufferProbe::probeBuffer(GstBuffer *)
+{
+ return true;
+}
+
+GstPadProbeReturn QGstreamerBufferProbe::capsProbe(GstPad *, GstPadProbeInfo *info, gpointer user_data)
+{
+ QGstreamerBufferProbe * const control = static_cast<QGstreamerBufferProbe *>(user_data);
+
+ if (GstEvent * const event = gst_pad_probe_info_get_event(info)) {
+ if (GST_EVENT_TYPE(event) == GST_EVENT_CAPS) {
+ GstCaps *caps;
+ gst_event_parse_caps(event, &caps);
+
+ control->probeCaps(caps);
+ }
+ }
+ return GST_PAD_PROBE_OK;
+}
+
+GstPadProbeReturn QGstreamerBufferProbe::bufferProbe(
+ GstPad *, GstPadProbeInfo *info, gpointer user_data)
+{
+ QGstreamerBufferProbe * const control = static_cast<QGstreamerBufferProbe *>(user_data);
+ if (GstBuffer * const buffer = gst_pad_probe_info_get_buffer(info))
+ return control->probeBuffer(buffer) ? GST_PAD_PROBE_OK : GST_PAD_PROBE_DROP;
+ return GST_PAD_PROBE_OK;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamerbufferprobe_p.h b/src/plugins/multimedia/gstreamer/common/qgstreamerbufferprobe_p.h
new file mode 100644
index 000000000..71996a0cc
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamerbufferprobe_p.h
@@ -0,0 +1,56 @@
+// 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
+
+//
+// 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 <gst/gst.h>
+
+#include <QtCore/qglobal.h>
+
+
+QT_BEGIN_NAMESPACE
+
+class QGstreamerBufferProbe
+{
+public:
+ enum Flags
+ {
+ ProbeCaps = 0x01,
+ ProbeBuffers = 0x02,
+ ProbeAll = ProbeCaps | ProbeBuffers
+ };
+
+ explicit QGstreamerBufferProbe(Flags flags = ProbeAll);
+ virtual ~QGstreamerBufferProbe();
+
+ void addProbeToPad(GstPad *pad, bool downstream = true);
+ void removeProbeFromPad(GstPad *pad);
+
+protected:
+ virtual void probeCaps(GstCaps *caps);
+ virtual bool probeBuffer(GstBuffer *buffer);
+
+private:
+ static GstPadProbeReturn capsProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data);
+ static GstPadProbeReturn bufferProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data);
+ int m_capsProbeId = -1;
+ int m_bufferProbeId = -1;
+ const Flags m_flags;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGSTREAMERBUFFERPROBE_H
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp
new file mode 100644
index 000000000..ce5efb648
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp
@@ -0,0 +1,1114 @@
+// 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 <QtMultimedia/qaudiodevice.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qsocketnotifier.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/private/quniquehandle_p.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#if QT_CONFIG(gstreamer_gl)
+# include <gst/gl/gl.h>
+#endif
+
+static Q_LOGGING_CATEGORY(qLcMediaPlayer, "qt.multimedia.player")
+
+QT_BEGIN_NAMESPACE
+
+QGstreamerMediaPlayer::TrackSelector::TrackSelector(TrackType type, QGstElement selector)
+ : selector(selector), type(type)
+{
+ selector.set("sync-streams", true);
+ selector.set("sync-mode", 1 /*clock*/);
+
+ if (type == SubtitleStream)
+ selector.set("cache-buffers", true);
+}
+
+QGstPad QGstreamerMediaPlayer::TrackSelector::createInputPad()
+{
+ auto pad = selector.getRequestPad("sink_%u");
+ tracks.append(pad);
+ return pad;
+}
+
+void QGstreamerMediaPlayer::TrackSelector::removeAllInputPads()
+{
+ for (auto &pad : tracks)
+ selector.releaseRequestPad(pad);
+ tracks.clear();
+}
+
+void QGstreamerMediaPlayer::TrackSelector::removeInputPad(QGstPad pad)
+{
+ selector.releaseRequestPad(pad);
+ tracks.removeOne(pad);
+}
+
+QGstPad QGstreamerMediaPlayer::TrackSelector::inputPad(int index)
+{
+ if (index >= 0 && index < tracks.count())
+ return tracks[index];
+ return {};
+}
+
+QGstreamerMediaPlayer::TrackSelector &QGstreamerMediaPlayer::trackSelector(TrackType type)
+{
+ auto &ts = trackSelectors[type];
+ Q_ASSERT(ts.type == type);
+ return ts;
+}
+
+void QGstreamerMediaPlayer::updateBufferProgress(float newProgress)
+{
+ if (qFuzzyIsNull(newProgress - m_bufferProgress))
+ return;
+
+ m_bufferProgress = newProgress;
+ bufferProgressChanged(m_bufferProgress);
+}
+
+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();
+
+ static const auto error =
+ qGstErrorMessageIfElementsNotAvailable("input-selector", "decodebin", "uridecodebin");
+ if (error)
+ return *error;
+
+ return new QGstreamerMediaPlayer(videoOutput.value(), parent);
+}
+
+QGstreamerMediaPlayer::QGstreamerMediaPlayer(QGstreamerVideoOutput *videoOutput,
+ QMediaPlayer *parent)
+ : QObject(parent),
+ QPlatformMediaPlayer(parent),
+ trackSelectors{ {
+ { VideoStream,
+ QGstElement::createFromFactory("input-selector", "videoInputSelector") },
+ { AudioStream,
+ QGstElement::createFromFactory("input-selector", "audioInputSelector") },
+ { SubtitleStream,
+ QGstElement::createFromFactory("input-selector", "subTitleInputSelector") },
+ } },
+ playerPipeline(QGstPipeline::create("playerPipeline")),
+ gstVideoOutput(videoOutput)
+{
+ playerPipeline.setFlushOnConfigChanges(true);
+
+ gstVideoOutput->setParent(this);
+ gstVideoOutput->setPipeline(playerPipeline);
+
+ for (auto &ts : trackSelectors)
+ playerPipeline.add(ts.selector);
+
+ playerPipeline.installMessageFilter(static_cast<QGstreamerBusMessageFilter *>(this));
+ playerPipeline.installMessageFilter(static_cast<QGstreamerSyncMessageFilter *>(this));
+
+ QGstClockHandle systemClock{
+ gst_system_clock_obtain(),
+ };
+
+ gst_pipeline_use_clock(playerPipeline.pipeline(), systemClock.get());
+
+ connect(&positionUpdateTimer, &QTimer::timeout, this, [this] {
+ updatePositionFromPipeline();
+ });
+}
+
+QGstreamerMediaPlayer::~QGstreamerMediaPlayer()
+{
+ playerPipeline.removeMessageFilter(static_cast<QGstreamerBusMessageFilter *>(this));
+ playerPipeline.removeMessageFilter(static_cast<QGstreamerSyncMessageFilter *>(this));
+ playerPipeline.setStateSync(GST_STATE_NULL);
+}
+
+std::chrono::nanoseconds QGstreamerMediaPlayer::pipelinePosition() const
+{
+ if (m_url.isEmpty())
+ return {};
+
+ Q_ASSERT(playerPipeline);
+ return playerPipeline.position();
+}
+
+void QGstreamerMediaPlayer::updatePositionFromPipeline()
+{
+ using namespace std::chrono;
+
+ positionChanged(round<milliseconds>(pipelinePosition()));
+}
+
+void QGstreamerMediaPlayer::updateDurationFromPipeline()
+{
+ std::optional<std::chrono::milliseconds> duration = playerPipeline.durationInMs();
+ if (!duration)
+ duration = std::chrono::milliseconds{ -1 };
+
+ if (duration != m_duration) {
+ qCDebug(qLcMediaPlayer) << "updateDurationFromPipeline" << *duration;
+ m_duration = *duration;
+ durationChanged(m_duration);
+ }
+}
+
+qint64 QGstreamerMediaPlayer::duration() const
+{
+ return m_duration.count();
+}
+
+float QGstreamerMediaPlayer::bufferProgress() const
+{
+ return m_bufferProgress;
+}
+
+QMediaTimeRange QGstreamerMediaPlayer::availablePlaybackRanges() const
+{
+ return QMediaTimeRange();
+}
+
+qreal QGstreamerMediaPlayer::playbackRate() const
+{
+ return playerPipeline.playbackRate();
+}
+
+void QGstreamerMediaPlayer::setPlaybackRate(qreal rate)
+{
+ if (rate == m_rate)
+ return;
+
+ m_rate = rate;
+
+ playerPipeline.setPlaybackRate(rate);
+ playbackRateChanged(rate);
+}
+
+void QGstreamerMediaPlayer::setPosition(qint64 pos)
+{
+ std::chrono::milliseconds posInMs{ pos };
+ setPosition(posInMs);
+}
+
+void QGstreamerMediaPlayer::setPosition(std::chrono::milliseconds pos)
+{
+ if (pos == playerPipeline.position())
+ return;
+ playerPipeline.finishStateChange();
+ playerPipeline.setPosition(pos);
+ qCDebug(qLcMediaPlayer) << Q_FUNC_INFO << pos << playerPipeline.positionInMs();
+ if (mediaStatus() == QMediaPlayer::EndOfMedia)
+ mediaStatusChanged(QMediaPlayer::LoadedMedia);
+ positionChanged(pos);
+}
+
+void QGstreamerMediaPlayer::play()
+{
+ if (state() == QMediaPlayer::PlayingState || m_url.isEmpty())
+ return;
+
+ if (state() != QMediaPlayer::PausedState)
+ resetCurrentLoop();
+
+ playerPipeline.setInStoppedState(false);
+ if (mediaStatus() == QMediaPlayer::EndOfMedia) {
+ playerPipeline.setPosition({});
+ positionChanged(0);
+ }
+
+ qCDebug(qLcMediaPlayer) << "play().";
+ int ret = playerPipeline.setState(GST_STATE_PLAYING);
+ if (m_requiresSeekOnPlay) {
+ // Flushing the pipeline is required to get track changes
+ // immediately, when they happen while paused.
+ playerPipeline.flush();
+ m_requiresSeekOnPlay = false;
+ } else {
+ // we get an assertion failure during instant playback rate changes
+ // https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3545
+ constexpr bool performInstantRateChange = false;
+ playerPipeline.applyPlaybackRate(/*instantRateChange=*/performInstantRateChange);
+ }
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ qCDebug(qLcMediaPlayer) << "Unable to set the pipeline to the playing state.";
+
+ positionUpdateTimer.start(100);
+ stateChanged(QMediaPlayer::PlayingState);
+}
+
+void QGstreamerMediaPlayer::pause()
+{
+ if (state() == QMediaPlayer::PausedState || m_url.isEmpty()
+ || m_resourceErrorState != ResourceErrorState::NoError)
+ return;
+
+ positionUpdateTimer.stop();
+ if (playerPipeline.inStoppedState()) {
+ playerPipeline.setInStoppedState(false);
+ playerPipeline.flush();
+ }
+ int ret = playerPipeline.setStateSync(GST_STATE_PAUSED);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ qCDebug(qLcMediaPlayer) << "Unable to set the pipeline to the paused state.";
+ if (mediaStatus() == QMediaPlayer::EndOfMedia) {
+ playerPipeline.setPosition({});
+ positionChanged(0);
+ } else {
+ updatePositionFromPipeline();
+ }
+ stateChanged(QMediaPlayer::PausedState);
+
+ if (m_bufferProgress > 0 || !canTrackProgress())
+ mediaStatusChanged(QMediaPlayer::BufferedMedia);
+ else
+ mediaStatusChanged(QMediaPlayer::BufferingMedia);
+}
+
+void QGstreamerMediaPlayer::stop()
+{
+ using namespace std::chrono_literals;
+ if (state() == QMediaPlayer::StoppedState) {
+ if (position() != 0) {
+ playerPipeline.setPosition({});
+ positionChanged(0ms);
+ mediaStatusChanged(QMediaPlayer::LoadedMedia);
+ }
+ return;
+ }
+ stopOrEOS(false);
+}
+
+const QGstPipeline &QGstreamerMediaPlayer::pipeline() const
+{
+ return playerPipeline;
+}
+
+void QGstreamerMediaPlayer::stopOrEOS(bool eos)
+{
+ using namespace std::chrono_literals;
+
+ positionUpdateTimer.stop();
+ playerPipeline.setInStoppedState(true);
+ bool ret = playerPipeline.setStateSync(GST_STATE_PAUSED);
+ if (!ret)
+ qCDebug(qLcMediaPlayer) << "Unable to set the pipeline to the stopped state.";
+ if (!eos) {
+ playerPipeline.setPosition(0ms);
+ positionChanged(0ms);
+ }
+ stateChanged(QMediaPlayer::StoppedState);
+ if (eos)
+ mediaStatusChanged(QMediaPlayer::EndOfMedia);
+ else
+ mediaStatusChanged(QMediaPlayer::LoadedMedia);
+ m_initialBufferProgressSent = false;
+ bufferProgressChanged(0.f);
+}
+
+void QGstreamerMediaPlayer::detectPipelineIsSeekable()
+{
+ 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);
+}
+
+bool QGstreamerMediaPlayer::processBusMessage(const QGstreamerMessage &message)
+{
+ qCDebug(qLcMediaPlayer) << "received bus message:" << message;
+
+ 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
+ QGstTagListHandle tagList;
+ gst_message_parse_tag(gm, &tagList);
+
+ qCDebug(qLcMediaPlayer) << " Got tags: " << tagList.get();
+
+ QMediaMetaData originalMetaData = m_metaData;
+ extendMetaDataFromTagList(m_metaData, tagList);
+ if (originalMetaData != m_metaData)
+ metaDataChanged();
+
+ if (gstVideoOutput) {
+ QVariant rotation = m_metaData.value(QMediaMetaData::Orientation);
+ gstVideoOutput->setRotation(rotation.value<QtVideo::Rotation>());
+ }
+ break;
+ }
+ case GST_MESSAGE_DURATION_CHANGED: {
+ if (!prerolling)
+ updateDurationFromPipeline();
+
+ return false;
+ }
+ case GST_MESSAGE_EOS: {
+ positionChanged(m_duration);
+ if (doLoop()) {
+ setPosition(0);
+ break;
+ }
+ stopOrEOS(true);
+ break;
+ }
+ case GST_MESSAGE_BUFFERING: {
+ 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);
+ }
+
+ updateBufferProgress(progress * 0.01);
+ break;
+ }
+ case GST_MESSAGE_STATE_CHANGED: {
+ if (message.source() != playerPipeline)
+ return false;
+
+ GstState oldState;
+ GstState newState;
+ GstState pending;
+
+ gst_message_parse_state_changed(gm, &oldState, &newState, &pending);
+ 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: {
+ if (prerolling) {
+ qCDebug(qLcMediaPlayer) << "Preroll done, setting status to Loaded";
+ playerPipeline.dumpGraph("playerPipelinePrerollDone");
+
+ prerolling = false;
+ updateDurationFromPipeline();
+
+ m_metaData.insert(QMediaMetaData::Duration, duration());
+ m_metaData.insert(QMediaMetaData::Url, m_url);
+ parseStreamsAndMetadata();
+ metaDataChanged();
+
+ tracksChanged();
+ mediaStatusChanged(QMediaPlayer::LoadedMedia);
+
+ if (!playerPipeline.inStoppedState()) {
+ Q_ASSERT(!m_initialBufferProgressSent);
+
+ bool immediatelySendBuffered = !canTrackProgress() || m_bufferProgress > 0;
+ mediaStatusChanged(QMediaPlayer::BufferingMedia);
+ m_initialBufferProgressSent = true;
+ if (immediatelySendBuffered)
+ mediaStatusChanged(QMediaPlayer::BufferedMedia);
+ }
+ }
+
+ break;
+ }
+ 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: {
+ qCDebug(qLcMediaPlayer) << " error" << QCompactGstMessageAdaptor(message);
+
+ QUniqueGErrorHandle err;
+ QUniqueGStringHandle debug;
+ gst_message_parse_error(gm, &err, &debug);
+ 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);
+ break;
+ }
+
+ case GST_MESSAGE_WARNING:
+ qCWarning(qLcMediaPlayer) << "Warning:" << QCompactGstMessageAdaptor(message);
+ playerPipeline.dumpGraph("warning");
+ break;
+
+ 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";
+ QGstStructureView structure(gst_message_get_structure(gm));
+ auto p = structure["position"].toInt64();
+ if (p) {
+ std::chrono::milliseconds position{
+ (*p) / 1000000,
+ };
+ positionChanged(position);
+ }
+ break;
+ }
+ case GST_MESSAGE_ELEMENT: {
+ QGstStructureView structure(gst_message_get_structure(gm));
+ auto type = structure.name();
+ if (type == "stream-topology")
+ topology = structure.clone();
+
+ break;
+ }
+
+ case GST_MESSAGE_ASYNC_DONE: {
+ detectPipelineIsSeekable();
+ break;
+ }
+
+ default:
+// qCDebug(qLcMediaPlayer) << " default message handler, doing nothing";
+
+ break;
+ }
+
+ return false;
+}
+
+bool QGstreamerMediaPlayer::processSyncMessage(const QGstreamerMessage &message)
+{
+#if QT_CONFIG(gstreamer_gl)
+ if (message.type() != GST_MESSAGE_NEED_CONTEXT)
+ return false;
+ const gchar *type = nullptr;
+ gst_message_parse_context_type (message.message(), &type);
+ if (strcmp(type, GST_GL_DISPLAY_CONTEXT_TYPE))
+ return false;
+ if (!gstVideoOutput || !gstVideoOutput->gstreamerVideoSink())
+ return false;
+ auto *context = gstVideoOutput->gstreamerVideoSink()->gstGlDisplayContext();
+ if (!context)
+ return false;
+ gst_element_set_context(GST_ELEMENT(GST_MESSAGE_SRC(message.message())), context);
+ playerPipeline.dumpGraph("need_context");
+ return true;
+#else
+ Q_UNUSED(message);
+ return false;
+#endif
+}
+
+QUrl QGstreamerMediaPlayer::media() const
+{
+ return m_url;
+}
+
+const QIODevice *QGstreamerMediaPlayer::mediaStream() const
+{
+ return m_stream;
+}
+
+void QGstreamerMediaPlayer::decoderPadAdded(const QGstElement &src, const QGstPad &pad)
+{
+ if (src != decoder)
+ return;
+
+ 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;
+
+ TrackType streamType = NTrackTypes;
+ if (type.startsWith("video/x-raw")) {
+ streamType = VideoStream;
+ } else if (type.startsWith("audio/x-raw")) {
+ streamType = AudioStream;
+ } else if (type.startsWith("text/")) {
+ streamType = SubtitleStream;
+ } else {
+ qCWarning(qLcMediaPlayer) << "Ignoring unknown media stream:" << pad.name() << type;
+ return;
+ }
+
+ auto &ts = trackSelector(streamType);
+ QGstPad sinkPad = ts.createInputPad();
+ if (!pad.link(sinkPad)) {
+ qCWarning(qLcMediaPlayer) << "Failed to add track, cannot link pads";
+ return;
+ }
+ qCDebug(qLcMediaPlayer) << "Adding track";
+
+ if (ts.trackCount() == 1) {
+ if (streamType == VideoStream) {
+ connectOutput(ts);
+ ts.setActiveInputPad(sinkPad);
+ videoAvailableChanged(true);
+ }
+ else if (streamType == AudioStream) {
+ connectOutput(ts);
+ ts.setActiveInputPad(sinkPad);
+ audioAvailableChanged(true);
+ }
+ }
+
+ if (!prerolling)
+ tracksChanged();
+
+ decoderOutputMap.emplace(pad, sinkPad);
+}
+
+void QGstreamerMediaPlayer::decoderPadRemoved(const QGstElement &src, const QGstPad &pad)
+{
+ if (src != decoder)
+ return;
+
+ qCDebug(qLcMediaPlayer) << "Removed pad" << pad.name() << "from" << src.name();
+
+ auto it = decoderOutputMap.find(pad);
+ if (it == decoderOutputMap.end())
+ return;
+ QGstPad track = it->second;
+
+ auto ts = std::find_if(std::begin(trackSelectors), std::end(trackSelectors),
+ [&](TrackSelector &ts){ return ts.selector == track.parent(); });
+ if (ts == std::end(trackSelectors))
+ return;
+
+ qCDebug(qLcMediaPlayer) << " was linked to pad" << track.name() << "from" << ts->selector.name();
+ ts->removeInputPad(track);
+
+ if (ts->trackCount() == 0) {
+ removeOutput(*ts);
+ if (ts->type == AudioStream)
+ audioAvailableChanged(false);
+ else if (ts->type == VideoStream)
+ videoAvailableChanged(false);
+ }
+
+ if (!prerolling)
+ tracksChanged();
+}
+
+void QGstreamerMediaPlayer::removeAllOutputs()
+{
+ for (auto &ts : trackSelectors) {
+ removeOutput(ts);
+ ts.removeAllInputPads();
+ }
+ audioAvailableChanged(false);
+ videoAvailableChanged(false);
+}
+
+void QGstreamerMediaPlayer::connectOutput(TrackSelector &ts)
+{
+ if (ts.isConnected)
+ return;
+
+ QGstElement e;
+ switch (ts.type) {
+ case AudioStream:
+ e = gstAudioOutput ? gstAudioOutput->gstElement() : QGstElement{};
+ break;
+ case VideoStream:
+ e = gstVideoOutput ? gstVideoOutput->gstElement() : QGstElement{};
+ break;
+ case SubtitleStream:
+ if (gstVideoOutput)
+ gstVideoOutput->linkSubtitleStream(ts.selector);
+ break;
+ default:
+ return;
+ }
+
+ if (!e.isNull()) {
+ qCDebug(qLcMediaPlayer) << "connecting output for track type" << ts.type;
+ playerPipeline.add(e);
+ qLinkGstElements(ts.selector, e);
+ e.syncStateWithParent();
+ }
+
+ ts.isConnected = true;
+}
+
+void QGstreamerMediaPlayer::removeOutput(TrackSelector &ts)
+{
+ if (!ts.isConnected)
+ return;
+
+ QGstElement e;
+ switch (ts.type) {
+ case AudioStream:
+ e = gstAudioOutput ? gstAudioOutput->gstElement() : QGstElement{};
+ break;
+ case VideoStream:
+ e = gstVideoOutput ? gstVideoOutput->gstElement() : QGstElement{};
+ break;
+ case SubtitleStream:
+ if (gstVideoOutput)
+ gstVideoOutput->unlinkSubtitleStream();
+ break;
+ default:
+ break;
+ }
+
+ if (!e.isNull()) {
+ qCDebug(qLcMediaPlayer) << "removing output for track type" << ts.type;
+ playerPipeline.stopAndRemoveElements(e);
+ }
+
+ ts.isConnected = false;
+}
+
+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::NeedsRef);
+ qCDebug(qLcMediaPlayer) << "New element added to uridecodebin:" << c.name();
+
+ static const GType decodeBinType = [] {
+ QGstElementFactoryHandle factory = QGstElement::findFactory("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 = QGstElement::findFactory("queue");
+ return gst_element_factory_get_element_type(factory.get());
+ }();
+
+ static const GType multiQueueType = [] {
+ QGstElementFactoryHandle factory = QGstElement::findFactory("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)
+{
+ using namespace std::chrono_literals;
+
+ qCDebug(qLcMediaPlayer) << Q_FUNC_INFO << "setting location to" << content;
+
+ prerolling = true;
+ m_resourceErrorState = ResourceErrorState::NoError;
+
+ bool ret = playerPipeline.setStateSync(GST_STATE_NULL);
+ if (!ret)
+ qCDebug(qLcMediaPlayer) << "Unable to set the pipeline to the stopped state.";
+
+ m_url = content;
+ m_stream = stream;
+
+ removeDynamicPipelineElements();
+ disconnectDecoderHandlers();
+ removeAllOutputs();
+ seekableChanged(false);
+ Q_ASSERT(playerPipeline.inStoppedState());
+
+ if (m_duration != 0ms) {
+ m_duration = 0ms;
+ durationChanged(0ms);
+ }
+ stateChanged(QMediaPlayer::StoppedState);
+ if (position() != 0)
+ positionChanged(0ms);
+ 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) {
+ auto maybeAppSrc = QGstAppSource::create(this);
+ if (maybeAppSrc) {
+ m_appSrc = maybeAppSrc.value();
+ } else {
+ error(QMediaPlayer::ResourceError, maybeAppSrc.error());
+ return;
+ }
+ }
+ src = m_appSrc->element();
+ decoder = QGstElement::createFromFactory("decodebin", "decoder");
+ if (!decoder) {
+ error(QMediaPlayer::ResourceError, qGstErrorMessageCannotFindElement("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);
+ qLinkGstElements(src, decoder);
+
+ m_appSrc->setup(m_stream);
+ seekableChanged(!stream->isSequential());
+ } else {
+ // use uridecodebin
+ decoder = QGstElement::createFromFactory("uridecodebin", "decoder");
+ if (!decoder) {
+ error(QMediaPlayer::ResourceError, qGstErrorMessageCannotFindElement("uridecodebin"));
+ return;
+ }
+ playerPipeline.add(decoder);
+
+ 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);
+
+ updateBufferProgress(0.f);
+
+ elementAdded = decoder.connect("deep-element-added",
+ GCallback(decodebinElementAddedCallback), this);
+ elementRemoved = decoder.connect("deep-element-removed",
+ GCallback(decodebinElementAddedCallback), this);
+ }
+ padAdded = decoder.onPadAdded<&QGstreamerMediaPlayer::decoderPadAdded>(this);
+ padRemoved = decoder.onPadRemoved<&QGstreamerMediaPlayer::decoderPadRemoved>(this);
+
+ mediaStatusChanged(QMediaPlayer::LoadingMedia);
+ if (!playerPipeline.setStateSync(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(0ms);
+ positionChanged(0ms);
+}
+
+void QGstreamerMediaPlayer::setAudioOutput(QPlatformAudioOutput *output)
+{
+ if (gstAudioOutput == output)
+ return;
+
+ auto &ts = trackSelector(AudioStream);
+
+ playerPipeline.modifyPipelineWhileNotRunning([&] {
+ if (gstAudioOutput)
+ removeOutput(ts);
+
+ gstAudioOutput = static_cast<QGstreamerAudioOutput *>(output);
+ if (gstAudioOutput)
+ connectOutput(ts);
+ });
+}
+
+QMediaMetaData QGstreamerMediaPlayer::metaData() const
+{
+ return m_metaData;
+}
+
+void QGstreamerMediaPlayer::setVideoSink(QVideoSink *sink)
+{
+ gstVideoOutput->setVideoSink(sink);
+}
+
+static QGstStructureView endOfChain(const QGstStructureView &s)
+{
+ QGstStructureView e = s;
+ while (1) {
+ auto next = e["next"].toStructure();
+ if (!next.isNull())
+ e = next;
+ else
+ break;
+ }
+ return e;
+}
+
+void QGstreamerMediaPlayer::parseStreamsAndMetadata()
+{
+ qCDebug(qLcMediaPlayer) << "============== parse topology ============";
+
+ if (!topology) {
+ qCDebug(qLcMediaPlayer) << " null topology";
+ return;
+ }
+
+ QGstStructureView topologyView{ topology };
+
+ QGstCaps caps = topologyView.caps();
+ extendMetaDataFromCaps(m_metaData, caps);
+
+ QGstTagListHandle tagList = QGstStructureView{ topology }.tags();
+ if (tagList)
+ extendMetaDataFromTagList(m_metaData, tagList);
+
+ QGstStructureView demux = endOfChain(topologyView);
+ QGValue next = demux["next"];
+ if (!next.isList()) {
+ qCDebug(qLcMediaPlayer) << " no additional streams";
+ metaDataChanged();
+ return;
+ }
+
+ // collect stream info
+ int size = next.listSize();
+ for (int i = 0; i < size; ++i) {
+ auto val = next.at(i);
+ caps = val.toStructure().caps();
+
+ extendMetaDataFromCaps(m_metaData, caps);
+
+ QGstStructureView structure = caps.at(0);
+
+ if (structure.name().startsWith("video/")) {
+ QSize nativeSize = structure.nativeSize();
+ gstVideoOutput->setNativeSize(nativeSize);
+ }
+ }
+
+ auto sinkPad = trackSelector(VideoStream).activeInputPad();
+ if (sinkPad) {
+ QGstTagListHandle tagList = sinkPad.tags();
+ if (tagList)
+ qCDebug(qLcMediaPlayer) << " tags=" << tagList.get();
+ else
+ qCDebug(qLcMediaPlayer) << " tags=(null)";
+ }
+
+ qCDebug(qLcMediaPlayer) << "============== end parse topology ============";
+ playerPipeline.dumpGraph("playback");
+}
+
+int QGstreamerMediaPlayer::trackCount(QPlatformMediaPlayer::TrackType type)
+{
+ return trackSelector(type).trackCount();
+}
+
+QMediaMetaData QGstreamerMediaPlayer::trackMetaData(QPlatformMediaPlayer::TrackType type, int index)
+{
+ auto track = trackSelector(type).inputPad(index);
+ if (!track)
+ return {};
+
+ QGstTagListHandle tagList = track.tags();
+ return taglistToMetaData(tagList);
+}
+
+int QGstreamerMediaPlayer::activeTrack(TrackType type)
+{
+ return trackSelector(type).activeInputIndex();
+}
+
+void QGstreamerMediaPlayer::setActiveTrack(TrackType type, int index)
+{
+ auto &ts = trackSelector(type);
+ auto track = ts.inputPad(index);
+ if (track.isNull() && index != -1) {
+ qCWarning(qLcMediaPlayer) << "Attempt to set an incorrect index" << index
+ << "for the track type" << type;
+ return;
+ }
+
+ qCDebug(qLcMediaPlayer) << "Setting the index" << index << "for the track type" << type;
+ if (type == QPlatformMediaPlayer::SubtitleStream)
+ gstVideoOutput->flushSubtitles();
+
+ 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)
+ playerPipeline.flush();
+ else
+ m_requiresSeekOnPlay = true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer_p.h b/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer_p.h
new file mode 100644
index 000000000..28e7a0c31
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer_p.h
@@ -0,0 +1,199 @@
+// 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
+
+//
+// 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/qstack.h>
+#include <private/qplatformmediaplayer_p.h>
+#include <private/qtmultimediaglobal_p.h>
+#include <private/qmultimediautils_p.h>
+#include <qurl.h>
+#include <common/qgst_p.h>
+#include <common/qgstpipeline_p.h>
+
+#include <QtCore/qtimer.h>
+
+#include <array>
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkAccessManager;
+class QGstreamerMessage;
+class QGstAppSource;
+class QGstreamerAudioOutput;
+class QGstreamerVideoOutput;
+
+class QGstreamerMediaPlayer : public QObject,
+ public QPlatformMediaPlayer,
+ public QGstreamerBusMessageFilter,
+ public QGstreamerSyncMessageFilter
+{
+public:
+ static QMaybe<QPlatformMediaPlayer *> create(QMediaPlayer *parent = nullptr);
+ ~QGstreamerMediaPlayer();
+
+ qint64 duration() const override;
+
+ float bufferProgress() 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 &, QIODevice *) override;
+
+ bool streamPlaybackSupported() const override { return true; }
+
+ void setAudioOutput(QPlatformAudioOutput *output) override;
+
+ QMediaMetaData metaData() const override;
+
+ void setVideoSink(QVideoSink *sink) override;
+
+ int trackCount(TrackType) override;
+ QMediaMetaData trackMetaData(TrackType /*type*/, int /*streamNumber*/) override;
+ int activeTrack(TrackType) override;
+ void setActiveTrack(TrackType, int /*streamNumber*/) override;
+
+ void setPosition(qint64 pos) override;
+ void setPosition(std::chrono::milliseconds pos);
+
+ void play() override;
+ void pause() override;
+ void stop() override;
+
+ const QGstPipeline &pipeline() const;
+
+ bool processBusMessage(const QGstreamerMessage& message) override;
+ bool processSyncMessage(const QGstreamerMessage& message) override;
+
+private:
+ QGstreamerMediaPlayer(QGstreamerVideoOutput *videoOutput, 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 ? QGstPad{ selector.getObject("active-pad") } : QGstPad{};
+ }
+ void setActiveInputPad(QGstPad input) { selector.set("active-pad", input); }
+ int trackCount() const { return tracks.count(); }
+
+ QGstElement selector;
+ TrackType type;
+ QList<QGstPad> tracks;
+ bool isConnected = false;
+ };
+
+ friend class QGstreamerStreamsControl;
+ void decoderPadAdded(const QGstElement &src, const QGstPad &pad);
+ void decoderPadRemoved(const QGstElement &src, const QGstPad &pad);
+ 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::chrono::nanoseconds pipelinePosition() const;
+ void updatePositionFromPipeline();
+ void updateDurationFromPipeline();
+ void updateBufferProgress(float);
+
+ std::array<TrackSelector, NTrackTypes> trackSelectors;
+ TrackSelector &trackSelector(TrackType type);
+
+ QMediaMetaData m_metaData;
+
+ 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;
+ float m_rate = 1.f;
+ float m_bufferProgress = 0.f;
+ std::chrono::milliseconds m_duration{};
+ QTimer positionUpdateTimer;
+
+ QGstAppSource *m_appSrc = nullptr;
+
+ QUniqueGstStructureHandle topology;
+
+ // Gst elements
+ QGstPipeline playerPipeline;
+ QGstElement src;
+ QGstElement decoder;
+
+ QGstreamerAudioOutput *gstAudioOutput = nullptr;
+ QGstreamerVideoOutput *gstVideoOutput = nullptr;
+
+ // QGstElement streamSynchronizer;
+
+ struct QGstPadLess
+ {
+ bool operator()(const QGstPad &lhs, const QGstPad &rhs) const
+ {
+ return lhs.pad() < rhs.pad();
+ }
+ };
+
+ std::map<QGstPad, QGstPad, QGstPadLess> decoderOutputMap;
+
+ // decoder connections
+ QGObjectHandlerScopedConnection padAdded;
+ QGObjectHandlerScopedConnection padRemoved;
+ QGObjectHandlerScopedConnection sourceSetup;
+ QGObjectHandlerScopedConnection uridecodebinElementAdded;
+ QGObjectHandlerScopedConnection unknownType;
+ QGObjectHandlerScopedConnection elementAdded;
+ QGObjectHandlerScopedConnection elementRemoved;
+
+ int decodeBinQueues = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamermessage_p.h b/src/plugins/multimedia/gstreamer/common/qgstreamermessage_p.h
new file mode 100644
index 000000000..9836bd0cb
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamermessage_p.h
@@ -0,0 +1,55 @@
+// 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
+
+//
+// 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 <common/qgst_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// Required for QDoc workaround
+class QString;
+
+template <>
+struct QGstPointerImpl::QGstRefcountingAdaptor<GstMessage>
+{
+ static void ref(GstMessage *arg) noexcept { gst_message_ref(arg); }
+ static void unref(GstMessage *arg) noexcept { gst_message_unref(arg); }
+};
+
+class QGstreamerMessage : public QGstPointerImpl::QGstObjectWrapper<GstMessage>
+{
+ using BaseClass = QGstPointerImpl::QGstObjectWrapper<GstMessage>;
+
+public:
+ using BaseClass::BaseClass;
+ QGstreamerMessage(const QGstreamerMessage &) = default;
+ QGstreamerMessage(QGstreamerMessage &&) noexcept = default;
+ QGstreamerMessage &operator=(const QGstreamerMessage &) = default;
+ QGstreamerMessage &operator=(QGstreamerMessage &&) noexcept = default;
+
+ GstMessageType type() const { return GST_MESSAGE_TYPE(get()); }
+ QGstObject source() const { return QGstObject(GST_MESSAGE_SRC(get()), QGstObject::NeedsRef); }
+ QGstStructureView structure() const { return QGstStructureView(gst_message_get_structure(get())); }
+
+ GstMessage *message() const { return get(); }
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QGstreamerMessage);
+
+#endif
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamermetadata.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamermetadata.cpp
new file mode 100644
index 000000000..9aa9406b9
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamermetadata.cpp
@@ -0,0 +1,489 @@
+// 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 <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 <common/qgst_handle_types_p.h>
+#include <common/qgstutils_p.h>
+#include <qgstreamerformatinfo_p.h>
+
+QT_BEGIN_NAMESPACE
+
+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;
+};
+
+constexpr const char *toTag(const char *t)
+{
+ return t;
+}
+constexpr const char *toTag(const MetadataKeyValuePair &kv)
+{
+ return kv.tag;
+}
+
+constexpr QMediaMetaData::Key toKey(QMediaMetaData::Key k)
+{
+ return k;
+}
+constexpr QMediaMetaData::Key toKey(const MetadataKeyValuePair &kv)
+{
+ return kv.key;
+}
+
+constexpr auto compareByKey = [](const auto &lhs, const auto &rhs) {
+ return toKey(lhs) < toKey(rhs);
+};
+
+constexpr auto compareByTag = [](const auto &lhs, const auto &rhs) {
+ return std::strcmp(toTag(lhs), toTag(rhs)) < 0;
+};
+
+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;
+}
+
+constexpr_lookup auto gstTagToMetaDataKey = makeLookupTable();
+constexpr_lookup auto metaDataKeyToGstTag = [] {
+ auto array = gstTagToMetaDataKey;
+ std::sort(array.begin(), array.end(), compareByKey);
+ return array;
+}();
+
+} // namespace MetadataLookupImpl
+
+QMediaMetaData::Key tagToKey(const char *tag)
+{
+ if (tag == nullptr)
+ return QMediaMetaData::Key(-1);
+
+ using namespace MetadataLookupImpl;
+ auto foundIterator = std::lower_bound(gstTagToMetaDataKey.begin(), gstTagToMetaDataKey.end(),
+ tag, compareByTag);
+ if (std::strcmp(foundIterator->tag, tag) == 0)
+ return foundIterator->key;
+
+ 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
+
+QtVideo::Rotation parseRotationTag(const char *string)
+{
+ 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 QDateTime{
+ QDate(year, month, day),
+ QTime(hour, minute, second),
+ QTimeZone(tz * 60 * 60),
+ };
+}
+
+QImage parseImage(const GValue &val)
+{
+ 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 {};
+}
+
+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);
+}
+
+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)) {
+ if (tag == extendedComment)
+ addTagsFromExtendedComment(list, tag, metadata);
+
+ return;
+ }
+
+ GValue val{};
+ gst_tag_list_copy_value(&val, list, tag);
+
+ GType type = G_VALUE_TYPE(&val);
+
+ 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::AnyLanguageCode)));
+ break;
+ }
+ case QMediaMetaData::Orientation: {
+ metadata.insert(key, QVariant::fromValue(parseRotationTag(str_value)));
+ break;
+ }
+ default:
+ 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
+
+QMediaMetaData taglistToMetaData(const QGstTagListHandle &handle)
+{
+ QMediaMetaData m;
+ extendMetaDataFromTagList(m, handle);
+ return m;
+}
+
+void extendMetaDataFromTagList(QMediaMetaData &metadata, const QGstTagListHandle &handle)
+{
+ if (handle)
+ gst_tag_list_foreach(handle.get(), reinterpret_cast<GstTagForeachFunc>(&addTagToMetaData),
+ &metadata);
+}
+
+static void applyMetaDataToTagSetter(const QMediaMetaData &metadata, GstTagSetter *element)
+{
+ gst_tag_setter_reset_tags(element);
+
+ for (QMediaMetaData::Key key : metadata.keys()) {
+ const char *tagName = keyToTag(key);
+ if (!tagName)
+ continue;
+ 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:
+ 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 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.bin(), GST_TYPE_TAG_SETTER);
+ GValue item = {};
+
+ while (gst_iterator_next(elements, &item) == GST_ITERATOR_OK) {
+ 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);
+}
+
+void extendMetaDataFromCaps(QMediaMetaData &metadata, const QGstCaps &caps)
+{
+ QGstStructureView structure = caps.at(0);
+
+ QMediaFormat::FileFormat fileFormat = QGstreamerFormatInfo::fileFormatForCaps(structure);
+ if (fileFormat != QMediaFormat::FileFormat::UnspecifiedFormat) {
+ // Container caps
+ metadata.insert(QMediaMetaData::FileFormat, fileFormat);
+ return;
+ }
+
+ QMediaFormat::AudioCodec audioCodec = QGstreamerFormatInfo::audioCodecForCaps(structure);
+ if (audioCodec != QMediaFormat::AudioCodec::Unspecified) {
+ // Audio stream caps
+ metadata.insert(QMediaMetaData::AudioCodec, QVariant::fromValue(audioCodec));
+ return;
+ }
+
+ QMediaFormat::VideoCodec videoCodec = QGstreamerFormatInfo::videoCodecForCaps(structure);
+ if (videoCodec != QMediaFormat::VideoCodec::Unspecified) {
+ // Video stream caps
+ metadata.insert(QMediaMetaData::VideoCodec, QVariant::fromValue(videoCodec));
+ std::optional<float> framerate = structure["framerate"].getFraction();
+ if (framerate)
+ metadata.insert(QMediaMetaData::VideoFrameRate, *framerate);
+
+ QSize resolution = structure.resolution();
+ if (resolution.isValid())
+ metadata.insert(QMediaMetaData::Resolution, resolution);
+ }
+}
+
+QMediaMetaData capsToMetaData(const QGstCaps &caps)
+{
+ QMediaMetaData metadata;
+ extendMetaDataFromCaps(metadata, caps);
+ return metadata;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamermetadata_p.h b/src/plugins/multimedia/gstreamer/common/qgstreamermetadata_p.h
new file mode 100644
index 000000000..f04a9aba9
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamermetadata_p.h
@@ -0,0 +1,35 @@
+// 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
+
+//
+// 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 "qgst_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QMediaMetaData taglistToMetaData(const QGstTagListHandle &);
+void extendMetaDataFromTagList(QMediaMetaData &, const QGstTagListHandle &);
+
+QMediaMetaData capsToMetaData(const QGstCaps &);
+void extendMetaDataFromCaps(QMediaMetaData &, const QGstCaps &);
+
+void applyMetaDataToTagSetter(const QMediaMetaData &metadata, const QGstBin &);
+void applyMetaDataToTagSetter(const QMediaMetaData &metadata, const QGstElement &);
+
+QT_END_NAMESPACE
+
+#endif // QGSTREAMERMETADATA_H
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput.cpp
new file mode 100644
index 000000000..ef991f5b4
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput.cpp
@@ -0,0 +1,220 @@
+// 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 <QtCore/qthread.h>
+
+#include <common/qgstreamervideooutput_p.h>
+#include <common/qgstreamervideosink_p.h>
+#include <common/qgstsubtitlesink_p.h>
+
+static Q_LOGGING_CATEGORY(qLcMediaVideoOutput, "qt.multimedia.videooutput")
+
+QT_BEGIN_NAMESPACE
+
+static QGstElement makeVideoConvertScale(const char *name)
+{
+ QGstElementFactoryHandle factory = QGstElement::findFactory("videoconvertscale");
+ if (factory) // videoconvertscale is only available in gstreamer 1.20
+ return QGstElement::createFromFactory(factory, name);
+
+ return QGstBin::createFromPipelineDescription("videoconvert ! videoscale", name,
+ /*ghostUnlinkedPads=*/true);
+}
+
+QMaybe<QGstreamerVideoOutput *> QGstreamerVideoOutput::create(QObject *parent)
+{
+ QGstElementFactoryHandle factory = QGstElement::findFactory("videoconvertscale");
+
+ static std::optional<QString> elementCheck = []() -> std::optional<QString> {
+ std::optional<QString> error = qGstErrorMessageIfElementsNotAvailable("fakesink", "queue");
+ if (error)
+ return error;
+
+ QGstElementFactoryHandle factory = QGstElement::findFactory("videoconvertscale");
+ if (factory)
+ return std::nullopt;
+
+ return qGstErrorMessageIfElementsNotAvailable("videoconvert", "videoscale");
+ }();
+
+ if (elementCheck)
+ return *elementCheck;
+
+ return new QGstreamerVideoOutput(parent);
+}
+
+QGstreamerVideoOutput::QGstreamerVideoOutput(QObject *parent)
+ : QObject(parent),
+ m_outputBin(QGstBin::create("videoOutput")),
+ m_videoQueue{
+ QGstElement::createFromFactory("queue", "videoQueue"),
+ },
+ m_videoConvertScale{
+ makeVideoConvertScale("videoConvertScale"),
+ },
+ m_videoSink{
+ QGstElement::createFromFactory("fakesink", "fakeVideoSink"),
+ }
+{
+ m_videoSink.set("sync", true);
+ m_videoSink.set("async", false); // no asynchronous state changes
+
+ m_outputBin.add(m_videoQueue, m_videoConvertScale, m_videoSink);
+ qLinkGstElements(m_videoQueue, m_videoConvertScale, m_videoSink);
+
+ m_outputBin.addGhostPad(m_videoQueue, "sink");
+}
+
+QGstreamerVideoOutput::~QGstreamerVideoOutput()
+{
+ m_outputBin.setStateSync(GST_STATE_NULL);
+}
+
+void QGstreamerVideoOutput::setVideoSink(QVideoSink *sink)
+{
+ auto *gstVideoSink = sink ? static_cast<QGstreamerVideoSink *>(sink->platformVideoSink()) : nullptr;
+ if (gstVideoSink == m_platformVideoSink)
+ return;
+
+ if (m_platformVideoSink)
+ m_platformVideoSink->setPipeline({});
+
+ m_platformVideoSink = gstVideoSink;
+ if (m_platformVideoSink) {
+ m_platformVideoSink->setPipeline(m_pipeline);
+ if (m_nativeSize.isValid())
+ m_platformVideoSink->setNativeSize(m_nativeSize);
+ }
+ QGstElement gstSink;
+ if (m_platformVideoSink) {
+ gstSink = m_platformVideoSink->gstSink();
+ } else {
+ gstSink = QGstElement::createFromFactory("fakesink", "fakevideosink");
+ Q_ASSERT(gstSink);
+ gstSink.set("sync", true);
+ gstSink.set("async", false); // no asynchronous state changes
+ }
+
+ if (m_videoSink == gstSink)
+ return;
+
+ m_pipeline.modifyPipelineWhileNotRunning([&] {
+ if (!m_videoSink.isNull())
+ m_outputBin.stopAndRemoveElements(m_videoSink);
+
+ m_videoSink = gstSink;
+ m_outputBin.add(m_videoSink);
+
+ qLinkGstElements(m_videoConvertScale, m_videoSink);
+
+ GstEvent *event = gst_event_new_reconfigure();
+ gst_element_send_event(m_videoSink.element(), event);
+ m_videoSink.syncStateWithParent();
+
+ doLinkSubtitleStream();
+ });
+
+ qCDebug(qLcMediaVideoOutput) << "sinkChanged" << gstSink.name();
+
+ m_pipeline.dumpGraph(m_videoSink.name().constData());
+}
+
+void QGstreamerVideoOutput::setPipeline(const QGstPipeline &pipeline)
+{
+ m_pipeline = pipeline;
+ if (m_platformVideoSink)
+ m_platformVideoSink->setPipeline(m_pipeline);
+}
+
+void QGstreamerVideoOutput::linkSubtitleStream(QGstElement src)
+{
+ qCDebug(qLcMediaVideoOutput) << "link subtitle stream" << src.isNull();
+ if (src == m_subtitleSrc)
+ return;
+
+ m_pipeline.modifyPipelineWhileNotRunning([&] {
+ m_subtitleSrc = src;
+ doLinkSubtitleStream();
+ });
+}
+
+void QGstreamerVideoOutput::unlinkSubtitleStream()
+{
+ if (m_subtitleSrc.isNull())
+ return;
+ qCDebug(qLcMediaVideoOutput) << "unlink subtitle stream";
+ m_subtitleSrc = {};
+ if (!m_subtitleSink.isNull()) {
+ m_pipeline.modifyPipelineWhileNotRunning([&] {
+ m_pipeline.stopAndRemoveElements(m_subtitleSink);
+ return;
+ });
+ m_subtitleSink = {};
+ }
+ if (m_platformVideoSink)
+ m_platformVideoSink->setSubtitleText({});
+}
+
+void QGstreamerVideoOutput::doLinkSubtitleStream()
+{
+ if (!m_subtitleSink.isNull()) {
+ m_pipeline.stopAndRemoveElements(m_subtitleSink);
+ m_subtitleSink = {};
+ }
+ if (!m_platformVideoSink || m_subtitleSrc.isNull())
+ return;
+ if (m_subtitleSink.isNull()) {
+ m_subtitleSink = m_platformVideoSink->subtitleSink();
+ m_pipeline.add(m_subtitleSink);
+ }
+ qLinkGstElements(m_subtitleSrc, m_subtitleSink);
+}
+
+void QGstreamerVideoOutput::updateNativeSize()
+{
+ if (!m_platformVideoSink)
+ return;
+
+ m_platformVideoSink->setNativeSize(qRotatedFrameSize(m_nativeSize, m_rotation));
+}
+
+void QGstreamerVideoOutput::setIsPreview()
+{
+ // configures the queue to be fast and lightweight for camera preview
+ // also avoids blocking the queue in case we have an encodebin attached to the tee as well
+ m_videoQueue.set("leaky", 2 /*downstream*/);
+ m_videoQueue.set("silent", true);
+ m_videoQueue.set("max-size-buffers", uint(1));
+ m_videoQueue.set("max-size-bytes", uint(0));
+ m_videoQueue.set("max-size-time", quint64(0));
+}
+
+void QGstreamerVideoOutput::flushSubtitles()
+{
+ if (!m_subtitleSink.isNull()) {
+ auto pad = m_subtitleSink.staticPad("sink");
+ auto *event = gst_event_new_flush_start();
+ pad.sendEvent(event);
+ event = gst_event_new_flush_stop(false);
+ pad.sendEvent(event);
+ }
+}
+
+void QGstreamerVideoOutput::setNativeSize(QSize sz)
+{
+ m_nativeSize = sz;
+ updateNativeSize();
+}
+
+void QGstreamerVideoOutput::setRotation(QtVideo::Rotation rot)
+{
+ m_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
new file mode 100644
index 000000000..10d2f3ee7
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput_p.h
@@ -0,0 +1,80 @@
+// 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
+
+//
+// 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 <private/qtmultimediaglobal_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>
+
+QT_BEGIN_NAMESPACE
+
+class QVideoSink;
+
+class QGstreamerVideoOutput : public QObject
+{
+ Q_OBJECT
+
+public:
+ static QMaybe<QGstreamerVideoOutput *> create(QObject *parent = nullptr);
+ ~QGstreamerVideoOutput();
+
+ void setVideoSink(QVideoSink *sink);
+ QGstreamerVideoSink *gstreamerVideoSink() const { return m_platformVideoSink; }
+
+ void setPipeline(const QGstPipeline &pipeline);
+
+ QGstElement gstElement() const { return m_outputBin; }
+ void linkSubtitleStream(QGstElement subtitleSrc);
+ void unlinkSubtitleStream();
+
+ void setIsPreview();
+ void flushSubtitles();
+
+ void setNativeSize(QSize);
+ void setRotation(QtVideo::Rotation);
+
+private:
+ explicit QGstreamerVideoOutput(QObject *parent);
+
+ void doLinkSubtitleStream();
+ void updateNativeSize();
+
+ QPointer<QGstreamerVideoSink> m_platformVideoSink;
+
+ // Gst elements
+ QGstPipeline m_pipeline;
+
+ QGstBin m_outputBin;
+ QGstElement m_videoQueue;
+ QGstElement m_videoConvertScale;
+ QGstElement m_videoSink;
+
+ QGstElement m_subtitleSrc;
+ QGstElement m_subtitleSink;
+
+ QSize m_nativeSize;
+ QtVideo::Rotation m_rotation{};
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamervideooverlay.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamervideooverlay.cpp
new file mode 100644
index 000000000..6ca23006b
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamervideooverlay.cpp
@@ -0,0 +1,218 @@
+// 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 <QtMultimedia/private/qtmultimediaglobal_p.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 <gst/video/videooverlay.h>
+
+QT_BEGIN_NAMESPACE
+
+struct ElementMap
+{
+ QStringView qtPlatform;
+ const char *gstreamerElement = nullptr;
+};
+
+// Ordered by descending priority
+static constexpr ElementMap elementMap[] = {
+ { u"xcb", "xvimagesink" },
+ { u"xcb", "ximagesink" },
+
+ // wayland
+ { u"wayland", "waylandsink" },
+};
+
+static bool qt_gst_element_is_functioning(QGstElement element)
+{
+ GstStateChangeReturn ret = element.setState(GST_STATE_READY);
+ if (ret == GST_STATE_CHANGE_SUCCESS) {
+ element.setState(GST_STATE_NULL);
+ return true;
+ }
+
+ return false;
+}
+
+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 (const auto &i : elementMap) {
+ if (platform != i.qtPlatform)
+ continue;
+ QGstElement choice = QGstElement::createFromFactory(i.gstreamerElement, i.gstreamerElement);
+ if (choice.isNull())
+ continue;
+
+ if (qt_gst_element_is_functioning(choice))
+ return choice;
+ }
+
+ // 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 != 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 (GstElementFactory *f : QGstUtils::GListRangeAdaptor<GstElementFactory *>(list)) {
+ if (!gst_element_factory_has_interface(f, "GstVideoOverlay"))
+ continue;
+
+ choice = QGstElement::createFromFactory(f, nullptr);
+ if (choice.isNull())
+ continue;
+
+ if (qt_gst_element_is_functioning(choice))
+ break;
+ choice = {};
+ }
+
+ gst_plugin_feature_list_free(list);
+ if (choice.isNull())
+ qWarning() << "Could not find a valid windowed video sink";
+
+ return choice;
+}
+
+QGstreamerVideoOverlay::QGstreamerVideoOverlay(QGstreamerVideoSink *parent, const QByteArray &elementName)
+ : QObject(parent)
+ , QGstreamerBufferProbe(QGstreamerBufferProbe::ProbeCaps)
+ , m_gstreamerVideoSink(parent)
+{
+ QGstElement sink;
+ if (!elementName.isEmpty())
+ sink = QGstElement::createFromFactory(elementName.constData());
+ else
+ sink = findBestVideoSink();
+
+ setVideoSink(sink);
+}
+
+QGstreamerVideoOverlay::~QGstreamerVideoOverlay()
+{
+ if (!m_videoSink.isNull()) {
+ QGstPad pad = m_videoSink.staticPad("sink");
+ removeProbeFromPad(pad.pad());
+ }
+}
+
+QGstElement QGstreamerVideoOverlay::videoSink() const
+{
+ return m_videoSink;
+}
+
+void QGstreamerVideoOverlay::setVideoSink(QGstElement sink)
+{
+ if (sink.isNull())
+ return;
+
+ m_videoSink = std::move(sink);
+
+ QGstPad pad = m_videoSink.staticPad("sink");
+ addProbeToPad(pad.pad());
+
+ auto *klass = G_OBJECT_GET_CLASS(m_videoSink.object());
+ m_hasForceAspectRatio = g_object_class_find_property(klass, "force-aspect-ratio");
+ m_hasFullscreen = g_object_class_find_property(klass, "fullscreen");
+}
+
+QSize QGstreamerVideoOverlay::nativeVideoSize() const
+{
+ return m_nativeVideoSize;
+}
+
+void QGstreamerVideoOverlay::setWindowHandle(WId id)
+{
+ m_windowId = id;
+
+ if (!m_videoSink.isNull() && GST_IS_VIDEO_OVERLAY(m_videoSink.object())) {
+ applyRenderRect();
+
+ // Properties need to be reset when changing the winId.
+ setAspectRatioMode(m_aspectRatioMode);
+ setFullScreen(m_fullScreen);
+ applyRenderRect();
+ }
+}
+
+void QGstreamerVideoOverlay::setRenderRectangle(const QRect &rect)
+{
+ renderRect = rect;
+ applyRenderRect();
+}
+
+void QGstreamerVideoOverlay::applyRenderRect()
+{
+ if (!m_windowId)
+ return;
+
+ int x = -1;
+ int y = -1;
+ int w = -1;
+ int h = -1;
+
+ if (!renderRect.isEmpty()) {
+ x = renderRect.x();
+ y = renderRect.y();
+ w = renderRect.width();
+ h = renderRect.height();
+ QSize scaledVideo = m_nativeVideoSize.scaled(w, h, m_aspectRatioMode);
+ x += (w - scaledVideo.width())/2;
+ y += (h - scaledVideo.height())/2;
+ w = scaledVideo.width();
+ h = scaledVideo.height();
+ }
+
+ if (!m_videoSink.isNull() && GST_IS_VIDEO_OVERLAY(m_videoSink.object()))
+ gst_video_overlay_set_render_rectangle(GST_VIDEO_OVERLAY(m_videoSink.object()), x, y, w, h);
+}
+
+void QGstreamerVideoOverlay::probeCaps(GstCaps *caps)
+{
+ QSize size = QGstCaps(caps, QGstCaps::NeedsRef).at(0).resolution();
+ if (size != m_nativeVideoSize) {
+ m_nativeVideoSize = size;
+ m_gstreamerVideoSink->setNativeSize(m_nativeVideoSize);
+ applyRenderRect();
+ }
+}
+
+void QGstreamerVideoOverlay::setAspectRatioMode(Qt::AspectRatioMode mode)
+{
+ m_aspectRatioMode = mode;
+ if (m_hasForceAspectRatio)
+ m_videoSink.set("force-aspect-ratio", (mode == Qt::KeepAspectRatio));
+}
+
+void QGstreamerVideoOverlay::setFullScreen(bool fullscreen)
+{
+ m_fullScreen = fullscreen;
+ if (m_hasFullscreen)
+ m_videoSink.set("fullscreen", fullscreen);
+}
+
+bool QGstreamerVideoOverlay::processSyncMessage(const QGstreamerMessage &message)
+{
+ 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
new file mode 100644
index 000000000..588e8b5e4
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamervideooverlay_p.h
@@ -0,0 +1,74 @@
+// 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
+
+//
+// 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 <common/qgstpipeline_p.h>
+#include <common/qgstreamerbufferprobe_p.h>
+#include <common/qgst_p.h>
+#include <QtGui/qwindowdefs.h>
+
+QT_BEGIN_NAMESPACE
+class QGstreamerVideoSink;
+
+class QGstreamerVideoOverlay : public QObject,
+ public QGstreamerSyncMessageFilter,
+ private QGstreamerBufferProbe
+{
+ Q_OBJECT
+public:
+ explicit QGstreamerVideoOverlay(QGstreamerVideoSink *parent = nullptr,
+ const QByteArray &elementName = QByteArray());
+ virtual ~QGstreamerVideoOverlay();
+
+ QGstElement videoSink() const;
+ void setVideoSink(QGstElement);
+ QSize nativeVideoSize() const;
+
+ void setWindowHandle(WId id);
+ void setRenderRectangle(const QRect &rect);
+
+ void setAspectRatioMode(Qt::AspectRatioMode mode);
+ void setFullScreen(bool fullscreen);
+
+ bool processSyncMessage(const QGstreamerMessage &message) override;
+
+ bool isNull() const { return m_videoSink.isNull(); }
+
+Q_SIGNALS:
+ void nativeVideoSizeChanged();
+ void activeChanged();
+
+private:
+ void probeCaps(GstCaps *caps) override;
+ void applyRenderRect();
+
+ QGstreamerVideoSink *m_gstreamerVideoSink = nullptr;
+ QGstElement m_videoSink;
+ QSize m_nativeVideoSize;
+
+ bool m_hasForceAspectRatio = false;
+ bool m_hasFullscreen = false;
+ Qt::AspectRatioMode m_aspectRatioMode = Qt::KeepAspectRatio;
+ bool m_fullScreen = false;
+
+ WId m_windowId = 0;
+ QRect renderRect;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGSTREAMERVIDEOOVERLAY_P_H
+
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamervideosink.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamervideosink.cpp
new file mode 100644
index 000000000..39377265a
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamervideosink.cpp
@@ -0,0 +1,314 @@
+// 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 <QGuiApplication>
+#include <QtGui/qopenglcontext.h>
+#include <QWindow>
+#include <qpa/qplatformnativeinterface.h>
+#include <gst/gl/gstglconfig.h>
+
+#if GST_GL_HAVE_WINDOW_X11 && __has_include("X11/Xlib-xcb.h")
+# include <gst/gl/x11/gstgldisplay_x11.h>
+#endif
+#if GST_GL_HAVE_PLATFORM_EGL
+# include <gst/gl/egl/gstgldisplay_egl.h>
+# include <EGL/egl.h>
+# include <EGL/eglext.h>
+#endif
+#if GST_GL_HAVE_WINDOW_WAYLAND && __has_include("wayland-client.h")
+# include <gst/gl/wayland/gstgldisplay_wayland.h>
+#endif
+#endif // #if QT_CONFIG(gstreamer_gl)
+
+#include <QtCore/qdebug.h>
+
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLcGstVideoSink, "qt.multimedia.gstvideosink");
+
+QGstreamerVideoSink::QGstreamerVideoSink(QVideoSink *parent)
+ : QPlatformVideoSink{
+ parent,
+ },
+ m_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 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.
+ QGstElementFactoryHandle factory;
+
+ // QT_GSTREAMER_OVERRIDE_VIDEO_CONVERSION_ELEMENT allows users to override the
+ // conversion element. Ideally we construct the element programatically, though.
+ QByteArray preprocessOverride = qgetenv("QT_GSTREAMER_OVERRIDE_VIDEO_CONVERSION_ELEMENT");
+ if (!preprocessOverride.isEmpty()) {
+ qCDebug(qLcGstVideoSink) << "requesting conversion element from environment:"
+ << preprocessOverride;
+
+ m_gstPreprocess = QGstBin::createFromPipelineDescription(preprocessOverride, nullptr,
+ /*ghostUnlinkedPads=*/true);
+ if (!m_gstPreprocess)
+ qCWarning(qLcGstVideoSink) << "Cannot create conversion element:" << preprocessOverride;
+ }
+
+ if (!m_gstPreprocess) {
+ // 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 sometimes marked as
+ // a regular supported video/x-raw format.
+ static constexpr auto decodersToTest = {
+ "imxvideoconvert_g2d",
+ "nvvidconv",
+ };
+
+ for (const char *decoder : decodersToTest) {
+ factory = QGstElement::findFactory(decoder);
+ if (factory)
+ break;
+ }
+
+ if (factory) {
+ qCDebug(qLcGstVideoSink)
+ << "instantiating conversion element:"
+ << g_type_name(gst_element_factory_get_element_type(factory.get()));
+
+ m_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
+ m_gstCapsFilter =
+ QGstElement::createFromFactory("identity", "nullPixelAspectRatioCapsFilter");
+ } else {
+ m_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(m_gstCapsFilter.element(), "caps", capsFilterCaps.caps(), NULL);
+ }
+
+ if (m_gstPreprocess) {
+ m_sinkBin.add(m_gstPreprocess, m_gstCapsFilter);
+ qLinkGstElements(m_gstPreprocess, m_gstCapsFilter);
+ m_sinkBin.addGhostPad(m_gstPreprocess, "sink");
+ } else {
+ m_sinkBin.add(m_gstCapsFilter);
+ m_sinkBin.addGhostPad(m_gstCapsFilter, "sink");
+ }
+
+ m_gstSubtitleSink =
+ QGstElement(GST_ELEMENT(QGstSubtitleSink::createSink(this)), QGstElement::NeedsRef);
+}
+
+QGstreamerVideoSink::~QGstreamerVideoSink()
+{
+ emit aboutToBeDestroyed();
+
+ unrefGstContexts();
+
+ setPipeline(QGstPipeline());
+}
+
+QGstElement QGstreamerVideoSink::gstSink()
+{
+ updateSinkElement();
+ return m_sinkBin;
+}
+
+void QGstreamerVideoSink::setPipeline(QGstPipeline pipeline)
+{
+ m_pipeline = std::move(pipeline);
+}
+
+bool QGstreamerVideoSink::inStoppedState() const
+{
+ if (m_pipeline.isNull())
+ return true;
+ return m_pipeline.inStoppedState();
+}
+
+void QGstreamerVideoSink::setRhi(QRhi *rhi)
+{
+ if (rhi && rhi->backend() != QRhi::OpenGLES2)
+ rhi = nullptr;
+ if (m_rhi == rhi)
+ return;
+
+ m_rhi = rhi;
+ updateGstContexts();
+ if (!m_gstQtSink.isNull()) {
+ // force creation of a new sink with proper caps
+ createQtSink();
+ updateSinkElement();
+ }
+}
+
+void QGstreamerVideoSink::createQtSink()
+{
+ if (m_gstQtSink)
+ m_gstQtSink.setStateSync(GST_STATE_NULL);
+
+ m_gstQtSink =
+ QGstElement(reinterpret_cast<GstElement *>(QGstVideoRendererSink::createSink(this)),
+ QGstElement::NeedsRef);
+}
+
+void QGstreamerVideoSink::updateSinkElement()
+{
+ QGstElement newSink;
+ if (m_gstQtSink.isNull())
+ createQtSink();
+ newSink = m_gstQtSink;
+
+ if (newSink == m_gstVideoSink)
+ return;
+
+ m_pipeline.modifyPipelineWhileNotRunning([&] {
+ if (!m_gstVideoSink.isNull())
+ m_sinkBin.stopAndRemoveElements(m_gstVideoSink);
+
+ newSink.set("async", false); // no asynchronous state changes
+
+ m_gstVideoSink = newSink;
+ m_sinkBin.add(m_gstVideoSink);
+ qLinkGstElements(m_gstCapsFilter, m_gstVideoSink);
+ m_gstVideoSink.setState(GST_STATE_PAUSED);
+ });
+
+ m_pipeline.dumpGraph("updateVideoSink");
+}
+
+void QGstreamerVideoSink::unrefGstContexts()
+{
+ m_gstGlDisplayContext.close();
+ m_gstGlLocalContext.close();
+ m_eglDisplay = nullptr;
+ m_eglImageTargetTexture2D = nullptr;
+}
+
+void QGstreamerVideoSink::updateGstContexts()
+{
+ using namespace Qt::Literals;
+
+ unrefGstContexts();
+
+#if QT_CONFIG(gstreamer_gl)
+ if (!m_rhi || m_rhi->backend() != QRhi::OpenGLES2)
+ return;
+
+ auto *nativeHandles = static_cast<const QRhiGles2NativeHandles *>(m_rhi->nativeHandles());
+ auto glContext = nativeHandles->context;
+ Q_ASSERT(glContext);
+
+ const QString platform = QGuiApplication::platformName();
+ QPlatformNativeInterface *pni = QGuiApplication::platformNativeInterface();
+ m_eglDisplay = pni->nativeResourceForIntegration("egldisplay"_ba);
+// qDebug() << "platform is" << platform << m_eglDisplay;
+
+ QGstGLDisplayHandle gstGlDisplay;
+
+ QByteArray contextName = "eglcontext"_ba;
+ GstGLPlatform glPlatform = GST_GL_PLATFORM_EGL;
+ // use the egl display if we have one
+ if (m_eglDisplay) {
+#if GST_GL_HAVE_PLATFORM_EGL
+ gstGlDisplay.reset(
+ GST_GL_DISPLAY_CAST(gst_gl_display_egl_new_with_egl_display(m_eglDisplay)));
+ m_eglImageTargetTexture2D = eglGetProcAddress("glEGLImageTargetTexture2DOES");
+#endif
+ } else {
+ auto display = pni->nativeResourceForIntegration("display"_ba);
+
+ if (display) {
+#if GST_GL_HAVE_WINDOW_X11 && __has_include("X11/Xlib-xcb.h")
+ if (platform == QLatin1String("xcb")) {
+ contextName = "glxcontext"_ba;
+ glPlatform = GST_GL_PLATFORM_GLX;
+
+ 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.reset(GST_GL_DISPLAY_CAST(gst_gl_display_wayland_new_with_display(
+ reinterpret_cast<struct wl_display *>(display))));
+ }
+#endif
+ }
+ }
+
+ if (!gstGlDisplay) {
+ qWarning() << "Could not create GstGLDisplay";
+ return;
+ }
+
+ void *nativeContext = pni->nativeResourceForContext(contextName, glContext);
+ if (!nativeContext)
+ qWarning() << "Could not find resource for" << contextName;
+
+ GstGLAPI glApi = QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL ? GST_GL_API_OPENGL : GST_GL_API_GLES2;
+ QGstGLContextHandle appContext{
+ gst_gl_context_new_wrapped(gstGlDisplay.get(), guintptr(nativeContext), glPlatform, glApi),
+ };
+ if (!appContext)
+ qWarning() << "Could not create wrappped context for platform:" << glPlatform;
+
+ gst_gl_context_activate(appContext.get(), true);
+
+ QUniqueGErrorHandle error;
+ gst_gl_context_fill_info(appContext.get(), &error);
+ if (error) {
+ qWarning() << "Could not fill context info:" << error;
+ error = {};
+ }
+
+ QGstGLContextHandle displayContext;
+ gst_gl_display_create_context(gstGlDisplay.get(), appContext.get(), &displayContext, &error);
+ if (error)
+ qWarning() << "Could not create display context:" << error;
+
+ appContext.close();
+
+ 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 (m_pipeline)
+ gst_element_set_context(m_pipeline.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
new file mode 100644
index 000000000..7ee1dd2e6
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamervideosink_p.h
@@ -0,0 +1,76 @@
+// 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
+// -------------
+//
+// 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/qvideosink.h>
+#include <QtMultimedia/private/qplatformvideosink_p.h>
+
+#include <common/qgstpipeline_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGstreamerVideoSink : public QPlatformVideoSink
+{
+ Q_OBJECT
+
+public:
+ explicit QGstreamerVideoSink(QVideoSink *parent = nullptr);
+ ~QGstreamerVideoSink();
+
+ void setRhi(QRhi *rhi) override;
+ QRhi *rhi() const { return m_rhi; }
+
+ QGstElement gstSink();
+ QGstElement subtitleSink() const { return m_gstSubtitleSink; }
+
+ void setPipeline(QGstPipeline pipeline);
+ bool inStoppedState() const;
+
+ 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; }
+
+Q_SIGNALS:
+ void aboutToBeDestroyed();
+
+private:
+ void createQtSink();
+ void updateSinkElement();
+
+ void unrefGstContexts();
+ void updateGstContexts();
+
+ QGstPipeline m_pipeline;
+ QGstBin m_sinkBin;
+ QGstElement m_gstPreprocess;
+ QGstElement m_gstCapsFilter;
+ QGstElement m_gstVideoSink;
+ QGstElement m_gstQtSink;
+ QGstElement m_gstSubtitleSink;
+
+ QRhi *m_rhi = nullptr;
+
+ Qt::HANDLE m_eglDisplay = nullptr;
+ QFunctionPointer m_eglImageTargetTexture2D = nullptr;
+
+ QGstContextHandle m_gstGlLocalContext;
+ QGstContextHandle m_gstGlDisplayContext;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/gstreamer/common/qgstsubtitlesink.cpp b/src/plugins/multimedia/gstreamer/common/qgstsubtitlesink.cpp
new file mode 100644
index 000000000..c6b230d85
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstsubtitlesink.cpp
@@ -0,0 +1,155 @@
+// 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>
+#include <QEvent>
+
+#include "qgstreamervideosink_p.h"
+#include "qgstsubtitlesink_p.h"
+
+QT_BEGIN_NAMESPACE
+
+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)
+{
+ gst_current_sink = sink;
+
+ QGstSubtitleSink *gstSink = reinterpret_cast<QGstSubtitleSink *>(
+ g_object_new(QGstSubtitleSink::get_type(), nullptr));
+ g_object_set(gstSink, "async", false, nullptr);
+
+ return gstSink;
+}
+
+GType QGstSubtitleSink::get_type()
+{
+ 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, result);
+
+ return result;
+ }();
+
+ return type;
+}
+
+void QGstSubtitleSink::class_init(gpointer g_class, gpointer class_data)
+{
+ Q_UNUSED(class_data);
+
+ 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;
+ base_sink_class->get_caps = QGstSubtitleSink::get_caps;
+ base_sink_class->set_caps = QGstSubtitleSink::set_caps;
+ base_sink_class->propose_allocation = QGstSubtitleSink::propose_allocation;
+ base_sink_class->wait_event = QGstSubtitleSink::wait_event;
+
+ GstElementClass *element_class = reinterpret_cast<GstElementClass *>(g_class);
+ element_class->change_state = QGstSubtitleSink::change_state;
+ gst_element_class_set_metadata(element_class,
+ "Qt built-in subtitle sink",
+ "Sink/Subtitle",
+ "Qt default built-in subtitle sink",
+ "The Qt Company");
+
+ GObjectClass *object_class = reinterpret_cast<GObjectClass *>(g_class);
+ object_class->finalize = QGstSubtitleSink::finalize;
+}
+
+void QGstSubtitleSink::base_init(gpointer g_class)
+{
+ static GstStaticPadTemplate sink_pad_template = GST_STATIC_PAD_TEMPLATE(
+ "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS("ANY"));
+
+ gst_element_class_add_pad_template(
+ GST_ELEMENT_CLASS(g_class), gst_static_pad_template_get(&sink_pad_template));
+}
+
+void QGstSubtitleSink::instance_init(GTypeInstance *instance, gpointer g_class)
+{
+ Q_UNUSED(g_class);
+ ST_SINK(instance);
+
+ Q_ASSERT(gst_current_sink);
+ sink->sink = gst_current_sink;
+ gst_current_sink = nullptr;
+}
+
+void QGstSubtitleSink::finalize(GObject *object)
+{
+ // Chain up
+ G_OBJECT_CLASS(gst_sink_parent_class)->finalize(object);
+}
+
+GstStateChangeReturn QGstSubtitleSink::change_state(GstElement *element, GstStateChange transition)
+{
+ return GST_ELEMENT_CLASS(gst_sink_parent_class)->change_state(element, transition);
+}
+
+GstCaps *QGstSubtitleSink::get_caps(GstBaseSink *base, GstCaps *filter)
+{
+ return gst_sink_parent_class->get_caps(base, filter);
+}
+
+gboolean QGstSubtitleSink::set_caps(GstBaseSink *base, GstCaps *caps)
+{
+ qDebug() << "set_caps:" << caps;
+ return gst_sink_parent_class->set_caps(base, caps);
+}
+
+gboolean QGstSubtitleSink::propose_allocation(GstBaseSink *base, GstQuery *query)
+{
+ return gst_sink_parent_class->propose_allocation(base, query);
+}
+
+GstFlowReturn QGstSubtitleSink::wait_event(GstBaseSink *base, GstEvent *event)
+{
+ GstFlowReturn retval = gst_sink_parent_class->wait_event(base, event);
+ ST_SINK(base);
+ if (event->type == GST_EVENT_GAP) {
+// qDebug() << "gap, clearing subtitle";
+ sink->sink->setSubtitleText(QString());
+ }
+ return retval;
+}
+
+GstFlowReturn QGstSubtitleSink::render(GstBaseSink *base, GstBuffer *buffer)
+{
+ ST_SINK(base);
+ GstMemory *mem = gst_buffer_get_memory(buffer, 0);
+ GstMapInfo info;
+ QString subtitle;
+ if (gst_memory_map(mem, &info, GST_MAP_READ))
+ subtitle = QString::fromUtf8(reinterpret_cast<const char *>(info.data));
+ gst_memory_unmap(mem, &info);
+// qDebug() << "render" << buffer << subtitle;
+ sink->sink->setSubtitleText(subtitle);
+ return GST_FLOW_OK;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstsubtitlesink_p.h b/src/plugins/multimedia/gstreamer/common/qgstsubtitlesink_p.h
new file mode 100644
index 000000000..0f515cb99
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstsubtitlesink_p.h
@@ -0,0 +1,70 @@
+// 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
+
+//
+// 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 <QtCore/qlist.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qqueue.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qwaitcondition.h>
+#include <common/qgst_p.h>
+#include <gst/base/gstbasesink.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGstreamerVideoSink;
+
+class QGstSubtitleSink
+{
+public:
+ GstBaseSink parent{};
+
+ static QGstSubtitleSink *createSink(QGstreamerVideoSink *sink);
+
+private:
+ static GType get_type();
+ static void class_init(gpointer g_class, gpointer class_data);
+ static void base_init(gpointer g_class);
+ static void instance_init(GTypeInstance *instance, gpointer g_class);
+
+ static void finalize(GObject *object);
+
+ static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition);
+
+ static GstCaps *get_caps(GstBaseSink *sink, GstCaps *filter);
+ static gboolean set_caps(GstBaseSink *sink, GstCaps *caps);
+
+ static gboolean propose_allocation(GstBaseSink *sink, GstQuery *query);
+
+ static GstFlowReturn wait_event(GstBaseSink * sink, GstEvent * event);
+ static GstFlowReturn render(GstBaseSink *sink, GstBuffer *buffer);
+
+private:
+ QGstreamerVideoSink *sink = nullptr;
+};
+
+
+class QGstSubtitleSinkClass
+{
+public:
+ GstBaseSinkClass parent_class;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/gstreamer/common/qgstutils.cpp b/src/plugins/multimedia/gstreamer/common/qgstutils.cpp
new file mode 100644
index 000000000..8ec2bde3c
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstutils.cpp
@@ -0,0 +1,141 @@
+// 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/qgstutils_p.h>
+#include <common/qgst_p.h>
+
+#include <QtMultimedia/qaudioformat.h>
+
+#include <chrono>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+const char *audioSampleFormatNames[QAudioFormat::NSampleFormats] = {
+ nullptr,
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ "U8",
+ "S16LE",
+ "S32LE",
+ "F32LE"
+#else
+ "U8",
+ "S16BE",
+ "S32BE",
+ "F32BE"
+#endif
+};
+
+QAudioFormat::SampleFormat gstSampleFormatToSampleFormat(const char *fmt)
+{
+ if (fmt) {
+ for (int i = 1; i < QAudioFormat::NSampleFormats; ++i) {
+ if (strcmp(fmt, audioSampleFormatNames[i]))
+ continue;
+ return QAudioFormat::SampleFormat(i);
+ }
+ }
+ return QAudioFormat::Unknown;
+}
+
+} // namespace
+
+/*
+ Returns audio format for a sample \a sample.
+ If the buffer doesn't have a valid audio format, an empty QAudioFormat is returned.
+*/
+QAudioFormat QGstUtils::audioFormatForSample(GstSample *sample)
+{
+ auto caps = QGstCaps(gst_sample_get_caps(sample), QGstCaps::NeedsRef);
+ if (caps.isNull())
+ return {};
+ return audioFormatForCaps(caps);
+}
+
+QAudioFormat QGstUtils::audioFormatForCaps(const QGstCaps &caps)
+{
+ QAudioFormat format;
+ QGstStructureView s = caps.at(0);
+ if (s.name() != "audio/x-raw")
+ return format;
+
+ auto rate = s["rate"].toInt();
+ auto channels = s["channels"].toInt();
+ QAudioFormat::SampleFormat fmt = gstSampleFormatToSampleFormat(s["format"].toString());
+ if (!rate || !channels || fmt == QAudioFormat::Unknown)
+ return format;
+
+ format.setSampleRate(*rate);
+ format.setChannelCount(*channels);
+ format.setSampleFormat(fmt);
+
+ return format;
+}
+
+/*
+ Builds GstCaps for an audio format \a format.
+ Returns 0 if the audio format is not valid.
+
+ \note Caller must unreference GstCaps.
+*/
+
+QGstCaps QGstUtils::capsForAudioFormat(const QAudioFormat &format)
+{
+ if (!format.isValid())
+ return {};
+
+ auto sampleFormat = format.sampleFormat();
+ 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
+{
+ if (!GST_VALUE_HOLDS_LIST(value))
+ return {};
+
+ QList<QAudioFormat::SampleFormat> formats;
+ guint nFormats = gst_value_list_get_size(value);
+ for (guint f = 0; f < nFormats; ++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;
+ formats.append(fmt);
+ }
+ return formats;
+}
+
+void QGstUtils::setFrameTimeStampsFromBuffer(QVideoFrame *frame, GstBuffer *buffer)
+{
+ using namespace std::chrono;
+ using namespace std::chrono_literals;
+
+ // GStreamer uses nanoseconds, Qt uses microseconds
+ nanoseconds startTime{ GST_BUFFER_TIMESTAMP(buffer) };
+ if (startTime >= 0ns) {
+ frame->setStartTime(floor<microseconds>(startTime).count());
+
+ nanoseconds duration{ GST_BUFFER_DURATION(buffer) };
+ if (duration >= 0ns)
+ frame->setEndTime(floor<microseconds>(startTime + duration).count());
+ }
+}
+
+GList *qt_gst_video_sinks()
+{
+ return gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_SINK
+ | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO,
+ GST_RANK_MARGINAL);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstutils_p.h b/src/plugins/multimedia/gstreamer/common/qgstutils_p.h
new file mode 100644
index 000000000..c65fcf090
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstutils_p.h
@@ -0,0 +1,41 @@
+// 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
+
+//
+// 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 <gst/gstsample.h>
+#include <gst/gstbuffer.h>
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioFormat;
+class QGstCaps;
+class QVideoFrame;
+
+namespace QGstUtils {
+QAudioFormat audioFormatForSample(GstSample *sample);
+QAudioFormat audioFormatForCaps(const QGstCaps &caps);
+QGstCaps capsForAudioFormat(const QAudioFormat &format);
+
+void setFrameTimeStampsFromBuffer(QVideoFrame *frame, GstBuffer *buffer);
+} // namespace QGstUtils
+
+GList *qt_gst_video_sinks();
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/gstreamer/common/qgstvideobuffer.cpp b/src/plugins/multimedia/gstreamer/common/qgstvideobuffer.cpp
new file mode 100644
index 000000000..be6342ea8
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstvideobuffer.cpp
@@ -0,0 +1,393 @@
+// 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"
+#include <private/qvideotexturehelper_p.h>
+#include <qpa/qplatformnativeinterface.h>
+#include <qguiapplication.h>
+
+#include <gst/video/video.h>
+#include <gst/video/video-frame.h>
+#include <gst/video/gstvideometa.h>
+#include <gst/pbutils/gstpluginsbaseversion.h>
+
+#include <common/qgstutils_p.h>
+
+#if QT_CONFIG(gstreamer_gl)
+# 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>
+
+# if QT_CONFIG(linux_dmabuf)
+# include <gst/allocators/gstdmabuf.h>
+# endif
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// keep things building without drm_fourcc.h
+#define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \
+ ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
+
+#define DRM_FORMAT_RGBA8888 fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */
+#define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */
+#define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */
+#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */
+#define DRM_FORMAT_BGR888 fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */
+#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */
+#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */
+#define DRM_FORMAT_R16 fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */
+#define DRM_FORMAT_RGB565 fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */
+#define DRM_FORMAT_RG1616 fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */
+#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(QGstBufferHandle buffer, const GstVideoInfo &info,
+ QGstreamerVideoSink *sink, const QVideoFrameFormat &frameFormat,
+ QGstCaps::MemoryFormat format)
+ : QHwVideoBuffer((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))
+{
+ if (sink) {
+ eglDisplay = sink->eglDisplay();
+ eglImageTargetTexture2D = sink->eglImageTargetTexture2D();
+ }
+
+#if !QT_CONFIG(gstreamer_gl)
+ Q_UNUSED(memoryFormat);
+#endif
+}
+
+QGstVideoBuffer::~QGstVideoBuffer()
+{
+ unmap();
+}
+
+QAbstractVideoBuffer::MapData QGstVideoBuffer::map(QtVideo::MapMode mode)
+{
+ const GstMapFlags flags = GstMapFlags(
+ ((mode & QtVideo::MapMode::ReadOnly ) == QtVideo::MapMode::NotMapped ? 0 : GST_MAP_READ)
+ | ((mode & QtVideo::MapMode::WriteOnly) == QtVideo::MapMode::NotMapped ? 0 : GST_MAP_WRITE));
+
+ MapData mapData;
+ if (mode == QtVideo::MapMode::NotMapped || m_mode != QtVideo::MapMode::NotMapped)
+ return mapData;
+
+ if (m_videoInfo.finfo->n_planes == 0) { // Encoded
+ if (gst_buffer_map(m_buffer.get(), &m_frame.map[0], flags)) {
+ mapData.planeCount = 1;
+ mapData.bytesPerLine[0] = -1;
+ mapData.dataSize[0] = m_frame.map[0].size;
+ mapData.data[0] = static_cast<uchar *>(m_frame.map[0].data);
+
+ m_mode = mode;
+ }
+ } else if (gst_video_frame_map(&m_frame, &m_videoInfo, m_buffer.get(), flags)) {
+ mapData.planeCount = GST_VIDEO_FRAME_N_PLANES(&m_frame);
+
+ for (guint i = 0; i < GST_VIDEO_FRAME_N_PLANES(&m_frame); ++i) {
+ mapData.bytesPerLine[i] = GST_VIDEO_FRAME_PLANE_STRIDE(&m_frame, i);
+ mapData.data[i] = static_cast<uchar *>(GST_VIDEO_FRAME_PLANE_DATA(&m_frame, i));
+ mapData.dataSize[i] = mapData.bytesPerLine[i]*GST_VIDEO_FRAME_COMP_HEIGHT(&m_frame, i);
+ }
+
+ m_mode = mode;
+ }
+ return mapData;
+}
+
+void QGstVideoBuffer::unmap()
+{
+ if (m_mode != QtVideo::MapMode::NotMapped) {
+ if (m_videoInfo.finfo->n_planes == 0)
+ gst_buffer_unmap(m_buffer.get(), &m_frame.map[0]);
+ else
+ gst_video_frame_unmap(&m_frame);
+ }
+ m_mode = QtVideo::MapMode::NotMapped;
+}
+
+#if QT_CONFIG(gstreamer_gl) && QT_CONFIG(linux_dmabuf)
+static int
+fourccFromVideoInfo(const GstVideoInfo * info, int plane)
+{
+ GstVideoFormat format = GST_VIDEO_INFO_FORMAT (info);
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ const gint rgba_fourcc = DRM_FORMAT_ABGR8888;
+ const gint rgb_fourcc = DRM_FORMAT_BGR888;
+ const gint rg_fourcc = DRM_FORMAT_GR88;
+#else
+ const gint rgba_fourcc = DRM_FORMAT_RGBA8888;
+ const gint rgb_fourcc = DRM_FORMAT_RGB888;
+ const gint rg_fourcc = DRM_FORMAT_RG88;
+#endif
+
+ GST_DEBUG ("Getting DRM fourcc for %s plane %i",
+ gst_video_format_to_string (format), plane);
+
+ switch (format) {
+ case GST_VIDEO_FORMAT_RGB16:
+ case GST_VIDEO_FORMAT_BGR16:
+ return DRM_FORMAT_RGB565;
+
+ case GST_VIDEO_FORMAT_RGB:
+ case GST_VIDEO_FORMAT_BGR:
+ return rgb_fourcc;
+
+ case GST_VIDEO_FORMAT_RGBA:
+ case GST_VIDEO_FORMAT_RGBx:
+ case GST_VIDEO_FORMAT_BGRA:
+ case GST_VIDEO_FORMAT_BGRx:
+ case GST_VIDEO_FORMAT_ARGB:
+ case GST_VIDEO_FORMAT_xRGB:
+ case GST_VIDEO_FORMAT_ABGR:
+ case GST_VIDEO_FORMAT_xBGR:
+ case GST_VIDEO_FORMAT_AYUV:
+#if GST_CHECK_PLUGINS_BASE_VERSION(1,16,0)
+ case GST_VIDEO_FORMAT_VUYA:
+#endif
+ return rgba_fourcc;
+
+ case GST_VIDEO_FORMAT_GRAY8:
+ return DRM_FORMAT_R8;
+
+ case GST_VIDEO_FORMAT_YUY2:
+ case GST_VIDEO_FORMAT_UYVY:
+ case GST_VIDEO_FORMAT_GRAY16_LE:
+ case GST_VIDEO_FORMAT_GRAY16_BE:
+ return rg_fourcc;
+
+ case GST_VIDEO_FORMAT_NV12:
+ case GST_VIDEO_FORMAT_NV21:
+ return plane == 0 ? DRM_FORMAT_R8 : rg_fourcc;
+
+ case GST_VIDEO_FORMAT_I420:
+ case GST_VIDEO_FORMAT_YV12:
+ case GST_VIDEO_FORMAT_Y41B:
+ case GST_VIDEO_FORMAT_Y42B:
+ case GST_VIDEO_FORMAT_Y444:
+ return DRM_FORMAT_R8;
+
+#if GST_CHECK_PLUGINS_BASE_VERSION(1,16,0)
+ case GST_VIDEO_FORMAT_BGR10A2_LE:
+ return DRM_FORMAT_BGRA1010102;
+#endif
+
+// case GST_VIDEO_FORMAT_RGB10A2_LE:
+// return DRM_FORMAT_RGBA1010102;
+
+ case GST_VIDEO_FORMAT_P010_10LE:
+// case GST_VIDEO_FORMAT_P012_LE:
+// case GST_VIDEO_FORMAT_P016_LE:
+ return plane == 0 ? DRM_FORMAT_R16 : DRM_FORMAT_GR1616;
+
+ case GST_VIDEO_FORMAT_P010_10BE:
+// case GST_VIDEO_FORMAT_P012_BE:
+// case GST_VIDEO_FORMAT_P016_BE:
+ return plane == 0 ? DRM_FORMAT_R16 : DRM_FORMAT_RG1616;
+
+ default:
+ GST_ERROR ("Unsupported format for DMABuf.");
+ return -1;
+ }
+}
+#endif
+
+#if QT_CONFIG(gstreamer_gl)
+struct GlTextures
+{
+ uint count = 0;
+ bool owned = false;
+ std::array<guint32, QVideoTextureHelper::TextureDescription::maxPlanes> names{};
+};
+
+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)
+static GlTextures mapFromDmaBuffer(QRhi *rhi, const QGstBufferHandle &bufferHandle,
+ GstVideoFrame &frame, GstVideoInfo &videoInfo,
+ Qt::HANDLE eglDisplay, QFunctionPointer eglImageTargetTexture2D)
+{
+ GstBuffer *buffer = bufferHandle.get();
+
+ Q_ASSERT(gst_is_dmabuf_memory(gst_buffer_peek_memory(buffer, 0)));
+ Q_ASSERT(eglDisplay);
+ Q_ASSERT(eglImageTargetTexture2D);
+
+ 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();
+ }
+ // 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
+
+std::unique_ptr<QVideoFrameTextures> QGstVideoBuffer::mapTextures(QRhi *rhi)
+{
+ if (!rhi)
+ return {};
+
+#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
new file mode 100644
index 000000000..573a4662c
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstvideobuffer_p.h
@@ -0,0 +1,55 @@
+// 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
+
+//
+// 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/qhwvideobuffer_p.h>
+#include <QtCore/qvariant.h>
+
+#include <common/qgst_p.h>
+#include <gst/video/video.h>
+
+QT_BEGIN_NAMESPACE
+class QVideoFrameFormat;
+class QGstreamerVideoSink;
+class QOpenGLContext;
+
+class QGstVideoBuffer final : public QHwVideoBuffer
+{
+public:
+ QGstVideoBuffer(QGstBufferHandle buffer, const GstVideoInfo &info, QGstreamerVideoSink *sink,
+ const QVideoFrameFormat &frameFormat, QGstCaps::MemoryFormat format);
+ ~QGstVideoBuffer();
+
+ MapData map(QtVideo::MapMode mode) override;
+ void unmap() override;
+
+ std::unique_ptr<QVideoFrameTextures> mapTextures(QRhi *) override;
+
+private:
+ const QGstCaps::MemoryFormat memoryFormat = QGstCaps::CpuMemory;
+ const QVideoFrameFormat m_frameFormat;
+ QRhi *m_rhi = nullptr;
+ mutable GstVideoInfo m_videoInfo;
+ mutable GstVideoFrame m_frame{};
+ const QGstBufferHandle m_buffer;
+ QtVideo::MapMode m_mode = QtVideo::MapMode::NotMapped;
+ Qt::HANDLE eglDisplay = nullptr;
+ QFunctionPointer eglImageTargetTexture2D = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/gstreamer/common/qgstvideorenderersink.cpp b/src/plugins/multimedia/gstreamer/common/qgstvideorenderersink.cpp
new file mode 100644
index 000000000..f9c936ea6
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstvideorenderersink.cpp
@@ -0,0 +1,499 @@
+// 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 <private/qvideoframe_p.h>
+
+#include <gst/video/video.h>
+#include <gst/video/gstvideometa.h>
+
+
+#include <rhi/qrhi.h>
+#if QT_CONFIG(gstreamer_gl)
+#include <gst/gl/gl.h>
+#endif // #if QT_CONFIG(gstreamer_gl)
+
+// DMA support
+#if QT_CONFIG(linux_dmabuf)
+#include <gst/allocators/gstdmabuf.h>
+#endif
+
+static Q_LOGGING_CATEGORY(qLcGstVideoRenderer, "qt.multimedia.gstvideorenderer")
+
+QT_BEGIN_NAMESPACE
+
+QGstVideoRenderer::QGstVideoRenderer(QGstreamerVideoSink *sink)
+ : m_sink(sink), m_surfaceCaps(createSurfaceCaps(sink))
+{
+ QObject::connect(
+ sink, &QGstreamerVideoSink::aboutToBeDestroyed, this,
+ [this] {
+ QMutexLocker locker(&m_sinkMutex);
+ m_sink = nullptr;
+ },
+ Qt::DirectConnection);
+}
+
+QGstVideoRenderer::~QGstVideoRenderer() = default;
+
+QGstCaps QGstVideoRenderer::createSurfaceCaps([[maybe_unused]] QGstreamerVideoSink *sink)
+{
+ QGstCaps caps = QGstCaps::create();
+
+ // All the formats that both we and gstreamer support
+ auto formats = QList<QVideoFrameFormat::PixelFormat>()
+ << QVideoFrameFormat::Format_YUV420P
+ << QVideoFrameFormat::Format_YUV422P
+ << QVideoFrameFormat::Format_YV12
+ << QVideoFrameFormat::Format_UYVY
+ << QVideoFrameFormat::Format_YUYV
+ << QVideoFrameFormat::Format_NV12
+ << QVideoFrameFormat::Format_NV21
+ << QVideoFrameFormat::Format_AYUV
+ << QVideoFrameFormat::Format_P010
+ << QVideoFrameFormat::Format_XRGB8888
+ << QVideoFrameFormat::Format_XBGR8888
+ << QVideoFrameFormat::Format_RGBX8888
+ << QVideoFrameFormat::Format_BGRX8888
+ << QVideoFrameFormat::Format_ARGB8888
+ << QVideoFrameFormat::Format_ABGR8888
+ << QVideoFrameFormat::Format_RGBA8888
+ << QVideoFrameFormat::Format_BGRA8888
+ << QVideoFrameFormat::Format_Y8
+ << 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 (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>()
+ << QVideoFrameFormat::Format_UYVY
+ << QVideoFrameFormat::Format_YUYV
+ << QVideoFrameFormat::Format_AYUV
+ << QVideoFrameFormat::Format_XRGB8888
+ << QVideoFrameFormat::Format_XBGR8888
+ << QVideoFrameFormat::Format_RGBX8888
+ << QVideoFrameFormat::Format_BGRX8888
+ << QVideoFrameFormat::Format_ARGB8888
+ << QVideoFrameFormat::Format_ABGR8888
+ << QVideoFrameFormat::Format_RGBA8888
+ << QVideoFrameFormat::Format_BGRA8888
+ << QVideoFrameFormat::Format_Y8
+ << QVideoFrameFormat::Format_Y16
+ ;
+ caps.addPixelFormats(singlePlaneFormats, GST_CAPS_FEATURE_MEMORY_DMABUF);
+ }
+#endif
+ }
+#endif
+ caps.addPixelFormats(formats);
+ return caps;
+}
+
+const QGstCaps &QGstVideoRenderer::caps()
+{
+ return m_surfaceCaps;
+}
+
+bool QGstVideoRenderer::start(const QGstCaps& caps)
+{
+ qCDebug(qLcGstVideoRenderer) << "QGstVideoRenderer::start" << caps;
+
+ {
+ m_frameRotationAngle = QtVideo::Rotation::None;
+ auto optionalFormatAndVideoInfo = caps.formatAndVideoInfo();
+ if (optionalFormatAndVideoInfo) {
+ std::tie(m_format, m_videoInfo) = std::move(*optionalFormatAndVideoInfo);
+ } else {
+ m_format = {};
+ m_videoInfo = {};
+ }
+ m_memoryFormat = caps.memoryFormat();
+ }
+
+ return true;
+}
+
+void QGstVideoRenderer::stop()
+{
+ qCDebug(qLcGstVideoRenderer) << "QGstVideoRenderer::stop";
+
+ QMetaObject::invokeMethod(this, [this] {
+ m_currentState.buffer = {};
+ m_sink->setVideoFrame(QVideoFrame{});
+ return;
+ });
+}
+
+void QGstVideoRenderer::unlock()
+{
+ qCDebug(qLcGstVideoRenderer) << "QGstVideoRenderer::unlock";
+}
+
+bool QGstVideoRenderer::proposeAllocation(GstQuery *)
+{
+ qCDebug(qLcGstVideoRenderer) << "QGstVideoRenderer::proposeAllocation";
+ return true;
+}
+
+GstFlowReturn QGstVideoRenderer::render(GstBuffer *buffer)
+{
+ qCDebug(qLcGstVideoRenderer) << "QGstVideoRenderer::render";
+
+ GstVideoCropMeta *meta = gst_buffer_get_video_crop_meta(buffer);
+ if (meta) {
+ QRect vp(meta->x, meta->y, meta->width, meta->height);
+ if (m_format.viewport() != vp) {
+ qCDebug(qLcGstVideoRenderer)
+ << Q_FUNC_INFO << " Update viewport on Metadata: [" << meta->height << "x"
+ << meta->width << " | " << meta->x << "x" << meta->y << "]";
+ // Update viewport if data is not the same
+ m_format.setViewport(vp);
+ }
+ }
+
+ RenderBufferState state{
+ .buffer = QGstBufferHandle{ buffer, QGstBufferHandle::NeedsRef },
+ .format = m_format,
+ .memoryFormat = m_memoryFormat,
+ .mirrored = m_frameMirrored,
+ .rotationAngle = m_frameRotationAngle,
+ };
+
+ qCDebug(qLcGstVideoRenderer) << " sending video frame";
+
+ QMetaObject::invokeMethod(this, [this, state = std::move(state)]() mutable {
+ if (state == m_currentState) {
+ // same buffer received twice
+ if (!m_sink || !m_sink->inStoppedState())
+ return;
+
+ qCDebug(qLcGstVideoRenderer) << " showing empty video frame";
+ m_currentVideoFrame = {};
+ m_sink->setVideoFrame(m_currentVideoFrame);
+ m_currentState = {};
+ return;
+ }
+
+ auto videoBuffer = std::make_unique<QGstVideoBuffer>(state.buffer, m_videoInfo, m_sink,
+ state.format, state.memoryFormat);
+ QVideoFrame frame = QVideoFramePrivate::createFrame(std::move(videoBuffer), state.format);
+ QGstUtils::setFrameTimeStampsFromBuffer(&frame, state.buffer.get());
+ frame.setMirrored(state.mirrored);
+ frame.setRotation(state.rotationAngle);
+ m_currentVideoFrame = std::move(frame);
+ m_currentState = std::move(state);
+
+ if (!m_sink)
+ return;
+
+ if (m_sink->inStoppedState()) {
+ qCDebug(qLcGstVideoRenderer) << " showing empty video frame";
+ m_currentVideoFrame = {};
+ }
+
+ m_sink->setVideoFrame(m_currentVideoFrame);
+ });
+
+ return GST_FLOW_OK;
+}
+
+bool QGstVideoRenderer::query(GstQuery *query)
+{
+#if QT_CONFIG(gstreamer_gl)
+ if (GST_QUERY_TYPE(query) == GST_QUERY_CONTEXT) {
+ const gchar *type;
+ gst_query_parse_context_type(query, &type);
+
+ if (strcmp(type, "gst.gl.local_context") != 0)
+ return false;
+
+ QMutexLocker locker(&m_sinkMutex);
+ if (!m_sink)
+ return false;
+
+ auto *gstGlContext = m_sink->gstGlLocalContext();
+ if (!gstGlContext)
+ return false;
+
+ gst_query_set_context(query, gstGlContext);
+
+ return true;
+ }
+#else
+ Q_UNUSED(query);
+#endif
+ return false;
+}
+
+void QGstVideoRenderer::gstEvent(GstEvent *event)
+{
+ 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;
+
+ QGString value;
+ if (!gst_tag_list_get_string(taglist, GST_TAG_IMAGE_ORIENTATION, &value))
+ return;
+
+ constexpr const char rotate[] = "rotate-";
+ constexpr const char flipRotate[] = "flip-rotate-";
+ constexpr size_t rotateLen = sizeof(rotate) - 1;
+ constexpr size_t flipRotateLen = sizeof(flipRotate) - 1;
+
+ bool mirrored = false;
+ int rotationAngle = 0;
+
+ 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.get() + flipRotateLen)) % 360;
+ }
+
+ m_frameMirrored = mirrored;
+ switch (rotationAngle) {
+ 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();
+}
+
+static GstVideoSinkClass *gvrs_sink_parent_class;
+static thread_local QGstreamerVideoSink *gvrs_current_sink;
+
+#define VO_SINK(s) QGstVideoRendererSink *sink(reinterpret_cast<QGstVideoRendererSink *>(s))
+
+QGstVideoRendererSink *QGstVideoRendererSink::createSink(QGstreamerVideoSink *sink)
+{
+ setSink(sink);
+ QGstVideoRendererSink *gstSink = reinterpret_cast<QGstVideoRendererSink *>(
+ g_object_new(QGstVideoRendererSink::get_type(), nullptr));
+
+ return gstSink;
+}
+
+void QGstVideoRendererSink::setSink(QGstreamerVideoSink *sink)
+{
+ gvrs_current_sink = sink;
+}
+
+GType QGstVideoRendererSink::get_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;
+}
+
+void QGstVideoRendererSink::class_init(gpointer g_class, gpointer class_data)
+{
+ Q_UNUSED(class_data);
+
+ 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;
+
+ GstBaseSinkClass *base_sink_class = reinterpret_cast<GstBaseSinkClass *>(g_class);
+ base_sink_class->get_caps = QGstVideoRendererSink::get_caps;
+ base_sink_class->set_caps = QGstVideoRendererSink::set_caps;
+ base_sink_class->propose_allocation = QGstVideoRendererSink::propose_allocation;
+ base_sink_class->stop = QGstVideoRendererSink::stop;
+ base_sink_class->unlock = QGstVideoRendererSink::unlock;
+ base_sink_class->query = QGstVideoRendererSink::query;
+ base_sink_class->event = QGstVideoRendererSink::event;
+
+ GstElementClass *element_class = reinterpret_cast<GstElementClass *>(g_class);
+ element_class->change_state = QGstVideoRendererSink::change_state;
+ gst_element_class_set_metadata(element_class,
+ "Qt built-in video renderer sink",
+ "Sink/Video",
+ "Qt default built-in video renderer sink",
+ "The Qt Company");
+
+ GObjectClass *object_class = reinterpret_cast<GObjectClass *>(g_class);
+ object_class->finalize = QGstVideoRendererSink::finalize;
+}
+
+void QGstVideoRendererSink::base_init(gpointer g_class)
+{
+ static GstStaticPadTemplate sink_pad_template = GST_STATIC_PAD_TEMPLATE(
+ "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS(
+ "video/x-raw, "
+ "framerate = (fraction) [ 0, MAX ], "
+ "width = (int) [ 1, MAX ], "
+ "height = (int) [ 1, MAX ]"));
+
+ gst_element_class_add_pad_template(
+ GST_ELEMENT_CLASS(g_class), gst_static_pad_template_get(&sink_pad_template));
+}
+
+void QGstVideoRendererSink::instance_init(GTypeInstance *instance, gpointer g_class)
+{
+ Q_UNUSED(g_class);
+ VO_SINK(instance);
+
+ Q_ASSERT(gvrs_current_sink);
+
+ sink->renderer = new QGstVideoRenderer(gvrs_current_sink);
+ sink->renderer->moveToThread(gvrs_current_sink->thread());
+ gvrs_current_sink = nullptr;
+}
+
+void QGstVideoRendererSink::finalize(GObject *object)
+{
+ VO_SINK(object);
+
+ delete sink->renderer;
+
+ // Chain up
+ G_OBJECT_CLASS(gvrs_sink_parent_class)->finalize(object);
+}
+
+GstStateChangeReturn QGstVideoRendererSink::change_state(
+ GstElement *element, GstStateChange transition)
+{
+ return GST_ELEMENT_CLASS(gvrs_sink_parent_class)->change_state(element, transition);
+}
+
+GstCaps *QGstVideoRendererSink::get_caps(GstBaseSink *base, GstCaps *filter)
+{
+ VO_SINK(base);
+
+ QGstCaps caps = sink->renderer->caps();
+ if (filter)
+ caps = QGstCaps(gst_caps_intersect(caps.caps(), filter), QGstCaps::HasRef);
+
+ return caps.release();
+}
+
+gboolean QGstVideoRendererSink::set_caps(GstBaseSink *base, GstCaps *gcaps)
+{
+ VO_SINK(base);
+ auto caps = QGstCaps(gcaps, QGstCaps::NeedsRef);
+
+ qCDebug(qLcGstVideoRenderer) << "set_caps:" << caps;
+
+ if (caps.isNull()) {
+ sink->renderer->stop();
+ return TRUE;
+ }
+
+ return sink->renderer->start(caps);
+}
+
+gboolean QGstVideoRendererSink::propose_allocation(GstBaseSink *base, GstQuery *query)
+{
+ VO_SINK(base);
+ return sink->renderer->proposeAllocation(query);
+}
+
+gboolean QGstVideoRendererSink::stop(GstBaseSink *base)
+{
+ VO_SINK(base);
+ sink->renderer->stop();
+ return TRUE;
+}
+
+gboolean QGstVideoRendererSink::unlock(GstBaseSink *base)
+{
+ VO_SINK(base);
+ sink->renderer->unlock();
+ return TRUE;
+}
+
+GstFlowReturn QGstVideoRendererSink::show_frame(GstVideoSink *base, GstBuffer *buffer)
+{
+ VO_SINK(base);
+ return sink->renderer->render(buffer);
+}
+
+gboolean QGstVideoRendererSink::query(GstBaseSink *base, GstQuery *query)
+{
+ VO_SINK(base);
+ if (sink->renderer->query(query))
+ return TRUE;
+
+ 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(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
new file mode 100644
index 000000000..d9e3db462
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstvideorenderersink_p.h
@@ -0,0 +1,138 @@
+// 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
+
+//
+// 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/qvideoframeformat.h>
+#include <QtMultimedia/qvideoframe.h>
+#include <QtMultimedia/private/qtmultimediaglobal_p.h>
+#include <QtCore/qmutex.h>
+
+#include <gst/video/gstvideosink.h>
+#include <gst/video/video.h>
+
+#include <QtCore/qlist.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qqueue.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qwaitcondition.h>
+#include <qvideoframeformat.h>
+#include <qvideoframe.h>
+#include <common/qgstvideobuffer_p.h>
+#include <common/qgst_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGstVideoRenderer : public QObject
+{
+public:
+ explicit QGstVideoRenderer(QGstreamerVideoSink *);
+ ~QGstVideoRenderer();
+
+ const QGstCaps &caps();
+
+ bool start(const QGstCaps &);
+ void stop();
+ void unlock();
+ bool proposeAllocation(GstQuery *);
+ GstFlowReturn render(GstBuffer *);
+ bool query(GstQuery *);
+ void gstEvent(GstEvent *);
+
+private:
+ void notify();
+ static QGstCaps createSurfaceCaps(QGstreamerVideoSink *);
+
+ void gstEventHandleTag(GstEvent *);
+ void gstEventHandleEOS(GstEvent *);
+
+ QMutex m_sinkMutex;
+ QGstreamerVideoSink *m_sink = nullptr; // written only from qt thread. so only readers on
+ // worker threads need to acquire the lock
+
+ // --- only accessed from gstreamer thread
+ const QGstCaps m_surfaceCaps;
+ QVideoFrameFormat m_format;
+ GstVideoInfo m_videoInfo{};
+ QGstCaps::MemoryFormat m_memoryFormat = QGstCaps::CpuMemory;
+ bool m_frameMirrored = false;
+ QtVideo::Rotation m_frameRotationAngle = QtVideo::Rotation::None;
+
+ // --- only accessed from qt thread
+ QVideoFrame m_currentVideoFrame;
+
+ struct RenderBufferState
+ {
+ QGstBufferHandle buffer;
+ QVideoFrameFormat format;
+ QGstCaps::MemoryFormat memoryFormat;
+ bool mirrored;
+ QtVideo::Rotation rotationAngle;
+
+ bool operator==(const RenderBufferState &rhs) const
+ {
+ return std::tie(buffer, format, memoryFormat, mirrored, rotationAngle)
+ == std::tie(rhs.buffer, rhs.format, rhs.memoryFormat, rhs.mirrored,
+ rhs.rotationAngle);
+ }
+ };
+ RenderBufferState m_currentState;
+};
+
+class QGstVideoRendererSink
+{
+public:
+ GstVideoSink parent{};
+
+ static QGstVideoRendererSink *createSink(QGstreamerVideoSink *surface);
+ static void setSink(QGstreamerVideoSink *surface);
+
+private:
+ static GType get_type();
+ static void class_init(gpointer g_class, gpointer class_data);
+ static void base_init(gpointer g_class);
+ static void instance_init(GTypeInstance *instance, gpointer g_class);
+
+ static void finalize(GObject *object);
+
+ static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition);
+
+ static GstCaps *get_caps(GstBaseSink *sink, GstCaps *filter);
+ static gboolean set_caps(GstBaseSink *sink, GstCaps *caps);
+
+ static gboolean propose_allocation(GstBaseSink *sink, GstQuery *query);
+
+ static gboolean stop(GstBaseSink *sink);
+
+ static gboolean unlock(GstBaseSink *sink);
+
+ static GstFlowReturn show_frame(GstVideoSink *sink, GstBuffer *buffer);
+ static gboolean query(GstBaseSink *element, GstQuery *query);
+ static gboolean event(GstBaseSink *element, GstEvent * event);
+
+private:
+ QGstVideoRenderer *renderer = nullptr;
+};
+
+
+class QGstVideoRendererSinkClass
+{
+public:
+ GstVideoSinkClass parent_class;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/gstreamer/gstreamer.json b/src/plugins/multimedia/gstreamer/gstreamer.json
new file mode 100644
index 000000000..6a709d9f4
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/gstreamer.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "gstreamer" ]
+}
diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera.cpp b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera.cpp
new file mode 100644
index 000000000..c54e8b74b
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera.cpp
@@ -0,0 +1,771 @@
+// 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 <QtMultimedia/private/qcameradevice_p.h>
+#include <QtCore/qdebug.h>
+
+#include <common/qgst_debug_p.h>
+#include <qgstreamervideodevices_p.h>
+#include <qgstreamerintegration_p.h>
+
+#if QT_CONFIG(linux_v4l)
+#include <linux/videodev2.h>
+#include <private/qcore_unix_p.h>
+#endif
+
+
+QT_BEGIN_NAMESPACE
+
+QMaybe<QPlatformCamera *> QGstreamerCamera::create(QCamera *camera)
+{
+ static const auto error = qGstErrorMessageIfElementsNotAvailable(
+ "videotestsrc", "capsfilter", "videoconvert", "videoscale", "identity");
+ if (error)
+ return *error;
+
+ return new QGstreamerCamera(camera);
+}
+
+QGstreamerCamera::QGstreamerCamera(QCamera *camera)
+ : QGstreamerCameraBase(camera),
+ gstCameraBin{
+ QGstBin::create("camerabin"),
+ },
+ gstCamera{
+ QGstElement::createFromFactory("videotestsrc"),
+ },
+ gstCapsFilter{
+ QGstElement::createFromFactory("capsfilter", "videoCapsFilter"),
+ },
+ gstDecode{
+ QGstElement::createFromFactory("identity"),
+ },
+ gstVideoConvert{
+ QGstElement::createFromFactory("videoconvert", "videoConvert"),
+ },
+ gstVideoScale{
+ QGstElement::createFromFactory("videoscale", "videoScale"),
+ }
+{
+ gstCameraBin.add(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert, gstVideoScale);
+ qLinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert, gstVideoScale);
+ gstCameraBin.addGhostPad(gstVideoScale, "src");
+}
+
+QGstreamerCamera::~QGstreamerCamera()
+{
+ gstCameraBin.setStateSync(GST_STATE_NULL);
+}
+
+bool QGstreamerCamera::isActive() const
+{
+ return m_active;
+}
+
+void QGstreamerCamera::setActive(bool active)
+{
+ if (m_active == active)
+ return;
+ if (m_cameraDevice.isNull() && active)
+ return;
+
+ m_active = active;
+
+ emit activeChanged(active);
+}
+
+void QGstreamerCamera::setCamera(const QCameraDevice &camera)
+{
+ using namespace Qt::Literals;
+
+ if (m_cameraDevice == camera)
+ return;
+
+ m_cameraDevice = camera;
+
+ QGstElement gstNewCamera;
+ if (camera.isNull()) {
+ gstNewCamera = QGstElement::createFromFactory("videotestsrc");
+ } else {
+ auto *integration = static_cast<QGstreamerIntegration *>(QGstreamerIntegration::instance());
+ GstDevice *device = integration->videoDevice(camera.id());
+
+ if (!device) {
+ updateError(QCamera::Error::CameraError,
+ u"Failed to create GstDevice for camera: "_s
+ + QString::fromUtf8(camera.id()));
+ return;
+ }
+
+ gstNewCamera = QGstElement::createFromDevice(device, "camerasrc");
+ QUniqueGstStructureHandle properties{
+ gst_device_get_properties(device),
+ };
+
+ if (properties) {
+ QGstStructureView propertiesView{ properties };
+ if (propertiesView.name() == "v4l2deviceprovider")
+ m_v4l2DevicePath = QString::fromUtf8(propertiesView["device.path"].toString());
+ }
+ }
+
+ QCameraFormat f = findBestCameraFormat(camera);
+ auto caps = QGstCaps::fromCameraFormat(f);
+ auto gstNewDecode = QGstElement::createFromFactory(
+ f.pixelFormat() == QVideoFrameFormat::Format_Jpeg ? "jpegdec" : "identity");
+
+ QGstPipeline::modifyPipelineWhileNotRunning(gstCamera.getPipeline(), [&] {
+ gstCamera.setStateSync(GST_STATE_READY); // stop camera, as it may have active tasks
+
+ qUnlinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
+ gstCameraBin.stopAndRemoveElements(gstCamera, gstDecode);
+
+ gstCapsFilter.set("caps", caps);
+
+ gstCamera = std::move(gstNewCamera);
+ gstDecode = std::move(gstNewDecode);
+
+ gstCameraBin.add(gstCamera, gstDecode);
+ qLinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
+
+ gstCameraBin.syncChildrenState();
+ });
+
+ updateCameraProperties();
+}
+
+bool QGstreamerCamera::setCameraFormat(const QCameraFormat &format)
+{
+ if (!format.isNull() && !m_cameraDevice.videoFormats().contains(format))
+ return false;
+
+ QCameraFormat f = format;
+ if (f.isNull())
+ f = findBestCameraFormat(m_cameraDevice);
+
+ auto caps = QGstCaps::fromCameraFormat(f);
+
+ auto newGstDecode = QGstElement::createFromFactory(
+ f.pixelFormat() == QVideoFrameFormat::Format_Jpeg ? "jpegdec" : "identity");
+
+ QGstPipeline::modifyPipelineWhileNotRunning(gstCamera.getPipeline(), [&] {
+ gstCamera.setStateSync(GST_STATE_READY); // stop camera, as it may have active tasks
+
+ qUnlinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
+ gstCameraBin.stopAndRemoveElements(gstDecode);
+
+ gstCapsFilter.set("caps", caps);
+
+ gstDecode = std::move(newGstDecode);
+
+ gstCameraBin.add(gstDecode);
+ qLinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
+ gstCameraBin.syncChildrenState();
+ });
+
+ return true;
+}
+
+void QGstreamerCamera::updateCameraProperties()
+{
+#if QT_CONFIG(linux_v4l)
+ if (isV4L2Camera()) {
+ initV4L2Controls();
+ return;
+ }
+#endif
+#if QT_CONFIG(gstreamer_photography)
+ if (auto *p = photography())
+ gst_photography_set_white_balance_mode(p, GST_PHOTOGRAPHY_WB_MODE_AUTO);
+ QCamera::Features f = QCamera::Feature::ColorTemperature | QCamera::Feature::ExposureCompensation |
+ QCamera::Feature::IsoSensitivity | QCamera::Feature::ManualExposureTime;
+ supportedFeaturesChanged(f);
+#endif
+
+}
+
+#if QT_CONFIG(gstreamer_photography)
+GstPhotography *QGstreamerCamera::photography() const
+{
+ if (!gstCamera.isNull() && GST_IS_PHOTOGRAPHY(gstCamera.element()))
+ return GST_PHOTOGRAPHY(gstCamera.element());
+ return nullptr;
+}
+#endif
+
+void QGstreamerCamera::setFocusMode(QCamera::FocusMode mode)
+{
+ if (mode == focusMode())
+ return;
+
+#if QT_CONFIG(gstreamer_photography)
+ auto p = photography();
+ if (p) {
+ GstPhotographyFocusMode photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_CONTINUOUS_NORMAL;
+
+ switch (mode) {
+ case QCamera::FocusModeAutoNear:
+ photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_MACRO;
+ break;
+ case QCamera::FocusModeAutoFar:
+ // not quite, but hey :)
+ Q_FALLTHROUGH();
+ case QCamera::FocusModeHyperfocal:
+ photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_HYPERFOCAL;
+ break;
+ case QCamera::FocusModeInfinity:
+ photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_INFINITY;
+ break;
+ case QCamera::FocusModeManual:
+ photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_MANUAL;
+ break;
+ default: // QCamera::FocusModeAuto:
+ break;
+ }
+
+ if (gst_photography_set_focus_mode(p, photographyMode))
+ focusModeChanged(mode);
+ }
+#endif
+}
+
+bool QGstreamerCamera::isFocusModeSupported(QCamera::FocusMode mode) const
+{
+#if QT_CONFIG(gstreamer_photography)
+ if (photography())
+ return true;
+#endif
+ return mode == QCamera::FocusModeAuto;
+}
+
+void QGstreamerCamera::setFlashMode(QCamera::FlashMode mode)
+{
+ Q_UNUSED(mode);
+
+#if QT_CONFIG(gstreamer_photography)
+ if (auto *p = photography()) {
+ GstPhotographyFlashMode flashMode;
+ gst_photography_get_flash_mode(p, &flashMode);
+
+ switch (mode) {
+ case QCamera::FlashAuto:
+ flashMode = GST_PHOTOGRAPHY_FLASH_MODE_AUTO;
+ break;
+ case QCamera::FlashOff:
+ flashMode = GST_PHOTOGRAPHY_FLASH_MODE_OFF;
+ break;
+ case QCamera::FlashOn:
+ flashMode = GST_PHOTOGRAPHY_FLASH_MODE_ON;
+ break;
+ }
+
+ if (gst_photography_set_flash_mode(p, flashMode))
+ flashModeChanged(mode);
+ }
+#endif
+}
+
+bool QGstreamerCamera::isFlashModeSupported(QCamera::FlashMode mode) const
+{
+#if QT_CONFIG(gstreamer_photography)
+ if (photography())
+ return true;
+#endif
+
+ return mode == QCamera::FlashAuto;
+}
+
+bool QGstreamerCamera::isFlashReady() const
+{
+#if QT_CONFIG(gstreamer_photography)
+ if (photography())
+ return true;
+#endif
+
+ return false;
+}
+
+void QGstreamerCamera::setExposureMode(QCamera::ExposureMode mode)
+{
+ Q_UNUSED(mode);
+#if QT_CONFIG(linux_v4l)
+ if (isV4L2Camera() && v4l2AutoExposureSupported && v4l2ManualExposureSupported) {
+ if (mode != QCamera::ExposureAuto && mode != QCamera::ExposureManual)
+ return;
+ int value = QCamera::ExposureAuto ? V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL;
+ setV4L2Parameter(V4L2_CID_EXPOSURE_AUTO, value);
+ exposureModeChanged(mode);
+ return;
+ }
+#endif
+
+#if QT_CONFIG(gstreamer_photography)
+ auto *p = photography();
+ if (!p)
+ return;
+
+ GstPhotographySceneMode sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_AUTO;
+
+ switch (mode) {
+ case QCamera::ExposureManual:
+ sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_MANUAL;
+ break;
+ case QCamera::ExposurePortrait:
+ sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_PORTRAIT;
+ break;
+ case QCamera::ExposureSports:
+ sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SPORT;
+ break;
+ case QCamera::ExposureNight:
+ sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_NIGHT;
+ break;
+ case QCamera::ExposureAuto:
+ sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_AUTO;
+ break;
+ case QCamera::ExposureLandscape:
+ sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_LANDSCAPE;
+ break;
+ case QCamera::ExposureSnow:
+ sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SNOW;
+ break;
+ case QCamera::ExposureBeach:
+ sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_BEACH;
+ break;
+ case QCamera::ExposureAction:
+ sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_ACTION;
+ break;
+ case QCamera::ExposureNightPortrait:
+ sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_NIGHT_PORTRAIT;
+ break;
+ case QCamera::ExposureTheatre:
+ sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_THEATRE;
+ break;
+ case QCamera::ExposureSunset:
+ sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SUNSET;
+ break;
+ case QCamera::ExposureSteadyPhoto:
+ sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_STEADY_PHOTO;
+ break;
+ case QCamera::ExposureFireworks:
+ sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_FIREWORKS;
+ break;
+ case QCamera::ExposureParty:
+ sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_PARTY;
+ break;
+ case QCamera::ExposureCandlelight:
+ sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_CANDLELIGHT;
+ break;
+ case QCamera::ExposureBarcode:
+ sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_BARCODE;
+ break;
+ default:
+ return;
+ }
+
+ if (gst_photography_set_scene_mode(p, sceneMode))
+ exposureModeChanged(mode);
+#endif
+}
+
+bool QGstreamerCamera::isExposureModeSupported(QCamera::ExposureMode mode) const
+{
+ if (mode == QCamera::ExposureAuto)
+ return true;
+#if QT_CONFIG(linux_v4l)
+ if (isV4L2Camera() && v4l2ManualExposureSupported && v4l2AutoExposureSupported)
+ return mode == QCamera::ExposureManual;
+#endif
+#if QT_CONFIG(gstreamer_photography)
+ if (photography())
+ return true;
+#endif
+
+ return false;
+}
+
+void QGstreamerCamera::setExposureCompensation(float compensation)
+{
+ Q_UNUSED(compensation);
+#if QT_CONFIG(linux_v4l)
+ if (isV4L2Camera() && (v4l2MinExposureAdjustment != 0 || v4l2MaxExposureAdjustment != 0)) {
+ int value = qBound(v4l2MinExposureAdjustment, (int)(compensation*1000), v4l2MaxExposureAdjustment);
+ setV4L2Parameter(V4L2_CID_AUTO_EXPOSURE_BIAS, value);
+ exposureCompensationChanged(value/1000.);
+ return;
+ }
+#endif
+
+#if QT_CONFIG(gstreamer_photography)
+ if (auto *p = photography()) {
+ if (gst_photography_set_ev_compensation(p, compensation))
+ exposureCompensationChanged(compensation);
+ }
+#endif
+}
+
+void QGstreamerCamera::setManualIsoSensitivity(int iso)
+{
+ Q_UNUSED(iso);
+#if QT_CONFIG(linux_v4l)
+ if (isV4L2Camera()) {
+ if (!(supportedFeatures() & QCamera::Feature::IsoSensitivity))
+ return;
+ setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY_AUTO, iso <= 0 ? V4L2_ISO_SENSITIVITY_AUTO : V4L2_ISO_SENSITIVITY_MANUAL);
+ if (iso > 0) {
+ iso = qBound(minIso(), iso, maxIso());
+ setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY, iso);
+ }
+ return;
+ }
+#endif
+#if QT_CONFIG(gstreamer_photography)
+ if (auto *p = photography()) {
+ if (gst_photography_set_iso_speed(p, iso))
+ isoSensitivityChanged(iso);
+ }
+#endif
+}
+
+int QGstreamerCamera::isoSensitivity() const
+{
+#if QT_CONFIG(linux_v4l)
+ if (isV4L2Camera()) {
+ if (!(supportedFeatures() & QCamera::Feature::IsoSensitivity))
+ return -1;
+ return getV4L2Parameter(V4L2_CID_ISO_SENSITIVITY);
+ }
+#endif
+#if QT_CONFIG(gstreamer_photography)
+ if (auto *p = photography()) {
+ guint speed = 0;
+ if (gst_photography_get_iso_speed(p, &speed))
+ return speed;
+ }
+#endif
+ return 100;
+}
+
+void QGstreamerCamera::setManualExposureTime(float secs)
+{
+ Q_UNUSED(secs);
+#if QT_CONFIG(linux_v4l)
+ if (isV4L2Camera() && v4l2ManualExposureSupported && v4l2AutoExposureSupported) {
+ int exposure = qBound(v4l2MinExposure, qRound(secs*10000.), v4l2MaxExposure);
+ setV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE, exposure);
+ exposureTimeChanged(exposure/10000.);
+ return;
+ }
+#endif
+
+#if QT_CONFIG(gstreamer_photography)
+ if (auto *p = photography()) {
+ if (gst_photography_set_exposure(p, guint(secs*1000000)))
+ exposureTimeChanged(secs);
+ }
+#endif
+}
+
+float QGstreamerCamera::exposureTime() const
+{
+#if QT_CONFIG(linux_v4l)
+ if (isV4L2Camera()) {
+ return getV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE)/10000.;
+ }
+#endif
+#if QT_CONFIG(gstreamer_photography)
+ if (auto *p = photography()) {
+ guint32 exposure = 0;
+ if (gst_photography_get_exposure(p, &exposure))
+ return exposure/1000000.;
+ }
+#endif
+ return -1;
+}
+
+bool QGstreamerCamera::isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const
+{
+ if (mode == QCamera::WhiteBalanceAuto)
+ return true;
+
+#if QT_CONFIG(linux_v4l)
+ if (isV4L2Camera()) {
+ if (v4l2AutoWhiteBalanceSupported && v4l2ColorTemperatureSupported)
+ return true;
+ }
+#endif
+#if QT_CONFIG(gstreamer_photography)
+ if (auto *p = photography()) {
+ Q_UNUSED(p);
+ switch (mode) {
+ case QCamera::WhiteBalanceAuto:
+ case QCamera::WhiteBalanceSunlight:
+ case QCamera::WhiteBalanceCloudy:
+ case QCamera::WhiteBalanceShade:
+ case QCamera::WhiteBalanceSunset:
+ case QCamera::WhiteBalanceTungsten:
+ case QCamera::WhiteBalanceFluorescent:
+ return true;
+ case QCamera::WhiteBalanceManual: {
+#if GST_CHECK_VERSION(1, 18, 0)
+ GstPhotographyInterface *iface = GST_PHOTOGRAPHY_GET_INTERFACE(p);
+ if (iface->set_color_temperature && iface->get_color_temperature)
+ return true;
+#endif
+ break;
+ }
+ default:
+ break;
+ }
+ }
+#endif
+
+ return mode == QCamera::WhiteBalanceAuto;
+}
+
+void QGstreamerCamera::setWhiteBalanceMode(QCamera::WhiteBalanceMode mode)
+{
+ Q_ASSERT(isWhiteBalanceModeSupported(mode));
+
+#if QT_CONFIG(linux_v4l)
+ if (isV4L2Camera()) {
+ int temperature = colorTemperatureForWhiteBalance(mode);
+ int t = setV4L2ColorTemperature(temperature);
+ if (t == 0)
+ mode = QCamera::WhiteBalanceAuto;
+ whiteBalanceModeChanged(mode);
+ return;
+ }
+#endif
+
+#if QT_CONFIG(gstreamer_photography)
+ if (auto *p = photography()) {
+ GstPhotographyWhiteBalanceMode gstMode = GST_PHOTOGRAPHY_WB_MODE_AUTO;
+ switch (mode) {
+ case QCamera::WhiteBalanceSunlight:
+ gstMode = GST_PHOTOGRAPHY_WB_MODE_DAYLIGHT;
+ break;
+ case QCamera::WhiteBalanceCloudy:
+ gstMode = GST_PHOTOGRAPHY_WB_MODE_CLOUDY;
+ break;
+ case QCamera::WhiteBalanceShade:
+ gstMode = GST_PHOTOGRAPHY_WB_MODE_SHADE;
+ break;
+ case QCamera::WhiteBalanceSunset:
+ gstMode = GST_PHOTOGRAPHY_WB_MODE_SUNSET;
+ break;
+ case QCamera::WhiteBalanceTungsten:
+ gstMode = GST_PHOTOGRAPHY_WB_MODE_TUNGSTEN;
+ break;
+ case QCamera::WhiteBalanceFluorescent:
+ gstMode = GST_PHOTOGRAPHY_WB_MODE_FLUORESCENT;
+ break;
+ case QCamera::WhiteBalanceAuto:
+ default:
+ break;
+ }
+ if (gst_photography_set_white_balance_mode(p, gstMode)) {
+ whiteBalanceModeChanged(mode);
+ return;
+ }
+ }
+#endif
+}
+
+void QGstreamerCamera::setColorTemperature(int temperature)
+{
+ if (temperature == 0) {
+ setWhiteBalanceMode(QCamera::WhiteBalanceAuto);
+ return;
+ }
+
+ Q_ASSERT(isWhiteBalanceModeSupported(QCamera::WhiteBalanceManual));
+
+#if QT_CONFIG(linux_v4l)
+ if (isV4L2Camera()) {
+ int t = setV4L2ColorTemperature(temperature);
+ if (t)
+ colorTemperatureChanged(t);
+ return;
+ }
+#endif
+
+#if QT_CONFIG(gstreamer_photography) && GST_CHECK_VERSION(1, 18, 0)
+ if (auto *p = photography()) {
+ GstPhotographyInterface *iface = GST_PHOTOGRAPHY_GET_INTERFACE(p);
+ Q_ASSERT(iface->set_color_temperature);
+ iface->set_color_temperature(p, temperature);
+ return;
+ }
+#endif
+}
+
+#if QT_CONFIG(linux_v4l)
+bool QGstreamerCamera::isV4L2Camera() const
+{
+ return !m_v4l2DevicePath.isEmpty();
+}
+
+void QGstreamerCamera::initV4L2Controls()
+{
+ v4l2AutoWhiteBalanceSupported = false;
+ v4l2ColorTemperatureSupported = false;
+ QCamera::Features features{};
+
+ Q_ASSERT(!m_v4l2DevicePath.isEmpty());
+
+
+ withV4L2DeviceFileDescriptor([&](int fd) {
+ struct v4l2_queryctrl queryControl = {};
+ queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE;
+
+ if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
+ v4l2AutoWhiteBalanceSupported = true;
+ setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, true);
+ }
+
+ 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;
+ }
+
+ queryControl = {};
+ queryControl.id = V4L2_CID_EXPOSURE_AUTO;
+ if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
+ v4l2AutoExposureSupported = true;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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)
+{
+ if (v4l2AutoWhiteBalanceSupported) {
+ 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)))
+ temperature = 0;
+ } else {
+ temperature = 0;
+ }
+
+ return temperature;
+}
+
+bool QGstreamerCamera::setV4L2Parameter(quint32 id, qint32 value)
+{
+ 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
+{
+ 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;
+ });
+}
+
+QGstreamerCustomCamera::QGstreamerCustomCamera(QCamera *camera)
+ : QGstreamerCameraBase{
+ camera,
+ },
+ m_userProvidedGstElement{
+ false,
+ }
+{
+}
+
+QGstreamerCustomCamera::QGstreamerCustomCamera(QCamera *camera, QGstElement element)
+ : QGstreamerCameraBase{
+ camera,
+ },
+ gstCamera{
+ std::move(element),
+ },
+ m_userProvidedGstElement{
+ true,
+ }
+{
+}
+
+void QGstreamerCustomCamera::setCamera(const QCameraDevice &device)
+{
+ if (m_userProvidedGstElement)
+ return;
+
+ gstCamera = QGstBin::createFromPipelineDescription(device.id(), /*name=*/nullptr,
+ /* ghostUnlinkedPads=*/true);
+}
+
+bool QGstreamerCustomCamera::isActive() const
+{
+ return m_active;
+}
+
+void QGstreamerCustomCamera::setActive(bool active)
+{
+ if (m_active == active)
+ return;
+
+ m_active = active;
+
+ emit activeChanged(active);
+}
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera_p.h b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera_p.h
new file mode 100644
index 000000000..f43c01f34
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera_p.h
@@ -0,0 +1,152 @@
+// 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
+
+//
+// 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/qmultimediautils_p.h>
+
+#include <mediacapture/qgstreamermediacapture_p.h>
+#include <common/qgst_p.h>
+#include <common/qgstpipeline_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGstreamerCameraBase : public QPlatformCamera
+{
+public:
+ using QPlatformCamera::QPlatformCamera;
+
+ virtual QGstElement gstElement() const = 0;
+};
+
+class QGstreamerCamera : public QGstreamerCameraBase
+{
+public:
+ static QMaybe<QPlatformCamera *> create(QCamera *camera);
+
+ virtual ~QGstreamerCamera();
+
+ bool isActive() const override;
+ void setActive(bool active) override;
+
+ void setCamera(const QCameraDevice &camera) override;
+ bool setCameraFormat(const QCameraFormat &format) override;
+
+ QGstElement gstElement() const override { return gstCameraBin; }
+#if QT_CONFIG(gstreamer_photography)
+ GstPhotography *photography() const;
+#endif
+
+ void setFocusMode(QCamera::FocusMode mode) override;
+ bool isFocusModeSupported(QCamera::FocusMode mode) const override;
+
+ void setFlashMode(QCamera::FlashMode mode) override;
+ bool isFlashModeSupported(QCamera::FlashMode mode) const override;
+ bool isFlashReady() const override;
+
+ void setExposureMode(QCamera::ExposureMode) override;
+ bool isExposureModeSupported(QCamera::ExposureMode mode) const override;
+ void setExposureCompensation(float) override;
+ void setManualIsoSensitivity(int) override;
+ int isoSensitivity() const override;
+ void setManualExposureTime(float) override;
+ float exposureTime() const override;
+
+ bool isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const override;
+ void setWhiteBalanceMode(QCamera::WhiteBalanceMode mode) override;
+ void setColorTemperature(int temperature) override;
+
+private:
+ QGstreamerCamera(QCamera *camera);
+
+ void updateCameraProperties();
+
+#if QT_CONFIG(linux_v4l)
+ bool isV4L2Camera() const;
+ void initV4L2Controls();
+ int setV4L2ColorTemperature(int temperature);
+ bool setV4L2Parameter(quint32 id, qint32 value);
+ int getV4L2Parameter(quint32 id) const;
+
+ 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;
+
+ 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;
+
+ QGstBin gstCameraBin;
+ QGstElement gstCamera;
+ QGstElement gstCapsFilter;
+ QGstElement gstDecode;
+ QGstElement gstVideoConvert;
+ QGstElement gstVideoScale;
+
+ bool m_active = false;
+ QString m_v4l2DevicePath;
+};
+
+class QGstreamerCustomCamera : public QGstreamerCameraBase
+{
+public:
+ explicit QGstreamerCustomCamera(QCamera *);
+ explicit QGstreamerCustomCamera(QCamera *, QGstElement element);
+
+ QGstElement gstElement() const override { return gstCamera; }
+ void setCamera(const QCameraDevice &) override;
+
+ bool isActive() const override;
+ void setActive(bool) override;
+
+private:
+ QGstElement gstCamera;
+ bool m_active{};
+ const bool m_userProvidedGstElement;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGSTREAMERCAMERACONTROL_H
diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp
new file mode 100644
index 000000000..9c21dc083
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp
@@ -0,0 +1,450 @@
+// 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 <QtMultimedia/qvideoframeformat.h>
+#include <QtMultimedia/private/qmediastoragelocation_p.h>
+#include <QtMultimedia/private/qplatformcamera_p.h>
+#include <QtMultimedia/private/qplatformimagecapture_p.h>
+#include <QtMultimedia/private/qvideoframe_p.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qstandardpaths.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qloggingcategory.h>
+
+#include <common/qgstreamermetadata_p.h>
+#include <common/qgstvideobuffer_p.h>
+#include <common/qgstutils_p.h>
+
+#include <utility>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+Q_LOGGING_CATEGORY(qLcImageCaptureGst, "qt.multimedia.imageCapture")
+
+struct ThreadPoolSingleton
+{
+ QObject m_context;
+ QMutex m_poolMutex;
+ QThreadPool *m_instance{};
+ bool m_appUnderDestruction = false;
+
+ QThreadPool *get(const QMutexLocker<QMutex> &)
+ {
+ if (m_instance)
+ return m_instance;
+ if (m_appUnderDestruction || !qApp)
+ return nullptr;
+
+ using namespace std::chrono;
+
+ m_instance = new QThreadPool(qApp);
+ m_instance->setMaxThreadCount(1); // 1 thread;
+ static constexpr auto expiryTimeout = minutes(5);
+ m_instance->setExpiryTimeout(round<milliseconds>(expiryTimeout).count());
+
+ QObject::connect(qApp, &QCoreApplication::aboutToQuit, &m_context, [&] {
+ // we need to make sure that thread-local QRhi is destroyed before the application to
+ // prevent QTBUG-124189
+ QMutexLocker guard(&m_poolMutex);
+ delete m_instance;
+ m_instance = {};
+ m_appUnderDestruction = true;
+ });
+
+ QObject::connect(qApp, &QCoreApplication::destroyed, &m_context, [&] {
+ m_appUnderDestruction = false;
+ });
+ return m_instance;
+ }
+
+ template <typename Functor>
+ QFuture<void> run(Functor &&f)
+ {
+ QMutexLocker guard(&m_poolMutex);
+ QThreadPool *pool = get(guard);
+ if (!pool)
+ return QFuture<void>{};
+
+ return QtConcurrent::run(pool, std::forward<Functor>(f));
+ }
+};
+
+ThreadPoolSingleton s_threadPoolSingleton;
+
+}; // namespace
+
+QMaybe<QPlatformImageCapture *> QGstreamerImageCapture::create(QImageCapture *parent)
+{
+ static const auto error = qGstErrorMessageIfElementsNotAvailable(
+ "queue", "capsfilter", "videoconvert", "jpegenc", "jifmux", "fakesink");
+ if (error)
+ return *error;
+
+ return new QGstreamerImageCapture(parent);
+}
+
+QGstreamerImageCapture::QGstreamerImageCapture(QImageCapture *parent)
+ : QPlatformImageCapture(parent),
+ QGstreamerBufferProbe(ProbeBuffers),
+ bin{
+ QGstBin::create("imageCaptureBin"),
+ },
+ queue{
+ QGstElement::createFromFactory("queue", "imageCaptureQueue"),
+ },
+ filter{
+ QGstElement::createFromFactory("capsfilter", "filter"),
+ },
+ videoConvert{
+ QGstElement::createFromFactory("videoconvert", "imageCaptureConvert"),
+ },
+ encoder{
+ QGstElement::createFromFactory("jpegenc", "jpegEncoder"),
+ },
+ muxer{
+ QGstElement::createFromFactory("jifmux", "jpegMuxer"),
+ },
+ sink{
+ QGstElement::createFromFactory("fakesink", "imageCaptureSink"),
+ }
+{
+ // configures the queue to be fast, lightweight and non blocking
+ queue.set("leaky", 2 /*downstream*/);
+ queue.set("silent", true);
+ queue.set("max-size-buffers", uint(1));
+ queue.set("max-size-bytes", uint(0));
+ queue.set("max-size-time", quint64(0));
+
+ // 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, 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);
+ m_handoffConnection = sink.connect("handoff", G_CALLBACK(&saveImageFilter), this);
+}
+
+QGstreamerImageCapture::~QGstreamerImageCapture()
+{
+ bin.setStateSync(GST_STATE_NULL);
+
+ // wait for pending futures
+ auto pendingFutures = [&] {
+ QMutexLocker guard(&m_mutex);
+ return std::move(m_pendingFutures);
+ }();
+
+ for (QFuture<void> &pendingImage : pendingFutures)
+ pendingImage.waitForFinished();
+}
+
+bool QGstreamerImageCapture::isReadyForCapture() const
+{
+ QMutexLocker guard(&m_mutex);
+ return m_session && !passImage && cameraActive;
+}
+
+int QGstreamerImageCapture::capture(const QString &fileName)
+{
+ using namespace Qt::Literals;
+ QString path = QMediaStorageLocation::generateFileName(
+ fileName, QStandardPaths::PicturesLocation, u"jpg"_s);
+ return doCapture(path);
+}
+
+int QGstreamerImageCapture::captureToBuffer()
+{
+ return doCapture(QString());
+}
+
+int QGstreamerImageCapture::doCapture(const QString &fileName)
+{
+ qCDebug(qLcImageCaptureGst) << "do capture";
+
+ {
+ QMutexLocker guard(&m_mutex);
+ if (!m_session) {
+ invokeDeferred([this] {
+ emit error(-1, QImageCapture::ResourceError,
+ QPlatformImageCapture::msgImageCaptureNotSet());
+ });
+
+ qCDebug(qLcImageCaptureGst) << "error 1";
+ return -1;
+ }
+ if (!m_session->camera()) {
+ invokeDeferred([this] {
+ emit error(-1, QImageCapture::ResourceError, tr("No camera available."));
+ });
+
+ qCDebug(qLcImageCaptureGst) << "error 2";
+ return -1;
+ }
+ if (passImage) {
+ invokeDeferred([this] {
+ emit error(-1, QImageCapture::NotReadyError,
+ QPlatformImageCapture::msgCameraNotReady());
+ });
+
+ qCDebug(qLcImageCaptureGst) << "error 3";
+ return -1;
+ }
+ m_lastId++;
+
+ pendingImages.enqueue({ m_lastId, fileName, QMediaMetaData{} });
+ // let one image pass the pipeline
+ passImage = true;
+ }
+
+ emit readyForCaptureChanged(false);
+ 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);
+}
+
+// HACK: gcc-10 and earlier reject [=,this] when building with c++17
+#if __cplusplus >= 202002L
+# define EQ_THIS_CAPTURE =, this
+#else
+# define EQ_THIS_CAPTURE =
+#endif
+
+bool QGstreamerImageCapture::probeBuffer(GstBuffer *buffer)
+{
+ QMutexLocker guard(&m_mutex);
+
+ if (!passImage)
+ return false;
+ qCDebug(qLcImageCaptureGst) << "probe buffer";
+
+ QGstBufferHandle bufferHandle{
+ buffer,
+ QGstBufferHandle::NeedsRef,
+ };
+
+ passImage = false;
+
+ bool ready = isReadyForCapture();
+ invokeDeferred([this, ready] {
+ emit readyForCaptureChanged(ready);
+ });
+
+ QGstCaps caps = bin.staticPad("sink").currentCaps();
+ auto memoryFormat = caps.memoryFormat();
+
+ GstVideoInfo previewInfo;
+ QVideoFrameFormat fmt;
+ auto optionalFormatAndVideoInfo = caps.formatAndVideoInfo();
+ if (optionalFormatAndVideoInfo)
+ std::tie(fmt, previewInfo) = std::move(*optionalFormatAndVideoInfo);
+
+ int futureId = futureIDAllocator += 1;
+
+ // ensure QVideoFrame::toImage is executed on a worker thread that is joined before the
+ // qApplication is destroyed
+ QFuture<void> future = s_threadPoolSingleton.run([EQ_THIS_CAPTURE]() mutable {
+ QMutexLocker guard(&m_mutex);
+ auto scopeExit = qScopeGuard([&] {
+ m_pendingFutures.remove(futureId);
+ });
+
+ if (!m_session) {
+ qDebug() << "QGstreamerImageCapture::probeBuffer: no session";
+ return;
+ }
+
+ auto *sink = m_session->gstreamerVideoSink();
+ auto gstBuffer = std::make_unique<QGstVideoBuffer>(std::move(bufferHandle), previewInfo,
+ sink, fmt, memoryFormat);
+
+ QVideoFrame frame = QVideoFramePrivate::createFrame(std::move(gstBuffer), fmt);
+ QImage img = frame.toImage();
+ if (img.isNull()) {
+ qDebug() << "received a null image";
+ return;
+ }
+
+ QMediaMetaData imageMetaData = metaData();
+ imageMetaData.insert(QMediaMetaData::Resolution, frame.size());
+ pendingImages.head().metaData = std::move(imageMetaData);
+ PendingImage pendingImage = pendingImages.head();
+
+ invokeDeferred([this, pendingImage = std::move(pendingImage), frame = std::move(frame),
+ img = std::move(img)]() mutable {
+ emit imageExposed(pendingImage.id);
+ qCDebug(qLcImageCaptureGst) << "Image available!";
+ emit imageAvailable(pendingImage.id, frame);
+ emit imageCaptured(pendingImage.id, img);
+ emit imageMetadataAvailable(pendingImage.id, pendingImage.metaData);
+ });
+ });
+
+ if (!future.isValid()) // during qApplication shutdown the threadpool becomes unusable
+ return true;
+
+ m_pendingFutures.insert(futureId, future);
+
+ return true;
+}
+
+#undef EQ_THIS_CAPTURE
+
+void QGstreamerImageCapture::setCaptureSession(QPlatformMediaCaptureSession *session)
+{
+ QMutexLocker guard(&m_mutex);
+ QGstreamerMediaCapture *captureSession = static_cast<QGstreamerMediaCapture *>(session);
+ if (m_session == captureSession)
+ return;
+
+ bool readyForCapture = isReadyForCapture();
+ if (m_session) {
+ disconnect(m_session, nullptr, this, nullptr);
+ m_lastId = 0;
+ pendingImages.clear();
+ passImage = false;
+ cameraActive = false;
+ }
+
+ m_session = captureSession;
+ if (!m_session) {
+ if (readyForCapture)
+ emit readyForCaptureChanged(false);
+ return;
+ }
+
+ 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(qLcImageCaptureGst) << "cameraActiveChanged" << cameraActive << active;
+ if (cameraActive == active)
+ return;
+ cameraActive = active;
+ 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);
+ } else {
+ cameraActiveChanged(false);
+ }
+}
+
+gboolean QGstreamerImageCapture::saveImageFilter(GstElement *, GstBuffer *buffer, GstPad *,
+ QGstreamerImageCapture *capture)
+{
+ capture->saveBufferToImage(buffer);
+ return true;
+}
+
+void QGstreamerImageCapture::saveBufferToImage(GstBuffer *buffer)
+{
+ QMutexLocker guard(&m_mutex);
+ passImage = false;
+
+ if (pendingImages.isEmpty())
+ return;
+
+ PendingImage imageData = pendingImages.dequeue();
+ if (imageData.filename.isEmpty())
+ return;
+
+ int id = futureIDAllocator++;
+ QGstBufferHandle bufferHandle{
+ buffer,
+ QGstBufferHandle::NeedsRef,
+ };
+
+ QFuture<void> saveImageFuture = QtConcurrent::run([this, imageData, bufferHandle,
+ id]() mutable {
+ auto cleanup = qScopeGuard([&] {
+ QMutexLocker guard(&m_mutex);
+ m_pendingFutures.remove(id);
+ });
+
+ qCDebug(qLcImageCaptureGst) << "saving image as" << imageData.filename;
+
+ QFile f(imageData.filename);
+ if (!f.open(QFile::WriteOnly)) {
+ qCDebug(qLcImageCaptureGst) << " could not open image file for writing";
+ return;
+ }
+
+ GstMapInfo info;
+ GstBuffer *buffer = bufferHandle.get();
+ 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();
+
+ QMetaObject::invokeMethod(this, [this, imageData = std::move(imageData)]() mutable {
+ emit imageSaved(imageData.id, imageData.filename);
+ });
+ });
+
+ m_pendingFutures.insert(id, saveImageFuture);
+}
+
+QImageEncoderSettings QGstreamerImageCapture::imageSettings() const
+{
+ return m_settings;
+}
+
+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
new file mode 100644
index 000000000..04a7c00b4
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h
@@ -0,0 +1,109 @@
+// 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
+
+//
+// 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/qplatformimagecapture_p.h>
+#include <QtMultimedia/private/qmultimediautils_p.h>
+
+#include <QtCore/qmutex.h>
+#include <QtCore/qqueue.h>
+#include <QtConcurrent/QtConcurrentRun>
+
+#include <common/qgst_p.h>
+#include <common/qgstreamerbufferprobe_p.h>
+#include <mediacapture/qgstreamermediacapture_p.h>
+#include <gst/video/video.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGstreamerImageCapture : public QPlatformImageCapture, private QGstreamerBufferProbe
+{
+ Q_OBJECT
+
+public:
+ static QMaybe<QPlatformImageCapture *> create(QImageCapture *parent);
+ virtual ~QGstreamerImageCapture();
+
+ bool isReadyForCapture() const override;
+ int capture(const QString &fileName) override;
+ int captureToBuffer() override;
+
+ QImageEncoderSettings imageSettings() const override;
+ void setImageSettings(const QImageEncoderSettings &settings) override;
+
+ bool probeBuffer(GstBuffer *buffer) override;
+
+ void setCaptureSession(QPlatformMediaCaptureSession *session);
+
+ QGstElement gstElement() const { return bin; }
+
+ void setMetaData(const QMediaMetaData &m) override;
+
+public Q_SLOTS:
+ void cameraActiveChanged(bool active);
+ void onCameraChanged();
+
+private:
+ QGstreamerImageCapture(QImageCapture *parent);
+
+ void setResolution(const QSize &resolution);
+ int doCapture(const QString &fileName);
+ 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;
+
+ struct PendingImage {
+ int id;
+ QString filename;
+ QMediaMetaData metaData;
+ };
+
+ QQueue<PendingImage> pendingImages;
+
+ QGstBin bin;
+ QGstElement queue;
+ QGstElement filter;
+ QGstElement videoConvert;
+ QGstElement encoder;
+ QGstElement muxer;
+ QGstElement sink;
+ QGstPad videoSrcPad;
+
+ bool passImage = false;
+ bool cameraActive = false;
+
+ QGObjectHandlerScopedConnection m_handoffConnection;
+
+ QMap<int, QFuture<void>> m_pendingFutures;
+ int futureIDAllocator = 0;
+
+ template <typename Functor>
+ void invokeDeferred(Functor &&fn)
+ {
+ QMetaObject::invokeMethod(this, std::forward<decltype(fn)>(fn), Qt::QueuedConnection);
+ };
+};
+
+QT_END_NAMESPACE
+
+#endif // QGSTREAMERCAPTURECORNTROL_H
diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp
new file mode 100644
index 000000000..7ecbb07d7
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp
@@ -0,0 +1,326 @@
+// 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/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>
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/private/quniquehandle_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static void linkTeeToPad(QGstElement tee, QGstPad sink)
+{
+ if (tee.isNull() || sink.isNull())
+ return;
+
+ auto source = tee.getRequestPad("src_%u");
+ source.link(sink);
+}
+
+QMaybe<QPlatformMediaCaptureSession *> QGstreamerMediaCapture::create()
+{
+ auto videoOutput = QGstreamerVideoOutput::create();
+ if (!videoOutput)
+ return videoOutput.error();
+
+ static const auto error = qGstErrorMessageIfElementsNotAvailable("tee", "capsfilter");
+ if (error)
+ return *error;
+
+ return new QGstreamerMediaCapture(videoOutput.value());
+}
+
+QGstreamerMediaCapture::QGstreamerMediaCapture(QGstreamerVideoOutput *videoOutput)
+ : capturePipeline(QGstPipeline::create("mediaCapturePipeline")), gstVideoOutput(videoOutput)
+{
+ gstVideoOutput->setParent(this);
+ gstVideoOutput->setIsPreview();
+ gstVideoOutput->setPipeline(capturePipeline);
+
+ // Use system clock to drive all elements in the pipeline. Otherwise,
+ // 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.
+
+ QGstClockHandle systemClock{
+ gst_system_clock_obtain(),
+ };
+ gst_pipeline_use_clock(capturePipeline.pipeline(), systemClock.get());
+
+ // This is the recording pipeline with only live sources, thus the pipeline
+ // will be always in the playing state.
+ capturePipeline.setState(GST_STATE_PLAYING);
+ capturePipeline.setInStoppedState(false);
+
+ capturePipeline.dumpGraph("initial");
+}
+
+QGstreamerMediaCapture::~QGstreamerMediaCapture()
+{
+ setMediaRecorder(nullptr);
+ setImageCapture(nullptr);
+ setCamera(nullptr);
+ capturePipeline.setStateSync(GST_STATE_NULL);
+}
+
+QPlatformCamera *QGstreamerMediaCapture::camera()
+{
+ return gstCamera;
+}
+
+void QGstreamerMediaCapture::setCamera(QPlatformCamera *platformCamera)
+{
+ auto *camera = static_cast<QGstreamerCameraBase *>(platformCamera);
+ if (gstCamera == camera)
+ return;
+
+ if (gstCamera) {
+ QObject::disconnect(gstCameraActiveConnection);
+ if (gstVideoTee)
+ setCameraActive(false);
+ }
+
+ gstCamera = camera;
+
+ if (gstCamera) {
+ gstCameraActiveConnection = QObject::connect(camera, &QPlatformCamera::activeChanged, this,
+ &QGstreamerMediaCapture::setCameraActive);
+ if (gstCamera->isActive())
+ setCameraActive(true);
+ }
+
+ emit cameraChanged();
+}
+
+void QGstreamerMediaCapture::setCameraActive(bool activate)
+{
+ capturePipeline.modifyPipelineWhileNotRunning([&] {
+ if (activate) {
+ QGstElement cameraElement = gstCamera->gstElement();
+ gstVideoTee = QGstElement::createFromFactory("tee", "videotee");
+ gstVideoTee.set("allow-not-linked", true);
+
+ capturePipeline.add(gstVideoOutput->gstElement(), cameraElement, gstVideoTee);
+
+ linkTeeToPad(gstVideoTee, encoderVideoSink);
+ linkTeeToPad(gstVideoTee, gstVideoOutput->gstElement().staticPad("sink"));
+ linkTeeToPad(gstVideoTee, imageCaptureSink);
+
+ qLinkGstElements(cameraElement, gstVideoTee);
+
+ capturePipeline.syncChildrenState();
+ } else {
+ if (encoderVideoCapsFilter)
+ qUnlinkGstElements(gstVideoTee, encoderVideoCapsFilter);
+ if (m_imageCapture)
+ qUnlinkGstElements(gstVideoTee, m_imageCapture->gstElement());
+
+ auto camera = gstCamera->gstElement();
+
+ capturePipeline.stopAndRemoveElements(camera, gstVideoTee,
+ gstVideoOutput->gstElement());
+
+ gstVideoTee = {};
+ gstCamera->setCaptureSession(nullptr);
+ }
+ });
+
+ capturePipeline.dumpGraph("camera");
+}
+
+QPlatformImageCapture *QGstreamerMediaCapture::imageCapture()
+{
+ return m_imageCapture;
+}
+
+void QGstreamerMediaCapture::setImageCapture(QPlatformImageCapture *imageCapture)
+{
+ QGstreamerImageCapture *control = static_cast<QGstreamerImageCapture *>(imageCapture);
+ if (m_imageCapture == control)
+ return;
+
+ capturePipeline.modifyPipelineWhileNotRunning([&] {
+ if (m_imageCapture) {
+ qUnlinkGstElements(gstVideoTee, m_imageCapture->gstElement());
+ capturePipeline.stopAndRemoveElements(m_imageCapture->gstElement());
+ imageCaptureSink = {};
+ m_imageCapture->setCaptureSession(nullptr);
+ }
+
+ m_imageCapture = control;
+ if (m_imageCapture) {
+ imageCaptureSink = m_imageCapture->gstElement().staticPad("sink");
+ capturePipeline.add(m_imageCapture->gstElement());
+ m_imageCapture->gstElement().syncStateWithParent();
+ linkTeeToPad(gstVideoTee, imageCaptureSink);
+ m_imageCapture->setCaptureSession(this);
+ }
+ });
+
+ capturePipeline.dumpGraph("imageCapture");
+
+ emit imageCaptureChanged();
+}
+
+void QGstreamerMediaCapture::setMediaRecorder(QPlatformMediaRecorder *recorder)
+{
+ QGstreamerMediaEncoder *control = static_cast<QGstreamerMediaEncoder *>(recorder);
+ if (m_mediaEncoder == control)
+ return;
+
+ if (m_mediaEncoder)
+ m_mediaEncoder->setCaptureSession(nullptr);
+ m_mediaEncoder = control;
+ if (m_mediaEncoder)
+ m_mediaEncoder->setCaptureSession(this);
+
+ emit encoderChanged();
+ capturePipeline.dumpGraph("encoder");
+}
+
+QPlatformMediaRecorder *QGstreamerMediaCapture::mediaRecorder()
+{
+ return m_mediaEncoder;
+}
+
+void QGstreamerMediaCapture::linkEncoder(QGstPad audioSink, QGstPad videoSink)
+{
+ capturePipeline.modifyPipelineWhileNotRunning([&] {
+ if (!gstVideoTee.isNull() && !videoSink.isNull()) {
+ QGstCaps caps = gstVideoTee.sink().currentCaps();
+
+ encoderVideoCapsFilter =
+ QGstElement::createFromFactory("capsfilter", "encoderVideoCapsFilter");
+ Q_ASSERT(encoderVideoCapsFilter);
+ encoderVideoCapsFilter.set("caps", caps);
+
+ capturePipeline.add(encoderVideoCapsFilter);
+
+ encoderVideoCapsFilter.src().link(videoSink);
+ linkTeeToPad(gstVideoTee, encoderVideoCapsFilter.sink());
+ encoderVideoSink = encoderVideoCapsFilter.sink();
+ }
+
+ if (!gstAudioTee.isNull() && !audioSink.isNull()) {
+ QGstCaps caps = gstAudioTee.sink().currentCaps();
+
+ encoderAudioCapsFilter =
+ QGstElement::createFromFactory("capsfilter", "encoderAudioCapsFilter");
+ Q_ASSERT(encoderAudioCapsFilter);
+ encoderAudioCapsFilter.set("caps", caps);
+
+ capturePipeline.add(encoderAudioCapsFilter);
+
+ encoderAudioCapsFilter.src().link(audioSink);
+ linkTeeToPad(gstAudioTee, encoderAudioCapsFilter.sink());
+ encoderAudioSink = encoderAudioCapsFilter.sink();
+ }
+ });
+}
+
+void QGstreamerMediaCapture::unlinkEncoder()
+{
+ capturePipeline.modifyPipelineWhileNotRunning([&] {
+ if (encoderVideoCapsFilter) {
+ qUnlinkGstElements(gstVideoTee, encoderVideoCapsFilter);
+ capturePipeline.stopAndRemoveElements(encoderVideoCapsFilter);
+ encoderVideoCapsFilter = {};
+ }
+
+ if (encoderAudioCapsFilter) {
+ qUnlinkGstElements(gstAudioTee, encoderAudioCapsFilter);
+ capturePipeline.stopAndRemoveElements(encoderAudioCapsFilter);
+ encoderAudioCapsFilter = {};
+ }
+
+ encoderAudioSink = {};
+ encoderVideoSink = {};
+ });
+}
+
+const QGstPipeline &QGstreamerMediaCapture::pipeline() const
+{
+ return capturePipeline;
+}
+
+void QGstreamerMediaCapture::setAudioInput(QPlatformAudioInput *input)
+{
+ if (gstAudioInput == input)
+ return;
+
+ capturePipeline.modifyPipelineWhileNotRunning([&] {
+ if (gstAudioInput) {
+ if (encoderAudioCapsFilter)
+ qUnlinkGstElements(gstAudioTee, encoderAudioCapsFilter);
+
+ if (gstAudioOutput) {
+ qUnlinkGstElements(gstAudioTee, gstAudioOutput->gstElement());
+ capturePipeline.stopAndRemoveElements(gstAudioOutput->gstElement());
+ }
+
+ capturePipeline.stopAndRemoveElements(gstAudioInput->gstElement(), gstAudioTee);
+ gstAudioTee = {};
+ }
+
+ gstAudioInput = static_cast<QGstreamerAudioInput *>(input);
+ if (gstAudioInput) {
+ Q_ASSERT(gstAudioTee.isNull());
+ gstAudioTee = QGstElement::createFromFactory("tee", "audiotee");
+ gstAudioTee.set("allow-not-linked", true);
+ capturePipeline.add(gstAudioInput->gstElement(), gstAudioTee);
+ qLinkGstElements(gstAudioInput->gstElement(), gstAudioTee);
+
+ if (gstAudioOutput) {
+ capturePipeline.add(gstAudioOutput->gstElement());
+ gstAudioOutput->gstElement().setState(GST_STATE_PLAYING);
+ linkTeeToPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
+ }
+
+ capturePipeline.syncChildrenState();
+
+ linkTeeToPad(gstAudioTee, encoderAudioSink);
+ }
+ });
+}
+
+void QGstreamerMediaCapture::setVideoPreview(QVideoSink *sink)
+{
+ gstVideoOutput->setVideoSink(sink);
+}
+
+void QGstreamerMediaCapture::setAudioOutput(QPlatformAudioOutput *output)
+{
+ if (gstAudioOutput == output)
+ return;
+
+ capturePipeline.modifyPipelineWhileNotRunning([&] {
+ if (gstAudioOutput && gstAudioInput) {
+ // If audio input is set, the output is in the pipeline
+ qUnlinkGstElements(gstAudioTee, gstAudioOutput->gstElement());
+ capturePipeline.stopAndRemoveElements(gstAudioOutput->gstElement());
+ }
+
+ gstAudioOutput = static_cast<QGstreamerAudioOutput *>(output);
+ if (gstAudioOutput && gstAudioInput) {
+ capturePipeline.add(gstAudioOutput->gstElement());
+ capturePipeline.syncChildrenState();
+ linkTeeToPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
+ }
+ });
+}
+
+QGstreamerVideoSink *QGstreamerMediaCapture::gstreamerVideoSink() const
+{
+ return gstVideoOutput ? gstVideoOutput->gstreamerVideoSink() : nullptr;
+}
+
+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
new file mode 100644
index 000000000..c44e31f0e
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture_p.h
@@ -0,0 +1,97 @@
+// 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
+
+//
+// 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/qplatformmediacapture_p.h>
+#include <private/qplatformmediaintegration_p.h>
+
+#include <common/qgst_p.h>
+#include <common/qgstpipeline_p.h>
+
+#include <qtimer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGstreamerCameraBase;
+class QGstreamerImageCapture;
+class QGstreamerMediaEncoder;
+class QGstreamerAudioInput;
+class QGstreamerAudioOutput;
+class QGstreamerVideoOutput;
+class QGstreamerVideoSink;
+
+class QGstreamerMediaCapture final : public QPlatformMediaCaptureSession
+{
+ Q_OBJECT
+
+public:
+ static QMaybe<QPlatformMediaCaptureSession *> create();
+ virtual ~QGstreamerMediaCapture();
+
+ 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;
+ QGstreamerAudioInput *audioInput() { return gstAudioInput; }
+
+ void setVideoPreview(QVideoSink *sink) override;
+ void setAudioOutput(QPlatformAudioOutput *output) override;
+
+ void linkEncoder(QGstPad audioSink, QGstPad videoSink);
+ void unlinkEncoder();
+
+ const QGstPipeline &pipeline() const;
+
+ QGstreamerVideoSink *gstreamerVideoSink() const;
+
+private:
+ void setCameraActive(bool activate);
+
+ explicit QGstreamerMediaCapture(QGstreamerVideoOutput *videoOutput);
+
+ friend QGstreamerMediaEncoder;
+ // Gst elements
+ QGstPipeline capturePipeline;
+
+ QGstreamerAudioInput *gstAudioInput = nullptr;
+ QGstreamerCameraBase *gstCamera = nullptr;
+ QMetaObject::Connection gstCameraActiveConnection;
+
+ QGstElement gstAudioTee;
+ QGstElement gstVideoTee;
+ QGstElement encoderVideoCapsFilter;
+ QGstElement encoderAudioCapsFilter;
+
+ QGstPad encoderAudioSink;
+ QGstPad encoderVideoSink;
+ QGstPad imageCaptureSink;
+
+ QGstreamerAudioOutput *gstAudioOutput = nullptr;
+ QGstreamerVideoOutput *gstVideoOutput = nullptr;
+
+ QGstreamerMediaEncoder *m_mediaEncoder = nullptr;
+ QGstreamerImageCapture *m_imageCapture = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGSTREAMERCAPTURESERVICE_H
diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder.cpp b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder.cpp
new file mode 100644
index 000000000..4ec10ca84
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder.cpp
@@ -0,0 +1,419 @@
+// 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>
+
+static Q_LOGGING_CATEGORY(qLcMediaEncoderGst, "qt.multimedia.encoder")
+
+QT_BEGIN_NAMESPACE
+
+QGstreamerMediaEncoder::QGstreamerMediaEncoder(QMediaRecorder *parent)
+ : QPlatformMediaRecorder(parent),
+ audioPauseControl(*this),
+ videoPauseControl(*this)
+{
+ signalDurationChangedTimer.setInterval(100);
+ signalDurationChangedTimer.callOnTimeout(&signalDurationChangedTimer, [this]() {
+ durationChanged(duration());
+ });
+}
+
+QGstreamerMediaEncoder::~QGstreamerMediaEncoder()
+{
+ if (!capturePipeline.isNull()) {
+ finalize();
+ capturePipeline.removeMessageFilter(this);
+ capturePipeline.setStateSync(GST_STATE_NULL);
+ }
+}
+
+bool QGstreamerMediaEncoder::isLocationWritable(const QUrl &) const
+{
+ return true;
+}
+
+void QGstreamerMediaEncoder::handleSessionError(QMediaRecorder::Error code, const QString &description)
+{
+ updateError(code, description);
+ stop();
+}
+
+bool QGstreamerMediaEncoder::processBusMessage(const QGstreamerMessage &msg)
+{
+ constexpr bool traceStateChange = false;
+ constexpr bool traceAllEvents = false;
+
+ if constexpr (traceAllEvents)
+ qCDebug(qLcMediaEncoderGst) << "received event:" << msg;
+
+ switch (msg.type()) {
+ case GST_MESSAGE_ELEMENT: {
+ QGstStructureView s = msg.structure();
+ if (s.name() == "GstBinForwarded")
+ return processBusMessage(s.getMessage());
+
+ qCDebug(qLcMediaEncoderGst)
+ << "received element message from" << msg.source().name() << s.name();
+ return false;
+ }
+
+ case GST_MESSAGE_EOS: {
+ qCDebug(qLcMediaEncoderGst) << "received EOS from" << msg.source().name();
+ finalize();
+ return false;
+ }
+
+ 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;
+ }
+
+ case GST_MESSAGE_STATE_CHANGED: {
+ if constexpr (traceStateChange)
+ qCDebug(qLcMediaEncoderGst)
+ << "received state change" << QCompactGstMessageAdaptor(msg);
+
+ return false;
+ }
+
+ default:
+ return false;
+ };
+}
+
+qint64 QGstreamerMediaEncoder::duration() const
+{
+ return std::max(audioPauseControl.duration, videoPauseControl.duration);
+}
+
+
+static GstEncodingContainerProfile *createContainerProfile(const QMediaEncoderSettings &settings)
+{
+ auto *formatInfo = QGstreamerIntegration::instance()->gstFormatsInfo();
+
+ auto caps = formatInfo->formatCaps(settings.fileFormat());
+
+ GstEncodingContainerProfile *profile =
+ (GstEncodingContainerProfile *)gst_encoding_container_profile_new(
+ "container_profile", (gchar *)"custom container profile",
+ const_cast<GstCaps *>(caps.caps()),
+ nullptr); // preset
+ return profile;
+}
+
+static GstEncodingProfile *createVideoProfile(const QMediaEncoderSettings &settings)
+{
+ auto *formatInfo = QGstreamerIntegration::instance()->gstFormatsInfo();
+
+ QGstCaps caps = formatInfo->videoCaps(settings.mediaFormat());
+ if (caps.isNull())
+ return nullptr;
+
+ 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);
+
+ return (GstEncodingProfile *)profile;
+}
+
+static GstEncodingProfile *createAudioProfile(const QMediaEncoderSettings &settings)
+{
+ auto *formatInfo = QGstreamerIntegration::instance()->gstFormatsInfo();
+
+ auto caps = formatInfo->audioCaps(settings.mediaFormat());
+ if (caps.isNull())
+ return nullptr;
+
+ GstEncodingProfile *profile =
+ (GstEncodingProfile *)gst_encoding_audio_profile_new(const_cast<GstCaps *>(caps.caps()),
+ nullptr, // preset
+ nullptr, // restriction
+ 0); // presence
+
+ return profile;
+}
+
+
+static GstEncodingContainerProfile *createEncodingProfile(const QMediaEncoderSettings &settings)
+{
+ auto *containerProfile = createContainerProfile(settings);
+ if (!containerProfile) {
+ qWarning() << "QGstreamerMediaEncoder: failed to create container profile!";
+ return nullptr;
+ }
+
+ GstEncodingProfile *audioProfile = createAudioProfile(settings);
+ GstEncodingProfile *videoProfile = nullptr;
+ if (settings.videoCodec() != QMediaFormat::VideoCodec::Unspecified)
+ videoProfile = createVideoProfile(settings);
+// qDebug() << "audio profile" << (audioProfile ? gst_caps_to_string(gst_encoding_profile_get_format(audioProfile)) : "(null)");
+// qDebug() << "video profile" << (videoProfile ? gst_caps_to_string(gst_encoding_profile_get_format(videoProfile)) : "(null)");
+// qDebug() << "conta profile" << gst_caps_to_string(gst_encoding_profile_get_format((GstEncodingProfile *)containerProfile));
+
+ if (videoProfile) {
+ if (!gst_encoding_container_profile_add_profile(containerProfile, videoProfile)) {
+ qWarning() << "QGstreamerMediaEncoder: failed to add video profile!";
+ gst_encoding_profile_unref(videoProfile);
+ }
+ }
+ if (audioProfile) {
+ if (!gst_encoding_container_profile_add_profile(containerProfile, audioProfile)) {
+ qWarning() << "QGstreamerMediaEncoder: failed to add audio profile!";
+ gst_encoding_profile_unref(audioProfile);
+ }
+ }
+
+ return containerProfile;
+}
+
+void QGstreamerMediaEncoder::PauseControl::reset()
+{
+ pauseOffsetPts = 0;
+ pauseStartPts.reset();
+ duration = 0;
+ firstBufferPts.reset();
+}
+
+void QGstreamerMediaEncoder::PauseControl::installOn(QGstPad pad)
+{
+ pad.addProbe<&QGstreamerMediaEncoder::PauseControl::processBuffer>(this, GST_PAD_PROBE_TYPE_BUFFER);
+}
+
+GstPadProbeReturn QGstreamerMediaEncoder::PauseControl::processBuffer(QGstPad, GstPadProbeInfo *info)
+{
+ auto buffer = GST_PAD_PROBE_INFO_BUFFER(info);
+ if (!buffer)
+ return GST_PAD_PROBE_OK;
+
+ buffer = gst_buffer_make_writable(buffer);
+
+ if (!buffer)
+ return GST_PAD_PROBE_OK;
+
+ GST_PAD_PROBE_INFO_DATA(info) = buffer;
+
+ if (!GST_BUFFER_PTS_IS_VALID(buffer))
+ return GST_PAD_PROBE_OK;
+
+ if (!firstBufferPts)
+ firstBufferPts = GST_BUFFER_PTS(buffer);
+
+ if (encoder.state() == QMediaRecorder::PausedState) {
+ if (!pauseStartPts)
+ pauseStartPts = GST_BUFFER_PTS(buffer);
+
+ return GST_PAD_PROBE_DROP;
+ }
+
+ if (pauseStartPts) {
+ pauseOffsetPts += GST_BUFFER_PTS(buffer) - *pauseStartPts;
+ pauseStartPts.reset();
+ }
+ GST_BUFFER_PTS(buffer) -= pauseOffsetPts;
+
+ duration = (GST_BUFFER_PTS(buffer) - *firstBufferPts) / GST_MSECOND;
+
+ return GST_PAD_PROBE_OK;
+}
+
+void QGstreamerMediaEncoder::record(QMediaEncoderSettings &settings)
+{
+ if (!m_session ||m_finalizing || state() != QMediaRecorder::StoppedState)
+ return;
+
+ const auto hasVideo = m_session->camera() && m_session->camera()->isActive();
+ const auto hasAudio = m_session->audioInput() != nullptr;
+
+ if (!hasVideo && !hasAudio) {
+ updateError(QMediaRecorder::ResourceError, QMediaRecorder::tr("No camera or audio input"));
+ return;
+ }
+
+ const auto audioOnly = settings.videoCodec() == QMediaFormat::VideoCodec::Unspecified;
+
+ auto primaryLocation = audioOnly ? QStandardPaths::MusicLocation : QStandardPaths::MoviesLocation;
+ auto container = settings.mimeType().preferredSuffix();
+ auto location = QMediaStorageLocation::generateFileName(outputLocation().toLocalFile(), primaryLocation, container);
+
+ QUrl actualSink = QUrl::fromLocalFile(QDir::currentPath()).resolved(location);
+ qCDebug(qLcMediaEncoderGst) << "recording new video to" << actualSink;
+
+ Q_ASSERT(!actualSink.isEmpty());
+
+ 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::createFromFactory("filesink", "filesink");
+ Q_ASSERT(gstFileSink);
+ gstFileSink.set("location", QFile::encodeName(actualSink.toLocalFile()).constData());
+ gstFileSink.set("async", false);
+
+ QGstPad audioSink = {};
+ QGstPad videoSink = {};
+
+ audioPauseControl.reset();
+ videoPauseControl.reset();
+
+ if (hasAudio) {
+ audioSink = gstEncoder.getRequestPad("audio_%u");
+ if (audioSink.isNull())
+ qWarning() << "Unsupported audio codec";
+ else
+ audioPauseControl.installOn(audioSink);
+ }
+
+ if (hasVideo) {
+ videoSink = gstEncoder.getRequestPad("video_%u");
+ if (videoSink.isNull())
+ qWarning() << "Unsupported video codec";
+ else
+ videoPauseControl.installOn(videoSink);
+ }
+
+ capturePipeline.modifyPipelineWhileNotRunning([&] {
+ capturePipeline.add(gstEncoder, gstFileSink);
+ qLinkGstElements(gstEncoder, gstFileSink);
+ applyMetaDataToTagSetter(m_metaData, gstEncoder);
+
+ m_session->linkEncoder(audioSink, videoSink);
+
+ gstEncoder.syncStateWithParent();
+ gstFileSink.syncStateWithParent();
+ });
+
+ signalDurationChangedTimer.start();
+ capturePipeline.dumpGraph("recording");
+
+ durationChanged(0);
+ stateChanged(QMediaRecorder::RecordingState);
+ actualLocationChanged(QUrl::fromLocalFile(location));
+}
+
+void QGstreamerMediaEncoder::pause()
+{
+ if (!m_session || m_finalizing || state() != QMediaRecorder::RecordingState)
+ return;
+ signalDurationChangedTimer.stop();
+ durationChanged(duration());
+ capturePipeline.dumpGraph("before-pause");
+ stateChanged(QMediaRecorder::PausedState);
+}
+
+void QGstreamerMediaEncoder::resume()
+{
+ capturePipeline.dumpGraph("before-resume");
+ if (!m_session || m_finalizing || state() != QMediaRecorder::PausedState)
+ return;
+ signalDurationChangedTimer.start();
+ stateChanged(QMediaRecorder::RecordingState);
+}
+
+void QGstreamerMediaEncoder::stop()
+{
+ if (!m_session || m_finalizing || state() == QMediaRecorder::StoppedState)
+ return;
+ durationChanged(duration());
+ qCDebug(qLcMediaEncoderGst) << "stop";
+ m_finalizing = true;
+ m_session->unlinkEncoder();
+ signalDurationChangedTimer.stop();
+
+ qCDebug(qLcMediaEncoderGst) << ">>>>>>>>>>>>> sending EOS";
+ gstEncoder.sendEos();
+}
+
+void QGstreamerMediaEncoder::finalize()
+{
+ if (!m_session || gstEncoder.isNull())
+ return;
+
+ qCDebug(qLcMediaEncoderGst) << "finalize";
+
+ capturePipeline.stopAndRemoveElements(gstEncoder, gstFileSink);
+ gstFileSink = {};
+ gstEncoder = {};
+ m_finalizing = false;
+ stateChanged(QMediaRecorder::StoppedState);
+}
+
+void QGstreamerMediaEncoder::setMetaData(const QMediaMetaData &metaData)
+{
+ if (!m_session)
+ return;
+ m_metaData = metaData;
+}
+
+QMediaMetaData QGstreamerMediaEncoder::metaData() const
+{
+ return m_metaData;
+}
+
+void QGstreamerMediaEncoder::setCaptureSession(QPlatformMediaCaptureSession *session)
+{
+ QGstreamerMediaCapture *captureSession = static_cast<QGstreamerMediaCapture *>(session);
+ if (m_session == captureSession)
+ return;
+
+ if (m_session) {
+ stop();
+ if (m_finalizing) {
+ QEventLoop loop;
+ QObject::connect(mediaRecorder(), &QMediaRecorder::recorderStateChanged, &loop,
+ &QEventLoop::quit);
+ loop.exec();
+ }
+
+ capturePipeline.removeMessageFilter(this);
+ capturePipeline = {};
+ }
+
+ m_session = captureSession;
+ if (!m_session)
+ return;
+
+ capturePipeline = captureSession->capturePipeline;
+ capturePipeline.set("message-forward", true);
+ capturePipeline.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
new file mode 100644
index 000000000..56e8c193b
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder_p.h
@@ -0,0 +1,91 @@
+// 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
+#define QGSTREAMERENCODERCONTROL_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 <mediacapture/qgstreamermediacapture_p.h>
+#include <common/qgstreamermetadata_p.h>
+
+#include <QtMultimedia/private/qplatformmediarecorder_p.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qtimer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QMediaMetaData;
+class QGstreamerMessage;
+
+class QGstreamerMediaEncoder : public QPlatformMediaRecorder, QGstreamerBusMessageFilter
+{
+public:
+ explicit QGstreamerMediaEncoder(QMediaRecorder *parent);
+ virtual ~QGstreamerMediaEncoder();
+
+ bool isLocationWritable(const QUrl &sink) const override;
+
+ qint64 duration() const override;
+
+ void record(QMediaEncoderSettings &settings) override;
+ void pause() override;
+ void resume() override;
+ void stop() override;
+
+ void setMetaData(const QMediaMetaData &) override;
+ QMediaMetaData metaData() const override;
+
+ void setCaptureSession(QPlatformMediaCaptureSession *session);
+
+ QGstElement getEncoder() { return gstEncoder; }
+private:
+ bool processBusMessage(const QGstreamerMessage& message) override;
+
+private:
+ struct PauseControl {
+ explicit PauseControl(QPlatformMediaRecorder &encoder) : encoder(encoder) { }
+
+ GstPadProbeReturn processBuffer(QGstPad pad, GstPadProbeInfo *info);
+ void installOn(QGstPad pad);
+ void reset();
+
+ QPlatformMediaRecorder &encoder;
+ GstClockTime pauseOffsetPts = 0;
+ std::optional<GstClockTime> pauseStartPts;
+ std::optional<GstClockTime> firstBufferPts;
+ qint64 duration = 0;
+ };
+
+ PauseControl audioPauseControl;
+ PauseControl videoPauseControl;
+
+ void handleSessionError(QMediaRecorder::Error code, const QString &description);
+ void finalize();
+
+ QGstreamerMediaCapture *m_session = nullptr;
+ QMediaMetaData m_metaData;
+ QTimer signalDurationChangedTimer;
+
+ QGstPipeline capturePipeline;
+ QGstBin gstEncoder;
+ QGstElement gstFileSink;
+
+ bool m_finalizing = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGSTREAMERENCODERCONTROL_H
diff --git a/src/plugins/multimedia/gstreamer/qgstreamerformatinfo.cpp b/src/plugins/multimedia/gstreamer/qgstreamerformatinfo.cpp
new file mode 100644
index 000000000..a657fc52f
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/qgstreamerformatinfo.cpp
@@ -0,0 +1,445 @@
+// 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 <gst/gst.h>
+
+QT_BEGIN_NAMESPACE
+
+QMediaFormat::AudioCodec QGstreamerFormatInfo::audioCodecForCaps(QGstStructureView structure)
+{
+ using namespace std::string_view_literals;
+ const char *name = structure.name().data();
+
+ if (!name || (strncmp(name, "audio/", 6) != 0))
+ return QMediaFormat::AudioCodec::Unspecified;
+ name += 6;
+ if (name == "mpeg"sv) {
+ auto version = structure["mpegversion"].toInt();
+ if (version == 1) {
+ auto layer = structure["layer"];
+ if (!layer.isNull())
+ return QMediaFormat::AudioCodec::MP3;
+ }
+ if (version == 4)
+ return QMediaFormat::AudioCodec::AAC;
+ return QMediaFormat::AudioCodec::Unspecified;
+ }
+ if (name == "x-ac3"sv)
+ return QMediaFormat::AudioCodec::AC3;
+
+ if (name == "x-eac3"sv)
+ return QMediaFormat::AudioCodec::EAC3;
+
+ if (name == "x-flac"sv)
+ return QMediaFormat::AudioCodec::FLAC;
+
+ if (name == "x-alac"sv)
+ return QMediaFormat::AudioCodec::ALAC;
+
+ if (name == "x-true-hd"sv)
+ return QMediaFormat::AudioCodec::DolbyTrueHD;
+
+ if (name == "x-vorbis"sv)
+ return QMediaFormat::AudioCodec::Vorbis;
+
+ if (name == "x-opus"sv)
+ return QMediaFormat::AudioCodec::Opus;
+
+ if (name == "x-wav"sv)
+ return QMediaFormat::AudioCodec::Wave;
+
+ if (name == "x-wma"sv)
+ return QMediaFormat::AudioCodec::WMA;
+
+ return QMediaFormat::AudioCodec::Unspecified;
+}
+
+QMediaFormat::VideoCodec QGstreamerFormatInfo::videoCodecForCaps(QGstStructureView structure)
+{
+ using namespace std::string_view_literals;
+ const char *name = structure.name().data();
+
+ if (!name || (strncmp(name, "video/", 6) != 0))
+ return QMediaFormat::VideoCodec::Unspecified;
+ name += 6;
+
+ if (name == "mpeg"sv) {
+ auto version = structure["mpegversion"].toInt();
+ if (version == 1)
+ return QMediaFormat::VideoCodec::MPEG1;
+ if (version == 2)
+ return QMediaFormat::VideoCodec::MPEG2;
+ if (version == 4)
+ return QMediaFormat::VideoCodec::MPEG4;
+ return QMediaFormat::VideoCodec::Unspecified;
+ }
+ if (name == "x-h264"sv)
+ return QMediaFormat::VideoCodec::H264;
+
+#if GST_CHECK_VERSION(1, 17, 0) // x265enc seems to be broken on 1.16 at least
+ if (name == "x-h265"sv)
+ return QMediaFormat::VideoCodec::H265;
+#endif
+
+ if (name == "x-vp8"sv)
+ return QMediaFormat::VideoCodec::VP8;
+
+ if (name == "x-vp9"sv)
+ return QMediaFormat::VideoCodec::VP9;
+
+ if (name == "x-av1"sv)
+ return QMediaFormat::VideoCodec::AV1;
+
+ if (name == "x-theora"sv)
+ return QMediaFormat::VideoCodec::Theora;
+
+ if (name == "x-jpeg"sv)
+ return QMediaFormat::VideoCodec::MotionJPEG;
+
+ if (name == "x-wmv"sv)
+ return QMediaFormat::VideoCodec::WMV;
+
+ return QMediaFormat::VideoCodec::Unspecified;
+}
+
+QMediaFormat::FileFormat QGstreamerFormatInfo::fileFormatForCaps(QGstStructureView structure)
+{
+ using namespace std::string_view_literals;
+ const char *name = structure.name().data();
+
+ if (name == "video/x-ms-asf"sv)
+ return QMediaFormat::FileFormat::WMV;
+
+ if (name == "video/x-msvideo"sv)
+ return QMediaFormat::FileFormat::AVI;
+
+ if (name == "video/x-matroska"sv)
+ return QMediaFormat::FileFormat::Matroska;
+
+ if (name == "video/quicktime"sv) {
+ const char *variant = structure["variant"].toString();
+ if (!variant)
+ return QMediaFormat::FileFormat::QuickTime;
+ if (variant == "iso"sv)
+ return QMediaFormat::FileFormat::MPEG4;
+ }
+ if (name == "video/ogg"sv)
+ return QMediaFormat::FileFormat::Ogg;
+
+ if (name == "video/webm"sv)
+ return QMediaFormat::FileFormat::WebM;
+
+ if (name == "audio/x-m4a"sv)
+ return QMediaFormat::FileFormat::Mpeg4Audio;
+
+ if (name == "audio/x-wav"sv)
+ return QMediaFormat::FileFormat::Wave;
+
+ if (name == "audio/mpeg"sv) {
+ auto mpegversion = structure["mpegversion"].toInt();
+ if (mpegversion == 1) {
+ auto layer = structure["layer"];
+ if (!layer.isNull())
+ return QMediaFormat::FileFormat::MP3;
+ }
+ }
+
+ return QMediaFormat::UnspecifiedFormat;
+}
+
+
+QImageCapture::FileFormat QGstreamerFormatInfo::imageFormatForCaps(QGstStructureView structure)
+{
+ using namespace std::string_view_literals;
+ const char *name = structure.name().data();
+
+ if (name == "image/jpeg"sv)
+ return QImageCapture::JPEG;
+
+ if (name == "image/png"sv)
+ return QImageCapture::PNG;
+
+ if (name == "image/webp"sv)
+ return QImageCapture::WebP;
+
+ if (name == "image/tiff"sv)
+ return QImageCapture::Tiff;
+
+ return QImageCapture::UnspecifiedFormat;
+}
+
+static QPair<QList<QMediaFormat::AudioCodec>, QList<QMediaFormat::VideoCodec>> getCodecsList(bool decode)
+{
+ QList<QMediaFormat::AudioCodec> audio;
+ QList<QMediaFormat::VideoCodec> video;
+
+ GstPadDirection padDirection = decode ? GST_PAD_SINK : GST_PAD_SRC;
+
+ GList *elementList = gst_element_factory_list_get_elements(decode ? GST_ELEMENT_FACTORY_TYPE_DECODER : GST_ELEMENT_FACTORY_TYPE_ENCODER,
+ GST_RANK_MARGINAL);
+
+ 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) {
+ auto caps = QGstCaps(gst_static_caps_get(&padTemplate->static_caps), QGstCaps::HasRef);
+
+ for (int i = 0; i < caps.size(); i++) {
+ QGstStructureView structure = caps.at(i);
+ auto a = QGstreamerFormatInfo::audioCodecForCaps(structure);
+ if (a != QMediaFormat::AudioCodec::Unspecified && !audio.contains(a))
+ audio.append(a);
+ auto v = QGstreamerFormatInfo::videoCodecForCaps(structure);
+ if (v != QMediaFormat::VideoCodec::Unspecified && !video.contains(v))
+ video.append(v);
+ }
+ }
+ }
+ }
+ gst_plugin_feature_list_free(elementList);
+ return {audio, video};
+}
+
+
+QList<QGstreamerFormatInfo::CodecMap> QGstreamerFormatInfo::getMuxerList(bool demuxer,
+ QList<QMediaFormat::AudioCodec> supportedAudioCodecs,
+ QList<QMediaFormat::VideoCodec> supportedVideoCodecs)
+{
+ QList<QGstreamerFormatInfo::CodecMap> muxers;
+
+ 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);
+
+ for (GstElementFactory *factory :
+ QGstUtils::GListRangeAdaptor<GstElementFactory *>(elementList)) {
+ QList<QMediaFormat::FileFormat> fileFormats;
+
+ for (GstStaticPadTemplate *padTemplate :
+ QGstUtils::GListRangeAdaptor<GstStaticPadTemplate *>(
+ gst_element_factory_get_static_pad_templates(factory))) {
+
+ if (padTemplate->direction == padDirection) {
+ auto caps = QGstCaps(gst_static_caps_get(&padTemplate->static_caps), QGstCaps::HasRef);
+
+ for (int i = 0; i < caps.size(); i++) {
+ QGstStructureView structure = caps.at(i);
+ auto fmt = fileFormatForCaps(structure);
+ if (fmt != QMediaFormat::UnspecifiedFormat)
+ fileFormats.append(fmt);
+ }
+ }
+ }
+ if (fileFormats.isEmpty())
+ continue;
+
+ QList<QMediaFormat::AudioCodec> audioCodecs;
+ QList<QMediaFormat::VideoCodec> videoCodecs;
+
+ 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) {
+ auto caps = QGstCaps(gst_static_caps_get(&padTemplate->static_caps), QGstCaps::HasRef);
+
+ bool acceptsRawAudio = false;
+ for (int i = 0; i < caps.size(); i++) {
+ QGstStructureView structure = caps.at(i);
+ if (structure.name() == "audio/x-raw")
+ acceptsRawAudio = true;
+ auto audio = audioCodecForCaps(structure);
+ if (audio != QMediaFormat::AudioCodec::Unspecified && supportedAudioCodecs.contains(audio))
+ audioCodecs.append(audio);
+ auto video = videoCodecForCaps(structure);
+ if (video != QMediaFormat::VideoCodec::Unspecified && supportedVideoCodecs.contains(video))
+ videoCodecs.append(video);
+ }
+ if (acceptsRawAudio && fileFormats.size() == 1) {
+ switch (fileFormats.at(0)) {
+ case QMediaFormat::Mpeg4Audio:
+ default:
+ break;
+ case QMediaFormat::MP3:
+ audioCodecs.append(QMediaFormat::AudioCodec::MP3);
+ break;
+ case QMediaFormat::FLAC:
+ audioCodecs.append(QMediaFormat::AudioCodec::FLAC);
+ break;
+ case QMediaFormat::Wave:
+ audioCodecs.append(QMediaFormat::AudioCodec::Wave);
+ break;
+ }
+ }
+ }
+ }
+ if (!audioCodecs.isEmpty() || !videoCodecs.isEmpty()) {
+ 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, {}});
+ if (audioCodecs.contains(QMediaFormat::AudioCodec::AAC))
+ muxers.append({QMediaFormat::AAC, { QMediaFormat::AudioCodec::AAC }, {}});
+ } else if (f == QMediaFormat::WMV && !fileFormats.contains(QMediaFormat::WMA)) {
+ muxers.append({QMediaFormat::WMA, audioCodecs, {}});
+ }
+ }
+ }
+ }
+ gst_plugin_feature_list_free(elementList);
+ return muxers;
+}
+
+static QList<QImageCapture::FileFormat> getImageFormatList()
+{
+ QSet<QImageCapture::FileFormat> formats;
+
+ GList *elementList = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_ENCODER,
+ GST_RANK_MARGINAL);
+
+ 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) {
+ QGstCaps caps = QGstCaps(gst_static_caps_get(&padTemplate->static_caps), QGstCaps::HasRef);
+
+ for (int i = 0; i < caps.size(); i++) {
+ QGstStructureView structure = caps.at(i);
+ auto f = QGstreamerFormatInfo::imageFormatForCaps(structure);
+ if (f != QImageCapture::UnspecifiedFormat) {
+// qDebug() << structure.toString() << f;
+ formats.insert(f);
+ }
+ }
+ }
+ }
+ }
+ gst_plugin_feature_list_free(elementList);
+ return formats.values();
+}
+
+#if 0
+static void dumpAudioCodecs(const QList<QMediaFormat::AudioCodec> &codecList)
+{
+ qDebug() << "Audio codecs:";
+ for (const auto &c : codecList)
+ qDebug() << " " << QMediaFormat::audioCodecName(c);
+}
+
+static void dumpVideoCodecs(const QList<QMediaFormat::VideoCodec> &codecList)
+{
+ qDebug() << "Video codecs:";
+ for (const auto &c : codecList)
+ qDebug() << " " << QMediaFormat::videoCodecName(c);
+}
+
+static void dumpMuxers(const QList<QPlatformMediaFormatInfo::CodecMap> &muxerList)
+{
+ for (const auto &m : muxerList) {
+ qDebug() << " " << QMediaFormat::fileFormatName(m.format);
+ qDebug() << " Audio";
+ for (const auto &a : m.audio)
+ qDebug() << " " << QMediaFormat::audioCodecName(a);
+ qDebug() << " Video";
+ for (const auto &v : m.video)
+ qDebug() << " " << QMediaFormat::videoCodecName(v);
+ }
+
+}
+#endif
+
+QGstreamerFormatInfo::QGstreamerFormatInfo()
+{
+ auto codecs = getCodecsList(/*decode = */ true);
+ decoders = getMuxerList(true, codecs.first, codecs.second);
+
+ codecs = getCodecsList(/*decode = */ false);
+ encoders = getMuxerList(/* demuxer = */false, codecs.first, codecs.second);
+// dumpAudioCodecs(codecs.first);
+// dumpVideoCodecs(codecs.second);
+// dumpMuxers(encoders);
+
+ imageFormats = getImageFormatList();
+}
+
+QGstreamerFormatInfo::~QGstreamerFormatInfo() = default;
+
+QGstCaps QGstreamerFormatInfo::formatCaps(const QMediaFormat &f) const
+{
+ auto format = f.fileFormat();
+ Q_ASSERT(format != QMediaFormat::UnspecifiedFormat);
+
+ const char *capsForFormat[QMediaFormat::LastFileFormat + 1] = {
+ "video/x-ms-asf", // WMV
+ "video/x-msvideo", // AVI
+ "video/x-matroska", // Matroska
+ "video/quicktime, variant=(string)iso", // MPEG4
+ "video/ogg", // Ogg
+ "video/quicktime", // QuickTime
+ "video/webm", // WebM
+ "video/quicktime, variant=(string)iso", // Mpeg4Audio is the same is mp4...
+ "video/quicktime, variant=(string)iso", // AAC is also an MP4 container
+ "video/x-ms-asf", // WMA, same as WMV
+ "audio/mpeg, mpegversion=(int)1, layer=(int)3", // MP3
+ "audio/x-flac", // FLAC
+ "audio/x-wav" // Wave
+ };
+ return QGstCaps(gst_caps_from_string(capsForFormat[format]), QGstCaps::HasRef);
+}
+
+QGstCaps QGstreamerFormatInfo::audioCaps(const QMediaFormat &f) const
+{
+ auto codec = f.audioCodec();
+ if (codec == QMediaFormat::AudioCodec::Unspecified)
+ return {};
+
+ const char *capsForCodec[(int)QMediaFormat::AudioCodec::LastAudioCodec + 1] = {
+ "audio/mpeg, mpegversion=(int)1, layer=(int)3", // MP3
+ "audio/mpeg, mpegversion=(int)4", // AAC
+ "audio/x-ac3", // AC3
+ "audio/x-eac3", // EAC3
+ "audio/x-flac", // FLAC
+ "audio/x-true-hd", // DolbyTrueHD
+ "audio/x-opus", // Opus
+ "audio/x-vorbis", // Vorbis
+ "audio/x-raw", // WAVE
+ "audio/x-wma", // WMA
+ "audio/x-alac", // ALAC
+ };
+ return QGstCaps(gst_caps_from_string(capsForCodec[(int)codec]), QGstCaps::HasRef);
+}
+
+QGstCaps QGstreamerFormatInfo::videoCaps(const QMediaFormat &f) const
+{
+ auto codec = f.videoCodec();
+ if (codec == QMediaFormat::VideoCodec::Unspecified)
+ return {};
+
+ const char *capsForCodec[(int)QMediaFormat::VideoCodec::LastVideoCodec + 1] = {
+ "video/mpeg, mpegversion=(int)1", // MPEG1,
+ "video/mpeg, mpegversion=(int)2", // MPEG2,
+ "video/mpeg, mpegversion=(int)4", // MPEG4,
+ "video/x-h264", // H264,
+ "video/x-h265", // H265,
+ "video/x-vp8", // VP8,
+ "video/x-vp9", // VP9,
+ "video/x-av1", // AV1,
+ "video/x-theora", // Theora,
+ "audio/x-wmv", // WMV
+ "video/x-jpeg", // MotionJPEG,
+ };
+ 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
new file mode 100644
index 000000000..bba10edb9
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/qgstreamerformatinfo_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 QGSTREAMERFORMATINFO_H
+#define QGSTREAMERFORMATINFO_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/qplatformmediaformatinfo_p.h>
+#include <qlist.h>
+#include <common/qgst_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGstreamerFormatInfo : public QPlatformMediaFormatInfo
+{
+public:
+ QGstreamerFormatInfo();
+ ~QGstreamerFormatInfo();
+
+ QGstCaps formatCaps(const QMediaFormat &f) const;
+ QGstCaps audioCaps(const QMediaFormat &f) const;
+ QGstCaps videoCaps(const QMediaFormat &f) const;
+
+ static QMediaFormat::AudioCodec audioCodecForCaps(QGstStructureView structure);
+ static QMediaFormat::VideoCodec videoCodecForCaps(QGstStructureView structure);
+ static QMediaFormat::FileFormat fileFormatForCaps(QGstStructureView structure);
+ static QImageCapture::FileFormat imageFormatForCaps(QGstStructureView structure);
+
+ QList<CodecMap> getMuxerList(bool demuxer, QList<QMediaFormat::AudioCodec> audioCodecs, QList<QMediaFormat::VideoCodec> videoCodecs);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/gstreamer/qgstreamerintegration.cpp b/src/plugins/multimedia/gstreamer/qgstreamerintegration.cpp
new file mode 100644
index 000000000..87c514f2e
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/qgstreamerintegration.cpp
@@ -0,0 +1,242 @@
+// 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/qgstreameraudiodevice_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>
+#include <QtMultimedia/private/qmediaplayer_p.h>
+#include <QtMultimedia/private/qmediacapturesession_p.h>
+#include <QtMultimedia/private/qcameradevice_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static thread_local bool inCustomCameraConstruction = false;
+static thread_local QGstElement pendingCameraElement{};
+
+QGStreamerPlatformSpecificInterfaceImplementation::
+ ~QGStreamerPlatformSpecificInterfaceImplementation() = default;
+
+QAudioDevice QGStreamerPlatformSpecificInterfaceImplementation::makeCustomGStreamerAudioInput(
+ const QByteArray &gstreamerPipeline)
+{
+ return qMakeCustomGStreamerAudioInput(gstreamerPipeline);
+}
+
+QAudioDevice QGStreamerPlatformSpecificInterfaceImplementation::makeCustomGStreamerAudioOutput(
+ const QByteArray &gstreamerPipeline)
+{
+ return qMakeCustomGStreamerAudioOutput(gstreamerPipeline);
+}
+
+QCamera *QGStreamerPlatformSpecificInterfaceImplementation::makeCustomGStreamerCamera(
+ const QByteArray &gstreamerPipeline, QObject *parent)
+{
+ QCameraDevicePrivate *info = new QCameraDevicePrivate;
+ info->id = gstreamerPipeline;
+ QCameraDevice device = info->create();
+
+ inCustomCameraConstruction = true;
+ auto guard = qScopeGuard([] {
+ inCustomCameraConstruction = false;
+ });
+
+ return new QCamera(device, parent);
+}
+
+QCamera *
+QGStreamerPlatformSpecificInterfaceImplementation::makeCustomGStreamerCamera(GstElement *element,
+ QObject *parent)
+{
+ QCameraDevicePrivate *info = new QCameraDevicePrivate;
+ info->id = "Custom Camera from GstElement";
+ QCameraDevice device = info->create();
+
+ pendingCameraElement = QGstElement{
+ element,
+ QGstElement::NeedsRef,
+ };
+
+ inCustomCameraConstruction = true;
+ auto guard = qScopeGuard([] {
+ inCustomCameraConstruction = false;
+ Q_ASSERT(!pendingCameraElement);
+ });
+
+ return new QCamera(device, parent);
+}
+
+GstPipeline *QGStreamerPlatformSpecificInterfaceImplementation::gstPipeline(QMediaPlayer *player)
+{
+ auto *priv = reinterpret_cast<QMediaPlayerPrivate *>(QMediaPlayerPrivate::get(player));
+ if (!priv)
+ return nullptr;
+
+ QGstreamerMediaPlayer *gstreamerPlayer = dynamic_cast<QGstreamerMediaPlayer *>(priv->control);
+ return gstreamerPlayer ? gstreamerPlayer->pipeline().pipeline() : nullptr;
+}
+
+GstPipeline *
+QGStreamerPlatformSpecificInterfaceImplementation::gstPipeline(QMediaCaptureSession *session)
+{
+ auto *priv = QMediaCaptureSessionPrivate::get(session);
+ if (!priv)
+ return nullptr;
+
+ QGstreamerMediaCapture *gstreamerCapture =
+ dynamic_cast<QGstreamerMediaCapture *>(priv->captureSession.get());
+ return gstreamerCapture ? gstreamerCapture->pipeline().pipeline() : nullptr;
+}
+
+Q_LOGGING_CATEGORY(lcGstreamer, "qt.multimedia.gstreamer")
+
+namespace {
+
+void rankDownPlugin(GstRegistry *reg, const char *name)
+{
+ QGstPluginFeatureHandle pluginFeature{
+ gst_registry_lookup_feature(reg, name),
+ QGstPluginFeatureHandle::HasRef,
+ };
+ if (pluginFeature)
+ gst_plugin_feature_set_rank(pluginFeature.get(), GST_RANK_PRIMARY - 1);
+}
+
+// https://gstreamer.freedesktop.org/documentation/vaapi/index.html
+constexpr auto vaapiPluginNames = {
+ "vaapidecodebin", "vaapih264dec", "vaapih264enc", "vaapih265dec",
+ "vaapijpegdec", "vaapijpegenc", "vaapimpeg2dec", "vaapipostproc",
+ "vaapisink", "vaapivp8dec", "vaapivp9dec",
+};
+
+// https://gstreamer.freedesktop.org/documentation/va/index.html
+constexpr auto vaPluginNames = {
+ "vaav1dec", "vacompositor", "vadeinterlace", "vah264dec", "vah264enc", "vah265dec",
+ "vajpegdec", "vampeg2dec", "vapostproc", "vavp8dec", "vavp9dec",
+};
+
+// https://gstreamer.freedesktop.org/documentation/nvcodec/index.html
+constexpr auto nvcodecPluginNames = {
+ "cudaconvert", "cudaconvertscale", "cudadownload", "cudaipcsink", "cudaipcsrc",
+ "cudascale", "cudaupload", "nvautogpuh264enc", "nvautogpuh265enc", "nvav1dec",
+ "nvcudah264enc", "nvcudah265enc", "nvd3d11h264enc", "nvd3d11h265enc", "nvh264dec",
+ "nvh264enc", "nvh265dec", "nvh265enc", "nvjpegdec", "nvjpegenc",
+ "nvmpeg2videodec", "nvmpeg4videodec", "nvmpegvideodec", "nvvp8dec", "nvvp9dec",
+};
+
+} // namespace
+
+QGstreamerIntegration::QGstreamerIntegration()
+ : QPlatformMediaIntegration(QLatin1String("gstreamer"))
+{
+ gst_init(nullptr, nullptr);
+ qCDebug(lcGstreamer) << "Using gstreamer version: " << gst_version_string();
+
+ GstRegistry *reg = gst_registry_get();
+
+ if constexpr (!GST_CHECK_VERSION(1, 22, 0)) {
+ GstRegistry* reg = gst_registry_get();
+ for (const char *name : vaapiPluginNames)
+ rankDownPlugin(reg, name);
+ }
+
+ if (qEnvironmentVariableIsSet("QT_GSTREAMER_DISABLE_VA")) {
+ for (const char *name : vaPluginNames)
+ rankDownPlugin(reg, name);
+ }
+
+ if (qEnvironmentVariableIsSet("QT_GSTREAMER_DISABLE_NVCODEC")) {
+ for (const char *name : nvcodecPluginNames)
+ rankDownPlugin(reg, name);
+ }
+}
+
+QPlatformMediaFormatInfo *QGstreamerIntegration::createFormatInfo()
+{
+ return new QGstreamerFormatInfo();
+}
+
+QPlatformVideoDevices *QGstreamerIntegration::createVideoDevices()
+{
+ return new QGstreamerVideoDevices(this);
+}
+
+const QGstreamerFormatInfo *QGstreamerIntegration::gstFormatsInfo()
+{
+ return static_cast<const QGstreamerFormatInfo *>(formatInfo());
+}
+
+QMaybe<QPlatformAudioDecoder *> QGstreamerIntegration::createAudioDecoder(QAudioDecoder *decoder)
+{
+ return QGstreamerAudioDecoder::create(decoder);
+}
+
+QMaybe<QPlatformMediaCaptureSession *> QGstreamerIntegration::createCaptureSession()
+{
+ return QGstreamerMediaCapture::create();
+}
+
+QMaybe<QPlatformMediaPlayer *> QGstreamerIntegration::createPlayer(QMediaPlayer *player)
+{
+ return QGstreamerMediaPlayer::create(player);
+}
+
+QMaybe<QPlatformCamera *> QGstreamerIntegration::createCamera(QCamera *camera)
+{
+ if (inCustomCameraConstruction) {
+ QGstElement element = std::exchange(pendingCameraElement, {});
+ return element ? new QGstreamerCustomCamera{ camera, std::move(element) }
+ : new QGstreamerCustomCamera{ camera };
+ }
+
+ return QGstreamerCamera::create(camera);
+}
+
+QMaybe<QPlatformMediaRecorder *> QGstreamerIntegration::createRecorder(QMediaRecorder *recorder)
+{
+ return new QGstreamerMediaEncoder(recorder);
+}
+
+QMaybe<QPlatformImageCapture *> QGstreamerIntegration::createImageCapture(QImageCapture *imageCapture)
+{
+ return QGstreamerImageCapture::create(imageCapture);
+}
+
+QMaybe<QPlatformVideoSink *> QGstreamerIntegration::createVideoSink(QVideoSink *sink)
+{
+ return new QGstreamerVideoSink(sink);
+}
+
+QMaybe<QPlatformAudioInput *> QGstreamerIntegration::createAudioInput(QAudioInput *q)
+{
+ return QGstreamerAudioInput::create(q);
+}
+
+QMaybe<QPlatformAudioOutput *> QGstreamerIntegration::createAudioOutput(QAudioOutput *q)
+{
+ return QGstreamerAudioOutput::create(q);
+}
+
+GstDevice *QGstreamerIntegration::videoDevice(const QByteArray &id)
+{
+ const auto devices = videoDevices();
+ return devices ? static_cast<QGstreamerVideoDevices *>(devices)->videoDevice(id) : nullptr;
+}
+
+QAbstractPlatformSpecificInterface *QGstreamerIntegration::platformSpecificInterface()
+{
+ return &m_platformSpecificImplementation;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/qgstreamerintegration_p.h b/src/plugins/multimedia/gstreamer/qgstreamerintegration_p.h
new file mode 100644
index 000000000..229bbd48e
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/qgstreamerintegration_p.h
@@ -0,0 +1,79 @@
+// 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
+
+//
+// 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/qplatformmediaintegration_p.h>
+#include <QtMultimedia/private/qgstreamer_platformspecificinterface_p.h>
+
+#include <gst/gst.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGstreamerFormatInfo;
+
+class QGStreamerPlatformSpecificInterfaceImplementation : public QGStreamerPlatformSpecificInterface
+{
+public:
+ ~QGStreamerPlatformSpecificInterfaceImplementation() override;
+
+ QAudioDevice makeCustomGStreamerAudioInput(const QByteArray &gstreamerPipeline) override;
+ QAudioDevice makeCustomGStreamerAudioOutput(const QByteArray &gstreamerPipeline) override;
+ QCamera *makeCustomGStreamerCamera(const QByteArray &gstreamerPipeline,
+ QObject *parent) override;
+
+ QCamera *makeCustomGStreamerCamera(GstElement *, QObject *parent) override;
+
+ GstPipeline *gstPipeline(QMediaPlayer *) override;
+ GstPipeline *gstPipeline(QMediaCaptureSession *) override;
+};
+
+class QGstreamerIntegration : public QPlatformMediaIntegration
+{
+public:
+ QGstreamerIntegration();
+
+ static QGstreamerIntegration *instance()
+ {
+ return static_cast<QGstreamerIntegration *>(QPlatformMediaIntegration::instance());
+ }
+
+ 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;
+
+ QMaybe<QPlatformVideoSink *> createVideoSink(QVideoSink *sink) override;
+
+ QMaybe<QPlatformAudioInput *> createAudioInput(QAudioInput *) override;
+ QMaybe<QPlatformAudioOutput *> createAudioOutput(QAudioOutput *) override;
+
+ const QGstreamerFormatInfo *gstFormatsInfo();
+ GstDevice *videoDevice(const QByteArray &id);
+
+ QAbstractPlatformSpecificInterface *platformSpecificInterface() override;
+
+protected:
+ QPlatformMediaFormatInfo *createFormatInfo() override;
+ QPlatformVideoDevices *createVideoDevices() override;
+
+ QGStreamerPlatformSpecificInterfaceImplementation m_platformSpecificImplementation;
+};
+
+QT_END_NAMESPACE
+
+#endif
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
new file mode 100644
index 000000000..78ac16eb4
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/qgstreamervideodevices.cpp
@@ -0,0 +1,158 @@
+// 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 <QtMultimedia/qmediadevices.h>
+#include <QtMultimedia/private/qcameradevice_p.h>
+
+#include <common/qgst_p.h>
+#include <common/qgstutils_p.h>
+#include <common/qglist_helper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static gboolean deviceMonitorCallback(GstBus *, GstMessage *message, gpointer m)
+{
+ auto *manager = static_cast<QGstreamerVideoDevices *>(m);
+ QGstDeviceHandle device;
+
+ switch (GST_MESSAGE_TYPE(message)) {
+ case GST_MESSAGE_DEVICE_ADDED:
+ gst_message_parse_device_added(message, &device);
+ manager->addDevice(std::move(device));
+ break;
+ case GST_MESSAGE_DEVICE_REMOVED:
+ gst_message_parse_device_removed(message, &device);
+ manager->removeDevice(std::move(device));
+ break;
+ default:
+ break;
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+QGstreamerVideoDevices::QGstreamerVideoDevices(QPlatformMediaIntegration *integration)
+ : QPlatformVideoDevices(integration),
+ m_deviceMonitor{
+ gst_device_monitor_new(),
+ }
+{
+ gst_device_monitor_add_filter(m_deviceMonitor.get(), "Video/Source", nullptr);
+
+ 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());
+
+ GList *devices = gst_device_monitor_get_devices(m_deviceMonitor.get());
+
+ for (GstDevice *device : QGstUtils::GListRangeAdaptor<GstDevice *>(devices)) {
+ addDevice(QGstDeviceHandle{
+ device,
+ QGstDeviceHandle::HasRef,
+ });
+ }
+
+ g_list_free(devices);
+}
+
+QGstreamerVideoDevices::~QGstreamerVideoDevices()
+{
+ gst_device_monitor_stop(m_deviceMonitor.get());
+}
+
+QList<QCameraDevice> QGstreamerVideoDevices::videoDevices() const
+{
+ QList<QCameraDevice> devices;
+
+ for (const auto &device : m_videoSources) {
+ QCameraDevicePrivate *info = new QCameraDevicePrivate;
+
+ QGString desc{
+ gst_device_get_display_name(device.gstDevice.get()),
+ };
+ info->description = desc.toQString();
+ info->id = device.id;
+
+ QUniqueGstStructureHandle properties{
+ gst_device_get_properties(device.gstDevice.get()),
+ };
+ if (properties) {
+ QGstStructureView view{ properties };
+ auto def = view["is-default"].toBool();
+ info->isDefault = def && *def;
+ }
+
+ 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(QGstDeviceHandle device)
+{
+ Q_ASSERT(gst_device_has_classes(device.get(), "Video/Source"));
+
+ auto it = std::find_if(m_videoSources.begin(), m_videoSources.end(),
+ [&](const QGstRecordDevice &a) { return a.gstDevice == device; });
+
+ if (it != m_videoSources.end())
+ return;
+
+ m_videoSources.push_back(QGstRecordDevice{
+ std::move(device),
+ QByteArray::number(m_idGenerator),
+ });
+ emit videoInputsChanged();
+ m_idGenerator++;
+}
+
+void QGstreamerVideoDevices::removeDevice(QGstDeviceHandle device)
+{
+ 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();
+ }
+}
+
+GstDevice *QGstreamerVideoDevices::videoDevice(const QByteArray &id) const
+{
+ 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
new file mode 100644
index 000000000..a321ae66b
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/qgstreamervideodevices_p.h
@@ -0,0 +1,54 @@
+// 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
+
+//
+// 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 <gst/gst.h>
+#include <qaudiodevice.h>
+#include <vector>
+
+#include <common/qgst_handle_types_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGstreamerVideoDevices : public QPlatformVideoDevices
+{
+public:
+ explicit QGstreamerVideoDevices(QPlatformMediaIntegration *integration);
+ ~QGstreamerVideoDevices();
+
+ QList<QCameraDevice> videoDevices() const override;
+ GstDevice *videoDevice(const QByteArray &id) const;
+
+ void addDevice(QGstDeviceHandle);
+ void removeDevice(QGstDeviceHandle);
+
+private:
+ struct QGstRecordDevice
+ {
+ QGstDeviceHandle gstDevice;
+ QByteArray id;
+ };
+
+ quint64 m_idGenerator = 0;
+ std::vector<QGstRecordDevice> m_videoSources;
+
+ QGstDeviceMonitorHandle m_deviceMonitor;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/qnx/CMakeLists.txt b/src/plugins/multimedia/qnx/CMakeLists.txt
new file mode 100644
index 000000000..e1ac0ffa3
--- /dev/null
+++ b/src/plugins/multimedia/qnx/CMakeLists.txt
@@ -0,0 +1,39 @@
+# 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
+ 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
+ common/qqnxaudiooutput.cpp common/qqnxaudiooutput_p.h
+ common/qqnxmediaeventthread.cpp common/qqnxmediaeventthread_p.h
+ common/qqnxwindowgrabber.cpp common/qqnxwindowgrabber_p.h
+ capture/qqnxaudiorecorder.cpp capture/qqnxaudiorecorder_p.h
+ capture/qqnxmediacapturesession.cpp capture/qqnxmediacapturesession_p.h
+ capture/qqnxmediarecorder.cpp capture/qqnxmediarecorder_p.h
+ mediaplayer/qqnxmediaplayer.cpp mediaplayer/qqnxmediaplayer_p.h
+ mediaplayer/qqnxmediametadata.cpp mediaplayer/qqnxmediametadata_p.h
+ mediaplayer/qqnxvideosink.cpp mediaplayer/qqnxvideosink_p.h
+ mediaplayer/qqnxmediautil.cpp mediaplayer/qqnxmediautil_p.h
+ qqnxformatinfo.cpp qqnxformatinfo_p.h
+ qqnxmediaintegration.cpp qqnxmediaintegration_p.h
+ qqnxvideodevices.cpp qqnxvideodevices_p.h
+ INCLUDE_DIRECTORIES
+ audio
+ camera
+ capture
+ common
+ mediaplayer
+ LIBRARIES
+ Qt::MultimediaPrivate
+ Qt::CorePrivate
+ MMRenderer::MMRenderer
+ strm
+ camapi
+)
diff --git a/src/plugins/multimedia/qnx/camera/qqnxcamera.cpp b/src/plugins/multimedia/qnx/camera/qqnxcamera.cpp
new file mode 100644
index 000000000..6976221bd
--- /dev/null
+++ b/src/plugins/multimedia/qnx/camera/qqnxcamera.cpp
@@ -0,0 +1,820 @@
+// 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"
+#include "qqnxvideosink_p.h"
+
+#include <qcameradevice.h>
+#include <qmediadevices.h>
+
+#include <private/qmediastoragelocation_p.h>
+
+QDebug &operator<<(QDebug &d, const QQnxCamera::VideoFormat &f)
+{
+ 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)
+{
+ switch (status) {
+ case CAMERA_STATUS_DISCONNECTED:
+ return QStringLiteral("No user is connected to the camera");
+ case CAMERA_STATUS_POWERDOWN:
+ return QStringLiteral("Power down");
+ case CAMERA_STATUS_VIDEOVF:
+ return QStringLiteral("The video viewfinder has started");
+ case CAMERA_STATUS_CAPTURE_ABORTED:
+ return QStringLiteral("The capture of a still image failed and was aborted");
+ case CAMERA_STATUS_FILESIZE_WARNING:
+ return QStringLiteral("Time-remaining threshold has been exceeded");
+ case CAMERA_STATUS_FOCUS_CHANGE:
+ return QStringLiteral("The focus has changed on the camera");
+ case CAMERA_STATUS_RESOURCENOTAVAIL:
+ return QStringLiteral("The camera is about to free resources");
+ case CAMERA_STATUS_VIEWFINDER_ERROR:
+ return QStringLiteral(" An unexpected error was encountered while the "
+ "viewfinder was active");
+ case CAMERA_STATUS_MM_ERROR:
+ return QStringLiteral("The recording has stopped due to a memory error or multimedia "
+ "framework error");
+ case CAMERA_STATUS_FILESIZE_ERROR:
+ return QStringLiteral("A file has exceeded the maximum size.");
+ case CAMERA_STATUS_NOSPACE_ERROR:
+ return QStringLiteral("Not enough disk space");
+ case CAMERA_STATUS_BUFFER_UNDERFLOW:
+ return QStringLiteral("The viewfinder is out of buffers");
+ default:
+ break;
+ }
+
+ return {};
+}
+
+QT_BEGIN_NAMESPACE
+
+QQnxCamera::QQnxCamera(camera_unit_t unit, QObject *parent)
+ : QObject(parent)
+ , m_cameraUnit(unit)
+{
+ 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()
+{
+ stop();
+}
+
+camera_unit_t QQnxCamera::unit() const
+{
+ return m_cameraUnit;
+}
+
+QString QQnxCamera::name() const
+{
+ char name[CAMERA_LOCATION_NAMELEN];
+
+ if (camera_get_location_property(m_cameraUnit,
+ CAMERA_LOCATION_NAME, &name, CAMERA_LOCATION_END) != CAMERA_EOK) {
+ qWarning("QQnxCamera: unable to obtain camera name");
+ return {};
+ }
+
+ return QString::fromUtf8(name);
+}
+
+bool QQnxCamera::isValid() const
+{
+ return m_valid;
+}
+
+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) {
+ qWarning("QQnxCamera: unable to start viewfinder");
+ return;
+ }
+
+ m_viewfinderActive = true;
+}
+
+void QQnxCamera::stop()
+{
+ if (!isActive())
+ return;
+
+ if (m_recordingVideo)
+ stopVideoRecording();
+
+ if (camera_stop_viewfinder(m_handle.get()) != CAMERA_EOK)
+ qWarning("QQnxCamera: Failed to stop camera");
+
+ m_viewfinderActive = false;
+}
+
+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, width,
+ CAMERA_IMGPROP_HEIGHT, height,
+ CAMERA_IMGPROP_FRAMERATE, frameRate);
+
+ if (error != CAMERA_EOK) {
+ qWarning("QQnxCamera: failed to set camera format");
+ return false;
+ }
+
+ return true;
+}
+
+bool QQnxCamera::isFocusModeSupported(camera_focusmode_t mode) const
+{
+ return supportedFocusModes().contains(mode);
+}
+
+bool QQnxCamera::setFocusMode(camera_focusmode_t 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;
+}
+
+camera_focusmode_t QQnxCamera::focusMode() const
+{
+ if (!isActive())
+ return CAMERA_FOCUSMODE_OFF;
+
+ camera_focusmode_t 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 CAMERA_FOCUSMODE_OFF;
+ }
+
+ 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)
+{
+ 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
+ constexpr int pixelSize = 40;
+
+ 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;
+ }
+
+ if (setFocusMode(focusMode()))
+ customFocusPointChanged(point);
+}
+
+void QQnxCamera::setManualFocusStep(int step)
+{
+ if (!isActive()) {
+ qWarning("QQnxCamera: Failed to set focus distance - view finder not active");
+ return;
+ }
+
+ if (!isFocusModeSupported(CAMERA_FOCUSMODE_MANUAL)) {
+ qWarning("QQnxCamera: Failed to set focus distance - manual focus mode not supported");
+ return;
+ }
+
+ if (camera_set_manual_focus_step(m_handle.get(), step) != CAMERA_EOK)
+ qWarning("QQnxCamera: Failed to set focus distance");
+}
+
+int QQnxCamera::manualFocusStep() const
+{
+ return focusStep().step;
+}
+
+int QQnxCamera::maxFocusStep() const
+{
+ return focusStep().maxStep;
+}
+
+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 invalidStep;
+ }
+
+ return focusStep;
+}
+
+
+QSize QQnxCamera::viewFinderSize() const
+{
+ // get the size of the viewfinder
+ int width = 0;
+ int height = 0;
+
+ 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 {};
+ }
+
+ return { width, height };
+}
+
+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 set up exposure compensation");
+}
+
+uint32_t QQnxCamera::manualIsoSensitivity() const
+{
+ if (!isActive())
+ return 0;
+
+ uint32_t isoValue;
+
+ if (camera_get_manual_iso(m_handle.get(), &isoValue) != CAMERA_EOK) {
+ qWarning("QQnxCamera: Failed to query ISO value");
+ return 0;
+ }
+
+ return isoValue;
+}
+
+void QQnxCamera::setManualIsoSensitivity(uint32_t value)
+{
+ if (!isActive())
+ return;
+
+ if (camera_set_manual_iso(m_handle.get(), value) != CAMERA_EOK)
+ qWarning("QQnxCamera: Failed to set ISO value");
+}
+
+void QQnxCamera::setManualExposureTime(double seconds)
+{
+ if (!isActive())
+ return;
+
+ if (camera_set_manual_shutter_speed(m_handle.get(), seconds) != CAMERA_EOK)
+ qWarning("QQnxCamera: Failed to set exposure time");
+}
+
+double QQnxCamera::manualExposureTime() const
+{
+ if (!isActive())
+ 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.0;
+ }
+
+ return shutterSpeed;
+}
+
+bool QQnxCamera::hasFeature(camera_feature_t feature) const
+{
+ return camera_has_feature(m_handle.get(), feature);
+}
+
+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;
+ }
+
+ return mode;
+}
+
+void QQnxCamera::setManualWhiteBalance(uint32_t value)
+{
+ if (!isActive())
+ return;
+
+ if (camera_set_manual_white_balance(m_handle.get(), value) != CAMERA_EOK)
+ qWarning("QQnxCamera: failed to set manual white balance");
+}
+
+uint32_t QQnxCamera::manualWhiteBalance() const
+{
+ if (!isActive())
+ return 0;
+
+ uint32_t value;
+
+ if (camera_get_manual_white_balance(m_handle.get(), &value) != CAMERA_EOK) {
+ qWarning("QQnxCamera: failed to get manual white balance");
+ return 0;
+ }
+
+ return value;
+}
+
+bool QQnxCamera::startVideoRecording(const QString &filename)
+{
+ // when preview is video, we must ensure that the recording properties
+ // match the view finder properties
+ if (hasFeature(CAMERA_FEATURE_PREVIEWISVIDEO)) {
+ VideoFormat newFormat = vfFormat();
+
+ 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()
+{
+ m_recordingVideo = false;
+
+ 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
+{
+ if (!isActive())
+ return false;
+
+ return camera_has_feature(m_handle.get(), CAMERA_FEATURE_VIDEO);
+}
+
+camera_handle_t QQnxCamera::handle() const
+{
+ return m_handle.get();
+}
+
+void QQnxCamera::updateZoomLimits()
+{
+ bool smooth;
+
+ 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::updateSupportedWhiteBalanceValues()
+{
+ uint32_t numSupported = 0;
+
+ 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;
+ }
+
+ m_supportedWhiteBalanceValues.resize(numSupported);
+
+ 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");
+
+ m_supportedWhiteBalanceValues.clear();
+ }
+}
+
+QList<camera_vfmode_t> QQnxCamera::supportedVfModes() const
+{
+ return queryValues(camera_get_supported_vf_modes);
+}
+
+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");
+
+ U numSupported = 0;
+
+ if (func(m_handle.get(), 0, &numSupported, nullptr) != CAMERA_EOK) {
+ qWarning("QQnxCamera: unable to query camera value count");
+ return {};
+ }
+
+ QList<T> values(numSupported);
+
+ if (func(m_handle.get(), values.size(), &numSupported, values.data()) != CAMERA_EOK) {
+ qWarning("QQnxCamera: unable to query camera values");
+ return {};
+ }
+
+ return values;
+}
+
+void QQnxCamera::handleVfBuffer(camera_buffer_t *buffer)
+{
+ // process the frame on this thread before locking the mutex
+ auto frame = std::make_unique<QQnxCameraFrameBuffer>(buffer);
+
+ // skip a frame if mutex is busy
+ if (m_currentFrameMutex.tryLock()) {
+ m_currentFrame = std::move(frame);
+ m_currentFrameMutex.unlock();
+
+ Q_EMIT frameAvailable();
+ }
+}
+
+void QQnxCamera::handleVfStatus(camera_devstatus_t status, uint16_t extraData)
+{
+ QMetaObject::invokeMethod(this, "handleStatusChange", Qt::QueuedConnection,
+ Q_ARG(camera_devstatus_t, status),
+ Q_ARG(uint16_t, extraData));
+}
+
+void QQnxCamera::handleStatusChange(camera_devstatus_t status, uint16_t extraData)
+{
+ Q_UNUSED(extraData);
+
+ switch (status) {
+ 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_FRAME_DROPPED:
+ case CAMERA_STATUS_LOWLIGHT:
+ case CAMERA_STATUS_MM_ERROR:
+ case CAMERA_STATUS_NOSPACE_ERROR:
+ 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;
+ }
+}
+
+std::unique_ptr<QQnxCameraFrameBuffer> QQnxCamera::takeCurrentFrame()
+{
+ QMutexLocker l(&m_currentFrameMutex);
+
+ return std::move(m_currentFrame);
+}
+
+void QQnxCamera::viewfinderCallback(camera_handle_t handle, camera_buffer_t *buffer, void *arg)
+{
+ Q_UNUSED(handle);
+
+ auto *camera = static_cast<QQnxCamera*>(arg);
+ camera->handleVfBuffer(buffer);
+}
+
+void QQnxCamera::statusCallback(camera_handle_t handle, camera_devstatus_t status,
+ uint16_t extraData, void *arg)
+{
+ Q_UNUSED(handle);
+
+ auto *camera = static_cast<QQnxCamera*>(arg);
+ camera->handleVfStatus(status, extraData);
+}
+
+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
new file mode 100644
index 000000000..a4ddbfed6
--- /dev/null
+++ b/src/plugins/multimedia/qnx/camera/qqnxcamera_p.h
@@ -0,0 +1,201 @@
+// 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
+// -------------
+//
+// 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 "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
+
+class QQnxCameraFrameBuffer;
+class QQnxMediaCaptureSession;
+class QQnxVideoSink;
+
+class QQnxCamera : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QQnxCamera(camera_unit_t unit, QObject *parent = nullptr);
+ ~QQnxCamera();
+
+ camera_unit_t unit() const;
+
+ QString name() const;
+
+ bool isValid() const;
+
+ bool isActive() const;
+ void start();
+ void stop();
+
+ bool startVideoRecording(const QString &filename);
+ void stopVideoRecording();
+
+ bool setCameraFormat(uint32_t width, uint32_t height, double frameRate);
+
+ bool isFocusModeSupported(camera_focusmode_t mode) const;
+ bool setFocusMode(camera_focusmode_t mode);
+ camera_focusmode_t focusMode() const;
+
+ void setCustomFocusPoint(const QPointF &point);
+
+ void setManualFocusStep(int step);
+ int manualFocusStep() const;
+ int maxFocusStep() const;
+
+ QSize viewFinderSize() const;
+
+ uint32_t minimumZoomLevel() const;
+ uint32_t maximumZoomLevel() const;
+ bool isSmoothZoom() const;
+ double zoomRatio(uint32_t zoomLevel) const;
+ bool setZoomFactor(uint32_t factor);
+
+ void setEvOffset(float ev);
+
+ uint32_t manualIsoSensitivity() const;
+ void setManualIsoSensitivity(uint32_t value);
+ void setManualExposureTime(double seconds);
+ double manualExposureTime() const;
+
+ void setWhiteBalanceMode(camera_whitebalancemode_t mode);
+ camera_whitebalancemode_t whiteBalanceMode() const;
+
+ void setManualWhiteBalance(uint32_t value);
+ uint32_t manualWhiteBalance() const;
+
+ bool hasFeature(camera_feature_t feature) const;
+
+ camera_handle_t handle() const;
+
+ 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;
+
+ QList<camera_frametype_t> supportedRecordingFrameTypes() const;
+
+ QList<uint32_t> supportedWhiteBalanceValues() const;
+
+ bool hasContinuousWhiteBalanceValues() const;
+
+ static QList<camera_unit_t> supportedUnits();
+
+ std::unique_ptr<QQnxCameraFrameBuffer> takeCurrentFrame();
+
+Q_SIGNALS:
+ void focusModeChanged(camera_focusmode_t mode);
+ void customFocusPointChanged(const QPointF &point);
+ void minimumZoomFactorChanged(double factor);
+
+ double maximumZoomFactorChanged(double factor);
+
+ void frameAvailable();
+
+private:
+ struct FocusStep
+ {
+ int step; // current step
+ int maxStep; // max supported step
+ };
+
+ FocusStep focusStep() const;
+
+ struct VideoFormat
+ {
+ uint32_t width;
+ uint32_t height;
+ uint32_t rotation;
+ double frameRate;
+ camera_frametype_t frameType;
+ };
+
+ friend QDebug &operator<<(QDebug&, const VideoFormat&);
+
+ VideoFormat vfFormat() const;
+ void setVfFormat(const VideoFormat &format);
+
+ VideoFormat recordingFormat() const;
+ void setRecordingFormat(const VideoFormat &format);
+
+ void updateZoomLimits();
+ void updateSupportedWhiteBalanceValues();
+ void setColorTemperatureInternal(unsigned temp);
+
+ bool isVideoEncodingSupported() const;
+
+ void handleVfBuffer(camera_buffer_t *buffer);
+
+ // viewfinder callback
+ void handleVfStatus(camera_devstatus_t status, uint16_t extraData);
+
+ // our handler running on main thread
+ 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 *);
+
+ template <typename T, typename U>
+ QList<T> queryValues(QueryFuncPtr<T, U> func) const;
+
+ static void viewfinderCallback(camera_handle_t handle,
+ camera_buffer_t *buffer, void *arg);
+
+ static void statusCallback(camera_handle_t handle, camera_devstatus_t status,
+ uint16_t extraData, void *arg);
+
+ QQnxMediaCaptureSession *m_session = nullptr;
+
+ camera_unit_t m_cameraUnit = CAMERA_UNIT_NONE;
+
+ QQnxCameraHandle m_handle;
+
+ 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
new file mode 100644
index 000000000..6595c5d42
--- /dev/null
+++ b/src/plugins/multimedia/qnx/camera/qqnxcameraframebuffer.cpp
@@ -0,0 +1,299 @@
+// 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) {
+ case CAMERA_FRAMETYPE_NV12:
+ return QVideoFrameFormat::Format_NV12;
+ case CAMERA_FRAMETYPE_RGB8888:
+ return QVideoFrameFormat::Format_ARGB8888;
+ case CAMERA_FRAMETYPE_GRAY8:
+ return QVideoFrameFormat::Format_Y8;
+ case CAMERA_FRAMETYPE_CBYCRY:
+ return QVideoFrameFormat::Format_UYVY;
+ case CAMERA_FRAMETYPE_YCBCR420P:
+ return QVideoFrameFormat::Format_YUV420P;
+ case CAMERA_FRAMETYPE_YCBYCR:
+ return QVideoFrameFormat::Format_YUYV;
+ default:
+ break;
+ }
+
+ return QVideoFrameFormat::Format_Invalid;
+}
+
+static constexpr size_t bufferDataSize(const camera_frame_nv12_t &frame)
+{
+ return frame.uv_offset + frame.uv_stride * frame.height / 2;
+}
+
+static constexpr size_t bufferDataSize(const camera_frame_rgb8888_t &frame)
+{
+ return frame.stride * frame.height;
+}
+
+static constexpr size_t bufferDataSize(const camera_frame_gray8_t &frame)
+{
+ return frame.stride * frame.height;
+}
+
+static constexpr size_t bufferDataSize(const camera_frame_cbycry_t &frame)
+{
+ return frame.bufsize;
+}
+
+static constexpr size_t bufferDataSize(const camera_frame_ycbcr420p_t &frame)
+{
+ return frame.cr_offset + frame.cr_stride * frame.height / 2;
+}
+
+static constexpr size_t bufferDataSize(const camera_frame_ycbycr_t &frame)
+{
+ return frame.stride * frame.height;
+}
+
+static constexpr size_t bufferDataSize(const camera_buffer_t *buffer)
+{
+ switch (buffer->frametype) {
+ case CAMERA_FRAMETYPE_NV12:
+ return bufferDataSize(buffer->framedesc.nv12);
+ case CAMERA_FRAMETYPE_RGB8888:
+ return bufferDataSize(buffer->framedesc.rgb8888);
+ case CAMERA_FRAMETYPE_GRAY8:
+ return bufferDataSize(buffer->framedesc.gray8);
+ case CAMERA_FRAMETYPE_CBYCRY:
+ return bufferDataSize(buffer->framedesc.cbycry);
+ case CAMERA_FRAMETYPE_YCBCR420P:
+ return bufferDataSize(buffer->framedesc.ycbcr420p);
+ case CAMERA_FRAMETYPE_YCBYCR:
+ return bufferDataSize(buffer->framedesc.ycbycr);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static QAbstractVideoBuffer::MapData mapData(const camera_frame_nv12_t &frame,
+ unsigned char *baseAddress)
+{
+
+ return {
+ .planeCount = 2,
+ .bytesPerLine = {
+ toInt(frame.stride),
+ toInt(frame.uv_stride)
+ },
+ .data = {
+ baseAddress,
+ baseAddress + frame.uv_offset
+ },
+ .dataSize = {
+ toInt(frame.stride * frame.height),
+ toInt(frame.uv_stride * frame.height / 2)
+ }
+ };
+}
+
+static QAbstractVideoBuffer::MapData mapData(const camera_frame_rgb8888_t &frame,
+ unsigned char *baseAddress)
+{
+ return {
+ .planeCount = 1,
+ .bytesPerLine = {
+ toInt(frame.stride)
+ },
+ .data = {
+ baseAddress
+ },
+ .dataSize = {
+ toInt(frame.stride * frame.height),
+ }
+ };
+}
+
+static QAbstractVideoBuffer::MapData mapData(const camera_frame_gray8_t &frame,
+ unsigned char *baseAddress)
+{
+ return {
+ .planeCount = 1,
+ .bytesPerLine = {
+ toInt(frame.stride)
+ },
+ .data = {
+ baseAddress
+ },
+ .dataSize = {
+ toInt(frame.stride * frame.height)
+ }
+ };
+}
+
+static QAbstractVideoBuffer::MapData mapData(const camera_frame_cbycry_t &frame,
+ unsigned char *baseAddress)
+{
+ return {
+ .planeCount = 1,
+ .bytesPerLine = {
+ toInt(frame.stride)
+ },
+ .data = {
+ baseAddress
+ },
+ .dataSize = {
+ toInt(frame.bufsize),
+ }
+ };
+}
+
+static QAbstractVideoBuffer::MapData mapData(const camera_frame_ycbcr420p_t &frame,
+ unsigned char *baseAddress)
+{
+ return {
+ .planeCount = 3,
+ .bytesPerLine = {
+ toInt(frame.y_stride),
+ frame.cb_stride,
+ frame.cr_stride,
+ },
+ .data = {
+ baseAddress,
+ baseAddress + frame.cb_offset,
+ baseAddress + frame.cr_offset,
+ },
+ .dataSize = {
+ toInt(frame.y_stride * frame.height),
+ toInt(frame.cb_stride * frame.height / 2),
+ toInt(frame.cr_stride * frame.height / 2)
+ }
+ };
+}
+
+static QAbstractVideoBuffer::MapData mapData(const camera_frame_ycbycr_t &frame,
+ unsigned char *baseAddress)
+{
+ return {
+ .planeCount = 1,
+ .bytesPerLine = {
+ toInt(frame.stride)
+ },
+ .data = {
+ baseAddress
+ },
+ .dataSize = {
+ toInt(frame.stride * frame.height)
+ }
+ };
+}
+
+static QAbstractVideoBuffer::MapData mapData(const camera_buffer_t *buffer,
+ unsigned char *baseAddress)
+{
+ switch (buffer->frametype) {
+ case CAMERA_FRAMETYPE_NV12:
+ return mapData(buffer->framedesc.nv12, baseAddress);
+ case CAMERA_FRAMETYPE_RGB8888:
+ return mapData(buffer->framedesc.rgb8888, baseAddress);
+ case CAMERA_FRAMETYPE_GRAY8:
+ return mapData(buffer->framedesc.gray8, baseAddress);
+ case CAMERA_FRAMETYPE_CBYCRY:
+ return mapData(buffer->framedesc.cbycry, baseAddress);
+ case CAMERA_FRAMETYPE_YCBCR420P:
+ return mapData(buffer->framedesc.ycbcr420p, baseAddress);
+ case CAMERA_FRAMETYPE_YCBYCR:
+ return mapData(buffer->framedesc.ycbycr, baseAddress);
+ default:
+ break;
+ }
+
+ return {};
+}
+
+static constexpr QSize frameSize(const camera_buffer_t *buffer)
+{
+ switch (buffer->frametype) {
+ case CAMERA_FRAMETYPE_NV12:
+ return frameSize(buffer->framedesc.nv12);
+ case CAMERA_FRAMETYPE_RGB8888:
+ return frameSize(buffer->framedesc.rgb8888);
+ case CAMERA_FRAMETYPE_GRAY8:
+ return frameSize(buffer->framedesc.gray8);
+ case CAMERA_FRAMETYPE_CBYCRY:
+ return frameSize(buffer->framedesc.cbycry);
+ case CAMERA_FRAMETYPE_YCBCR420P:
+ return frameSize(buffer->framedesc.ycbcr420p);
+ case CAMERA_FRAMETYPE_YCBYCR:
+ return frameSize(buffer->framedesc.ycbycr);
+ default:
+ break;
+ }
+
+ return {};
+}
+
+QT_BEGIN_NAMESPACE
+
+QQnxCameraFrameBuffer::QQnxCameraFrameBuffer(const camera_buffer_t *buffer, QRhi *rhi)
+ : QHwVideoBuffer(rhi ? QVideoFrame::RhiTextureHandle : QVideoFrame::NoHandle, rhi),
+ m_rhi(rhi),
+ m_pixelFormat(::frameTypeToPixelFormat(buffer->frametype)),
+ m_dataSize(::bufferDataSize(buffer))
+{
+ if (m_dataSize <= 0)
+ return;
+
+ m_data = std::make_unique<unsigned char[]>(m_dataSize);
+
+ memcpy(m_data.get(), buffer->framebuf, m_dataSize);
+
+ m_mapData = ::mapData(buffer, m_data.get());
+
+ m_frameSize = ::frameSize(buffer);
+}
+
+QAbstractVideoBuffer::MapData QQnxCameraFrameBuffer::map(QtVideo::MapMode)
+{
+ return m_mapData;
+}
+
+void QQnxCameraFrameBuffer::unmap()
+{
+}
+
+QVideoFrameFormat::PixelFormat QQnxCameraFrameBuffer::pixelFormat() const
+{
+ return m_pixelFormat;
+}
+
+QSize QQnxCameraFrameBuffer::size() const
+{
+ return m_frameSize;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/qnx/camera/qqnxcameraframebuffer_p.h b/src/plugins/multimedia/qnx/camera/qqnxcameraframebuffer_p.h
new file mode 100644
index 000000000..20f724552
--- /dev/null
+++ b/src/plugins/multimedia/qnx/camera/qqnxcameraframebuffer_p.h
@@ -0,0 +1,60 @@
+// 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
+
+//
+// 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/qhwvideobuffer_p.h>
+
+#include <QtCore/qsize.h>
+
+#include <camera/camera_api.h>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+class QRhi;
+
+class QQnxCameraFrameBuffer : public QHwVideoBuffer
+{
+public:
+ explicit QQnxCameraFrameBuffer(const camera_buffer_t *buffer, QRhi *rhi = nullptr);
+
+ QQnxCameraFrameBuffer(const QQnxCameraFrameBuffer&) = delete;
+ QQnxCameraFrameBuffer& operator=(const QQnxCameraFrameBuffer&) = delete;
+
+ MapData map(QtVideo::MapMode mode) override;
+ void unmap() override;
+
+ QVideoFrameFormat::PixelFormat pixelFormat() const;
+
+ QSize size() const;
+
+private:
+ QRhi *m_rhi;
+
+ QVideoFrameFormat::PixelFormat m_pixelFormat;
+
+ std::unique_ptr<unsigned char[]> m_data;
+
+ size_t m_dataSize;
+
+ MapData m_mapData;
+
+ QSize m_frameSize;
+};
+
+QT_END_NAMESPACE
+
+#endif
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
new file mode 100644
index 000000000..3983dddbb
--- /dev/null
+++ b/src/plugins/multimedia/qnx/camera/qqnximagecapture.cpp
@@ -0,0 +1,257 @@
+// 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)
+ : QPlatformImageCapture(parent)
+{
+}
+
+bool QQnxImageCapture::isReadyForCapture() const
+{
+ return m_camera && m_camera->isActive();
+}
+
+int QQnxImageCapture::capture(const QString &fileName)
+{
+ if (!isReadyForCapture()) {
+ Q_EMIT error(-1, QImageCapture::NotReadyError, QPlatformImageCapture::msgCameraNotReady());
+ return -1;
+ }
+
+ // default to PNG format if no format has been specified
+ const QImageCapture::FileFormat format =
+ m_settings.format() == QImageCapture::UnspecifiedFormat
+ ? QImageCapture::PNG : m_settings.format();
+
+ 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;
+}
+
+int QQnxImageCapture::captureToBuffer()
+{
+ if (!isReadyForCapture()) {
+ Q_EMIT error(-1, QImageCapture::NotReadyError, QPlatformImageCapture::msgCameraNotReady());
+ return -1;
+ }
+
+ const int id = m_lastId++;
+
+ auto callback = [this, id](const QVideoFrame &frame) { decodeFrame(id, frame); };
+
+ m_camera->requestVideoFrame(std::move(callback));
+
+ 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();
+
+ // 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");
+ }
+ };
+
+ connect(worker, &QThread::finished, this, std::move(onFinished));
+
+ Q_EMIT imageExposed(id);
+
+ worker->start();
+
+ return future;
+}
+
+void QQnxImageCapture::saveFrame(int id, const QVideoFrame &frame, const QString &fileName)
+{
+ 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
+{
+ return m_settings;
+}
+
+void QQnxImageCapture::setImageSettings(const QImageEncoderSettings &settings)
+{
+ m_settings = settings;
+}
+
+void QQnxImageCapture::setCaptureSession(QQnxMediaCaptureSession *captureSession)
+{
+ if (m_session == captureSession)
+ return;
+
+ if (m_session)
+ m_session->disconnect(this);
+
+ m_session = captureSession;
+
+ if (m_session) {
+ connect(m_session, &QQnxMediaCaptureSession::cameraChanged,
+ this, &QQnxImageCapture::onCameraChanged);
+ }
+
+ onCameraChanged();
+}
+
+void QQnxImageCapture::onCameraChanged()
+{
+ if (m_camera)
+ m_camera->disconnect(this);
+
+ m_camera = m_session ? static_cast<QQnxPlatformCamera*>(m_session->camera()) : nullptr;
+
+ 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;
+
+ 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
new file mode 100644
index 000000000..832039654
--- /dev/null
+++ b/src/plugins/multimedia/qnx/camera/qqnximagecapture_p.h
@@ -0,0 +1,63 @@
+// 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
+
+//
+// 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/qplatformimagecapture_p.h>
+
+#include <QtCore/qfuture.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQnxMediaCaptureSession;
+class QQnxPlatformCamera;
+
+class QThread;
+
+class QQnxImageCapture : public QPlatformImageCapture
+{
+ Q_OBJECT
+public:
+ explicit QQnxImageCapture(QImageCapture *parent);
+
+ bool isReadyForCapture() const override;
+
+ int capture(const QString &fileName) override;
+ int captureToBuffer() override;
+
+ QImageEncoderSettings imageSettings() const override;
+ void setImageSettings(const QImageEncoderSettings &settings) override;
+
+ void setCaptureSession(QQnxMediaCaptureSession *session);
+
+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
+
+#endif
diff --git a/src/plugins/multimedia/qnx/camera/qqnxplatformcamera.cpp b/src/plugins/multimedia/qnx/camera/qqnxplatformcamera.cpp
new file mode 100644
index 000000000..b604f4561
--- /dev/null
+++ b/src/plugins/multimedia/qnx/camera/qqnxplatformcamera.cpp
@@ -0,0 +1,426 @@
+// 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 <private/qvideoframe_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> currentFrameBuffer = m_qnxCamera->takeCurrentFrame();
+
+ if (!currentFrameBuffer)
+ return;
+
+ QVideoFrameFormat format(currentFrameBuffer->size(), currentFrameBuffer->pixelFormat());
+ const QVideoFrame actualFrame =
+ QVideoFramePrivate::createFrame(std::move(currentFrameBuffer), std::move(format));
+
+ 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
new file mode 100644
index 000000000..00a20bbd7
--- /dev/null
+++ b/src/plugins/multimedia/qnx/capture/qqnxaudiorecorder.cpp
@@ -0,0 +1,284 @@
+// 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"
+
+#include <QtCore/qcoreapplication.h>
+
+#include <private/qmediastoragelocation_p.h>
+
+#include <mm/renderer.h>
+
+#include <sys/stat.h>
+#include <sys/strm.h>
+
+static QByteArray buildDevicePath(const QByteArray &deviceId, const QMediaEncoderSettings &settings)
+{
+ QByteArray devicePath = QByteArrayLiteral("snd:/dev/snd/") + deviceId + QByteArrayLiteral("?");
+
+ if (settings.audioSampleRate() > 0)
+ devicePath += QByteArrayLiteral("frate=") + QByteArray::number(settings.audioSampleRate());
+
+ if (settings.audioChannelCount() > 0)
+ devicePath += QByteArrayLiteral("nchan=") + QByteArray::number(settings.audioChannelCount());
+
+ return devicePath;
+}
+
+QT_BEGIN_NAMESPACE
+
+QQnxAudioRecorder::QQnxAudioRecorder(QObject *parent)
+ : QObject(parent)
+{
+ openConnection();
+}
+
+QQnxAudioRecorder::~QQnxAudioRecorder()
+{
+ stop();
+ closeConnection();
+}
+
+void QQnxAudioRecorder::openConnection()
+{
+ static int idCounter = 0;
+
+ m_connection = ConnectionUniquePtr { mmr_connect(nullptr) };
+
+ if (!m_connection) {
+ qWarning("QQnxAudioRecorder: Unable to connect to the multimedia renderer");
+ return;
+ }
+
+ m_id = idCounter++;
+
+ char contextName[256];
+
+ std::snprintf(contextName, sizeof contextName, "QQnxAudioRecorder_%d_%llu",
+ m_id, QCoreApplication::applicationPid());
+
+ m_context = ContextUniquePtr { mmr_context_create(m_connection.get(),
+ contextName, 0, S_IRWXU|S_IRWXG|S_IRWXO) };
+
+ if (m_context) {
+ startMonitoring();
+ } else {
+ qWarning("QQnxAudioRecorder: Unable to create context");
+ closeConnection();
+ }
+}
+
+void QQnxAudioRecorder::closeConnection()
+{
+ m_context.reset();
+ m_context.reset();
+
+ stopMonitoring();
+}
+
+void QQnxAudioRecorder::attach()
+{
+ if (isAttached())
+ return;
+
+ const QString container = m_encoderSettings.mimeType().preferredSuffix();
+ const QString location = QMediaStorageLocation::generateFileName(m_outputUrl.toLocalFile(),
+ QStandardPaths::MusicLocation, container);
+
+ m_audioId = mmr_output_attach(m_context.get(), qPrintable(location), "file");
+
+ if (m_audioId == -1) {
+ qWarning("QQnxAudioRecorder: mmr_output_attach() for file failed");
+ return;
+ }
+
+ configureOutputBitRate();
+
+ const QByteArray devicePath = buildDevicePath(m_inputDeviceId, m_encoderSettings);
+
+ if (mmr_input_attach(m_context.get(), devicePath.constData(), "track") != 0) {
+ qWarning("QQnxAudioRecorder: mmr_input_attach() failed");
+ detach();
+ } else {
+ Q_EMIT actualLocationChanged(location);
+ }
+}
+
+void QQnxAudioRecorder::detach()
+{
+ if (!isAttached())
+ return;
+
+ mmr_input_detach(m_context.get());
+ mmr_output_detach(m_context.get(), m_audioId);
+
+ m_audioId = -1;
+}
+
+void QQnxAudioRecorder::configureOutputBitRate()
+{
+ const int bitRate = m_encoderSettings.audioBitRate();
+
+ if (!isAttached() || bitRate <= 0)
+ return;
+
+ char buf[12];
+ std::snprintf(buf, sizeof buf, "%d", bitRate);
+
+ strm_dict_t *dict = strm_dict_new();
+ dict = strm_dict_set(dict, "audio_bitrate", buf);
+
+ if (mmr_output_parameters(m_context.get(), m_audioId, dict) != 0)
+ qWarning("mmr_output_parameters: setting bitrate failed");
+}
+
+bool QQnxAudioRecorder::isAttached() const
+{
+ return m_context && m_audioId != -1;
+}
+
+void QQnxAudioRecorder::setInputDeviceId(const QByteArray &id)
+{
+ m_inputDeviceId = id;
+}
+
+void QQnxAudioRecorder::setOutputUrl(const QUrl &url)
+{
+ m_outputUrl = url;
+}
+
+void QQnxAudioRecorder::setMediaEncoderSettings(const QMediaEncoderSettings &settings)
+{
+ m_encoderSettings = settings;
+}
+
+void QQnxAudioRecorder::record()
+{
+ if (!isAttached()) {
+ attach();
+
+ if (!isAttached())
+ return;
+ }
+
+ if (mmr_play(m_context.get()) != 0)
+ qWarning("QQnxAudioRecorder: mmr_play() failed");
+}
+
+void QQnxAudioRecorder::stop()
+{
+ if (!isAttached())
+ return;
+
+ mmr_stop(m_context.get());
+
+ detach();
+}
+
+void QQnxAudioRecorder::startMonitoring()
+{
+ m_eventThread = std::make_unique<QQnxMediaEventThread>(m_context.get());
+
+ connect(m_eventThread.get(), &QQnxMediaEventThread::eventPending,
+ this, &QQnxAudioRecorder::readEvents);
+
+ m_eventThread->setObjectName(QStringLiteral("MmrAudioEventThread-") + QString::number(m_id));
+ m_eventThread->start();
+}
+
+void QQnxAudioRecorder::stopMonitoring()
+{
+ if (m_eventThread)
+ m_eventThread.reset();
+}
+
+void QQnxAudioRecorder::readEvents()
+{
+ while (const mmr_event_t *event = mmr_event_get(m_context.get())) {
+ if (event->type == MMR_EVENT_NONE)
+ break;
+
+ switch (event->type) {
+ case MMR_EVENT_STATUS:
+ handleMmEventStatus(event);
+ break;
+ case MMR_EVENT_STATE:
+ handleMmEventState(event);
+ break;
+ case MMR_EVENT_ERROR:
+ handleMmEventError(event);
+ break;
+ case MMR_EVENT_METADATA:
+ case MMR_EVENT_NONE:
+ case MMR_EVENT_OVERFLOW:
+ case MMR_EVENT_WARNING:
+ case MMR_EVENT_PLAYLIST:
+ case MMR_EVENT_INPUT:
+ case MMR_EVENT_OUTPUT:
+ case MMR_EVENT_CTXTPAR:
+ case MMR_EVENT_TRKPAR:
+ case MMR_EVENT_OTHER:
+ break;
+ }
+ }
+
+ if (m_eventThread)
+ m_eventThread->signalRead();
+}
+
+void QQnxAudioRecorder::handleMmEventStatus(const mmr_event_t *event)
+{
+ if (!event || event->type != MMR_EVENT_STATUS)
+ return;
+
+ if (!event->pos_str)
+ return;
+
+ const QByteArray valueBa(event->pos_str);
+
+ bool ok;
+ const qint64 duration = valueBa.toLongLong(&ok);
+
+ if (!ok)
+ qCritical("Could not parse duration from '%s'", valueBa.constData());
+ else
+ durationChanged(duration);
+}
+
+void QQnxAudioRecorder::handleMmEventState(const mmr_event_t *event)
+{
+ if (!event || event->type != MMR_EVENT_STATE)
+ return;
+
+ switch (event->state) {
+ case MMR_STATE_DESTROYED:
+ case MMR_STATE_IDLE:
+ case MMR_STATE_STOPPED:
+ Q_EMIT stateChanged(QMediaRecorder::StoppedState);
+ break;
+ case MMR_STATE_PLAYING:
+ Q_EMIT stateChanged(QMediaRecorder::RecordingState);
+ break;
+ }
+}
+
+void QQnxAudioRecorder::handleMmEventError(const mmr_event_t *event)
+{
+ if (!event)
+ return;
+
+ // When playback is explicitly stopped using mmr_stop(), mm-renderer
+ // generates a STATE event. When the end of media is reached, an ERROR
+ // event is generated and the error code contained in the event information
+ // is set to MMR_ERROR_NONE. When an error causes playback to stop,
+ // the error code is set to something else.
+ if (event->details.error.info.error_code == MMR_ERROR_NONE) {
+ //TODO add error
+ Q_EMIT stateChanged(QMediaRecorder::StoppedState);
+ detach();
+ }
+}
+
+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
new file mode 100644
index 000000000..f343cee14
--- /dev/null
+++ b/src/plugins/multimedia/qnx/capture/qqnxaudiorecorder_p.h
@@ -0,0 +1,103 @@
+// 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
+
+//
+// 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 "mmrenderertypes.h"
+
+#include <QByteArray>
+#include <QUrl>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qtconfigmacros.h>
+
+#include <QtMultimedia/qmediarecorder.h>
+
+#include <private/qplatformmediarecorder_p.h>
+
+#include <mm/renderer.h>
+#include <mm/renderer/types.h>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+class QQnxMediaEventThread;
+
+class QQnxAudioRecorder : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit QQnxAudioRecorder(QObject *parent = nullptr);
+ ~QQnxAudioRecorder();
+
+ void setInputDeviceId(const QByteArray &id);
+ void setOutputUrl(const QUrl &url);
+ void setMediaEncoderSettings(const QMediaEncoderSettings &settings);
+
+ void record();
+ void stop();
+
+Q_SIGNALS:
+ void stateChanged(QMediaRecorder::RecorderState state);
+ void durationChanged(qint64 durationMs);
+ void actualLocationChanged(const QUrl &location);
+
+private:
+ void openConnection();
+ void closeConnection();
+ void attach();
+ void detach();
+ void configureOutputBitRate();
+ void startMonitoring();
+ void stopMonitoring();
+ void readEvents();
+ void handleMmEventStatus(const mmr_event_t *event);
+ void handleMmEventState(const mmr_event_t *event);
+ void handleMmEventError(const mmr_event_t *event);
+
+ bool isAttached() const;
+
+ struct ContextDeleter
+ {
+ void operator()(mmr_context_t *ctx) { if (ctx) mmr_context_destroy(ctx); }
+ };
+
+ struct ConnectionDeleter
+ {
+ void operator()(mmr_connection_t *conn) { if (conn) mmr_disconnect(conn); }
+ };
+
+ using ContextUniquePtr = std::unique_ptr<mmr_context_t, ContextDeleter>;
+ ContextUniquePtr m_context;
+
+ using ConnectionUniquePtr = std::unique_ptr<mmr_connection_t, ConnectionDeleter>;
+ ConnectionUniquePtr m_connection;
+
+ int m_id = -1;
+ int m_audioId = -1;
+
+ QByteArray m_inputDeviceId;
+
+ QUrl m_outputUrl;
+
+ QMediaEncoderSettings m_encoderSettings;
+
+ std::unique_ptr<QQnxMediaEventThread> m_eventThread;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/qnx/capture/qqnxmediacapturesession.cpp b/src/plugins/multimedia/qnx/capture/qqnxmediacapturesession.cpp
new file mode 100644
index 000000000..d73ca7e54
--- /dev/null
+++ b/src/plugins/multimedia/qnx/capture/qqnxmediacapturesession.cpp
@@ -0,0 +1,121 @@
+// 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 "qqnxplatformcamera_p.h"
+#include "qqnximagecapture_p.h"
+#include "qqnxmediarecorder_p.h"
+#include "qqnxvideosink_p.h"
+#include "qvideosink.h"
+
+QT_BEGIN_NAMESPACE
+
+QQnxMediaCaptureSession::QQnxMediaCaptureSession()
+ : QPlatformMediaCaptureSession()
+{
+}
+
+QQnxMediaCaptureSession::~QQnxMediaCaptureSession()
+{
+}
+
+QPlatformCamera *QQnxMediaCaptureSession::camera()
+{
+ return m_camera;
+}
+
+void QQnxMediaCaptureSession::setCamera(QPlatformCamera *camera)
+{
+ if (camera == m_camera)
+ return;
+
+ if (m_camera)
+ m_camera->setCaptureSession(nullptr);
+
+ m_camera = static_cast<QQnxPlatformCamera *>(camera);
+
+ if (m_camera)
+ m_camera->setCaptureSession(this);
+
+ emit cameraChanged();
+}
+
+QPlatformImageCapture *QQnxMediaCaptureSession::imageCapture()
+{
+ return m_imageCapture;
+}
+
+void QQnxMediaCaptureSession::setImageCapture(QPlatformImageCapture *imageCapture)
+{
+ 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();
+}
+
+QPlatformMediaRecorder *QQnxMediaCaptureSession::mediaRecorder()
+{
+ return m_mediaRecorder;
+}
+
+void QQnxMediaCaptureSession::setMediaRecorder(QPlatformMediaRecorder *mediaRecorder)
+{
+ if (m_mediaRecorder == mediaRecorder)
+ return;
+
+ if (m_mediaRecorder)
+ m_mediaRecorder->setCaptureSession(nullptr);
+
+ m_mediaRecorder = static_cast<QQnxMediaRecorder *>(mediaRecorder);
+
+ if (m_mediaRecorder)
+ m_mediaRecorder->setCaptureSession(this);
+
+ emit encoderChanged();
+}
+
+void QQnxMediaCaptureSession::setAudioInput(QPlatformAudioInput *input)
+{
+ if (m_audioInput == input)
+ return;
+
+ m_audioInput = static_cast<QQnxAudioInput*>(input);
+}
+
+void QQnxMediaCaptureSession::setVideoPreview(QVideoSink *sink)
+{
+ auto qnxSink = sink ? static_cast<QQnxVideoSink *>(sink->platformVideoSink()) : nullptr;
+ if (m_videoSink == qnxSink)
+ return;
+ m_videoSink = qnxSink;
+}
+
+void QQnxMediaCaptureSession::setAudioOutput(QPlatformAudioOutput *output)
+{
+ if (m_audioOutput == output)
+ return;
+ m_audioOutput = output;
+}
+
+QQnxAudioInput * QQnxMediaCaptureSession::audioInput() const
+{
+ return m_audioInput;
+}
+
+QQnxVideoSink * QQnxMediaCaptureSession::videoSink() const
+{
+ return m_videoSink;
+}
+
+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
new file mode 100644
index 000000000..551416a61
--- /dev/null
+++ b/src/plugins/multimedia/qnx/capture/qqnxmediacapturesession_p.h
@@ -0,0 +1,67 @@
+// 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
+
+//
+// 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/qplatformmediacapture_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQnxAudioInput;
+class QQnxPlatformCamera;
+class QQnxImageCapture;
+class QQnxMediaRecorder;
+class QQnxVideoSink;
+
+class QQnxMediaCaptureSession : public QPlatformMediaCaptureSession
+{
+ Q_OBJECT
+
+public:
+ QQnxMediaCaptureSession();
+ ~QQnxMediaCaptureSession();
+
+ QPlatformCamera *camera() override;
+ void setCamera(QPlatformCamera *camera) override;
+
+ QPlatformImageCapture *imageCapture() override;
+ void setImageCapture(QPlatformImageCapture *imageCapture) override;
+
+ QPlatformMediaRecorder *mediaRecorder() override;
+ void setMediaRecorder(QPlatformMediaRecorder *mediaRecorder) override;
+
+ void setAudioInput(QPlatformAudioInput *input) override;
+
+ void setVideoPreview(QVideoSink *sink) override;
+
+ void setAudioOutput(QPlatformAudioOutput *output) override;
+
+ QQnxAudioInput *audioInput() const;
+
+ QQnxVideoSink *videoSink() const;
+
+private:
+ QQnxPlatformCamera *m_camera = nullptr;
+ QQnxImageCapture *m_imageCapture = nullptr;
+ QQnxMediaRecorder *m_mediaRecorder = nullptr;
+ QQnxAudioInput *m_audioInput = nullptr;
+ QPlatformAudioOutput *m_audioOutput = nullptr;
+ QQnxVideoSink *m_videoSink = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/qnx/capture/qqnxmediarecorder.cpp b/src/plugins/multimedia/qnx/capture/qqnxmediarecorder.cpp
new file mode 100644
index 000000000..62ac030db
--- /dev/null
+++ b/src/plugins/multimedia/qnx/capture/qqnxmediarecorder.cpp
@@ -0,0 +1,115 @@
+// 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"
+
+#include <private/qplatformcamera_p.h>
+
+#include <QDebug>
+#include <QUrl>
+
+QT_BEGIN_NAMESPACE
+
+QQnxMediaRecorder::QQnxMediaRecorder(QMediaRecorder *parent)
+ : QPlatformMediaRecorder(parent)
+{
+}
+
+bool QQnxMediaRecorder::isLocationWritable(const QUrl &/*location*/) const
+{
+ return true;
+}
+
+void QQnxMediaRecorder::setCaptureSession(QQnxMediaCaptureSession *session)
+{
+ m_session = session;
+}
+
+void QQnxMediaRecorder::record(QMediaEncoderSettings &settings)
+{
+ if (!m_session)
+ return;
+
+ m_audioRecorder.disconnect();
+
+ if (hasCamera()) {
+ startVideoRecording(settings);
+ } else {
+ QObject::connect(&m_audioRecorder, &QQnxAudioRecorder::durationChanged,
+ [this](qint64 d) { durationChanged(d); });
+
+ QObject::connect(&m_audioRecorder, &QQnxAudioRecorder::stateChanged,
+ [this](QMediaRecorder::RecorderState s) { stateChanged(s); });
+
+ QObject::connect(&m_audioRecorder, &QQnxAudioRecorder::actualLocationChanged,
+ [this](const QUrl &l) { actualLocationChanged(l); });
+
+ startAudioRecording(settings);
+ }
+}
+
+void QQnxMediaRecorder::stop()
+{
+ if (hasCamera()) {
+ stopVideoRecording();
+ } else {
+ m_audioRecorder.stop();
+ }
+}
+
+void QQnxMediaRecorder::startAudioRecording(QMediaEncoderSettings &settings)
+{
+ if (!m_session)
+ return;
+
+ QQnxAudioInput *audioInput = m_session->audioInput();
+
+ if (!audioInput)
+ return;
+
+ m_audioRecorder.setInputDeviceId(audioInput->device.id());
+ m_audioRecorder.setMediaEncoderSettings(settings);
+ m_audioRecorder.setOutputUrl(outputLocation());
+ m_audioRecorder.record();
+}
+
+void QQnxMediaRecorder::startVideoRecording(QMediaEncoderSettings &settings)
+{
+ if (!hasCamera())
+ return;
+
+ auto *camera = static_cast<QQnxPlatformCamera*>(m_session->camera());
+
+ camera->setMediaEncoderSettings(settings);
+ camera->setOutputUrl(outputLocation());
+
+ if (camera->startVideoRecording())
+ stateChanged(QMediaRecorder::RecordingState);
+}
+
+void QQnxMediaRecorder::stopVideoRecording()
+{
+ if (!hasCamera())
+ return;
+
+ auto *camera = static_cast<QQnxPlatformCamera*>(m_session->camera());
+
+ camera->stop();
+
+ stateChanged(QMediaRecorder::StoppedState);
+}
+
+bool QQnxMediaRecorder::hasCamera() const
+{
+ return m_session && m_session->camera();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/qnx/capture/qqnxmediarecorder_p.h b/src/plugins/multimedia/qnx/capture/qqnxmediarecorder_p.h
new file mode 100644
index 000000000..8b3ea21d3
--- /dev/null
+++ b/src/plugins/multimedia/qnx/capture/qqnxmediarecorder_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 QQNXMEDIARECORDER_H
+#define QQNXMEDIARECORDER_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 "qqnxaudiorecorder_p.h"
+
+#include <private/qplatformmediarecorder_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQnxMediaCaptureSession;
+
+class QQnxMediaRecorder : public QPlatformMediaRecorder
+{
+public:
+ explicit QQnxMediaRecorder(QMediaRecorder *parent);
+
+ bool isLocationWritable(const QUrl &location) const override;
+
+ void record(QMediaEncoderSettings &settings) override;
+ void stop() override;
+
+ void setCaptureSession(QQnxMediaCaptureSession *session);
+
+private:
+ bool hasCamera() const;
+
+ void startAudioRecording(QMediaEncoderSettings &settings);
+ void startVideoRecording(QMediaEncoderSettings &settings);
+ void stopVideoRecording();
+
+ QQnxAudioRecorder m_audioRecorder;
+
+ QQnxMediaCaptureSession *m_session = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/qnx/common/mmrenderertypes.h b/src/plugins/multimedia/qnx/common/mmrenderertypes.h
new file mode 100644
index 000000000..f1d498388
--- /dev/null
+++ b/src/plugins/multimedia/qnx/common/mmrenderertypes.h
@@ -0,0 +1,95 @@
+// 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
+
+//
+// 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 <mm/renderer.h>
+#include <mm/renderer/types.h>
+
+extern "C" {
+// ### replace with proper include: mm/renderer/events.h
+typedef enum mmr_state {
+ MMR_STATE_DESTROYED,
+ MMR_STATE_IDLE,
+ MMR_STATE_STOPPED,
+ MMR_STATE_PLAYING
+} mmr_state_t;
+
+typedef enum mmr_event_type {
+ MMR_EVENT_NONE,
+ MMR_EVENT_ERROR,
+ MMR_EVENT_STATE,
+ MMR_EVENT_OVERFLOW,
+ MMR_EVENT_WARNING,
+ MMR_EVENT_STATUS,
+ MMR_EVENT_METADATA,
+ MMR_EVENT_PLAYLIST,
+ MMR_EVENT_INPUT,
+ MMR_EVENT_OUTPUT,
+ MMR_EVENT_CTXTPAR,
+ MMR_EVENT_TRKPAR,
+ MMR_EVENT_OTHER
+} mmr_event_type_t;
+
+typedef struct mmr_event {
+ mmr_event_type_t type;
+ mmr_state_t state;
+ int speed;
+ union mmr_event_details {
+
+ struct mmr_event_state {
+ mmr_state_t oldstate;
+ int oldspeed;
+ } state;
+
+ struct mmr_event_error {
+ mmr_error_info_t info;
+ } error;
+
+ struct mmr_event_warning {
+ const char *str;
+ const strm_string_t *obj;
+ } warning;
+
+ struct mmr_event_metadata {
+ unsigned index;
+ } metadata;
+
+ struct mmr_event_trkparam {
+ unsigned index;
+ } trkparam;
+
+ struct mmr_event_playlist {
+ unsigned start;
+ unsigned end;
+ unsigned length;
+ } playlist;
+
+ struct mmr_event_output {
+ unsigned id;
+ } output;
+ } details;
+
+ const strm_string_t* pos_obj;
+ const char* pos_str;
+ const strm_dict_t* data;
+ const char* objname;
+ void* usrdata;
+} mmr_event_t;
+
+const mmr_event_t* mmr_event_get(mmr_context_t *ctxt);
+
+}
+
+#endif
diff --git a/src/plugins/multimedia/qnx/common/qqnxaudioinput.cpp b/src/plugins/multimedia/qnx/common/qqnxaudioinput.cpp
new file mode 100644
index 000000000..fff3cf1eb
--- /dev/null
+++ b/src/plugins/multimedia/qnx/common/qqnxaudioinput.cpp
@@ -0,0 +1,25 @@
+// 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"
+
+QT_BEGIN_NAMESPACE
+
+QQnxAudioInput::QQnxAudioInput(QAudioInput *parent)
+ : QPlatformAudioInput(parent)
+{
+}
+
+QQnxAudioInput::~QQnxAudioInput()
+{
+}
+
+void QQnxAudioInput::setAudioDevice(const QAudioDevice &info)
+{
+ if (info == device)
+ return;
+
+ device = info;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/qnx/common/qqnxaudioinput_p.h b/src/plugins/multimedia/qnx/common/qqnxaudioinput_p.h
new file mode 100644
index 000000000..62a573cc1
--- /dev/null
+++ b/src/plugins/multimedia/qnx/common/qqnxaudioinput_p.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-2.0-only OR GPL-3.0-only
+
+#ifndef QQNXAUDIOINPUT_P_H
+#define QQNXAUDIOINPUT_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 <private/qplatformaudioinput_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_MULTIMEDIA_EXPORT QQnxAudioInput : public QPlatformAudioInput
+{
+public:
+ explicit QQnxAudioInput(QAudioInput *parent);
+ ~QQnxAudioInput();
+
+ void setAudioDevice(const QAudioDevice &device) override;
+};
+
+QT_END_NAMESPACE
+#endif
diff --git a/src/plugins/multimedia/qnx/common/qqnxaudiooutput.cpp b/src/plugins/multimedia/qnx/common/qqnxaudiooutput.cpp
new file mode 100644
index 000000000..76f8fbafd
--- /dev/null
+++ b/src/plugins/multimedia/qnx/common/qqnxaudiooutput.cpp
@@ -0,0 +1,52 @@
+// 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"
+
+#include <private/qqnxaudiodevice_p.h>
+
+#include <qaudiodevice.h>
+#include <qaudiooutput.h>
+
+#include <QtCore/qloggingcategory.h>
+
+static Q_LOGGING_CATEGORY(qLcMediaAudioOutput, "qt.multimedia.audiooutput")
+
+QT_BEGIN_NAMESPACE
+
+QQnxAudioOutput::QQnxAudioOutput(QAudioOutput *parent)
+ : QPlatformAudioOutput(parent)
+{
+}
+
+QQnxAudioOutput::~QQnxAudioOutput()
+{
+}
+
+void QQnxAudioOutput::setVolume(float vol)
+{
+ if (vol == volume)
+ return;
+ vol = volume;
+ q->volumeChanged(vol);
+}
+
+void QQnxAudioOutput::setMuted(bool m)
+{
+ if (muted == m)
+ return;
+ muted = m;
+ q->mutedChanged(muted);
+}
+
+void QQnxAudioOutput::setAudioDevice(const QAudioDevice &info)
+{
+ if (info == device)
+ return;
+ qCDebug(qLcMediaAudioOutput) << "setAudioDevice" << info.description() << info.isNull();
+ device = info;
+
+ // ### handle device changes
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/qnx/common/qqnxaudiooutput_p.h b/src/plugins/multimedia/qnx/common/qqnxaudiooutput_p.h
new file mode 100644
index 000000000..2ae5844e6
--- /dev/null
+++ b/src/plugins/multimedia/qnx/common/qqnxaudiooutput_p.h
@@ -0,0 +1,39 @@
+// 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
+
+//
+// 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/qplatformaudiooutput_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioDevice;
+class QAudioOutput;
+
+class Q_MULTIMEDIA_EXPORT QQnxAudioOutput : public QPlatformAudioOutput
+{
+public:
+ explicit QQnxAudioOutput(QAudioOutput *parent);
+ ~QQnxAudioOutput();
+
+ void setAudioDevice(const QAudioDevice &) override;
+ void setVolume(float volume) override;
+ void setMuted(bool muted) override;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/qnx/common/qqnxmediaeventthread.cpp b/src/plugins/multimedia/qnx/common/qqnxmediaeventthread.cpp
new file mode 100644
index 000000000..f0cc9b1c0
--- /dev/null
+++ b/src/plugins/multimedia/qnx/common/qqnxmediaeventthread.cpp
@@ -0,0 +1,98 @@
+// 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"
+
+#include <QtCore/QDebug>
+
+#include <errno.h>
+#include <mm/renderer/types.h>
+#include <sys/neutrino.h>
+
+extern "C" {
+// ### Include mm/renderer/events.h once we have it
+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;
+
+QQnxMediaEventThread::QQnxMediaEventThread(mmr_context_t *context)
+ : QThread(),
+ m_mmrContext(context)
+{
+ if (Q_UNLIKELY((m_channelId = ChannelCreate(_NTO_CHF_DISCONNECT
+ | _NTO_CHF_UNBLOCK
+ | _NTO_CHF_PRIVATE)) == -1)) {
+ qFatal("QQnxMediaEventThread: Can't continue without a channel");
+ }
+
+ if (Q_UNLIKELY((m_connectionId = ConnectAttach(0, 0, m_channelId,
+ _NTO_SIDE_CHANNEL, 0)) == -1)) {
+ ChannelDestroy(m_channelId);
+ qFatal("QQnxMediaEventThread: Can't continue without a channel connection");
+ }
+
+ SIGEV_PULSE_INIT(&m_mmrEvent, m_connectionId, SIGEV_PULSE_PRIO_INHERIT, c_mmrCode, 0);
+}
+
+QQnxMediaEventThread::~QQnxMediaEventThread()
+{
+ // block until thread terminates
+ shutdown();
+
+ ConnectDetach(m_connectionId);
+ ChannelDestroy(m_channelId);
+}
+
+void QQnxMediaEventThread::run()
+{
+ int armResult = mmr_event_arm(m_mmrContext, &m_mmrEvent);
+ if (armResult > 0)
+ emit eventPending();
+
+ while (1) {
+ struct _pulse msg;
+ memset(&msg, 0, sizeof(msg));
+ int receiveId = MsgReceive(m_channelId, &msg, sizeof(msg), nullptr);
+ if (receiveId == 0) {
+ if (msg.code == c_mmrCode) {
+ emit eventPending();
+ } else if (msg.code == c_readCode) {
+ armResult = mmr_event_arm(m_mmrContext, &m_mmrEvent);
+ if (armResult > 0)
+ emit eventPending();
+ } else if (msg.code == c_quitCode) {
+ break;
+ } else {
+ qWarning() << Q_FUNC_INFO << "Unexpected pulse" << msg.code;
+ }
+ } else if (receiveId > 0) {
+ qWarning() << Q_FUNC_INFO << "Unexpected message" << msg.code;
+ } else {
+ qWarning() << Q_FUNC_INFO << "MsgReceive error" << strerror(errno);
+ }
+ }
+}
+
+void QQnxMediaEventThread::signalRead()
+{
+ MsgSendPulse(m_connectionId, SIGEV_PULSE_PRIO_INHERIT, c_readCode, 0);
+}
+
+void QQnxMediaEventThread::shutdown()
+{
+ MsgSendPulse(m_connectionId, SIGEV_PULSE_PRIO_INHERIT, c_quitCode, 0);
+
+ // 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
new file mode 100644
index 000000000..a622fcb62
--- /dev/null
+++ b/src/plugins/multimedia/qnx/common/qqnxmediaeventthread_p.h
@@ -0,0 +1,55 @@
+// 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
+
+//
+// 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/QThread>
+
+#include <sys/neutrino.h>
+#include <sys/siginfo.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef struct mmr_context mmr_context_t;
+
+class QQnxMediaEventThread : public QThread
+{
+ Q_OBJECT
+
+public:
+ QQnxMediaEventThread(mmr_context_t *context);
+ ~QQnxMediaEventThread() override;
+
+ void signalRead();
+
+protected:
+ void run() override;
+
+Q_SIGNALS:
+ void eventPending();
+
+private:
+ void shutdown();
+
+ int m_channelId;
+ int m_connectionId;
+ struct sigevent m_mmrEvent;
+ mmr_context_t *m_mmrContext;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQnxMediaEventThread_H
diff --git a/src/plugins/multimedia/qnx/common/qqnxwindowgrabber.cpp b/src/plugins/multimedia/qnx/common/qqnxwindowgrabber.cpp
new file mode 100644
index 000000000..28f16b70a
--- /dev/null
+++ b/src/plugins/multimedia/qnx/common/qqnxwindowgrabber.cpp
@@ -0,0 +1,435 @@
+// 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"
+
+#include <QAbstractEventDispatcher>
+#include <QDebug>
+#include <QGuiApplication>
+#include <QImage>
+#include <QThread>
+#include <qpa/qplatformnativeinterface.h>
+
+#include <QOpenGLContext>
+#include <QOpenGLFunctions>
+
+#include <rhi/qrhi.h>
+
+#include <cstring>
+
+#include <EGL/egl.h>
+#include <errno.h>
+
+QT_BEGIN_NAMESPACE
+
+static PFNEGLCREATEIMAGEKHRPROC s_eglCreateImageKHR;
+static PFNEGLDESTROYIMAGEKHRPROC s_eglDestroyImageKHR;
+
+class QQnxWindowGrabberImage
+{
+public:
+ QQnxWindowGrabberImage();
+ ~QQnxWindowGrabberImage();
+
+ bool initialize(screen_context_t screenContext);
+
+ QQnxWindowGrabber::BufferView getBuffer(screen_window_t window, const QSize &size);
+ GLuint getTexture(screen_window_t window, const QSize &size);
+
+private:
+ bool grab(screen_window_t window);
+ bool resize(const QSize &size);
+
+ QSize m_size;
+ screen_pixmap_t m_pixmap;
+ screen_buffer_t m_pixmapBuffer;
+ EGLImageKHR m_eglImage;
+ GLuint m_glTexture;
+ unsigned char *m_bufferAddress;
+ int m_bufferStride;
+};
+
+QQnxWindowGrabber::QQnxWindowGrabber(QObject *parent)
+ : QObject(parent),
+ m_windowParent(nullptr),
+ m_window(nullptr),
+ m_screenContext(nullptr),
+ m_rhi(nullptr),
+ m_active(false),
+ m_eglImageSupported(false),
+ m_startPending(false)
+{
+ // grab the window frame with 60 frames per second
+ m_timer.setInterval(1000/60);
+
+ connect(&m_timer, &QTimer::timeout, this, &QQnxWindowGrabber::triggerUpdate);
+
+ QCoreApplication::eventDispatcher()->installNativeEventFilter(this);
+
+ // Use of EGL images can be disabled by setting QQNX_MM_DISABLE_EGLIMAGE_SUPPORT to something
+ // non-zero. This is probably useful only to test that this path still works since it results
+ // in a high CPU load.
+ if (!s_eglCreateImageKHR && qgetenv("QQNX_MM_DISABLE_EGLIMAGE_SUPPORT").toInt() == 0) {
+ s_eglCreateImageKHR = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"));
+ s_eglDestroyImageKHR = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR"));
+ }
+
+ QPlatformNativeInterface *const nativeInterface = QGuiApplication::platformNativeInterface();
+ if (nativeInterface) {
+ m_screenContext = static_cast<screen_context_t>(
+ nativeInterface->nativeResourceForIntegration("screenContext"));
+ }
+
+ // Create a parent window for the window whose content will be grabbed. Since the
+ // window is only a buffer conduit, the characteristics of the parent window are
+ // irrelevant. The contents of the window can be grabbed so long as the window
+ // joins the parent window's group and the parent window is in this process.
+ // Using the window that displays this content isn't possible because there's no
+ // way to reliably retrieve it from this code or any calling code.
+ screen_create_window(&m_windowParent, m_screenContext);
+ screen_create_window_group(m_windowParent, nullptr);
+}
+
+QQnxWindowGrabber::~QQnxWindowGrabber()
+{
+ screen_destroy_window(m_windowParent);
+ QCoreApplication::eventDispatcher()->removeNativeEventFilter(this);
+}
+
+void QQnxWindowGrabber::setFrameRate(int frameRate)
+{
+ m_timer.setInterval(1000/frameRate);
+}
+
+void QQnxWindowGrabber::setWindowId(const QByteArray &windowId)
+{
+ m_windowId = windowId;
+}
+
+void QQnxWindowGrabber::setRhi(QRhi *rhi)
+{
+ m_rhi = rhi;
+
+ checkForEglImageExtension();
+}
+
+void QQnxWindowGrabber::start()
+{
+ if (m_active)
+ return;
+
+ if (!m_window) {
+ m_startPending = true;
+ return;
+ }
+
+ m_startPending = false;
+
+ if (!m_screenContext)
+ screen_get_window_property_pv(m_window, SCREEN_PROPERTY_CONTEXT, reinterpret_cast<void**>(&m_screenContext));
+
+ m_timer.start();
+
+ m_active = true;
+}
+
+void QQnxWindowGrabber::stop()
+{
+ if (!m_active)
+ return;
+
+ resetBuffers();
+
+ m_timer.stop();
+
+ m_active = false;
+}
+
+void QQnxWindowGrabber::pause()
+{
+ m_timer.stop();
+}
+
+void QQnxWindowGrabber::resume()
+{
+ if (!m_active)
+ return;
+
+ m_timer.start();
+}
+
+void QQnxWindowGrabber::forceUpdate()
+{
+ if (!m_active)
+ return;
+
+ triggerUpdate();
+}
+
+bool QQnxWindowGrabber::handleScreenEvent(screen_event_t screen_event)
+{
+
+ int eventType;
+ if (screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TYPE, &eventType) != 0) {
+ qWarning() << "QQnxWindowGrabber: Failed to query screen event type";
+ return false;
+ }
+
+ if (eventType != SCREEN_EVENT_CREATE)
+ return false;
+
+ screen_window_t window = 0;
+ if (screen_get_event_property_pv(screen_event, SCREEN_PROPERTY_WINDOW, (void**)&window) != 0) {
+ qWarning() << "QQnxWindowGrabber: Failed to query window property";
+ return false;
+ }
+
+ const int maxIdStrLength = 128;
+ char idString[maxIdStrLength];
+ if (screen_get_window_property_cv(window, SCREEN_PROPERTY_ID_STRING, maxIdStrLength, idString) != 0) {
+ qWarning() << "QQnxWindowGrabber: Failed to query window ID string";
+ return false;
+ }
+
+ // Grab windows that have a non-empty ID string and a matching window id to grab
+ if (idString[0] != '\0' && m_windowId == idString) {
+ m_window = window;
+
+ if (m_startPending)
+ start();
+ }
+
+ return false;
+}
+
+bool QQnxWindowGrabber::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *)
+{
+ if (eventType == "screen_event_t") {
+ const screen_event_t event = static_cast<screen_event_t>(message);
+ return handleScreenEvent(event);
+ }
+
+ return false;
+}
+
+QByteArray QQnxWindowGrabber::windowGroupId() const
+{
+ char groupName[256];
+ memset(groupName, 0, sizeof(groupName));
+ screen_get_window_property_cv(m_windowParent,
+ SCREEN_PROPERTY_GROUP,
+ sizeof(groupName) - 1,
+ groupName);
+ return QByteArray(groupName);
+}
+
+bool QQnxWindowGrabber::isEglImageSupported() const
+{
+ return m_eglImageSupported;
+}
+
+void QQnxWindowGrabber::checkForEglImageExtension()
+{
+ m_eglImageSupported = false;
+
+ if (!m_rhi || m_rhi->backend() != QRhi::OpenGLES2)
+ return;
+
+ const EGLDisplay defaultDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+ const char *vendor = eglQueryString(defaultDisplay, EGL_VENDOR);
+
+ if (vendor && std::strstr(vendor, "VMWare"))
+ return;
+
+ const char *eglExtensions = eglQueryString(defaultDisplay, EGL_EXTENSIONS);
+
+ if (!eglExtensions)
+ return;
+
+ m_eglImageSupported = std::strstr(eglExtensions, "EGL_KHR_image")
+ && s_eglCreateImageKHR
+ && s_eglDestroyImageKHR;
+}
+
+void QQnxWindowGrabber::triggerUpdate()
+{
+ int size[2] = { 0, 0 };
+
+ const int result = screen_get_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, size);
+
+ if (result != 0) {
+ resetBuffers();
+ qWarning() << "QQnxWindowGrabber: cannot get window size:" << strerror(errno);
+ return;
+ }
+
+ if (m_size.width() != size[0] || m_size.height() != size[1])
+ m_size = QSize(size[0], size[1]);
+
+ emit updateScene(m_size);
+}
+
+bool QQnxWindowGrabber::selectBuffer()
+{
+ // If we're using egl images we need to double buffer since the gpu may still be using the last
+ // video frame. If we're not, it doesn't matter since the data is immediately copied.
+ if (isEglImageSupported())
+ std::swap(m_frontBuffer, m_backBuffer);
+
+ if (m_frontBuffer)
+ return true;
+
+ auto frontBuffer = std::make_unique<QQnxWindowGrabberImage>();
+
+ if (!frontBuffer->initialize(m_screenContext))
+ return false;
+
+ m_frontBuffer = std::move(frontBuffer);
+
+ return true;
+}
+
+int QQnxWindowGrabber::getNextTextureId()
+{
+ if (!selectBuffer())
+ return 0;
+
+ return m_frontBuffer->getTexture(m_window, m_size);
+}
+
+QQnxWindowGrabber::BufferView QQnxWindowGrabber::getNextBuffer()
+{
+ if (!selectBuffer())
+ return {};
+
+ return m_frontBuffer->getBuffer(m_window, m_size);
+}
+
+void QQnxWindowGrabber::resetBuffers()
+{
+ m_frontBuffer.reset();
+ m_backBuffer.reset();
+}
+
+QQnxWindowGrabberImage::QQnxWindowGrabberImage()
+ : m_pixmap(0),
+ m_pixmapBuffer(0),
+ m_eglImage(0),
+ m_glTexture(0),
+ m_bufferAddress(nullptr),
+ m_bufferStride(0)
+{
+}
+
+QQnxWindowGrabberImage::~QQnxWindowGrabberImage()
+{
+ if (m_glTexture)
+ glDeleteTextures(1, &m_glTexture);
+ if (m_eglImage)
+ s_eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), m_eglImage);
+ if (m_pixmap)
+ screen_destroy_pixmap(m_pixmap);
+}
+
+bool QQnxWindowGrabberImage::initialize(screen_context_t screenContext)
+{
+ if (screen_create_pixmap(&m_pixmap, screenContext) != 0) {
+ qWarning() << "QQnxWindowGrabber: cannot create pixmap:" << strerror(errno);
+ return false;
+ }
+ const int usage = SCREEN_USAGE_WRITE | SCREEN_USAGE_READ | SCREEN_USAGE_NATIVE;
+ screen_set_pixmap_property_iv(m_pixmap, SCREEN_PROPERTY_USAGE, &usage);
+
+ // XXX as a matter of fact, the underlying buffer is BGRX8888 (according to
+ // QNX, screen formats can be loose on the ARGB ordering) - as there is no
+ // SCREEN_FORMAT_BGRX8888 constant, we use SCREEN_FORMAT_RGBX8888, which
+ // carries the same depth and allows us to use the buffer.
+ const int format = SCREEN_FORMAT_RGBX8888;
+ screen_set_pixmap_property_iv(m_pixmap, SCREEN_PROPERTY_FORMAT, &format);
+
+ return true;
+}
+
+bool QQnxWindowGrabberImage::resize(const QSize &newSize)
+{
+ if (m_pixmapBuffer) {
+ screen_destroy_pixmap_buffer(m_pixmap);
+ m_pixmapBuffer = 0;
+ m_bufferAddress = 0;
+ m_bufferStride = 0;
+ }
+
+ const int size[2] = { newSize.width(), newSize.height() };
+
+ screen_set_pixmap_property_iv(m_pixmap, SCREEN_PROPERTY_BUFFER_SIZE, size);
+
+ if (screen_create_pixmap_buffer(m_pixmap) == 0) {
+ screen_get_pixmap_property_pv(m_pixmap, SCREEN_PROPERTY_RENDER_BUFFERS,
+ reinterpret_cast<void**>(&m_pixmapBuffer));
+ screen_get_buffer_property_pv(m_pixmapBuffer, SCREEN_PROPERTY_POINTER,
+ reinterpret_cast<void**>(&m_bufferAddress));
+ screen_get_buffer_property_iv(m_pixmapBuffer, SCREEN_PROPERTY_STRIDE, &m_bufferStride);
+ m_size = newSize;
+
+ return true;
+ } else {
+ m_size = QSize();
+ return false;
+ }
+}
+
+bool QQnxWindowGrabberImage::grab(screen_window_t window)
+{
+ const int rect[] = { 0, 0, m_size.width(), m_size.height() };
+ return screen_read_window(window, m_pixmapBuffer, 1, rect, 0) == 0;
+}
+
+QQnxWindowGrabber::BufferView QQnxWindowGrabberImage::getBuffer(
+ screen_window_t window, const QSize &size)
+{
+ if (size != m_size && !resize(size))
+ return {};
+
+ if (!m_bufferAddress || !grab(window))
+ return {};
+
+ return {
+ .width = m_size.width(),
+ .height = m_size.height(),
+ .stride = m_bufferStride,
+ .data = m_bufferAddress
+ };
+}
+
+GLuint QQnxWindowGrabberImage::getTexture(screen_window_t window, const QSize &size)
+{
+ if (size != m_size) {
+ // 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);
+ s_eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), m_eglImage);
+ m_eglImage = 0;
+ }
+ if (!resize(size))
+ return 0;
+ m_eglImage = s_eglCreateImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), EGL_NO_CONTEXT,
+ EGL_NATIVE_PIXMAP_KHR, m_pixmap, 0);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
+ }
+
+ if (!m_pixmap || !grab(window))
+ return 0;
+
+ return m_glTexture;
+}
+
+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
new file mode 100644
index 000000000..1ffd96b63
--- /dev/null
+++ b/src/plugins/multimedia/qnx/common/qqnxwindowgrabber_p.h
@@ -0,0 +1,114 @@
+// 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
+
+//
+// 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.
+//
+
+#define EGL_EGLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <EGL/eglext.h>
+#include <QAbstractNativeEventFilter>
+#include <QObject>
+#include <QSize>
+#include <QTimer>
+
+#include <memory>
+
+#include <screen/screen.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRhi;
+class QQnxWindowGrabberImage;
+
+class QQnxWindowGrabber : public QObject, public QAbstractNativeEventFilter
+{
+ Q_OBJECT
+
+public:
+ struct BufferView
+ {
+ int width = -1;
+ int height = -1;
+ int stride = -1;
+
+ unsigned char *data = nullptr;
+
+ static constexpr int pixelSize = 4; // BGRX8888;
+ };
+
+ explicit QQnxWindowGrabber(QObject *parent = 0);
+ ~QQnxWindowGrabber();
+
+ void setFrameRate(int frameRate);
+
+ void setWindowId(const QByteArray &windowId);
+
+ void setRhi(QRhi *rhi);
+
+ void start();
+ void stop();
+
+ void pause();
+ void resume();
+
+ void forceUpdate();
+
+ bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
+
+ bool handleScreenEvent(screen_event_t event);
+
+ QByteArray windowGroupId() const;
+
+ bool isEglImageSupported() const;
+
+ int getNextTextureId();
+ BufferView getNextBuffer();
+
+signals:
+ void updateScene(const QSize &size);
+
+private slots:
+ void triggerUpdate();
+
+private:
+ bool selectBuffer();
+ void resetBuffers();
+ void checkForEglImageExtension();
+
+ QTimer m_timer;
+
+ QByteArray m_windowId;
+
+ screen_window_t m_windowParent;
+ screen_window_t m_window;
+ screen_context_t m_screenContext;
+
+ std::unique_ptr<QQnxWindowGrabberImage> m_frontBuffer;
+ std::unique_ptr<QQnxWindowGrabberImage> m_backBuffer;
+
+ QSize m_size;
+
+ QRhi *m_rhi;
+
+ bool m_active;
+ bool m_eglImageSupported;
+ bool m_startPending;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/qnx/mediaplayer/qqnxmediametadata.cpp b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediametadata.cpp
new file mode 100644
index 000000000..fcd535814
--- /dev/null
+++ b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediametadata.cpp
@@ -0,0 +1,262 @@
+// 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>
+#include <QtCore/qfile.h>
+#include <QtCore/qstringlist.h>
+
+#include <mm/renderer/types.h>
+#include <sys/neutrino.h>
+#include <sys/strm.h>
+
+extern "C" {
+// ### include this properly from mm/renderer/events.h once the toolchain is fixed
+extern strm_dict_t* mmr_metadata_split(strm_dict_t const *md,
+ const char *type,
+ unsigned idx);
+}
+
+static const char *strm_string_getx(const strm_string_t *sstr, const char *defaultValue)
+{
+ return sstr ? strm_string_get(sstr) : defaultValue;
+}
+
+QT_BEGIN_NAMESPACE
+
+QQnxMediaMetaData::QQnxMediaMetaData()
+{
+ clear();
+}
+
+static const char * titleKey = "md_title_name";
+static const char * artistKey = "md_title_artist";
+static const char * commentKey = "md_title_comment";
+static const char * genreKey = "md_title_genre";
+static const char * yearKey = "md_title_year";
+static const char * durationKey = "md_title_duration";
+static const char * bitRateKey = "md_title_bitrate";
+static const char * sampleKey = "md_title_samplerate";
+static const char * albumKey = "md_title_album";
+static const char * trackKey = "md_title_track";
+static const char * widthKey = "md_video_width";
+static const char * heightKey = "md_video_height";
+static const char * mediaTypeKey = "md_title_mediatype";
+static const char * pixelWidthKey = "md_video_pixel_width";
+static const char * pixelHeightKey = "md_video_pixel_height";
+static const char * seekableKey = "md_title_seekable";
+static const char * trackSampleKey = "sample_rate";
+static const char * trackBitRateKey = "bitrate";
+static const char * trackWidthKey = "width";
+static const char * trackHeightKey = "height";
+static const char * trackPixelWidthKey = "pixel_width";
+static const char * trackPixelHeightKey = "pixel_height";
+
+static const int mediaTypeAudioFlag = 4;
+static const int mediaTypeVideoFlag = 2;
+
+bool QQnxMediaMetaData::update(const strm_dict_t *dict)
+{
+ if (!dict) {
+ clear();
+ return true;
+ }
+
+ const strm_string_t *value;
+
+ value = strm_dict_find_rstr(dict, durationKey);
+ m_duration = QByteArray(strm_string_getx(value, "0")).toLongLong();
+
+ value = strm_dict_find_rstr(dict, mediaTypeKey);
+ m_mediaType = QByteArray(strm_string_getx(value, "-1")).toInt();
+
+ value = strm_dict_find_rstr(dict, titleKey);
+ m_title = QString::fromLatin1(QByteArray(strm_string_getx(value, nullptr)));
+
+ value = strm_dict_find_rstr(dict, seekableKey);
+ m_seekable = (strcmp(strm_string_getx(value, "1"), "0") != 0);
+
+ value = strm_dict_find_rstr(dict, artistKey);
+ m_artist = QString::fromLatin1(QByteArray(strm_string_getx(value, nullptr)));
+
+ value = strm_dict_find_rstr(dict, commentKey);
+ m_comment = QString::fromLatin1(QByteArray(strm_string_getx(value, nullptr)));
+
+ value = strm_dict_find_rstr(dict, genreKey);
+ m_genre = QString::fromLatin1(QByteArray(strm_string_getx(value, nullptr)));
+
+ value = strm_dict_find_rstr(dict, yearKey);
+ m_year = QByteArray(strm_string_getx(value, "0")).toInt();
+
+ value = strm_dict_find_rstr(dict, albumKey);
+ m_album = QString::fromLatin1(QByteArray(strm_string_getx(value, nullptr)));
+
+ value = strm_dict_find_rstr(dict, trackKey);
+ m_track = QByteArray(strm_string_getx(value, "0")).toInt();
+
+ strm_dict_t *at = mmr_metadata_split(dict, "audio", 0);
+ if (at) {
+ value = strm_dict_find_rstr(at, trackSampleKey);
+ m_sampleRate = QByteArray(strm_string_getx(value, "0")).toInt();
+
+ value = strm_dict_find_rstr(at, trackBitRateKey);
+ m_audioBitRate = QByteArray(strm_string_getx(value, "0")).toInt();
+
+ strm_dict_destroy(at);
+ } else {
+ value = strm_dict_find_rstr(dict, sampleKey);
+ m_sampleRate = QByteArray(strm_string_getx(value, "0")).toInt();
+
+ value = strm_dict_find_rstr(dict, bitRateKey);
+ m_audioBitRate = QByteArray(strm_string_getx(value, "0")).toInt();
+ }
+
+ strm_dict_t *vt = mmr_metadata_split(dict, "video", 0);
+ if (vt) {
+ value = strm_dict_find_rstr(vt, trackWidthKey);
+ m_width = QByteArray(strm_string_getx(value, "0")).toInt();
+
+ value = strm_dict_find_rstr(vt, trackHeightKey);
+ m_height = QByteArray(strm_string_getx(value, "0")).toInt();
+
+ value = strm_dict_find_rstr(vt, trackPixelWidthKey);
+ m_pixelWidth = QByteArray(strm_string_getx(value, "1")).toFloat();
+
+ value = strm_dict_find_rstr(vt, trackPixelHeightKey);
+ m_pixelHeight = QByteArray(strm_string_getx(value, "1")).toFloat();
+
+ strm_dict_destroy(vt);
+ } else {
+ value = strm_dict_find_rstr(dict, widthKey);
+ m_width = QByteArray(strm_string_getx(value, "0")).toInt();
+
+ value = strm_dict_find_rstr(dict, heightKey);
+ m_height = QByteArray(strm_string_getx(value, "0")).toInt();
+
+ value = strm_dict_find_rstr(dict, pixelWidthKey);
+ m_pixelWidth = QByteArray(strm_string_getx(value, "1")).toFloat();
+
+ value = strm_dict_find_rstr(dict, pixelHeightKey);
+ m_pixelHeight = QByteArray(strm_string_getx(value, "1")).toFloat();
+ }
+
+ return true;
+}
+
+void QQnxMediaMetaData::clear()
+{
+ strm_dict_t *dict;
+ dict = strm_dict_new();
+ update(dict);
+ strm_dict_destroy(dict);
+}
+
+qlonglong QQnxMediaMetaData::duration() const
+{
+ return m_duration;
+}
+
+// Handling of pixel aspect ratio
+//
+// If the pixel aspect ratio is different from 1:1, it means the video needs to be stretched in
+// order to look natural.
+// For example, if the pixel width is 2, and the pixel height is 1, it means a video of 300x200
+// pixels needs to be displayed as 600x200 to look correct.
+// In order to support this the easiest way, we simply pretend that the actual size of the video
+// is 600x200, which will cause the video to be displayed in an aspect ratio of 3:1 instead of 3:2,
+// and therefore look correct.
+
+int QQnxMediaMetaData::height() const
+{
+ return m_height * m_pixelHeight;
+}
+
+int QQnxMediaMetaData::width() const
+{
+ return m_width * m_pixelWidth;
+}
+
+bool QQnxMediaMetaData::hasVideo() const
+{
+ // By default, assume no video if we can't extract the information
+ if (m_mediaType == -1)
+ return false;
+
+ return (m_mediaType & mediaTypeVideoFlag);
+}
+
+bool QQnxMediaMetaData::hasAudio() const
+{
+ // By default, assume audio only if we can't extract the information
+ if (m_mediaType == -1)
+ return true;
+
+ return (m_mediaType & mediaTypeAudioFlag);
+}
+
+QString QQnxMediaMetaData::title() const
+{
+ return m_title;
+}
+
+bool QQnxMediaMetaData::isSeekable() const
+{
+ return m_seekable;
+}
+
+QString QQnxMediaMetaData::artist() const
+{
+ return m_artist;
+}
+
+QString QQnxMediaMetaData::comment() const
+{
+ return m_comment;
+}
+
+QString QQnxMediaMetaData::genre() const
+{
+ return m_genre;
+}
+
+int QQnxMediaMetaData::year() const
+{
+ return m_year;
+}
+
+QString QQnxMediaMetaData::mediaType() const
+{
+ if (hasVideo())
+ return QLatin1String("video");
+ else if (hasAudio())
+ return QLatin1String("audio");
+ else
+ return QString();
+}
+
+int QQnxMediaMetaData::audioBitRate() const
+{
+ return m_audioBitRate;
+}
+
+int QQnxMediaMetaData::sampleRate() const
+{
+ return m_sampleRate;
+}
+
+QString QQnxMediaMetaData::album() const
+{
+ return m_album;
+}
+
+int QQnxMediaMetaData::track() const
+{
+ return m_track;
+}
+
+QSize QQnxMediaMetaData::resolution() const
+{
+ return QSize(width(), height());
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/qnx/mediaplayer/qqnxmediametadata_p.h b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediametadata_p.h
new file mode 100644
index 000000000..db7639dc5
--- /dev/null
+++ b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediametadata_p.h
@@ -0,0 +1,74 @@
+// 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
+
+//
+// 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/qglobal.h>
+#include <QtCore/QSize>
+#include <QtCore/QString>
+
+typedef struct strm_dict strm_dict_t;
+
+QT_BEGIN_NAMESPACE
+
+class QQnxMediaMetaData
+{
+public:
+ QQnxMediaMetaData();
+ bool update(const strm_dict_t *dict);
+ void clear();
+
+ // Duration in milliseconds
+ qlonglong duration() const;
+
+ int height() const;
+ int width() const;
+ bool hasVideo() const;
+ bool hasAudio() const;
+ bool isSeekable() const;
+
+ QString title() const;
+ QString artist() const;
+ QString comment() const;
+ QString genre() const;
+ int year() const;
+ QString mediaType() const;
+ int audioBitRate() const;
+ int sampleRate() const;
+ QString album() const;
+ int track() const;
+ QSize resolution() const;
+
+private:
+ qlonglong m_duration;
+ int m_height;
+ int m_width;
+ int m_mediaType;
+ float m_pixelWidth;
+ float m_pixelHeight;
+ bool m_seekable;
+ QString m_title;
+ QString m_artist;
+ QString m_comment;
+ QString m_genre;
+ int m_year;
+ int m_audioBitRate;
+ int m_sampleRate;
+ QString m_album;
+ int m_track;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/qnx/mediaplayer/qqnxmediaplayer.cpp b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediaplayer.cpp
new file mode 100644
index 000000000..14b190836
--- /dev/null
+++ b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediaplayer.cpp
@@ -0,0 +1,887 @@
+// 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"
+#include "qqnxmediaeventthread_p.h"
+#include "qqnxwindowgrabber_p.h"
+
+#include <private/qhwvideobuffer_p.h>
+#include <private/qvideoframe_p.h>
+
+#include <QtCore/qabstracteventdispatcher.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/quuid.h>
+#include <mm/renderer.h>
+#include <qmediaplayer.h>
+#include <qqnxaudiooutput_p.h>
+#include <qaudiooutput.h>
+
+#include <errno.h>
+#include <sys/strm.h>
+#include <sys/stat.h>
+
+#include <algorithm>
+#include <tuple>
+
+static constexpr int rateToSpeed(qreal rate)
+{
+ return std::floor(rate * 1000);
+}
+
+static constexpr qreal speedToRate(int speed)
+{
+ return std::floor(speed / 1000.0);
+}
+
+static constexpr int normalizeVolume(float volume)
+{
+ return std::clamp<int>(std::floor(volume * 100.0), 0, 100);
+}
+
+static std::tuple<int, int, bool> parseBufferLevel(const QString &value)
+{
+ if (value.isEmpty())
+ return {};
+
+ const int slashPos = value.indexOf('/');
+ if (slashPos <= 0)
+ return {};
+
+ bool ok = false;
+ const int level = value.left(slashPos).toInt(&ok);
+ if (!ok || level < 0)
+ return {};
+
+ const int capacity = value.mid(slashPos + 1).toInt(&ok);
+ if (!ok || capacity < 0)
+ return {};
+
+ return { level, capacity, true };
+}
+
+class QnxTextureBuffer : public QHwVideoBuffer
+{
+public:
+ QnxTextureBuffer(QQnxWindowGrabber *QQnxWindowGrabber)
+ : QHwVideoBuffer(QVideoFrame::RhiTextureHandle)
+ {
+ m_windowGrabber = QQnxWindowGrabber;
+ m_handle = 0;
+ }
+
+ void unmap() override {}
+
+ MapData map(QtVideo::MapMode /*mode*/) override
+ {
+ return {};
+ }
+
+ quint64 textureHandle(QRhi *, int plane) const override
+ {
+ if (plane != 0)
+ return 0;
+ if (!m_handle) {
+ const_cast<QnxTextureBuffer*>(this)->m_handle = m_windowGrabber->getNextTextureId();
+ }
+ return m_handle;
+ }
+
+private:
+ QQnxWindowGrabber *m_windowGrabber;
+ quint64 m_handle;
+};
+
+class QnxRasterBuffer : public QAbstractVideoBuffer
+{
+public:
+ QnxRasterBuffer(QQnxWindowGrabber *windowGrabber) { m_windowGrabber = windowGrabber; }
+
+ MapData map(QtVideo::MapMode mode) override
+ {
+ if (mode != QtVideo::MapMode::ReadOnly)
+ return {};
+
+ if (buffer.data) {
+ qWarning("QnxRasterBuffer: need to unmap before mapping");
+ return {};
+ }
+
+ buffer = m_windowGrabber->getNextBuffer();
+
+ return {
+ .planeCount = 1,
+ .bytesPerLine = { buffer.stride },
+ .data = { buffer.data },
+ .dataSize = { buffer.width * buffer.height * buffer.pixelSize }
+ };
+ }
+
+ void unmap() override
+ {
+ buffer = {};
+ }
+
+ QVideoFrameFormat format() const override { return {}; }
+
+private:
+ QQnxWindowGrabber *m_windowGrabber;
+ QQnxWindowGrabber::BufferView buffer;
+};
+
+QT_BEGIN_NAMESPACE
+
+QQnxMediaPlayer::QQnxMediaPlayer(QMediaPlayer *parent)
+ : QObject(parent)
+ , QPlatformMediaPlayer(parent)
+ , m_windowGrabber(new QQnxWindowGrabber(this))
+{
+ m_flushPositionTimer.setSingleShot(true);
+ m_flushPositionTimer.setInterval(100);
+
+ connect(&m_flushPositionTimer, &QTimer::timeout, this, &QQnxMediaPlayer::flushPosition);
+
+ connect(m_windowGrabber, &QQnxWindowGrabber::updateScene, this, &QQnxMediaPlayer::updateScene);
+
+ openConnection();
+}
+
+QQnxMediaPlayer::~QQnxMediaPlayer()
+{
+ stop();
+ detach();
+ closeConnection();
+}
+
+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"));
+ return;
+ }
+
+ m_id = idCounter++;
+ m_contextName = QString::fromLatin1("QQnxMediaPlayer_%1_%2").arg(m_id)
+ .arg(QCoreApplication::applicationPid());
+ m_context = mmr_context_create(m_connection, m_contextName.toLatin1(),
+ 0, S_IRWXU|S_IRWXG|S_IRWXO);
+ if (!m_context) {
+ emitPError(QString::fromLatin1("Unable to create context"));
+ closeConnection();
+ return;
+ }
+
+ startMonitoring();
+}
+
+void QQnxMediaPlayer::handleMmEventState(const mmr_event_t *event)
+{
+ if (!event || event->type != MMR_EVENT_STATE)
+ return;
+
+ switch (event->state) {
+ case MMR_STATE_DESTROYED:
+ break;
+ case MMR_STATE_IDLE:
+ mediaStatusChanged(QMediaPlayer::NoMedia);
+ stateChanged(QMediaPlayer::StoppedState);
+ detachVideoOutput();
+ detachInput();
+ break;
+ case MMR_STATE_STOPPED:
+ stateChanged(QMediaPlayer::StoppedState);
+ m_windowGrabber->stop();
+
+ if (m_platformVideoSink)
+ m_platformVideoSink->setVideoFrame({});
+ break;
+ case MMR_STATE_PLAYING:
+ if (event->speed == 0) {
+ stateChanged(QMediaPlayer::PausedState);
+ m_windowGrabber->pause();
+ } else if (state() == QMediaPlayer::PausedState) {
+ m_windowGrabber->resume();
+ stateChanged(QMediaPlayer::PlayingState);
+ } else {
+ m_windowGrabber->start();
+ stateChanged(QMediaPlayer::PlayingState);
+ }
+
+ if (event->speed != m_speed) {
+ m_speed = event->speed;
+
+ if (state() != QMediaPlayer::PausedState)
+ m_configuredSpeed = m_speed;
+
+ playbackRateChanged(::speedToRate(m_speed));
+ }
+ break;
+ }
+}
+
+void QQnxMediaPlayer::handleMmEventStatus(const mmr_event_t *event)
+{
+ if (!event || event->type != MMR_EVENT_STATUS)
+ return;
+
+ if (event->data)
+ handleMmEventStatusData(event->data);
+
+ // update pos
+ if (!event->pos_str || isPendingPositionFlush())
+ return;
+
+ const QByteArray valueBa(event->pos_str);
+
+ bool ok;
+ const qint64 position = valueBa.toLongLong(&ok);
+
+ if (!ok)
+ qCritical("Could not parse position from '%s'", valueBa.constData());
+ else
+ handleMmPositionChanged(position);
+}
+
+void QQnxMediaPlayer::handleMmEventStatusData(const strm_dict_t *data)
+{
+ if (!data)
+ return;
+
+ const auto getValue = [data](const char *key) -> QString {
+ const strm_string_t *value = strm_dict_find_rstr(data, key);
+
+ if (!value)
+ return {};
+
+ return QString::fromUtf8(strm_string_get(value));
+ };
+
+ // update bufferProgress
+ const QString bufferLevel = getValue("bufferlevel");
+
+ if (!bufferLevel.isEmpty()) {
+ const auto & [level, capacity, ok] = ::parseBufferLevel(bufferLevel);
+
+ if (ok)
+ updateBufferLevel(level, capacity);
+ else
+ qCritical("Could not parse buffer capacity from '%s'", qUtf8Printable(bufferLevel));
+ }
+
+ // update MediaStatus
+ const QString bufferStatus = getValue("bufferstatus");
+ const QString suspended = getValue("suspended");
+
+ if (suspended == QStringLiteral("yes"))
+ mediaStatusChanged(QMediaPlayer::StalledMedia);
+ else if (bufferStatus == QStringLiteral("buffering"))
+ mediaStatusChanged(QMediaPlayer::BufferingMedia);
+ else if (bufferStatus == QStringLiteral("playing"))
+ mediaStatusChanged(QMediaPlayer::BufferedMedia);
+}
+
+void QQnxMediaPlayer::handleMmEventError(const mmr_event_t *event)
+{
+ if (!event)
+ return;
+
+ // When playback is explicitly stopped using mmr_stop(), mm-renderer
+ // generates a STATE event. When the end of media is reached, an ERROR
+ // event is generated and the error code contained in the event information
+ // is set to MMR_ERROR_NONE. When an error causes playback to stop,
+ // the error code is set to something else.
+ if (event->details.error.info.error_code == MMR_ERROR_NONE) {
+ mediaStatusChanged(QMediaPlayer::EndOfMedia);
+ stateChanged(QMediaPlayer::StoppedState);
+ }
+}
+
+void QQnxMediaPlayer::closeConnection()
+{
+ stopMonitoring();
+
+ if (m_context) {
+ mmr_context_destroy(m_context);
+ m_context = nullptr;
+ m_contextName.clear();
+ }
+
+ if (m_connection) {
+ mmr_disconnect(m_connection);
+ m_connection = nullptr;
+ }
+}
+
+QByteArray QQnxMediaPlayer::resourcePathForUrl(const QUrl &url)
+{
+ // If this is a local file, mmrenderer expects the file:// prefix and an absolute path.
+ // We treat URLs without scheme as local files, most likely someone just forgot to set the
+ // file:// prefix when constructing the URL.
+ if (url.isLocalFile() || url.scheme().isEmpty()) {
+ const QString relativeFilePath = url.scheme().isEmpty() ? url.path() : url.toLocalFile();
+ const QFileInfo fileInfo(relativeFilePath);
+ return QFile::encodeName(QStringLiteral("file://") + fileInfo.absoluteFilePath());
+
+ // HTTP or similar URL
+ } else {
+ return url.toEncoded();
+ }
+}
+
+void QQnxMediaPlayer::attach()
+{
+ // Should only be called in detached state
+ if (isInputAttached())
+ return;
+
+ if (!m_media.isValid() || !m_context) {
+ mediaStatusChanged(QMediaPlayer::NoMedia);
+ return;
+ }
+
+ resetMonitoring();
+
+ if (!(attachVideoOutput() && attachAudioOutput() && attachInput())) {
+ detach();
+ return;
+ }
+
+ mediaStatusChanged(QMediaPlayer::LoadedMedia);
+}
+
+bool QQnxMediaPlayer::attachVideoOutput()
+{
+ if (isVideoOutputAttached()) {
+ qWarning() << "QQnxMediaPlayer: Video output already attached!";
+ return true;
+ }
+
+ if (!m_context) {
+ qWarning() << "QQnxMediaPlayer: No media player context!";
+ return false;
+ }
+
+ const QByteArray windowGroupId = m_windowGrabber->windowGroupId();
+ if (windowGroupId.isEmpty()) {
+ qWarning() << "QQnxMediaPlayer: Unable to find window group";
+ return false;
+ }
+
+ static int winIdCounter = 0;
+
+ const QString windowName = QStringLiteral("QQnxVideoSink_%1_%2")
+ .arg(winIdCounter++)
+ .arg(QCoreApplication::applicationPid());
+
+ m_windowGrabber->setWindowId(windowName.toLatin1());
+
+ if (m_platformVideoSink)
+ m_windowGrabber->setRhi(m_platformVideoSink->rhi());
+
+ // Start with an invisible window, because we just want to grab the frames from it.
+ const QString videoDeviceUrl = QStringLiteral("screen:?winid=%1&wingrp=%2&initflags=invisible&nodstviewport=1")
+ .arg(windowName, QString::fromLatin1(windowGroupId));
+
+ m_videoId = mmr_output_attach(m_context, videoDeviceUrl.toLatin1(), "video");
+
+ if (m_videoId == -1) {
+ qWarning() << "mmr_output_attach() for video failed";
+ return false;
+ }
+
+ return true;
+}
+
+bool QQnxMediaPlayer::attachAudioOutput()
+{
+ if (isAudioOutputAttached()) {
+ qWarning() << "QQnxMediaPlayer: Audio output already attached!";
+ return true;
+ }
+
+ const QByteArray defaultAudioDevice = qgetenv("QQNX_RENDERER_DEFAULT_AUDIO_SINK");
+
+ m_audioId = mmr_output_attach(m_context,
+ defaultAudioDevice.isEmpty() ? "snd:" : defaultAudioDevice.constData(), "audio");
+
+ if (m_audioId == -1) {
+ emitMmError("mmr_output_attach() for audio failed");
+
+ return false;
+ }
+
+ return true;
+}
+
+bool QQnxMediaPlayer::attachInput()
+{
+ if (isInputAttached())
+ return true;
+
+ const QByteArray resourcePath = resourcePathForUrl(m_media);
+
+ if (resourcePath.isEmpty())
+ return false;
+
+ if (mmr_input_attach(m_context, resourcePath.constData(), "track") != 0) {
+ emitMmError(QStringLiteral("mmr_input_attach() failed for ")
+ + QString::fromUtf8(resourcePath));
+
+ mediaStatusChanged(QMediaPlayer::InvalidMedia);
+
+ return false;
+ }
+
+ m_inputAttached = true;
+
+ return true;
+}
+
+void QQnxMediaPlayer::detach()
+{
+ if (!m_context)
+ return;
+
+ if (isVideoOutputAttached())
+ detachVideoOutput();
+
+ if (isAudioOutputAttached())
+ detachAudioOutput();
+
+ if (isInputAttached())
+ detachInput();
+
+ resetMonitoring();
+}
+
+void QQnxMediaPlayer::detachVideoOutput()
+{
+ m_windowGrabber->stop();
+
+ if (m_platformVideoSink)
+ m_platformVideoSink->setVideoFrame({});
+
+ if (isVideoOutputAttached())
+ mmr_output_detach(m_context, m_videoId);
+
+ m_videoId = -1;
+}
+
+void QQnxMediaPlayer::detachAudioOutput()
+{
+ if (isAudioOutputAttached())
+ mmr_output_detach(m_context, m_audioId);
+
+ m_audioId = -1;
+}
+
+void QQnxMediaPlayer::detachInput()
+{
+ if (isInputAttached())
+ mmr_input_detach(m_context);
+
+ m_inputAttached = false;
+}
+
+bool QQnxMediaPlayer::isVideoOutputAttached() const
+{
+ return m_videoId != -1;
+}
+
+bool QQnxMediaPlayer::isAudioOutputAttached() const
+{
+ return m_audioId != -1;
+}
+
+bool QQnxMediaPlayer::isInputAttached() const
+{
+ return m_inputAttached;
+}
+
+void QQnxMediaPlayer::updateScene(const QSize &size)
+{
+ if (!m_platformVideoSink)
+ return;
+
+ QVideoFrameFormat format(size, QVideoFrameFormat::Format_BGRX8888);
+
+ const QVideoFrame actualFrame = m_windowGrabber->isEglImageSupported()
+ ? QVideoFramePrivate::createFrame(std::make_unique<QnxTextureBuffer>(m_windowGrabber),
+ std::move(format))
+ : QVideoFramePrivate::createFrame(std::make_unique<QnxRasterBuffer>(m_windowGrabber),
+ std::move(format));
+
+ m_platformVideoSink->setVideoFrame(actualFrame);
+}
+
+qint64 QQnxMediaPlayer::duration() const
+{
+ return m_metaData.duration();
+}
+
+qint64 QQnxMediaPlayer::position() const
+{
+ return m_position;
+}
+
+void QQnxMediaPlayer::setPosition(qint64 position)
+{
+ if (m_position == position)
+ return;
+
+ m_pendingPosition = position;
+ m_flushPositionTimer.start();
+}
+
+void QQnxMediaPlayer::setPositionInternal(qint64 position)
+{
+ if (!m_context || !m_metaData.isSeekable() || mediaStatus() == QMediaPlayer::NoMedia)
+ return;
+
+ if (mmr_seek(m_context, QString::number(position).toLatin1()) != 0)
+ emitMmError("Seeking failed");
+}
+
+void QQnxMediaPlayer::flushPosition()
+{
+ setPositionInternal(m_pendingPosition);
+}
+
+bool QQnxMediaPlayer::isPendingPositionFlush() const
+{
+ return m_flushPositionTimer.isActive();
+}
+
+void QQnxMediaPlayer::setDeferredSpeedEnabled(bool enabled)
+{
+ m_deferredSpeedEnabled = enabled;
+}
+
+bool QQnxMediaPlayer::isDeferredSpeedEnabled() const
+{
+ return m_deferredSpeedEnabled;
+}
+
+void QQnxMediaPlayer::setVolume(float volume)
+{
+ const int normalizedVolume = ::normalizeVolume(volume);
+
+ if (m_volume == normalizedVolume)
+ return;
+
+ m_volume = normalizedVolume;
+
+ if (!m_muted)
+ updateVolume();
+}
+
+void QQnxMediaPlayer::setMuted(bool muted)
+{
+ if (m_muted == muted)
+ return;
+
+ m_muted = muted;
+
+ updateVolume();
+}
+
+void QQnxMediaPlayer::updateVolume()
+{
+ if (!m_context || m_audioId == -1)
+ return;
+
+ const int volume = m_muted ? 0 : m_volume;
+
+ char buf[] = "100";
+ std::snprintf(buf, sizeof buf, "%d", volume);
+
+ strm_dict_t * dict = strm_dict_new();
+ dict = strm_dict_set(dict, "volume", buf);
+
+ if (mmr_output_parameters(m_context, m_audioId, dict) != 0)
+ emitMmError("mmr_output_parameters: Setting volume failed");
+}
+
+void QQnxMediaPlayer::setAudioOutput(QPlatformAudioOutput *output)
+{
+ QAudioOutput *out = output ? output->q : nullptr;
+ if (m_audioOutput == out)
+ return;
+
+ if (m_audioOutput)
+ disconnect(m_audioOutput.get());
+ m_audioOutput = out;
+ if (m_audioOutput) {
+ connect(out, &QAudioOutput::volumeChanged, this, &QQnxMediaPlayer::setVolume);
+ connect(out, &QAudioOutput::mutedChanged, this, &QQnxMediaPlayer::setMuted);
+ }
+ setVolume(out ? out->volume() : 1.);
+ setMuted(out ? out->isMuted() : true);
+}
+
+float QQnxMediaPlayer::bufferProgress() const
+{
+ // mm-renderer has buffer properties "status" and "level"
+ // QMediaPlayer's buffer status maps to mm-renderer's buffer level
+ return m_bufferLevel/100.0f;
+}
+
+bool QQnxMediaPlayer::isAudioAvailable() const
+{
+ return m_metaData.hasAudio();
+}
+
+bool QQnxMediaPlayer::isVideoAvailable() const
+{
+ return m_metaData.hasVideo();
+}
+
+bool QQnxMediaPlayer::isSeekable() const
+{
+ return m_metaData.isSeekable();
+}
+
+QMediaTimeRange QQnxMediaPlayer::availablePlaybackRanges() const
+{
+ // We can't get this information from the mmrenderer API yet, so pretend we can seek everywhere
+ return QMediaTimeRange(0, m_metaData.duration());
+}
+
+qreal QQnxMediaPlayer::playbackRate() const
+{
+ return ::speedToRate(m_speed);
+}
+
+void QQnxMediaPlayer::setPlaybackRate(qreal rate)
+{
+ if (!m_context)
+ return;
+
+ const int speed = ::rateToSpeed(rate);
+
+ if (m_speed == speed)
+ return;
+
+ // defer setting the playback speed for when play() is called to prevent
+ // mm-renderer from inadvertently transitioning into play state
+ if (isDeferredSpeedEnabled() && state() != QMediaPlayer::PlayingState) {
+ m_deferredSpeed = speed;
+ return;
+ }
+
+ if (mmr_speed_set(m_context, speed) != 0)
+ emitMmError("mmr_speed_set failed");
+}
+
+QUrl QQnxMediaPlayer::media() const
+{
+ return m_media;
+}
+
+const QIODevice *QQnxMediaPlayer::mediaStream() const
+{
+ // Always 0, we don't support QIODevice streams
+ return 0;
+}
+
+void QQnxMediaPlayer::setMedia(const QUrl &media, QIODevice *stream)
+{
+ Q_UNUSED(stream); // not supported
+
+ stop();
+ detach();
+
+ stateChanged(QMediaPlayer::StoppedState);
+ mediaStatusChanged(QMediaPlayer::LoadingMedia);
+
+ m_media = media;
+
+ updateMetaData(nullptr);
+ attach();
+}
+
+void QQnxMediaPlayer::play()
+{
+ if (!m_media.isValid() || !m_connection || !m_context || m_audioId == -1) {
+ stateChanged(QMediaPlayer::StoppedState);
+ return;
+ }
+
+ if (state() == QMediaPlayer::PlayingState)
+ return;
+
+ setDeferredSpeedEnabled(false);
+
+ if (m_deferredSpeed) {
+ setPlaybackRate(::speedToRate(*m_deferredSpeed));
+ m_deferredSpeed = {};
+ } else {
+ setPlaybackRate(::speedToRate(m_configuredSpeed));
+ }
+
+ setDeferredSpeedEnabled(true);
+
+ // Un-pause the state when it is paused
+ if (state() == QMediaPlayer::PausedState) {
+ return;
+ }
+
+
+ if (mediaStatus() == QMediaPlayer::EndOfMedia)
+ setPositionInternal(0);
+
+ resetMonitoring();
+ updateVolume();
+
+ if (mmr_play(m_context) != 0) {
+ stateChanged(QMediaPlayer::StoppedState);
+ emitMmError("mmr_play() failed");
+ return;
+ }
+
+ stateChanged(QMediaPlayer::PlayingState);
+}
+
+void QQnxMediaPlayer::pause()
+{
+ if (state() != QMediaPlayer::PlayingState)
+ return;
+
+ setPlaybackRate(0);
+}
+
+void QQnxMediaPlayer::stop()
+{
+ if (!m_context
+ || state() == QMediaPlayer::StoppedState
+ || mediaStatus() == QMediaPlayer::NoMedia)
+ return;
+
+ // mm-renderer does not rewind by default
+ setPositionInternal(0);
+
+ mmr_stop(m_context);
+}
+
+void QQnxMediaPlayer::setVideoSink(QVideoSink *videoSink)
+{
+ m_platformVideoSink = videoSink
+ ? static_cast<QQnxVideoSink *>(videoSink->platformVideoSink())
+ : nullptr;
+}
+
+void QQnxMediaPlayer::startMonitoring()
+{
+ m_eventThread = new QQnxMediaEventThread(m_context);
+
+ connect(m_eventThread, &QQnxMediaEventThread::eventPending,
+ this, &QQnxMediaPlayer::readEvents);
+
+ m_eventThread->setObjectName(QStringLiteral("MmrEventThread-") + QString::number(m_id));
+ m_eventThread->start();
+}
+
+void QQnxMediaPlayer::stopMonitoring()
+{
+ delete m_eventThread;
+ m_eventThread = nullptr;
+}
+
+void QQnxMediaPlayer::resetMonitoring()
+{
+ m_bufferLevel = 0;
+ m_position = 0;
+ m_speed = 0;
+}
+
+void QQnxMediaPlayer::handleMmPositionChanged(qint64 newPosition)
+{
+ m_position = newPosition;
+
+ if (state() == QMediaPlayer::PausedState)
+ m_windowGrabber->forceUpdate();
+
+ positionChanged(m_position);
+}
+
+void QQnxMediaPlayer::updateBufferLevel(int level, int capacity)
+{
+ m_bufferLevel = capacity == 0 ? 0 : level / static_cast<float>(capacity) * 100.0f;
+ m_bufferLevel = qBound(0, m_bufferLevel, 100);
+ bufferProgressChanged(m_bufferLevel/100.0f);
+}
+
+void QQnxMediaPlayer::updateMetaData(const strm_dict *dict)
+{
+ m_metaData.update(dict);
+
+ durationChanged(m_metaData.duration());
+ audioAvailableChanged(m_metaData.hasAudio());
+ videoAvailableChanged(m_metaData.hasVideo());
+ seekableChanged(m_metaData.isSeekable());
+}
+
+void QQnxMediaPlayer::emitMmError(const char *msg)
+{
+ emitMmError(QString::fromUtf8(msg));
+}
+
+void QQnxMediaPlayer::emitMmError(const QString &msg)
+{
+ int errorCode = MMR_ERROR_NONE;
+ const QString errorMessage = mmErrorMessage(msg, m_context, &errorCode);
+ emit error(errorCode, errorMessage);
+}
+
+void QQnxMediaPlayer::emitPError(const QString &msg)
+{
+ const QString errorMessage = QString::fromLatin1("%1: %2").arg(msg).arg(QString::fromUtf8(strerror(errno)));
+ emit error(errno, errorMessage);
+}
+
+
+void QQnxMediaPlayer::readEvents()
+{
+ while (const mmr_event_t *event = mmr_event_get(m_context)) {
+ if (event->type == MMR_EVENT_NONE)
+ break;
+
+ switch (event->type) {
+ case MMR_EVENT_STATUS:
+ handleMmEventStatus(event);
+ break;
+ case MMR_EVENT_STATE:
+ handleMmEventState(event);
+ break;
+ case MMR_EVENT_METADATA:
+ updateMetaData(event->data);
+ break;
+ case MMR_EVENT_ERROR:
+ handleMmEventError(event);
+ break;
+ case MMR_EVENT_NONE:
+ case MMR_EVENT_OVERFLOW:
+ case MMR_EVENT_WARNING:
+ case MMR_EVENT_PLAYLIST:
+ case MMR_EVENT_INPUT:
+ case MMR_EVENT_OUTPUT:
+ case MMR_EVENT_CTXTPAR:
+ case MMR_EVENT_TRKPAR:
+ case MMR_EVENT_OTHER:
+ break;
+ }
+ }
+
+ if (m_eventThread)
+ m_eventThread->signalRead();
+}
+
+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
new file mode 100644
index 000000000..c570a6334
--- /dev/null
+++ b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediaplayer_p.h
@@ -0,0 +1,167 @@
+// 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
+
+//
+// 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 "qqnxmediametadata_p.h"
+#include "mmrenderertypes.h"
+
+#include <private/qplatformmediaplayer_p.h>
+#include <QtCore/qabstractnativeeventfilter.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qtimer.h>
+
+#include <mm/renderer.h>
+#include <mm/renderer/types.h>
+
+#include <optional>
+
+QT_BEGIN_NAMESPACE
+
+class QQnxVideoSink;
+class QQnxMediaEventThread;
+class QQnxWindowGrabber;
+
+class QQnxMediaPlayer : public QObject
+ , public QPlatformMediaPlayer
+{
+ Q_OBJECT
+public:
+ explicit QQnxMediaPlayer(QMediaPlayer *parent = nullptr);
+ ~QQnxMediaPlayer();
+
+ qint64 duration() const override;
+
+ qint64 position() const override;
+ void setPosition(qint64 position) override;
+
+ void setAudioOutput(QPlatformAudioOutput *) override;
+
+ float bufferProgress() const override;
+
+ bool isAudioAvailable() const override;
+ bool isVideoAvailable() const override;
+
+ bool isSeekable() 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 &media, QIODevice *stream) override;
+
+ void play() override;
+ void pause() override;
+ void stop() override;
+
+ void setVideoSink(QVideoSink *videoSink);
+
+private Q_SLOTS:
+ void setVolume(float volume);
+ void setMuted(bool muted);
+ void readEvents();
+
+private:
+ void startMonitoring();
+ void stopMonitoring();
+ void resetMonitoring();
+
+ void openConnection();
+ void emitMmError(const char *msg);
+ void emitMmError(const QString &msg);
+ void emitPError(const QString &msg);
+
+ void handleMmPositionChanged(qint64 newPosition);
+ void updateBufferLevel(int level, int capacity);
+ void updateMetaData(const strm_dict_t *dict);
+
+ void handleMmEventState(const mmr_event_t *event);
+ void handleMmEventStatus(const mmr_event_t *event);
+ void handleMmEventStatusData(const strm_dict_t *data);
+ void handleMmEventError(const mmr_event_t *event);
+
+ QByteArray resourcePathForUrl(const QUrl &url);
+
+ void closeConnection();
+ void attach();
+
+ bool attachVideoOutput();
+ bool attachAudioOutput();
+ bool attachInput();
+
+ void detach();
+ void detachVideoOutput();
+ void detachAudioOutput();
+ void detachInput();
+
+ bool isVideoOutputAttached() const;
+ bool isAudioOutputAttached() const;
+ bool isInputAttached() const;
+
+ void updateScene(const QSize &size);
+
+ void updateVolume();
+
+ void setPositionInternal(qint64 position);
+ void flushPosition();
+
+ bool isPendingPositionFlush() const;
+
+ void setDeferredSpeedEnabled(bool enabled);
+ bool isDeferredSpeedEnabled() const;
+
+ mmr_context_t *m_context = nullptr;
+ mmr_connection_t *m_connection = nullptr;
+
+ QString m_contextName;
+
+ int m_id = -1;
+ int m_audioId = -1;
+ int m_volume = 50; // range is 0-100
+
+ QUrl m_media;
+ QPointer<QAudioOutput> m_audioOutput;
+ QPointer<QQnxVideoSink> m_platformVideoSink;
+
+ QQnxMediaMetaData m_metaData;
+
+ qint64 m_position = 0;
+ qint64 m_pendingPosition = 0;
+
+ int m_bufferLevel = 0;
+
+ int m_videoId = -1;
+
+ QTimer m_flushPositionTimer;
+
+ QQnxMediaEventThread *m_eventThread = nullptr;
+
+ int m_speed = 1000;
+ int m_configuredSpeed = 1000;
+
+ std::optional<int> m_deferredSpeed;
+
+ QQnxWindowGrabber* m_windowGrabber = nullptr;
+
+ bool m_inputAttached = false;
+ bool m_muted = false;
+ bool m_deferredSpeedEnabled = false;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/qnx/mediaplayer/qqnxmediautil.cpp b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediautil.cpp
new file mode 100644
index 000000000..074989642
--- /dev/null
+++ b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediautil.cpp
@@ -0,0 +1,126 @@
+// 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>
+#include <QDir>
+#include <QFile>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonValue>
+#include <QMutex>
+#include <QMutex>
+#include <QString>
+#include <QXmlStreamReader>
+
+#include <mm/renderer.h>
+#include <mm/renderer/types.h>
+
+QT_BEGIN_NAMESPACE
+
+struct MmError {
+ int errorCode;
+ const char *name;
+};
+
+#define MM_ERROR_ENTRY(error) { error, #error }
+static const MmError mmErrors[] = {
+ MM_ERROR_ENTRY(MMR_ERROR_NONE),
+ MM_ERROR_ENTRY(MMR_ERROR_UNKNOWN ),
+ MM_ERROR_ENTRY(MMR_ERROR_INVALID_PARAMETER ),
+ MM_ERROR_ENTRY(MMR_ERROR_INVALID_STATE),
+ MM_ERROR_ENTRY(MMR_ERROR_UNSUPPORTED_VALUE),
+ MM_ERROR_ENTRY(MMR_ERROR_UNSUPPORTED_MEDIA_TYPE),
+ MM_ERROR_ENTRY(MMR_ERROR_MEDIA_PROTECTED),
+ MM_ERROR_ENTRY(MMR_ERROR_UNSUPPORTED_OPERATION),
+ MM_ERROR_ENTRY(MMR_ERROR_READ),
+ MM_ERROR_ENTRY(MMR_ERROR_WRITE),
+ MM_ERROR_ENTRY(MMR_ERROR_MEDIA_UNAVAILABLE),
+ MM_ERROR_ENTRY(MMR_ERROR_MEDIA_CORRUPTED),
+ MM_ERROR_ENTRY(MMR_ERROR_OUTPUT_UNAVAILABLE),
+ MM_ERROR_ENTRY(MMR_ERROR_NO_MEMORY),
+ MM_ERROR_ENTRY(MMR_ERROR_RESOURCE_UNAVAILABLE),
+ MM_ERROR_ENTRY(MMR_ERROR_MEDIA_DRM_NO_RIGHTS),
+ MM_ERROR_ENTRY(MMR_ERROR_DRM_CORRUPTED_DATA_STORE),
+ MM_ERROR_ENTRY(MMR_ERROR_DRM_OUTPUT_PROTECTION),
+ MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_HDMI),
+ MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_DISPLAYPORT),
+ MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_DVI),
+ MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_ANALOG_VIDEO),
+ MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_ANALOG_AUDIO),
+ MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_TOSLINK),
+ MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_SPDIF),
+ MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_BLUETOOTH),
+ MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_WIRELESSHD),
+};
+static const unsigned int numMmErrors = sizeof(mmErrors) / sizeof(MmError);
+
+template <typename T, size_t N>
+constexpr size_t countof(T (&)[N])
+{
+ return N;
+}
+
+QString keyValueMapsLocation()
+{
+ QByteArray qtKeyValueMaps = qgetenv("QT_KEY_VALUE_MAPS");
+ if (qtKeyValueMaps.isNull())
+ return QString::fromUtf8("/etc/qt/keyvaluemaps");
+ else
+ return QString::fromUtf8(qtKeyValueMaps);
+}
+
+QJsonObject loadMapObject(const QString &keyValueMapPath)
+{
+ QFile mapFile(keyValueMapsLocation() + keyValueMapPath);
+ if (mapFile.open(QIODevice::ReadOnly)) {
+ QByteArray mapFileContents = mapFile.readAll();
+ QJsonDocument mapDocument = QJsonDocument::fromJson(mapFileContents);
+ if (mapDocument.isObject()) {
+ QJsonObject mapObject = mapDocument.object();
+ return mapObject;
+ }
+ }
+ return QJsonObject();
+}
+
+QString mmErrorMessage(const QString &msg, mmr_context_t *context, int *errorCode)
+{
+ const mmr_error_info_t * const mmError = mmr_error_info(context);
+
+ if (errorCode)
+ *errorCode = mmError->error_code;
+
+ if (mmError->error_code < numMmErrors) {
+ return QString::fromLatin1("%1: %2 (code %3)").arg(msg).arg(QString::fromUtf8(mmErrors[mmError->error_code].name))
+ .arg(mmError->error_code);
+ } else {
+ return QString::fromLatin1("%1: Unknown error code %2").arg(msg).arg(mmError->error_code);
+ }
+}
+
+bool checkForDrmPermission()
+{
+ QDir sandboxDir = QDir::home(); // always returns 'data' directory
+ sandboxDir.cdUp(); // change to app sandbox directory
+
+ QFile file(sandboxDir.filePath(QString::fromUtf8("app/native/bar-descriptor.xml")));
+ if (!file.open(QIODevice::ReadOnly)) {
+ qWarning() << "checkForDrmPermission: Unable to open bar-descriptor.xml";
+ return false;
+ }
+
+ QXmlStreamReader reader(&file);
+ while (!reader.atEnd()) {
+ reader.readNextStartElement();
+ if (reader.name() == QLatin1String("action")
+ || reader.name() == QLatin1String("permission")) {
+ if (reader.readElementText().trimmed() == QLatin1String("access_protected_media"))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/qnx/mediaplayer/qqnxmediautil_p.h b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediautil_p.h
new file mode 100644
index 000000000..7b709142f
--- /dev/null
+++ b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediautil_p.h
@@ -0,0 +1,32 @@
+// 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
+
+//
+// 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/qglobal.h>
+#include <QtMultimedia/qaudio.h>
+
+typedef struct mmr_context mmr_context_t;
+
+QT_BEGIN_NAMESPACE
+
+class QString;
+
+QString mmErrorMessage(const QString &msg, mmr_context_t *context, int * errorCode = 0);
+
+bool checkForDrmPermission();
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/qnx/mediaplayer/qqnxvideosink.cpp b/src/plugins/multimedia/qnx/mediaplayer/qqnxvideosink.cpp
new file mode 100644
index 000000000..18d4d1828
--- /dev/null
+++ b/src/plugins/multimedia/qnx/mediaplayer/qqnxvideosink.cpp
@@ -0,0 +1,26 @@
+// 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"
+
+QT_BEGIN_NAMESPACE
+
+QQnxVideoSink::QQnxVideoSink(QVideoSink *parent)
+ : QPlatformVideoSink(parent)
+{
+}
+
+void QQnxVideoSink::setRhi(QRhi *rhi)
+{
+ m_rhi = rhi;
+}
+
+QRhi *QQnxVideoSink::rhi() const
+{
+ return m_rhi;
+}
+
+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
new file mode 100644
index 000000000..2cc7990db
--- /dev/null
+++ b/src/plugins/multimedia/qnx/mediaplayer/qqnxvideosink_p.h
@@ -0,0 +1,41 @@
+// 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
+
+//
+// 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/qplatformvideosink_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQnxWindowGrabber;
+class QVideoSink;
+
+class QQnxVideoSink : public QPlatformVideoSink
+{
+ Q_OBJECT
+public:
+ explicit QQnxVideoSink(QVideoSink *parent = 0);
+
+ void setRhi(QRhi *) override;
+
+ QRhi *rhi() const;
+
+private:
+ QRhi *m_rhi = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/qnx/qnx.json b/src/plugins/multimedia/qnx/qnx.json
new file mode 100644
index 000000000..38df228ef
--- /dev/null
+++ b/src/plugins/multimedia/qnx/qnx.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "qnx" ]
+}
diff --git a/src/plugins/multimedia/qnx/qqnxformatinfo.cpp b/src/plugins/multimedia/qnx/qqnxformatinfo.cpp
new file mode 100644
index 000000000..77492e80d
--- /dev/null
+++ b/src/plugins/multimedia/qnx/qqnxformatinfo.cpp
@@ -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
+
+#include "qqnxformatinfo_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QQnxFormatInfo::QQnxFormatInfo()
+{
+ // ### This is probably somewhat correct for encoding, but should be checked
+ encoders = {
+ { QMediaFormat::MPEG4,
+ { QMediaFormat::AudioCodec::AAC },
+ { QMediaFormat::VideoCodec::H264 } },
+ { QMediaFormat::Mpeg4Audio,
+ { QMediaFormat::AudioCodec::AAC },
+ {} },
+ { QMediaFormat::Wave,
+ { QMediaFormat::AudioCodec::Wave },
+ {} },
+ { QMediaFormat::AAC,
+ { QMediaFormat::AudioCodec::AAC },
+ {} },
+ };
+
+ // ### There can apparently be more codecs and demuxers installed on the system as plugins
+ // Need to find a way to determine the list at compile time or runtime
+ decoders = encoders;
+
+ // ###
+ imageFormats << QImageCapture::JPEG;
+}
+
+QQnxFormatInfo::~QQnxFormatInfo() = default;
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/qnx/qqnxformatinfo_p.h b/src/plugins/multimedia/qnx/qqnxformatinfo_p.h
new file mode 100644
index 000000000..aae3a026a
--- /dev/null
+++ b/src/plugins/multimedia/qnx/qqnxformatinfo_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 QQNXFORMATINFO_H
+#define QQNXFORMATINFO_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/qplatformmediaformatinfo_p.h>
+#include <qhash.h>
+#include <qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQnxFormatInfo : public QPlatformMediaFormatInfo
+{
+public:
+ QQnxFormatInfo();
+ ~QQnxFormatInfo();
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/qnx/qqnxmediaintegration.cpp b/src/plugins/multimedia/qnx/qqnxmediaintegration.cpp
new file mode 100644
index 000000000..8567a69fd
--- /dev/null
+++ b/src/plugins/multimedia/qnx/qqnxmediaintegration.cpp
@@ -0,0 +1,79 @@
+// 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"
+#include "qqnxmediarecorder_p.h"
+#include "qqnxformatinfo_p.h"
+#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
+
+class QQnxMediaPlugin : public QPlatformMediaPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QPlatformMediaPlugin_iid FILE "qnx.json")
+
+public:
+ QQnxMediaPlugin()
+ : QPlatformMediaPlugin()
+ {}
+
+ QPlatformMediaIntegration* create(const QString &name) override
+ {
+ if (name == u"qnx")
+ return new QQnxMediaIntegration;
+ return nullptr;
+ }
+};
+
+QQnxMediaIntegration::QQnxMediaIntegration() : QPlatformMediaIntegration(QLatin1String("qnx")) { }
+
+QPlatformMediaFormatInfo *QQnxMediaIntegration::createFormatInfo()
+{
+ return new QQnxFormatInfo;
+}
+
+QPlatformVideoDevices *QQnxMediaIntegration::createVideoDevices()
+{
+ return new QQnxVideoDevices(this);
+}
+
+QMaybe<QPlatformVideoSink *> QQnxMediaIntegration::createVideoSink(QVideoSink *sink)
+{
+ return new QQnxVideoSink(sink);
+}
+
+QMaybe<QPlatformMediaPlayer *> QQnxMediaIntegration::createPlayer(QMediaPlayer *parent)
+{
+ return new QQnxMediaPlayer(parent);
+}
+
+QMaybe<QPlatformMediaCaptureSession *> QQnxMediaIntegration::createCaptureSession()
+{
+ return new QQnxMediaCaptureSession();
+}
+
+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
new file mode 100644
index 000000000..60fafc246
--- /dev/null
+++ b/src/plugins/multimedia/qnx/qqnxmediaintegration_p.h
@@ -0,0 +1,50 @@
+// 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
+
+//
+// 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 QQnxPlayerInterface;
+class QQnxFormatInfo;
+
+class QQnxMediaIntegration : public QPlatformMediaIntegration
+{
+public:
+ QQnxMediaIntegration();
+
+ QMaybe<QPlatformVideoSink *> createVideoSink(QVideoSink *sink) override;
+
+ QMaybe<QPlatformMediaPlayer *> createPlayer(QMediaPlayer *parent) override;
+
+ QMaybe<QPlatformMediaCaptureSession *> createCaptureSession() override;
+
+ QMaybe<QPlatformMediaRecorder *> createRecorder(QMediaRecorder *parent) override;
+
+ QMaybe<QPlatformCamera *> createCamera(QCamera *parent) override;
+
+ QMaybe<QPlatformImageCapture *> createImageCapture(QImageCapture *parent) override;
+
+protected:
+ QPlatformMediaFormatInfo *createFormatInfo() override;
+
+ QPlatformVideoDevices *createVideoDevices() override;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/qnx/qqnxvideodevices.cpp b/src/plugins/multimedia/qnx/qqnxvideodevices.cpp
new file mode 100644
index 000000000..ea0cfd956
--- /dev/null
+++ b/src/plugins/multimedia/qnx/qqnxvideodevices.cpp
@@ -0,0 +1,111 @@
+// 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 <qdir.h>
+#include <qdebug.h>
+
+#include <optional>
+
+QT_BEGIN_NAMESPACE
+
+static QVideoFrameFormat::PixelFormat fromCameraFrametype(camera_frametype_t type)
+{
+ switch (type) {
+ default:
+ case CAMERA_FRAMETYPE_UNSPECIFIED:
+ return QVideoFrameFormat::Format_Invalid;
+ case CAMERA_FRAMETYPE_NV12:
+ return QVideoFrameFormat::Format_NV12;
+ case CAMERA_FRAMETYPE_RGB8888:
+ return QVideoFrameFormat::Format_ARGB8888;
+ case CAMERA_FRAMETYPE_JPEG:
+ return QVideoFrameFormat::Format_Jpeg;
+ case CAMERA_FRAMETYPE_GRAY8:
+ return QVideoFrameFormat::Format_Y8;
+ case CAMERA_FRAMETYPE_CBYCRY:
+ return QVideoFrameFormat::Format_UYVY;
+ case CAMERA_FRAMETYPE_YCBCR420P:
+ return QVideoFrameFormat::Format_YUV420P;
+ case CAMERA_FRAMETYPE_YCBYCR:
+ return QVideoFrameFormat::Format_YUYV;
+ }
+}
+
+static std::optional<QCameraDevice> createCameraDevice(camera_unit_t unit, bool isDefault)
+{
+ const QQnxCamera camera(unit);
+
+ if (!camera.isValid()) {
+ qWarning() << "Invalid camera unit:" << unit;
+ return {};
+ }
+
+ 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;
+ }
+ }
+ }
+
+ return p->create();
+}
+
+QQnxVideoDevices::QQnxVideoDevices(QPlatformMediaIntegration *integration)
+ : QPlatformVideoDevices(integration)
+{
+}
+
+QList<QCameraDevice> QQnxVideoDevices::videoDevices() const
+{
+ 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;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/qnx/qqnxvideodevices_p.h b/src/plugins/multimedia/qnx/qqnxvideodevices_p.h
new file mode 100644
index 000000000..cc2284e57
--- /dev/null
+++ b/src/plugins/multimedia/qnx/qqnxvideodevices_p.h
@@ -0,0 +1,32 @@
+// 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
+
+//
+// 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>
+
+QT_BEGIN_NAMESPACE
+
+class QQnxVideoDevices : public QPlatformVideoDevices
+{
+public:
+ explicit QQnxVideoDevices(QPlatformMediaIntegration *integration);
+
+ QList<QCameraDevice> videoDevices() const override;
+};
+
+QT_END_NAMESPACE
+
+#endif
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..84d325635
--- /dev/null
+++ b/src/plugins/multimedia/wasm/common/qwasmvideooutput.cpp
@@ -0,0 +1,1071 @@
+// 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/qplatformvideosink_p.h>
+#include <private/qmemoryvideobuffer_p.h>
+#include <private/qvideotexturehelper_p.h>
+#include <private/qvideoframe_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());
+}
+
+QWasmVideoOutput::QWasmVideoOutput(QObject *parent) : QObject{ parent }
+{
+ m_hasVideoFrame = checkForVideoFrame();
+}
+
+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 = QVideoFramePrivate::createFrame(
+ std::make_unique<QMemoryVideoBuffer>(
+ std::move(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());
+
+ auto buffer = std::make_unique<QMemoryVideoBuffer>(
+ std::move(frameBytes),
+ textureDescription->strideForWidth(frameFormat.frameWidth()));
+
+ QVideoFrame vFrame =
+ QVideoFramePrivate::createFrame(std::move(buffer), std::move(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..f078ffb44
--- /dev/null
+++ b/src/plugins/multimedia/wasm/common/qwasmvideooutput_p.h
@@ -0,0 +1,153 @@
+// 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; }
+ bool m_hasVideoFrame = false;
+
+ 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..fbc5cf262
--- /dev/null
+++ b/src/plugins/multimedia/wasm/mediacapture/qwasmcamera.cpp
@@ -0,0 +1,478 @@
+// 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/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) {
+ updateError(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 {
+ updateError(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
new file mode 100644
index 000000000..963081e0a
--- /dev/null
+++ b/src/plugins/multimedia/windows/CMakeLists.txt
@@ -0,0 +1,69 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_plugin(QWindowsMediaPlugin
+ OUTPUT_NAME windowsmediaplugin
+ PLUGIN_TYPE multimedia
+ SOURCES
+ common/mfmetadata.cpp common/mfmetadata_p.h
+ decoder/mfaudiodecodercontrol.cpp decoder/mfaudiodecodercontrol_p.h
+ decoder/mfdecodersourcereader.cpp decoder/mfdecodersourcereader_p.h
+ evr/evrcustompresenter.cpp evr/evrcustompresenter_p.h
+ evr/evrd3dpresentengine.cpp evr/evrd3dpresentengine_p.h
+ evr/evrhelpers.cpp evr/evrhelpers_p.h
+ evr/evrvideowindowcontrol.cpp evr/evrvideowindowcontrol_p.h
+ mfstream.cpp mfstream_p.h
+ player/mfactivate.cpp player/mfactivate_p.h
+ player/mfevrvideowindowcontrol.cpp player/mfevrvideowindowcontrol_p.h
+ player/mfplayercontrol.cpp player/mfplayercontrol_p.h
+ player/mfplayersession.cpp player/mfplayersession_p.h
+ player/mfvideorenderercontrol.cpp player/mfvideorenderercontrol_p.h
+ mediacapture/qwindowscamera.cpp
+ mediacapture/qwindowscamera_p.h
+ mediacapture/qwindowsimagecapture.cpp
+ mediacapture/qwindowsimagecapture_p.h
+ mediacapture/qwindowsmediacapture.cpp
+ mediacapture/qwindowsmediacapture_p.h
+ mediacapture/qwindowsmediadevicereader.cpp
+ mediacapture/qwindowsmediadevicereader_p.h
+ mediacapture/qwindowsmediadevicesession.cpp
+ mediacapture/qwindowsmediadevicesession_p.h
+ mediacapture/qwindowsmediaencoder.cpp
+ mediacapture/qwindowsmediaencoder_p.h
+ qwindowsformatinfo.cpp qwindowsformatinfo_p.h
+ qwindowsintegration.cpp qwindowsintegration_p.h
+ qwindowsvideodevices.cpp qwindowsvideodevices_p.h
+ sourceresolver.cpp sourceresolver_p.h
+ INCLUDE_DIRECTORIES
+ audio
+ common
+ decoder
+ evr
+ player
+ mediacapture
+ LIBRARIES
+ Qt::MultimediaPrivate
+ Qt::CorePrivate
+ uuid
+ WMF::WMF
+ d3d9
+ dxva2
+ evr
+ gdi32
+ ksuser
+ mf
+ mfcore
+ mfplat
+ mfreadwrite
+ mfuuid
+ ole32
+ oleaut32
+ propsys
+ shlwapi
+ strmiids
+ amstrmid
+ user32
+ winmm
+ wmcodecdspuuid
+)
+
diff --git a/src/plugins/multimedia/windows/common/mfmetadata.cpp b/src/plugins/multimedia/windows/common/mfmetadata.cpp
new file mode 100644
index 000000000..cc8c425e3
--- /dev/null
+++ b/src/plugins/multimedia/windows/common/mfmetadata.cpp
@@ -0,0 +1,408 @@
+// 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>
+
+#include <mfapi.h>
+#include <mfidl.h>
+#include <propvarutil.h>
+#include <propkey.h>
+
+#include "private/qwindowsmultimediautils_p.h"
+#include "mfmetadata_p.h"
+
+//#define DEBUG_MEDIAFOUNDATION
+
+static const PROPERTYKEY PROP_KEY_NULL = {GUID_NULL, 0};
+
+static QVariant convertValue(const PROPVARIANT& var)
+{
+ QVariant value;
+ switch (var.vt) {
+ case VT_LPWSTR:
+ value = QString::fromUtf16(reinterpret_cast<const char16_t *>(var.pwszVal));
+ break;
+ case VT_UI4:
+ value = uint(var.ulVal);
+ break;
+ case VT_UI8:
+ value = qulonglong(var.uhVal.QuadPart);
+ break;
+ case VT_BOOL:
+ value = bool(var.boolVal);
+ break;
+ case VT_FILETIME:
+ SYSTEMTIME t;
+ if (!FileTimeToSystemTime(&var.filetime, &t))
+ break;
+
+ value = QDateTime(QDate(t.wYear, t.wMonth, t.wDay),
+ QTime(t.wHour, t.wMinute, t.wSecond, t.wMilliseconds),
+ QTimeZone(QTimeZone::UTC));
+ break;
+ case VT_STREAM:
+ {
+ STATSTG stat;
+ if (FAILED(var.pStream->Stat(&stat, STATFLAG_NONAME)))
+ break;
+ void *data = malloc(stat.cbSize.QuadPart);
+ ULONG read = 0;
+ if (FAILED(var.pStream->Read(data, stat.cbSize.QuadPart, &read))) {
+ free(data);
+ break;
+ }
+ value = QImage::fromData((const uchar*)data, read);
+ free(data);
+ }
+ break;
+ case VT_VECTOR | VT_LPWSTR:
+ QStringList vList;
+ for (ULONG i = 0; i < var.calpwstr.cElems; ++i)
+ vList.append(QString::fromUtf16(reinterpret_cast<const char16_t *>(var.calpwstr.pElems[i])));
+ value = vList;
+ break;
+ }
+ return value;
+}
+
+static QVariant metaDataValue(IPropertyStore *content, const PROPERTYKEY &key)
+{
+ QVariant value;
+
+ PROPVARIANT var;
+ PropVariantInit(&var);
+ HRESULT hr = S_FALSE;
+ if (content)
+ hr = content->GetValue(key, &var);
+
+ if (SUCCEEDED(hr)) {
+ value = convertValue(var);
+
+ // some metadata needs to be reformatted
+ if (value.isValid() && content) {
+ if (key == PKEY_Media_ClassPrimaryID /*QMediaMetaData::MediaType*/) {
+ QString v = value.toString();
+ if (v == QLatin1String("{D1607DBC-E323-4BE2-86A1-48A42A28441E}"))
+ value = QStringLiteral("Music");
+ else if (v == QLatin1String("{DB9830BD-3AB3-4FAB-8A37-1A995F7FF74B}"))
+ value = QStringLiteral("Video");
+ else if (v == QLatin1String("{01CD0F29-DA4E-4157-897B-6275D50C4F11}"))
+ value = QStringLiteral("Audio");
+ else if (v == QLatin1String("{FCF24A76-9A57-4036-990D-E35DD8B244E1}"))
+ value = QStringLiteral("Other");
+ } else if (key == PKEY_Media_Duration) {
+ // duration is provided in 100-nanosecond units, convert to milliseconds
+ value = (value.toLongLong() + 10000) / 10000;
+ } else if (key == PKEY_Video_Compression) {
+ value = int(QWindowsMultimediaUtils::codecForVideoFormat(value.toUuid()));
+ } else if (key == PKEY_Audio_Format) {
+ value = int(QWindowsMultimediaUtils::codecForAudioFormat(value.toUuid()));
+ } else if (key == PKEY_Video_FrameHeight /*Resolution*/) {
+ QSize res;
+ res.setHeight(value.toUInt());
+ if (content && SUCCEEDED(content->GetValue(PKEY_Video_FrameWidth, &var)))
+ res.setWidth(convertValue(var).toUInt());
+ value = res;
+ } else if (key == PKEY_Video_Orientation) {
+ uint orientation = 0;
+ if (content && SUCCEEDED(content->GetValue(PKEY_Video_Orientation, &var)))
+ orientation = convertValue(var).toUInt();
+ value = orientation;
+ } else if (key == PKEY_Video_FrameRate) {
+ value = value.toReal() / 1000.f;
+ }
+ }
+ }
+
+ PropVariantClear(&var);
+ return value;
+}
+
+QMediaMetaData MFMetaData::fromNative(IMFMediaSource* mediaSource)
+{
+ QMediaMetaData metaData;
+
+ IPropertyStore *content = nullptr;
+ if (!SUCCEEDED(MFGetService(mediaSource, MF_PROPERTY_HANDLER_SERVICE, IID_PPV_ARGS(&content))))
+ return metaData;
+
+ Q_ASSERT(content);
+ DWORD cProps;
+ if (SUCCEEDED(content->GetCount(&cProps))) {
+ for (DWORD i = 0; i < cProps; i++)
+ {
+ PROPERTYKEY key;
+ if (FAILED(content->GetAt(i, &key)))
+ continue;
+ QMediaMetaData::Key mediaKey;
+ if (key == PKEY_Author) {
+ mediaKey = QMediaMetaData::Author;
+ } else if (key == PKEY_Title) {
+ mediaKey = QMediaMetaData::Title;
+// } else if (key == PKEY_Media_SubTitle) {
+// mediaKey = QMediaMetaData::SubTitle;
+// } else if (key == PKEY_ParentalRating) {
+// mediaKey = QMediaMetaData::ParentalRating;
+ } else if (key == PKEY_Media_EncodingSettings) {
+ mediaKey = QMediaMetaData::Description;
+ } else if (key == PKEY_Copyright) {
+ mediaKey = QMediaMetaData::Copyright;
+ } else if (key == PKEY_Comment) {
+ mediaKey = QMediaMetaData::Comment;
+ } else if (key == PKEY_Media_ProviderStyle) {
+ mediaKey = QMediaMetaData::Genre;
+ } else if (key == PKEY_Media_DateEncoded) {
+ mediaKey = QMediaMetaData::Date;
+// } else if (key == PKEY_Rating) {
+// mediaKey = QMediaMetaData::UserRating;
+// } else if (key == PKEY_Keywords) {
+// mediaKey = QMediaMetaData::Keywords;
+ } else if (key == PKEY_Language) {
+ mediaKey = QMediaMetaData::Language;
+ } else if (key == PKEY_Media_Publisher) {
+ mediaKey = QMediaMetaData::Publisher;
+ } else if (key == PKEY_Media_ClassPrimaryID) {
+ mediaKey = QMediaMetaData::MediaType;
+ } else if (key == PKEY_Media_Duration) {
+ mediaKey = QMediaMetaData::Duration;
+ } else if (key == PKEY_Audio_EncodingBitrate) {
+ mediaKey = QMediaMetaData::AudioBitRate;
+ } else if (key == PKEY_Audio_Format) {
+ mediaKey = QMediaMetaData::AudioCodec;
+// } else if (key == PKEY_Media_AverageLevel) {
+// mediaKey = QMediaMetaData::AverageLevel;
+// } else if (key == PKEY_Audio_ChannelCount) {
+// mediaKey = QMediaMetaData::ChannelCount;
+// } else if (key == PKEY_Audio_PeakValue) {
+// mediaKey = QMediaMetaData::PeakValue;
+// } else if (key == PKEY_Audio_SampleRate) {
+// mediaKey = QMediaMetaData::SampleRate;
+ } else if (key == PKEY_Music_AlbumTitle) {
+ mediaKey = QMediaMetaData::AlbumTitle;
+ } else if (key == PKEY_Music_AlbumArtist) {
+ mediaKey = QMediaMetaData::AlbumArtist;
+ } else if (key == PKEY_Music_Artist) {
+ mediaKey = QMediaMetaData::ContributingArtist;
+ } else if (key == PKEY_Music_Composer) {
+ mediaKey = QMediaMetaData::Composer;
+// } else if (key == PKEY_Music_Conductor) {
+// mediaKey = QMediaMetaData::Conductor;
+// } else if (key == PKEY_Music_Lyrics) {
+// mediaKey = QMediaMetaData::Lyrics;
+// } else if (key == PKEY_Music_Mood) {
+// mediaKey = QMediaMetaData::Mood;
+ } else if (key == PKEY_Music_TrackNumber) {
+ mediaKey = QMediaMetaData::TrackNumber;
+ } else if (key == PKEY_Music_Genre) {
+ mediaKey = QMediaMetaData::Genre;
+ } else if (key == PKEY_ThumbnailStream) {
+ mediaKey = QMediaMetaData::ThumbnailImage;
+ } else if (key == PKEY_Video_FrameHeight) {
+ mediaKey = QMediaMetaData::Resolution;
+ } else if (key == PKEY_Video_Orientation) {
+ mediaKey = QMediaMetaData::Orientation;
+ } else if (key == PKEY_Video_FrameRate) {
+ mediaKey = QMediaMetaData::VideoFrameRate;
+ } else if (key == PKEY_Video_EncodingBitrate) {
+ mediaKey = QMediaMetaData::VideoBitRate;
+ } else if (key == PKEY_Video_Compression) {
+ mediaKey = QMediaMetaData::VideoCodec;
+// } else if (key == PKEY_Video_Director) {
+// mediaKey = QMediaMetaData::Director;
+// } else if (key == PKEY_Media_Writer) {
+// mediaKey = QMediaMetaData::Writer;
+ } else {
+ continue;
+ }
+ metaData.insert(mediaKey, metaDataValue(content, key));
+ }
+ }
+
+ content->Release();
+
+ return metaData;
+}
+
+static REFPROPERTYKEY propertyKeyForMetaDataKey(QMediaMetaData::Key key)
+{
+ switch (key) {
+ case QMediaMetaData::Key::Title:
+ return PKEY_Title;
+ case QMediaMetaData::Key::Author:
+ return PKEY_Author;
+ case QMediaMetaData::Key::Comment:
+ return PKEY_Comment;
+ case QMediaMetaData::Key::Genre:
+ return PKEY_Music_Genre;
+ case QMediaMetaData::Key::Copyright:
+ return PKEY_Copyright;
+ case QMediaMetaData::Key::Publisher:
+ return PKEY_Media_Publisher;
+ case QMediaMetaData::Key::Url:
+ return PKEY_Media_AuthorUrl;
+ case QMediaMetaData::Key::AlbumTitle:
+ return PKEY_Music_AlbumTitle;
+ case QMediaMetaData::Key::AlbumArtist:
+ return PKEY_Music_AlbumArtist;
+ case QMediaMetaData::Key::TrackNumber:
+ return PKEY_Music_TrackNumber;
+ case QMediaMetaData::Key::Date:
+ return PKEY_Media_DateEncoded;
+ case QMediaMetaData::Key::Composer:
+ return PKEY_Music_Composer;
+ case QMediaMetaData::Key::Duration:
+ return PKEY_Media_Duration;
+ case QMediaMetaData::Key::Language:
+ return PKEY_Language;
+ case QMediaMetaData::Key::Description:
+ return PKEY_Media_EncodingSettings;
+ case QMediaMetaData::Key::AudioBitRate:
+ return PKEY_Audio_EncodingBitrate;
+ case QMediaMetaData::Key::ContributingArtist:
+ return PKEY_Music_Artist;
+ case QMediaMetaData::Key::ThumbnailImage:
+ return PKEY_ThumbnailStream;
+ case QMediaMetaData::Key::Orientation:
+ return PKEY_Video_Orientation;
+ case QMediaMetaData::Key::VideoFrameRate:
+ return PKEY_Video_FrameRate;
+ case QMediaMetaData::Key::VideoBitRate:
+ return PKEY_Video_EncodingBitrate;
+ case QMediaMetaData::MediaType:
+ return PKEY_Media_ClassPrimaryID;
+ default:
+ return PROP_KEY_NULL;
+ }
+}
+
+static void setStringProperty(IPropertyStore *content, REFPROPERTYKEY key, const QString &value)
+{
+ PROPVARIANT propValue = {};
+ if (SUCCEEDED(InitPropVariantFromString(reinterpret_cast<LPCWSTR>(value.utf16()), &propValue))) {
+ if (SUCCEEDED(PSCoerceToCanonicalValue(key, &propValue)))
+ content->SetValue(key, propValue);
+ PropVariantClear(&propValue);
+ }
+}
+
+static void setUInt32Property(IPropertyStore *content, REFPROPERTYKEY key, quint32 value)
+{
+ PROPVARIANT propValue = {};
+ if (SUCCEEDED(InitPropVariantFromUInt32(ULONG(value), &propValue))) {
+ if (SUCCEEDED(PSCoerceToCanonicalValue(key, &propValue)))
+ content->SetValue(key, propValue);
+ PropVariantClear(&propValue);
+ }
+}
+
+static void setUInt64Property(IPropertyStore *content, REFPROPERTYKEY key, quint64 value)
+{
+ PROPVARIANT propValue = {};
+ if (SUCCEEDED(InitPropVariantFromUInt64(ULONGLONG(value), &propValue))) {
+ if (SUCCEEDED(PSCoerceToCanonicalValue(key, &propValue)))
+ content->SetValue(key, propValue);
+ PropVariantClear(&propValue);
+ }
+}
+
+static void setFileTimeProperty(IPropertyStore *content, REFPROPERTYKEY key, const FILETIME *ft)
+{
+ PROPVARIANT propValue = {};
+ if (SUCCEEDED(InitPropVariantFromFileTime(ft, &propValue))) {
+ if (SUCCEEDED(PSCoerceToCanonicalValue(key, &propValue)))
+ content->SetValue(key, propValue);
+ PropVariantClear(&propValue);
+ }
+}
+
+void MFMetaData::toNative(const QMediaMetaData &metaData, IPropertyStore *content)
+{
+ if (content) {
+
+ for (const auto &key : metaData.keys()) {
+
+ QVariant value = metaData.value(key);
+
+ if (key == QMediaMetaData::Key::MediaType) {
+
+ QString strValue = metaData.stringValue(key);
+ QString v;
+
+ // Sets property to one of the MediaClassPrimaryID values defined by Microsoft:
+ // https://docs.microsoft.com/en-us/windows/win32/wmformat/wm-mediaprimaryid
+ if (strValue == QLatin1String("Music"))
+ v = QLatin1String("{D1607DBC-E323-4BE2-86A1-48A42A28441E}");
+ else if (strValue == QLatin1String("Video"))
+ v = QLatin1String("{DB9830BD-3AB3-4FAB-8A37-1A995F7FF74B}");
+ else if (strValue == QLatin1String("Audio"))
+ v = QLatin1String("{01CD0F29-DA4E-4157-897B-6275D50C4F11}");
+ else
+ v = QLatin1String("{FCF24A76-9A57-4036-990D-E35DD8B244E1}");
+
+ setStringProperty(content, PKEY_Media_ClassPrimaryID, v);
+
+ } else if (key == QMediaMetaData::Key::Duration) {
+
+ setUInt64Property(content, PKEY_Media_Duration, value.toULongLong() * 10000);
+
+ } else if (key == QMediaMetaData::Key::Resolution) {
+
+ QSize res = value.toSize();
+ setUInt32Property(content, PKEY_Video_FrameWidth, quint32(res.width()));
+ setUInt32Property(content, PKEY_Video_FrameHeight, quint32(res.height()));
+
+ } else if (key == QMediaMetaData::Key::Orientation) {
+
+ setUInt32Property(content, PKEY_Video_Orientation, value.toUInt());
+
+ } else if (key == QMediaMetaData::Key::VideoFrameRate) {
+
+ qreal fps = value.toReal();
+ setUInt32Property(content, PKEY_Video_FrameRate, quint32(fps * 1000));
+
+ } else if (key == QMediaMetaData::Key::TrackNumber) {
+
+ setUInt32Property(content, PKEY_Music_TrackNumber, value.toUInt());
+
+ } else if (key == QMediaMetaData::Key::AudioBitRate) {
+
+ setUInt32Property(content, PKEY_Audio_EncodingBitrate, value.toUInt());
+
+ } else if (key == QMediaMetaData::Key::VideoBitRate) {
+
+ setUInt32Property(content, PKEY_Video_EncodingBitrate, value.toUInt());
+
+ } else if (key == QMediaMetaData::Key::Date) {
+
+ // Convert QDateTime to FILETIME by converting to 100-nsecs since
+ // 01/01/1970 UTC and adding the difference from 1601 to 1970.
+ ULARGE_INTEGER t = {};
+ t.QuadPart = ULONGLONG(value.toDateTime().toUTC().toMSecsSinceEpoch() * 10000
+ + 116444736000000000LL);
+
+ FILETIME ft = {};
+ ft.dwHighDateTime = t.HighPart;
+ ft.dwLowDateTime = t.LowPart;
+
+ setFileTimeProperty(content, PKEY_Media_DateEncoded, &ft);
+
+ } else {
+
+ // By default use as string and let PSCoerceToCanonicalValue()
+ // do validation and type conversion.
+ REFPROPERTYKEY propKey = propertyKeyForMetaDataKey(key);
+
+ if (propKey != PROP_KEY_NULL) {
+ QString strValue = metaData.stringValue(key);
+ if (!strValue.isEmpty())
+ setStringProperty(content, propKey, strValue);
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/plugins/multimedia/windows/common/mfmetadata_p.h b/src/plugins/multimedia/windows/common/mfmetadata_p.h
new file mode 100644
index 000000000..9ff196240
--- /dev/null
+++ b/src/plugins/multimedia/windows/common/mfmetadata_p.h
@@ -0,0 +1,30 @@
+// 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
+
+//
+// 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 "mfidl.h"
+
+QT_USE_NAMESPACE
+
+class MFMetaData
+{
+public:
+ static QMediaMetaData fromNative(IMFMediaSource* mediaSource);
+ static void toNative(const QMediaMetaData &metaData, IPropertyStore *content);
+};
+
+#endif
diff --git a/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol.cpp b/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol.cpp
new file mode 100644
index 000000000..912ab5e94
--- /dev/null
+++ b/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol.cpp
@@ -0,0 +1,225 @@
+// 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 "mfaudiodecodercontrol_p.h"
+#include <private/qwindowsaudioutils_p.h>
+
+QT_BEGIN_NAMESPACE
+
+MFAudioDecoderControl::MFAudioDecoderControl(QAudioDecoder *parent)
+ : QPlatformAudioDecoder(parent)
+ , m_sourceResolver(new SourceResolver)
+{
+ connect(m_sourceResolver, &SourceResolver::mediaSourceReady, this, &MFAudioDecoderControl::handleMediaSourceReady);
+ connect(m_sourceResolver, &SourceResolver::error, this, &MFAudioDecoderControl::handleMediaSourceError);
+}
+
+MFAudioDecoderControl::~MFAudioDecoderControl()
+{
+ m_sourceResolver->shutdown();
+ m_sourceResolver->Release();
+}
+
+void MFAudioDecoderControl::setSource(const QUrl &fileName)
+{
+ if (!m_device && m_source == fileName)
+ return;
+ stop();
+ m_sourceResolver->cancel();
+ m_sourceResolver->shutdown();
+ m_device = nullptr;
+ m_source = fileName;
+ sourceChanged();
+
+ if (!m_source.isEmpty()) {
+ m_sourceResolver->load(m_source, 0);
+ m_loadingSource = true;
+ }
+}
+
+void MFAudioDecoderControl::setSourceDevice(QIODevice *device)
+{
+ if (m_device == device && m_source.isEmpty())
+ return;
+ stop();
+ m_sourceResolver->cancel();
+ m_sourceResolver->shutdown();
+ m_source.clear();
+ m_device = device;
+ sourceChanged();
+
+ if (m_device) {
+ if (m_device->isOpen() && m_device->isReadable()) {
+ m_sourceResolver->load(QUrl(), m_device);
+ m_loadingSource = true;
+ }
+ }
+}
+
+void MFAudioDecoderControl::handleMediaSourceReady()
+{
+ m_loadingSource = false;
+ if (m_deferredStart) {
+ m_deferredStart = false;
+ startReadingSource(m_sourceResolver->mediaSource());
+ }
+}
+
+void MFAudioDecoderControl::handleMediaSourceError(long hr)
+{
+ m_loadingSource = false;
+ m_deferredStart = false;
+ if (hr == MF_E_UNSUPPORTED_BYTESTREAM_TYPE) {
+ error(QAudioDecoder::FormatError, tr("Unsupported media type"));
+ } else if (hr == ERROR_FILE_NOT_FOUND) {
+ error(QAudioDecoder::ResourceError, tr("Media not found"));
+ } else {
+ error(QAudioDecoder::ResourceError, tr("Unable to load specified URL")
+ + QString::fromStdString(std::system_category().message(hr)));
+ }
+}
+
+void MFAudioDecoderControl::startReadingSource(IMFMediaSource *source)
+{
+ Q_ASSERT(source);
+
+ 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());
+ if (!mediaFormat.isValid()) {
+ error(QAudioDecoder::FormatError, tr("Invalid media format"));
+ m_decoderSourceReader.Reset();
+ return;
+ }
+
+ ComPtr<IMFPresentationDescriptor> pd;
+ if (SUCCEEDED(source->CreatePresentationDescriptor(pd.GetAddressOf()))) {
+ UINT64 duration = 0;
+ pd->GetUINT64(MF_PD_DURATION, &duration);
+ duration /= 10000;
+ m_duration = qint64(duration);
+ durationChanged(m_duration);
+ }
+
+ if (!m_resampler.setup(mediaFormat, m_outputFormat.isValid() ? m_outputFormat : mediaFormat)) {
+ qWarning() << "Failed to set up resampler";
+ return;
+ }
+
+ connect(m_decoderSourceReader.Get(), &MFDecoderSourceReader::finished, this, &MFAudioDecoderControl::handleSourceFinished);
+ connect(m_decoderSourceReader.Get(), &MFDecoderSourceReader::newSample, this, &MFAudioDecoderControl::handleNewSample);
+
+ setIsDecoding(true);
+
+ m_decoderSourceReader->readNextSample();
+}
+
+void MFAudioDecoderControl::start()
+{
+ if (isDecoding())
+ return;
+
+ if (m_loadingSource) {
+ m_deferredStart = true;
+ } else {
+ IMFMediaSource *source = m_sourceResolver->mediaSource();
+ if (!source) {
+ if (m_device)
+ error(QAudioDecoder::ResourceError, tr("Unable to read from specified device"));
+ else if (m_source.isValid())
+ error(QAudioDecoder::ResourceError, tr("Unable to load specified URL"));
+ else
+ error(QAudioDecoder::ResourceError, tr("No media source specified"));
+ return;
+ } else {
+ startReadingSource(source);
+ }
+ }
+}
+
+void MFAudioDecoderControl::stop()
+{
+ m_deferredStart = false;
+ if (!isDecoding())
+ return;
+
+ disconnect(m_decoderSourceReader.Get());
+ m_decoderSourceReader->clearSource();
+ m_decoderSourceReader.Reset();
+
+ if (bufferAvailable()) {
+ QAudioBuffer buffer;
+ m_audioBuffer.swap(buffer);
+ bufferAvailableChanged(false);
+ }
+ setIsDecoding(false);
+
+ if (m_position != -1) {
+ m_position = -1;
+ positionChanged(m_position);
+ }
+ if (m_duration != -1) {
+ m_duration = -1;
+ durationChanged(m_duration);
+ }
+}
+
+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());
+
+ if (out.isEmpty()) {
+ error(QAudioDecoder::Error::ResourceError, tr("Failed processing a sample"));
+
+ } else {
+ m_audioBuffer = QAudioBuffer(out, m_resampler.outputFormat(), sampleStartTimeUs);
+
+ bufferAvailableChanged(true);
+ bufferReady();
+ }
+}
+
+void MFAudioDecoderControl::handleSourceFinished()
+{
+ stop();
+ finished();
+}
+
+void MFAudioDecoderControl::setAudioFormat(const QAudioFormat &format)
+{
+ if (m_outputFormat == format)
+ return;
+ m_outputFormat = format;
+ formatChanged(m_outputFormat);
+}
+
+QAudioBuffer MFAudioDecoderControl::read()
+{
+ QAudioBuffer buffer;
+
+ if (bufferAvailable()) {
+ buffer.swap(m_audioBuffer);
+ m_position = buffer.startTime() / 1000;
+ 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
new file mode 100644
index 000000000..9bb2371ec
--- /dev/null
+++ b/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol_p.h
@@ -0,0 +1,75 @@
+// 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
+
+//
+// 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 "mfdecodersourcereader_p.h"
+#include <private/qplatformaudiodecoder_p.h>
+#include <sourceresolver_p.h>
+#include <private/qcomptr_p.h>
+#include <private/qwindowsresampler_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class MFAudioDecoderControl : public QPlatformAudioDecoder
+{
+ Q_OBJECT
+public:
+ MFAudioDecoderControl(QAudioDecoder *parent);
+ ~MFAudioDecoderControl() override;
+
+ QUrl source() const override { return m_source; }
+ void setSource(const QUrl &fileName) override;
+
+ QIODevice* sourceDevice() const override { return m_device; }
+ void setSourceDevice(QIODevice *device) override;
+
+ void start() override;
+ void stop() override;
+
+ QAudioFormat audioFormat() const override { return m_outputFormat; }
+ void setAudioFormat(const QAudioFormat &format) override;
+
+ QAudioBuffer read() override;
+ bool bufferAvailable() const override { return m_audioBuffer.sampleCount() > 0; }
+
+ qint64 position() const override { return m_position; }
+ qint64 duration() const override { return m_duration; }
+
+private Q_SLOTS:
+ void handleMediaSourceReady();
+ void handleMediaSourceError(long hr);
+ void handleNewSample(ComPtr<IMFSample>);
+ void handleSourceFinished();
+
+private:
+ void startReadingSource(IMFMediaSource *source);
+
+ ComPtr<MFDecoderSourceReader> m_decoderSourceReader;
+ SourceResolver *m_sourceResolver;
+ QWindowsResampler m_resampler;
+ QUrl m_source;
+ QIODevice *m_device = nullptr;
+ QAudioFormat m_outputFormat;
+ QAudioBuffer m_audioBuffer;
+ qint64 m_duration = -1;
+ qint64 m_position = -1;
+ bool m_loadingSource = false;
+ 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
new file mode 100644
index 000000000..097f83437
--- /dev/null
+++ b/src/plugins/multimedia/windows/decoder/mfdecodersourcereader.cpp
@@ -0,0 +1,103 @@
+// 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 <qlogging.h>
+#include <qdebug.h>
+#include "mfdecodersourcereader_p.h"
+
+QT_BEGIN_NAMESPACE
+
+ComPtr<IMFMediaType> MFDecoderSourceReader::setSource(IMFMediaSource *source, QAudioFormat::SampleFormat sampleFormat)
+{
+ ComPtr<IMFMediaType> mediaType;
+ m_sourceReader.Reset();
+
+ if (!source)
+ return mediaType;
+
+ 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.GetAddressOf());
+ if (FAILED(hr)) {
+ qWarning() << "MFDecoderSourceReader: failed to set up source reader: "
+ << std::system_category().message(hr).c_str();
+ return mediaType;
+ }
+
+ m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_ALL_STREAMS), FALSE);
+ m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), TRUE);
+
+ 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.GetAddressOf());
+ // Ensure the stream is selected.
+ m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), TRUE);
+
+ return mediaType;
+}
+
+void MFDecoderSourceReader::readNextSample()
+{
+ if (m_sourceReader)
+ m_sourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, NULL, NULL, NULL, NULL);
+}
+
+//from IUnknown
+STDMETHODIMP MFDecoderSourceReader::QueryInterface(REFIID riid, LPVOID *ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+ if (riid == IID_IMFSourceReaderCallback) {
+ *ppvObject = static_cast<IMFSourceReaderCallback*>(this);
+ } else if (riid == IID_IUnknown) {
+ *ppvObject = static_cast<IUnknown*>(this);
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+STDMETHODIMP_(ULONG) MFDecoderSourceReader::AddRef()
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+STDMETHODIMP_(ULONG) MFDecoderSourceReader::Release()
+{
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0) {
+ this->deleteLater();
+ }
+ return cRef;
+}
+
+//from IMFSourceReaderCallback
+STDMETHODIMP MFDecoderSourceReader::OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex,
+ DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample)
+{
+ Q_UNUSED(hrStatus);
+ Q_UNUSED(dwStreamIndex);
+ Q_UNUSED(llTimestamp);
+ if (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
new file mode 100644
index 000000000..dee6f8bf5
--- /dev/null
+++ b/src/plugins/multimedia/windows/decoder/mfdecodersourcereader_p.h
@@ -0,0 +1,63 @@
+// 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
+
+//
+// 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 <mfreadwrite.h>
+
+#include <QtCore/qobject.h>
+#include "qaudioformat.h"
+#include <private/qcomptr_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class MFDecoderSourceReader : public QObject, public IMFSourceReaderCallback
+{
+ Q_OBJECT
+public:
+ MFDecoderSourceReader() {}
+ ~MFDecoderSourceReader() override {}
+
+ void clearSource() { m_sourceReader.Reset(); }
+ ComPtr<IMFMediaType> setSource(IMFMediaSource *source, QAudioFormat::SampleFormat);
+
+ void readNextSample();
+
+ //from IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject) override;
+ STDMETHODIMP_(ULONG) AddRef() override;
+ STDMETHODIMP_(ULONG) Release() override;
+
+ //from IMFSourceReaderCallback
+ STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex,
+ DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample) override;
+ STDMETHODIMP OnFlush(DWORD) override { return S_OK; }
+ STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) override { return S_OK; }
+
+Q_SIGNALS:
+ void newSample(ComPtr<IMFSample>);
+ void finished();
+
+private:
+ long m_cRef = 1;
+ 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
new file mode 100644
index 000000000..2a3433f4d
--- /dev/null
+++ b/src/plugins/multimedia/windows/evr/evrcustompresenter.cpp
@@ -0,0 +1,1849 @@
+// 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"
+
+#include "evrd3dpresentengine_p.h"
+#include "evrhelpers_p.h"
+#include <private/qwindowsmultimediautils_p.h>
+#include <private/qplatformvideosink_p.h>
+#include <private/qwindowsmfdefs_p.h>
+
+#include <rhi/qrhi.h>
+
+#include <QtCore/qmutex.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qrect.h>
+#include <qthread.h>
+#include <qcoreapplication.h>
+#include <qmath.h>
+#include <qloggingcategory.h>
+
+#include <mutex>
+
+#include <float.h>
+#include <evcode.h>
+
+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 setMixerSourceRect(IMFTransform *mixer, const MFVideoNormalizedRect& nrcSource);
+static QVideoFrameFormat::PixelFormat pixelFormatFromMediaType(IMFMediaType *type);
+
+static inline LONG MFTimeToMsec(const LONGLONG& time)
+{
+ return (LONG)(time / (ONE_SECOND / ONE_MSEC));
+}
+
+bool qt_evr_setCustomPresenter(IUnknown *evr, EVRCustomPresenter *presenter)
+{
+ if (!evr || !presenter)
+ return false;
+
+ HRESULT result = E_FAIL;
+
+ IMFVideoRenderer *renderer = NULL;
+ if (SUCCEEDED(evr->QueryInterface(IID_PPV_ARGS(&renderer)))) {
+ result = renderer->InitializeRenderer(NULL, presenter);
+ renderer->Release();
+ }
+
+ return result == S_OK;
+}
+
+class PresentSampleEvent : public QEvent
+{
+public:
+ explicit PresentSampleEvent(const ComPtr<IMFSample> &sample)
+ : QEvent(static_cast<Type>(EVRCustomPresenter::PresentSample)), m_sample(sample)
+ {
+ }
+
+ ComPtr<IMFSample> sample() const { return m_sample; }
+
+private:
+ const ComPtr<IMFSample> m_sample;
+};
+
+Scheduler::Scheduler(EVRCustomPresenter *presenter)
+ : m_presenter(presenter)
+ , m_threadID(0)
+ , m_playbackRate(1.0f)
+ , m_perFrame_1_4th(0)
+{
+}
+
+Scheduler::~Scheduler()
+{
+ m_scheduledSamples.clear();
+}
+
+void Scheduler::setFrameRate(const MFRatio& fps)
+{
+ UINT64 AvgTimePerFrame = 0;
+
+ // Convert to a duration.
+ MFFrameRateToAverageTimePerFrame(fps.Numerator, fps.Denominator, &AvgTimePerFrame);
+
+ // Calculate 1/4th of this value, because we use it frequently.
+ m_perFrame_1_4th = AvgTimePerFrame / 4;
+}
+
+HRESULT Scheduler::startScheduler(ComPtr<IMFClock> clock)
+{
+ if (m_schedulerThread)
+ return E_UNEXPECTED;
+
+ HRESULT hr = S_OK;
+ DWORD dwID = 0;
+ HANDLE hObjects[2];
+ DWORD dwWait = 0;
+
+ m_clock = clock;
+
+ // Set a high the timer resolution (ie, short timer period).
+ timeBeginPeriod(1);
+
+ // Create an event to wait for the thread to start.
+ 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 = EventHandle{ CreateEvent(NULL, FALSE, FALSE, NULL) };
+ if (!m_flushEvent) {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ goto done;
+ }
+
+ // Create the scheduler thread.
+ 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.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.
+ m_schedulerThread = {};
+
+ hr = E_UNEXPECTED;
+ goto done;
+ }
+
+ m_threadID = dwID;
+
+done:
+ // Regardless success/failure, we are done using the "thread ready" event.
+ m_threadReadyEvent = {};
+
+ return hr;
+}
+
+HRESULT Scheduler::stopScheduler()
+{
+ if (!m_schedulerThread)
+ return S_OK;
+
+ // Ask the scheduler thread to exit.
+ PostThreadMessage(m_threadID, Terminate, 0, 0);
+
+ // Wait for the thread to exit.
+ WaitForSingleObject(m_schedulerThread.get(), INFINITE);
+
+ // Close handles.
+ m_schedulerThread = {};
+ m_flushEvent = {};
+
+ // Discard samples.
+ m_mutex.lock();
+ m_scheduledSamples.clear();
+ m_mutex.unlock();
+
+ // Restore the timer resolution.
+ timeEndPeriod(1);
+
+ return S_OK;
+}
+
+HRESULT Scheduler::flush()
+{
+ if (m_schedulerThread) {
+ // Ask the scheduler thread to flush.
+ PostThreadMessage(m_threadID, Flush, 0 , 0);
+
+ // Wait for the scheduler thread to signal the flush event,
+ // OR for the thread to terminate.
+ HANDLE objects[] = { m_flushEvent.get(), m_schedulerThread.get() };
+
+ WaitForMultipleObjects(ARRAYSIZE(objects), objects, FALSE, SCHEDULER_TIMEOUT);
+ }
+
+ return S_OK;
+}
+
+bool Scheduler::areSamplesScheduled()
+{
+ QMutexLocker locker(&m_mutex);
+ return m_scheduledSamples.count() > 0;
+}
+
+HRESULT Scheduler::scheduleSample(const ComPtr<IMFSample> &sample, bool presentNow)
+{
+ if (!m_schedulerThread)
+ return MF_E_NOT_INITIALIZED;
+
+ HRESULT hr = S_OK;
+ DWORD dwExitCode = 0;
+
+ 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();
+ m_scheduledSamples.enqueue(sample);
+ m_mutex.unlock();
+
+ if (SUCCEEDED(hr))
+ PostThreadMessage(m_threadID, Schedule, 0, 0);
+ }
+
+ return hr;
+}
+
+HRESULT Scheduler::processSamplesInQueue(LONG *nextSleep)
+{
+ HRESULT hr = S_OK;
+ LONG wait = 0;
+
+ 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 (!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;
+ }
+
+ 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.
+ if (wait == 0)
+ wait = INFINITE;
+
+ *nextSleep = wait;
+ return hr;
+}
+
+bool Scheduler::isSampleReadyToPresent(IMFSample *sample, LONG *pNextSleep) const
+{
+ *pNextSleep = 0;
+ if (!m_clock)
+ return true;
+
+ MFTIME hnsPresentationTime = 0;
+ MFTIME hnsTimeNow = 0;
+ MFTIME hnsSystemTime = 0;
+
+ // Get the sample's time stamp. It is valid for a sample to
+ // have no time stamp.
+ HRESULT 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.
+ MFTIME hnsDelta = hnsPresentationTime - hnsTimeNow;
+ if (m_playbackRate < 0) {
+ // For reverse playback, the clock runs backward. Therefore, the
+ // delta is reversed.
+ hnsDelta = - hnsDelta;
+ }
+
+ 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 {
+ // This sample can be presented right now
+ return true;
+ }
+}
+
+DWORD WINAPI Scheduler::schedulerThreadProc(LPVOID parameter)
+{
+ Scheduler* scheduler = reinterpret_cast<Scheduler*>(parameter);
+ if (!scheduler)
+ return -1;
+ return scheduler->schedulerThreadProcPrivate();
+}
+
+DWORD Scheduler::schedulerThreadProcPrivate()
+{
+ HRESULT hr = S_OK;
+ MSG msg;
+ LONG wait = INFINITE;
+ bool exitThread = false;
+
+ // Force the system to create a message queue for this thread.
+ // (See MSDN documentation for PostThreadMessage.)
+ PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+
+ // Signal to the scheduler that the thread is ready.
+ SetEvent(m_threadReadyEvent.get());
+
+ while (!exitThread) {
+ // Wait for a thread message OR until the wait time expires.
+ DWORD result = MsgWaitForMultipleObjects(0, NULL, FALSE, wait, QS_POSTMESSAGE);
+
+ if (result == WAIT_TIMEOUT) {
+ // If we timed out, then process the samples in the queue
+ hr = processSamplesInQueue(&wait);
+ if (FAILED(hr))
+ exitThread = true;
+ }
+
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ bool processSamples = true;
+
+ switch (msg.message) {
+ case Terminate:
+ exitThread = true;
+ break;
+ case Flush:
+ // Flushing: Clear the sample queue and set the event.
+ m_mutex.lock();
+ m_scheduledSamples.clear();
+ m_mutex.unlock();
+ wait = INFINITE;
+ SetEvent(m_flushEvent.get());
+ break;
+ case Schedule:
+ // Process as many samples as we can.
+ if (processSamples) {
+ hr = processSamplesInQueue(&wait);
+ if (FAILED(hr))
+ exitThread = true;
+ processSamples = (wait != (LONG)INFINITE);
+ }
+ break;
+ }
+ }
+
+ }
+
+ return (SUCCEEDED(hr) ? 0 : 1);
+}
+
+
+SamplePool::SamplePool()
+ : m_initialized(false)
+{
+}
+
+SamplePool::~SamplePool()
+{
+ clear();
+}
+
+ComPtr<IMFSample> SamplePool::takeSample()
+{
+ QMutexLocker locker(&m_mutex);
+
+ Q_ASSERT(m_initialized);
+ if (!m_initialized) {
+ qCWarning(qLcEvrCustomPresenter) << "SamplePool is not initialized yet";
+ return nullptr;
+ }
+
+ 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)
+
+ return m_videoSampleQueue.takeFirst();
+}
+
+void SamplePool::returnSample(const ComPtr<IMFSample> &sample)
+{
+ QMutexLocker locker(&m_mutex);
+
+ Q_ASSERT(m_initialized);
+ if (!m_initialized) {
+ qCWarning(qLcEvrCustomPresenter) << "SamplePool is not initialized yet";
+ return;
+ }
+
+ m_videoSampleQueue.append(sample);
+}
+
+HRESULT SamplePool::initialize(QList<ComPtr<IMFSample>> &&samples)
+{
+ QMutexLocker locker(&m_mutex);
+
+ if (m_initialized)
+ return MF_E_INVALIDREQUEST;
+
+ // Move these samples into our allocated queue.
+ m_videoSampleQueue.append(std::move(samples));
+
+ m_initialized = true;
+
+ return S_OK;
+}
+
+HRESULT SamplePool::clear()
+{
+ QMutexLocker locker(&m_mutex);
+
+ m_videoSampleQueue.clear();
+ m_initialized = false;
+
+ return S_OK;
+}
+
+
+EVRCustomPresenter::EVRCustomPresenter(QVideoSink *sink)
+ : QObject()
+ , m_sampleFreeCB(this, &EVRCustomPresenter::onSampleFree)
+ , m_refCount(1)
+ , m_renderState(RenderShutdown)
+ , m_scheduler(this)
+ , m_tokenCounter(0)
+ , m_sampleNotify(false)
+ , m_prerolled(false)
+ , m_endStreaming(false)
+ , m_playbackRate(1.0f)
+ , m_presentEngine(new D3DPresentEngine(sink))
+ , m_mediaType(0)
+ , m_videoSink(0)
+ , m_canRenderToSurface(false)
+ , m_positionOffset(0)
+{
+ // Initial source rectangle = (0,0,1,1)
+ m_sourceRect.top = 0;
+ m_sourceRect.left = 0;
+ m_sourceRect.bottom = 1;
+ m_sourceRect.right = 1;
+
+ setSink(sink);
+}
+
+EVRCustomPresenter::~EVRCustomPresenter()
+{
+ m_scheduler.flush();
+ m_scheduler.stopScheduler();
+ m_samplePool.clear();
+
+ delete m_presentEngine;
+}
+
+HRESULT EVRCustomPresenter::QueryInterface(REFIID riid, void ** ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+ if (riid == IID_IMFGetService) {
+ *ppvObject = static_cast<IMFGetService*>(this);
+ } else if (riid == IID_IMFTopologyServiceLookupClient) {
+ *ppvObject = static_cast<IMFTopologyServiceLookupClient*>(this);
+ } else if (riid == IID_IMFVideoDeviceID) {
+ *ppvObject = static_cast<IMFVideoDeviceID*>(this);
+ } else if (riid == IID_IMFVideoPresenter) {
+ *ppvObject = static_cast<IMFVideoPresenter*>(this);
+ } else if (riid == IID_IMFRateSupport) {
+ *ppvObject = static_cast<IMFRateSupport*>(this);
+ } else if (riid == IID_IUnknown) {
+ *ppvObject = static_cast<IUnknown*>(static_cast<IMFGetService*>(this));
+ } else if (riid == IID_IMFClockStateSink) {
+ *ppvObject = static_cast<IMFClockStateSink*>(this);
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+ULONG EVRCustomPresenter::AddRef()
+{
+ return InterlockedIncrement(&m_refCount);
+}
+
+ULONG EVRCustomPresenter::Release()
+{
+ ULONG uCount = InterlockedDecrement(&m_refCount);
+ if (uCount == 0)
+ deleteLater();
+ return uCount;
+}
+
+HRESULT EVRCustomPresenter::GetService(REFGUID guidService, REFIID riid, LPVOID *ppvObject)
+{
+ HRESULT hr = S_OK;
+
+ if (!ppvObject)
+ return E_POINTER;
+
+ // The only service GUID that we support is MR_VIDEO_RENDER_SERVICE.
+ if (guidService != MR_VIDEO_RENDER_SERVICE)
+ return MF_E_UNSUPPORTED_SERVICE;
+
+ // First try to get the service interface from the D3DPresentEngine object.
+ hr = m_presentEngine->getService(guidService, riid, ppvObject);
+ if (FAILED(hr))
+ // Next, check if this object supports the interface.
+ hr = QueryInterface(riid, ppvObject);
+
+ return hr;
+}
+
+HRESULT EVRCustomPresenter::GetDeviceID(IID* deviceID)
+{
+ if (!deviceID)
+ return E_POINTER;
+
+ *deviceID = IID_IDirect3DDevice9;
+
+ return S_OK;
+}
+
+HRESULT EVRCustomPresenter::InitServicePointers(IMFTopologyServiceLookup *lookup)
+{
+ if (!lookup)
+ return E_POINTER;
+
+ HRESULT hr = S_OK;
+ DWORD objectCount = 0;
+
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);
+
+ // Do not allow initializing when playing or paused.
+ if (isActive())
+ return MF_E_INVALIDREQUEST;
+
+ m_clock.Reset();
+ m_mixer.Reset();
+ m_mediaEventSink.Reset();
+
+ // Ask for the clock. Optional, because the EVR might not have a clock.
+ objectCount = 1;
+
+ lookup->LookupService(MF_SERVICE_LOOKUP_GLOBAL, 0,
+ MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_clock),
+ &objectCount
+ );
+
+ // Ask for the mixer. (Required.)
+ objectCount = 1;
+
+ hr = lookup->LookupService(MF_SERVICE_LOOKUP_GLOBAL, 0,
+ MR_VIDEO_MIXER_SERVICE, IID_PPV_ARGS(&m_mixer),
+ &objectCount
+ );
+
+ if (FAILED(hr))
+ return hr;
+
+ // Make sure that we can work with this mixer.
+ hr = configureMixer(m_mixer.Get());
+ if (FAILED(hr))
+ return hr;
+
+ // Ask for the EVR's event-sink interface. (Required.)
+ objectCount = 1;
+
+ hr = lookup->LookupService(MF_SERVICE_LOOKUP_GLOBAL, 0,
+ MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_mediaEventSink),
+ &objectCount
+ );
+
+ if (SUCCEEDED(hr))
+ m_renderState = RenderStopped;
+
+ return hr;
+}
+
+HRESULT EVRCustomPresenter::ReleaseServicePointers()
+{
+ // Enter the shut-down state.
+ m_mutex.lock();
+
+ m_renderState = RenderShutdown;
+
+ m_mutex.unlock();
+
+ // Flush any samples that were scheduled.
+ flush();
+
+ // Clear the media type and release related resources.
+ setMediaType(NULL);
+
+ // Release all services that were acquired from InitServicePointers.
+ m_clock.Reset();
+ m_mixer.Reset();
+ m_mediaEventSink.Reset();
+
+ return S_OK;
+}
+
+bool EVRCustomPresenter::isValid() const
+{
+ return m_presentEngine->isValid() && m_canRenderToSurface;
+}
+
+HRESULT EVRCustomPresenter::ProcessMessage(MFVP_MESSAGE_TYPE message, ULONG_PTR param)
+{
+ HRESULT hr = S_OK;
+
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);
+
+ hr = checkShutdown();
+ if (FAILED(hr))
+ return hr;
+
+ switch (message) {
+ // Flush all pending samples.
+ case MFVP_MESSAGE_FLUSH:
+ hr = flush();
+ break;
+
+ // Renegotiate the media type with the mixer.
+ case MFVP_MESSAGE_INVALIDATEMEDIATYPE:
+ hr = renegotiateMediaType();
+ break;
+
+ // The mixer received a new input sample.
+ case MFVP_MESSAGE_PROCESSINPUTNOTIFY:
+ hr = processInputNotify();
+ break;
+
+ // Streaming is about to start.
+ case MFVP_MESSAGE_BEGINSTREAMING:
+ hr = beginStreaming();
+ break;
+
+ // Streaming has ended. (The EVR has stopped.)
+ case MFVP_MESSAGE_ENDSTREAMING:
+ hr = endStreaming();
+ break;
+
+ // All input streams have ended.
+ case MFVP_MESSAGE_ENDOFSTREAM:
+ // Set the EOS flag.
+ m_endStreaming = true;
+ // Check if it's time to send the EC_COMPLETE event to the EVR.
+ hr = checkEndOfStream();
+ break;
+
+ // Frame-stepping is starting.
+ case MFVP_MESSAGE_STEP:
+ hr = prepareFrameStep(DWORD(param));
+ break;
+
+ // Cancels frame-stepping.
+ case MFVP_MESSAGE_CANCELSTEP:
+ hr = cancelFrameStep();
+ break;
+
+ default:
+ hr = E_INVALIDARG; // Unknown message. This case should never occur.
+ break;
+ }
+
+ return hr;
+}
+
+HRESULT EVRCustomPresenter::GetCurrentMediaType(IMFVideoMediaType **mediaType)
+{
+ HRESULT hr = S_OK;
+
+ if (!mediaType)
+ return E_POINTER;
+
+ *mediaType = NULL;
+
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);
+
+ hr = checkShutdown();
+ if (FAILED(hr))
+ return hr;
+
+ if (!m_mediaType)
+ return MF_E_NOT_INITIALIZED;
+
+ return m_mediaType->QueryInterface(IID_PPV_ARGS(mediaType));
+}
+
+HRESULT EVRCustomPresenter::OnClockStart(MFTIME, LONGLONG clockStartOffset)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);
+
+ // We cannot start after shutdown.
+ HRESULT hr = checkShutdown();
+ if (FAILED(hr))
+ return hr;
+
+ // Check if the clock is already active (not stopped).
+ if (isActive()) {
+ m_renderState = RenderStarted;
+
+ // If the clock position changes while the clock is active, it
+ // is a seek request. We need to flush all pending samples.
+ if (clockStartOffset != QMM_PRESENTATION_CURRENT_POSITION)
+ flush();
+ } else {
+ m_renderState = RenderStarted;
+
+ // The clock has started from the stopped state.
+
+ // Possibly we are in the middle of frame-stepping OR have samples waiting
+ // in the frame-step queue. Deal with these two cases first:
+ hr = startFrameStep();
+ if (FAILED(hr))
+ return hr;
+ }
+
+ // Now try to get new output samples from the mixer.
+ processOutputLoop();
+
+ return hr;
+}
+
+HRESULT EVRCustomPresenter::OnClockRestart(MFTIME)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);
+
+ HRESULT hr = checkShutdown();
+ if (FAILED(hr))
+ return hr;
+
+ // The EVR calls OnClockRestart only while paused.
+
+ m_renderState = RenderStarted;
+
+ // Possibly we are in the middle of frame-stepping OR we have samples waiting
+ // in the frame-step queue. Deal with these two cases first:
+ hr = startFrameStep();
+ if (FAILED(hr))
+ return hr;
+
+ // Now resume the presentation loop.
+ processOutputLoop();
+
+ return hr;
+}
+
+HRESULT EVRCustomPresenter::OnClockStop(MFTIME)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);
+
+ HRESULT hr = checkShutdown();
+ if (FAILED(hr))
+ return hr;
+
+ if (m_renderState != RenderStopped) {
+ m_renderState = RenderStopped;
+ flush();
+
+ // If we are in the middle of frame-stepping, cancel it now.
+ if (m_frameStep.state != FrameStepNone)
+ cancelFrameStep();
+ }
+
+ return S_OK;
+}
+
+HRESULT EVRCustomPresenter::OnClockPause(MFTIME)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);
+
+ // We cannot pause the clock after shutdown.
+ HRESULT hr = checkShutdown();
+
+ if (SUCCEEDED(hr))
+ m_renderState = RenderPaused;
+
+ return hr;
+}
+
+HRESULT EVRCustomPresenter::OnClockSetRate(MFTIME, float rate)
+{
+ // Note:
+ // The presenter reports its maximum rate through the IMFRateSupport interface.
+ // Here, we assume that the EVR honors the maximum rate.
+
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);
+
+ HRESULT hr = checkShutdown();
+ if (FAILED(hr))
+ return hr;
+
+ // If the rate is changing from zero (scrubbing) to non-zero, cancel the
+ // frame-step operation.
+ if ((m_playbackRate == 0.0f) && (rate != 0.0f)) {
+ cancelFrameStep();
+ m_frameStep.samples.clear();
+ }
+
+ m_playbackRate = rate;
+
+ // Tell the scheduler about the new rate.
+ m_scheduler.setClockRate(rate);
+
+ return S_OK;
+}
+
+HRESULT EVRCustomPresenter::GetSlowestRate(MFRATE_DIRECTION, BOOL, float *rate)
+{
+ if (!rate)
+ return E_POINTER;
+
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);
+
+ HRESULT hr = checkShutdown();
+
+ if (SUCCEEDED(hr)) {
+ // There is no minimum playback rate, so the minimum is zero.
+ *rate = 0;
+ }
+
+ return S_OK;
+}
+
+HRESULT EVRCustomPresenter::GetFastestRate(MFRATE_DIRECTION direction, BOOL thin, float *rate)
+{
+ if (!rate)
+ return E_POINTER;
+
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);
+
+ float maxRate = 0.0f;
+
+ HRESULT hr = checkShutdown();
+ if (FAILED(hr))
+ return hr;
+
+ // Get the maximum *forward* rate.
+ maxRate = getMaxRate(thin);
+
+ // For reverse playback, it's the negative of maxRate.
+ if (direction == MFRATE_REVERSE)
+ maxRate = -maxRate;
+
+ *rate = maxRate;
+
+ return S_OK;
+}
+
+HRESULT EVRCustomPresenter::IsRateSupported(BOOL thin, float rate, float *nearestSupportedRate)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);
+
+ float maxRate = 0.0f;
+ float nearestRate = rate; // If we support rate, that is the nearest.
+
+ HRESULT hr = checkShutdown();
+ if (FAILED(hr))
+ return hr;
+
+ // Find the maximum forward rate.
+ // Note: We have no minimum rate (that is, we support anything down to 0).
+ maxRate = getMaxRate(thin);
+
+ if (qFabs(rate) > maxRate) {
+ // The (absolute) requested rate exceeds the maximum rate.
+ hr = MF_E_UNSUPPORTED_RATE;
+
+ // The nearest supported rate is maxRate.
+ nearestRate = maxRate;
+ if (rate < 0) {
+ // Negative for reverse playback.
+ nearestRate = -nearestRate;
+ }
+ }
+
+ // Return the nearest supported rate.
+ if (nearestSupportedRate)
+ *nearestSupportedRate = nearestRate;
+
+ return hr;
+}
+
+void EVRCustomPresenter::supportedFormatsChanged()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);
+
+ m_canRenderToSurface = false;
+
+ // check if we can render to the surface (compatible formats)
+ if (m_videoSink) {
+ for (int f = 0; f < QVideoFrameFormat::NPixelFormats; ++f) {
+ // ### set a better preference order
+ QVideoFrameFormat::PixelFormat format = QVideoFrameFormat::PixelFormat(f);
+ if (SUCCEEDED(m_presentEngine->checkFormat(qt_evr_D3DFormatFromPixelFormat(format)))) {
+ m_canRenderToSurface = true;
+ break;
+ }
+ }
+ }
+
+ // TODO: if media type already set, renegotiate?
+}
+
+void EVRCustomPresenter::setSink(QVideoSink *sink)
+{
+ m_mutex.lock();
+ m_videoSink = sink;
+ m_presentEngine->setSink(sink);
+ m_mutex.unlock();
+
+ 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).
+ return setMixerSourceRect(mixer, m_sourceRect);
+}
+
+HRESULT EVRCustomPresenter::renegotiateMediaType()
+{
+ HRESULT hr = S_OK;
+ bool foundMediaType = false;
+
+ IMFMediaType *mixerType = NULL;
+ IMFMediaType *optimalType = NULL;
+
+ if (!m_mixer)
+ return MF_E_INVALIDREQUEST;
+
+ // Loop through all of the mixer's proposed output types.
+ DWORD typeIndex = 0;
+ while (!foundMediaType && (hr != MF_E_NO_MORE_TYPES)) {
+ qt_evr_safe_release(&mixerType);
+ qt_evr_safe_release(&optimalType);
+
+ // Step 1. Get the next media type supported by mixer.
+ hr = m_mixer->GetOutputAvailableType(0, typeIndex++, &mixerType);
+ if (FAILED(hr))
+ break;
+
+ // From now on, if anything in this loop fails, try the next type,
+ // until we succeed or the mixer runs out of types.
+
+ // Step 2. Check if we support this media type.
+ if (SUCCEEDED(hr))
+ hr = isMediaTypeSupported(mixerType);
+
+ // Step 3. Adjust the mixer's type to match our requirements.
+ if (SUCCEEDED(hr))
+ hr = createOptimalVideoType(mixerType, &optimalType);
+
+ // Step 4. Check if the mixer will accept this media type.
+ if (SUCCEEDED(hr))
+ hr = m_mixer->SetOutputType(0, optimalType, MFT_SET_TYPE_TEST_ONLY);
+
+ // Step 5. Try to set the media type on ourselves.
+ if (SUCCEEDED(hr))
+ hr = setMediaType(optimalType);
+
+ // Step 6. Set output media type on mixer.
+ if (SUCCEEDED(hr)) {
+ hr = m_mixer->SetOutputType(0, optimalType, 0);
+
+ // If something went wrong, clear the media type.
+ if (FAILED(hr))
+ setMediaType(NULL);
+ }
+
+ if (SUCCEEDED(hr))
+ foundMediaType = true;
+ }
+
+ qt_evr_safe_release(&mixerType);
+ qt_evr_safe_release(&optimalType);
+
+ return hr;
+}
+
+HRESULT EVRCustomPresenter::flush()
+{
+ m_prerolled = false;
+
+ // The scheduler might have samples that are waiting for
+ // their presentation time. Tell the scheduler to flush.
+
+ // This call blocks until the scheduler threads discards all scheduled samples.
+ m_scheduler.flush();
+
+ // Flush the frame-step queue.
+ m_frameStep.samples.clear();
+
+ if (m_renderState == RenderStopped && m_videoSink) {
+ // Repaint with black.
+ presentSample(nullptr);
+ }
+
+ return S_OK;
+}
+
+HRESULT EVRCustomPresenter::processInputNotify()
+{
+ HRESULT hr = S_OK;
+
+ // Set the flag that says the mixer has a new sample.
+ m_sampleNotify = true;
+
+ if (!m_mediaType) {
+ // We don't have a valid media type yet.
+ hr = MF_E_TRANSFORM_TYPE_NOT_SET;
+ } else {
+ // Try to process an output sample.
+ processOutputLoop();
+ }
+ return hr;
+}
+
+HRESULT EVRCustomPresenter::beginStreaming()
+{
+ HRESULT hr = S_OK;
+
+ // Start the scheduler thread.
+ hr = m_scheduler.startScheduler(m_clock);
+
+ return hr;
+}
+
+HRESULT EVRCustomPresenter::endStreaming()
+{
+ HRESULT hr = S_OK;
+
+ // Stop the scheduler thread.
+ hr = m_scheduler.stopScheduler();
+
+ return hr;
+}
+
+HRESULT EVRCustomPresenter::checkEndOfStream()
+{
+ if (!m_endStreaming) {
+ // The EVR did not send the MFVP_MESSAGE_ENDOFSTREAM message.
+ return S_OK;
+ }
+
+ if (m_sampleNotify) {
+ // The mixer still has input.
+ return S_OK;
+ }
+
+ if (m_scheduler.areSamplesScheduled()) {
+ // Samples are still scheduled for rendering.
+ return S_OK;
+ }
+
+ // Everything is complete. Now we can tell the EVR that we are done.
+ notifyEvent(EC_COMPLETE, (LONG_PTR)S_OK, 0);
+ m_endStreaming = false;
+
+ stopSurface();
+ return S_OK;
+}
+
+HRESULT EVRCustomPresenter::prepareFrameStep(DWORD steps)
+{
+ HRESULT hr = S_OK;
+
+ // Cache the step count.
+ m_frameStep.steps += steps;
+
+ // Set the frame-step state.
+ m_frameStep.state = FrameStepWaitingStart;
+
+ // If the clock is are already running, we can start frame-stepping now.
+ // Otherwise, we will start when the clock starts.
+ if (m_renderState == RenderStarted)
+ hr = startFrameStep();
+
+ return hr;
+}
+
+HRESULT EVRCustomPresenter::startFrameStep()
+{
+ 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.
+ m_frameStep.state = FrameStepPending;
+
+ // If the frame-step queue already has samples, process them now.
+ while (!m_frameStep.samples.isEmpty() && (m_frameStep.state == FrameStepPending)) {
+ const ComPtr<IMFSample> sample = m_frameStep.samples.takeFirst();
+
+ const HRESULT hr = deliverFrameStepSample(sample.Get());
+ if (FAILED(hr))
+ return hr;
+
+ // We break from this loop when:
+ // (a) the frame-step queue is empty, or
+ // (b) the frame-step operation is complete.
+ }
+ } else if (m_frameStep.state == FrameStepNone) {
+ // We are not frame stepping. Therefore, if the frame-step queue has samples,
+ // we need to process them normally.
+ while (!m_frameStep.samples.isEmpty()) {
+ const ComPtr<IMFSample> sample = m_frameStep.samples.takeFirst();
+
+ const HRESULT hr = deliverSample(sample.Get());
+ if (FAILED(hr))
+ return hr;
+ }
+ }
+
+ return S_OK;
+}
+
+HRESULT EVRCustomPresenter::completeFrameStep(const ComPtr<IMFSample> &sample)
+{
+ HRESULT hr = S_OK;
+ MFTIME sampleTime = 0;
+ MFTIME systemTime = 0;
+
+ // Update our state.
+ m_frameStep.state = FrameStepComplete;
+ m_frameStep.sampleNoRef = 0;
+
+ // Notify the EVR that the frame-step is complete.
+ notifyEvent(EC_STEP_COMPLETE, FALSE, 0); // FALSE = completed (not cancelled)
+
+ // If we are scrubbing (rate == 0), also send the "scrub time" event.
+ if (isScrubbing()) {
+ // Get the time stamp from the sample.
+ hr = sample->GetSampleTime(&sampleTime);
+ if (FAILED(hr)) {
+ // No time stamp. Use the current presentation time.
+ if (m_clock)
+ m_clock->GetCorrelatedTime(0, &sampleTime, &systemTime);
+
+ hr = S_OK; // (Not an error condition.)
+ }
+
+ notifyEvent(EC_SCRUB_TIME, DWORD(sampleTime), DWORD(((sampleTime) >> 32) & 0xffffffff));
+ }
+ return hr;
+}
+
+HRESULT EVRCustomPresenter::cancelFrameStep()
+{
+ FrameStepState oldState = m_frameStep.state;
+
+ m_frameStep.state = FrameStepNone;
+ m_frameStep.steps = 0;
+ m_frameStep.sampleNoRef = 0;
+ // Don't clear the frame-step queue yet, because we might frame step again.
+
+ if (oldState > FrameStepNone && oldState < FrameStepComplete) {
+ // We were in the middle of frame-stepping when it was cancelled.
+ // Notify the EVR.
+ notifyEvent(EC_STEP_COMPLETE, TRUE, 0); // TRUE = cancelled
+ }
+ return S_OK;
+}
+
+HRESULT EVRCustomPresenter::createOptimalVideoType(IMFMediaType *proposedType, IMFMediaType **optimalType)
+{
+ HRESULT hr = S_OK;
+
+ RECT rcOutput;
+ ZeroMemory(&rcOutput, sizeof(rcOutput));
+
+ MFVideoArea displayArea;
+ ZeroMemory(&displayArea, sizeof(displayArea));
+
+ IMFMediaType *mtOptimal = NULL;
+
+ UINT64 size;
+ int width;
+ int height;
+
+ // Clone the proposed type.
+
+ hr = MFCreateMediaType(&mtOptimal);
+ if (FAILED(hr))
+ goto done;
+
+ hr = proposedType->CopyAllItems(mtOptimal);
+ if (FAILED(hr))
+ goto done;
+
+ // Modify the new type.
+
+ hr = proposedType->GetUINT64(MF_MT_FRAME_SIZE, &size);
+ width = int(HI32(size));
+ height = int(LO32(size));
+
+ 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.left,
+ rcOutput.bottom - rcOutput.top);
+
+ hr = mtOptimal->SetUINT32(MF_MT_PAN_SCAN_ENABLED, FALSE);
+ if (FAILED(hr))
+ goto done;
+
+ hr = mtOptimal->SetBlob(MF_MT_GEOMETRIC_APERTURE, reinterpret_cast<UINT8*>(&displayArea),
+ sizeof(displayArea));
+ if (FAILED(hr))
+ goto done;
+
+ // Set the pan/scan aperture and the minimum display aperture. We don't care
+ // about them per se, but the mixer will reject the type if these exceed the
+ // frame dimentions.
+ hr = mtOptimal->SetBlob(MF_MT_PAN_SCAN_APERTURE, reinterpret_cast<UINT8*>(&displayArea),
+ sizeof(displayArea));
+ if (FAILED(hr))
+ goto done;
+
+ hr = mtOptimal->SetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, reinterpret_cast<UINT8*>(&displayArea),
+ sizeof(displayArea));
+ if (FAILED(hr))
+ goto done;
+
+ // Return the pointer to the caller.
+ *optimalType = mtOptimal;
+ (*optimalType)->AddRef();
+
+done:
+ qt_evr_safe_release(&mtOptimal);
+ return hr;
+
+}
+
+HRESULT EVRCustomPresenter::setMediaType(IMFMediaType *mediaType)
+{
+ // Note: mediaType can be NULL (to clear the type)
+
+ // Clearing the media type is allowed in any state (including shutdown).
+ if (!mediaType) {
+ stopSurface();
+ m_mediaType.Reset();
+ releaseResources();
+ return S_OK;
+ }
+
+ MFRatio fps = { 0, 0 };
+ QList<ComPtr<IMFSample>> sampleQueue;
+
+ // Cannot set the media type after shutdown.
+ HRESULT hr = checkShutdown();
+ if (FAILED(hr))
+ goto done;
+
+ // Check if the new type is actually different.
+ // Note: This function safely handles NULL input parameters.
+ 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.
+ 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, 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 : 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(std::move(sampleQueue));
+ if (FAILED(hr))
+ goto done;
+
+ // Set the frame rate on the scheduler.
+ if (SUCCEEDED(qt_evr_getFrameRate(mediaType, &fps)) && (fps.Numerator != 0) && (fps.Denominator != 0)) {
+ m_scheduler.setFrameRate(fps);
+ } else {
+ // NOTE: The mixer's proposed type might not have a frame rate, in which case
+ // we'll use an arbitrary default. (Although it's unlikely the video source
+ // does not have a frame rate.)
+ m_scheduler.setFrameRate(g_DefaultFrameRate);
+ }
+
+ // Store the media type.
+ m_mediaType = mediaType;
+ m_mediaType->AddRef();
+
+ startSurface();
+
+done:
+ if (FAILED(hr))
+ releaseResources();
+ return hr;
+}
+
+HRESULT EVRCustomPresenter::isMediaTypeSupported(IMFMediaType *proposed)
+{
+ D3DFORMAT d3dFormat = D3DFMT_UNKNOWN;
+ BOOL compressed = FALSE;
+ MFVideoInterlaceMode interlaceMode = MFVideoInterlace_Unknown;
+ MFVideoArea videoCropArea;
+ UINT32 width = 0, height = 0;
+
+ // Validate the format.
+ HRESULT hr = qt_evr_getFourCC(proposed, reinterpret_cast<DWORD*>(&d3dFormat));
+ if (FAILED(hr))
+ return hr;
+
+ QVideoFrameFormat::PixelFormat pixelFormat = pixelFormatFromMediaType(proposed);
+ if (pixelFormat == QVideoFrameFormat::Format_Invalid)
+ return MF_E_INVALIDMEDIATYPE;
+
+ // Reject compressed media types.
+ hr = proposed->IsCompressedFormat(&compressed);
+ if (FAILED(hr))
+ return hr;
+
+ if (compressed)
+ return MF_E_INVALIDMEDIATYPE;
+
+ // The D3DPresentEngine checks whether surfaces can be created using this format
+ hr = m_presentEngine->checkFormat(d3dFormat);
+ if (FAILED(hr))
+ return hr;
+
+ // Reject interlaced formats.
+ hr = proposed->GetUINT32(MF_MT_INTERLACE_MODE, reinterpret_cast<UINT32*>(&interlaceMode));
+ if (FAILED(hr))
+ return hr;
+
+ if (interlaceMode != MFVideoInterlace_Progressive)
+ return MF_E_INVALIDMEDIATYPE;
+
+ hr = MFGetAttributeSize(proposed, MF_MT_FRAME_SIZE, &width, &height);
+ if (FAILED(hr))
+ return hr;
+
+ // Validate the various apertures (cropping regions) against the frame size.
+ // Any of these apertures may be unspecified in the media type, in which case
+ // we ignore it. We just want to reject invalid apertures.
+
+ if (SUCCEEDED(proposed->GetBlob(MF_MT_PAN_SCAN_APERTURE,
+ reinterpret_cast<UINT8*>(&videoCropArea),
+ sizeof(videoCropArea), nullptr))) {
+ hr = qt_evr_validateVideoArea(videoCropArea, width, height);
+ }
+ if (SUCCEEDED(proposed->GetBlob(MF_MT_GEOMETRIC_APERTURE,
+ reinterpret_cast<UINT8*>(&videoCropArea),
+ sizeof(videoCropArea), nullptr))) {
+ hr = qt_evr_validateVideoArea(videoCropArea, width, height);
+ }
+ if (SUCCEEDED(proposed->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE,
+ reinterpret_cast<UINT8*>(&videoCropArea),
+ sizeof(videoCropArea), nullptr))) {
+ hr = qt_evr_validateVideoArea(videoCropArea, width, height);
+ }
+ return hr;
+}
+
+void EVRCustomPresenter::processOutputLoop()
+{
+ HRESULT hr = S_OK;
+
+ // Process as many samples as possible.
+ while (hr == S_OK) {
+ // If the mixer doesn't have a new input sample, break from the loop.
+ if (!m_sampleNotify) {
+ hr = MF_E_TRANSFORM_NEED_MORE_INPUT;
+ break;
+ }
+
+ // Try to process a sample.
+ hr = processOutput();
+
+ // NOTE: ProcessOutput can return S_FALSE to indicate it did not
+ // process a sample. If so, break out of the loop.
+ }
+
+ if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
+ // The mixer has run out of input data. Check for end-of-stream.
+ checkEndOfStream();
+ }
+}
+
+HRESULT EVRCustomPresenter::processOutput()
+{
+ // 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_prerolled)
+ return S_FALSE;
+
+ // Make sure we have a pointer to the mixer.
+ if (!m_mixer)
+ return MF_E_INVALIDREQUEST;
+
+ // Try to get a free sample from the video sample pool.
+ 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.
+
+ LONGLONG mixerStartTime = 0, mixerEndTime = 0;
+ MFTIME systemTime = 0;
+
+ 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.
+ 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.
+ 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.
+ hr = renegotiateMediaType();
+ } else if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
+ // There was a dynamic media type change. Clear our media type.
+ setMediaType(NULL);
+ } else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
+ // The mixer needs more input.
+ // We have to wait for the mixer to get more input.
+ m_sampleNotify = false;
+ }
+
+ return hr;
+ }
+
+ // 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.
+
+ m_clock->GetCorrelatedTime(0, &mixerEndTime, &systemTime);
+
+ LONGLONG latencyTime = mixerEndTime - mixerStartTime;
+ notifyEvent(EC_PROCESSING_LATENCY, reinterpret_cast<LONG_PTR>(&latencyTime), 0);
+ }
+
+ // Set up notification for when the sample is released.
+ hr = trackSample(sample);
+ if (FAILED(hr))
+ return hr;
+
+ // Schedule the sample.
+ if (m_frameStep.state == FrameStepNone)
+ hr = deliverSample(sample);
+ else // We are frame-stepping
+ hr = deliverFrameStepSample(sample);
+
+ if (FAILED(hr))
+ return hr;
+
+ m_prerolled = true; // We have presented at least one sample now.
+ return S_OK;
+}
+
+HRESULT EVRCustomPresenter::deliverSample(const ComPtr<IMFSample> &sample)
+{
+ // 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());
+
+ HRESULT hr = m_scheduler.scheduleSample(sample, presentNow);
+
+ if (FAILED(hr)) {
+ // Notify the EVR that we have failed during streaming. The EVR will notify the
+ // pipeline.
+
+ notifyEvent(EC_ERRORABORT, hr, 0);
+ }
+
+ return hr;
+}
+
+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.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.
+ m_frameStep.samples.append(sample);
+ } else {
+ // We're ready to frame-step.
+
+ // Decrement the number of steps.
+ if (m_frameStep.steps > 0)
+ m_frameStep.steps--;
+
+ if (m_frameStep.steps > 0) {
+ // This is not the last step. Discard this sample.
+ } else if (m_frameStep.state == FrameStepWaitingStart) {
+ // 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.
+ m_frameStep.samples.append(sample);
+ } else {
+ // This is the right frame *and* the clock has started. Deliver this sample.
+ hr = deliverSample(sample);
+ if (FAILED(hr))
+ goto done;
+
+ // Query for IUnknown so that we can identify the sample later.
+ // Per COM rules, an object always returns the same pointer when QI'ed for IUnknown.
+ hr = sample->QueryInterface(IID_PPV_ARGS(&unk));
+ if (FAILED(hr))
+ goto done;
+
+ m_frameStep.sampleNoRef = reinterpret_cast<DWORD_PTR>(unk); // No add-ref.
+
+ // NOTE: We do not AddRef the IUnknown pointer, because that would prevent the
+ // sample from invoking the OnSampleFree callback after the sample is presented.
+ // We use this IUnknown pointer purely to identify the sample later; we never
+ // attempt to dereference the pointer.
+
+ m_frameStep.state = FrameStepScheduled;
+ }
+ }
+done:
+ qt_evr_safe_release(&unk);
+ return hr;
+}
+
+HRESULT EVRCustomPresenter::trackSample(const ComPtr<IMFSample> &sample)
+{
+ IMFTrackedSample *tracked = NULL;
+
+ HRESULT hr = sample->QueryInterface(IID_PPV_ARGS(&tracked));
+
+ if (SUCCEEDED(hr))
+ hr = tracked->SetAllocator(&m_sampleFreeCB, NULL);
+
+ qt_evr_safe_release(&tracked);
+ return hr;
+}
+
+void EVRCustomPresenter::releaseResources()
+{
+ // Increment the token counter to indicate that all existing video samples
+ // are "stale." As these samples get released, we'll dispose of them.
+ //
+ // Note: The token counter is required because the samples are shared
+ // between more than one thread, and they are returned to the presenter
+ // through an asynchronous callback (onSampleFree). Without the token, we
+ // might accidentally re-use a stale sample after the ReleaseResources
+ // method returns.
+
+ m_tokenCounter++;
+
+ flush();
+
+ m_samplePool.clear();
+
+ m_presentEngine->releaseResources();
+}
+
+HRESULT EVRCustomPresenter::onSampleFree(IMFAsyncResult *result)
+{
+ IUnknown *object = NULL;
+ IMFSample *sample = NULL;
+ IUnknown *unk = NULL;
+ UINT32 token;
+
+ // Get the sample from the async result object.
+ HRESULT hr = result->GetObject(&object);
+ if (FAILED(hr))
+ goto done;
+
+ hr = object->QueryInterface(IID_PPV_ARGS(&sample));
+ if (FAILED(hr))
+ goto done;
+
+ // If this sample was submitted for a frame-step, the frame step operation
+ // is complete.
+
+ if (m_frameStep.state == FrameStepScheduled) {
+ // Query the sample for IUnknown and compare it to our cached value.
+ hr = sample->QueryInterface(IID_PPV_ARGS(&unk));
+ if (FAILED(hr))
+ goto done;
+
+ if (m_frameStep.sampleNoRef == reinterpret_cast<DWORD_PTR>(unk)) {
+ // Notify the EVR.
+ hr = completeFrameStep(sample);
+ if (FAILED(hr))
+ goto done;
+ }
+
+ // Note: Although object is also an IUnknown pointer, it is not
+ // guaranteed to be the exact pointer value returned through
+ // QueryInterface. Therefore, the second QueryInterface call is
+ // required.
+ }
+
+ m_mutex.lock();
+
+ token = MFGetAttributeUINT32(sample, MFSamplePresenter_SampleCounter, (UINT32)-1);
+
+ if (token == m_tokenCounter) {
+ // Return the sample to the sample pool.
+ m_samplePool.returnSample(sample);
+ // A free sample is available. Process more data if possible.
+ processOutputLoop();
+ }
+
+ m_mutex.unlock();
+
+done:
+ if (FAILED(hr))
+ notifyEvent(EC_ERRORABORT, hr, 0);
+ qt_evr_safe_release(&object);
+ qt_evr_safe_release(&sample);
+ qt_evr_safe_release(&unk);
+ return hr;
+}
+
+float EVRCustomPresenter::getMaxRate(bool thin)
+{
+ // Non-thinned:
+ // If we have a valid frame rate and a monitor refresh rate, the maximum
+ // playback rate is equal to the refresh rate. Otherwise, the maximum rate
+ // is unbounded (FLT_MAX).
+
+ // Thinned: The maximum rate is unbounded.
+
+ float maxRate = FLT_MAX;
+ MFRatio fps = { 0, 0 };
+ UINT monitorRateHz = 0;
+
+ if (!thin && m_mediaType) {
+ qt_evr_getFrameRate(m_mediaType.Get(), &fps);
+ monitorRateHz = m_presentEngine->refreshRate();
+
+ if (fps.Denominator && fps.Numerator && monitorRateHz) {
+ // Max Rate = Refresh Rate / Frame Rate
+ maxRate = (float)MulDiv(monitorRateHz, fps.Denominator, fps.Numerator);
+ }
+ }
+
+ return maxRate;
+}
+
+bool EVRCustomPresenter::event(QEvent *e)
+{
+ switch (int(e->type())) {
+ case StartSurface:
+ startSurface();
+ return true;
+ case StopSurface:
+ stopSurface();
+ return true;
+ case PresentSample:
+ presentSample(static_cast<PresentSampleEvent *>(e)->sample());
+ return true;
+ default:
+ break;
+ }
+ return QObject::event(e);
+}
+
+void EVRCustomPresenter::startSurface()
+{
+ if (thread() != QThread::currentThread()) {
+ QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StartSurface)));
+ return;
+ }
+}
+
+void EVRCustomPresenter::stopSurface()
+{
+ if (thread() != QThread::currentThread()) {
+ QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StopSurface)));
+ return;
+ }
+}
+
+void EVRCustomPresenter::presentSample(const ComPtr<IMFSample> &sample)
+{
+ if (thread() != QThread::currentThread()) {
+ QCoreApplication::postEvent(this, new PresentSampleEvent(sample));
+ return;
+ }
+
+ if (!m_videoSink || !m_presentEngine->videoSurfaceFormat().isValid())
+ return;
+
+ QVideoFrame frame = m_presentEngine->makeVideoFrame(sample);
+
+ // Since start/end times are related to a position when the clock is started,
+ // to have times from the beginning, need to adjust it by adding seeked position.
+ if (m_positionOffset) {
+ if (frame.startTime())
+ frame.setStartTime(frame.startTime() + m_positionOffset);
+ if (frame.endTime())
+ frame.setEndTime(frame.endTime() + m_positionOffset);
+ }
+
+ 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.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);
+ }
+ }
+
+ m_videoSink->platformVideoSink()->setVideoFrame(frame);
+}
+
+void EVRCustomPresenter::positionChanged(qint64 position)
+{
+ m_positionOffset = position * 1000;
+}
+
+HRESULT setMixerSourceRect(IMFTransform *mixer, const MFVideoNormalizedRect &sourceRect)
+{
+ if (!mixer)
+ return E_POINTER;
+
+ IMFAttributes *attributes = NULL;
+
+ HRESULT hr = mixer->GetAttributes(&attributes);
+ if (SUCCEEDED(hr)) {
+ hr = attributes->SetBlob(VIDEO_ZOOM_RECT, reinterpret_cast<const UINT8*>(&sourceRect),
+ sizeof(sourceRect));
+ attributes->Release();
+ }
+ return hr;
+}
+
+static QVideoFrameFormat::PixelFormat pixelFormatFromMediaType(IMFMediaType *type)
+{
+ GUID majorType;
+ if (FAILED(type->GetMajorType(&majorType)))
+ return QVideoFrameFormat::Format_Invalid;
+ if (majorType != MFMediaType_Video)
+ return QVideoFrameFormat::Format_Invalid;
+
+ GUID subtype;
+ if (FAILED(type->GetGUID(MF_MT_SUBTYPE, &subtype)))
+ return QVideoFrameFormat::Format_Invalid;
+
+ return QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(subtype);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/windows/evr/evrcustompresenter_p.h b/src/plugins/multimedia/windows/evr/evrcustompresenter_p.h
new file mode 100644
index 000000000..28f1cbc68
--- /dev/null
+++ b/src/plugins/multimedia/windows/evr/evrcustompresenter_p.h
@@ -0,0 +1,357 @@
+// 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
+
+//
+// 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 <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>
+#include <evr9.h>
+#include <evr.h>
+#include <mfidl.h>
+#include <mfapi.h>
+#include <mferror.h>
+
+QT_BEGIN_NAMESPACE
+
+class EVRCustomPresenter;
+class D3DPresentEngine;
+
+template<class T>
+class AsyncCallback : public IMFAsyncCallback
+{
+ Q_DISABLE_COPY(AsyncCallback)
+public:
+ typedef HRESULT (T::*InvokeFn)(IMFAsyncResult *asyncResult);
+
+ AsyncCallback(T *parent, InvokeFn fn) : m_parent(parent), m_invokeFn(fn)
+ {
+ }
+
+ // IUnknown
+ STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override
+ {
+ if (!ppv)
+ return E_POINTER;
+
+ if (iid == __uuidof(IUnknown)) {
+ *ppv = static_cast<IUnknown*>(static_cast<IMFAsyncCallback*>(this));
+ } else if (iid == __uuidof(IMFAsyncCallback)) {
+ *ppv = static_cast<IMFAsyncCallback*>(this);
+ } else {
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+ }
+
+ STDMETHODIMP_(ULONG) AddRef() override {
+ // Delegate to parent class.
+ return m_parent->AddRef();
+ }
+ STDMETHODIMP_(ULONG) Release() override {
+ // Delegate to parent class.
+ return m_parent->Release();
+ }
+
+ // IMFAsyncCallback methods
+ STDMETHODIMP GetParameters(DWORD*, DWORD*) override
+ {
+ // Implementation of this method is optional.
+ return E_NOTIMPL;
+ }
+
+ STDMETHODIMP Invoke(IMFAsyncResult* asyncResult) override
+ {
+ return (m_parent->*m_invokeFn)(asyncResult);
+ }
+
+ T *m_parent;
+ InvokeFn m_invokeFn;
+};
+
+class Scheduler
+{
+ Q_DISABLE_COPY(Scheduler)
+public:
+ enum ScheduleEvent
+ {
+ Terminate = WM_USER,
+ Schedule = WM_USER + 1,
+ Flush = WM_USER + 2
+ };
+
+ Scheduler(EVRCustomPresenter *presenter);
+ ~Scheduler();
+
+ void setFrameRate(const MFRatio &fps);
+ void setClockRate(float rate) { m_playbackRate = rate; }
+
+ HRESULT startScheduler(ComPtr<IMFClock> clock);
+ HRESULT stopScheduler();
+
+ HRESULT scheduleSample(const ComPtr<IMFSample> &sample, bool presentNow);
+ HRESULT processSamplesInQueue(LONG *nextSleep);
+ HRESULT flush();
+
+ bool areSamplesScheduled();
+
+ // ThreadProc for the scheduler thread.
+ static DWORD WINAPI schedulerThreadProc(LPVOID parameter);
+
+private:
+ DWORD schedulerThreadProcPrivate();
+ bool isSampleReadyToPresent(IMFSample *sample, LONG *pNextSleep) const;
+
+ EVRCustomPresenter *m_presenter;
+
+ QQueue<ComPtr<IMFSample>> m_scheduledSamples; // Samples waiting to be presented.
+
+ ComPtr<IMFClock> m_clock; // Presentation clock. Can be NULL.
+
+ DWORD m_threadID;
+ ThreadHandle m_schedulerThread;
+ EventHandle m_threadReadyEvent;
+ EventHandle m_flushEvent;
+
+ float m_playbackRate;
+ MFTIME m_perFrame_1_4th; // 1/4th of the frame duration.
+
+ QMutex m_mutex;
+};
+
+class SamplePool
+{
+ Q_DISABLE_COPY(SamplePool)
+public:
+ SamplePool();
+ ~SamplePool();
+
+ HRESULT initialize(QList<ComPtr<IMFSample>> &&samples);
+ HRESULT clear();
+
+ ComPtr<IMFSample> takeSample();
+ void returnSample(const ComPtr<IMFSample> &sample);
+
+private:
+ QMutex m_mutex;
+ QList<ComPtr<IMFSample>> m_videoSampleQueue;
+ bool m_initialized;
+};
+
+class EVRCustomPresenter
+ : public QObject
+ , public IMFVideoDeviceID
+ , public IMFVideoPresenter // Inherits IMFClockStateSink
+ , public IMFRateSupport
+ , public IMFGetService
+ , public IMFTopologyServiceLookupClient
+{
+ Q_DISABLE_COPY(EVRCustomPresenter)
+public:
+ // Defines the state of the presenter.
+ enum RenderState
+ {
+ RenderStarted = 1,
+ RenderStopped,
+ RenderPaused,
+ RenderShutdown // Initial state.
+ };
+
+ // Defines the presenter's state with respect to frame-stepping.
+ enum FrameStepState
+ {
+ FrameStepNone, // Not frame stepping.
+ FrameStepWaitingStart, // Frame stepping, but the clock is not started.
+ FrameStepPending, // Clock is started. Waiting for samples.
+ FrameStepScheduled, // Submitted a sample for rendering.
+ FrameStepComplete // Sample was rendered.
+ };
+
+ enum PresenterEvents
+ {
+ StartSurface = QEvent::User,
+ StopSurface = QEvent::User + 1,
+ PresentSample = QEvent::User + 2
+ };
+
+ EVRCustomPresenter(QVideoSink *sink = 0);
+ ~EVRCustomPresenter() override;
+
+ bool isValid() const;
+
+ // IUnknown methods
+ STDMETHODIMP QueryInterface(REFIID riid, void ** ppv) override;
+ STDMETHODIMP_(ULONG) AddRef() override;
+ STDMETHODIMP_(ULONG) Release() override;
+
+ // IMFGetService methods
+ STDMETHODIMP GetService(REFGUID guidService, REFIID riid, LPVOID *ppvObject) override;
+
+ // IMFVideoPresenter methods
+ STDMETHODIMP ProcessMessage(MFVP_MESSAGE_TYPE message, ULONG_PTR param) override;
+ STDMETHODIMP GetCurrentMediaType(IMFVideoMediaType** mediaType) override;
+
+ // IMFClockStateSink methods
+ STDMETHODIMP OnClockStart(MFTIME systemTime, LONGLONG clockStartOffset) override;
+ STDMETHODIMP OnClockStop(MFTIME systemTime) override;
+ STDMETHODIMP OnClockPause(MFTIME systemTime) override;
+ STDMETHODIMP OnClockRestart(MFTIME systemTime) override;
+ STDMETHODIMP OnClockSetRate(MFTIME systemTime, float rate) override;
+
+ // IMFRateSupport methods
+ STDMETHODIMP GetSlowestRate(MFRATE_DIRECTION direction, BOOL thin, float *rate) override;
+ STDMETHODIMP GetFastestRate(MFRATE_DIRECTION direction, BOOL thin, float *rate) override;
+ STDMETHODIMP IsRateSupported(BOOL thin, float rate, float *nearestSupportedRate) override;
+
+ // IMFVideoDeviceID methods
+ STDMETHODIMP GetDeviceID(IID* deviceID) override;
+
+ // IMFTopologyServiceLookupClient methods
+ STDMETHODIMP InitServicePointers(IMFTopologyServiceLookup *lookup) override;
+ STDMETHODIMP ReleaseServicePointers() override;
+
+ void supportedFormatsChanged();
+ void setSink(QVideoSink *sink);
+ void setCropRect(QRect cropRect);
+
+ void startSurface();
+ void stopSurface();
+ void presentSample(const ComPtr<IMFSample> &sample);
+
+ bool event(QEvent *) override;
+
+public Q_SLOTS:
+ void positionChanged(qint64 position);
+
+private:
+ HRESULT checkShutdown() const
+ {
+ if (m_renderState == RenderShutdown)
+ return MF_E_SHUTDOWN;
+ else
+ return S_OK;
+ }
+
+ // The "active" state is started or paused.
+ inline bool isActive() const
+ {
+ return ((m_renderState == RenderStarted) || (m_renderState == RenderPaused));
+ }
+
+ // Scrubbing occurs when the frame rate is 0.
+ inline bool isScrubbing() const { return m_playbackRate == 0.0f; }
+
+ // Send an event to the EVR through its IMediaEventSink interface.
+ void notifyEvent(long eventCode, LONG_PTR param1, LONG_PTR param2)
+ {
+ if (m_mediaEventSink)
+ m_mediaEventSink->Notify(eventCode, param1, param2);
+ }
+
+ float getMaxRate(bool thin);
+
+ // Mixer operations
+ HRESULT configureMixer(IMFTransform *mixer);
+
+ // Formats
+ HRESULT createOptimalVideoType(IMFMediaType* proposed, IMFMediaType **optimal);
+ HRESULT setMediaType(IMFMediaType *mediaType);
+ HRESULT isMediaTypeSupported(IMFMediaType *mediaType);
+
+ // Message handlers
+ HRESULT flush();
+ HRESULT renegotiateMediaType();
+ HRESULT processInputNotify();
+ HRESULT beginStreaming();
+ HRESULT endStreaming();
+ HRESULT checkEndOfStream();
+
+ // Managing samples
+ void processOutputLoop();
+ HRESULT processOutput();
+ HRESULT deliverSample(const ComPtr<IMFSample> &sample);
+ HRESULT trackSample(const ComPtr<IMFSample> &sample);
+ void releaseResources();
+
+ // Frame-stepping
+ HRESULT prepareFrameStep(DWORD steps);
+ HRESULT startFrameStep();
+ HRESULT deliverFrameStepSample(const ComPtr<IMFSample> &sample);
+ HRESULT completeFrameStep(const ComPtr<IMFSample> &sample);
+ HRESULT cancelFrameStep();
+
+ // Callback when a video sample is released.
+ HRESULT onSampleFree(IMFAsyncResult *result);
+ AsyncCallback<EVRCustomPresenter> m_sampleFreeCB;
+
+ // Holds information related to frame-stepping.
+ struct FrameStep
+ {
+ FrameStepState state = FrameStepNone;
+ QList<ComPtr<IMFSample>> samples;
+ DWORD steps = 0;
+ DWORD_PTR sampleNoRef = 0;
+ };
+
+ long m_refCount;
+
+ RenderState m_renderState;
+ FrameStep m_frameStep;
+
+ QRecursiveMutex m_mutex;
+
+ // Samples and scheduling
+ Scheduler m_scheduler; // Manages scheduling of samples.
+ SamplePool m_samplePool; // Pool of allocated samples.
+ DWORD m_tokenCounter; // Counter. Incremented whenever we create new samples.
+
+ // Rendering state
+ bool m_sampleNotify; // Did the mixer signal it has an input sample?
+ bool m_prerolled; // Have we presented at least one sample?
+ bool m_endStreaming; // Did we reach the end of the stream (EOS)?
+
+ MFVideoNormalizedRect m_sourceRect;
+ float m_playbackRate;
+
+ D3DPresentEngine *m_presentEngine; // Rendering engine. (Never null if the constructor succeeds.)
+
+ 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
+
+ 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);
+
+QT_END_NAMESPACE
+
+#endif // EVRCUSTOMPRESENTER_H
diff --git a/src/plugins/multimedia/windows/evr/evrd3dpresentengine.cpp b/src/plugins/multimedia/windows/evr/evrd3dpresentengine.cpp
new file mode 100644
index 000000000..517f1d969
--- /dev/null
+++ b/src/plugins/multimedia/windows/evr/evrd3dpresentengine.cpp
@@ -0,0 +1,699 @@
+// 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"
+
+#include "evrhelpers_p.h"
+
+#include <private/qhwvideobuffer_p.h>
+#include <private/qvideoframe_p.h>
+#include <qvideoframe.h>
+#include <QDebug>
+#include <qthread.h>
+#include <qvideosink.h>
+#include <qloggingcategory.h>
+
+#include <d3d11_1.h>
+
+#include <rhi/qrhi.h>
+
+#if QT_CONFIG(opengl)
+# include <qopenglcontext.h>
+# include <qopenglfunctions.h>
+# include <qoffscreensurface.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLcEvrD3DPresentEngine, "qt.multimedia.evrd3dpresentengine");
+
+class IMFSampleVideoBuffer : public QHwVideoBuffer
+{
+public:
+ IMFSampleVideoBuffer(ComPtr<IDirect3DDevice9Ex> device, const ComPtr<IMFSample> &sample,
+ QRhi *rhi, QVideoFrame::HandleType type = QVideoFrame::NoHandle)
+ : QHwVideoBuffer(type, rhi),
+ m_device(device),
+ m_sample(sample),
+ m_mapMode(QtVideo::MapMode::NotMapped)
+ {
+ }
+
+ ~IMFSampleVideoBuffer() override
+ {
+ if (m_memSurface && m_mapMode != QtVideo::MapMode::NotMapped)
+ m_memSurface->UnlockRect();
+ }
+
+ MapData map(QtVideo::MapMode mode) override
+ {
+ if (!m_sample || m_mapMode != QtVideo::MapMode::NotMapped || mode != QtVideo::MapMode::ReadOnly)
+ return {};
+
+ D3DSURFACE_DESC desc;
+ if (m_memSurface) {
+ if (FAILED(m_memSurface->GetDesc(&desc)))
+ return {};
+
+ } else {
+ 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 {};
+
+ if (FAILED(surface->GetDesc(&desc)))
+ return {};
+
+ 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();
+ return {};
+ }
+ }
+
+ D3DLOCKED_RECT rect;
+ if (FAILED(m_memSurface->LockRect(&rect, NULL, mode == QtVideo::MapMode::ReadOnly ? D3DLOCK_READONLY : 0)))
+ return {};
+
+ m_mapMode = mode;
+
+ MapData mapData;
+ mapData.planeCount = 1;
+ mapData.bytesPerLine[0] = (int)rect.Pitch;
+ mapData.data[0] = reinterpret_cast<uchar *>(rect.pBits);
+ mapData.dataSize[0] = (int)(rect.Pitch * desc.Height);
+ return mapData;
+ }
+
+ void unmap() override
+ {
+ if (m_mapMode == QtVideo::MapMode::NotMapped)
+ return;
+
+ m_mapMode = QtVideo::MapMode::NotMapped;
+ if (m_memSurface)
+ m_memSurface->UnlockRect();
+ }
+
+protected:
+ ComPtr<IDirect3DDevice9Ex> m_device;
+ ComPtr<IMFSample> m_sample;
+
+private:
+ ComPtr<IDirect3DSurface9> m_memSurface;
+ QtVideo::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(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<QVideoFrameTextures> mapTextures(QRhi *rhi) override
+ {
+ if (!rhi || rhi->backend() != QRhi::D3D11)
+ return {};
+
+ auto nh = static_cast<const QRhiD3D11NativeHandles*>(rhi->nativeHandles());
+ if (!nh)
+ return {};
+
+ auto dev = reinterpret_cast<ID3D11Device *>(nh->dev);
+ if (!dev)
+ return {};
+
+ 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:
+ HANDLE m_sharedHandle = nullptr;
+};
+
+#if QT_CONFIG(opengl)
+class QVideoFrameOpenGlTextures : public QVideoFrameTextures
+{
+ struct InterOpHandles {
+ GLuint textureName = 0;
+ HANDLE device = nullptr;
+ HANDLE texture = nullptr;
+ };
+
+public:
+ Q_DISABLE_COPY(QVideoFrameOpenGlTextures);
+
+ 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_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 {
+ qCDebug(qLcEvrD3DPresentEngine) << "Could not release texture, OpenGL context missing";
+ }
+ }
+
+ static std::unique_ptr<QVideoFrameOpenGlTextures> create(const WglNvDxInterop &wgl, QRhi *rhi,
+ IDirect3DDevice9Ex *device, IDirect3DTexture9 *texture,
+ HANDLE sharedHandle)
+ {
+ if (!rhi || rhi->backend() != QRhi::OpenGLES2)
+ return {};
+
+ if (!QOpenGLContext::currentContext())
+ return {};
+
+ InterOpHandles handles = {};
+ handles.device = wgl.wglDXOpenDeviceNV(device);
+ if (!handles.device) {
+ qCDebug(qLcEvrD3DPresentEngine) << "Failed to open D3D device";
+ return {};
+ }
+
+ wgl.wglDXSetResourceShareHandleNV(texture, sharedHandle);
+
+ QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
+ if (funcs) {
+ 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";
+ wgl.wglDXUnregisterObjectNV(handles.device, handles.texture);
+ } else {
+ qCDebug(qLcEvrD3DPresentEngine) << "Could not register D3D9 texture in OpenGL";
+ }
+
+ funcs->glDeleteTextures(1, &handles.textureName);
+ } else {
+ qCDebug(qLcEvrD3DPresentEngine) << "Failed generate texture names, OpenGL context functions missing";
+ }
+ return {};
+ }
+
+ QRhiTexture *texture(uint plane) const override
+ {
+ return plane == 0 ? m_tex.get() : nullptr;
+ };
+private:
+ std::unique_ptr<QRhiTexture> m_tex;
+ WglNvDxInterop m_wgl;
+ InterOpHandles m_handles;
+};
+
+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 {};
+
+ 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:
+ HANDLE m_sharedHandle = nullptr;
+ WglNvDxInterop m_wgl;
+ ComPtr<IDirect3DTexture9> m_texture;
+};
+#endif
+
+D3DPresentEngine::D3DPresentEngine(QVideoSink *sink)
+ : m_deviceResetToken(0)
+{
+ ZeroMemory(&m_displayMode, sizeof(m_displayMode));
+ setSink(sink);
+}
+
+D3DPresentEngine::~D3DPresentEngine()
+{
+ releaseResources();
+}
+
+void D3DPresentEngine::setSink(QVideoSink *sink)
+{
+ if (sink == m_sink)
+ return;
+
+ m_sink = sink;
+
+ releaseResources();
+ m_device.Reset();
+ m_devices.Reset();
+ m_D3D9.Reset();
+
+ if (!m_sink)
+ return;
+
+ HRESULT hr = initializeD3D();
+
+ if (SUCCEEDED(hr)) {
+ hr = createD3DDevice();
+ if (FAILED(hr))
+ qWarning("Failed to create D3D device");
+ } else {
+ qWarning("Failed to initialize D3D");
+ }
+}
+
+HRESULT D3DPresentEngine::initializeD3D()
+{
+ HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, m_D3D9.GetAddressOf());
+
+ if (SUCCEEDED(hr))
+ hr = DXVA2CreateDirect3DDeviceManager9(&m_deviceResetToken, m_devices.GetAddressOf());
+
+ return hr;
+}
+
+static bool findD3D11AdapterID(QRhi &rhi, IDirect3D9Ex *D3D9, UINT &adapterID)
+{
+ auto nh = static_cast<const QRhiD3D11NativeHandles*>(rhi.nativeHandles());
+ if (D3D9 && nh) {
+ for (auto i = 0u; i < D3D9->GetAdapterCount(); ++i) {
+ LUID luid = {};
+ D3D9->GetAdapterLUID(i, &luid);
+ if (luid.LowPart == nh->adapterLuidLow && luid.HighPart == nh->adapterLuidHigh) {
+ adapterID = i;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+#if QT_CONFIG(opengl)
+template <typename T>
+static bool getProc(const QOpenGLContext *ctx, T &fn, const char *fName)
+{
+ fn = reinterpret_cast<T>(ctx->getProcAddress(fName));
+ return fn != nullptr;
+}
+
+static bool readWglNvDxInteropProc(WglNvDxInterop &f)
+{
+ QScopedPointer<QOffscreenSurface> surface(new QOffscreenSurface);
+ surface->create();
+ QScopedPointer<QOpenGLContext> ctx(new QOpenGLContext);
+ ctx->create();
+ ctx->makeCurrent(surface.get());
+
+ auto wglGetExtensionsStringARB = reinterpret_cast<const char* (WINAPI* )(HDC)>
+ (ctx->getProcAddress("wglGetExtensionsStringARB"));
+ if (!wglGetExtensionsStringARB) {
+ qCDebug(qLcEvrD3DPresentEngine) << "WGL extensions missing (no wglGetExtensionsStringARB function)";
+ return false;
+ }
+
+ 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;
+ }
+
+ return getProc(ctx.get(), f.wglDXOpenDeviceNV, "wglDXOpenDeviceNV")
+ && getProc(ctx.get(), f.wglDXCloseDeviceNV, "wglDXCloseDeviceNV")
+ && getProc(ctx.get(), f.wglDXSetResourceShareHandleNV, "wglDXSetResourceShareHandleNV")
+ && getProc(ctx.get(), f.wglDXRegisterObjectNV, "wglDXRegisterObjectNV")
+ && getProc(ctx.get(), f.wglDXUnregisterObjectNV, "wglDXUnregisterObjectNV")
+ && getProc(ctx.get(), f.wglDXLockObjectsNV, "wglDXLockObjectsNV")
+ && getProc(ctx.get(), f.wglDXUnlockObjectsNV, "wglDXUnlockObjectsNV");
+}
+#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)
+ return MF_E_NOT_INITIALIZED;
+
+ m_useTextureRendering = false;
+ UINT adapterID = 0;
+
+ 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);
+#endif
+ } else {
+ qCDebug(qLcEvrD3DPresentEngine) << "Not supported RHI backend type";
+ }
+ } else {
+ qCDebug(qLcEvrD3DPresentEngine) << "No RHI associated with this sink";
+ }
+
+ if (!m_useTextureRendering)
+ qCDebug(qLcEvrD3DPresentEngine) << "Could not find compatible RHI adapter, zero copy disabled";
+ }
+
+ D3DCAPS9 ddCaps;
+ ZeroMemory(&ddCaps, sizeof(ddCaps));
+
+ HRESULT hr = m_D3D9->GetDeviceCaps(adapterID, D3DDEVTYPE_HAL, &ddCaps);
+ if (FAILED(hr))
+ return hr;
+
+ DWORD vp = 0;
+ if (ddCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
+ vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
+ else
+ vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
+
+ D3DPRESENT_PARAMETERS pp;
+ ZeroMemory(&pp, sizeof(pp));
+
+ pp.BackBufferWidth = 1;
+ pp.BackBufferHeight = 1;
+ pp.BackBufferCount = 1;
+ pp.Windowed = TRUE;
+ pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ pp.BackBufferFormat = D3DFMT_UNKNOWN;
+ pp.hDeviceWindow = nullptr;
+ pp.Flags = D3DPRESENTFLAG_VIDEO;
+ pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
+
+ ComPtr<IDirect3DDevice9Ex> device;
+
+ hr = m_D3D9->CreateDeviceEx(
+ adapterID,
+ D3DDEVTYPE_HAL,
+ pp.hDeviceWindow,
+ vp | D3DCREATE_NOWINDOWCHANGES | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
+ &pp,
+ NULL,
+ device.GetAddressOf()
+ );
+ if (FAILED(hr))
+ return hr;
+
+ hr = m_D3D9->GetAdapterDisplayMode(adapterID, &m_displayMode);
+ if (FAILED(hr))
+ return hr;
+
+ hr = m_devices->ResetDevice(device.Get(), m_deviceResetToken);
+ if (FAILED(hr))
+ return hr;
+
+ m_device = device;
+ return hr;
+}
+
+bool D3DPresentEngine::isValid() const
+{
+ return m_device.Get() != nullptr;
+}
+
+void D3DPresentEngine::releaseResources()
+{
+ m_surfaceFormat = QVideoFrameFormat();
+}
+
+HRESULT D3DPresentEngine::getService(REFGUID, REFIID riid, void** ppv)
+{
+ HRESULT hr = S_OK;
+
+ if (riid == __uuidof(IDirect3DDeviceManager9)) {
+ if (!m_devices) {
+ hr = MF_E_UNSUPPORTED_SERVICE;
+ } else {
+ *ppv = m_devices.Get();
+ m_devices->AddRef();
+ }
+ } else {
+ hr = MF_E_UNSUPPORTED_SERVICE;
+ }
+
+ return hr;
+}
+
+HRESULT D3DPresentEngine::checkFormat(D3DFORMAT format)
+{
+ if (!m_D3D9 || !m_device)
+ return E_FAIL;
+
+ HRESULT hr = S_OK;
+
+ D3DDISPLAYMODE mode;
+ D3DDEVICE_CREATION_PARAMETERS params;
+
+ hr = m_device->GetCreationParameters(&params);
+ if (FAILED(hr))
+ return hr;
+
+ UINT uAdapter = params.AdapterOrdinal;
+ D3DDEVTYPE type = params.DeviceType;
+
+ hr = m_D3D9->GetAdapterDisplayMode(uAdapter, &mode);
+ if (FAILED(hr))
+ return hr;
+
+ hr = m_D3D9->CheckDeviceFormat(uAdapter, type, mode.Format,
+ D3DUSAGE_RENDERTARGET,
+ D3DRTYPE_SURFACE,
+ format);
+ if (FAILED(hr))
+ return hr;
+
+ bool ok = format == D3DFMT_X8R8G8B8
+ || format == D3DFMT_A8R8G8B8
+ || format == D3DFMT_X8B8G8R8
+ || format == D3DFMT_A8B8G8R8;
+
+ return ok ? S_OK : D3DERR_NOTAVAILABLE;
+}
+
+HRESULT D3DPresentEngine::createVideoSamples(IMFMediaType *format,
+ QList<ComPtr<IMFSample>> &videoSampleQueue,
+ QSize frameSize)
+{
+ if (!format || !m_device)
+ return MF_E_UNEXPECTED;
+
+ HRESULT hr = S_OK;
+ releaseResources();
+
+ UINT32 width = 0, height = 0;
+ hr = MFGetAttributeSize(format, MF_MT_FRAME_SIZE, &width, &height);
+ 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))
+ return hr;
+
+ // FIXME: RHI defines only RGBA, thus add the alpha channel to the selected format
+ if (d3dFormat == D3DFMT_X8R8G8B8)
+ d3dFormat = D3DFMT_A8R8G8B8;
+ else if (d3dFormat == D3DFMT_X8B8G8R8)
+ d3dFormat = D3DFMT_A8B8G8R8;
+
+ 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.
+ ComPtr<IDirect3DTexture9> texture;
+ HANDLE sharedHandle = nullptr;
+ hr = m_device->CreateTexture(width, height, 1, D3DUSAGE_RENDERTARGET, (D3DFORMAT)d3dFormat, D3DPOOL_DEFAULT, texture.GetAddressOf(), &sharedHandle);
+ if (FAILED(hr))
+ break;
+
+ ComPtr<IDirect3DSurface9> surface;
+ hr = texture->GetSurfaceLevel(0, surface.GetAddressOf());
+ if (FAILED(hr))
+ break;
+
+ ComPtr<IMFSample> videoSample;
+ hr = MFCreateVideoSampleFromSurface(surface.Get(), videoSample.GetAddressOf());
+ if (FAILED(hr))
+ break;
+
+ m_sampleTextureHandle[i] = {videoSample.Get(), sharedHandle};
+ videoSampleQueue.append(videoSample);
+ }
+
+ if (SUCCEEDED(hr)) {
+ m_surfaceFormat = QVideoFrameFormat(QSize(width, height), qt_evr_pixelFormatFromD3DFormat(d3dFormat));
+ } else {
+ releaseResources();
+ }
+
+ return hr;
+}
+
+QVideoFrame D3DPresentEngine::makeVideoFrame(const ComPtr<IMFSample> &sample)
+{
+ if (!sample)
+ return {};
+
+ HANDLE sharedHandle = nullptr;
+ for (const auto &p : m_sampleTextureHandle)
+ if (p.first == sample.Get())
+ sharedHandle = p.second;
+
+ std::unique_ptr<IMFSampleVideoBuffer> vb;
+ QRhi *rhi = m_sink ? m_sink->rhi() : nullptr;
+ if (m_useTextureRendering && sharedHandle && rhi) {
+ if (rhi->backend() == QRhi::D3D11) {
+ vb = std::make_unique<D3D11TextureVideoBuffer>(m_device, sample, sharedHandle, rhi);
+#if QT_CONFIG(opengl)
+ } else if (rhi->backend() == QRhi::OpenGLES2) {
+ vb = std::make_unique<OpenGlVideoBuffer>(m_device, sample, m_wglNvDxInterop,
+ sharedHandle, rhi);
+#endif
+ }
+ }
+
+ if (!vb)
+ vb = std::make_unique<IMFSampleVideoBuffer>(m_device, sample, rhi);
+
+ QVideoFrame frame = QVideoFramePrivate::createFrame(std::move(vb), m_surfaceFormat);
+
+ // WMF uses 100-nanosecond units, Qt uses microseconds
+ LONGLONG startTime = 0;
+ auto hr = sample->GetSampleTime(&startTime);
+ if (SUCCEEDED(hr)) {
+ frame.setStartTime(startTime / 10);
+
+ LONGLONG duration = -1;
+ if (SUCCEEDED(sample->GetSampleDuration(&duration)))
+ frame.setEndTime((startTime + duration) / 10);
+ }
+
+ return frame;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/windows/evr/evrd3dpresentengine_p.h b/src/plugins/multimedia/windows/evr/evrd3dpresentengine_p.h
new file mode 100644
index 000000000..93aa90b71
--- /dev/null
+++ b/src/plugins/multimedia/windows/evr/evrd3dpresentengine_p.h
@@ -0,0 +1,153 @@
+// 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
+
+//
+// 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 <QMutex>
+#include <QSize>
+#include <QVideoFrameFormat>
+#include <private/qcomptr_p.h>
+#include <qpointer.h>
+
+#include <d3d9.h>
+
+struct IDirect3D9Ex;
+struct IDirect3DDevice9Ex;
+struct IDirect3DDeviceManager9;
+struct IDirect3DSurface9;
+struct IDirect3DTexture9;
+struct IMFSample;
+struct IMFMediaType;
+
+QT_BEGIN_NAMESPACE
+class QVideoFrame;
+class QVideoSink;
+QT_END_NAMESPACE
+
+// Randomly generated GUIDs
+static const GUID MFSamplePresenter_SampleCounter =
+{ 0xb0bb83cc, 0xf10f, 0x4e2e, { 0xaa, 0x2b, 0x29, 0xea, 0x5e, 0x92, 0xef, 0x85 } };
+
+#if QT_CONFIG(opengl)
+# include <qopengl.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifdef MAYBE_ANGLE
+
+class OpenGLResources;
+
+class EGLWrapper
+{
+ Q_DISABLE_COPY(EGLWrapper)
+public:
+ EGLWrapper();
+
+ __eglMustCastToProperFunctionPointerType getProcAddress(const char *procname);
+ EGLSurface createPbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list);
+ EGLBoolean destroySurface(EGLDisplay dpy, EGLSurface surface);
+ EGLBoolean bindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer);
+ EGLBoolean releaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer);
+
+private:
+ typedef __eglMustCastToProperFunctionPointerType (EGLAPIENTRYP EglGetProcAddress)(const char *procname);
+ typedef EGLSurface (EGLAPIENTRYP EglCreatePbufferSurface)(EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list);
+ typedef EGLBoolean (EGLAPIENTRYP EglDestroySurface)(EGLDisplay dpy, EGLSurface surface);
+ typedef EGLBoolean (EGLAPIENTRYP EglBindTexImage)(EGLDisplay dpy, EGLSurface surface, EGLint buffer);
+ typedef EGLBoolean (EGLAPIENTRYP EglReleaseTexImage)(EGLDisplay dpy, EGLSurface surface, EGLint buffer);
+
+ EglGetProcAddress m_eglGetProcAddress;
+ EglCreatePbufferSurface m_eglCreatePbufferSurface;
+ EglDestroySurface m_eglDestroySurface;
+ EglBindTexImage m_eglBindTexImage;
+ EglReleaseTexImage m_eglReleaseTexImage;
+};
+
+#endif // MAYBE_ANGLE
+
+#if QT_CONFIG(opengl)
+
+struct WglNvDxInterop {
+ HANDLE (WINAPI* wglDXOpenDeviceNV) (void* dxDevice);
+ BOOL (WINAPI* wglDXCloseDeviceNV) (HANDLE hDevice);
+ HANDLE (WINAPI* wglDXRegisterObjectNV) (HANDLE hDevice, void *dxObject, GLuint name, GLenum type, GLenum access);
+ BOOL (WINAPI* wglDXSetResourceShareHandleNV) (void *dxResource, HANDLE shareHandle);
+ BOOL (WINAPI* wglDXLockObjectsNV) (HANDLE hDevice, GLint count, HANDLE *hObjects);
+ BOOL (WINAPI* wglDXUnlockObjectsNV) (HANDLE hDevice, GLint count, HANDLE *hObjects);
+ BOOL (WINAPI* wglDXUnregisterObjectNV) (HANDLE hDevice, HANDLE hObject);
+
+ static const int WGL_ACCESS_READ_ONLY_NV = 0;
+};
+
+#endif
+
+class D3DPresentEngine
+{
+ Q_DISABLE_COPY(D3DPresentEngine)
+public:
+ D3DPresentEngine(QVideoSink *sink);
+ virtual ~D3DPresentEngine();
+
+ bool isValid() const;
+
+ HRESULT getService(REFGUID guidService, REFIID riid, void** ppv);
+ HRESULT checkFormat(D3DFORMAT format);
+ UINT refreshRate() const { return m_displayMode.RefreshRate; }
+
+ HRESULT createVideoSamples(IMFMediaType *format, QList<ComPtr<IMFSample>> &videoSampleQueue,
+ QSize frameSize);
+ QVideoFrameFormat videoSurfaceFormat() const { return m_surfaceFormat; }
+ QVideoFrame makeVideoFrame(const ComPtr<IMFSample> &sample);
+
+ void releaseResources();
+ void setSink(QVideoSink *sink);
+
+private:
+ static const int PRESENTER_BUFFER_COUNT = 3;
+
+ HRESULT initializeD3D();
+ HRESULT createD3DDevice();
+
+ std::pair<IMFSample *, HANDLE> m_sampleTextureHandle[PRESENTER_BUFFER_COUNT] = {};
+
+ UINT m_deviceResetToken;
+ D3DDISPLAYMODE m_displayMode;
+
+ ComPtr<IDirect3D9Ex> m_D3D9;
+ ComPtr<IDirect3DDevice9Ex> m_device;
+ ComPtr<IDirect3DDeviceManager9> m_devices;
+
+ QVideoFrameFormat m_surfaceFormat;
+
+ QPointer<QVideoSink> m_sink;
+ bool m_useTextureRendering = false;
+#if QT_CONFIG(opengl)
+ WglNvDxInterop m_wglNvDxInterop;
+#endif
+
+#ifdef MAYBE_ANGLE
+ unsigned int updateTexture(IDirect3DSurface9 *src);
+
+ OpenGLResources *m_glResources;
+ IDirect3DTexture9 *m_texture;
+#endif
+
+ friend class IMFSampleVideoBuffer;
+};
+
+QT_END_NAMESPACE
+
+#endif // EVRD3DPRESENTENGINE_H
diff --git a/src/plugins/multimedia/windows/evr/evrhelpers.cpp b/src/plugins/multimedia/windows/evr/evrhelpers.cpp
new file mode 100644
index 000000000..bf4347c69
--- /dev/null
+++ b/src/plugins/multimedia/windows/evr/evrhelpers.cpp
@@ -0,0 +1,140 @@
+// 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"
+
+#ifndef D3DFMT_YV12
+#define D3DFMT_YV12 (D3DFORMAT)MAKEFOURCC ('Y', 'V', '1', '2')
+#endif
+#ifndef D3DFMT_NV12
+#define D3DFMT_NV12 (D3DFORMAT)MAKEFOURCC ('N', 'V', '1', '2')
+#endif
+
+QT_BEGIN_NAMESPACE
+
+HRESULT qt_evr_getFourCC(IMFMediaType *type, DWORD *fourCC)
+{
+ if (!fourCC)
+ return E_POINTER;
+
+ HRESULT hr = S_OK;
+ GUID guidSubType = GUID_NULL;
+
+ if (SUCCEEDED(hr))
+ hr = type->GetGUID(MF_MT_SUBTYPE, &guidSubType);
+
+ if (SUCCEEDED(hr))
+ *fourCC = guidSubType.Data1;
+
+ return hr;
+}
+
+bool qt_evr_areMediaTypesEqual(IMFMediaType *type1, IMFMediaType *type2)
+{
+ if (!type1 && !type2)
+ return true;
+ if (!type1 || !type2)
+ return false;
+
+ DWORD dwFlags = 0;
+ HRESULT hr = type1->IsEqual(type2, &dwFlags);
+
+ return (hr == S_OK);
+}
+
+HRESULT qt_evr_validateVideoArea(const MFVideoArea& area, UINT32 width, UINT32 height)
+{
+ float fOffsetX = qt_evr_MFOffsetToFloat(area.OffsetX);
+ float fOffsetY = qt_evr_MFOffsetToFloat(area.OffsetY);
+
+ if ( ((LONG)fOffsetX + area.Area.cx > (LONG)width) ||
+ ((LONG)fOffsetY + area.Area.cy > (LONG)height) ) {
+ return MF_E_INVALIDMEDIATYPE;
+ }
+ return S_OK;
+}
+
+bool qt_evr_isSampleTimePassed(IMFClock *clock, IMFSample *sample)
+{
+ if (!sample || !clock)
+ return false;
+
+ HRESULT hr = S_OK;
+ MFTIME hnsTimeNow = 0;
+ MFTIME hnsSystemTime = 0;
+ MFTIME hnsSampleStart = 0;
+ MFTIME hnsSampleDuration = 0;
+
+ hr = clock->GetCorrelatedTime(0, &hnsTimeNow, &hnsSystemTime);
+
+ if (SUCCEEDED(hr))
+ hr = sample->GetSampleTime(&hnsSampleStart);
+
+ if (SUCCEEDED(hr))
+ hr = sample->GetSampleDuration(&hnsSampleDuration);
+
+ if (SUCCEEDED(hr)) {
+ if (hnsSampleStart + hnsSampleDuration < hnsTimeNow)
+ return true;
+ }
+
+ return false;
+}
+
+QVideoFrameFormat::PixelFormat qt_evr_pixelFormatFromD3DFormat(DWORD format)
+{
+ switch (format) {
+ case D3DFMT_A8R8G8B8:
+ return QVideoFrameFormat::Format_BGRA8888;
+ case D3DFMT_X8R8G8B8:
+ return QVideoFrameFormat::Format_BGRX8888;
+ case D3DFMT_A8:
+ return QVideoFrameFormat::Format_Y8;
+ case D3DFMT_A8B8G8R8:
+ return QVideoFrameFormat::Format_RGBA8888;
+ case D3DFMT_X8B8G8R8:
+ return QVideoFrameFormat::Format_RGBX8888;
+ case D3DFMT_UYVY:
+ return QVideoFrameFormat::Format_UYVY;
+ case D3DFMT_YUY2:
+ return QVideoFrameFormat::Format_YUYV;
+ case D3DFMT_NV12:
+ return QVideoFrameFormat::Format_NV12;
+ case D3DFMT_YV12:
+ return QVideoFrameFormat::Format_YV12;
+ case D3DFMT_UNKNOWN:
+ default:
+ return QVideoFrameFormat::Format_Invalid;
+ }
+}
+
+D3DFORMAT qt_evr_D3DFormatFromPixelFormat(QVideoFrameFormat::PixelFormat format)
+{
+ switch (format) {
+ case QVideoFrameFormat::Format_ARGB8888:
+ return D3DFMT_A8B8G8R8;
+ case QVideoFrameFormat::Format_BGRA8888:
+ return D3DFMT_A8R8G8B8;
+ case QVideoFrameFormat::Format_BGRX8888:
+ return D3DFMT_X8R8G8B8;
+ case QVideoFrameFormat::Format_Y8:
+ return D3DFMT_A8;
+ case QVideoFrameFormat::Format_RGBA8888:
+ return D3DFMT_A8B8G8R8;
+ case QVideoFrameFormat::Format_RGBX8888:
+ return D3DFMT_X8B8G8R8;
+ case QVideoFrameFormat::Format_UYVY:
+ return D3DFMT_UYVY;
+ case QVideoFrameFormat::Format_YUYV:
+ return D3DFMT_YUY2;
+ case QVideoFrameFormat::Format_NV12:
+ return D3DFMT_NV12;
+ case QVideoFrameFormat::Format_YV12:
+ return D3DFMT_YV12;
+ case QVideoFrameFormat::Format_Invalid:
+ default:
+ return D3DFMT_UNKNOWN;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/windows/evr/evrhelpers_p.h b/src/plugins/multimedia/windows/evr/evrhelpers_p.h
new file mode 100644
index 000000000..30779c835
--- /dev/null
+++ b/src/plugins/multimedia/windows/evr/evrhelpers_p.h
@@ -0,0 +1,93 @@
+// 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
+
+//
+// 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 <d3d9.h>
+#include <dxva2api.h>
+#include <evr9.h>
+#include <evr.h>
+#include <mfidl.h>
+#include <mfapi.h>
+#include <mferror.h>
+#include <private/quniquehandle_p.h>
+
+QT_BEGIN_NAMESPACE
+
+template<class T>
+static inline void qt_evr_safe_release(T **unk)
+{
+ if (*unk) {
+ (*unk)->Release();
+ *unk = NULL;
+ }
+}
+
+HRESULT qt_evr_getFourCC(IMFMediaType *type, DWORD *fourCC);
+
+bool qt_evr_areMediaTypesEqual(IMFMediaType *type1, IMFMediaType *type2);
+
+HRESULT qt_evr_validateVideoArea(const MFVideoArea& area, UINT32 width, UINT32 height);
+
+bool qt_evr_isSampleTimePassed(IMFClock *clock, IMFSample *sample);
+
+inline float qt_evr_MFOffsetToFloat(const MFOffset& offset)
+{
+ return offset.value + (float(offset.fract) / 65536);
+}
+
+inline MFOffset qt_evr_makeMFOffset(float v)
+{
+ MFOffset offset;
+ offset.value = short(v);
+ offset.fract = WORD(65536 * (v-offset.value));
+ return offset;
+}
+
+inline MFVideoArea qt_evr_makeMFArea(float x, float y, DWORD width, DWORD height)
+{
+ MFVideoArea area;
+ area.OffsetX = qt_evr_makeMFOffset(x);
+ area.OffsetY = qt_evr_makeMFOffset(y);
+ area.Area.cx = width;
+ area.Area.cy = height;
+ return area;
+}
+
+inline HRESULT qt_evr_getFrameRate(IMFMediaType *pType, MFRatio *pRatio)
+{
+ return MFGetAttributeRatio(pType, MF_MT_FRAME_RATE,
+ reinterpret_cast<UINT32*>(&pRatio->Numerator),
+ reinterpret_cast<UINT32*>(&pRatio->Denominator));
+}
+
+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
new file mode 100644
index 000000000..854c9ddb2
--- /dev/null
+++ b/src/plugins/multimedia/windows/evr/evrvideowindowcontrol.cpp
@@ -0,0 +1,228 @@
+// 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)
+ , m_windowColor(RGB(0, 0, 0))
+ , m_dirtyValues(0)
+ , m_aspectRatioMode(Qt::KeepAspectRatio)
+ , m_brightness(0)
+ , m_contrast(0)
+ , m_hue(0)
+ , m_saturation(0)
+ , m_fullScreen(false)
+ , m_displayControl(0)
+ , m_processor(0)
+{
+}
+
+EvrVideoWindowControl::~EvrVideoWindowControl()
+{
+ clear();
+}
+
+bool EvrVideoWindowControl::setEvr(IUnknown *evr)
+{
+ clear();
+
+ if (!evr)
+ return true;
+
+ IMFGetService *service = NULL;
+
+ if (SUCCEEDED(evr->QueryInterface(IID_PPV_ARGS(&service)))
+ && SUCCEEDED(service->GetService(MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_displayControl)))) {
+
+ service->GetService(MR_VIDEO_MIXER_SERVICE, IID_PPV_ARGS(&m_processor));
+
+ setWinId(m_windowId);
+ setDisplayRect(m_displayRect);
+ setAspectRatioMode(m_aspectRatioMode);
+ m_dirtyValues = DXVA2_ProcAmp_Brightness | DXVA2_ProcAmp_Contrast | DXVA2_ProcAmp_Hue | DXVA2_ProcAmp_Saturation;
+ applyImageControls();
+ }
+
+ if (service)
+ service->Release();
+
+ return m_displayControl != NULL;
+}
+
+void EvrVideoWindowControl::clear()
+{
+ if (m_displayControl)
+ m_displayControl->Release();
+ m_displayControl = NULL;
+
+ if (m_processor)
+ m_processor->Release();
+ m_processor = NULL;
+}
+
+void EvrVideoWindowControl::setWinId(WId id)
+{
+ m_windowId = id;
+
+ if (m_displayControl)
+ m_displayControl->SetVideoWindow(HWND(m_windowId));
+}
+
+void EvrVideoWindowControl::setDisplayRect(const QRect &rect)
+{
+ m_displayRect = rect;
+
+ if (m_displayControl) {
+ RECT displayRect = { rect.left(), rect.top(), rect.right() + 1, rect.bottom() + 1 };
+ QSize sourceSize = nativeSize();
+
+ RECT sourceRect = { 0, 0, sourceSize.width(), sourceSize.height() };
+
+ if (m_aspectRatioMode == Qt::KeepAspectRatioByExpanding) {
+ QSize clippedSize = rect.size();
+ clippedSize.scale(sourceRect.right, sourceRect.bottom, Qt::KeepAspectRatio);
+
+ sourceRect.left = (sourceRect.right - clippedSize.width()) / 2;
+ sourceRect.top = (sourceRect.bottom - clippedSize.height()) / 2;
+ sourceRect.right = sourceRect.left + clippedSize.width();
+ sourceRect.bottom = sourceRect.top + clippedSize.height();
+ }
+
+ if (sourceSize.width() > 0 && sourceSize.height() > 0) {
+ MFVideoNormalizedRect sourceNormRect;
+ sourceNormRect.left = float(sourceRect.left) / float(sourceRect.right);
+ sourceNormRect.top = float(sourceRect.top) / float(sourceRect.bottom);
+ sourceNormRect.right = float(sourceRect.right) / float(sourceRect.right);
+ sourceNormRect.bottom = float(sourceRect.bottom) / float(sourceRect.bottom);
+ m_displayControl->SetVideoPosition(&sourceNormRect, &displayRect);
+ } else {
+ m_displayControl->SetVideoPosition(NULL, &displayRect);
+ }
+ }
+}
+
+void EvrVideoWindowControl::setFullScreen(bool fullScreen)
+{
+ if (m_fullScreen == fullScreen)
+ return;
+}
+
+void EvrVideoWindowControl::setAspectRatioMode(Qt::AspectRatioMode mode)
+{
+ m_aspectRatioMode = mode;
+
+ if (m_displayControl) {
+ switch (mode) {
+ case Qt::IgnoreAspectRatio:
+ //comment from MSDN: Do not maintain the aspect ratio of the video. Stretch the video to fit the output rectangle.
+ m_displayControl->SetAspectRatioMode(MFVideoARMode_None);
+ break;
+ case Qt::KeepAspectRatio:
+ //comment from MSDN: Preserve the aspect ratio of the video by letterboxing or within the output rectangle.
+ m_displayControl->SetAspectRatioMode(MFVideoARMode_PreservePicture);
+ break;
+ case Qt::KeepAspectRatioByExpanding:
+ //for this mode, more adjustment will be done in setDisplayRect
+ m_displayControl->SetAspectRatioMode(MFVideoARMode_PreservePicture);
+ break;
+ default:
+ break;
+ }
+ setDisplayRect(m_displayRect);
+ }
+}
+
+void EvrVideoWindowControl::setBrightness(float brightness)
+{
+ if (m_brightness == brightness)
+ return;
+
+ m_brightness = brightness;
+
+ m_dirtyValues |= DXVA2_ProcAmp_Brightness;
+
+ applyImageControls();
+}
+
+void EvrVideoWindowControl::setContrast(float contrast)
+{
+ if (m_contrast == contrast)
+ return;
+
+ m_contrast = contrast;
+
+ m_dirtyValues |= DXVA2_ProcAmp_Contrast;
+
+ applyImageControls();
+}
+
+void EvrVideoWindowControl::setHue(float hue)
+{
+ if (m_hue == hue)
+ return;
+
+ m_hue = hue;
+
+ m_dirtyValues |= DXVA2_ProcAmp_Hue;
+
+ applyImageControls();
+}
+
+void EvrVideoWindowControl::setSaturation(float saturation)
+{
+ if (m_saturation == saturation)
+ return;
+
+ m_saturation = saturation;
+
+ m_dirtyValues |= DXVA2_ProcAmp_Saturation;
+
+ applyImageControls();
+}
+
+void EvrVideoWindowControl::applyImageControls()
+{
+ if (m_processor) {
+ DXVA2_ProcAmpValues values;
+ if (m_dirtyValues & DXVA2_ProcAmp_Brightness) {
+ values.Brightness = scaleProcAmpValue(DXVA2_ProcAmp_Brightness, m_brightness);
+ }
+ if (m_dirtyValues & DXVA2_ProcAmp_Contrast) {
+ values.Contrast = scaleProcAmpValue(DXVA2_ProcAmp_Contrast, m_contrast);
+ }
+ if (m_dirtyValues & DXVA2_ProcAmp_Hue) {
+ values.Hue = scaleProcAmpValue(DXVA2_ProcAmp_Hue, m_hue);
+ }
+ if (m_dirtyValues & DXVA2_ProcAmp_Saturation) {
+ values.Saturation = scaleProcAmpValue(DXVA2_ProcAmp_Saturation, m_saturation);
+ }
+
+ if (SUCCEEDED(m_processor->SetProcAmpValues(m_dirtyValues, &values))) {
+ m_dirtyValues = 0;
+ }
+ }
+}
+
+DXVA2_Fixed32 EvrVideoWindowControl::scaleProcAmpValue(DWORD prop, float value) const
+{
+ float scaledValue = 0.0;
+
+ DXVA2_ValueRange range;
+ if (SUCCEEDED(m_processor->GetProcAmpRange(prop, &range))) {
+ scaledValue = DXVA2FixedToFloat(range.DefaultValue);
+ if (value > 0)
+ scaledValue += float(value) * (DXVA2FixedToFloat(range.MaxValue) - DXVA2FixedToFloat(range.DefaultValue));
+ else if (value < 0)
+ scaledValue -= float(value) * (DXVA2FixedToFloat(range.MinValue) - DXVA2FixedToFloat(range.DefaultValue));
+ }
+
+ 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
new file mode 100644
index 000000000..c4875d28d
--- /dev/null
+++ b/src/plugins/multimedia/windows/evr/evrvideowindowcontrol_p.h
@@ -0,0 +1,72 @@
+// 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
+
+//
+// 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 <d3d9.h>
+#include <dxva2api.h>
+#include <evr9.h>
+#include <evr.h>
+#include <private/qplatformvideosink_p.h>
+#include <private/qwindowsmfdefs_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class EvrVideoWindowControl : public QPlatformVideoSink
+{
+ Q_OBJECT
+public:
+ EvrVideoWindowControl(QVideoSink *parent = 0);
+ ~EvrVideoWindowControl() override;
+
+ bool setEvr(IUnknown *evr);
+
+ void setWinId(WId id) override;
+
+ void setDisplayRect(const QRect &rect) override;
+
+ void setFullScreen(bool fullScreen) override;
+
+ void setAspectRatioMode(Qt::AspectRatioMode mode) override;
+
+ void setBrightness(float brightness) override;
+ void setContrast(float contrast) override;
+ void setHue(float hue) override;
+ void setSaturation(float saturation) override;
+
+ void applyImageControls();
+
+private:
+ void clear();
+ DXVA2_Fixed32 scaleProcAmpValue(DWORD prop, float value) const;
+
+ WId m_windowId;
+ COLORREF m_windowColor;
+ DWORD m_dirtyValues;
+ Qt::AspectRatioMode m_aspectRatioMode;
+ QRect m_displayRect;
+ float m_brightness;
+ float m_contrast;
+ float m_hue;
+ float m_saturation;
+ bool m_fullScreen;
+
+ IMFVideoDisplayControl *m_displayControl;
+ IMFVideoProcessor *m_processor;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowscamera.cpp b/src/plugins/multimedia/windows/mediacapture/qwindowscamera.cpp
new file mode 100644
index 000000000..d5e25e1c5
--- /dev/null
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowscamera.cpp
@@ -0,0 +1,101 @@
+// 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"
+
+#include "qwindowsmediadevicesession_p.h"
+#include "qwindowsmediacapture_p.h"
+#include <qcameradevice.h>
+
+QT_BEGIN_NAMESPACE
+
+QWindowsCamera::QWindowsCamera(QCamera *camera)
+ : QPlatformCamera(camera)
+{
+}
+
+QWindowsCamera::~QWindowsCamera() = default;
+
+bool QWindowsCamera::isActive() const
+{
+ return m_active;
+}
+
+void QWindowsCamera::setActive(bool active)
+{
+ if (m_active == active)
+ return;
+ if (m_cameraDevice.isNull() && active)
+ return;
+ m_active = active;
+ if (m_mediaDeviceSession)
+ m_mediaDeviceSession->setActive(active);
+
+ emit activeChanged(m_active);
+}
+
+void QWindowsCamera::setCamera(const QCameraDevice &camera)
+{
+ if (m_cameraDevice == camera)
+ return;
+ m_cameraDevice = camera;
+ if (m_mediaDeviceSession)
+ m_mediaDeviceSession->setActiveCamera(camera);
+}
+
+void QWindowsCamera::setCaptureSession(QPlatformMediaCaptureSession *session)
+{
+ QWindowsMediaCaptureService *captureService = static_cast<QWindowsMediaCaptureService *>(session);
+ if (m_captureService == captureService)
+ return;
+
+ if (m_mediaDeviceSession) {
+ m_mediaDeviceSession->disconnect(this);
+ m_mediaDeviceSession->setActive(false);
+ m_mediaDeviceSession->setCameraFormat({});
+ m_mediaDeviceSession->setActiveCamera({});
+ }
+
+ m_captureService = captureService;
+ if (!m_captureService) {
+ m_mediaDeviceSession = nullptr;
+ return;
+ }
+
+ m_mediaDeviceSession = m_captureService->session();
+ Q_ASSERT(m_mediaDeviceSession);
+
+ m_mediaDeviceSession->setActive(false);
+ m_mediaDeviceSession->setActiveCamera(m_cameraDevice);
+ m_mediaDeviceSession->setCameraFormat(m_cameraFormat);
+ m_mediaDeviceSession->setActive(m_active);
+
+ connect(m_mediaDeviceSession, &QWindowsMediaDeviceSession::activeChanged,
+ this, &QWindowsCamera::onActiveChanged);
+}
+
+bool QWindowsCamera::setCameraFormat(const QCameraFormat &format)
+{
+ if (!format.isNull() && !m_cameraDevice.videoFormats().contains(format))
+ return false;
+
+ m_cameraFormat = format.isNull() ? findBestCameraFormat(m_cameraDevice) : format;
+
+ if (m_mediaDeviceSession)
+ m_mediaDeviceSession->setCameraFormat(m_cameraFormat);
+ return true;
+}
+
+void QWindowsCamera::onActiveChanged(bool active)
+{
+ if (m_active == active)
+ return;
+ if (m_cameraDevice.isNull() && active)
+ return;
+ m_active = active;
+ emit activeChanged(m_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
new file mode 100644
index 000000000..2aec11165
--- /dev/null
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowscamera_p.h
@@ -0,0 +1,55 @@
+// 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
+
+//
+// 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>
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsMediaCaptureService;
+class QWindowsMediaDeviceSession;
+
+class QWindowsCamera : public QPlatformCamera
+{
+ Q_OBJECT
+public:
+ explicit QWindowsCamera(QCamera *camera);
+ virtual ~QWindowsCamera();
+
+ bool isActive() const override;
+
+ void setCamera(const QCameraDevice &camera) override;
+
+ void setCaptureSession(QPlatformMediaCaptureSession *) override;
+
+ bool setCameraFormat(const QCameraFormat &format) override;
+
+ void setActive(bool active) override;
+
+private Q_SLOTS:
+ void onActiveChanged(bool active);
+
+private:
+ QWindowsMediaCaptureService *m_captureService = nullptr;
+ QWindowsMediaDeviceSession *m_mediaDeviceSession = nullptr;
+ QCameraDevice m_cameraDevice;
+ QCameraFormat m_cameraFormat;
+ bool m_active = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSCAMERA_H
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsimagecapture.cpp b/src/plugins/multimedia/windows/mediacapture/qwindowsimagecapture.cpp
new file mode 100644
index 000000000..ea66d561a
--- /dev/null
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsimagecapture.cpp
@@ -0,0 +1,207 @@
+// 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"
+
+#include "qwindowsmediadevicesession_p.h"
+#include "qwindowsmediacapture_p.h"
+#include <private/qmediastoragelocation_p.h>
+
+#include <QtConcurrent/qtconcurrentrun.h>
+#include <QtGui/qimagewriter.h>
+
+QT_BEGIN_NAMESPACE
+
+QWindowsImageCapture::QWindowsImageCapture(QImageCapture *parent)
+ : QPlatformImageCapture(parent)
+{
+}
+
+QWindowsImageCapture::~QWindowsImageCapture() = default;
+
+bool QWindowsImageCapture::isReadyForCapture() const
+{
+ if (!m_mediaDeviceSession)
+ return false;
+ return !m_capturing && m_mediaDeviceSession->isActive() && !m_mediaDeviceSession->activeCamera().isNull();
+}
+
+int QWindowsImageCapture::capture(const QString &fileName)
+{
+ auto ext = writerFormat(m_settings.format());
+ auto path = QMediaStorageLocation::generateFileName(fileName, QStandardPaths::PicturesLocation, ext);
+ return doCapture(path);
+}
+
+int QWindowsImageCapture::captureToBuffer()
+{
+ return doCapture(QString());
+}
+
+int QWindowsImageCapture::doCapture(const QString &fileName)
+{
+ if (!isReadyForCapture())
+ return -1;
+ m_fileName = fileName;
+ m_capturing = true;
+ return m_captureId;
+}
+
+QImageEncoderSettings QWindowsImageCapture::imageSettings() const
+{
+ return m_settings;
+}
+
+void QWindowsImageCapture::setImageSettings(const QImageEncoderSettings &settings)
+{
+ m_settings = settings;
+}
+
+void QWindowsImageCapture::setCaptureSession(QPlatformMediaCaptureSession *session)
+{
+ QWindowsMediaCaptureService *captureService = static_cast<QWindowsMediaCaptureService *>(session);
+ if (m_captureService == captureService)
+ return;
+
+ auto readyForCapture = isReadyForCapture();
+ if (m_mediaDeviceSession)
+ disconnect(m_mediaDeviceSession, nullptr, this, nullptr);
+
+ m_captureService = captureService;
+ if (!m_captureService) {
+ if (readyForCapture)
+ emit readyForCaptureChanged(false);
+ m_mediaDeviceSession = nullptr;
+ return;
+ }
+
+ m_mediaDeviceSession = m_captureService->session();
+ Q_ASSERT(m_mediaDeviceSession);
+
+ if (isReadyForCapture() != readyForCapture)
+ emit readyForCaptureChanged(isReadyForCapture());
+
+ connect(m_mediaDeviceSession, &QWindowsMediaDeviceSession::readyForCaptureChanged,
+ this, &QWindowsImageCapture::readyForCaptureChanged);
+
+ connect(m_mediaDeviceSession, &QWindowsMediaDeviceSession::videoFrameChanged,
+ this, &QWindowsImageCapture::handleVideoFrameChanged);
+}
+
+void QWindowsImageCapture::handleVideoFrameChanged(const QVideoFrame &frame)
+{
+ if (m_capturing) {
+
+ 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, size);
+
+ emit imageMetadataAvailable(m_captureId, metaData);
+
+ if (!m_fileName.isEmpty()) {
+
+ (void)QtConcurrent::run(&QWindowsImageCapture::saveImage, this,
+ m_captureId, m_fileName, image, metaData, m_settings);
+ }
+
+ ++m_captureId;
+ m_capturing = false;
+ }
+}
+
+void QWindowsImageCapture::saveImage(int captureId, const QString &fileName,
+ const QImage &image, const QMediaMetaData &metaData,
+ const QImageEncoderSettings &settings)
+{
+ QImageWriter imageWriter;
+ imageWriter.setFileName(fileName);
+
+ QString format = writerFormat(settings.format());
+ imageWriter.setFormat(format.toUtf8());
+
+ int quality = writerQuality(format, settings.quality());
+ if (quality > -1)
+ imageWriter.setQuality(quality);
+
+ for (auto key : metaData.keys())
+ imageWriter.setText(QMediaMetaData::metaDataKeyToString(key),
+ metaData.stringValue(key));
+
+ imageWriter.write(image);
+
+ QMetaObject::invokeMethod(this, "imageSaved", Qt::QueuedConnection,
+ Q_ARG(int, captureId), Q_ARG(QString, fileName));
+}
+
+QString QWindowsImageCapture::writerFormat(QImageCapture::FileFormat reqFormat)
+{
+ QString format;
+
+ switch (reqFormat) {
+ case QImageCapture::FileFormat::JPEG:
+ format = QLatin1String("jpg");
+ break;
+ case QImageCapture::FileFormat::PNG:
+ format = QLatin1String("png");
+ break;
+ case QImageCapture::FileFormat::WebP:
+ format = QLatin1String("webp");
+ break;
+ case QImageCapture::FileFormat::Tiff:
+ format = QLatin1String("tiff");
+ break;
+ default:
+ format = QLatin1String("jpg");
+ }
+
+ auto supported = QImageWriter::supportedImageFormats();
+ for (const auto &f : supported)
+ if (format.compare(QString::fromUtf8(f), Qt::CaseInsensitive) == 0)
+ return format;
+
+ return QLatin1String("jpg");
+}
+
+int QWindowsImageCapture::writerQuality(const QString &writerFormat,
+ QImageCapture::Quality quality)
+{
+ if (writerFormat.compare(QLatin1String("jpg"), Qt::CaseInsensitive) == 0 ||
+ writerFormat.compare(QLatin1String("jpeg"), Qt::CaseInsensitive) == 0) {
+
+ switch (quality) {
+ case QImageCapture::Quality::VeryLowQuality:
+ return 10;
+ case QImageCapture::Quality::LowQuality:
+ return 30;
+ case QImageCapture::Quality::NormalQuality:
+ return 75;
+ case QImageCapture::Quality::HighQuality:
+ return 90;
+ case QImageCapture::Quality::VeryHighQuality:
+ return 98;
+ default:
+ return 75;
+ }
+ }
+ return -1;
+}
+
+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
new file mode 100644
index 000000000..746732e73
--- /dev/null
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsimagecapture_p.h
@@ -0,0 +1,64 @@
+// 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
+
+//
+// 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/qplatformimagecapture_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsMediaDeviceSession;
+class QWindowsMediaCaptureService;
+
+class QWindowsImageCapture : public QPlatformImageCapture
+{
+ Q_OBJECT
+public:
+ explicit QWindowsImageCapture(QImageCapture *parent);
+ virtual ~QWindowsImageCapture();
+
+ bool isReadyForCapture() const override;
+
+ int capture(const QString &fileName) override;
+ int captureToBuffer() override;
+
+ QImageEncoderSettings imageSettings() const override;
+ void setImageSettings(const QImageEncoderSettings &settings) override;
+
+ void setCaptureSession(QPlatformMediaCaptureSession *session);
+
+private Q_SLOTS:
+ void handleVideoFrameChanged(const QVideoFrame &frame);
+
+private:
+ int doCapture(const QString &fileName);
+ void saveImage(int captureId, const QString &fileName,
+ const QImage &image, const QMediaMetaData &metaData,
+ const QImageEncoderSettings &settings);
+ QString writerFormat(QImageCapture::FileFormat reqFormat);
+ int writerQuality(const QString &writerFormat,
+ QImageCapture::Quality quality);
+
+ QWindowsMediaCaptureService *m_captureService = nullptr;
+ QWindowsMediaDeviceSession *m_mediaDeviceSession = nullptr;
+ QImageEncoderSettings m_settings;
+ int m_captureId = 0;
+ bool m_capturing = false;
+ QString m_fileName;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSIMAGECAPTURE_H
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsmediacapture.cpp b/src/plugins/multimedia/windows/mediacapture/qwindowsmediacapture.cpp
new file mode 100644
index 000000000..d349b2c43
--- /dev/null
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediacapture.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 "qwindowsmediacapture_p.h"
+
+#include "qwindowsmediaencoder_p.h"
+#include "qwindowscamera_p.h"
+#include "qwindowsmediadevicesession_p.h"
+#include "qwindowsimagecapture_p.h"
+#include "qmediadevices.h"
+#include "qaudiodevice.h"
+#include "private/qplatformaudioinput_p.h"
+#include "private/qplatformaudiooutput_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QWindowsMediaCaptureService::QWindowsMediaCaptureService()
+{
+ m_mediaDeviceSession = new QWindowsMediaDeviceSession(this);
+}
+
+QWindowsMediaCaptureService::~QWindowsMediaCaptureService()
+{
+ delete m_mediaDeviceSession;
+}
+
+QPlatformCamera *QWindowsMediaCaptureService::camera()
+{
+ return m_camera;
+}
+
+void QWindowsMediaCaptureService::setCamera(QPlatformCamera *camera)
+{
+ QWindowsCamera *control = static_cast<QWindowsCamera*>(camera);
+ if (m_camera == control)
+ return;
+
+ if (m_camera)
+ m_camera->setCaptureSession(nullptr);
+
+ m_camera = control;
+ if (m_camera)
+ m_camera->setCaptureSession(this);
+ emit cameraChanged();
+}
+
+QPlatformImageCapture *QWindowsMediaCaptureService::imageCapture()
+{
+ return m_imageCapture;
+}
+
+void QWindowsMediaCaptureService::setImageCapture(QPlatformImageCapture *imageCapture)
+{
+ QWindowsImageCapture *control = static_cast<QWindowsImageCapture *>(imageCapture);
+ if (m_imageCapture == control)
+ return;
+
+ if (m_imageCapture)
+ m_imageCapture->setCaptureSession(nullptr);
+
+ m_imageCapture = control;
+ if (m_imageCapture)
+ m_imageCapture->setCaptureSession(this);
+ emit imageCaptureChanged();
+}
+
+QPlatformMediaRecorder *QWindowsMediaCaptureService::mediaRecorder()
+{
+ return m_encoder;
+}
+
+void QWindowsMediaCaptureService::setMediaRecorder(QPlatformMediaRecorder *recorder)
+{
+ QWindowsMediaEncoder *control = static_cast<QWindowsMediaEncoder *>(recorder);
+ if (m_encoder == control)
+ return;
+
+ if (m_encoder)
+ m_encoder->setCaptureSession(nullptr);
+
+ m_encoder = control;
+ if (m_encoder)
+ m_encoder->setCaptureSession(this);
+ emit encoderChanged();
+}
+
+void QWindowsMediaCaptureService::setAudioInput(QPlatformAudioInput *input)
+{
+ m_mediaDeviceSession->setAudioInput(input ? input->q : nullptr);
+}
+
+void QWindowsMediaCaptureService::setAudioOutput(QPlatformAudioOutput *output)
+{
+ m_mediaDeviceSession->setAudioOutput(output ? output->q : nullptr);
+}
+
+void QWindowsMediaCaptureService::setVideoPreview(QVideoSink *sink)
+{
+ m_mediaDeviceSession->setVideoSink(sink);
+}
+
+QWindowsMediaDeviceSession *QWindowsMediaCaptureService::session() const
+{
+ return m_mediaDeviceSession;
+}
+
+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
new file mode 100644
index 000000000..579310afd
--- /dev/null
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediacapture_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 QWINDOWSMEDIACAPTURE_H
+#define QWINDOWSMEDIACAPTURE_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/qplatformmediacapture_p.h>
+#include <private/qplatformmediaintegration_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsMediaEncoder;
+class QWindowsCamera;
+class QWindowsMediaDeviceSession;
+class QWindowsImageCapture;
+class QPlatformAudioInput;
+
+class QWindowsMediaCaptureService : public QPlatformMediaCaptureSession
+{
+ Q_OBJECT
+
+public:
+ QWindowsMediaCaptureService();
+ virtual ~QWindowsMediaCaptureService();
+
+ 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 *) override;
+
+ void setAudioOutput(QPlatformAudioOutput *output) override;
+
+ void setVideoPreview(QVideoSink *sink) override;
+
+ QWindowsMediaDeviceSession *session() const;
+
+private:
+ QWindowsCamera *m_camera = nullptr;
+ QWindowsMediaDeviceSession *m_mediaDeviceSession = nullptr;
+ QWindowsImageCapture *m_imageCapture = nullptr;
+ QWindowsMediaEncoder *m_encoder = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSMEDIAINTERFACE_H
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader.cpp b/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader.cpp
new file mode 100644
index 000000000..e99b95ad2
--- /dev/null
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader.cpp
@@ -0,0 +1,1019 @@
+// 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"
+
+#include "private/qwindowsmultimediautils_p.h"
+#include <qvideosink.h>
+#include <qmediadevices.h>
+#include <qaudiodevice.h>
+#include <private/qmemoryvideobuffer_p.h>
+#include <private/qvideoframe_p.h>
+#include <private/qwindowsmfdefs_p.h>
+#include <private/qcomptr_p.h>
+#include <QtCore/qdebug.h>
+
+#include <mmdeviceapi.h>
+
+QT_BEGIN_NAMESPACE
+
+enum { MEDIA_TYPE_INDEX_DEFAULT = 0xffffffff };
+
+QWindowsMediaDeviceReader::QWindowsMediaDeviceReader(QObject *parent)
+ : QObject(parent)
+{
+ m_durationTimer.setInterval(100);
+ connect(&m_durationTimer, &QTimer::timeout, this, &QWindowsMediaDeviceReader::updateDuration);
+}
+
+QWindowsMediaDeviceReader::~QWindowsMediaDeviceReader()
+{
+ stopRecording();
+ deactivate();
+}
+
+// Creates a video or audio media source specified by deviceId (symbolic link)
+HRESULT QWindowsMediaDeviceReader::createSource(const QString &deviceId, bool video, IMFMediaSource **source)
+{
+ if (!source)
+ return E_INVALIDARG;
+
+ *source = nullptr;
+ IMFAttributes *sourceAttributes = nullptr;
+
+ HRESULT hr = MFCreateAttributes(&sourceAttributes, 2);
+ if (SUCCEEDED(hr)) {
+
+ hr = sourceAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
+ video ? QMM_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID
+ : QMM_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID);
+ if (SUCCEEDED(hr)) {
+
+ hr = sourceAttributes->SetString(video ? MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK
+ : MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID,
+ reinterpret_cast<LPCWSTR>(deviceId.utf16()));
+ if (SUCCEEDED(hr)) {
+
+ hr = MFCreateDeviceSource(sourceAttributes, source);
+ }
+ }
+ sourceAttributes->Release();
+ }
+
+ return hr;
+}
+
+// Creates a source/reader aggregating two other sources (video/audio).
+// If one of the sources is null the result will be video-only or audio-only.
+HRESULT QWindowsMediaDeviceReader::createAggregateReader(IMFMediaSource *firstSource,
+ IMFMediaSource *secondSource,
+ IMFMediaSource **aggregateSource,
+ IMFSourceReader **sourceReader)
+{
+ if ((!firstSource && !secondSource) || !aggregateSource || !sourceReader)
+ return E_INVALIDARG;
+
+ *aggregateSource = nullptr;
+ *sourceReader = nullptr;
+
+ IMFCollection *sourceCollection = nullptr;
+
+ HRESULT hr = MFCreateCollection(&sourceCollection);
+ if (SUCCEEDED(hr)) {
+
+ if (firstSource)
+ sourceCollection->AddElement(firstSource);
+
+ if (secondSource)
+ sourceCollection->AddElement(secondSource);
+
+ hr = MFCreateAggregateSource(sourceCollection, aggregateSource);
+ if (SUCCEEDED(hr)) {
+
+ IMFAttributes *readerAttributes = nullptr;
+
+ hr = MFCreateAttributes(&readerAttributes, 1);
+ if (SUCCEEDED(hr)) {
+
+ // Set callback so OnReadSample() is called for each new video frame or audio sample.
+ hr = readerAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK,
+ static_cast<IMFSourceReaderCallback*>(this));
+ if (SUCCEEDED(hr)) {
+
+ hr = MFCreateSourceReaderFromMediaSource(*aggregateSource, readerAttributes, sourceReader);
+ }
+ readerAttributes->Release();
+ }
+ }
+ sourceCollection->Release();
+ }
+ return hr;
+}
+
+// Selects the requested resolution/frame rate (if specified),
+// or chooses a high quality configuration otherwise.
+DWORD QWindowsMediaDeviceReader::findMediaTypeIndex(const QCameraFormat &reqFormat)
+{
+ DWORD mediaIndex = MEDIA_TYPE_INDEX_DEFAULT;
+
+ if (m_sourceReader && m_videoSource) {
+
+ DWORD index = 0;
+ IMFMediaType *mediaType = nullptr;
+
+ UINT32 currArea = 0;
+ float currFrameRate = 0.0f;
+
+ while (SUCCEEDED(m_sourceReader->GetNativeMediaType(DWORD(MF_SOURCE_READER_FIRST_VIDEO_STREAM),
+ index, &mediaType))) {
+
+ GUID subtype = GUID_NULL;
+ if (SUCCEEDED(mediaType->GetGUID(MF_MT_SUBTYPE, &subtype))) {
+
+ auto pixelFormat = QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(subtype);
+ if (pixelFormat != QVideoFrameFormat::Format_Invalid) {
+
+ UINT32 width, height;
+ if (SUCCEEDED(MFGetAttributeSize(mediaType, MF_MT_FRAME_SIZE, &width, &height))) {
+
+ UINT32 num, den;
+ if (SUCCEEDED(MFGetAttributeRatio(mediaType, MF_MT_FRAME_RATE, &num, &den))) {
+
+ UINT32 area = width * height;
+ float frameRate = float(num) / den;
+
+ if (!reqFormat.isNull()
+ && UINT32(reqFormat.resolution().width()) == width
+ && UINT32(reqFormat.resolution().height()) == height
+ && qFuzzyCompare(reqFormat.maxFrameRate(), frameRate)
+ && reqFormat.pixelFormat() == pixelFormat) {
+ mediaType->Release();
+ return index;
+ }
+
+ if ((currFrameRate < 29.9 && currFrameRate < frameRate) ||
+ (currFrameRate == frameRate && currArea < area)) {
+ currArea = area;
+ currFrameRate = frameRate;
+ mediaIndex = index;
+ }
+ }
+ }
+ }
+ }
+ mediaType->Release();
+ ++index;
+ }
+ }
+
+ return mediaIndex;
+}
+
+
+// Prepares the source video stream and gets some metadata.
+HRESULT QWindowsMediaDeviceReader::prepareVideoStream(DWORD mediaTypeIndex)
+{
+ if (!m_sourceReader)
+ return E_FAIL;
+
+ if (!m_videoSource)
+ return S_OK; // It may be audio-only
+
+ HRESULT hr;
+
+ if (mediaTypeIndex == MEDIA_TYPE_INDEX_DEFAULT) {
+ hr = m_sourceReader->GetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_VIDEO_STREAM),
+ &m_videoMediaType);
+ } else {
+ hr = m_sourceReader->GetNativeMediaType(DWORD(MF_SOURCE_READER_FIRST_VIDEO_STREAM),
+ mediaTypeIndex, &m_videoMediaType);
+ if (SUCCEEDED(hr))
+ hr = m_sourceReader->SetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_VIDEO_STREAM),
+ nullptr, m_videoMediaType);
+ }
+
+ if (SUCCEEDED(hr)) {
+
+ GUID subtype = GUID_NULL;
+ hr = m_videoMediaType->GetGUID(MF_MT_SUBTYPE, &subtype);
+ if (SUCCEEDED(hr)) {
+
+ m_pixelFormat = QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(subtype);
+
+ if (m_pixelFormat == QVideoFrameFormat::Format_Invalid) {
+ hr = E_FAIL;
+ } else {
+
+ // get the frame dimensions
+ hr = MFGetAttributeSize(m_videoMediaType, MF_MT_FRAME_SIZE, &m_frameWidth, &m_frameHeight);
+ if (SUCCEEDED(hr)) {
+
+ // 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)) {
+
+ m_frameRate = qreal(frameRateNum) / frameRateDen;
+
+ hr = m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_VIDEO_STREAM), TRUE);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return hr;
+}
+
+HRESULT QWindowsMediaDeviceReader::initAudioType(IMFMediaType *mediaType, UINT32 channels, UINT32 samplesPerSec, bool flt)
+{
+ if (!mediaType)
+ return E_INVALIDARG;
+
+ HRESULT hr = mediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
+ if (SUCCEEDED(hr)) {
+ hr = mediaType->SetGUID(MF_MT_SUBTYPE, flt ? MFAudioFormat_Float : MFAudioFormat_PCM);
+ if (SUCCEEDED(hr)) {
+ hr = mediaType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, channels);
+ if (SUCCEEDED(hr)) {
+ hr = mediaType->SetUINT32(MF_MT_AUDIO_CHANNEL_MASK,
+ (channels == 1) ? SPEAKER_FRONT_CENTER : (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT ));
+ if (SUCCEEDED(hr)) {
+ hr = mediaType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, samplesPerSec);
+ if (SUCCEEDED(hr)) {
+ UINT32 bitsPerSample = flt ? 32 : 16;
+ UINT32 bytesPerFrame = channels * bitsPerSample/8;
+ hr = mediaType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample);
+ if (SUCCEEDED(hr)) {
+ hr = mediaType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, bytesPerFrame);
+ if (SUCCEEDED(hr)) {
+ hr = mediaType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, bytesPerFrame * samplesPerSec);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return hr;
+}
+
+// Prepares the source audio stream.
+HRESULT QWindowsMediaDeviceReader::prepareAudioStream()
+{
+ if (!m_sourceReader)
+ return E_FAIL;
+
+ if (!m_audioSource)
+ return S_OK; // It may be video-only
+
+ HRESULT hr = m_sourceReader->GetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM),
+ &m_audioMediaType);
+ if (SUCCEEDED(hr)) {
+ hr = initAudioType(m_audioMediaType, 2, 48000, true);
+ if (SUCCEEDED(hr)) {
+ hr = m_sourceReader->SetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM),
+ nullptr, m_audioMediaType);
+ if (SUCCEEDED(hr)) {
+ hr = m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), TRUE);
+ }
+ }
+ }
+ return hr;
+}
+
+// Retrieves the indexes for selected video/audio streams.
+HRESULT QWindowsMediaDeviceReader::initSourceIndexes()
+{
+ if (!m_sourceReader)
+ return E_FAIL;
+
+ m_sourceVideoStreamIndex = MF_SOURCE_READER_INVALID_STREAM_INDEX;
+ m_sourceAudioStreamIndex = MF_SOURCE_READER_INVALID_STREAM_INDEX;
+
+ DWORD index = 0;
+ BOOL selected = FALSE;
+
+ while (m_sourceReader->GetStreamSelection(index, &selected) == S_OK) {
+ if (selected) {
+ IMFMediaType *mediaType = nullptr;
+ if (SUCCEEDED(m_sourceReader->GetCurrentMediaType(index, &mediaType))) {
+ GUID majorType = GUID_NULL;
+ if (SUCCEEDED(mediaType->GetGUID(MF_MT_MAJOR_TYPE, &majorType))) {
+ if (majorType == MFMediaType_Video)
+ m_sourceVideoStreamIndex = index;
+ else if (majorType == MFMediaType_Audio)
+ m_sourceAudioStreamIndex = index;
+ }
+ mediaType->Release();
+ }
+ }
+ ++index;
+ }
+ if ((m_videoSource && m_sourceVideoStreamIndex == MF_SOURCE_READER_INVALID_STREAM_INDEX) ||
+ (m_audioSource && m_sourceAudioStreamIndex == MF_SOURCE_READER_INVALID_STREAM_INDEX))
+ return E_FAIL;
+ return S_OK;
+}
+
+bool QWindowsMediaDeviceReader::setAudioOutput(const QString &audioOutputId)
+{
+ QMutexLocker locker(&m_mutex);
+
+ stopMonitoring();
+
+ m_audioOutputId = audioOutputId;
+
+ if (!m_active || m_audioOutputId.isEmpty())
+ return true;
+
+ HRESULT hr = startMonitoring();
+
+ return SUCCEEDED(hr);
+}
+
+HRESULT QWindowsMediaDeviceReader::startMonitoring()
+{
+ if (m_audioOutputId.isEmpty())
+ return E_FAIL;
+
+ IMFAttributes *sinkAttributes = nullptr;
+
+ HRESULT hr = MFCreateAttributes(&sinkAttributes, 1);
+ if (SUCCEEDED(hr)) {
+
+ hr = sinkAttributes->SetString(MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ID,
+ reinterpret_cast<LPCWSTR>(m_audioOutputId.utf16()));
+ if (SUCCEEDED(hr)) {
+
+ IMFMediaSink *mediaSink = nullptr;
+ hr = MFCreateAudioRenderer(sinkAttributes, &mediaSink);
+ if (SUCCEEDED(hr)) {
+
+ IMFStreamSink *streamSink = nullptr;
+ hr = mediaSink->GetStreamSinkByIndex(0, &streamSink);
+ if (SUCCEEDED(hr)) {
+
+ IMFMediaTypeHandler *typeHandler = nullptr;
+ hr = streamSink->GetMediaTypeHandler(&typeHandler);
+ if (SUCCEEDED(hr)) {
+
+ hr = typeHandler->IsMediaTypeSupported(m_audioMediaType, nullptr);
+ if (SUCCEEDED(hr)) {
+
+ hr = typeHandler->SetCurrentMediaType(m_audioMediaType);
+ if (SUCCEEDED(hr)) {
+
+ IMFAttributes *writerAttributes = nullptr;
+
+ HRESULT hr = MFCreateAttributes(&writerAttributes, 1);
+ if (SUCCEEDED(hr)) {
+
+ hr = writerAttributes->SetUINT32(MF_SINK_WRITER_DISABLE_THROTTLING, TRUE);
+ if (SUCCEEDED(hr)) {
+
+ IMFSinkWriter *sinkWriter = nullptr;
+ hr = MFCreateSinkWriterFromMediaSink(mediaSink, writerAttributes, &sinkWriter);
+ if (SUCCEEDED(hr)) {
+
+ hr = sinkWriter->SetInputMediaType(0, m_audioMediaType, nullptr);
+ if (SUCCEEDED(hr)) {
+
+ IMFSimpleAudioVolume *audioVolume = nullptr;
+
+ if (SUCCEEDED(MFGetService(mediaSink, QMM_MR_POLICY_VOLUME_SERVICE, IID_PPV_ARGS(&audioVolume)))) {
+ audioVolume->SetMasterVolume(float(m_outputVolume));
+ audioVolume->SetMute(m_outputMuted);
+ audioVolume->Release();
+ }
+
+ hr = sinkWriter->BeginWriting();
+ if (SUCCEEDED(hr)) {
+ m_monitorSink = mediaSink;
+ m_monitorSink->AddRef();
+ m_monitorWriter = sinkWriter;
+ m_monitorWriter->AddRef();
+ }
+ }
+ sinkWriter->Release();
+ }
+ }
+ writerAttributes->Release();
+ }
+ }
+ }
+ typeHandler->Release();
+ }
+ streamSink->Release();
+ }
+ mediaSink->Release();
+ }
+ }
+ sinkAttributes->Release();
+ }
+
+ return hr;
+}
+
+void QWindowsMediaDeviceReader::stopMonitoring()
+{
+ if (m_monitorWriter) {
+ m_monitorWriter->Release();
+ m_monitorWriter = nullptr;
+ }
+ if (m_monitorSink) {
+ m_monitorSink->Shutdown();
+ m_monitorSink->Release();
+ m_monitorSink = nullptr;
+ }
+}
+
+// Activates the requested camera/microphone for streaming.
+// One of the IDs may be empty for video-only/audio-only.
+bool QWindowsMediaDeviceReader::activate(const QString &cameraId,
+ const QCameraFormat &cameraFormat,
+ const QString &microphoneId)
+{
+ QMutexLocker locker(&m_mutex);
+
+ if (cameraId.isEmpty() && microphoneId.isEmpty())
+ return false;
+
+ stopMonitoring();
+ releaseResources();
+
+ m_active = false;
+ m_streaming = false;
+
+ if (!cameraId.isEmpty()) {
+ if (!SUCCEEDED(createSource(cameraId, true, &m_videoSource))) {
+ releaseResources();
+ return false;
+ }
+ }
+
+ if (!microphoneId.isEmpty()) {
+ if (!SUCCEEDED(createSource(microphoneId, false, &m_audioSource))) {
+ releaseResources();
+ return false;
+ }
+ }
+
+ if (!SUCCEEDED(createAggregateReader(m_videoSource, m_audioSource, &m_aggregateSource, &m_sourceReader))) {
+ releaseResources();
+ return false;
+ }
+
+ DWORD mediaTypeIndex = findMediaTypeIndex(cameraFormat);
+
+ if (!SUCCEEDED(prepareVideoStream(mediaTypeIndex))) {
+ releaseResources();
+ return false;
+ }
+
+ if (!SUCCEEDED(prepareAudioStream())) {
+ releaseResources();
+ return false;
+ }
+
+ if (!SUCCEEDED(initSourceIndexes())) {
+ releaseResources();
+ return false;
+ }
+
+ updateSinkInputMediaTypes();
+ startMonitoring();
+
+ // Request the first frame or audio sample.
+ if (!SUCCEEDED(m_sourceReader->ReadSample(MF_SOURCE_READER_ANY_STREAM, 0, nullptr, nullptr, nullptr, nullptr))) {
+ releaseResources();
+ return false;
+ }
+
+ m_active = true;
+ return true;
+}
+
+void QWindowsMediaDeviceReader::deactivate()
+{
+ stopMonitoring();
+ stopStreaming();
+ m_active = false;
+ m_streaming = false;
+}
+
+void QWindowsMediaDeviceReader::stopStreaming()
+{
+ QMutexLocker locker(&m_mutex);
+ releaseResources();
+}
+
+// Releases allocated streaming stuff.
+void QWindowsMediaDeviceReader::releaseResources()
+{
+ if (m_videoMediaType) {
+ m_videoMediaType->Release();
+ m_videoMediaType = nullptr;
+ }
+ if (m_audioMediaType) {
+ m_audioMediaType->Release();
+ m_audioMediaType = nullptr;
+ }
+ if (m_sourceReader) {
+ m_sourceReader->Release();
+ m_sourceReader = nullptr;
+ }
+ if (m_aggregateSource) {
+ m_aggregateSource->Release();
+ m_aggregateSource = nullptr;
+ }
+ if (m_videoSource) {
+ m_videoSource->Release();
+ m_videoSource = nullptr;
+ }
+ if (m_audioSource) {
+ m_audioSource->Release();
+ m_audioSource = nullptr;
+ }
+}
+
+HRESULT QWindowsMediaDeviceReader::createVideoMediaType(const GUID &format, UINT32 bitRate, UINT32 width,
+ UINT32 height, qreal frameRate, IMFMediaType **mediaType)
+{
+ if (!mediaType)
+ return E_INVALIDARG;
+
+ *mediaType = nullptr;
+ IMFMediaType *targetMediaType = nullptr;
+
+ if (SUCCEEDED(MFCreateMediaType(&targetMediaType))) {
+
+ if (SUCCEEDED(targetMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video))) {
+
+ if (SUCCEEDED(targetMediaType->SetGUID(MF_MT_SUBTYPE, format))) {
+
+ if (SUCCEEDED(targetMediaType->SetUINT32(MF_MT_AVG_BITRATE, bitRate))) {
+
+ if (SUCCEEDED(MFSetAttributeSize(targetMediaType, MF_MT_FRAME_SIZE, width, height))) {
+
+ if (SUCCEEDED(MFSetAttributeRatio(targetMediaType, MF_MT_FRAME_RATE,
+ UINT32(frameRate * 1000), 1000))) {
+ UINT32 t1, t2;
+ if (SUCCEEDED(MFGetAttributeRatio(m_videoMediaType, MF_MT_PIXEL_ASPECT_RATIO, &t1, &t2))) {
+
+ if (SUCCEEDED(MFSetAttributeRatio(targetMediaType, MF_MT_PIXEL_ASPECT_RATIO, t1, t2))) {
+
+ if (SUCCEEDED(m_videoMediaType->GetUINT32(MF_MT_INTERLACE_MODE, &t1))) {
+
+ if (SUCCEEDED(targetMediaType->SetUINT32(MF_MT_INTERLACE_MODE, t1))) {
+
+ *mediaType = targetMediaType;
+ return S_OK;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ targetMediaType->Release();
+ }
+ return E_FAIL;
+}
+
+HRESULT QWindowsMediaDeviceReader::createAudioMediaType(const GUID &format, UINT32 bitRate, IMFMediaType **mediaType)
+{
+ if (!mediaType)
+ return E_INVALIDARG;
+
+ *mediaType = nullptr;
+ IMFMediaType *targetMediaType = nullptr;
+
+ if (SUCCEEDED(MFCreateMediaType(&targetMediaType))) {
+
+ if (SUCCEEDED(targetMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio))) {
+
+ if (SUCCEEDED(targetMediaType->SetGUID(MF_MT_SUBTYPE, format))) {
+
+ if (bitRate == 0 || SUCCEEDED(targetMediaType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, bitRate / 8))) {
+
+ *mediaType = targetMediaType;
+ return S_OK;
+ }
+ }
+ }
+ targetMediaType->Release();
+ }
+ return E_FAIL;
+}
+
+HRESULT QWindowsMediaDeviceReader::updateSinkInputMediaTypes()
+{
+ HRESULT hr = S_OK;
+ if (m_sinkWriter) {
+ if (m_videoSource && m_videoMediaType && m_sinkVideoStreamIndex != MF_SINK_WRITER_INVALID_STREAM_INDEX) {
+ hr = m_sinkWriter->SetInputMediaType(m_sinkVideoStreamIndex, m_videoMediaType, nullptr);
+ }
+ if (SUCCEEDED(hr)) {
+ if (m_audioSource && m_audioMediaType && m_sinkAudioStreamIndex != MF_SINK_WRITER_INVALID_STREAM_INDEX) {
+ hr = m_sinkWriter->SetInputMediaType(m_sinkAudioStreamIndex, m_audioMediaType, nullptr);
+ }
+ }
+ }
+ return hr;
+}
+
+QMediaRecorder::Error QWindowsMediaDeviceReader::startRecording(
+ const QString &fileName, const GUID &container, const GUID &videoFormat, UINT32 videoBitRate,
+ UINT32 width, UINT32 height, qreal frameRate, const GUID &audioFormat, UINT32 audioBitRate)
+{
+ QMutexLocker locker(&m_mutex);
+
+ if (!m_active || m_recording || (videoFormat == GUID_NULL && audioFormat == GUID_NULL))
+ return QMediaRecorder::ResourceError;
+
+ ComPtr<IMFAttributes> writerAttributes;
+
+ HRESULT hr = MFCreateAttributes(writerAttributes.GetAddressOf(), 2);
+ if (FAILED(hr))
+ return QMediaRecorder::ResourceError;
+
+ // Set callback so OnFinalize() is called after video is saved.
+ hr = writerAttributes->SetUnknown(MF_SINK_WRITER_ASYNC_CALLBACK,
+ static_cast<IMFSinkWriterCallback*>(this));
+ if (FAILED(hr))
+ return QMediaRecorder::ResourceError;
+
+ hr = writerAttributes->SetGUID(QMM_MF_TRANSCODE_CONTAINERTYPE, container);
+ if (FAILED(hr))
+ return QMediaRecorder::ResourceError;
+
+ ComPtr<IMFSinkWriter> sinkWriter;
+ hr = MFCreateSinkWriterFromURL(reinterpret_cast<LPCWSTR>(fileName.utf16()),
+ nullptr, writerAttributes.Get(), sinkWriter.GetAddressOf());
+ if (FAILED(hr))
+ return QMediaRecorder::LocationNotWritable;
+
+ m_sinkVideoStreamIndex = MF_SINK_WRITER_INVALID_STREAM_INDEX;
+ m_sinkAudioStreamIndex = MF_SINK_WRITER_INVALID_STREAM_INDEX;
+
+ if (m_videoSource && videoFormat != GUID_NULL) {
+ IMFMediaType *targetMediaType = nullptr;
+
+ hr = createVideoMediaType(videoFormat, videoBitRate, width, height, frameRate, &targetMediaType);
+ if (SUCCEEDED(hr)) {
+
+ hr = sinkWriter->AddStream(targetMediaType, &m_sinkVideoStreamIndex);
+ if (SUCCEEDED(hr)) {
+
+ hr = sinkWriter->SetInputMediaType(m_sinkVideoStreamIndex, m_videoMediaType, nullptr);
+ }
+ targetMediaType->Release();
+ }
+ }
+
+ if (SUCCEEDED(hr)) {
+ if (m_audioSource && audioFormat != GUID_NULL) {
+ IMFMediaType *targetMediaType = nullptr;
+
+ hr = createAudioMediaType(audioFormat, audioBitRate, &targetMediaType);
+ if (SUCCEEDED(hr)) {
+
+ hr = sinkWriter->AddStream(targetMediaType, &m_sinkAudioStreamIndex);
+ if (SUCCEEDED(hr)) {
+
+ hr = sinkWriter->SetInputMediaType(m_sinkAudioStreamIndex, m_audioMediaType, nullptr);
+ }
+ targetMediaType->Release();
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ return QMediaRecorder::FormatError;
+
+ hr = sinkWriter->BeginWriting();
+ if (FAILED(hr))
+ return QMediaRecorder::ResourceError;
+
+ m_sinkWriter = sinkWriter.Detach();
+ m_lastDuration = -1;
+ m_currentDuration = 0;
+ updateDuration();
+ m_durationTimer.start();
+ m_recording = true;
+ m_firstFrame = true;
+ m_paused = false;
+ m_pauseChanging = false;
+
+ return QMediaRecorder::NoError;
+}
+
+void QWindowsMediaDeviceReader::stopRecording()
+{
+ QMutexLocker locker(&m_mutex);
+
+ if (m_sinkWriter && m_recording) {
+
+ HRESULT hr = m_sinkWriter->Finalize();
+
+ if (SUCCEEDED(hr)) {
+ m_hasFinalized.wait(&m_mutex);
+ } else {
+ m_sinkWriter->Release();
+ m_sinkWriter = nullptr;
+
+ QMetaObject::invokeMethod(this, "recordingError",
+ Qt::QueuedConnection, Q_ARG(int, hr));
+ }
+ }
+
+ m_recording = false;
+ m_paused = false;
+ m_pauseChanging = false;
+
+ m_durationTimer.stop();
+ m_lastDuration = -1;
+ m_currentDuration = -1;
+}
+
+bool QWindowsMediaDeviceReader::pauseRecording()
+{
+ if (!m_recording || m_paused)
+ return false;
+ m_pauseTime = m_lastTimestamp;
+ m_paused = true;
+ m_pauseChanging = true;
+ return true;
+}
+
+bool QWindowsMediaDeviceReader::resumeRecording()
+{
+ if (!m_recording || !m_paused)
+ return false;
+ m_paused = false;
+ m_pauseChanging = true;
+ return true;
+}
+
+//from IUnknown
+STDMETHODIMP QWindowsMediaDeviceReader::QueryInterface(REFIID riid, LPVOID *ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+ if (riid == IID_IMFSourceReaderCallback) {
+ *ppvObject = static_cast<IMFSourceReaderCallback*>(this);
+ } else if (riid == IID_IMFSinkWriterCallback) {
+ *ppvObject = static_cast<IMFSinkWriterCallback*>(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) QWindowsMediaDeviceReader::AddRef(void)
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+STDMETHODIMP_(ULONG) QWindowsMediaDeviceReader::Release(void)
+{
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0) {
+ this->deleteLater();
+ }
+ return cRef;
+}
+
+UINT32 QWindowsMediaDeviceReader::frameWidth() const
+{
+ return m_frameWidth;
+}
+
+UINT32 QWindowsMediaDeviceReader::frameHeight() const
+{
+ return m_frameHeight;
+}
+
+qreal QWindowsMediaDeviceReader::frameRate() const
+{
+ return m_frameRate;
+}
+
+void QWindowsMediaDeviceReader::setInputMuted(bool muted)
+{
+ m_inputMuted = muted;
+}
+
+void QWindowsMediaDeviceReader::setInputVolume(qreal volume)
+{
+ m_inputVolume = qBound(0.0, volume, 1.0);
+}
+
+void QWindowsMediaDeviceReader::setOutputMuted(bool muted)
+{
+ QMutexLocker locker(&m_mutex);
+
+ m_outputMuted = muted;
+
+ if (m_active && m_monitorSink) {
+ IMFSimpleAudioVolume *audioVolume = nullptr;
+ if (SUCCEEDED(MFGetService(m_monitorSink, QMM_MR_POLICY_VOLUME_SERVICE,
+ IID_PPV_ARGS(&audioVolume)))) {
+ audioVolume->SetMute(m_outputMuted);
+ audioVolume->Release();
+ }
+ }
+}
+
+void QWindowsMediaDeviceReader::setOutputVolume(qreal volume)
+{
+ QMutexLocker locker(&m_mutex);
+
+ m_outputVolume = qBound(0.0, volume, 1.0);
+
+ if (m_active && m_monitorSink) {
+ IMFSimpleAudioVolume *audioVolume = nullptr;
+ if (SUCCEEDED(MFGetService(m_monitorSink, QMM_MR_POLICY_VOLUME_SERVICE,
+ IID_PPV_ARGS(&audioVolume)))) {
+ audioVolume->SetMasterVolume(float(m_outputVolume));
+ audioVolume->Release();
+ }
+ }
+}
+
+void QWindowsMediaDeviceReader::updateDuration()
+{
+ if (m_currentDuration >= 0 && m_lastDuration != m_currentDuration) {
+ m_lastDuration = m_currentDuration;
+ emit durationChanged(m_currentDuration);
+ }
+}
+
+//from IMFSourceReaderCallback
+STDMETHODIMP QWindowsMediaDeviceReader::OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex,
+ DWORD dwStreamFlags, LONGLONG llTimestamp,
+ IMFSample *pSample)
+{
+ QMutexLocker locker(&m_mutex);
+
+ if (FAILED(hrStatus)) {
+ emit streamingError(int(hrStatus));
+ return hrStatus;
+ }
+
+ m_lastTimestamp = llTimestamp;
+
+ if ((dwStreamFlags & MF_SOURCE_READERF_ENDOFSTREAM) == MF_SOURCE_READERF_ENDOFSTREAM) {
+ m_streaming = false;
+ emit streamingStopped();
+ } else {
+
+ if (!m_streaming) {
+ m_streaming = true;
+ emit streamingStarted();
+ }
+ if (pSample) {
+
+ if (m_monitorWriter && dwStreamIndex == m_sourceAudioStreamIndex)
+ m_monitorWriter->WriteSample(0, pSample);
+
+ if (m_recording) {
+
+ if (m_firstFrame) {
+ m_timeOffset = llTimestamp;
+ m_firstFrame = false;
+ emit recordingStarted();
+ }
+
+ if (m_pauseChanging) {
+ // Recording time should not pass while paused.
+ if (m_paused)
+ m_pauseTime = llTimestamp;
+ else
+ m_timeOffset += llTimestamp - m_pauseTime;
+ m_pauseChanging = false;
+ }
+
+ // Send the video frame or audio sample to be encoded.
+ if (m_sinkWriter && !m_paused) {
+
+ pSample->SetSampleTime(llTimestamp - m_timeOffset);
+
+ if (dwStreamIndex == m_sourceVideoStreamIndex) {
+
+ m_sinkWriter->WriteSample(m_sinkVideoStreamIndex, pSample);
+
+ } else if (dwStreamIndex == m_sourceAudioStreamIndex) {
+
+ float volume = m_inputMuted ? 0.0f : float(m_inputVolume);
+
+ // Change the volume of the audio sample, if needed.
+ if (volume != 1.0f) {
+ IMFMediaBuffer *mediaBuffer = nullptr;
+ if (SUCCEEDED(pSample->ConvertToContiguousBuffer(&mediaBuffer))) {
+
+ DWORD bufLen = 0;
+ BYTE *buffer = nullptr;
+
+ if (SUCCEEDED(mediaBuffer->Lock(&buffer, nullptr, &bufLen))) {
+
+ float *floatBuffer = reinterpret_cast<float*>(buffer);
+
+ for (DWORD i = 0; i < bufLen/4; ++i)
+ floatBuffer[i] *= volume;
+
+ mediaBuffer->Unlock();
+ }
+ mediaBuffer->Release();
+ }
+ }
+
+ m_sinkWriter->WriteSample(m_sinkAudioStreamIndex, pSample);
+ }
+ m_currentDuration = (llTimestamp - m_timeOffset) / 10000;
+ }
+ }
+
+ // Generate a new QVideoFrame from IMFSample.
+ if (dwStreamIndex == m_sourceVideoStreamIndex) {
+ IMFMediaBuffer *mediaBuffer = nullptr;
+ if (SUCCEEDED(pSample->ConvertToContiguousBuffer(&mediaBuffer))) {
+
+ DWORD bufLen = 0;
+ BYTE *buffer = nullptr;
+
+ if (SUCCEEDED(mediaBuffer->Lock(&buffer, nullptr, &bufLen))) {
+ auto bytes = QByteArray(reinterpret_cast<char*>(buffer), bufLen);
+ QVideoFrameFormat format(QSize(m_frameWidth, m_frameHeight), m_pixelFormat);
+
+ QVideoFrame frame = QVideoFramePrivate::createFrame(
+ std::make_unique<QMemoryVideoBuffer>(std::move(bytes), m_stride),
+ std::move(format));
+
+ // WMF uses 100-nanosecond units, Qt uses microseconds
+ frame.setStartTime(llTimestamp * 0.1);
+
+ LONGLONG duration = -1;
+ if (SUCCEEDED(pSample->GetSampleDuration(&duration)))
+ frame.setEndTime((llTimestamp + duration) * 0.1);
+
+ emit videoFrameChanged(frame);
+
+ mediaBuffer->Unlock();
+ }
+ mediaBuffer->Release();
+ }
+ }
+ }
+ // request the next video frame or sound sample
+ if (m_sourceReader)
+ m_sourceReader->ReadSample(MF_SOURCE_READER_ANY_STREAM,
+ 0, nullptr, nullptr, nullptr, nullptr);
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP QWindowsMediaDeviceReader::OnFlush(DWORD)
+{
+ return S_OK;
+}
+
+STDMETHODIMP QWindowsMediaDeviceReader::OnEvent(DWORD, IMFMediaEvent*)
+{
+ return S_OK;
+}
+
+//from IMFSinkWriterCallback
+STDMETHODIMP QWindowsMediaDeviceReader::OnFinalize(HRESULT)
+{
+ QMutexLocker locker(&m_mutex);
+ if (m_sinkWriter) {
+ m_sinkWriter->Release();
+ m_sinkWriter = nullptr;
+ }
+ emit recordingStopped();
+ m_hasFinalized.notify_one();
+ return S_OK;
+}
+
+STDMETHODIMP QWindowsMediaDeviceReader::OnMarker(DWORD, LPVOID)
+{
+ return S_OK;
+}
+
+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
new file mode 100644
index 000000000..4699a463a
--- /dev/null
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader_p.h
@@ -0,0 +1,154 @@
+// 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
+
+//
+// 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 <mferror.h>
+#include <mfreadwrite.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qwaitcondition.h>
+#include <QtCore/qtimer.h>
+#include <qvideoframe.h>
+#include <qcameradevice.h>
+#include <qmediarecorder.h>
+
+QT_BEGIN_NAMESPACE
+
+class QVideoSink;
+
+class QWindowsMediaDeviceReader : public QObject,
+ public IMFSourceReaderCallback,
+ public IMFSinkWriterCallback
+{
+ Q_OBJECT
+public:
+ explicit QWindowsMediaDeviceReader(QObject *parent = nullptr);
+ ~QWindowsMediaDeviceReader();
+
+ //from IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject) override;
+ STDMETHODIMP_(ULONG) AddRef(void) override;
+ STDMETHODIMP_(ULONG) Release(void) override;
+
+ //from IMFSourceReaderCallback
+ STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex,
+ DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample) override;
+ STDMETHODIMP OnFlush(DWORD dwStreamIndex) override;
+ STDMETHODIMP OnEvent(DWORD dwStreamIndex, IMFMediaEvent *pEvent) override;
+
+ //from IMFSinkWriterCallback
+ STDMETHODIMP OnFinalize(HRESULT hrStatus) override;
+ STDMETHODIMP OnMarker(DWORD dwStreamIndex, LPVOID pvContext) override;
+
+ bool activate(const QString &cameraId,
+ const QCameraFormat &cameraFormat,
+ const QString &microphoneId);
+ void deactivate();
+
+ QMediaRecorder::Error startRecording(const QString &fileName, const GUID &container,
+ const GUID &videoFormat, UINT32 videoBitRate, UINT32 width,
+ UINT32 height, qreal frameRate, const GUID &audioFormat,
+ UINT32 audioBitRate);
+ void stopRecording();
+ bool pauseRecording();
+ bool resumeRecording();
+
+ UINT32 frameWidth() const;
+ UINT32 frameHeight() const;
+ qreal frameRate() const;
+ void setInputMuted(bool muted);
+ void setInputVolume(qreal volume);
+ void setOutputMuted(bool muted);
+ void setOutputVolume(qreal volume);
+ bool setAudioOutput(const QString &audioOutputId);
+
+Q_SIGNALS:
+ void streamingStarted();
+ void streamingStopped();
+ void streamingError(int errorCode);
+ void recordingStarted();
+ void recordingStopped();
+ void recordingError(int errorCode);
+ void durationChanged(qint64 duration);
+ void videoFrameChanged(const QVideoFrame &frame);
+
+private slots:
+ void updateDuration();
+
+private:
+ HRESULT createSource(const QString &deviceId, bool video, IMFMediaSource **source);
+ HRESULT createAggregateReader(IMFMediaSource *firstSource, IMFMediaSource *secondSource,
+ IMFMediaSource **aggregateSource, IMFSourceReader **sourceReader);
+ HRESULT createVideoMediaType(const GUID &format, UINT32 bitRate, UINT32 width, UINT32 height,
+ qreal frameRate, IMFMediaType **mediaType);
+ HRESULT createAudioMediaType(const GUID &format, UINT32 bitRate, IMFMediaType **mediaType);
+ HRESULT initAudioType(IMFMediaType *mediaType, UINT32 channels, UINT32 samplesPerSec, bool flt);
+ HRESULT prepareVideoStream(DWORD mediaTypeIndex);
+ HRESULT prepareAudioStream();
+ HRESULT initSourceIndexes();
+ HRESULT updateSinkInputMediaTypes();
+ HRESULT startMonitoring();
+ void stopMonitoring();
+ void releaseResources();
+ void stopStreaming();
+ DWORD findMediaTypeIndex(const QCameraFormat &reqFormat);
+
+ long m_cRef = 1;
+ QMutex m_mutex;
+ QWaitCondition m_hasFinalized;
+ IMFMediaSource *m_videoSource = nullptr;
+ IMFMediaType *m_videoMediaType = nullptr;
+ IMFMediaSource *m_audioSource = nullptr;
+ IMFMediaType *m_audioMediaType = nullptr;
+ IMFMediaSource *m_aggregateSource = nullptr;
+ IMFSourceReader *m_sourceReader = nullptr;
+ IMFSinkWriter *m_sinkWriter = nullptr;
+ IMFMediaSink *m_monitorSink = nullptr;
+ IMFSinkWriter *m_monitorWriter = nullptr;
+ QString m_audioOutputId;
+ DWORD m_sourceVideoStreamIndex = MF_SOURCE_READER_INVALID_STREAM_INDEX;
+ DWORD m_sourceAudioStreamIndex = MF_SOURCE_READER_INVALID_STREAM_INDEX;
+ DWORD m_sinkVideoStreamIndex = MF_SINK_WRITER_INVALID_STREAM_INDEX;
+ DWORD m_sinkAudioStreamIndex = MF_SINK_WRITER_INVALID_STREAM_INDEX;
+ UINT32 m_frameWidth = 0;
+ UINT32 m_frameHeight = 0;
+ qreal m_frameRate = 0.0;
+ LONG m_stride = 0;
+ bool m_active = false;
+ bool m_streaming = false;
+ bool m_recording = false;
+ bool m_firstFrame = false;
+ bool m_paused = false;
+ bool m_pauseChanging = false;
+ bool m_inputMuted = false;
+ bool m_outputMuted = false;
+ qreal m_inputVolume = 1.0;
+ qreal m_outputVolume = 1.0;
+ QVideoFrameFormat::PixelFormat m_pixelFormat = QVideoFrameFormat::Format_Invalid;
+ LONGLONG m_timeOffset = 0;
+ LONGLONG m_pauseTime = 0;
+ LONGLONG m_lastTimestamp = 0;
+ QTimer m_durationTimer;
+ qint64 m_currentDuration = -1;
+ qint64 m_lastDuration = -1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSMEDIADEVICEREADER_H
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession.cpp b/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession.cpp
new file mode 100644
index 000000000..b13599444
--- /dev/null
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession.cpp
@@ -0,0 +1,376 @@
+// 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"
+
+#include "qwindowsmediadevicereader_p.h"
+#include "private/qwindowsmultimediautils_p.h"
+#include "private/qplatformvideosink_p.h"
+#include <qvideosink.h>
+#include <QtCore/qdebug.h>
+#include <qaudioinput.h>
+#include <qaudiooutput.h>
+
+QT_BEGIN_NAMESPACE
+
+QWindowsMediaDeviceSession::QWindowsMediaDeviceSession(QObject *parent)
+ : QObject(parent)
+{
+ m_mediaDeviceReader = new QWindowsMediaDeviceReader(this);
+ 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()
+{
+ delete m_mediaDeviceReader;
+}
+
+bool QWindowsMediaDeviceSession::isActive() const
+{
+ return m_active;
+}
+
+bool QWindowsMediaDeviceSession::isActivating() const
+{
+ return m_activating;
+}
+
+void QWindowsMediaDeviceSession::setActive(bool active)
+{
+ if ((active && (m_active || m_activating)) || (!active && !m_active && !m_activating))
+ return;
+
+ if (active) {
+ auto camId = QString::fromUtf8(m_activeCameraDevice.id());
+ auto micId = m_audioInput ? QString::fromUtf8(m_audioInput->device().id()) : QString();
+ if (!camId.isEmpty() || !micId.isEmpty()) {
+ if (m_mediaDeviceReader->activate(camId, m_cameraFormat, micId)) {
+ m_activating = true;
+ } else {
+ emit streamingError(MF_E_NOT_AVAILABLE);
+ }
+ } else {
+ qWarning() << Q_FUNC_INFO << "Camera ID and Microphone ID both undefined.";
+ }
+ } else {
+ m_mediaDeviceReader->deactivate();
+ m_active = false;
+ m_activating = false;
+ emit activeChanged(m_active);
+ emit readyForCaptureChanged(m_active);
+ }
+}
+
+void QWindowsMediaDeviceSession::reactivate()
+{
+ if (m_active || m_activating) {
+ pauseRecording();
+ setActive(false);
+ setActive(true);
+ resumeRecording();
+ }
+}
+
+void QWindowsMediaDeviceSession::setActiveCamera(const QCameraDevice &camera)
+{
+ m_activeCameraDevice = camera;
+ reactivate();
+}
+
+QCameraDevice QWindowsMediaDeviceSession::activeCamera() const
+{
+ return m_activeCameraDevice;
+}
+
+void QWindowsMediaDeviceSession::setCameraFormat(const QCameraFormat &cameraFormat)
+{
+ m_cameraFormat = cameraFormat;
+}
+
+void QWindowsMediaDeviceSession::setVideoSink(QVideoSink *surface)
+{
+ m_surface = surface;
+}
+
+void QWindowsMediaDeviceSession::handleStreamingStarted()
+{
+ if (m_activating) {
+ m_active = true;
+ m_activating = false;
+ emit activeChanged(m_active);
+ emit readyForCaptureChanged(m_active);
+ }
+}
+
+void QWindowsMediaDeviceSession::handleStreamingStopped()
+{
+ m_active = false;
+ emit activeChanged(m_active);
+ emit readyForCaptureChanged(m_active);
+}
+
+void QWindowsMediaDeviceSession::handleStreamingError(int errorCode)
+{
+ if (m_surface)
+ m_surface->platformVideoSink()->setVideoFrame(QVideoFrame());
+ emit streamingError(errorCode);
+}
+
+void QWindowsMediaDeviceSession::handleVideoFrameChanged(const QVideoFrame &frame)
+{
+ if (m_surface)
+ m_surface->platformVideoSink()->setVideoFrame(frame);
+ emit videoFrameChanged(frame);
+}
+
+void QWindowsMediaDeviceSession::setAudioInputMuted(bool muted)
+{
+ m_mediaDeviceReader->setInputMuted(muted);
+}
+
+void QWindowsMediaDeviceSession::setAudioInputVolume(float volume)
+{
+ m_mediaDeviceReader->setInputVolume(volume);
+}
+
+void QWindowsMediaDeviceSession::audioInputDeviceChanged()
+{
+ reactivate();
+}
+
+void QWindowsMediaDeviceSession::setAudioOutputMuted(bool muted)
+{
+ m_mediaDeviceReader->setOutputMuted(muted);
+}
+
+void QWindowsMediaDeviceSession::setAudioOutputVolume(float volume)
+{
+ m_mediaDeviceReader->setOutputVolume(volume);
+}
+
+void QWindowsMediaDeviceSession::audioOutputDeviceChanged()
+{
+ if (m_active || m_activating)
+ m_mediaDeviceReader->setAudioOutput(QString::fromUtf8(m_audioOutput->device().id()));
+}
+
+void QWindowsMediaDeviceSession::setAudioInput(QAudioInput *input)
+{
+ if (m_audioInput == input)
+ return;
+ if (m_audioInput)
+ m_audioInput->disconnect(this);
+ m_audioInput = input;
+
+ audioInputDeviceChanged();
+
+ if (!m_audioInput)
+ return;
+ connect(m_audioInput, &QAudioInput::mutedChanged, this, &QWindowsMediaDeviceSession::setAudioInputMuted);
+ connect(m_audioInput, &QAudioInput::volumeChanged, this, &QWindowsMediaDeviceSession::setAudioInputVolume);
+ connect(m_audioInput, &QAudioInput::deviceChanged, this, &QWindowsMediaDeviceSession::audioInputDeviceChanged);
+}
+
+void QWindowsMediaDeviceSession::setAudioOutput(QAudioOutput *output)
+{
+ if (m_audioOutput == output)
+ return;
+ if (m_audioOutput)
+ m_audioOutput->disconnect(this);
+ m_audioOutput = output;
+ if (!m_audioOutput) {
+ m_mediaDeviceReader->setAudioOutput({});
+ return;
+ }
+
+ m_mediaDeviceReader->setAudioOutput(QString::fromUtf8(m_audioOutput->device().id()));
+
+ connect(m_audioOutput, &QAudioOutput::mutedChanged, this, &QWindowsMediaDeviceSession::setAudioOutputMuted);
+ connect(m_audioOutput, &QAudioOutput::volumeChanged, this, &QWindowsMediaDeviceSession::setAudioOutputVolume);
+ connect(m_audioOutput, &QAudioOutput::deviceChanged, this, &QWindowsMediaDeviceSession::audioOutputDeviceChanged);
+}
+
+QMediaRecorder::Error QWindowsMediaDeviceSession::startRecording(QMediaEncoderSettings &settings, const QString &fileName, bool audioOnly)
+{
+ GUID container = audioOnly ? QWindowsMultimediaUtils::containerForAudioFileFormat(settings.mediaFormat().fileFormat())
+ : QWindowsMultimediaUtils::containerForVideoFileFormat(settings.mediaFormat().fileFormat());
+ GUID videoFormat = QWindowsMultimediaUtils::videoFormatForCodec(settings.videoCodec());
+ GUID audioFormat = QWindowsMultimediaUtils::audioFormatForCodec(settings.audioCodec());
+
+ QSize res = settings.videoResolution();
+ UINT32 width, height;
+ if (res.width() > 0 && res.height() > 0) {
+ width = UINT32(res.width());
+ height = UINT32(res.height());
+ } else {
+ width = m_mediaDeviceReader->frameWidth();
+ height = m_mediaDeviceReader->frameHeight();
+ settings.setVideoResolution(QSize(int(width), int(height)));
+ }
+
+ qreal frameRate = settings.videoFrameRate();
+ if (frameRate <= 0) {
+ frameRate = m_mediaDeviceReader->frameRate();
+ settings.setVideoFrameRate(frameRate);
+ }
+
+ auto quality = settings.quality();
+
+ UINT32 videoBitRate = 0;
+ if (settings.videoBitRate() > 0) {
+ videoBitRate = UINT32(settings.videoBitRate());
+ } else {
+ videoBitRate = estimateVideoBitRate(videoFormat, width, height, frameRate, quality);
+ settings.setVideoBitRate(int(videoBitRate));
+ }
+
+ UINT32 audioBitRate = 0;
+ if (settings.audioBitRate() > 0) {
+ audioBitRate = UINT32(settings.audioBitRate());
+ } else {
+ audioBitRate = estimateAudioBitRate(audioFormat, quality);
+ settings.setAudioBitRate(int(audioBitRate));
+ }
+
+ return m_mediaDeviceReader->startRecording(fileName, container, audioOnly ? GUID_NULL : videoFormat,
+ videoBitRate, width, height, frameRate,
+ audioFormat, audioBitRate);
+}
+
+void QWindowsMediaDeviceSession::stopRecording()
+{
+ m_mediaDeviceReader->stopRecording();
+}
+
+bool QWindowsMediaDeviceSession::pauseRecording()
+{
+ return m_mediaDeviceReader->pauseRecording();
+}
+
+bool QWindowsMediaDeviceSession::resumeRecording()
+{
+ return m_mediaDeviceReader->resumeRecording();
+}
+
+// empirical estimate of the required video bitrate (for H.264)
+quint32 QWindowsMediaDeviceSession::estimateVideoBitRate(const GUID &videoFormat, quint32 width, quint32 height,
+ qreal frameRate, QMediaRecorder::Quality quality)
+{
+ Q_UNUSED(videoFormat);
+
+ qreal bitsPerPixel;
+ switch (quality) {
+ case QMediaRecorder::Quality::VeryLowQuality:
+ bitsPerPixel = 0.08;
+ break;
+ case QMediaRecorder::Quality::LowQuality:
+ bitsPerPixel = 0.2;
+ break;
+ case QMediaRecorder::Quality::NormalQuality:
+ bitsPerPixel = 0.3;
+ break;
+ case QMediaRecorder::Quality::HighQuality:
+ bitsPerPixel = 0.5;
+ break;
+ case QMediaRecorder::Quality::VeryHighQuality:
+ bitsPerPixel = 0.8;
+ break;
+ default:
+ bitsPerPixel = 0.3;
+ }
+
+ // Required bitrate is not linear on the number of pixels; small resolutions
+ // require more BPP, thus the minimum values, to try to compensate it.
+ quint32 pixelsPerSec = quint32(qMax(width, 320u) * qMax(height, 240u) * qMax(frameRate, 6.0));
+ return pixelsPerSec * bitsPerPixel;
+}
+
+quint32 QWindowsMediaDeviceSession::estimateAudioBitRate(const GUID &audioFormat, QMediaRecorder::Quality quality)
+{
+ if (audioFormat == MFAudioFormat_AAC) {
+ // Bitrates supported by the AAC encoder are 96K, 128K, 160K, 192K.
+ switch (quality) {
+ case QMediaRecorder::Quality::VeryLowQuality:
+ return 96000;
+ case QMediaRecorder::Quality::LowQuality:
+ return 96000;
+ case QMediaRecorder::Quality::NormalQuality:
+ return 128000;
+ case QMediaRecorder::Quality::HighQuality:
+ return 160000;
+ case QMediaRecorder::Quality::VeryHighQuality:
+ return 192000;
+ default:
+ return 128000;
+ }
+ } else if (audioFormat == MFAudioFormat_MP3) {
+ // Bitrates supported by the MP3 encoder are
+ // 32K, 40K, 48K, 56K, 64K, 80K, 96K, 112K, 128K, 160K, 192K, 224K, 256K, 320K.
+ switch (quality) {
+ case QMediaRecorder::Quality::VeryLowQuality:
+ return 48000;
+ case QMediaRecorder::Quality::LowQuality:
+ return 96000;
+ case QMediaRecorder::Quality::NormalQuality:
+ return 128000;
+ case QMediaRecorder::Quality::HighQuality:
+ return 224000;
+ case QMediaRecorder::Quality::VeryHighQuality:
+ return 320000;
+ default:
+ return 128000;
+ }
+ } else if (audioFormat == MFAudioFormat_WMAudioV8) {
+ // Bitrates supported by the Windows Media Audio 8 encoder
+ switch (quality) {
+ case QMediaRecorder::Quality::VeryLowQuality:
+ return 32000;
+ case QMediaRecorder::Quality::LowQuality:
+ return 96000;
+ case QMediaRecorder::Quality::NormalQuality:
+ return 192000;
+ case QMediaRecorder::Quality::HighQuality:
+ return 256016;
+ case QMediaRecorder::Quality::VeryHighQuality:
+ return 320032;
+ default:
+ return 192000;
+ }
+ } else if (audioFormat == MFAudioFormat_WMAudioV9) {
+ // Bitrates supported by the Windows Media Audio 9 encoder
+ switch (quality) {
+ case QMediaRecorder::Quality::VeryLowQuality:
+ return 32000;
+ case QMediaRecorder::Quality::LowQuality:
+ return 96000;
+ case QMediaRecorder::Quality::NormalQuality:
+ return 192000;
+ case QMediaRecorder::Quality::HighQuality:
+ return 256016;
+ case QMediaRecorder::Quality::VeryHighQuality:
+ return 384000;
+ default:
+ return 192000;
+ }
+ }
+ return 0; // Use default for format
+}
+
+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
new file mode 100644
index 000000000..c3998ce6c
--- /dev/null
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession_p.h
@@ -0,0 +1,100 @@
+// 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
+
+//
+// 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 <qcamera.h>
+#include <qaudiodevice.h>
+#include <private/qwindowsmultimediautils_p.h>
+#include <private/qplatformmediarecorder_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioInput;
+class QAudioOutput;
+class QVideoSink;
+class QWindowsMediaDeviceReader;
+
+class QWindowsMediaDeviceSession : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QWindowsMediaDeviceSession(QObject *parent = nullptr);
+ ~QWindowsMediaDeviceSession();
+
+ bool isActive() const;
+ void setActive(bool active);
+
+ bool isActivating() const;
+
+ void setActiveCamera(const QCameraDevice &camera);
+ QCameraDevice activeCamera() const;
+
+ void setCameraFormat(const QCameraFormat &cameraFormat);
+
+ void setVideoSink(QVideoSink *surface);
+
+public Q_SLOTS:
+ void setAudioInputMuted(bool muted);
+ void setAudioInputVolume(float volume);
+ void audioInputDeviceChanged();
+ void setAudioOutputMuted(bool muted);
+ void setAudioOutputVolume(float volume);
+ void audioOutputDeviceChanged();
+
+public:
+ void setAudioInput(QAudioInput *input);
+ void setAudioOutput(QAudioOutput *output);
+
+ QMediaRecorder::Error startRecording(QMediaEncoderSettings &settings, const QString &fileName, bool audioOnly);
+ void stopRecording();
+ bool pauseRecording();
+ bool resumeRecording();
+
+Q_SIGNALS:
+ void activeChanged(bool);
+ void readyForCaptureChanged(bool);
+ void durationChanged(qint64 duration);
+ void recordingStarted();
+ void recordingStopped();
+ void streamingError(int errorCode);
+ void recordingError(int errorCode);
+ void videoFrameChanged(const QVideoFrame &frame);
+
+private Q_SLOTS:
+ void handleStreamingStarted();
+ void handleStreamingStopped();
+ void handleStreamingError(int errorCode);
+ void handleVideoFrameChanged(const QVideoFrame &frame);
+
+private:
+ void reactivate();
+ quint32 estimateVideoBitRate(const GUID &videoFormat, quint32 width, quint32 height,
+ qreal frameRate, QMediaRecorder::Quality quality);
+ quint32 estimateAudioBitRate(const GUID &audioFormat, QMediaRecorder::Quality quality);
+ bool m_active = false;
+ bool m_activating = false;
+ QCameraDevice m_activeCameraDevice;
+ QCameraFormat m_cameraFormat;
+ QWindowsMediaDeviceReader *m_mediaDeviceReader = nullptr;
+ QAudioInput *m_audioInput = nullptr;
+ QAudioOutput *m_audioOutput = nullptr;
+ QVideoSink *m_surface = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSMEDIADEVICESESSION_H
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder.cpp b/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder.cpp
new file mode 100644
index 000000000..512110af6
--- /dev/null
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder.cpp
@@ -0,0 +1,225 @@
+// 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"
+
+#include "qwindowsmediadevicesession_p.h"
+#include "qwindowsmediacapture_p.h"
+#include "mfmetadata_p.h"
+#include <QtCore/QUrl>
+#include <QtCore/QMimeType>
+#include <mferror.h>
+#include <shobjidl.h>
+#include <private/qmediastoragelocation_p.h>
+#include <private/qmediarecorder_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QWindowsMediaEncoder::QWindowsMediaEncoder(QMediaRecorder *parent)
+ : QObject(parent),
+ QPlatformMediaRecorder(parent)
+{
+}
+
+bool QWindowsMediaEncoder::isLocationWritable(const QUrl &location) const
+{
+ return location.scheme() == QLatin1String("file") || location.scheme().isEmpty();
+}
+
+QMediaRecorder::RecorderState QWindowsMediaEncoder::state() const
+{
+ return m_state;
+}
+
+qint64 QWindowsMediaEncoder::duration() const
+{
+ return m_duration;
+}
+
+void QWindowsMediaEncoder::record(QMediaEncoderSettings &settings)
+{
+ if (!m_captureService || !m_mediaDeviceSession) {
+ qWarning() << Q_FUNC_INFO << "Encoder is not set to a capture session";
+ return;
+ }
+ if (m_state != QMediaRecorder::StoppedState)
+ return;
+
+ m_sessionWasActive = m_mediaDeviceSession->isActive() || m_mediaDeviceSession->isActivating();
+
+ if (!m_sessionWasActive) {
+
+ m_mediaDeviceSession->setActive(true);
+
+ if (!m_mediaDeviceSession->isActivating()) {
+ updateError(QMediaRecorder::ResourceError,
+ QMediaRecorderPrivate::msgFailedStartRecording());
+ return;
+ }
+ }
+
+ const auto audioOnly = settings.videoCodec() == QMediaFormat::VideoCodec::Unspecified;
+ m_fileName = QMediaStorageLocation::generateFileName(outputLocation().toLocalFile(), audioOnly
+ ? QStandardPaths::MusicLocation
+ : QStandardPaths::MoviesLocation,
+ settings.mimeType().preferredSuffix());
+
+ QMediaRecorder::Error ec = m_mediaDeviceSession->startRecording(settings, m_fileName, audioOnly);
+ if (ec == QMediaRecorder::NoError) {
+ m_state = QMediaRecorder::RecordingState;
+
+ actualLocationChanged(QUrl::fromLocalFile(m_fileName));
+ stateChanged(m_state);
+
+ } else {
+ updateError(ec, QMediaRecorderPrivate::msgFailedStartRecording());
+ }
+}
+
+void QWindowsMediaEncoder::pause()
+{
+ if (!m_mediaDeviceSession || m_state != QMediaRecorder::RecordingState)
+ return;
+
+ if (m_mediaDeviceSession->pauseRecording()) {
+ m_state = QMediaRecorder::PausedState;
+ stateChanged(m_state);
+ } else {
+ updateError(QMediaRecorder::FormatError, tr("Failed to pause recording"));
+ }
+}
+
+void QWindowsMediaEncoder::resume()
+{
+ if (!m_mediaDeviceSession || m_state != QMediaRecorder::PausedState)
+ return;
+
+ if (m_mediaDeviceSession->resumeRecording()) {
+ m_state = QMediaRecorder::RecordingState;
+ stateChanged(m_state);
+ } else {
+ updateError(QMediaRecorder::FormatError, tr("Failed to resume recording"));
+ }
+}
+
+void QWindowsMediaEncoder::stop()
+{
+ if (m_mediaDeviceSession && m_state != QMediaRecorder::StoppedState) {
+ m_mediaDeviceSession->stopRecording();
+ if (!m_sessionWasActive)
+ m_mediaDeviceSession->setActive(false);
+ }
+}
+
+
+
+void QWindowsMediaEncoder::setCaptureSession(QPlatformMediaCaptureSession *session)
+{
+ QWindowsMediaCaptureService *captureSession = static_cast<QWindowsMediaCaptureService *>(session);
+ if (m_captureService == captureSession)
+ return;
+
+ if (m_captureService)
+ stop();
+
+ m_captureService = captureSession;
+ if (!m_captureService) {
+ m_mediaDeviceSession = nullptr;
+ return;
+ }
+
+ m_mediaDeviceSession = m_captureService->session();
+ Q_ASSERT(m_mediaDeviceSession);
+
+ connect(m_mediaDeviceSession, &QWindowsMediaDeviceSession::recordingStarted, this, &QWindowsMediaEncoder::onRecordingStarted);
+ connect(m_mediaDeviceSession, &QWindowsMediaDeviceSession::recordingStopped, this, &QWindowsMediaEncoder::onRecordingStopped);
+ connect(m_mediaDeviceSession, &QWindowsMediaDeviceSession::streamingError, this, &QWindowsMediaEncoder::onStreamingError);
+ connect(m_mediaDeviceSession, &QWindowsMediaDeviceSession::recordingError, this, &QWindowsMediaEncoder::onRecordingError);
+ connect(m_mediaDeviceSession, &QWindowsMediaDeviceSession::durationChanged, this, &QWindowsMediaEncoder::onDurationChanged);
+ connect(m_captureService, &QWindowsMediaCaptureService::cameraChanged, this, &QWindowsMediaEncoder::onCameraChanged);
+ onCameraChanged();
+}
+
+void QWindowsMediaEncoder::setMetaData(const QMediaMetaData &metaData)
+{
+ m_metaData = metaData;
+}
+
+QMediaMetaData QWindowsMediaEncoder::metaData() const
+{
+ return m_metaData;
+}
+
+void QWindowsMediaEncoder::saveMetadata()
+{
+ if (!m_metaData.isEmpty()) {
+
+ const QString nativeFileName = QDir::toNativeSeparators(m_fileName);
+
+ IPropertyStore *store = nullptr;
+
+ if (SUCCEEDED(SHGetPropertyStoreFromParsingName(reinterpret_cast<LPCWSTR>(nativeFileName.utf16()),
+ nullptr, GPS_READWRITE, IID_PPV_ARGS(&store)))) {
+
+ MFMetaData::toNative(m_metaData, store);
+
+ store->Commit();
+ store->Release();
+ }
+ }
+}
+
+void QWindowsMediaEncoder::onDurationChanged(qint64 duration)
+{
+ m_duration = duration;
+ durationChanged(m_duration);
+}
+
+void QWindowsMediaEncoder::onStreamingError(int errorCode)
+{
+ if (errorCode == MF_E_VIDEO_RECORDING_DEVICE_INVALIDATED)
+ updateError(QMediaRecorder::ResourceError, tr("Camera is no longer present"));
+ else if (errorCode == MF_E_AUDIO_RECORDING_DEVICE_INVALIDATED)
+ updateError(QMediaRecorder::ResourceError, tr("Audio input is no longer present"));
+ else
+ updateError(QMediaRecorder::ResourceError, tr("Streaming error"));
+
+ if (m_state != QMediaRecorder::StoppedState) {
+ m_mediaDeviceSession->stopRecording();
+ if (!m_sessionWasActive)
+ m_mediaDeviceSession->setActive(false);
+ }
+}
+
+void QWindowsMediaEncoder::onRecordingError(int errorCode)
+{
+ Q_UNUSED(errorCode);
+ updateError(QMediaRecorder::ResourceError, tr("Recording error"));
+
+ auto lastState = m_state;
+ m_state = QMediaRecorder::StoppedState;
+ if (m_state != lastState)
+ stateChanged(m_state);
+}
+
+void QWindowsMediaEncoder::onCameraChanged()
+{
+}
+
+void QWindowsMediaEncoder::onRecordingStarted()
+{
+}
+
+void QWindowsMediaEncoder::onRecordingStopped()
+{
+ saveMetadata();
+
+ auto lastState = m_state;
+ m_state = QMediaRecorder::StoppedState;
+ if (m_state != lastState)
+ stateChanged(m_state);
+}
+
+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
new file mode 100644
index 000000000..51f35ce9d
--- /dev/null
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder_p.h
@@ -0,0 +1,71 @@
+// 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
+// -------------
+//
+// 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 QWINDOWSMEDIAENCODER_H
+#define QWINDOWSMEDIAENCODER_H
+
+#include <private/qplatformmediarecorder_p.h>
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qurl.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsMediaDeviceSession;
+class QPlatformMediaCaptureSession;
+class QWindowsMediaCaptureService;
+
+class QWindowsMediaEncoder : public QObject, public QPlatformMediaRecorder
+{
+ Q_OBJECT
+public:
+ explicit QWindowsMediaEncoder(QMediaRecorder *parent);
+
+ bool isLocationWritable(const QUrl &location) const override;
+ QMediaRecorder::RecorderState state() const override;
+ qint64 duration() const override;
+
+ void setMetaData(const QMediaMetaData &metaData) override;
+ QMediaMetaData metaData() const override;
+
+ void setCaptureSession(QPlatformMediaCaptureSession *session);
+
+ void record(QMediaEncoderSettings &settings) override;
+ void pause() override;
+ void resume() override;
+ void stop() override;
+
+private Q_SLOTS:
+ void onCameraChanged();
+ void onRecordingStarted();
+ void onRecordingStopped();
+ void onDurationChanged(qint64 duration);
+ void onStreamingError(int errorCode);
+ void onRecordingError(int errorCode);
+
+private:
+ void saveMetadata();
+
+ QWindowsMediaCaptureService *m_captureService = nullptr;
+ QWindowsMediaDeviceSession *m_mediaDeviceSession = nullptr;
+ QMediaRecorder::RecorderState m_state = QMediaRecorder::StoppedState;
+ QString m_fileName;
+ QMediaMetaData m_metaData;
+ qint64 m_duration = 0;
+ bool m_sessionWasActive = false;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/windows/mfstream.cpp b/src/plugins/multimedia/windows/mfstream.cpp
new file mode 100644
index 000000000..fb37ce293
--- /dev/null
+++ b/src/plugins/multimedia/windows/mfstream.cpp
@@ -0,0 +1,326 @@
+// 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.
+
+MFStream::MFStream(QIODevice *stream, bool ownStream)
+ : m_cRef(1)
+ , m_stream(stream)
+ , m_ownStream(ownStream)
+ , m_currentReadResult(0)
+{
+ //Move to the thread of the stream object
+ //to make sure invocations on stream
+ //are happened in the same thread of stream object
+ this->moveToThread(stream->thread());
+}
+
+MFStream::~MFStream()
+{
+ if (m_currentReadResult)
+ m_currentReadResult->Release();
+ if (m_ownStream)
+ m_stream->deleteLater();
+}
+
+//from IUnknown
+STDMETHODIMP MFStream::QueryInterface(REFIID riid, LPVOID *ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+ if (riid == IID_IMFByteStream) {
+ *ppvObject = static_cast<IMFByteStream*>(this);
+ } else if (riid == IID_IUnknown) {
+ *ppvObject = static_cast<IUnknown*>(this);
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+STDMETHODIMP_(ULONG) MFStream::AddRef(void)
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+STDMETHODIMP_(ULONG) MFStream::Release(void)
+{
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0) {
+ this->deleteLater();
+ }
+ return cRef;
+}
+
+
+//from IMFByteStream
+STDMETHODIMP MFStream::GetCapabilities(DWORD *pdwCapabilities)
+{
+ if (!pdwCapabilities)
+ return E_INVALIDARG;
+ *pdwCapabilities = MFBYTESTREAM_IS_READABLE;
+ if (!m_stream->isSequential())
+ *pdwCapabilities |= MFBYTESTREAM_IS_SEEKABLE;
+ return S_OK;
+}
+
+STDMETHODIMP MFStream::GetLength(QWORD *pqwLength)
+{
+ if (!pqwLength)
+ return E_INVALIDARG;
+ QMutexLocker locker(&m_mutex);
+ *pqwLength = QWORD(m_stream->size());
+ return S_OK;
+}
+
+STDMETHODIMP MFStream::SetLength(QWORD)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP MFStream::GetCurrentPosition(QWORD *pqwPosition)
+{
+ if (!pqwPosition)
+ return E_INVALIDARG;
+ QMutexLocker locker(&m_mutex);
+ *pqwPosition = m_stream->pos();
+ return S_OK;
+}
+
+STDMETHODIMP MFStream::SetCurrentPosition(QWORD qwPosition)
+{
+ QMutexLocker locker(&m_mutex);
+ //SetCurrentPosition may happend during the BeginRead/EndRead pair,
+ //refusing to execute SetCurrentPosition during that time seems to be
+ //the simplest workable solution
+ if (m_currentReadResult)
+ return S_FALSE;
+
+ bool seekOK = m_stream->seek(qint64(qwPosition));
+ if (seekOK)
+ return S_OK;
+ else
+ return S_FALSE;
+}
+
+STDMETHODIMP MFStream::IsEndOfStream(BOOL *pfEndOfStream)
+{
+ if (!pfEndOfStream)
+ return E_INVALIDARG;
+ QMutexLocker locker(&m_mutex);
+ *pfEndOfStream = m_stream->atEnd() ? TRUE : FALSE;
+ return S_OK;
+}
+
+STDMETHODIMP MFStream::Read(BYTE *pb, ULONG cb, ULONG *pcbRead)
+{
+ QMutexLocker locker(&m_mutex);
+ qint64 read = m_stream->read((char*)(pb), qint64(cb));
+ if (pcbRead)
+ *pcbRead = ULONG(read);
+ return S_OK;
+}
+
+STDMETHODIMP MFStream::BeginRead(BYTE *pb, ULONG cb, IMFAsyncCallback *pCallback,
+ IUnknown *punkState)
+{
+ if (!pCallback || !pb)
+ return E_INVALIDARG;
+
+ Q_ASSERT(m_currentReadResult == NULL);
+
+ AsyncReadState *state = new (std::nothrow) AsyncReadState(pb, cb);
+ if (state == NULL)
+ return E_OUTOFMEMORY;
+
+ HRESULT hr = MFCreateAsyncResult(state, pCallback, punkState, &m_currentReadResult);
+ state->Release();
+ if (FAILED(hr))
+ return hr;
+
+ QCoreApplication::postEvent(this, new QEvent(QEvent::User));
+ return hr;
+}
+
+STDMETHODIMP MFStream::EndRead(IMFAsyncResult* pResult, ULONG *pcbRead)
+{
+ if (!pcbRead)
+ return E_INVALIDARG;
+ IUnknown *pUnk;
+ pResult->GetObject(&pUnk);
+ AsyncReadState *state = static_cast<AsyncReadState*>(pUnk);
+ *pcbRead = state->bytesRead();
+ pUnk->Release();
+
+ m_currentReadResult->Release();
+ m_currentReadResult = NULL;
+
+ return S_OK;
+}
+
+STDMETHODIMP MFStream::Write(const BYTE *, ULONG, ULONG *)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP MFStream::BeginWrite(const BYTE *, ULONG ,
+ IMFAsyncCallback *,
+ IUnknown *)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP MFStream::EndWrite(IMFAsyncResult *,
+ ULONG *)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP MFStream::Seek(
+ MFBYTESTREAM_SEEK_ORIGIN SeekOrigin,
+ LONGLONG llSeekOffset,
+ DWORD,
+ QWORD *pqwCurrentPosition)
+{
+ QMutexLocker locker(&m_mutex);
+ if (m_currentReadResult)
+ return S_FALSE;
+
+ qint64 pos = qint64(llSeekOffset);
+ switch (SeekOrigin) {
+ case msoBegin:
+ break;
+ case msoCurrent:
+ pos += m_stream->pos();
+ break;
+ }
+ bool seekOK = m_stream->seek(pos);
+ if (pqwCurrentPosition)
+ *pqwCurrentPosition = pos;
+ if (seekOK)
+ return S_OK;
+ else
+ return S_FALSE;
+}
+
+STDMETHODIMP MFStream::Flush()
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP MFStream::Close()
+{
+ QMutexLocker locker(&m_mutex);
+ if (m_ownStream)
+ m_stream->close();
+ return S_OK;
+}
+
+void MFStream::doRead()
+{
+ if (!m_stream)
+ return;
+
+ bool readDone = true;
+ IUnknown *pUnk = NULL;
+ HRESULT hr = m_currentReadResult->GetObject(&pUnk);
+ if (SUCCEEDED(hr)) {
+ //do actual read
+ AsyncReadState *state = static_cast<AsyncReadState*>(pUnk);
+ ULONG cbRead;
+ Read(state->pb(), state->cb() - state->bytesRead(), &cbRead);
+ pUnk->Release();
+
+ state->setBytesRead(cbRead + state->bytesRead());
+ if (state->cb() > state->bytesRead() && !m_stream->atEnd()) {
+ readDone = false;
+ }
+ }
+
+ if (readDone) {
+ //now inform the original caller
+ m_currentReadResult->SetStatus(hr);
+ MFInvokeCallback(m_currentReadResult);
+ }
+}
+
+void MFStream::customEvent(QEvent *event)
+{
+ if (event->type() != QEvent::User) {
+ QObject::customEvent(event);
+ return;
+ }
+ doRead();
+}
+
+//AsyncReadState is a helper class used in BeginRead for asynchronous operation
+//to record some BeginRead parameters, so these parameters could be
+//used later when actually executing the read operation in another thread.
+MFStream::AsyncReadState::AsyncReadState(BYTE *pb, ULONG cb)
+ : m_cRef(1)
+ , m_pb(pb)
+ , m_cb(cb)
+ , m_cbRead(0)
+{
+}
+
+//from IUnknown
+STDMETHODIMP MFStream::AsyncReadState::QueryInterface(REFIID riid, LPVOID *ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+
+ if (riid == IID_IUnknown) {
+ *ppvObject = static_cast<IUnknown*>(this);
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+STDMETHODIMP_(ULONG) MFStream::AsyncReadState::AddRef(void)
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+STDMETHODIMP_(ULONG) MFStream::AsyncReadState::Release(void)
+{
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ delete this;
+ // For thread safety, return a temporary variable.
+ return cRef;
+}
+
+BYTE* MFStream::AsyncReadState::pb() const
+{
+ return m_pb;
+}
+
+ULONG MFStream::AsyncReadState::cb() const
+{
+ return m_cb;
+}
+
+ULONG MFStream::AsyncReadState::bytesRead() const
+{
+ return m_cbRead;
+}
+
+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
new file mode 100644
index 000000000..a5221ed75
--- /dev/null
+++ b/src/plugins/multimedia/windows/mfstream_p.h
@@ -0,0 +1,124 @@
+// 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
+
+//
+// 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/qmutex.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class MFStream : public QObject, public IMFByteStream
+{
+ Q_OBJECT
+public:
+ MFStream(QIODevice *stream, bool ownStream);
+
+ ~MFStream();
+
+ //from IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject) override;
+
+ STDMETHODIMP_(ULONG) AddRef(void) override;
+
+ STDMETHODIMP_(ULONG) Release(void) override;
+
+
+ //from IMFByteStream
+ STDMETHODIMP GetCapabilities(DWORD *pdwCapabilities) override;
+
+ STDMETHODIMP GetLength(QWORD *pqwLength) override;
+
+ STDMETHODIMP SetLength(QWORD) override;
+
+ STDMETHODIMP GetCurrentPosition(QWORD *pqwPosition) override;
+
+ STDMETHODIMP SetCurrentPosition(QWORD qwPosition) override;
+
+ STDMETHODIMP IsEndOfStream(BOOL *pfEndOfStream) override;
+
+ STDMETHODIMP Read(BYTE *pb, ULONG cb, ULONG *pcbRead) override;
+
+ STDMETHODIMP BeginRead(BYTE *pb, ULONG cb, IMFAsyncCallback *pCallback,
+ IUnknown *punkState) override;
+
+ STDMETHODIMP EndRead(IMFAsyncResult* pResult, ULONG *pcbRead) override;
+
+ STDMETHODIMP Write(const BYTE *, ULONG, ULONG *) override;
+
+ STDMETHODIMP BeginWrite(const BYTE *, ULONG ,
+ IMFAsyncCallback *,
+ IUnknown *) override;
+
+ STDMETHODIMP EndWrite(IMFAsyncResult *,
+ ULONG *) override;
+
+ STDMETHODIMP Seek(
+ MFBYTESTREAM_SEEK_ORIGIN SeekOrigin,
+ LONGLONG llSeekOffset,
+ DWORD,
+ QWORD *pqwCurrentPosition) override;
+
+ STDMETHODIMP Flush() override;
+
+ STDMETHODIMP Close() override;
+
+private:
+ class AsyncReadState : public IUnknown
+ {
+ public:
+ AsyncReadState(BYTE *pb, ULONG cb);
+ virtual ~AsyncReadState() = default;
+
+ //from IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject) override;
+
+ STDMETHODIMP_(ULONG) AddRef(void) override;
+
+ STDMETHODIMP_(ULONG) Release(void) override;
+
+ BYTE* pb() const;
+ ULONG cb() const;
+ ULONG bytesRead() const;
+
+ void setBytesRead(ULONG cbRead);
+
+ private:
+ long m_cRef;
+ BYTE *m_pb;
+ ULONG m_cb;
+ ULONG m_cbRead;
+ };
+
+ long m_cRef;
+ QPointer<QIODevice> m_stream;
+ bool m_ownStream;
+ DWORD m_workQueueId;
+ QMutex m_mutex;
+
+ void doRead();
+
+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
new file mode 100644
index 000000000..644c96529
--- /dev/null
+++ b/src/plugins/multimedia/windows/player/mfactivate.cpp
@@ -0,0 +1,17 @@
+// 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()
+{
+ MFCreateAttributes(&m_attributes, 0);
+}
+
+MFAbstractActivate::~MFAbstractActivate()
+{
+ if (m_attributes)
+ m_attributes->Release();
+}
diff --git a/src/plugins/multimedia/windows/player/mfactivate_p.h b/src/plugins/multimedia/windows/player/mfactivate_p.h
new file mode 100644
index 000000000..efe75474b
--- /dev/null
+++ b/src/plugins/multimedia/windows/player/mfactivate_p.h
@@ -0,0 +1,202 @@
+// 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
+
+//
+// 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 <mfidl.h>
+#include <private/qcomobject_p.h>
+
+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();
+
+ //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() 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() override
+ {
+ return m_attributes->LockStore();
+ }
+
+ STDMETHODIMP UnlockStore() 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);
+ }
+
+protected:
+ // Destructor is not public. Caller should call Release.
+ ~MFAbstractActivate() override;
+
+private:
+ 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
new file mode 100644
index 000000000..109f7964b
--- /dev/null
+++ b/src/plugins/multimedia/windows/player/mfevrvideowindowcontrol.cpp
@@ -0,0 +1,55 @@
+// 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"
+
+#include <qdebug.h>
+
+MFEvrVideoWindowControl::MFEvrVideoWindowControl(QVideoSink *parent)
+ : EvrVideoWindowControl(parent)
+ , m_currentActivate(NULL)
+ , m_evrSink(NULL)
+{
+}
+
+MFEvrVideoWindowControl::~MFEvrVideoWindowControl()
+{
+ clear();
+}
+
+void MFEvrVideoWindowControl::clear()
+{
+ setEvr(NULL);
+
+ if (m_evrSink)
+ m_evrSink->Release();
+ if (m_currentActivate) {
+ m_currentActivate->ShutdownObject();
+ m_currentActivate->Release();
+ }
+ m_evrSink = NULL;
+ m_currentActivate = NULL;
+}
+
+IMFActivate* MFEvrVideoWindowControl::createActivate()
+{
+ clear();
+
+ if (FAILED(MFCreateVideoRendererActivate(0, &m_currentActivate))) {
+ qWarning() << "Failed to create evr video renderer activate!";
+ return NULL;
+ }
+ if (FAILED(m_currentActivate->ActivateObject(IID_IMFMediaSink, (LPVOID*)(&m_evrSink)))) {
+ qWarning() << "Failed to activate evr media sink!";
+ return NULL;
+ }
+ if (!setEvr(m_evrSink))
+ return NULL;
+
+ return m_currentActivate;
+}
+
+void MFEvrVideoWindowControl::releaseActivate()
+{
+ clear();
+}
diff --git a/src/plugins/multimedia/windows/player/mfevrvideowindowcontrol_p.h b/src/plugins/multimedia/windows/player/mfevrvideowindowcontrol_p.h
new file mode 100644
index 000000000..1ac90e8ce
--- /dev/null
+++ b/src/plugins/multimedia/windows/player/mfevrvideowindowcontrol_p.h
@@ -0,0 +1,38 @@
+// 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
+
+//
+// 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 "evrvideowindowcontrol_p.h"
+
+QT_USE_NAMESPACE
+
+class MFEvrVideoWindowControl : public EvrVideoWindowControl
+{
+public:
+ MFEvrVideoWindowControl(QVideoSink *parent = 0);
+ ~MFEvrVideoWindowControl();
+
+ IMFActivate* createActivate();
+ void releaseActivate();
+
+private:
+ void clear();
+
+ IMFActivate *m_currentActivate;
+ IMFMediaSink *m_evrSink;
+};
+
+#endif // MFEVRVIDEOWINDOWCONTROL_H
diff --git a/src/plugins/multimedia/windows/player/mfplayercontrol.cpp b/src/plugins/multimedia/windows/player/mfplayercontrol.cpp
new file mode 100644
index 000000000..ae0022773
--- /dev/null
+++ b/src/plugins/multimedia/windows/player/mfplayercontrol.cpp
@@ -0,0 +1,306 @@
+// 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"
+#include "mfvideorenderercontrol_p.h"
+#include <qdebug.h>
+
+//#define DEBUG_MEDIAFOUNDATION
+
+MFPlayerControl::MFPlayerControl(QMediaPlayer *player)
+ : QPlatformMediaPlayer(player)
+ , m_state(QMediaPlayer::StoppedState)
+ , m_stateDirty(false)
+ , m_videoAvailable(false)
+ , m_audioAvailable(false)
+ , m_duration(0)
+ , m_seekable(false)
+{
+ m_session = new MFPlayerSession(this);
+}
+
+MFPlayerControl::~MFPlayerControl()
+{
+ m_session->close();
+ m_session->setPlayerControl(nullptr);
+ m_session->Release();
+}
+
+void MFPlayerControl::setMedia(const QUrl &media, QIODevice *stream)
+{
+ if (m_state != QMediaPlayer::StoppedState) {
+ changeState(QMediaPlayer::StoppedState);
+ m_session->stop(true);
+ refreshState();
+ }
+
+ m_media = media;
+ m_stream = stream;
+ resetAudioVideoAvailable();
+ handleDurationUpdate(0);
+ handleSeekableUpdate(false);
+ m_session->load(media, stream);
+}
+
+void MFPlayerControl::play()
+{
+ if (m_state == QMediaPlayer::PlayingState)
+ return;
+ resetCurrentLoop();
+ if (QMediaPlayer::InvalidMedia == m_session->status())
+ m_session->load(m_media, m_stream);
+
+ switch (m_session->status()) {
+ case QMediaPlayer::NoMedia:
+ case QMediaPlayer::InvalidMedia:
+ return;
+ case QMediaPlayer::LoadedMedia:
+ case QMediaPlayer::BufferingMedia:
+ case QMediaPlayer::BufferedMedia:
+ case QMediaPlayer::EndOfMedia:
+ changeState(QMediaPlayer::PlayingState);
+ m_session->start();
+ break;
+ default: //Loading/Stalled
+ changeState(QMediaPlayer::PlayingState);
+ break;
+ }
+ refreshState();
+}
+
+void MFPlayerControl::pause()
+{
+ if (m_state == QMediaPlayer::PausedState)
+ return;
+
+ if (m_session->status() == QMediaPlayer::NoMedia ||
+ m_session->status() == QMediaPlayer::InvalidMedia)
+ return;
+
+ changeState(QMediaPlayer::PausedState);
+ m_session->pause();
+ refreshState();
+}
+
+void MFPlayerControl::stop()
+{
+ if (m_state == QMediaPlayer::StoppedState)
+ return;
+ changeState(QMediaPlayer::StoppedState);
+ m_session->stop();
+ refreshState();
+}
+
+QMediaMetaData MFPlayerControl::metaData() const
+{
+ return m_session->metaData();
+}
+
+void MFPlayerControl::setAudioOutput(QPlatformAudioOutput *output)
+{
+ m_session->setAudioOutput(output);
+}
+
+void MFPlayerControl::setVideoSink(QVideoSink *sink)
+{
+ m_session->setVideoSink(sink);
+}
+
+void MFPlayerControl::changeState(QMediaPlayer::PlaybackState state)
+{
+ if (m_state == state)
+ return;
+ m_state = state;
+ m_stateDirty = true;
+}
+
+void MFPlayerControl::refreshState()
+{
+ if (!m_stateDirty)
+ return;
+ m_stateDirty = false;
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "MFPlayerControl::emit stateChanged" << m_state;
+#endif
+ stateChanged(m_state);
+}
+
+void MFPlayerControl::handleStatusChanged()
+{
+ QMediaPlayer::MediaStatus status = m_session->status();
+ switch (status) {
+ case QMediaPlayer::EndOfMedia:
+ if (doLoop()) {
+ setPosition(0);
+ m_session->start();
+ } else {
+ changeState(QMediaPlayer::StoppedState);
+ }
+ break;
+ case QMediaPlayer::InvalidMedia:
+ break;
+ case QMediaPlayer::LoadedMedia:
+ case QMediaPlayer::BufferingMedia:
+ case QMediaPlayer::BufferedMedia:
+ if (m_state == QMediaPlayer::PlayingState)
+ m_session->start();
+ break;
+ default:
+ break;
+ }
+ mediaStatusChanged(m_session->status());
+ refreshState();
+}
+
+void MFPlayerControl::handleTracksChanged()
+{
+ tracksChanged();
+}
+
+void MFPlayerControl::handleVideoAvailable()
+{
+ if (m_videoAvailable)
+ return;
+ m_videoAvailable = true;
+ videoAvailableChanged(m_videoAvailable);
+}
+
+void MFPlayerControl::handleAudioAvailable()
+{
+ if (m_audioAvailable)
+ return;
+ m_audioAvailable = true;
+ audioAvailableChanged(m_audioAvailable);
+}
+
+void MFPlayerControl::resetAudioVideoAvailable()
+{
+ bool videoDirty = false;
+ if (m_videoAvailable) {
+ m_videoAvailable = false;
+ videoDirty = true;
+ }
+ if (m_audioAvailable) {
+ m_audioAvailable = false;
+ audioAvailableChanged(m_audioAvailable);
+ }
+ if (videoDirty)
+ videoAvailableChanged(m_videoAvailable);
+}
+
+void MFPlayerControl::handleDurationUpdate(qint64 duration)
+{
+ if (m_duration == duration)
+ return;
+ m_duration = duration;
+ durationChanged(m_duration);
+}
+
+void MFPlayerControl::handleSeekableUpdate(bool seekable)
+{
+ if (m_seekable == seekable)
+ return;
+ m_seekable = seekable;
+ seekableChanged(m_seekable);
+}
+
+QMediaPlayer::PlaybackState MFPlayerControl::state() const
+{
+ return m_state;
+}
+
+QMediaPlayer::MediaStatus MFPlayerControl::mediaStatus() const
+{
+ return m_session->status();
+}
+
+qint64 MFPlayerControl::duration() const
+{
+ return m_duration;
+}
+
+qint64 MFPlayerControl::position() const
+{
+ return m_session->position();
+}
+
+void MFPlayerControl::setPosition(qint64 position)
+{
+ if (!m_seekable || position == m_session->position())
+ return;
+ m_session->setPosition(position);
+}
+
+float MFPlayerControl::bufferProgress() const
+{
+ return m_session->bufferProgress() / 100.;
+}
+
+bool MFPlayerControl::isAudioAvailable() const
+{
+ return m_audioAvailable;
+}
+
+bool MFPlayerControl::isVideoAvailable() const
+{
+ return m_videoAvailable;
+}
+
+bool MFPlayerControl::isSeekable() const
+{
+ return m_seekable;
+}
+
+QMediaTimeRange MFPlayerControl::availablePlaybackRanges() const
+{
+ return m_session->availablePlaybackRanges();
+}
+
+qreal MFPlayerControl::playbackRate() const
+{
+ return m_session->playbackRate();
+}
+
+void MFPlayerControl::setPlaybackRate(qreal rate)
+{
+ m_session->setPlaybackRate(rate);
+}
+
+QUrl MFPlayerControl::media() const
+{
+ return m_media;
+}
+
+const QIODevice* MFPlayerControl::mediaStream() const
+{
+ return m_stream;
+}
+
+void MFPlayerControl::handleError(QMediaPlayer::Error errorCode, const QString& errorString, bool isFatal)
+{
+ if (isFatal)
+ stop();
+ error(int(errorCode), errorString);
+}
+
+void MFPlayerControl::setActiveTrack(TrackType type, int index)
+{
+ m_session->setActiveTrack(type, index);
+}
+
+int MFPlayerControl::activeTrack(TrackType type)
+{
+ return m_session->activeTrack(type);
+}
+
+int MFPlayerControl::trackCount(TrackType type)
+{
+ return m_session->trackCount(type);
+}
+
+QMediaMetaData MFPlayerControl::trackMetaData(TrackType type, int trackNumber)
+{
+ return m_session->trackMetaData(type, trackNumber);
+}
+
diff --git a/src/plugins/multimedia/windows/player/mfplayercontrol_p.h b/src/plugins/multimedia/windows/player/mfplayercontrol_p.h
new file mode 100644
index 000000000..db863afaa
--- /dev/null
+++ b/src/plugins/multimedia/windows/player/mfplayercontrol_p.h
@@ -0,0 +1,103 @@
+// 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
+
+//
+// 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 "qurl.h"
+#include "private/qplatformmediaplayer_p.h"
+
+#include <QtCore/qcoreevent.h>
+
+QT_BEGIN_NAMESPACE
+
+class MFPlayerSession;
+
+class MFPlayerControl : public QPlatformMediaPlayer
+{
+public:
+ MFPlayerControl(QMediaPlayer *player);
+ ~MFPlayerControl();
+
+ QMediaPlayer::PlaybackState state() const override;
+
+ QMediaPlayer::MediaStatus mediaStatus() const override;
+
+ qint64 duration() const override;
+
+ qint64 position() const override;
+ void setPosition(qint64 position) override;
+
+ float bufferProgress() const override;
+
+ bool isAudioAvailable() const override;
+ bool isVideoAvailable() const override;
+
+ bool isSeekable() 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 &media, QIODevice *stream) override;
+
+ void play() override;
+ void pause() override;
+ void stop() override;
+
+ bool streamPlaybackSupported() const override { return true; }
+
+ QMediaMetaData metaData() const override;
+
+ void setAudioOutput(QPlatformAudioOutput *output) override;
+
+ void setVideoSink(QVideoSink *sink) override;
+
+ void setActiveTrack(TrackType type, int index) override;
+ int activeTrack(TrackType type) override;
+ int trackCount(TrackType type) override;
+ QMediaMetaData trackMetaData(TrackType type, int trackNumber) override;
+
+ void handleStatusChanged();
+ void handleTracksChanged();
+ void handleVideoAvailable();
+ void handleAudioAvailable();
+ void handleDurationUpdate(qint64 duration);
+ void handleSeekableUpdate(bool seekable);
+ void handleError(QMediaPlayer::Error errorCode, const QString& errorString, bool isFatal);
+
+private:
+ void changeState(QMediaPlayer::PlaybackState state);
+ void resetAudioVideoAvailable();
+ void refreshState();
+
+ QMediaPlayer::PlaybackState m_state;
+ bool m_stateDirty;
+
+ bool m_videoAvailable;
+ bool m_audioAvailable;
+ qint64 m_duration;
+ bool m_seekable;
+
+ QIODevice *m_stream;
+ QUrl m_media;
+ 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
new file mode 100644
index 000000000..996ce35d8
--- /dev/null
+++ b/src/plugins/multimedia/windows/player/mfplayersession.cpp
@@ -0,0 +1,1736 @@
+// 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"
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qthread.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qbuffer.h>
+
+#include "private/qplatformaudiooutput_p.h"
+#include "qaudiooutput.h"
+
+#include "mfplayercontrol_p.h"
+#include "mfvideorenderercontrol_p.h"
+#include <mfmetadata_p.h>
+#include <private/qwindowsmfdefs_p.h>
+#include <private/qwindowsaudioutils_p.h>
+
+#include "mfplayersession_p.h"
+#include <mferror.h>
+#include <nserror.h>
+#include <winerror.h>
+#include "sourceresolver_p.h"
+#include <wmcodecdsp.h>
+
+#include <mmdeviceapi.h>
+#include <propvarutil.h>
+#include <functiondiscoverykeys_devpkey.h>
+
+//#define DEBUG_MEDIAFOUNDATION
+
+QT_BEGIN_NAMESPACE
+
+MFPlayerSession::MFPlayerSession(MFPlayerControl *playerControl)
+ : 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)
+
+{
+ connect(this, &MFPlayerSession::sessionEvent, this, &MFPlayerSession::handleSessionEvent);
+
+ m_signalPositionChangeTimer.setInterval(10);
+ m_signalPositionChangeTimer.setTimerType(Qt::PreciseTimer);
+ m_signalPositionChangeTimer.callOnTimeout(this, &MFPlayerSession::timeout);
+
+ m_pendingState = NoPending;
+ ZeroMemory(&m_state, sizeof(m_state));
+ m_state.command = CmdStop;
+ m_state.prevCmd = CmdNone;
+ m_state.rate = 1.0f;
+ ZeroMemory(&m_request, sizeof(m_request));
+ m_request.command = CmdNone;
+ m_request.prevCmd = CmdNone;
+ m_request.rate = 1.0f;
+
+ m_videoRendererControl = new MFVideoRendererControl(this);
+}
+
+void MFPlayerSession::timeout()
+{
+ const qint64 pos = position();
+
+ if (pos != m_lastPosition) {
+ const bool updatePos = m_timeCounter++ % 10 == 0;
+ if (pos >= qint64(m_duration / 10000 - 20)) {
+ if (m_playerControl->doLoop()) {
+ m_session->Pause();
+ setPosition(0);
+ positionChanged(0);
+ } else {
+ if (updatePos)
+ positionChanged(pos);
+ }
+ } else {
+ if (updatePos)
+ positionChanged(pos);
+ }
+ m_lastPosition = pos;
+ }
+}
+
+void MFPlayerSession::close()
+{
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "close";
+#endif
+
+ m_signalPositionChangeTimer.stop();
+ clear();
+ if (!m_session)
+ return;
+
+ HRESULT hr = S_OK;
+ if (m_session) {
+ m_closing = true;
+ hr = m_session->Close();
+ if (SUCCEEDED(hr)) {
+ DWORD dwWaitResult = WaitForSingleObject(m_hCloseEvent.get(), 2000);
+ if (dwWaitResult == WAIT_TIMEOUT) {
+ qWarning() << "session close time out!";
+ }
+ }
+ m_closing = false;
+ }
+
+ if (SUCCEEDED(hr)) {
+ if (m_session)
+ m_session->Shutdown();
+ if (m_sourceResolver)
+ m_sourceResolver->shutdown();
+ }
+ m_sourceResolver.Reset();
+
+ m_videoRendererControl->releaseActivate();
+// } else if (m_playerService->videoWindowControl()) {
+// m_playerService->videoWindowControl()->releaseActivate();
+// }
+
+ m_session.Reset();
+ m_hCloseEvent = {};
+ m_lastPosition = -1;
+ m_position = 0;
+}
+
+void MFPlayerSession::load(const QUrl &url, QIODevice *stream)
+{
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "load";
+#endif
+ clear();
+
+ if (m_status == QMediaPlayer::LoadingMedia && m_sourceResolver)
+ m_sourceResolver->cancel();
+
+ if (url.isEmpty() && !stream) {
+ close();
+ changeStatus(QMediaPlayer::NoMedia);
+ } else if (stream && (!stream->isReadable())) {
+ close();
+ changeStatus(QMediaPlayer::InvalidMedia);
+ error(QMediaPlayer::ResourceError, tr("Invalid stream source."), true);
+ } else if (createSession()) {
+ changeStatus(QMediaPlayer::LoadingMedia);
+ m_sourceResolver->load(url, stream);
+ if (url.isLocalFile())
+ m_updateRoutingOnStart = true;
+ }
+ positionChanged(position());
+}
+
+void MFPlayerSession::handleSourceError(long hr)
+{
+ QString errorString;
+ QMediaPlayer::Error errorCode = QMediaPlayer::ResourceError;
+ switch (hr) {
+ case QMediaPlayer::FormatError:
+ errorCode = QMediaPlayer::FormatError;
+ errorString = tr("Attempting to play invalid Qt resource.");
+ break;
+ case NS_E_FILE_NOT_FOUND:
+ errorString = tr("The system cannot find the file specified.");
+ break;
+ case NS_E_SERVER_NOT_FOUND:
+ errorString = tr("The specified server could not be found.");
+ break;
+ case MF_E_UNSUPPORTED_BYTESTREAM_TYPE:
+ 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);
+ error(errorCode, errorString, true);
+}
+
+void MFPlayerSession::handleMediaSourceReady()
+{
+ if (QMediaPlayer::LoadingMedia != m_status || !m_sourceResolver
+ || m_sourceResolver.Get() != sender())
+ return;
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "handleMediaSourceReady";
+#endif
+ HRESULT hr = S_OK;
+ IMFMediaSource* mediaSource = m_sourceResolver->mediaSource();
+
+ DWORD dwCharacteristics = 0;
+ mediaSource->GetCharacteristics(&dwCharacteristics);
+ seekableUpdate(MFMEDIASOURCE_CAN_SEEK & dwCharacteristics);
+
+ ComPtr<IMFPresentationDescriptor> sourcePD;
+ hr = mediaSource->CreatePresentationDescriptor(&sourcePD);
+ if (SUCCEEDED(hr)) {
+ m_duration = 0;
+ m_metaData = MFMetaData::fromNative(mediaSource);
+ metaDataChanged();
+ sourcePD->GetUINT64(MF_PD_DURATION, &m_duration);
+ //convert from 100 nanosecond to milisecond
+ durationUpdate(qint64(m_duration / 10000));
+ setupPlaybackTopology(mediaSource, sourcePD.Get());
+ tracksChanged();
+ } else {
+ changeStatus(QMediaPlayer::InvalidMedia);
+ error(QMediaPlayer::ResourceError, tr("Cannot create presentation descriptor."), true);
+ }
+}
+
+bool MFPlayerSession::getStreamInfo(IMFStreamDescriptor *stream,
+ MFPlayerSession::MediaType *type,
+ QString *name,
+ QString *language,
+ GUID *format) const
+{
+ if (!stream || !type || !name || !language || !format)
+ return false;
+
+ *type = Unknown;
+ *name = QString();
+ *language = QString();
+
+ ComPtr<IMFMediaTypeHandler> typeHandler;
+
+ if (SUCCEEDED(stream->GetMediaTypeHandler(&typeHandler))) {
+
+ UINT32 len = 0;
+ if (SUCCEEDED(stream->GetStringLength(QMM_MF_SD_STREAM_NAME, &len)) && len > 0) {
+ WCHAR *wstr = new WCHAR[len+1];
+ if (SUCCEEDED(stream->GetString(QMM_MF_SD_STREAM_NAME, wstr, len+1, &len))) {
+ *name = QString::fromUtf16(reinterpret_cast<const char16_t *>(wstr));
+ }
+ delete []wstr;
+ }
+ if (SUCCEEDED(stream->GetStringLength(QMM_MF_SD_LANGUAGE, &len)) && len > 0) {
+ WCHAR *wstr = new WCHAR[len+1];
+ if (SUCCEEDED(stream->GetString(QMM_MF_SD_LANGUAGE, wstr, len+1, &len))) {
+ *language = QString::fromUtf16(reinterpret_cast<const char16_t *>(wstr));
+ }
+ delete []wstr;
+ }
+
+ GUID guidMajorType;
+ if (SUCCEEDED(typeHandler->GetMajorType(&guidMajorType))) {
+ if (guidMajorType == MFMediaType_Audio)
+ *type = Audio;
+ else if (guidMajorType == MFMediaType_Video)
+ *type = Video;
+ }
+
+ ComPtr<IMFMediaType> mediaType;
+ if (SUCCEEDED(typeHandler->GetCurrentMediaType(&mediaType))) {
+ mediaType->GetGUID(MF_MT_SUBTYPE, format);
+ }
+ }
+
+ return *type != Unknown;
+}
+
+void MFPlayerSession::setupPlaybackTopology(IMFMediaSource *source, IMFPresentationDescriptor *sourcePD)
+{
+ HRESULT hr = S_OK;
+ // Get the number of streams in the media source.
+ DWORD cSourceStreams = 0;
+ hr = sourcePD->GetStreamDescriptorCount(&cSourceStreams);
+ if (FAILED(hr)) {
+ changeStatus(QMediaPlayer::InvalidMedia);
+ error(QMediaPlayer::ResourceError, tr("Failed to get stream count."), true);
+ return;
+ }
+
+ ComPtr<IMFTopology> topology;
+ hr = MFCreateTopology(&topology);
+ if (FAILED(hr)) {
+ changeStatus(QMediaPlayer::InvalidMedia);
+ error(QMediaPlayer::ResourceError, tr("Failed to create topology."), true);
+ return;
+ }
+
+ // For each stream, create the topology nodes and add them to the topology.
+ DWORD succeededCount = 0;
+ for (DWORD i = 0; i < cSourceStreams; i++) {
+ BOOL selected = FALSE;
+ bool streamAdded = false;
+ ComPtr<IMFStreamDescriptor> streamDesc;
+
+ HRESULT hr = sourcePD->GetStreamDescriptorByIndex(i, &selected, &streamDesc);
+ if (SUCCEEDED(hr)) {
+ // The media might have multiple audio and video streams,
+ // only use one of each kind, and only if it is selected by default.
+ MediaType mediaType = Unknown;
+ QString streamName;
+ QString streamLanguage;
+ GUID format = GUID_NULL;
+
+ if (getStreamInfo(streamDesc.Get(), &mediaType, &streamName, &streamLanguage,
+ &format)) {
+
+ QPlatformMediaPlayer::TrackType trackType = (mediaType == Audio) ?
+ QPlatformMediaPlayer::AudioStream : QPlatformMediaPlayer::VideoStream;
+
+ QLocale::Language lang = streamLanguage.isEmpty() ?
+ QLocale::Language::AnyLanguage : QLocale(streamLanguage).language();
+
+ QMediaMetaData metaData;
+ metaData.insert(QMediaMetaData::Title, streamName);
+ metaData.insert(QMediaMetaData::Language, lang);
+
+ m_trackInfo[trackType].metaData.append(metaData);
+ m_trackInfo[trackType].nativeIndexes.append(i);
+ m_trackInfo[trackType].format = format;
+
+ if (((m_mediaTypes & mediaType) == 0) && selected) { // Check if this type isn't already added
+ ComPtr<IMFTopologyNode> sourceNode =
+ addSourceNode(topology.Get(), source, sourcePD, streamDesc.Get());
+ if (sourceNode) {
+ ComPtr<IMFTopologyNode> outputNode =
+ addOutputNode(mediaType, topology.Get(), 0);
+ if (outputNode) {
+ sourceNode->GetTopoNodeID(&m_trackInfo[trackType].sourceNodeId);
+ outputNode->GetTopoNodeID(&m_trackInfo[trackType].outputNodeId);
+
+ hr = sourceNode->ConnectOutput(0, outputNode.Get(), 0);
+
+ if (FAILED(hr)) {
+ error(QMediaPlayer::FormatError, tr("Unable to play any stream."), false);
+ } else {
+ m_trackInfo[trackType].currentIndex = m_trackInfo[trackType].nativeIndexes.count() - 1;
+ streamAdded = true;
+ succeededCount++;
+ m_mediaTypes |= mediaType;
+ switch (mediaType) {
+ case Audio:
+ audioAvailable();
+ break;
+ case Video:
+ videoAvailable();
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ // remove the source node if the output node cannot be created
+ topology->RemoveNode(sourceNode.Get());
+ }
+ }
+ }
+ }
+
+ if (selected && !streamAdded)
+ sourcePD->DeselectStream(i);
+ }
+ }
+
+ if (succeededCount == 0) {
+ changeStatus(QMediaPlayer::InvalidMedia);
+ 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.Get());
+ if (SUCCEEDED(hr)) {
+ m_updatingTopology = true;
+ } else {
+ changeStatus(QMediaPlayer::InvalidMedia);
+ error(QMediaPlayer::ResourceError, tr("Failed to set topology."), true);
+ }
+ }
+}
+
+ComPtr<IMFTopologyNode> MFPlayerSession::addSourceNode(IMFTopology *topology,
+ IMFMediaSource *source,
+ IMFPresentationDescriptor *presentationDesc,
+ IMFStreamDescriptor *streamDesc)
+{
+ ComPtr<IMFTopologyNode> node;
+ HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node);
+ if (SUCCEEDED(hr)) {
+ hr = node->SetUnknown(MF_TOPONODE_SOURCE, source);
+ if (SUCCEEDED(hr)) {
+ hr = node->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, presentationDesc);
+ if (SUCCEEDED(hr)) {
+ hr = node->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, streamDesc);
+ if (SUCCEEDED(hr)) {
+ hr = topology->AddNode(node.Get());
+ if (SUCCEEDED(hr))
+ return node;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+ComPtr<IMFTopologyNode> MFPlayerSession::addOutputNode(MediaType mediaType, IMFTopology *topology,
+ DWORD sinkID)
+{
+ ComPtr<IMFTopologyNode> node;
+ if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node)))
+ return 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";
+ return NULL;
+ }
+
+ 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();
+ 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.
+ error(QMediaPlayer::FormatError, tr("Unknown stream type."), false);
+ }
+
+ 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.Reset();
+
+ return node;
+}
+
+// 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)
+{
+ ComPtr<IUnknown> nodeObject;
+ ComPtr<IMFActivate> activate;
+ ComPtr<IMFStreamSink> stream;
+ ComPtr<IMFMediaSink> sink;
+
+ HRESULT hr = pNode->GetObject(&nodeObject);
+ if (FAILED(hr))
+ return hr;
+
+ hr = nodeObject->QueryInterface(IID_PPV_ARGS(&activate));
+ if (SUCCEEDED(hr)) {
+ DWORD dwStreamID = 0;
+
+ // Try to create the media sink.
+ hr = activate->ActivateObject(IID_PPV_ARGS(&sink));
+ if (SUCCEEDED(hr))
+ dwStreamID = MFGetAttributeUINT32(pNode, MF_TOPONODE_STREAMID, 0);
+
+ if (SUCCEEDED(hr)) {
+ // First check if the media sink already has a stream sink with the requested ID.
+ hr = sink->GetStreamSinkById(dwStreamID, &stream);
+ if (FAILED(hr)) {
+ // Create the stream sink.
+ hr = sink->AddStreamSink(dwStreamID, NULL, &stream);
+ }
+ }
+
+ // Replace the node's object pointer with the stream sink.
+ if (SUCCEEDED(hr)) {
+ hr = pNode->SetObject(stream.Get());
+ }
+ } else {
+ hr = nodeObject->QueryInterface(IID_PPV_ARGS(&stream));
+ }
+
+ return hr;
+}
+
+// BindOutputNodes
+// Sets the IMFStreamSink pointers on all of the output nodes in a topology.
+HRESULT BindOutputNodes(IMFTopology *pTopology)
+{
+ ComPtr<IMFCollection> collection;
+
+ // Get the collection of output nodes.
+ HRESULT hr = pTopology->GetOutputNodeCollection(&collection);
+
+ // Enumerate all of the nodes in the collection.
+ if (SUCCEEDED(hr)) {
+ DWORD cNodes;
+ hr = collection->GetElementCount(&cNodes);
+
+ if (SUCCEEDED(hr)) {
+ for (DWORD i = 0; i < cNodes; i++) {
+ ComPtr<IUnknown> element;
+ hr = collection->GetElement(i, &element);
+ if (FAILED(hr))
+ break;
+
+ ComPtr<IMFTopologyNode> node;
+ hr = element->QueryInterface(IID_IMFTopologyNode, &node);
+ if (FAILED(hr))
+ break;
+
+ // Bind this node.
+ hr = BindOutputNode(node.Get());
+ if (FAILED(hr))
+ break;
+ }
+ }
+ }
+
+ return hr;
+}
+
+// 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.
+ComPtr<IMFTopology> MFPlayerSession::insertMFT(const ComPtr<IMFTopology> &topology,
+ TOPOID outputNodeId)
+{
+ bool isNewTopology = false;
+
+ ComPtr<IMFTopoLoader> topoLoader;
+ ComPtr<IMFTopology> resolvedTopology;
+ ComPtr<IMFCollection> outputNodes;
+
+ do {
+ if (FAILED(BindOutputNodes(topology.Get())))
+ break;
+
+ if (FAILED(MFCreateTopoLoader(&topoLoader)))
+ break;
+
+ 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.Get(), outputNodeId);
+ if (FAILED(topoLoader->Load(topology.Get(), &resolvedTopology, NULL)))
+ break;
+ }
+
+ if (insertResizer(resolvedTopology.Get()))
+ isNewTopology = true;
+ } while (false);
+
+ if (isNewTopology) {
+ return resolvedTopology;
+ }
+
+ return topology;
+}
+
+// This method checks if the topology contains a color converter transform (CColorConvertDMO),
+// if it does it inserts a resizer transform (CResizerDMO) to handle dynamic frame size change
+// of the video stream.
+// Returns true if it inserted a resizer
+bool MFPlayerSession::insertResizer(IMFTopology *topology)
+{
+ bool inserted = false;
+ WORD elementCount = 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) {
+ node.Reset();
+ object.Reset();
+
+ if (FAILED(topology->GetNode(i, &node)))
+ break;
+
+ MF_TOPOLOGY_TYPE nodeType;
+ if (FAILED(node->GetNodeType(&nodeType)))
+ break;
+
+ if (nodeType != MF_TOPOLOGY_TRANSFORM_NODE)
+ continue;
+
+ if (FAILED(node->GetObject(&object)))
+ break;
+
+ if (FAILED(object->QueryInterface(IID_PPV_ARGS(&colorConv))))
+ continue;
+
+ 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.Get())))
+ break;
+
+ if (FAILED(topology->AddNode(resizerNode.Get())))
+ break;
+
+ DWORD outputIndex = 0;
+ if (FAILED(node->GetInput(0, &inputNode, &outputIndex))) {
+ topology->RemoveNode(resizerNode.Get());
+ break;
+ }
+
+ if (FAILED(inputNode->ConnectOutput(0, resizerNode.Get(), 0))) {
+ topology->RemoveNode(resizerNode.Get());
+ break;
+ }
+
+ if (FAILED(resizerNode->ConnectOutput(0, node.Get(), 0))) {
+ inputNode->ConnectOutput(0, node.Get(), 0);
+ topology->RemoveNode(resizerNode.Get());
+ break;
+ }
+
+ inserted = true;
+ break;
+ }
+
+ return inserted;
+}
+
+// This method inserts a color converter (CColorConvertDMO) in the topology,
+// typically to convert to RGB format.
+// Usually this converter is automatically inserted when the topology is resolved but
+// for some reason it fails to do so in some cases, we then do it ourselves.
+void MFPlayerSession::insertColorConverter(IMFTopology *topology, TOPOID outputNodeId)
+{
+ ComPtr<IMFCollection> outputNodes;
+
+ if (FAILED(topology->GetOutputNodeCollection(&outputNodes)))
+ return;
+
+ DWORD elementCount = 0;
+ if (FAILED(outputNodes->GetElementCount(&elementCount)))
+ return;
+
+ for (DWORD n = 0; n < elementCount; n++) {
+ 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, &node)))
+ break;
+
+ TOPOID id;
+ if (FAILED(node->GetTopoNodeID(&id)))
+ break;
+
+ if (id != outputNodeId)
+ break;
+
+ DWORD outputIndex = 0;
+ if (FAILED(node->GetInput(0, &inputNode, &outputIndex)))
+ break;
+
+ if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &mftNode)))
+ break;
+
+ if (FAILED(CoCreateInstance(CLSID_CColorConvertDMO, NULL, CLSCTX_INPROC_SERVER,
+ IID_IMFTransform, &converter)))
+ break;
+
+ if (FAILED(mftNode->SetObject(converter.Get())))
+ break;
+
+ if (FAILED(topology->AddNode(mftNode.Get())))
+ break;
+
+ if (FAILED(inputNode->ConnectOutput(0, mftNode.Get(), 0)))
+ break;
+
+ if (FAILED(mftNode->ConnectOutput(0, node.Get(), 0)))
+ break;
+
+ } while (false);
+ }
+}
+
+void MFPlayerSession::stop(bool immediate)
+{
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "stop";
+#endif
+ if (!immediate && m_pendingState != NoPending) {
+ m_request.setCommand(CmdStop);
+ } else {
+ if (m_state.command == CmdStop)
+ return;
+
+ if (m_scrubbing)
+ scrub(false);
+
+ if (SUCCEEDED(m_session->Stop())) {
+
+ m_state.setCommand(CmdStop);
+ m_pendingState = CmdPending;
+ if (m_status != QMediaPlayer::EndOfMedia) {
+ m_position = 0;
+ positionChanged(0);
+ }
+ } else {
+ error(QMediaPlayer::ResourceError, tr("Failed to stop."), true);
+ }
+ }
+}
+
+void MFPlayerSession::start()
+{
+ if (status() == QMediaPlayer::LoadedMedia && m_updateRoutingOnStart) {
+ m_updateRoutingOnStart = false;
+ updateOutputRouting();
+ }
+
+ if (m_status == QMediaPlayer::EndOfMedia) {
+ m_position = 0; // restart from the beginning
+ positionChanged(0);
+ }
+
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "start";
+#endif
+
+ if (m_pendingState != NoPending) {
+ m_request.setCommand(CmdStart);
+ } else {
+ if (m_state.command == CmdStart)
+ return;
+
+ if (m_scrubbing) {
+ scrub(false);
+ m_position = position() * 10000;
+ }
+
+ if (m_restorePosition >= 0) {
+ m_position = m_restorePosition;
+ if (!m_updatingTopology)
+ m_restorePosition = -1;
+ }
+
+ PROPVARIANT varStart;
+ InitPropVariantFromInt64(m_position, &varStart);
+
+ if (SUCCEEDED(m_session->Start(&GUID_NULL, &varStart))) {
+ m_state.setCommand(CmdStart);
+ m_pendingState = CmdPending;
+ } else {
+ error(QMediaPlayer::ResourceError, tr("failed to start playback"), true);
+ }
+ PropVariantClear(&varStart);
+ }
+}
+
+void MFPlayerSession::pause()
+{
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "pause";
+#endif
+ if (m_pendingState != NoPending) {
+ m_request.setCommand(CmdPause);
+ } else {
+ if (m_state.command == CmdPause)
+ return;
+
+ if (SUCCEEDED(m_session->Pause())) {
+ m_state.setCommand(CmdPause);
+ m_pendingState = CmdPending;
+ } else {
+ error(QMediaPlayer::ResourceError, tr("Failed to pause."), false);
+ }
+ if (m_status == QMediaPlayer::EndOfMedia) {
+ setPosition(0);
+ positionChanged(0);
+ }
+ }
+}
+
+void MFPlayerSession::changeStatus(QMediaPlayer::MediaStatus newStatus)
+{
+ if (m_status == newStatus)
+ return;
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "MFPlayerSession::changeStatus" << newStatus;
+#endif
+ m_status = newStatus;
+ statusChanged();
+}
+
+QMediaPlayer::MediaStatus MFPlayerSession::status() const
+{
+ return m_status;
+}
+
+bool MFPlayerSession::createSession()
+{
+ close();
+
+ Q_ASSERT(m_session == NULL);
+
+ HRESULT hr = MFCreateMediaSession(NULL, &m_session);
+ if (FAILED(hr)) {
+ changeStatus(QMediaPlayer::InvalidMedia);
+ error(QMediaPlayer::ResourceError, tr("Unable to create mediasession."), true);
+ return false;
+ }
+
+ m_hCloseEvent = EventHandle{ CreateEvent(NULL, FALSE, FALSE, NULL) };
+
+ hr = m_session->BeginGetEvent(this, m_session.Get());
+ if (FAILED(hr)) {
+ changeStatus(QMediaPlayer::InvalidMedia);
+ error(QMediaPlayer::ResourceError, tr("Unable to pull session events."), false);
+ close();
+ return false;
+ }
+
+ 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;
+}
+
+qint64 MFPlayerSession::position()
+{
+ if (m_request.command == CmdSeek || m_request.command == CmdSeekResume)
+ return m_request.start;
+
+ if (m_pendingState == SeekPending)
+ return m_state.start;
+
+ if (m_state.command == CmdStop)
+ return m_position / 10000;
+
+ if (m_presentationClock) {
+ MFTIME time, sysTime;
+ if (FAILED(m_presentationClock->GetCorrelatedTime(0, &time, &sysTime)))
+ return m_position / 10000;
+ return qint64(time / 10000);
+ }
+ return m_position / 10000;
+}
+
+void MFPlayerSession::setPosition(qint64 position)
+{
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "setPosition";
+#endif
+ if (m_pendingState != NoPending) {
+ m_request.setCommand(CmdSeek);
+ m_request.start = position;
+ } else {
+ setPositionInternal(position, CmdNone);
+ }
+}
+
+void MFPlayerSession::setPositionInternal(qint64 position, Command requestCmd)
+{
+ if (m_status == QMediaPlayer::EndOfMedia)
+ changeStatus(QMediaPlayer::LoadedMedia);
+ if (m_state.command == CmdStop && requestCmd != CmdSeekResume) {
+ m_position = position * 10000;
+ // Even though the position is not actually set on the session yet,
+ // report it to have changed anyway for UI controls to be updated
+ positionChanged(this->position());
+ return;
+ }
+
+ if (m_state.command == CmdPause)
+ scrub(true);
+
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "setPositionInternal";
+#endif
+
+ PROPVARIANT varStart;
+ varStart.vt = VT_I8;
+ varStart.hVal.QuadPart = LONGLONG(position * 10000);
+ 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 {
+ error(QMediaPlayer::ResourceError, tr("Failed to seek."), true);
+ }
+}
+
+qreal MFPlayerSession::playbackRate() const
+{
+ if (m_scrubbing)
+ return m_restoreRate;
+ return m_state.rate;
+}
+
+void MFPlayerSession::setPlaybackRate(qreal rate)
+{
+ if (m_scrubbing) {
+ m_restoreRate = rate;
+ playbackRateChanged(rate);
+ return;
+ }
+ setPlaybackRateInternal(rate);
+}
+
+void MFPlayerSession::setPlaybackRateInternal(qreal rate)
+{
+ if (rate == m_request.rate)
+ return;
+
+ m_pendingRate = rate;
+ if (!m_rateSupport)
+ return;
+
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "setPlaybackRate";
+#endif
+ BOOL isThin = FALSE;
+
+ //from MSDN http://msdn.microsoft.com/en-us/library/aa965220%28v=vs.85%29.aspx
+ //Thinning applies primarily to video streams.
+ //In thinned mode, the source drops delta frames and deliver only key frames.
+ //At very high playback rates, the source might skip some key frames (for example, deliver every other key frame).
+
+ if (FAILED(m_rateSupport->IsRateSupported(FALSE, rate, NULL))) {
+ isThin = TRUE;
+ if (FAILED(m_rateSupport->IsRateSupported(isThin, rate, NULL))) {
+ qWarning() << "unable to set playbackrate = " << rate;
+ m_pendingRate = m_request.rate = m_state.rate;
+ return;
+ }
+ }
+ if (m_pendingState != NoPending) {
+ m_request.rate = rate;
+ m_request.isThin = isThin;
+ // Remember the current transport state (play, paused, etc), so that we
+ // can restore it after the rate change, if necessary. However, if
+ // anothercommand is already pending, that one takes precedent.
+ if (m_request.command == CmdNone)
+ m_request.setCommand(m_state.command);
+ } else {
+ //No pending operation. Commit the new rate.
+ commitRateChange(rate, isThin);
+ }
+}
+
+void MFPlayerSession::commitRateChange(qreal rate, BOOL isThin)
+{
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "commitRateChange";
+#endif
+ Q_ASSERT(m_pendingState == NoPending);
+ MFTIME hnsSystemTime = 0;
+ MFTIME hnsClockTime = 0;
+ Command cmdNow = m_state.command;
+ bool resetPosition = false;
+ // Allowed rate transitions:
+ // Positive <-> negative: Stopped
+ // Negative <-> zero: Stopped
+ // Postive <-> zero: Paused or stopped
+ if ((rate > 0 && m_state.rate <= 0) || (rate < 0 && m_state.rate >= 0)) {
+ if (cmdNow == CmdStart) {
+ // Get the current clock position. This will be the restart time.
+ m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
+ Q_ASSERT(hnsSystemTime != 0);
+
+ if (rate < 0 || m_state.rate < 0)
+ m_request.setCommand(CmdSeekResume);
+ else if (isThin || m_state.isThin)
+ m_request.setCommand(CmdStartAndSeek);
+ else
+ m_request.setCommand(CmdStart);
+
+ // We need to stop only when dealing with negative rates
+ if (rate >= 0 && m_state.rate >= 0)
+ pause();
+ else
+ stop();
+
+ // If we deal with negative rates, we stopped the session and consequently
+ // reset the position to zero. We then need to resume to the current position.
+ m_request.start = hnsClockTime / 10000;
+ } else if (cmdNow == CmdPause) {
+ if (rate < 0 || m_state.rate < 0) {
+ // The current state is paused.
+ // For this rate change, the session must be stopped. However, the
+ // session cannot transition back from stopped to paused.
+ // Therefore, this rate transition is not supported while paused.
+ qWarning() << "Unable to change rate from positive to negative or vice versa in paused state";
+ rate = m_state.rate;
+ isThin = m_state.isThin;
+ goto done;
+ }
+
+ // This happens when resuming playback after scrubbing in pause mode.
+ // This transition requires the session to be paused. Even though our
+ // internal state is set to paused, the session might not be so we need
+ // to enforce it
+ if (rate > 0 && m_state.rate == 0) {
+ m_state.setCommand(CmdNone);
+ pause();
+ }
+ }
+ } else if (rate == 0 && m_state.rate > 0) {
+ if (cmdNow != CmdPause) {
+ // Transition to paused.
+ // This transisition requires the paused state.
+ // Pause and set the rate.
+ pause();
+
+ // Request: Switch back to current state.
+ m_request.setCommand(cmdNow);
+ }
+ } else if (rate == 0 && m_state.rate < 0) {
+ // Changing rate from negative to zero requires to stop the session
+ m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
+
+ m_request.setCommand(CmdSeekResume);
+
+ stop();
+
+ // Resume to the current position (stop() will reset the position to 0)
+ m_request.start = hnsClockTime / 10000;
+ } else if (!isThin && m_state.isThin) {
+ if (cmdNow == CmdStart) {
+ // When thinning, only key frames are read and presented. Going back
+ // to normal playback requires to reset the current position to force
+ // the pipeline to decode the actual frame at the current position
+ // (which might be earlier than the last decoded key frame)
+ resetPosition = true;
+ } else if (cmdNow == CmdPause) {
+ // If paused, don't reset the position until we resume, otherwise
+ // a new frame will be rendered
+ m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
+ m_request.setCommand(CmdSeekResume);
+ m_request.start = hnsClockTime / 10000;
+ }
+
+ }
+
+ // Set the rate.
+ if (FAILED(m_rateControl->SetRate(isThin, rate))) {
+ qWarning() << "failed to set playbackrate = " << rate;
+ rate = m_state.rate;
+ isThin = m_state.isThin;
+ goto done;
+ }
+
+ if (resetPosition) {
+ m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
+ setPosition(hnsClockTime / 10000);
+ }
+
+done:
+ // Adjust our current rate and requested rate.
+ m_pendingRate = m_request.rate = m_state.rate = rate;
+ if (rate != 0)
+ m_state.isThin = isThin;
+ playbackRateChanged(rate);
+}
+
+void MFPlayerSession::scrub(bool enableScrub)
+{
+ if (m_scrubbing == enableScrub)
+ return;
+
+ m_scrubbing = enableScrub;
+
+ if (!canScrub()) {
+ if (!enableScrub)
+ m_pendingRate = m_restoreRate;
+ return;
+ }
+
+ if (enableScrub) {
+ // Enter scrubbing mode. Cache the rate.
+ m_restoreRate = m_request.rate;
+ setPlaybackRateInternal(0.0f);
+ } else {
+ // Leaving scrubbing mode. Restore the old rate.
+ setPlaybackRateInternal(m_restoreRate);
+ }
+}
+
+void MFPlayerSession::setVolume(float volume)
+{
+ if (m_volume == volume)
+ return;
+ m_volume = volume;
+
+ if (!m_muted)
+ setVolumeInternal(volume);
+}
+
+void MFPlayerSession::setMuted(bool muted)
+{
+ if (m_muted == muted)
+ return;
+ m_muted = muted;
+
+ setVolumeInternal(muted ? 0 : m_volume);
+}
+
+void MFPlayerSession::setVolumeInternal(float volume)
+{
+ if (m_volumeControl) {
+ quint32 channelCount = 0;
+ if (!SUCCEEDED(m_volumeControl->GetChannelCount(&channelCount))
+ || channelCount == 0)
+ return;
+
+ for (quint32 i = 0; i < channelCount; ++i)
+ m_volumeControl->SetChannelVolume(i, volume);
+ }
+}
+
+float MFPlayerSession::bufferProgress()
+{
+ if (!m_netsourceStatistics)
+ return 0;
+ PROPVARIANT var;
+ PropVariantInit(&var);
+ PROPERTYKEY key;
+ key.fmtid = MFNETSOURCE_STATISTICS;
+ key.pid = MFNETSOURCE_BUFFERPROGRESS_ID;
+ int progress = -1;
+ // GetValue returns S_FALSE if the property is not available, which has
+ // a value > 0. We therefore can't use the SUCCEEDED macro here.
+ if (m_netsourceStatistics->GetValue(key, &var) == S_OK) {
+ progress = var.lVal;
+ PropVariantClear(&var);
+ }
+
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "bufferProgress: progress = " << progress;
+#endif
+
+ return progress/100.;
+}
+
+QMediaTimeRange MFPlayerSession::availablePlaybackRanges()
+{
+ // defaults to the whole media
+ qint64 start = 0;
+ qint64 end = qint64(m_duration / 10000);
+
+ if (m_netsourceStatistics) {
+ PROPVARIANT var;
+ PropVariantInit(&var);
+ PROPERTYKEY key;
+ key.fmtid = MFNETSOURCE_STATISTICS;
+ key.pid = MFNETSOURCE_SEEKRANGESTART_ID;
+ // GetValue returns S_FALSE if the property is not available, which has
+ // a value > 0. We therefore can't use the SUCCEEDED macro here.
+ if (m_netsourceStatistics->GetValue(key, &var) == S_OK) {
+ start = qint64(var.uhVal.QuadPart / 10000);
+ PropVariantClear(&var);
+ PropVariantInit(&var);
+ key.pid = MFNETSOURCE_SEEKRANGEEND_ID;
+ if (m_netsourceStatistics->GetValue(key, &var) == S_OK) {
+ end = qint64(var.uhVal.QuadPart / 10000);
+ PropVariantClear(&var);
+ }
+ }
+ }
+
+ return QMediaTimeRange(start, end);
+}
+
+HRESULT MFPlayerSession::QueryInterface(REFIID riid, void** ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+ if (riid == IID_IMFAsyncCallback) {
+ *ppvObject = static_cast<IMFAsyncCallback*>(this);
+ } else if (riid == IID_IUnknown) {
+ *ppvObject = static_cast<IUnknown*>(this);
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ return S_OK;
+}
+
+ULONG MFPlayerSession::AddRef(void)
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+ULONG MFPlayerSession::Release(void)
+{
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ 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.Get())
+ return S_OK;
+
+ ComPtr<IMFMediaEvent> pEvent;
+ // Get the event from the event queue.
+ HRESULT hr = m_session->EndGetEvent(pResult, &pEvent);
+ if (FAILED(hr)) {
+ return S_OK;
+ }
+
+ MediaEventType meType = MEUnknown;
+ hr = pEvent->GetType(&meType);
+ if (FAILED(hr)) {
+ return S_OK;
+ }
+
+ if (meType == MESessionClosed) {
+ SetEvent(m_hCloseEvent.get());
+ return S_OK;
+ } else {
+ hr = m_session->BeginGetEvent(this, m_session.Get());
+ if (FAILED(hr)) {
+ return S_OK;
+ }
+ }
+
+ if (!m_closing) {
+ emit sessionEvent(pEvent);
+ }
+ return S_OK;
+}
+
+void MFPlayerSession::handleSessionEvent(const ComPtr<IMFMediaEvent> &sessionEvent)
+{
+ HRESULT hrStatus = S_OK;
+ HRESULT hr = sessionEvent->GetStatus(&hrStatus);
+ if (FAILED(hr) || !m_session) {
+ return;
+ }
+
+ MediaEventType meType = MEUnknown;
+ hr = sessionEvent->GetType(&meType);
+#ifdef DEBUG_MEDIAFOUNDATION
+ if (FAILED(hrStatus))
+ qDebug() << "handleSessionEvent: MediaEventType = " << meType << "Failed";
+ else
+ qDebug() << "handleSessionEvent: MediaEventType = " << meType;
+#endif
+
+ switch (meType) {
+ case MENonFatalError: {
+ PROPVARIANT var;
+ PropVariantInit(&var);
+ sessionEvent->GetValue(&var);
+ qWarning() << "handleSessionEvent: non fatal error = " << var.ulVal;
+ PropVariantClear(&var);
+ error(QMediaPlayer::ResourceError, tr("Media session non-fatal error."), false);
+ }
+ break;
+ case MESourceUnknown:
+ changeStatus(QMediaPlayer::InvalidMedia);
+ break;
+ case MEError:
+ if (hrStatus == MF_E_ALREADY_INITIALIZED) {
+ // Workaround for a possible WMF issue that causes an error
+ // with some specific videos, which play fine otherwise.
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "handleSessionEvent: ignoring MF_E_ALREADY_INITIALIZED";
+#endif
+ break;
+ }
+ changeStatus(QMediaPlayer::InvalidMedia);
+ 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
+ // cached. If it failed, try to get the actual rate.
+ if (FAILED(hrStatus)) {
+ PROPVARIANT var;
+ PropVariantInit(&var);
+ if (SUCCEEDED(sessionEvent->GetValue(&var)) && (var.vt == VT_R4)) {
+ m_state.rate = var.fltVal;
+ }
+ playbackRateChanged(playbackRate());
+ }
+ break;
+ case MESessionScrubSampleComplete :
+ if (m_scrubbing)
+ updatePendingCommands(CmdStart);
+ break;
+ case MESessionStarted:
+ if (m_status == QMediaPlayer::EndOfMedia
+ || m_status == QMediaPlayer::LoadedMedia) {
+ // If the session started, then enough data is buffered to play
+ changeStatus(QMediaPlayer::BufferedMedia);
+ }
+
+ updatePendingCommands(CmdStart);
+ // playback started, we can now set again the procAmpValues if they have been
+ // changed previously (these are lost when loading a new media)
+// if (m_playerService->videoWindowControl()) {
+// m_playerService->videoWindowControl()->applyImageControls();
+// }
+ m_signalPositionChangeTimer.start();
+ break;
+ case MESessionStopped:
+ if (m_status != QMediaPlayer::EndOfMedia) {
+ m_position = 0;
+
+ // Reset to Loaded status unless we are loading a new media
+ // or changing the playback rate to negative values (stop required)
+ if (m_status != QMediaPlayer::LoadingMedia && m_request.command != CmdSeekResume)
+ changeStatus(QMediaPlayer::LoadedMedia);
+ }
+ updatePendingCommands(CmdStop);
+ m_signalPositionChangeTimer.stop();
+ break;
+ case MESessionPaused:
+ m_position = position() * 10000;
+ updatePendingCommands(CmdPause);
+ m_signalPositionChangeTimer.stop();
+ if (m_status == QMediaPlayer::LoadedMedia)
+ setPosition(position());
+ break;
+ case MEReconnectStart:
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "MEReconnectStart" << ((hrStatus == S_OK) ? "OK" : "Failed");
+#endif
+ break;
+ case MEReconnectEnd:
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "MEReconnectEnd" << ((hrStatus == S_OK) ? "OK" : "Failed");
+#endif
+ break;
+ case MESessionTopologySet:
+ if (FAILED(hrStatus)) {
+ changeStatus(QMediaPlayer::InvalidMedia);
+ error(QMediaPlayer::FormatError, tr("Unsupported media, a codec is missing."), true);
+ } else {
+ // 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;
+ m_position = 0;
+ positionChanged(0);
+ changeStatus(QMediaPlayer::LoadedMedia);
+ }
+ break;
+ }
+
+ if (FAILED(hrStatus)) {
+ return;
+ }
+
+ switch (meType) {
+ case MEBufferingStarted:
+ changeStatus(QMediaPlayer::StalledMedia);
+ bufferProgressChanged(bufferProgress());
+ break;
+ case MEBufferingStopped:
+ changeStatus(QMediaPlayer::BufferedMedia);
+ bufferProgressChanged(bufferProgress());
+ break;
+ case MESessionEnded:
+ m_pendingState = NoPending;
+ m_state.command = CmdStop;
+ m_state.prevCmd = CmdNone;
+ m_request.command = CmdNone;
+ m_request.prevCmd = CmdNone;
+
+ //keep reporting the final position after end of media
+ m_position = qint64(m_duration);
+ positionChanged(position());
+
+ changeStatus(QMediaPlayer::EndOfMedia);
+ break;
+ case MEEndOfPresentationSegment:
+ break;
+ case MESessionTopologyStatus: {
+ UINT32 status;
+ if (SUCCEEDED(sessionEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, &status))) {
+ if (status == MF_TOPOSTATUS_READY) {
+ ComPtr<IMFClock> clock;
+ if (SUCCEEDED(m_session->GetClock(&clock))) {
+ clock->QueryInterface(IID_IMFPresentationClock, &m_presentationClock);
+ }
+
+ 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;
+ }
+ BOOL isThin = FALSE;
+ float rate = 1;
+ if (SUCCEEDED(m_rateControl->GetRate(&isThin, &rate))) {
+ if (m_pendingRate != rate) {
+ m_state.rate = m_request.rate = rate;
+ setPlaybackRate(m_pendingRate);
+ }
+ }
+ }
+ MFGetService(m_session.Get(), MFNETSOURCE_STATISTICS_SERVICE,
+ IID_PPV_ARGS(&m_netsourceStatistics));
+
+ if (SUCCEEDED(MFGetService(m_session.Get(), MR_STREAM_VOLUME_SERVICE,
+ IID_PPV_ARGS(&m_volumeControl))))
+ setVolumeInternal(m_muted ? 0 : m_volume);
+
+ m_updatingTopology = false;
+ stop();
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void MFPlayerSession::updatePendingCommands(Command command)
+{
+ positionChanged(position());
+ if (m_state.command != command || m_pendingState == NoPending)
+ return;
+
+ // Seek while paused completed
+ if (m_pendingState == SeekPending && m_state.prevCmd == CmdPause) {
+ m_pendingState = NoPending;
+ // A seek operation actually restarts playback. If scrubbing is possible, playback rate
+ // is set to 0.0 at this point and we just need to reset the current state to Pause.
+ // If scrubbing is not possible, the playback rate was not changed and we explicitly need
+ // to re-pause playback.
+ if (!canScrub())
+ pause();
+ else
+ m_state.setCommand(CmdPause);
+ }
+
+ m_pendingState = NoPending;
+
+ //First look for rate changes.
+ if (m_request.rate != m_state.rate) {
+ commitRateChange(m_request.rate, m_request.isThin);
+ }
+
+ // Now look for new requests.
+ if (m_pendingState == NoPending) {
+ switch (m_request.command) {
+ case CmdStart:
+ start();
+ break;
+ case CmdPause:
+ pause();
+ break;
+ case CmdStop:
+ stop();
+ break;
+ case CmdSeek:
+ case CmdSeekResume:
+ setPositionInternal(m_request.start, m_request.command);
+ break;
+ case CmdStartAndSeek:
+ start();
+ setPositionInternal(m_request.start, m_request.command);
+ break;
+ default:
+ break;
+ }
+ m_request.setCommand(CmdNone);
+ }
+
+}
+
+bool MFPlayerSession::canScrub() const
+{
+ return m_canScrub && m_rateSupport && m_rateControl;
+}
+
+void MFPlayerSession::clear()
+{
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "MFPlayerSession::clear";
+#endif
+ m_mediaTypes = 0;
+ m_canScrub = false;
+
+ m_pendingState = NoPending;
+ m_state.command = CmdStop;
+ m_state.prevCmd = CmdNone;
+ m_request.command = CmdNone;
+ m_request.prevCmd = CmdNone;
+
+ for (int i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
+ m_trackInfo[i].metaData.clear();
+ m_trackInfo[i].nativeIndexes.clear();
+ m_trackInfo[i].currentIndex = -1;
+ m_trackInfo[i].sourceNodeId = TOPOID(-1);
+ m_trackInfo[i].outputNodeId = TOPOID(-1);
+ m_trackInfo[i].format = GUID_NULL;
+ }
+
+ if (!m_metaData.isEmpty()) {
+ m_metaData.clear();
+ metaDataChanged();
+ }
+
+ m_presentationClock.Reset();
+ m_rateControl.Reset();
+ m_rateSupport.Reset();
+ m_volumeControl.Reset();
+ m_netsourceStatistics.Reset();
+}
+
+void MFPlayerSession::setAudioOutput(QPlatformAudioOutput *device)
+{
+ if (m_audioOutput == device)
+ return;
+
+ if (m_audioOutput)
+ m_audioOutput->q->disconnect(this);
+
+ m_audioOutput = device;
+ if (m_audioOutput) {
+ setMuted(m_audioOutput->q->isMuted());
+ setVolume(m_audioOutput->q->volume());
+ updateOutputRouting();
+ connect(m_audioOutput->q, &QAudioOutput::deviceChanged, this, &MFPlayerSession::updateOutputRouting);
+ connect(m_audioOutput->q, &QAudioOutput::volumeChanged, this, &MFPlayerSession::setVolume);
+ connect(m_audioOutput->q, &QAudioOutput::mutedChanged, this, &MFPlayerSession::setMuted);
+ }
+}
+
+void MFPlayerSession::updateOutputRouting()
+{
+ int currentAudioTrack = m_trackInfo[QPlatformMediaPlayer::AudioStream].currentIndex;
+ if (currentAudioTrack > -1)
+ setActiveTrack(QPlatformMediaPlayer::AudioStream, currentAudioTrack);
+}
+
+void MFPlayerSession::setVideoSink(QVideoSink *sink)
+{
+ m_videoRendererControl->setSink(sink);
+}
+
+void MFPlayerSession::setActiveTrack(QPlatformMediaPlayer::TrackType type, int index)
+{
+ if (!m_session)
+ return;
+
+ // Only audio track selection is currently supported.
+ if (type != QPlatformMediaPlayer::AudioStream)
+ return;
+
+ const auto &nativeIndexes = m_trackInfo[type].nativeIndexes;
+
+ if (index < -1 || index >= nativeIndexes.count())
+ return;
+
+ // Updating the topology fails if there is a HEVC video stream,
+ // which causes other issues. Ignoring the change, for now.
+ if (m_trackInfo[QPlatformMediaPlayer::VideoStream].format == MFVideoFormat_HEVC)
+ return;
+
+ ComPtr<IMFTopology> topology;
+
+ if (SUCCEEDED(m_session->GetFullTopology(QMM_MFSESSION_GETFULLTOPOLOGY_CURRENT, 0, &topology))) {
+
+ m_restorePosition = position() * 10000;
+
+ if (m_state.command == CmdStart)
+ stop();
+
+ if (m_trackInfo[type].outputNodeId != TOPOID(-1)) {
+ ComPtr<IMFTopologyNode> node;
+ if (SUCCEEDED(topology->GetNodeByID(m_trackInfo[type].outputNodeId, &node))) {
+ topology->RemoveNode(node.Get());
+ m_trackInfo[type].outputNodeId = TOPOID(-1);
+ }
+ }
+ if (m_trackInfo[type].sourceNodeId != TOPOID(-1)) {
+ ComPtr<IMFTopologyNode> node;
+ if (SUCCEEDED(topology->GetNodeByID(m_trackInfo[type].sourceNodeId, &node))) {
+ topology->RemoveNode(node.Get());
+ m_trackInfo[type].sourceNodeId = TOPOID(-1);
+ }
+ }
+
+ IMFMediaSource *mediaSource = m_sourceResolver->mediaSource();
+
+ ComPtr<IMFPresentationDescriptor> sourcePD;
+ if (SUCCEEDED(mediaSource->CreatePresentationDescriptor(&sourcePD))) {
+
+ if (m_trackInfo[type].currentIndex >= 0 && m_trackInfo[type].currentIndex < nativeIndexes.count())
+ sourcePD->DeselectStream(nativeIndexes.at(m_trackInfo[type].currentIndex));
+
+ m_trackInfo[type].currentIndex = index;
+
+ if (index == -1) {
+ m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology.Get());
+ } else {
+ int nativeIndex = nativeIndexes.at(index);
+ sourcePD->SelectStream(nativeIndex);
+
+ ComPtr<IMFStreamDescriptor> streamDesc;
+ BOOL selected = FALSE;
+
+ if (SUCCEEDED(sourcePD->GetStreamDescriptorByIndex(nativeIndex, &selected, &streamDesc))) {
+ ComPtr<IMFTopologyNode> sourceNode = addSourceNode(
+ topology.Get(), mediaSource, sourcePD.Get(), streamDesc.Get());
+ if (sourceNode) {
+ ComPtr<IMFTopologyNode> outputNode =
+ addOutputNode(MFPlayerSession::Audio, topology.Get(), 0);
+ if (outputNode) {
+ 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.Get());
+ }
+ }
+ }
+ }
+ }
+ m_updatingTopology = true;
+ }
+ }
+}
+
+int MFPlayerSession::activeTrack(QPlatformMediaPlayer::TrackType type)
+{
+ if (type >= QPlatformMediaPlayer::NTrackTypes)
+ return -1;
+ return m_trackInfo[type].currentIndex;
+}
+
+int MFPlayerSession::trackCount(QPlatformMediaPlayer::TrackType type)
+{
+ if (type >= QPlatformMediaPlayer::NTrackTypes)
+ return -1;
+ return m_trackInfo[type].metaData.count();
+}
+
+QMediaMetaData MFPlayerSession::trackMetaData(QPlatformMediaPlayer::TrackType type, int trackNumber)
+{
+ if (type >= QPlatformMediaPlayer::NTrackTypes)
+ return {};
+
+ if (trackNumber < 0 || trackNumber >= m_trackInfo[type].metaData.count())
+ return {};
+
+ 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
new file mode 100644
index 000000000..50141a7fb
--- /dev/null
+++ b/src/plugins/multimedia/windows/player/mfplayersession_p.h
@@ -0,0 +1,240 @@
+// 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
+
+//
+// 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 "qmediaplayer.h"
+#include "qmediatimerange.h"
+
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qwaitcondition.h>
+#include <QtMultimedia/qaudioformat.h>
+#include <QtMultimedia/qvideoframeformat.h>
+#include <qaudiodevice.h>
+#include <qtimer.h>
+#include "mfplayercontrol_p.h"
+#include <private/qcomptr_p.h>
+#include <evrhelpers_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QUrl;
+
+class SourceResolver;
+class MFVideoRendererControl;
+class MFPlayerControl;
+class MFPlayerService;
+class AudioSampleGrabberCallback;
+class MFTransform;
+
+class MFPlayerSession : public QObject, public IMFAsyncCallback
+{
+ Q_OBJECT
+ friend class SourceResolver;
+public:
+ MFPlayerSession(MFPlayerControl *playerControl = 0);
+
+ STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject) override;
+
+ STDMETHODIMP_(ULONG) AddRef(void) override;
+
+ STDMETHODIMP_(ULONG) Release(void) override;
+
+ STDMETHODIMP Invoke(IMFAsyncResult *pResult) override;
+
+ STDMETHODIMP GetParameters(DWORD *pdwFlags, DWORD *pdwQueue) override
+ {
+ Q_UNUSED(pdwFlags);
+ Q_UNUSED(pdwQueue);
+ return E_NOTIMPL;
+ }
+
+ void load(const QUrl &media, QIODevice *stream);
+ void stop(bool immediate = false);
+ void start();
+ void pause();
+
+ QMediaPlayer::MediaStatus status() const;
+ qint64 position();
+ void setPosition(qint64 position);
+ qreal playbackRate() const;
+ void setPlaybackRate(qreal rate);
+ float bufferProgress();
+ QMediaTimeRange availablePlaybackRanges();
+
+ void changeStatus(QMediaPlayer::MediaStatus newStatus);
+
+ void close();
+
+ void setAudioOutput(QPlatformAudioOutput *device);
+
+ QMediaMetaData metaData() const { return m_metaData; }
+
+ void setVideoSink(QVideoSink *sink);
+
+ void setActiveTrack(QPlatformMediaPlayer::TrackType type, int index);
+ int activeTrack(QPlatformMediaPlayer::TrackType type);
+ int trackCount(QPlatformMediaPlayer::TrackType);
+ QMediaMetaData trackMetaData(QPlatformMediaPlayer::TrackType type, int trackNumber);
+
+ void setPlayerControl(MFPlayerControl *playerControl) { m_playerControl = playerControl; }
+
+ void statusChanged() { if (m_playerControl) m_playerControl->handleStatusChanged(); }
+ void tracksChanged() { if (m_playerControl) m_playerControl->handleTracksChanged(); }
+ void audioAvailable() { if (m_playerControl) m_playerControl->handleAudioAvailable(); }
+ void videoAvailable() { if (m_playerControl) m_playerControl->handleVideoAvailable(); }
+ void durationUpdate(qint64 duration) { if (m_playerControl) m_playerControl->handleDurationUpdate(duration); }
+ void seekableUpdate(bool seekable) { if (m_playerControl) m_playerControl->handleSeekableUpdate(seekable); }
+ void error(QMediaPlayer::Error error, QString errorString, bool isFatal) { if (m_playerControl) m_playerControl->handleError(error, errorString, isFatal); }
+ void playbackRateChanged(qreal rate) { if (m_playerControl) m_playerControl->playbackRateChanged(rate); }
+ void bufferProgressChanged(float percentFilled) { if (m_playerControl) m_playerControl->bufferProgressChanged(percentFilled); }
+ void metaDataChanged() { if (m_playerControl) m_playerControl->metaDataChanged(); }
+ void positionChanged(qint64 position) { if (m_playerControl) m_playerControl->positionChanged(position); }
+
+public Q_SLOTS:
+ void setVolume(float volume);
+ void setMuted(bool muted);
+ void updateOutputRouting();
+
+Q_SIGNALS:
+ void sessionEvent(const ComPtr<IMFMediaEvent> &sessionEvent);
+
+private Q_SLOTS:
+ void handleMediaSourceReady();
+ void handleSessionEvent(const ComPtr<IMFMediaEvent> &sessionEvent);
+ void handleSourceError(long hr);
+ void timeout();
+
+private:
+ long m_cRef;
+ MFPlayerControl *m_playerControl = nullptr;
+ MFVideoRendererControl *m_videoRendererControl = nullptr;
+ 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;
+ bool m_updateRoutingOnStart = false;
+
+ enum Command
+ {
+ CmdNone = 0,
+ CmdStop,
+ CmdStart,
+ CmdPause,
+ CmdSeek,
+ CmdSeekResume,
+ CmdStartAndSeek
+ };
+
+ void clear();
+ void setPositionInternal(qint64 position, Command requestCmd);
+ void setPlaybackRateInternal(qreal rate);
+ void commitRateChange(qreal rate, BOOL isThin);
+ bool canScrub() const;
+ void scrub(bool enableScrub);
+ bool m_scrubbing;
+ float m_restoreRate;
+
+ ComPtr<SourceResolver> m_sourceResolver;
+ EventHandle m_hCloseEvent;
+ bool m_closing;
+
+ enum MediaType
+ {
+ Unknown = 0,
+ Audio = 1,
+ Video = 2,
+ };
+ DWORD m_mediaTypes;
+
+ enum PendingState
+ {
+ NoPending = 0,
+ CmdPending,
+ SeekPending,
+ };
+
+ struct SeekState
+ {
+ void setCommand(Command cmd) {
+ prevCmd = command;
+ command = cmd;
+ }
+ Command command;
+ Command prevCmd;
+ float rate; // Playback rate
+ BOOL isThin; // Thinned playback?
+ qint64 start; // Start position
+ };
+ SeekState m_state; // Current nominal state.
+ SeekState m_request; // Pending request.
+ PendingState m_pendingState;
+ float m_pendingRate;
+ void updatePendingCommands(Command command);
+
+ struct TrackInfo
+ {
+ QList<QMediaMetaData> metaData;
+ QList<int> nativeIndexes;
+ int currentIndex = -1;
+ TOPOID sourceNodeId = -1;
+ TOPOID outputNodeId = -1;
+ GUID format = GUID_NULL;
+ };
+ TrackInfo m_trackInfo[QPlatformMediaPlayer::NTrackTypes];
+
+ QMediaPlayer::MediaStatus m_status;
+ bool m_canScrub;
+ float m_volume = 1.;
+ bool m_muted = false;
+
+ QPlatformAudioOutput *m_audioOutput = nullptr;
+ QMediaMetaData m_metaData;
+
+ void setVolumeInternal(float volume);
+
+ bool createSession();
+ void setupPlaybackTopology(IMFMediaSource *source, IMFPresentationDescriptor *sourcePD);
+ bool getStreamInfo(IMFStreamDescriptor *stream, MFPlayerSession::MediaType *type, QString *name, QString *language, GUID *format) const;
+ ComPtr<IMFTopologyNode> addSourceNode(IMFTopology *topology, IMFMediaSource *source,
+ IMFPresentationDescriptor *presentationDesc,
+ IMFStreamDescriptor *streamDesc);
+ ComPtr<IMFTopologyNode> addOutputNode(MediaType mediaType, IMFTopology *topology, DWORD sinkID);
+
+ QAudioFormat audioFormatForMFMediaType(IMFMediaType *mediaType) const;
+
+ ComPtr<IMFTopology> insertMFT(const ComPtr<IMFTopology> &topology, TOPOID outputNodeId);
+ bool insertResizer(IMFTopology *topology);
+ void insertColorConverter(IMFTopology *topology, TOPOID outputNodeId);
+
+ QTimer m_signalPositionChangeTimer;
+ qint64 m_lastPosition = -1;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/windows/player/mfvideorenderercontrol.cpp b/src/plugins/multimedia/windows/player/mfvideorenderercontrol.cpp
new file mode 100644
index 000000000..7c79c3a8a
--- /dev/null
+++ b/src/plugins/multimedia/windows/player/mfvideorenderercontrol.cpp
@@ -0,0 +1,152 @@
+// 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"
+
+#include "evrcustompresenter_p.h"
+
+#include <private/qplatformvideosink_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class EVRCustomPresenterActivate : public MFAbstractActivate
+{
+public:
+ EVRCustomPresenterActivate(QVideoSink *sink);
+
+ 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;
+};
+
+
+MFVideoRendererControl::MFVideoRendererControl(QObject *parent)
+ : QObject(parent)
+{
+}
+
+MFVideoRendererControl::~MFVideoRendererControl()
+{
+ releaseActivate();
+}
+
+void MFVideoRendererControl::releaseActivate()
+{
+ if (m_sink)
+ m_sink->platformVideoSink()->setVideoFrame(QVideoFrame());
+
+ if (m_presenterActivate) {
+ m_presenterActivate->ShutdownObject();
+ m_presenterActivate->Release();
+ m_presenterActivate = NULL;
+ }
+
+ if (m_currentActivate) {
+ m_currentActivate->ShutdownObject();
+ m_currentActivate->Release();
+ }
+ m_currentActivate = NULL;
+}
+
+void MFVideoRendererControl::setSink(QVideoSink *sink)
+{
+ m_sink = sink;
+
+ if (m_presenterActivate)
+ m_presenterActivate->setSink(m_sink);
+}
+
+void MFVideoRendererControl::setCropRect(const QRect &cropRect)
+{
+ if (m_presenterActivate)
+ m_presenterActivate->setCropRect(cropRect);
+}
+
+IMFActivate* MFVideoRendererControl::createActivate()
+{
+ 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);
+ }
+ }
+
+ return m_currentActivate;
+}
+
+EVRCustomPresenterActivate::EVRCustomPresenterActivate(QVideoSink *sink)
+ : MFAbstractActivate()
+ , m_presenter(0)
+ , m_videoSink(sink)
+{ }
+
+HRESULT EVRCustomPresenterActivate::ActivateObject(REFIID riid, void **ppv)
+{
+ if (!ppv)
+ return E_INVALIDARG;
+ QMutexLocker locker(&m_mutex);
+ if (!m_presenter) {
+ m_presenter = new EVRCustomPresenter(m_videoSink);
+ m_presenter->setCropRect(m_cropRect);
+ }
+ return m_presenter->QueryInterface(riid, ppv);
+}
+
+HRESULT EVRCustomPresenterActivate::ShutdownObject()
+{
+ // The presenter does not implement IMFShutdown so
+ // this function is the same as DetachObject()
+ return DetachObject();
+}
+
+HRESULT EVRCustomPresenterActivate::DetachObject()
+{
+ QMutexLocker locker(&m_mutex);
+ if (m_presenter) {
+ m_presenter->Release();
+ m_presenter = 0;
+ }
+ return S_OK;
+}
+
+void EVRCustomPresenterActivate::setSink(QVideoSink *sink)
+{
+ QMutexLocker locker(&m_mutex);
+ if (m_videoSink == sink)
+ return;
+
+ m_videoSink = sink;
+
+ if (m_presenter)
+ m_presenter->setSink(sink);
+}
+
+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
new file mode 100644
index 000000000..ed5195240
--- /dev/null
+++ b/src/plugins/multimedia/windows/player/mfvideorenderercontrol_p.h
@@ -0,0 +1,47 @@
+// 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
+
+//
+// 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 <qrect.h>
+#include <mfobjects.h>
+
+QT_BEGIN_NAMESPACE
+class EVRCustomPresenterActivate;
+class QVideoSink;
+
+class MFVideoRendererControl : public QObject
+{
+public:
+ MFVideoRendererControl(QObject *parent = 0);
+ ~MFVideoRendererControl();
+
+ void setSink(QVideoSink *surface);
+ void setCropRect(const QRect &cropRect);
+
+ IMFActivate* createActivate();
+ void releaseActivate();
+
+private:
+ QPointer<QVideoSink> m_sink;
+ IMFActivate *m_currentActivate = nullptr;
+ EVRCustomPresenterActivate *m_presenterActivate = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/windows/qwindowsformatinfo.cpp b/src/plugins/multimedia/windows/qwindowsformatinfo.cpp
new file mode 100644
index 000000000..6ef1f7f7f
--- /dev/null
+++ b/src/plugins/multimedia/windows/qwindowsformatinfo.cpp
@@ -0,0 +1,187 @@
+// 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/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>
+using CheckedCodecs = QHash<QPair<T, QMediaFormat::ConversionMode>, bool>;
+
+bool isSupportedMFT(const GUID &category, const MFT_REGISTER_TYPE_INFO &type, QMediaFormat::ConversionMode mode)
+{
+ 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;
+}
+
+bool isSupportedCodec(QMediaFormat::AudioCodec codec, QMediaFormat::ConversionMode mode)
+{
+ return isSupportedMFT((mode == QMediaFormat::Encode) ? MFT_CATEGORY_AUDIO_ENCODER : MFT_CATEGORY_AUDIO_DECODER,
+ { MFMediaType_Audio, QWindowsMultimediaUtils::audioFormatForCodec(codec) },
+ mode);
+}
+
+bool isSupportedCodec(QMediaFormat::VideoCodec codec, QMediaFormat::ConversionMode mode)
+{
+ 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();
+
+ const bool supported = isSupportedCodec(codec, m);
+
+ checkedCodecs.insert(qMakePair(codec, m), supported);
+ return supported;
+}
+
+}
+
+static QList<QImageCapture::FileFormat> getImageFormatList()
+{
+ QList<QImageCapture::FileFormat> list;
+ const auto formats = QImageWriter::supportedImageFormats();
+
+ for (const auto &f : formats) {
+ auto format = QString::fromUtf8(f);
+ if (format.compare(QLatin1String("jpg"), Qt::CaseInsensitive) == 0)
+ list.append(QImageCapture::FileFormat::JPEG);
+ else if (format.compare(QLatin1String("png"), Qt::CaseInsensitive) == 0)
+ list.append(QImageCapture::FileFormat::PNG);
+ else if (format.compare(QLatin1String("webp"), Qt::CaseInsensitive) == 0)
+ list.append(QImageCapture::FileFormat::WebP);
+ else if (format.compare(QLatin1String("tiff"), Qt::CaseInsensitive) == 0)
+ list.append(QImageCapture::FileFormat::Tiff);
+ }
+
+ return list;
+}
+
+QWindowsFormatInfo::QWindowsFormatInfo()
+{
+ const QList<CodecMap> containerTable = {
+ { QMediaFormat::MPEG4,
+ { QMediaFormat::AudioCodec::AAC, QMediaFormat::AudioCodec::MP3, QMediaFormat::AudioCodec::ALAC, QMediaFormat::AudioCodec::AC3, QMediaFormat::AudioCodec::EAC3 },
+ { QMediaFormat::VideoCodec::H264, QMediaFormat::VideoCodec::H265, QMediaFormat::VideoCodec::MotionJPEG } },
+ { QMediaFormat::Matroska,
+ { QMediaFormat::AudioCodec::AAC, QMediaFormat::AudioCodec::MP3, QMediaFormat::AudioCodec::ALAC, QMediaFormat::AudioCodec::AC3, QMediaFormat::AudioCodec::EAC3, QMediaFormat::AudioCodec::FLAC, QMediaFormat::AudioCodec::Vorbis, QMediaFormat::AudioCodec::Opus },
+ { QMediaFormat::VideoCodec::H264, QMediaFormat::VideoCodec::H265, QMediaFormat::VideoCodec::VP8, QMediaFormat::VideoCodec::VP9, QMediaFormat::VideoCodec::MotionJPEG } },
+ { QMediaFormat::WebM,
+ { QMediaFormat::AudioCodec::Vorbis, QMediaFormat::AudioCodec::Opus },
+ { QMediaFormat::VideoCodec::VP8, QMediaFormat::VideoCodec::VP9 } },
+ { QMediaFormat::QuickTime,
+ { QMediaFormat::AudioCodec::AAC, QMediaFormat::AudioCodec::MP3, QMediaFormat::AudioCodec::ALAC, QMediaFormat::AudioCodec::AC3, QMediaFormat::AudioCodec::EAC3 },
+ { QMediaFormat::VideoCodec::H264, QMediaFormat::VideoCodec::H265, QMediaFormat::VideoCodec::MotionJPEG } },
+ { QMediaFormat::AAC,
+ { QMediaFormat::AudioCodec::AAC },
+ {} },
+ { QMediaFormat::MP3,
+ { QMediaFormat::AudioCodec::MP3 },
+ {} },
+ { QMediaFormat::FLAC,
+ { QMediaFormat::AudioCodec::FLAC },
+ {} },
+ { QMediaFormat::Mpeg4Audio,
+ { QMediaFormat::AudioCodec::AAC, QMediaFormat::AudioCodec::MP3, QMediaFormat::AudioCodec::ALAC, QMediaFormat::AudioCodec::AC3, QMediaFormat::AudioCodec::EAC3 },
+ {} },
+ { QMediaFormat::WMA,
+ { QMediaFormat::AudioCodec::WMA },
+ {} },
+ { QMediaFormat::WMV,
+ { QMediaFormat::AudioCodec::WMA },
+ { QMediaFormat::VideoCodec::WMV } }
+ };
+
+ const QSet<QMediaFormat::FileFormat> decoderFormats = {
+ QMediaFormat::MPEG4,
+ QMediaFormat::Matroska,
+ QMediaFormat::WebM,
+ QMediaFormat::QuickTime,
+ QMediaFormat::AAC,
+ QMediaFormat::MP3,
+ QMediaFormat::FLAC,
+ QMediaFormat::Mpeg4Audio,
+ QMediaFormat::WMA,
+ QMediaFormat::WMV,
+ };
+
+ const QSet<QMediaFormat::FileFormat> encoderFormats = {
+ QMediaFormat::MPEG4,
+ QMediaFormat::AAC,
+ QMediaFormat::MP3,
+ QMediaFormat::FLAC,
+ QMediaFormat::Mpeg4Audio,
+ QMediaFormat::WMA,
+ QMediaFormat::WMV,
+ };
+
+ CheckedCodecs<QMediaFormat::AudioCodec> checkedAudioCodecs;
+ CheckedCodecs<QMediaFormat::VideoCodec> checkedVideoCodecs;
+
+ 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)) {
+ auto m = codecMap;
+ if (ensureCodecs(m, QMediaFormat::Decode))
+ decoders.append(m);
+ }
+
+ if (encoderFormats.contains(codecMap.format)) {
+ auto m = codecMap;
+ if (ensureCodecs(m, QMediaFormat::Encode))
+ encoders.append(m);
+ }
+ }
+
+ imageFormats = getImageFormatList();
+}
+
+QWindowsFormatInfo::~QWindowsFormatInfo()
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/windows/qwindowsformatinfo_p.h b/src/plugins/multimedia/windows/qwindowsformatinfo_p.h
new file mode 100644
index 000000000..31e6dd986
--- /dev/null
+++ b/src/plugins/multimedia/windows/qwindowsformatinfo_p.h
@@ -0,0 +1,31 @@
+// 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
+
+//
+// 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/qplatformmediaformatinfo_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsFormatInfo : public QPlatformMediaFormatInfo
+{
+public:
+ QWindowsFormatInfo();
+ ~QWindowsFormatInfo();
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/windows/qwindowsintegration.cpp b/src/plugins/multimedia/windows/qwindowsintegration.cpp
new file mode 100644
index 000000000..1053f3c95
--- /dev/null
+++ b/src/plugins/multimedia/windows/qwindowsintegration.cpp
@@ -0,0 +1,96 @@
+// 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>
+#include <qwindowsformatinfo_p.h>
+#include <qwindowsmediacapture_p.h>
+#include <qwindowsimagecapture_p.h>
+#include <qwindowscamera_p.h>
+#include <qwindowsmediaencoder_p.h>
+#include <mfplayercontrol_p.h>
+#include <mfaudiodecodercontrol_p.h>
+#include <mfevrvideowindowcontrol_p.h>
+#include <private/qplatformmediaplugin_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsMediaPlugin : public QPlatformMediaPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QPlatformMediaPlugin_iid FILE "windows.json")
+
+public:
+ QWindowsMediaPlugin()
+ : QPlatformMediaPlugin()
+ {}
+
+ QPlatformMediaIntegration* create(const QString &name) override
+ {
+ if (name == u"windows")
+ return new QWindowsMediaIntegration;
+ return nullptr;
+ }
+};
+
+QWindowsMediaIntegration::QWindowsMediaIntegration()
+ : QPlatformMediaIntegration(QLatin1String("windows"))
+{
+ CoInitialize(NULL);
+ MFStartup(MF_VERSION);
+}
+
+QWindowsMediaIntegration::~QWindowsMediaIntegration()
+{
+ MFShutdown();
+ CoUninitialize();
+}
+
+QPlatformMediaFormatInfo *QWindowsMediaIntegration::createFormatInfo()
+{
+ return new QWindowsFormatInfo();
+}
+
+QPlatformVideoDevices *QWindowsMediaIntegration::createVideoDevices()
+{
+ return new QWindowsVideoDevices(this);
+}
+
+QMaybe<QPlatformMediaCaptureSession *> QWindowsMediaIntegration::createCaptureSession()
+{
+ return new QWindowsMediaCaptureService();
+}
+
+QMaybe<QPlatformAudioDecoder *> QWindowsMediaIntegration::createAudioDecoder(QAudioDecoder *decoder)
+{
+ return new MFAudioDecoderControl(decoder);
+}
+
+QMaybe<QPlatformMediaPlayer *> QWindowsMediaIntegration::createPlayer(QMediaPlayer *parent)
+{
+ return new MFPlayerControl(parent);
+}
+
+QMaybe<QPlatformCamera *> QWindowsMediaIntegration::createCamera(QCamera *camera)
+{
+ return new QWindowsCamera(camera);
+}
+
+QMaybe<QPlatformMediaRecorder *> QWindowsMediaIntegration::createRecorder(QMediaRecorder *recorder)
+{
+ return new QWindowsMediaEncoder(recorder);
+}
+
+QMaybe<QPlatformImageCapture *> QWindowsMediaIntegration::createImageCapture(QImageCapture *imageCapture)
+{
+ return new QWindowsImageCapture(imageCapture);
+}
+
+QMaybe<QPlatformVideoSink *> QWindowsMediaIntegration::createVideoSink(QVideoSink *sink)
+{
+ return new MFEvrVideoWindowControl(sink);
+}
+
+QT_END_NAMESPACE
+
+#include "qwindowsintegration.moc"
diff --git a/src/plugins/multimedia/windows/qwindowsintegration_p.h b/src/plugins/multimedia/windows/qwindowsintegration_p.h
new file mode 100644
index 000000000..29498fa42
--- /dev/null
+++ b/src/plugins/multimedia/windows/qwindowsintegration_p.h
@@ -0,0 +1,51 @@
+// 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
+
+//
+// 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 "qwindowsvideodevices_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsMediaDevices;
+class QWindowsFormatInfo;
+
+class QWindowsMediaIntegration : public QPlatformMediaIntegration
+{
+ Q_OBJECT
+public:
+ QWindowsMediaIntegration();
+ ~QWindowsMediaIntegration();
+
+ QMaybe<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;
+
+ QMaybe<QPlatformVideoSink *> createVideoSink(QVideoSink *sink) override;
+
+protected:
+ QPlatformMediaFormatInfo *createFormatInfo() override;
+
+ QPlatformVideoDevices *createVideoDevices() override;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/windows/qwindowsvideodevices.cpp b/src/plugins/multimedia/windows/qwindowsvideodevices.cpp
new file mode 100644
index 000000000..8e5081d3b
--- /dev/null
+++ b/src/plugins/multimedia/windows/qwindowsvideodevices.cpp
@@ -0,0 +1,228 @@
+// 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 <mfapi.h>
+#include <mfreadwrite.h>
+#include <mferror.h>
+
+QT_BEGIN_NAMESPACE
+
+LRESULT QT_WIN_CALLBACK deviceNotificationWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ if (message == WM_DEVICECHANGE) {
+ auto b = (PDEV_BROADCAST_HDR)lParam;
+ if (b && b->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
+ auto wmd = reinterpret_cast<QWindowsVideoDevices *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
+ if (wmd) {
+ if (wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE) {
+ emit wmd->videoInputsChanged();
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+static const auto windowClassName = TEXT("QWindowsMediaDevicesMessageWindow");
+
+static HWND createMessageOnlyWindow()
+{
+ WNDCLASSEX wx = {};
+ wx.cbSize = sizeof(WNDCLASSEX);
+ wx.lpfnWndProc = deviceNotificationWndProc;
+ wx.hInstance = GetModuleHandle(nullptr);
+ wx.lpszClassName = windowClassName;
+
+ if (!RegisterClassEx(&wx))
+ return nullptr;
+
+ auto hwnd = CreateWindowEx(0, windowClassName, TEXT("Message"),
+ 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr);
+ if (!hwnd) {
+ UnregisterClass(windowClassName, GetModuleHandle(nullptr));
+ return nullptr;
+ }
+
+ return hwnd;
+}
+
+QWindowsVideoDevices::QWindowsVideoDevices(QPlatformMediaIntegration *integration)
+ : QPlatformVideoDevices(integration)
+{
+ CoInitialize(nullptr);
+
+ m_videoDeviceMsgWindow = createMessageOnlyWindow();
+ if (m_videoDeviceMsgWindow) {
+ SetWindowLongPtr(m_videoDeviceMsgWindow, GWLP_USERDATA, (LONG_PTR)this);
+
+ DEV_BROADCAST_DEVICEINTERFACE di = {};
+ di.dbcc_size = sizeof(di);
+ di.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
+ di.dbcc_classguid = QMM_KSCATEGORY_VIDEO_CAMERA;
+
+ m_videoDeviceNotification =
+ RegisterDeviceNotification(m_videoDeviceMsgWindow, &di, DEVICE_NOTIFY_WINDOW_HANDLE);
+ if (!m_videoDeviceNotification) {
+ DestroyWindow(m_videoDeviceMsgWindow);
+ m_videoDeviceMsgWindow = nullptr;
+
+ UnregisterClass(windowClassName, GetModuleHandle(nullptr));
+ }
+ }
+
+ if (!m_videoDeviceNotification) {
+ qWarning() << "Video device change notification disabled";
+ }
+}
+
+QWindowsVideoDevices::~QWindowsVideoDevices()
+{
+ if (m_videoDeviceNotification) {
+ UnregisterDeviceNotification(m_videoDeviceNotification);
+ }
+
+ if (m_videoDeviceMsgWindow) {
+ DestroyWindow(m_videoDeviceMsgWindow);
+ UnregisterClass(windowClassName, GetModuleHandle(nullptr));
+ }
+
+ CoUninitialize();
+}
+
+static std::optional<QCameraFormat> createCameraFormat(IMFMediaType *mediaFormat)
+{
+ GUID subtype = GUID_NULL;
+ if (FAILED(mediaFormat->GetGUID(MF_MT_SUBTYPE, &subtype)))
+ return {};
+
+ auto pixelFormat = QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(subtype);
+ if (pixelFormat == QVideoFrameFormat::Format_Invalid)
+ return {};
+
+ 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)) {
+ 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;
+ }
+ }
+ }
+ 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
new file mode 100644
index 000000000..f8f5ed920
--- /dev/null
+++ b/src/plugins/multimedia/windows/qwindowsvideodevices_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 QWINDOWSVIDEODEVICES_H
+#define QWINDOWSVIDEODEVICES_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 <QtCore/qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+LRESULT QT_WIN_CALLBACK deviceNotificationWndProc(HWND, UINT, WPARAM, LPARAM);
+
+class QWindowsVideoDevices : public QPlatformVideoDevices
+{
+public:
+ QWindowsVideoDevices(QPlatformMediaIntegration *integration);
+ ~QWindowsVideoDevices();
+
+ QList<QCameraDevice> videoDevices() const override;
+
+private:
+ HWND m_videoDeviceMsgWindow = nullptr;
+ HDEVNOTIFY m_videoDeviceNotification = nullptr;
+
+ friend LRESULT QT_WIN_CALLBACK deviceNotificationWndProc(HWND, UINT, WPARAM, LPARAM);
+};
+
+
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/windows/sourceresolver.cpp b/src/plugins/multimedia/windows/sourceresolver.cpp
new file mode 100644
index 000000000..52fb024be
--- /dev/null
+++ b/src/plugins/multimedia/windows/sourceresolver.cpp
@@ -0,0 +1,294 @@
+// 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"
+#include <mferror.h>
+#include <nserror.h>
+#include <QtCore/qfile.h>
+#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,
+ and it will emit mediaSourceReady() when resolving is done. You can call SourceResolver::cancel to
+ stop the previous load operation if there is any.
+*/
+
+SourceResolver::SourceResolver()
+ : m_cRef(1)
+ , m_cancelCookie(0)
+ , m_sourceResolver(0)
+ , m_mediaSource(0)
+ , m_stream(0)
+{
+}
+
+SourceResolver::~SourceResolver()
+{
+ shutdown();
+ if (m_mediaSource) {
+ m_mediaSource->Release();
+ m_mediaSource = NULL;
+ }
+
+ if (m_cancelCookie)
+ m_cancelCookie->Release();
+ if (m_sourceResolver)
+ m_sourceResolver->Release();
+}
+
+STDMETHODIMP SourceResolver::QueryInterface(REFIID riid, LPVOID *ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+ if (riid == IID_IUnknown) {
+ *ppvObject = static_cast<IUnknown*>(this);
+ } else if (riid == IID_IMFAsyncCallback) {
+ *ppvObject = static_cast<IMFAsyncCallback*>(this);
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+STDMETHODIMP_(ULONG) SourceResolver::AddRef(void)
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+STDMETHODIMP_(ULONG) SourceResolver::Release(void)
+{
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ this->deleteLater();
+ return cRef;
+}
+
+HRESULT STDMETHODCALLTYPE SourceResolver::Invoke(IMFAsyncResult *pAsyncResult)
+{
+ QMutexLocker locker(&m_mutex);
+
+ if (!m_sourceResolver)
+ return S_OK;
+
+ MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;
+ IUnknown* pSource = NULL;
+ State *state = static_cast<State*>(pAsyncResult->GetStateNoAddRef());
+
+ HRESULT hr = S_OK;
+ if (state->fromStream())
+ hr = m_sourceResolver->EndCreateObjectFromByteStream(pAsyncResult, &ObjectType, &pSource);
+ else
+ hr = m_sourceResolver->EndCreateObjectFromURL(pAsyncResult, &ObjectType, &pSource);
+
+ if (state->sourceResolver() != m_sourceResolver) {
+ //This is a cancelled one
+ return S_OK;
+ }
+
+ if (m_cancelCookie) {
+ m_cancelCookie->Release();
+ m_cancelCookie = NULL;
+ }
+
+ if (FAILED(hr)) {
+ emit error(hr);
+ return S_OK;
+ }
+
+ if (m_mediaSource) {
+ m_mediaSource->Release();
+ m_mediaSource = NULL;
+ }
+
+ hr = pSource->QueryInterface(IID_PPV_ARGS(&m_mediaSource));
+ pSource->Release();
+ if (FAILED(hr)) {
+ emit error(hr);
+ return S_OK;
+ }
+
+ emit mediaSourceReady();
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE SourceResolver::GetParameters(DWORD*, DWORD*)
+{
+ return E_NOTIMPL;
+}
+
+void SourceResolver::load(const QUrl &url, QIODevice* stream)
+{
+ QMutexLocker locker(&m_mutex);
+ HRESULT hr = S_OK;
+ if (!m_sourceResolver)
+ hr = MFCreateSourceResolver(&m_sourceResolver);
+
+ if (m_stream) {
+ m_stream->Release();
+ m_stream = NULL;
+ }
+
+ if (FAILED(hr)) {
+ qWarning() << "Failed to create Source Resolver!";
+ emit error(hr);
+ } else if (stream) {
+ QString urlString = url.toString();
+ m_stream = new MFStream(stream, false);
+ hr = m_sourceResolver->BeginCreateObjectFromByteStream(
+ m_stream, urlString.isEmpty() ? 0 : reinterpret_cast<LPCWSTR>(urlString.utf16()),
+ MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE
+ , NULL, &m_cancelCookie, this, new State(m_sourceResolver, true));
+ if (FAILED(hr)) {
+ qWarning() << "Unsupported stream!";
+ emit error(hr);
+ }
+ } else {
+#ifdef DEBUG_MEDIAFOUNDATION
+ qDebug() << "loading :" << url;
+ qDebug() << "url path =" << url.path().mid(1);
+#endif
+#ifdef TEST_STREAMING
+ //Testing stream function
+ if (url.scheme() == QLatin1String("file")) {
+ stream = new QFile(url.path().mid(1));
+ if (stream->open(QIODevice::ReadOnly)) {
+ m_stream = new MFStream(stream, true);
+ hr = m_sourceResolver->BeginCreateObjectFromByteStream(
+ m_stream, reinterpret_cast<const OLECHAR *>(url.toString().utf16()),
+ MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE,
+ NULL, &m_cancelCookie, this, new State(m_sourceResolver, true));
+ if (FAILED(hr)) {
+ qWarning() << "Unsupported stream!";
+ emit error(hr);
+ }
+ } else {
+ delete stream;
+ emit error(QMediaPlayer::FormatError);
+ }
+ } else
+#endif
+ if (url.scheme() == QLatin1String("qrc")) {
+ // If the canonical URL refers to a Qt resource, open with QFile and use
+ // the stream playback capability to play.
+ stream = new QFile(QLatin1Char(':') + url.path());
+ if (stream->open(QIODevice::ReadOnly)) {
+ m_stream = new MFStream(stream, true);
+ hr = m_sourceResolver->BeginCreateObjectFromByteStream(
+ m_stream, reinterpret_cast<const OLECHAR *>(url.toString().utf16()),
+ MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE,
+ NULL, &m_cancelCookie, this, new State(m_sourceResolver, true));
+ if (FAILED(hr)) {
+ qWarning() << "Unsupported stream!";
+ emit error(hr);
+ }
+ } else {
+ delete stream;
+ emit error(QMediaPlayer::FormatError);
+ }
+ } else {
+ hr = m_sourceResolver->BeginCreateObjectFromURL(
+ reinterpret_cast<const OLECHAR *>(url.toString().utf16()),
+ MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE,
+ NULL, &m_cancelCookie, this, new State(m_sourceResolver, false));
+ if (FAILED(hr)) {
+ qWarning() << "Unsupported url scheme!";
+ emit error(hr);
+ }
+ }
+ }
+}
+
+void SourceResolver::cancel()
+{
+ QMutexLocker locker(&m_mutex);
+ if (m_cancelCookie) {
+ m_sourceResolver->CancelObjectCreation(m_cancelCookie);
+ m_cancelCookie->Release();
+ m_cancelCookie = NULL;
+ m_sourceResolver->Release();
+ m_sourceResolver = NULL;
+ }
+}
+
+void SourceResolver::shutdown()
+{
+ if (m_mediaSource) {
+ m_mediaSource->Shutdown();
+ m_mediaSource->Release();
+ m_mediaSource = NULL;
+ }
+
+ if (m_stream) {
+ m_stream->Release();
+ m_stream = NULL;
+ }
+}
+
+IMFMediaSource* SourceResolver::mediaSource() const
+{
+ return m_mediaSource;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+SourceResolver::State::State(IMFSourceResolver *sourceResolver, bool fromStream)
+ : m_cRef(0)
+ , m_sourceResolver(sourceResolver)
+ , m_fromStream(fromStream)
+{
+ sourceResolver->AddRef();
+}
+
+SourceResolver::State::~State()
+{
+ m_sourceResolver->Release();
+}
+
+STDMETHODIMP SourceResolver::State::QueryInterface(REFIID riid, LPVOID *ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+ if (riid == IID_IUnknown) {
+ *ppvObject = static_cast<IUnknown*>(this);
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+STDMETHODIMP_(ULONG) SourceResolver::State::AddRef(void)
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+STDMETHODIMP_(ULONG) SourceResolver::State::Release(void)
+{
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ delete this;
+ // For thread safety, return a temporary variable.
+ return cRef;
+}
+
+IMFSourceResolver* SourceResolver::State::sourceResolver() const
+{
+ return m_sourceResolver;
+}
+
+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
new file mode 100644
index 000000000..57ac6fc9d
--- /dev/null
+++ b/src/plugins/multimedia/windows/sourceresolver_p.h
@@ -0,0 +1,83 @@
+// 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
+
+//
+// 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 "mfstream_p.h"
+#include <QUrl>
+
+QT_BEGIN_NAMESPACE
+
+class SourceResolver: public QObject, public IMFAsyncCallback
+{
+ Q_OBJECT
+public:
+ SourceResolver();
+
+ ~SourceResolver();
+
+ STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject) override;
+ STDMETHODIMP_(ULONG) AddRef(void) override;
+ STDMETHODIMP_(ULONG) Release(void) override;
+
+ HRESULT STDMETHODCALLTYPE Invoke(IMFAsyncResult *pAsyncResult) override;
+
+ HRESULT STDMETHODCALLTYPE GetParameters(DWORD*, DWORD*) override;
+
+ void load(const QUrl &url, QIODevice* stream);
+
+ void cancel();
+
+ void shutdown();
+
+ IMFMediaSource* mediaSource() const;
+
+Q_SIGNALS:
+ void error(long hr);
+ void mediaSourceReady();
+
+private:
+ class State : public IUnknown
+ {
+ public:
+ State(IMFSourceResolver *sourceResolver, bool fromStream);
+ virtual ~State();
+
+ STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject) override;
+
+ STDMETHODIMP_(ULONG) AddRef(void) override;
+
+ STDMETHODIMP_(ULONG) Release(void) override;
+
+ IMFSourceResolver* sourceResolver() const;
+ bool fromStream() const;
+
+ private:
+ long m_cRef;
+ IMFSourceResolver *m_sourceResolver;
+ bool m_fromStream;
+ };
+
+ long m_cRef;
+ IUnknown *m_cancelCookie;
+ IMFSourceResolver *m_sourceResolver;
+ IMFMediaSource *m_mediaSource;
+ MFStream *m_stream;
+ QMutex m_mutex;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/windows/windows.json b/src/plugins/multimedia/windows/windows.json
new file mode 100644
index 000000000..05032c1b7
--- /dev/null
+++ b/src/plugins/multimedia/windows/windows.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "windows" ]
+}
diff --git a/src/plugins/opensles/opensles.json b/src/plugins/opensles/opensles.json
deleted file mode 100644
index a31d52107..000000000
--- a/src/plugins/opensles/opensles.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "Keys": ["default"]
-}
diff --git a/src/plugins/opensles/opensles.pro b/src/plugins/opensles/opensles.pro
deleted file mode 100644
index 1a554f53e..000000000
--- a/src/plugins/opensles/opensles.pro
+++ /dev/null
@@ -1,25 +0,0 @@
-TARGET = qtaudio_opensles
-QT += opengl multimedia-private core-private
-
-LIBS += -lOpenSLES
-
-HEADERS += \
- qopenslesplugin.h \
- qopenslesengine.h \
- qopenslesdeviceinfo.h \
- qopenslesaudioinput.h \
- qopenslesaudiooutput.h
-
-SOURCES += \
- qopenslesplugin.cpp \
- qopenslesengine.cpp \
- qopenslesdeviceinfo.cpp \
- qopenslesaudioinput.cpp \
- qopenslesaudiooutput.cpp
-
-OTHER_FILES += \
- opensles.json
-
-PLUGIN_TYPE = audio
-PLUGIN_CLASS_NAME = QOpenSLESPlugin
-load(qt_plugin)
diff --git a/src/plugins/opensles/qopenslesaudioinput.cpp b/src/plugins/opensles/qopenslesaudioinput.cpp
deleted file mode 100644
index 9105fca4c..000000000
--- a/src/plugins/opensles/qopenslesaudioinput.cpp
+++ /dev/null
@@ -1,548 +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 "qopenslesaudioinput.h"
-
-#include "qopenslesengine.h"
-#include <qbuffer.h>
-#include <private/qaudiohelpers_p.h>
-#include <qdebug.h>
-
-#ifdef ANDROID
-#include <SLES/OpenSLES_AndroidConfiguration.h>
-#include <QtCore/private/qjnihelpers_p.h>
-#include <QtCore/private/qjni_p.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-#define NUM_BUFFERS 2
-#define DEFAULT_PERIOD_TIME_MS 50
-#define MINIMUM_PERIOD_TIME_MS 5
-
-#ifdef ANDROID
-static bool hasRecordingPermission()
-{
- using namespace QtAndroidPrivate;
- if (androidSdkVersion() < 23)
- return true;
-
- const QString key(QLatin1String("android.permission.RECORD_AUDIO"));
- PermissionsResult res = checkPermission(key);
- if (res == PermissionsResult::Granted) // Permission already granted?
- return true;
-
- QJNIEnvironmentPrivate env;
- const auto &results = requestPermissionsSync(env, QStringList() << key);
- if (!results.contains(key)) {
- qWarning("No permission found for key: %s", qPrintable(key));
- return false;
- }
-
- if (results[key] == PermissionsResult::Denied) {
- qDebug("%s - Permission denied by user!", qPrintable(key));
- return false;
- }
-
- return true;
-}
-
-static void bufferQueueCallback(SLAndroidSimpleBufferQueueItf, void *context)
-#else
-static void bufferQueueCallback(SLBufferQueueItf, void *context)
-#endif
-{
- // Process buffer in main thread
- QMetaObject::invokeMethod(reinterpret_cast<QOpenSLESAudioInput*>(context), "processBuffer");
-}
-
-QOpenSLESAudioInput::QOpenSLESAudioInput(const QByteArray &device)
- : m_device(device)
- , m_engine(QOpenSLESEngine::instance())
- , m_recorderObject(0)
- , m_recorder(0)
- , m_bufferQueue(0)
- , m_pullMode(true)
- , m_processedBytes(0)
- , m_audioSource(0)
- , m_bufferIODevice(0)
- , m_errorState(QAudio::NoError)
- , m_deviceState(QAudio::StoppedState)
- , m_lastNotifyTime(0)
- , m_volume(1.0)
- , m_bufferSize(0)
- , m_periodSize(0)
- , m_intervalTime(1000)
- , m_buffers(new QByteArray[NUM_BUFFERS])
- , m_currentBuffer(0)
-{
-#ifdef ANDROID
- if (qstrcmp(device, QT_ANDROID_PRESET_CAMCORDER) == 0)
- m_recorderPreset = SL_ANDROID_RECORDING_PRESET_CAMCORDER;
- else if (qstrcmp(device, QT_ANDROID_PRESET_VOICE_RECOGNITION) == 0)
- m_recorderPreset = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
- else if (qstrcmp(device, QT_ANDROID_PRESET_VOICE_COMMUNICATION) == 0)
- m_recorderPreset = SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION;
- else
- m_recorderPreset = SL_ANDROID_RECORDING_PRESET_GENERIC;
-#endif
-}
-
-QOpenSLESAudioInput::~QOpenSLESAudioInput()
-{
- if (m_recorderObject)
- (*m_recorderObject)->Destroy(m_recorderObject);
- delete[] m_buffers;
-}
-
-QAudio::Error QOpenSLESAudioInput::error() const
-{
- return m_errorState;
-}
-
-QAudio::State QOpenSLESAudioInput::state() const
-{
- return m_deviceState;
-}
-
-void QOpenSLESAudioInput::setFormat(const QAudioFormat &format)
-{
- if (m_deviceState == QAudio::StoppedState)
- m_format = format;
-}
-
-QAudioFormat QOpenSLESAudioInput::format() const
-{
- return m_format;
-}
-
-void QOpenSLESAudioInput::start(QIODevice *device)
-{
- if (m_deviceState != QAudio::StoppedState)
- stopRecording();
-
- if (!m_pullMode && m_bufferIODevice) {
- m_bufferIODevice->close();
- delete m_bufferIODevice;
- m_bufferIODevice = 0;
- }
-
- m_pullMode = true;
- m_audioSource = device;
-
- if (startRecording()) {
- m_deviceState = QAudio::ActiveState;
- } else {
- m_deviceState = QAudio::StoppedState;
- Q_EMIT errorChanged(m_errorState);
- }
-
- Q_EMIT stateChanged(m_deviceState);
-}
-
-QIODevice *QOpenSLESAudioInput::start()
-{
- if (m_deviceState != QAudio::StoppedState)
- stopRecording();
-
- m_audioSource = 0;
-
- if (!m_pullMode && m_bufferIODevice) {
- m_bufferIODevice->close();
- delete m_bufferIODevice;
- }
-
- m_pullMode = false;
- m_pushBuffer.clear();
- m_bufferIODevice = new QBuffer(&m_pushBuffer);
- m_bufferIODevice->open(QIODevice::ReadOnly);
-
- if (startRecording()) {
- m_deviceState = QAudio::IdleState;
- } else {
- m_deviceState = QAudio::StoppedState;
- Q_EMIT errorChanged(m_errorState);
- m_bufferIODevice->close();
- delete m_bufferIODevice;
- m_bufferIODevice = 0;
- }
-
- Q_EMIT stateChanged(m_deviceState);
- return m_bufferIODevice;
-}
-
-bool QOpenSLESAudioInput::startRecording()
-{
- if (!hasRecordingPermission())
- return false;
-
- m_processedBytes = 0;
- m_clockStamp.restart();
- m_lastNotifyTime = 0;
-
- SLresult result;
-
- // configure audio source
- SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
- SL_DEFAULTDEVICEID_AUDIOINPUT, NULL };
- SLDataSource audioSrc = { &loc_dev, NULL };
-
- // configure audio sink
-#ifdef ANDROID
- SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
- NUM_BUFFERS };
-#else
- SLDataLocator_BufferQueue loc_bq = { SL_DATALOCATOR_BUFFERQUEUE, NUM_BUFFERS };
-#endif
-
- SLDataFormat_PCM format_pcm = QOpenSLESEngine::audioFormatToSLFormatPCM(m_format);
- SLDataSink audioSnk = { &loc_bq, &format_pcm };
-
- // create audio recorder
- // (requires the RECORD_AUDIO permission)
-#ifdef ANDROID
- const SLInterfaceID id[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION };
- const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
-#else
- const SLInterfaceID id[1] = { SL_IID_BUFFERQUEUE };
- const SLboolean req[1] = { SL_BOOLEAN_TRUE };
-#endif
-
- result = (*m_engine->slEngine())->CreateAudioRecorder(m_engine->slEngine(), &m_recorderObject,
- &audioSrc, &audioSnk,
- sizeof(req) / sizeof(SLboolean), id, req);
- if (result != SL_RESULT_SUCCESS) {
- m_errorState = QAudio::OpenError;
- return false;
- }
-
-#ifdef ANDROID
- // configure recorder source
- SLAndroidConfigurationItf configItf;
- result = (*m_recorderObject)->GetInterface(m_recorderObject, SL_IID_ANDROIDCONFIGURATION,
- &configItf);
- if (result != SL_RESULT_SUCCESS) {
- m_errorState = QAudio::OpenError;
- return false;
- }
-
- result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET,
- &m_recorderPreset, sizeof(SLuint32));
-
- SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_NONE;
- SLuint32 presetSize = 2*sizeof(SLuint32); // intentionally too big
- result = (*configItf)->GetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET,
- &presetSize, (void*)&presetValue);
-
- if (result != SL_RESULT_SUCCESS || presetValue == SL_ANDROID_RECORDING_PRESET_NONE) {
- m_errorState = QAudio::OpenError;
- return false;
- }
-#endif
-
- // realize the audio recorder
- result = (*m_recorderObject)->Realize(m_recorderObject, SL_BOOLEAN_FALSE);
- if (result != SL_RESULT_SUCCESS) {
- m_errorState = QAudio::OpenError;
- return false;
- }
-
- // get the record interface
- result = (*m_recorderObject)->GetInterface(m_recorderObject, SL_IID_RECORD, &m_recorder);
- if (result != SL_RESULT_SUCCESS) {
- m_errorState = QAudio::FatalError;
- return false;
- }
-
- // get the buffer queue interface
-#ifdef ANDROID
- SLInterfaceID bufferqueueItfID = SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
-#else
- SLInterfaceID bufferqueueItfID = SL_IID_BUFFERQUEUE;
-#endif
- result = (*m_recorderObject)->GetInterface(m_recorderObject, bufferqueueItfID, &m_bufferQueue);
- if (result != SL_RESULT_SUCCESS) {
- m_errorState = QAudio::FatalError;
- return false;
- }
-
- // register callback on the buffer queue
- result = (*m_bufferQueue)->RegisterCallback(m_bufferQueue, bufferQueueCallback, this);
- if (result != SL_RESULT_SUCCESS) {
- m_errorState = QAudio::FatalError;
- return false;
- }
-
- if (m_bufferSize <= 0) {
- m_bufferSize = m_format.bytesForDuration(DEFAULT_PERIOD_TIME_MS * 1000);
- } else {
- int minimumBufSize = m_format.bytesForDuration(MINIMUM_PERIOD_TIME_MS * 1000);
- if (m_bufferSize < minimumBufSize)
- m_bufferSize = minimumBufSize;
- }
-
- m_periodSize = m_bufferSize;
-
- // enqueue empty buffers to be filled by the recorder
- for (int i = 0; i < NUM_BUFFERS; ++i) {
- m_buffers[i].resize(m_periodSize);
-
- result = (*m_bufferQueue)->Enqueue(m_bufferQueue, m_buffers[i].data(), m_periodSize);
- if (result != SL_RESULT_SUCCESS) {
- m_errorState = QAudio::FatalError;
- return false;
- }
- }
-
- // start recording
- result = (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_RECORDING);
- if (result != SL_RESULT_SUCCESS) {
- m_errorState = QAudio::FatalError;
- return false;
- }
-
- m_errorState = QAudio::NoError;
-
- return true;
-}
-
-void QOpenSLESAudioInput::stop()
-{
- if (m_deviceState == QAudio::StoppedState)
- return;
-
- m_deviceState = QAudio::StoppedState;
-
- stopRecording();
-
- m_errorState = QAudio::NoError;
- Q_EMIT stateChanged(m_deviceState);
-}
-
-void QOpenSLESAudioInput::stopRecording()
-{
- flushBuffers();
-
- (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_STOPPED);
- (*m_bufferQueue)->Clear(m_bufferQueue);
-
- (*m_recorderObject)->Destroy(m_recorderObject);
- m_recorderObject = 0;
-
- for (int i = 0; i < NUM_BUFFERS; ++i)
- m_buffers[i].clear();
- m_currentBuffer = 0;
-
- if (!m_pullMode && m_bufferIODevice) {
- m_bufferIODevice->close();
- delete m_bufferIODevice;
- m_bufferIODevice = 0;
- m_pushBuffer.clear();
- }
-}
-
-void QOpenSLESAudioInput::suspend()
-{
- if (m_deviceState == QAudio::ActiveState) {
- m_deviceState = QAudio::SuspendedState;
- emit stateChanged(m_deviceState);
-
- (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_PAUSED);
- }
-}
-
-void QOpenSLESAudioInput::resume()
-{
- if (m_deviceState == QAudio::SuspendedState || m_deviceState == QAudio::IdleState) {
- (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_RECORDING);
-
- m_deviceState = QAudio::ActiveState;
- emit stateChanged(m_deviceState);
- }
-}
-
-void QOpenSLESAudioInput::processBuffer()
-{
- if (m_deviceState == QAudio::StoppedState || m_deviceState == QAudio::SuspendedState)
- return;
-
- if (m_deviceState != QAudio::ActiveState) {
- m_errorState = QAudio::NoError;
- m_deviceState = QAudio::ActiveState;
- emit stateChanged(m_deviceState);
- }
-
- QByteArray *processedBuffer = &m_buffers[m_currentBuffer];
- writeDataToDevice(processedBuffer->constData(), processedBuffer->size());
-
- // Re-enqueue the buffer
- SLresult result = (*m_bufferQueue)->Enqueue(m_bufferQueue,
- processedBuffer->data(),
- processedBuffer->size());
-
- m_currentBuffer = (m_currentBuffer + 1) % NUM_BUFFERS;
-
- // If the buffer queue is empty (shouldn't happen), stop recording.
-#ifdef ANDROID
- SLAndroidSimpleBufferQueueState state;
-#else
- SLBufferQueueState state;
-#endif
- result = (*m_bufferQueue)->GetState(m_bufferQueue, &state);
- if (result != SL_RESULT_SUCCESS || state.count == 0) {
- stop();
- m_errorState = QAudio::FatalError;
- Q_EMIT errorChanged(m_errorState);
- }
-}
-
-void QOpenSLESAudioInput::writeDataToDevice(const char *data, int size)
-{
- m_processedBytes += size;
-
- QByteArray outData;
-
- // Apply volume
- if (m_volume < 1.0f) {
- outData.resize(size);
- QAudioHelperInternal::qMultiplySamples(m_volume, m_format, data, outData.data(), size);
- } else {
- outData.append(data, size);
- }
-
- if (m_pullMode) {
- // write buffer to the QIODevice
- if (m_audioSource->write(outData) < 0) {
- stop();
- m_errorState = QAudio::IOError;
- Q_EMIT errorChanged(m_errorState);
- }
- } else {
- // emits readyRead() so user will call read() on QIODevice to get some audio data
- if (m_bufferIODevice != 0) {
- m_pushBuffer.append(outData);
- Q_EMIT m_bufferIODevice->readyRead();
- }
- }
-
- // Send notify signal if needed
- qint64 processedMsecs = processedUSecs() / 1000;
- if (m_intervalTime && (processedMsecs - m_lastNotifyTime) >= m_intervalTime) {
- Q_EMIT notify();
- m_lastNotifyTime = processedMsecs;
- }
-}
-
-void QOpenSLESAudioInput::flushBuffers()
-{
- SLmillisecond recorderPos;
- (*m_recorder)->GetPosition(m_recorder, &recorderPos);
- qint64 devicePos = processedUSecs();
-
- qint64 delta = recorderPos * 1000 - devicePos;
-
- if (delta > 0) {
- const int writeSize = qMin(m_buffers[m_currentBuffer].size(),
- m_format.bytesForDuration(delta));
- writeDataToDevice(m_buffers[m_currentBuffer].constData(), writeSize);
- }
-}
-
-int QOpenSLESAudioInput::bytesReady() const
-{
- if (m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::SuspendedState)
- return m_bufferIODevice ? m_bufferIODevice->bytesAvailable() : m_periodSize;
-
- return 0;
-}
-
-void QOpenSLESAudioInput::setBufferSize(int value)
-{
- m_bufferSize = value;
-}
-
-int QOpenSLESAudioInput::bufferSize() const
-{
- return m_bufferSize;
-}
-
-int QOpenSLESAudioInput::periodSize() const
-{
- return m_periodSize;
-}
-
-void QOpenSLESAudioInput::setNotifyInterval(int ms)
-{
- m_intervalTime = qMax(0, ms);
-}
-
-int QOpenSLESAudioInput::notifyInterval() const
-{
- return m_intervalTime;
-}
-
-qint64 QOpenSLESAudioInput::processedUSecs() const
-{
- return m_format.durationForBytes(m_processedBytes);
-}
-
-qint64 QOpenSLESAudioInput::elapsedUSecs() const
-{
- if (m_deviceState == QAudio::StoppedState)
- return 0;
-
- return m_clockStamp.elapsed() * qint64(1000);
-}
-
-void QOpenSLESAudioInput::setVolume(qreal vol)
-{
- m_volume = vol;
-}
-
-qreal QOpenSLESAudioInput::volume() const
-{
- return m_volume;
-}
-
-void QOpenSLESAudioInput::reset()
-{
- stop();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/opensles/qopenslesaudioinput.h b/src/plugins/opensles/qopenslesaudioinput.h
deleted file mode 100644
index 730c58bd6..000000000
--- a/src/plugins/opensles/qopenslesaudioinput.h
+++ /dev/null
@@ -1,133 +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 QOPENSLESAUDIOINPUT_H
-#define QOPENSLESAUDIOINPUT_H
-
-#include <qaudiosystem.h>
-#include <QElapsedTimer>
-#include <SLES/OpenSLES.h>
-
-#ifdef ANDROID
-#include <SLES/OpenSLES_Android.h>
-
-#define QT_ANDROID_PRESET_MIC "mic"
-#define QT_ANDROID_PRESET_CAMCORDER "camcorder"
-#define QT_ANDROID_PRESET_VOICE_RECOGNITION "voicerecognition"
-#define QT_ANDROID_PRESET_VOICE_COMMUNICATION "voicecommunication"
-
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class QOpenSLESEngine;
-class QIODevice;
-class QBuffer;
-
-class QOpenSLESAudioInput : public QAbstractAudioInput
-{
- Q_OBJECT
-
-public:
- QOpenSLESAudioInput(const QByteArray &device);
- ~QOpenSLESAudioInput();
-
- void start(QIODevice *device);
- QIODevice *start();
- void stop();
- void reset();
- void suspend();
- void resume();
- int bytesReady() const;
- int periodSize() const;
- void setBufferSize(int value);
- int bufferSize() const;
- void setNotifyInterval(int milliSeconds);
- int notifyInterval() const;
- qint64 processedUSecs() const;
- qint64 elapsedUSecs() const;
- QAudio::Error error() const;
- QAudio::State state() const;
- void setFormat(const QAudioFormat &format);
- QAudioFormat format() const;
-
- void setVolume(qreal volume);
- qreal volume() const;
-
-public Q_SLOTS:
- void processBuffer();
-
-private:
- bool startRecording();
- void stopRecording();
- void writeDataToDevice(const char *data, int size);
- void flushBuffers();
-
- QByteArray m_device;
- QOpenSLESEngine *m_engine;
- SLObjectItf m_recorderObject;
- SLRecordItf m_recorder;
-#ifdef ANDROID
- SLuint32 m_recorderPreset;
- SLAndroidSimpleBufferQueueItf m_bufferQueue;
-#else
- SLBufferQueueItf m_bufferQueue;
-#endif
-
- bool m_pullMode;
- qint64 m_processedBytes;
- QIODevice *m_audioSource;
- QBuffer *m_bufferIODevice;
- QByteArray m_pushBuffer;
- QAudioFormat m_format;
- QAudio::Error m_errorState;
- QAudio::State m_deviceState;
- QElapsedTimer m_clockStamp;
- qint64 m_lastNotifyTime;
- qreal m_volume;
- int m_bufferSize;
- int m_periodSize;
- int m_intervalTime;
- QByteArray *m_buffers;
- int m_currentBuffer;
-};
-
-QT_END_NAMESPACE
-
-#endif // QOPENSLESAUDIOINPUT_H
diff --git a/src/plugins/opensles/qopenslesaudiooutput.cpp b/src/plugins/opensles/qopenslesaudiooutput.cpp
deleted file mode 100644
index 381ce0ec2..000000000
--- a/src/plugins/opensles/qopenslesaudiooutput.cpp
+++ /dev/null
@@ -1,733 +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 "qopenslesaudiooutput.h"
-#include "qopenslesengine.h"
-#include <QDebug>
-#include <qmath.h>
-
-#ifdef ANDROID
-#include <SLES/OpenSLES_Android.h>
-#include <SLES/OpenSLES_AndroidConfiguration.h>
-#endif // ANDROID
-
-#define BUFFER_COUNT 2
-
-QT_BEGIN_NAMESPACE
-
-static inline void openSlDebugInfo()
-{
- const QAudioFormat &format = QAudioDeviceInfo::defaultOutputDevice().preferredFormat();
- qDebug() << "======= OpenSL ES Device info ======="
- << "\nSupports low-latency playback: " << (QOpenSLESEngine::supportsLowLatency() ? "YES" : "NO")
- << "\nPreferred sample rate: " << QOpenSLESEngine::getOutputValue(QOpenSLESEngine::SampleRate, -1)
- << "\nFrames per buffer: " << QOpenSLESEngine::getOutputValue(QOpenSLESEngine::FramesPerBuffer, -1)
- << "\nPreferred Format: " << format
- << "\nLow-latency buffer size: " << QOpenSLESEngine::getLowLatencyBufferSize(format)
- << "\nDefault buffer size: " << QOpenSLESEngine::getDefaultBufferSize(format);
-}
-
-QMap<QString, qint32> QOpenSLESAudioOutput::m_categories;
-
-QOpenSLESAudioOutput::QOpenSLESAudioOutput(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_notifyInterval(1000),
- m_periodSize(0),
- m_elapsedTime(0),
- m_processedBytes(0),
- m_availableBuffers(BUFFER_COUNT),
- m_eventMask(SL_PLAYEVENT_HEADATEND),
- m_startRequiresInit(true)
-{
-#ifndef ANDROID
- m_streamType = -1;
-#else
- m_streamType = SL_ANDROID_STREAM_MEDIA;
- m_category = QLatin1String("media");
-#endif // ANDROID
-}
-
-QOpenSLESAudioOutput::~QOpenSLESAudioOutput()
-{
- destroyPlayer();
-}
-
-QAudio::Error QOpenSLESAudioOutput::error() const
-{
- return m_error;
-}
-
-QAudio::State QOpenSLESAudioOutput::state() const
-{
- return m_state;
-}
-
-void QOpenSLESAudioOutput::start(QIODevice *device)
-{
- Q_ASSERT(device);
-
- if (m_state != QAudio::StoppedState)
- stop();
-
- if (!preparePlayer())
- return;
-
- m_pullMode = true;
- m_audioSource = device;
- m_nextBuffer = 0;
- m_processedBytes = 0;
- m_availableBuffers = BUFFER_COUNT;
- setState(QAudio::ActiveState);
- setError(QAudio::NoError);
-
- // Attempt to fill buffers first.
- for (int i = 0; i != BUFFER_COUNT; ++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,
- m_buffers + index,
- readSize)) {
- setError(QAudio::FatalError);
- destroyPlayer();
- return;
- }
- m_processedBytes += readSize;
- }
-
- if (m_processedBytes < 1)
- onEOSEvent();
-
- // Change the state to playing.
- // We need to do this after filling the buffers or processedBytes might get corrupted.
- startPlayer();
-}
-
-QIODevice *QOpenSLESAudioOutput::start()
-{
- if (m_state != QAudio::StoppedState)
- stop();
-
- if (!preparePlayer())
- return nullptr;
-
- m_pullMode = false;
- m_processedBytes = 0;
- m_availableBuffers = BUFFER_COUNT;
- m_audioSource = new SLIODevicePrivate(this);
- m_audioSource->open(QIODevice::WriteOnly | QIODevice::Unbuffered);
-
- // Change the state to playing
- startPlayer();
-
- setState(QAudio::IdleState);
- return m_audioSource;
-}
-
-void QOpenSLESAudioOutput::stop()
-{
- if (m_state == QAudio::StoppedState)
- return;
-
- stopPlayer();
- setError(QAudio::NoError);
-}
-
-int QOpenSLESAudioOutput::bytesFree() const
-{
- if (m_state != QAudio::ActiveState && m_state != QAudio::IdleState)
- return 0;
-
- return m_availableBuffers.loadAcquire() ? m_bufferSize : 0;
-}
-
-int QOpenSLESAudioOutput::periodSize() const
-{
- return m_periodSize;
-}
-
-void QOpenSLESAudioOutput::setBufferSize(int value)
-{
- if (m_state != QAudio::StoppedState)
- return;
-
- m_startRequiresInit = true;
- m_bufferSize = value;
-}
-
-int QOpenSLESAudioOutput::bufferSize() const
-{
- return m_bufferSize;
-}
-
-void QOpenSLESAudioOutput::setNotifyInterval(int ms)
-{
- const int newInterval = ms > 0 ? ms : 0;
-
- if (newInterval == m_notifyInterval)
- return;
-
- const SLuint32 newEvenMask = newInterval == 0 ? m_eventMask & ~SL_PLAYEVENT_HEADATNEWPOS
- : m_eventMask & SL_PLAYEVENT_HEADATNEWPOS;
-
- if (m_state == QAudio::StoppedState) {
- m_eventMask = newEvenMask;
- m_notifyInterval = newInterval;
- return;
- }
-
- if (newEvenMask != m_eventMask
- && SL_RESULT_SUCCESS != (*m_playItf)->SetCallbackEventsMask(m_playItf, newEvenMask)) {
- return;
- }
-
- m_eventMask = newEvenMask;
-
- if (newInterval && SL_RESULT_SUCCESS != (*m_playItf)->SetPositionUpdatePeriod(m_playItf,
- newInterval)) {
- return;
- }
-
- m_notifyInterval = newInterval;
-}
-
-int QOpenSLESAudioOutput::notifyInterval() const
-{
- return m_notifyInterval;
-}
-
-qint64 QOpenSLESAudioOutput::processedUSecs() const
-{
- if (m_state == QAudio::IdleState || m_state == QAudio::SuspendedState)
- return m_format.durationForBytes(m_processedBytes);
-
- SLmillisecond processMSec = 0;
- if (m_playItf)
- (*m_playItf)->GetPosition(m_playItf, &processMSec);
-
- return processMSec * 1000;
-}
-
-void QOpenSLESAudioOutput::resume()
-{
- if (m_state != QAudio::SuspendedState)
- return;
-
- if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) {
- setError(QAudio::FatalError);
- destroyPlayer();
- return;
- }
-
- setState(m_pullMode ? QAudio::ActiveState : QAudio::IdleState);
- setError(QAudio::NoError);
-}
-
-void QOpenSLESAudioOutput::setFormat(const QAudioFormat &format)
-{
- m_startRequiresInit = true;
- m_format = format;
-}
-
-QAudioFormat QOpenSLESAudioOutput::format() const
-{
- return m_format;
-}
-
-void QOpenSLESAudioOutput::suspend()
-{
- if (m_state != QAudio::ActiveState && m_state != QAudio::IdleState)
- return;
-
- if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PAUSED)) {
- setError(QAudio::FatalError);
- destroyPlayer();
- return;
- }
-
- setState(QAudio::SuspendedState);
- setError(QAudio::NoError);
-}
-
-qint64 QOpenSLESAudioOutput::elapsedUSecs() const
-{
- if (m_state == QAudio::StoppedState)
- return 0;
-
- return m_clockStamp.elapsed() * qint64(1000);
-}
-
-void QOpenSLESAudioOutput::reset()
-{
- destroyPlayer();
-}
-
-void QOpenSLESAudioOutput::setVolume(qreal vol)
-{
- m_volume = qBound(qreal(0.0), vol, qreal(1.0));
- const SLmillibel newVolume = adjustVolume(m_volume);
- if (m_volumeItf && SL_RESULT_SUCCESS != (*m_volumeItf)->SetVolumeLevel(m_volumeItf, newVolume))
- qWarning() << "Unable to change volume";
-}
-
-qreal QOpenSLESAudioOutput::volume() const
-{
- return m_volume;
-}
-
-void QOpenSLESAudioOutput::setCategory(const QString &category)
-{
-#ifndef ANDROID
- Q_UNUSED(category);
-#else
- if (m_categories.isEmpty()) {
- m_categories.insert(QLatin1String("voice"), SL_ANDROID_STREAM_VOICE);
- m_categories.insert(QLatin1String("system"), SL_ANDROID_STREAM_SYSTEM);
- m_categories.insert(QLatin1String("ring"), SL_ANDROID_STREAM_RING);
- m_categories.insert(QLatin1String("media"), SL_ANDROID_STREAM_MEDIA);
- m_categories.insert(QLatin1String("alarm"), SL_ANDROID_STREAM_ALARM);
- m_categories.insert(QLatin1String("notification"), SL_ANDROID_STREAM_NOTIFICATION);
- }
-
- const SLint32 streamType = m_categories.value(category, -1);
- if (streamType == -1) {
- qWarning() << "Unknown category" << category
- << ", available categories are:" << m_categories.keys()
- << ". Defaulting to category \"media\"";
- return;
- }
-
- m_startRequiresInit = true;
- m_streamType = streamType;
- m_category = category;
-#endif // ANDROID
-}
-
-QString QOpenSLESAudioOutput::category() const
-{
- return m_category;
-}
-
-void QOpenSLESAudioOutput::onEOSEvent()
-{
- if (m_state != QAudio::ActiveState)
- return;
-
- SLBufferQueueState state;
- if (SL_RESULT_SUCCESS != (*m_bufferQueueItf)->GetState(m_bufferQueueItf, &state))
- return;
-
- if (state.count > 0)
- return;
-
- setState(QAudio::IdleState);
- setError(QAudio::UnderrunError);
-}
-
-void QOpenSLESAudioOutput::onBytesProcessed(qint64 bytes)
-{
- m_processedBytes += bytes;
-}
-
-void QOpenSLESAudioOutput::bufferAvailable(quint32 count, quint32 playIndex)
-{
- 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)
- QMetaObject::invokeMethod(this, "onEOSEvent", Qt::QueuedConnection);
-
- return;
- }
-
- // We're in pull mode.
- const int index = m_nextBuffer * m_bufferSize;
- const qint64 readSize = m_audioSource->read(m_buffers + index, m_bufferSize);
-
- if (readSize < 1) {
- QMetaObject::invokeMethod(this, "onEOSEvent", Qt::QueuedConnection);
- return;
- }
-
-
- if (SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Enqueue(m_bufferQueueItf,
- m_buffers + index,
- readSize)) {
- setError(QAudio::FatalError);
- destroyPlayer();
- return;
- }
-
- m_nextBuffer = (m_nextBuffer + 1) % BUFFER_COUNT;
- QMetaObject::invokeMethod(this, "onBytesProcessed", Qt::QueuedConnection, Q_ARG(qint64, readSize));
-}
-
-void QOpenSLESAudioOutput::playCallback(SLPlayItf player, void *ctx, SLuint32 event)
-{
- Q_UNUSED(player);
- QOpenSLESAudioOutput *audioOutput = reinterpret_cast<QOpenSLESAudioOutput *>(ctx);
- if (event & SL_PLAYEVENT_HEADATEND)
- QMetaObject::invokeMethod(audioOutput, "onEOSEvent", Qt::QueuedConnection);
- if (event & SL_PLAYEVENT_HEADATNEWPOS)
- Q_EMIT audioOutput->notify();
-
-}
-
-void QOpenSLESAudioOutput::bufferQueueCallback(SLBufferQueueItf bufferQueue, void *ctx)
-{
- SLBufferQueueState state;
- (*bufferQueue)->GetState(bufferQueue, &state);
- QOpenSLESAudioOutput *audioOutput = reinterpret_cast<QOpenSLESAudioOutput *>(ctx);
- audioOutput->bufferAvailable(state.count, state.playIndex);
-}
-
-bool QOpenSLESAudioOutput::preparePlayer()
-{
- if (m_startRequiresInit)
- destroyPlayer();
- else
- return true;
-
- SLEngineItf engine = QOpenSLESEngine::instance()->slEngine();
- if (!engine) {
- qWarning() << "No engine";
- setError(QAudio::FatalError);
- return false;
- }
-
- SLDataLocator_BufferQueue bufferQueueLocator = { SL_DATALOCATOR_BUFFERQUEUE, BUFFER_COUNT };
- SLDataFormat_PCM pcmFormat = QOpenSLESEngine::audioFormatToSLFormatPCM(m_format);
-
- SLDataSource audioSrc = { &bufferQueueLocator, &pcmFormat };
-
- // OutputMix
- if (SL_RESULT_SUCCESS != (*engine)->CreateOutputMix(engine,
- &m_outputMixObject,
- 0,
- nullptr,
- nullptr)) {
- qWarning() << "Unable to create output mix";
- setError(QAudio::FatalError);
- return false;
- }
-
- if (SL_RESULT_SUCCESS != (*m_outputMixObject)->Realize(m_outputMixObject, SL_BOOLEAN_FALSE)) {
- qWarning() << "Unable to initialize output mix";
- setError(QAudio::FatalError);
- return false;
- }
-
- SLDataLocator_OutputMix outputMixLocator = { SL_DATALOCATOR_OUTPUTMIX, m_outputMixObject };
- SLDataSink audioSink = { &outputMixLocator, nullptr };
-
-#ifndef ANDROID
- const int iids = 2;
- const SLInterfaceID ids[iids] = { SL_IID_BUFFERQUEUE, SL_IID_VOLUME };
- const SLboolean req[iids] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
-#else
- const int iids = 3;
- const SLInterfaceID ids[iids] = { SL_IID_BUFFERQUEUE,
- SL_IID_VOLUME,
- SL_IID_ANDROIDCONFIGURATION };
- const SLboolean req[iids] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
-#endif // ANDROID
-
- // AudioPlayer
- if (SL_RESULT_SUCCESS != (*engine)->CreateAudioPlayer(engine,
- &m_playerObject,
- &audioSrc,
- &audioSink,
- iids,
- ids,
- req)) {
- qWarning() << "Unable to create AudioPlayer";
- setError(QAudio::OpenError);
- return false;
- }
-
-#ifdef ANDROID
- // Set profile/category
- SLAndroidConfigurationItf playerConfig;
- if (SL_RESULT_SUCCESS == (*m_playerObject)->GetInterface(m_playerObject,
- SL_IID_ANDROIDCONFIGURATION,
- &playerConfig)) {
- (*playerConfig)->SetConfiguration(playerConfig,
- SL_ANDROID_KEY_STREAM_TYPE,
- &m_streamType,
- sizeof(SLint32));
- }
-#endif // ANDROID
-
- if (SL_RESULT_SUCCESS != (*m_playerObject)->Realize(m_playerObject, SL_BOOLEAN_FALSE)) {
- qWarning() << "Unable to initialize AudioPlayer";
- setError(QAudio::OpenError);
- return false;
- }
-
- // Buffer interface
- if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject,
- SL_IID_BUFFERQUEUE,
- &m_bufferQueueItf)) {
- setError(QAudio::FatalError);
- return false;
- }
-
- if (SL_RESULT_SUCCESS != (*m_bufferQueueItf)->RegisterCallback(m_bufferQueueItf,
- bufferQueueCallback,
- this)) {
- setError(QAudio::FatalError);
- return false;
- }
-
- // Play interface
- if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject,
- SL_IID_PLAY,
- &m_playItf)) {
- setError(QAudio::FatalError);
- return false;
- }
-
- if (SL_RESULT_SUCCESS != (*m_playItf)->RegisterCallback(m_playItf, playCallback, this)) {
- setError(QAudio::FatalError);
- return false;
- }
-
- if (m_notifyInterval && SL_RESULT_SUCCESS == (*m_playItf)->SetPositionUpdatePeriod(m_playItf,
- m_notifyInterval)) {
- m_eventMask |= SL_PLAYEVENT_HEADATNEWPOS;
- }
-
- if (SL_RESULT_SUCCESS != (*m_playItf)->SetCallbackEventsMask(m_playItf, m_eventMask)) {
- setError(QAudio::FatalError);
- return false;
- }
-
- // Volume interface
- if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject,
- SL_IID_VOLUME,
- &m_volumeItf)) {
- setError(QAudio::FatalError);
- return false;
- }
-
- setVolume(m_volume);
-
- const int lowLatencyBufferSize = QOpenSLESEngine::getLowLatencyBufferSize(m_format);
- const int defaultBufferSize = QOpenSLESEngine::getDefaultBufferSize(m_format);
-
- if (defaultBufferSize <= 0) {
- qWarning() << "Unable to get minimum buffer size, returned" << defaultBufferSize;
- setError(QAudio::FatalError);
- return false;
- }
-
- // Buffer size
- if (m_bufferSize <= 0) {
- m_bufferSize = defaultBufferSize;
- } else if (QOpenSLESEngine::supportsLowLatency()) {
- if (m_bufferSize < lowLatencyBufferSize)
- m_bufferSize = lowLatencyBufferSize;
- } else if (m_bufferSize < defaultBufferSize) {
- m_bufferSize = defaultBufferSize;
- }
-
- m_periodSize = m_bufferSize;
-
- if (!m_buffers)
- m_buffers = new char[BUFFER_COUNT * m_bufferSize];
-
- m_clockStamp.restart();
- setError(QAudio::NoError);
- m_startRequiresInit = false;
-
- return true;
-}
-
-void QOpenSLESAudioOutput::destroyPlayer()
-{
- if (m_state != QAudio::StoppedState)
- stopPlayer();
-
- if (m_playerObject) {
- (*m_playerObject)->Destroy(m_playerObject);
- m_playerObject = nullptr;
- }
-
- if (m_outputMixObject) {
- (*m_outputMixObject)->Destroy(m_outputMixObject);
- m_outputMixObject = nullptr;
- }
-
- if (!m_pullMode && m_audioSource) {
- m_audioSource->close();
- delete m_audioSource;
- m_audioSource = nullptr;
- }
-
- delete [] m_buffers;
- m_buffers = nullptr;
- m_processedBytes = 0;
- m_nextBuffer = 0;
- m_availableBuffers.storeRelease(BUFFER_COUNT);
- m_playItf = nullptr;
- m_volumeItf = nullptr;
- m_bufferQueueItf = nullptr;
- m_startRequiresInit = true;
-}
-
-void QOpenSLESAudioOutput::stopPlayer()
-{
- setState(QAudio::StoppedState);
-
- if (m_audioSource && !m_pullMode) {
- m_audioSource->close();
- delete m_audioSource;
- m_audioSource = nullptr;
- }
-
- // We need to change the state manually...
- if (m_playItf)
- (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_STOPPED);
-
- if (m_bufferQueueItf && SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Clear(m_bufferQueueItf))
- qWarning() << "Unable to clear buffer";
-}
-
-void QOpenSLESAudioOutput::startPlayer()
-{
- if (QOpenSLESEngine::printDebugInfo())
- openSlDebugInfo();
-
- if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) {
- setError(QAudio::FatalError);
- destroyPlayer();
- }
-}
-
-qint64 QOpenSLESAudioOutput::writeData(const char *data, qint64 len)
-{
- if (!len)
- return 0;
-
- if (len > m_bufferSize)
- len = m_bufferSize;
-
- // Acquire one slot in the buffer
- const int before = m_availableBuffers.fetchAndAddAcquire(-1);
-
- // If there where no vacant slots, then we just overdrew the buffer account...
- if (before < 1) {
- m_availableBuffers.fetchAndAddRelease(1);
- return 0;
- }
-
- const int index = m_nextBuffer * m_bufferSize;
- ::memcpy(m_buffers + index, data, len);
- const SLuint32 res = (*m_bufferQueueItf)->Enqueue(m_bufferQueueItf,
- m_buffers + index,
- len);
-
- // If we where unable to enqueue a new buffer, give back the acquired slot.
- if (res == SL_RESULT_BUFFER_INSUFFICIENT) {
- m_availableBuffers.fetchAndAddRelease(1);
- return 0;
- }
-
- if (res != SL_RESULT_SUCCESS) {
- setError(QAudio::FatalError);
- destroyPlayer();
- return -1;
- }
-
- m_processedBytes += len;
- setState(QAudio::ActiveState);
- setError(QAudio::NoError);
- m_nextBuffer = (m_nextBuffer + 1) % BUFFER_COUNT;
-
- return len;
-}
-
-inline void QOpenSLESAudioOutput::setState(QAudio::State state)
-{
- if (m_state == state)
- return;
-
- m_state = state;
- Q_EMIT stateChanged(m_state);
-}
-
-inline void QOpenSLESAudioOutput::setError(QAudio::Error error)
-{
- if (m_error == error)
- return;
-
- m_error = error;
- Q_EMIT errorChanged(m_error);
-}
-
-inline SLmillibel QOpenSLESAudioOutput::adjustVolume(qreal vol)
-{
- if (qFuzzyIsNull(vol))
- return SL_MILLIBEL_MIN;
-
- if (qFuzzyCompare(vol, qreal(1.0)))
- return 0;
-
- return QAudio::convertVolume(vol, QAudio::LinearVolumeScale, QAudio::DecibelVolumeScale) * 100;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/opensles/qopenslesaudiooutput.h b/src/plugins/opensles/qopenslesaudiooutput.h
deleted file mode 100644
index 80959df30..000000000
--- a/src/plugins/opensles/qopenslesaudiooutput.h
+++ /dev/null
@@ -1,159 +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 QOPENSLESAUDIOOUTPUT_H
-#define QOPENSLESAUDIOOUTPUT_H
-
-#include <qaudiosystem.h>
-#include <SLES/OpenSLES.h>
-#include <qbytearray.h>
-#include <qmap.h>
-#include <QElapsedTimer>
-#include <QIODevice>
-
-QT_BEGIN_NAMESPACE
-
-class QOpenSLESAudioOutput : public QAbstractAudioOutput
-{
- Q_OBJECT
-
-public:
- QOpenSLESAudioOutput(const QByteArray &device);
- ~QOpenSLESAudioOutput();
-
- void start(QIODevice *device) override;
- QIODevice *start() override;
- void stop() override;
- void reset() override;
- void suspend() override;
- void resume() override;
- int bytesFree() const override;
- int periodSize() const override;
- void setBufferSize(int value) override;
- int bufferSize() const override;
- void setNotifyInterval(int milliSeconds) override;
- int notifyInterval() const override;
- qint64 processedUSecs() const override;
- qint64 elapsedUSecs() const override;
- QAudio::Error error() const override;
- QAudio::State state() const override;
- void setFormat(const QAudioFormat &format) override;
- QAudioFormat format() const override;
-
- void setVolume(qreal volume) override;
- qreal volume() const override;
-
- void setCategory(const QString &category) override;
- QString category() const override;
-
-private:
- friend class SLIODevicePrivate;
-
- Q_INVOKABLE void onEOSEvent();
- Q_INVOKABLE void onBytesProcessed(qint64 bytes);
- void bufferAvailable(quint32 count, quint32 playIndex);
-
- static void playCallback(SLPlayItf playItf, void *ctx, SLuint32 event);
- static void bufferQueueCallback(SLBufferQueueItf bufferQueue, void *ctx);
-
- bool preparePlayer();
- void destroyPlayer();
- void stopPlayer();
- void startPlayer();
- qint64 writeData(const char *data, qint64 len);
-
- void setState(QAudio::State state);
- void setError(QAudio::Error error);
-
- SLmillibel adjustVolume(qreal vol);
-
- 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;
- int m_notifyInterval;
- int m_periodSize;
- qint64 m_elapsedTime;
- qint64 m_processedBytes;
- QAtomicInt m_availableBuffers;
- SLuint32 m_eventMask;
- bool m_startRequiresInit;
-
- qint32 m_streamType;
- QElapsedTimer m_clockStamp;
- QAudioFormat m_format;
- QString m_category;
- static QMap<QString, qint32> m_categories;
-};
-
-class SLIODevicePrivate : public QIODevice
-{
- Q_OBJECT
-
-public:
- inline SLIODevicePrivate(QOpenSLESAudioOutput *audio) : m_audioDevice(audio) {}
- inline ~SLIODevicePrivate() override {}
-
-protected:
- inline qint64 readData(char *, qint64) override { return 0; }
- inline qint64 writeData(const char *data, qint64 len) override;
-
-private:
- QOpenSLESAudioOutput *m_audioDevice;
-};
-
-qint64 SLIODevicePrivate::writeData(const char *data, qint64 len)
-{
- Q_ASSERT(m_audioDevice);
- return m_audioDevice->writeData(data, len);
-}
-
-QT_END_NAMESPACE
-
-#endif // QOPENSLESAUDIOOUTPUT_H
diff --git a/src/plugins/opensles/qopenslesdeviceinfo.cpp b/src/plugins/opensles/qopenslesdeviceinfo.cpp
deleted file mode 100644
index 8aaf67c76..000000000
--- a/src/plugins/opensles/qopenslesdeviceinfo.cpp
+++ /dev/null
@@ -1,113 +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 "qopenslesdeviceinfo.h"
-
-#include "qopenslesengine.h"
-
-QT_BEGIN_NAMESPACE
-
-QOpenSLESDeviceInfo::QOpenSLESDeviceInfo(const QByteArray &device, QAudio::Mode mode)
- : m_engine(QOpenSLESEngine::instance())
- , m_device(device)
- , m_mode(mode)
-{
-}
-
-bool QOpenSLESDeviceInfo::isFormatSupported(const QAudioFormat &format) const
-{
- QOpenSLESDeviceInfo *that = const_cast<QOpenSLESDeviceInfo*>(this);
- return that->supportedCodecs().contains(format.codec())
- && that->supportedSampleRates().contains(format.sampleRate())
- && that->supportedChannelCounts().contains(format.channelCount())
- && that->supportedSampleSizes().contains(format.sampleSize())
- && that->supportedByteOrders().contains(format.byteOrder())
- && that->supportedSampleTypes().contains(format.sampleType());
-}
-
-QAudioFormat QOpenSLESDeviceInfo::preferredFormat() const
-{
- QAudioFormat format;
- format.setCodec(QStringLiteral("audio/pcm"));
- format.setSampleSize(16);
- format.setSampleType(QAudioFormat::SignedInt);
- format.setSampleRate(QOpenSLESEngine::getOutputValue(QOpenSLESEngine::SampleRate, 48000));
- format.setChannelCount(m_mode == QAudio::AudioInput ? 1 : 2);
- return format;
-}
-
-QString QOpenSLESDeviceInfo::deviceName() const
-{
- return m_device;
-}
-
-QStringList QOpenSLESDeviceInfo::supportedCodecs()
-{
- return QStringList() << QStringLiteral("audio/pcm");
-}
-
-QList<int> QOpenSLESDeviceInfo::supportedSampleRates()
-{
- return m_engine->supportedSampleRates(m_mode);
-}
-
-QList<int> QOpenSLESDeviceInfo::supportedChannelCounts()
-{
- return m_engine->supportedChannelCounts(m_mode);
-}
-
-QList<int> QOpenSLESDeviceInfo::supportedSampleSizes()
-{
- if (m_mode == QAudio::AudioInput)
- return QList<int>() << 16;
- else
- return QList<int>() << 8 << 16;
-}
-
-QList<QAudioFormat::Endian> QOpenSLESDeviceInfo::supportedByteOrders()
-{
- return QList<QAudioFormat::Endian>() << QAudioFormat::LittleEndian;
-}
-
-QList<QAudioFormat::SampleType> QOpenSLESDeviceInfo::supportedSampleTypes()
-{
- return QList<QAudioFormat::SampleType>() << QAudioFormat::SignedInt;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/opensles/qopenslesdeviceinfo.h b/src/plugins/opensles/qopenslesdeviceinfo.h
deleted file mode 100644
index ebeb6b4b2..000000000
--- a/src/plugins/opensles/qopenslesdeviceinfo.h
+++ /dev/null
@@ -1,75 +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 QOPENSLESDEVICEINFO_H
-#define QOPENSLESDEVICEINFO_H
-
-#include <qaudiosystem.h>
-
-QT_BEGIN_NAMESPACE
-
-class QOpenSLESEngine;
-
-class QOpenSLESDeviceInfo : public QAbstractAudioDeviceInfo
-{
- Q_OBJECT
-
-public:
- QOpenSLESDeviceInfo(const QByteArray &device, QAudio::Mode mode);
- ~QOpenSLESDeviceInfo() {}
-
- QAudioFormat preferredFormat() const;
- bool isFormatSupported(const QAudioFormat &format) const;
- QString deviceName() const;
- QStringList supportedCodecs();
- QList<int> supportedSampleRates();
- QList<int> supportedChannelCounts();
- QList<int> supportedSampleSizes();
- QList<QAudioFormat::Endian> supportedByteOrders();
- QList<QAudioFormat::SampleType> supportedSampleTypes();
-
-private:
- QOpenSLESEngine *m_engine;
- QByteArray m_device;
- QAudio::Mode m_mode;
-};
-
-QT_END_NAMESPACE
-
-#endif // QOPENSLESDEVICEINFO_H
diff --git a/src/plugins/opensles/qopenslesengine.cpp b/src/plugins/opensles/qopenslesengine.cpp
deleted file mode 100644
index 36025dcfd..000000000
--- a/src/plugins/opensles/qopenslesengine.cpp
+++ /dev/null
@@ -1,369 +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 "qopenslesengine.h"
-
-#include "qopenslesaudioinput.h"
-#include <qdebug.h>
-
-#ifdef ANDROID
-#include <SLES/OpenSLES_Android.h>
-#include <QtCore/private/qjnihelpers_p.h>
-#include <QtCore/private/qjni_p.h>
-#endif
-
-#define MINIMUM_PERIOD_TIME_MS 5
-#define DEFAULT_PERIOD_TIME_MS 50
-
-#define CheckError(message) if (result != SL_RESULT_SUCCESS) { qWarning(message); return; }
-
-Q_GLOBAL_STATIC(QOpenSLESEngine, openslesEngine);
-
-QOpenSLESEngine::QOpenSLESEngine()
- : m_engineObject(0)
- , m_engine(0)
- , m_checkedInputFormats(false)
-{
- SLresult result;
-
- result = slCreateEngine(&m_engineObject, 0, 0, 0, 0, 0);
- CheckError("Failed to create engine");
-
- result = (*m_engineObject)->Realize(m_engineObject, SL_BOOLEAN_FALSE);
- CheckError("Failed to realize engine");
-
- result = (*m_engineObject)->GetInterface(m_engineObject, SL_IID_ENGINE, &m_engine);
- CheckError("Failed to get engine interface");
-}
-
-QOpenSLESEngine::~QOpenSLESEngine()
-{
- if (m_engineObject)
- (*m_engineObject)->Destroy(m_engineObject);
-}
-
-QOpenSLESEngine *QOpenSLESEngine::instance()
-{
- return openslesEngine();
-}
-
-SLDataFormat_PCM QOpenSLESEngine::audioFormatToSLFormatPCM(const QAudioFormat &format)
-{
- SLDataFormat_PCM format_pcm;
- format_pcm.formatType = SL_DATAFORMAT_PCM;
- format_pcm.numChannels = format.channelCount();
- format_pcm.samplesPerSec = format.sampleRate() * 1000;
- format_pcm.bitsPerSample = format.sampleSize();
- format_pcm.containerSize = format.sampleSize();
- format_pcm.channelMask = (format.channelCount() == 1 ?
- SL_SPEAKER_FRONT_CENTER :
- SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT);
- format_pcm.endianness = (format.byteOrder() == QAudioFormat::LittleEndian ?
- SL_BYTEORDER_LITTLEENDIAN :
- SL_BYTEORDER_BIGENDIAN);
- return format_pcm;
-
-}
-
-QByteArray QOpenSLESEngine::defaultDevice(QAudio::Mode mode) const
-{
- const auto &devices = availableDevices(mode);
- return !devices.isEmpty() ? devices.first() : QByteArray();
-}
-
-QList<QByteArray> QOpenSLESEngine::availableDevices(QAudio::Mode mode) const
-{
- QList<QByteArray> devices;
- if (mode == QAudio::AudioInput) {
-#ifdef ANDROID
- devices << QT_ANDROID_PRESET_MIC
- << QT_ANDROID_PRESET_CAMCORDER
- << QT_ANDROID_PRESET_VOICE_RECOGNITION
- << QT_ANDROID_PRESET_VOICE_COMMUNICATION;
-#else
- devices << "default";
-#endif
- } else {
- devices << "default";
- }
- return devices;
-}
-
-QList<int> QOpenSLESEngine::supportedChannelCounts(QAudio::Mode mode) const
-{
- if (mode == QAudio::AudioInput) {
- if (!m_checkedInputFormats)
- const_cast<QOpenSLESEngine *>(this)->checkSupportedInputFormats();
- return m_supportedInputChannelCounts;
- } else {
- return QList<int>() << 1 << 2;
- }
-}
-
-QList<int> QOpenSLESEngine::supportedSampleRates(QAudio::Mode mode) const
-{
- if (mode == QAudio::AudioInput) {
- if (!m_checkedInputFormats)
- const_cast<QOpenSLESEngine *>(this)->checkSupportedInputFormats();
- return m_supportedInputSampleRates;
- } else {
- return QList<int>() << 8000 << 11025 << 12000 << 16000 << 22050
- << 24000 << 32000 << 44100 << 48000;
- }
-}
-
-int QOpenSLESEngine::getOutputValue(QOpenSLESEngine::OutputValue type, int defaultValue)
-{
-#if defined(Q_OS_ANDROID)
- static int sampleRate = 0;
- static int framesPerBuffer = 0;
- static const int sdkVersion = QtAndroidPrivate::androidSdkVersion();
-
- if (sdkVersion < 17) // getProperty() was added in API level 17...
- return defaultValue;
-
- if (type == FramesPerBuffer && framesPerBuffer != 0)
- return framesPerBuffer;
-
- if (type == SampleRate && sampleRate != 0)
- return sampleRate;
-
- QJNIObjectPrivate ctx(QtAndroidPrivate::activity());
- if (!ctx.isValid())
- return defaultValue;
-
-
- QJNIObjectPrivate audioServiceString = ctx.getStaticObjectField("android/content/Context",
- "AUDIO_SERVICE",
- "Ljava/lang/String;");
- QJNIObjectPrivate am = ctx.callObjectMethod("getSystemService",
- "(Ljava/lang/String;)Ljava/lang/Object;",
- audioServiceString.object());
- if (!am.isValid())
- return defaultValue;
-
- QJNIObjectPrivate sampleRateField = QJNIObjectPrivate::getStaticObjectField("android/media/AudioManager",
- "PROPERTY_OUTPUT_SAMPLE_RATE",
- "Ljava/lang/String;");
- QJNIObjectPrivate framesPerBufferField = QJNIObjectPrivate::getStaticObjectField("android/media/AudioManager",
- "PROPERTY_OUTPUT_FRAMES_PER_BUFFER",
- "Ljava/lang/String;");
-
- QJNIObjectPrivate sampleRateString = am.callObjectMethod("getProperty",
- "(Ljava/lang/String;)Ljava/lang/String;",
- sampleRateField.object());
- QJNIObjectPrivate framesPerBufferString = am.callObjectMethod("getProperty",
- "(Ljava/lang/String;)Ljava/lang/String;",
- framesPerBufferField.object());
-
- if (!sampleRateString.isValid() || !framesPerBufferString.isValid())
- return defaultValue;
-
- framesPerBuffer = framesPerBufferString.toString().toInt();
- sampleRate = sampleRateString.toString().toInt();
-
- if (type == FramesPerBuffer)
- return framesPerBuffer;
-
- if (type == SampleRate)
- return sampleRate;
-
-#endif // Q_OS_ANDROID
-
- return defaultValue;
-}
-
-int QOpenSLESEngine::getDefaultBufferSize(const QAudioFormat &format)
-{
-#if defined(Q_OS_ANDROID)
- if (!format.isValid())
- return 0;
-
- const int channelConfig = [&format]() -> int
- {
- if (format.channelCount() == 1)
- return 4; /* MONO */
- else if (format.channelCount() == 2)
- return 12; /* STEREO */
- else if (format.channelCount() > 2)
- return 1052; /* SURROUND */
- else
- return 1; /* DEFAULT */
- }();
-
- const int audioFormat = [&format]() -> int
- {
- if (format.sampleType() == QAudioFormat::Float && QtAndroidPrivate::androidSdkVersion() >= 21)
- return 4; /* PCM_FLOAT */
- else if (format.sampleSize() == 8)
- return 3; /* PCM_8BIT */
- else if (format.sampleSize() == 16)
- return 2; /* PCM_16BIT*/
- else
- return 1; /* DEFAULT */
- }();
-
- const int sampleRate = format.sampleRate();
- const int minBufferSize = QJNIObjectPrivate::callStaticMethod<jint>("android/media/AudioTrack",
- "getMinBufferSize",
- "(III)I",
- sampleRate,
- channelConfig,
- audioFormat);
- return minBufferSize > 0 ? minBufferSize : format.bytesForDuration(DEFAULT_PERIOD_TIME_MS);
-#else
- return format.bytesForDuration(DEFAULT_PERIOD_TIME_MS);
-#endif // Q_OS_ANDROID
-}
-
-int QOpenSLESEngine::getLowLatencyBufferSize(const QAudioFormat &format)
-{
- return format.bytesForFrames(QOpenSLESEngine::getOutputValue(QOpenSLESEngine::FramesPerBuffer,
- format.framesForDuration(MINIMUM_PERIOD_TIME_MS)));
-}
-
-bool QOpenSLESEngine::supportsLowLatency()
-{
-#if defined(Q_OS_ANDROID)
- static int isSupported = -1;
-
- if (isSupported != -1)
- return (isSupported == 1);
-
- QJNIObjectPrivate ctx(QtAndroidPrivate::activity());
- if (!ctx.isValid())
- return false;
-
- QJNIObjectPrivate pm = ctx.callObjectMethod("getPackageManager", "()Landroid/content/pm/PackageManager;");
- if (!pm.isValid())
- return false;
-
- QJNIObjectPrivate audioFeatureField = QJNIObjectPrivate::getStaticObjectField("android/content/pm/PackageManager",
- "FEATURE_AUDIO_LOW_LATENCY",
- "Ljava/lang/String;");
- if (!audioFeatureField.isValid())
- return false;
-
- isSupported = pm.callMethod<jboolean>("hasSystemFeature",
- "(Ljava/lang/String;)Z",
- audioFeatureField.object());
- return (isSupported == 1);
-#else
- return true;
-#endif // Q_OS_ANDROID
-}
-
-bool QOpenSLESEngine::printDebugInfo()
-{
- return qEnvironmentVariableIsSet("QT_OPENSL_INFO");
-}
-
-void QOpenSLESEngine::checkSupportedInputFormats()
-{
- m_supportedInputChannelCounts = QList<int>() << 1;
- m_supportedInputSampleRates.clear();
-
- SLDataFormat_PCM defaultFormat;
- defaultFormat.formatType = SL_DATAFORMAT_PCM;
- defaultFormat.numChannels = 1;
- defaultFormat.samplesPerSec = SL_SAMPLINGRATE_44_1;
- defaultFormat.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
- defaultFormat.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
- defaultFormat.channelMask = SL_SPEAKER_FRONT_CENTER;
- defaultFormat.endianness = SL_BYTEORDER_LITTLEENDIAN;
-
- const SLuint32 rates[9] = { SL_SAMPLINGRATE_8,
- SL_SAMPLINGRATE_11_025,
- SL_SAMPLINGRATE_12,
- SL_SAMPLINGRATE_16,
- SL_SAMPLINGRATE_22_05,
- SL_SAMPLINGRATE_24,
- SL_SAMPLINGRATE_32,
- SL_SAMPLINGRATE_44_1,
- SL_SAMPLINGRATE_48 };
-
-
- // Test sampling rates
- for (int i = 0 ; i < 9; ++i) {
- SLDataFormat_PCM format = defaultFormat;
- format.samplesPerSec = rates[i];
-
- if (inputFormatIsSupported(format))
- m_supportedInputSampleRates.append(rates[i] / 1000);
-
- }
-
- // Test if stereo is supported
- {
- SLDataFormat_PCM format = defaultFormat;
- format.numChannels = 2;
- format.channelMask = 0;
- if (inputFormatIsSupported(format))
- m_supportedInputChannelCounts.append(2);
- }
-
- m_checkedInputFormats = true;
-}
-
-bool QOpenSLESEngine::inputFormatIsSupported(SLDataFormat_PCM format)
-{
- SLresult result;
- SLObjectItf recorder = 0;
- SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
- SL_DEFAULTDEVICEID_AUDIOINPUT, NULL };
- SLDataSource audioSrc = { &loc_dev, NULL };
-
-#ifdef ANDROID
- SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1 };
-#else
- SLDataLocator_BufferQueue loc_bq = { SL_DATALOCATOR_BUFFERQUEUE, 1 };
-#endif
- SLDataSink audioSnk = { &loc_bq, &format };
-
- result = (*m_engine)->CreateAudioRecorder(m_engine, &recorder, &audioSrc, &audioSnk, 0, 0, 0);
- if (result == SL_RESULT_SUCCESS)
- result = (*recorder)->Realize(recorder, false);
-
- if (result == SL_RESULT_SUCCESS) {
- (*recorder)->Destroy(recorder);
- return true;
- }
-
- return false;
-}
diff --git a/src/plugins/opensles/qopenslesengine.h b/src/plugins/opensles/qopenslesengine.h
deleted file mode 100644
index c36b21488..000000000
--- a/src/plugins/opensles/qopenslesengine.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 QOPENSLESENGINE_H
-#define QOPENSLESENGINE_H
-
-#include <qglobal.h>
-#include <qaudio.h>
-#include <qlist.h>
-#include <qaudioformat.h>
-#include <SLES/OpenSLES.h>
-
-QT_BEGIN_NAMESPACE
-
-class QOpenSLESEngine
-{
-public:
- enum OutputValue { FramesPerBuffer, SampleRate };
-
- QOpenSLESEngine();
- ~QOpenSLESEngine();
-
- static QOpenSLESEngine *instance();
-
- SLEngineItf slEngine() const { return m_engine; }
-
- static SLDataFormat_PCM audioFormatToSLFormatPCM(const QAudioFormat &format);
-
- QByteArray defaultDevice(QAudio::Mode mode) const;
- QList<QByteArray> availableDevices(QAudio::Mode mode) const;
- QList<int> supportedChannelCounts(QAudio::Mode mode) const;
- QList<int> supportedSampleRates(QAudio::Mode mode) const;
-
- static int getOutputValue(OutputValue type, int defaultValue = 0);
- static int getDefaultBufferSize(const QAudioFormat &format);
- static int getLowLatencyBufferSize(const QAudioFormat &format);
- static bool supportsLowLatency();
- static bool printDebugInfo();
-
-private:
- void checkSupportedInputFormats();
- bool inputFormatIsSupported(SLDataFormat_PCM format);
-
- SLObjectItf m_engineObject;
- SLEngineItf m_engine;
-
- QList<int> m_supportedInputChannelCounts;
- QList<int> m_supportedInputSampleRates;
- bool m_checkedInputFormats;
-};
-
-QT_END_NAMESPACE
-
-#endif // QOPENSLESENGINE_H
diff --git a/src/plugins/opensles/qopenslesplugin.cpp b/src/plugins/opensles/qopenslesplugin.cpp
deleted file mode 100644
index 9a2fbbf79..000000000
--- a/src/plugins/opensles/qopenslesplugin.cpp
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "qopenslesplugin.h"
-
-#include "qopenslesengine.h"
-#include "qopenslesdeviceinfo.h"
-#include "qopenslesaudioinput.h"
-#include "qopenslesaudiooutput.h"
-
-QT_BEGIN_NAMESPACE
-
-QOpenSLESPlugin::QOpenSLESPlugin(QObject *parent)
- : QAudioSystemPlugin(parent)
- , m_engine(QOpenSLESEngine::instance())
-{
-}
-
-QByteArray QOpenSLESPlugin::defaultDevice(QAudio::Mode mode) const
-{
- return m_engine->defaultDevice(mode);
-}
-
-QList<QByteArray> QOpenSLESPlugin::availableDevices(QAudio::Mode mode) const
-{
- return m_engine->availableDevices(mode);
-}
-
-QAbstractAudioInput *QOpenSLESPlugin::createInput(const QByteArray &device)
-{
- return new QOpenSLESAudioInput(device);
-}
-
-QAbstractAudioOutput *QOpenSLESPlugin::createOutput(const QByteArray &device)
-{
- return new QOpenSLESAudioOutput(device);
-}
-
-QAbstractAudioDeviceInfo *QOpenSLESPlugin::createDeviceInfo(const QByteArray &device, QAudio::Mode mode)
-{
- return new QOpenSLESDeviceInfo(device, mode);
-}
-
-QT_END_NAMESPACE
-
diff --git a/src/plugins/opensles/qopenslesplugin.h b/src/plugins/opensles/qopenslesplugin.h
deleted file mode 100644
index d45a47923..000000000
--- a/src/plugins/opensles/qopenslesplugin.h
+++ /dev/null
@@ -1,73 +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 QOPENSLESPLUGIN_H
-#define QOPENSLESPLUGIN_H
-
-#include <QtMultimedia/qaudiosystemplugin.h>
-#include <QtMultimedia/private/qaudiosystempluginext_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class QOpenSLESEngine;
-
-class QOpenSLESPlugin : public QAudioSystemPlugin, public QAudioSystemPluginExtension
-{
- Q_OBJECT
-
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.audiosystemfactory/5.0" FILE "opensles.json")
- Q_INTERFACES(QAudioSystemPluginExtension)
-
-public:
- QOpenSLESPlugin(QObject *parent = 0);
- ~QOpenSLESPlugin() {}
-
- QByteArray defaultDevice(QAudio::Mode mode) const;
- QList<QByteArray> availableDevices(QAudio::Mode mode) const;
- QAbstractAudioInput *createInput(const QByteArray &device);
- QAbstractAudioOutput *createOutput(const QByteArray &device);
- QAbstractAudioDeviceInfo *createDeviceInfo(const QByteArray &device, QAudio::Mode mode);
-
-private:
- QOpenSLESEngine *m_engine;
-};
-
-QT_END_NAMESPACE
-
-#endif // QOPENSLESPLUGIN_H
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
deleted file mode 100644
index a087e8182..000000000
--- a/src/plugins/plugins.pro
+++ /dev/null
@@ -1,54 +0,0 @@
-######################################################################
-#
-# Qt Multimedia
-#
-######################################################################
-
-TEMPLATE = subdirs
-QT_FOR_CONFIG += multimedia-private
-
-SUBDIRS += m3u
-
-qtHaveModule(quick) {
- SUBDIRS += videonode
-}
-
-android {
- SUBDIRS += android opensles
-}
-
-qnx {
- qtConfig(mmrenderer): SUBDIRS += qnx
- SUBDIRS += audiocapture
-}
-
-qnx {
- SUBDIRS += qnx-audio
-}
-
-win32: {
- SUBDIRS += audiocapture \
- windowsaudio
-
- qtConfig(directshow): SUBDIRS += directshow
- qtConfig(wmf): SUBDIRS += wmf
-}
-
-qtConfig(gstreamer): SUBDIRS += gstreamer
-
-unix:!mac:!android {
- !qtConfig(gstreamer): SUBDIRS += audiocapture
-
- qtConfig(pulseaudio): SUBDIRS += pulseaudio
- qtConfig(alsa): SUBDIRS += alsa
-}
-
-darwin:!watchos {
- SUBDIRS += audiocapture coreaudio
- qtConfig(avfoundation): SUBDIRS += avfoundation
-}
-
-qtConfig(resourcepolicy) {
- SUBDIRS += resourcepolicy
-}
-
diff --git a/src/plugins/pulseaudio/pulseaudio.json b/src/plugins/pulseaudio/pulseaudio.json
deleted file mode 100644
index a31d52107..000000000
--- a/src/plugins/pulseaudio/pulseaudio.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "Keys": ["default"]
-}
diff --git a/src/plugins/pulseaudio/pulseaudio.pro b/src/plugins/pulseaudio/pulseaudio.pro
deleted file mode 100644
index c6339e9e8..000000000
--- a/src/plugins/pulseaudio/pulseaudio.pro
+++ /dev/null
@@ -1,25 +0,0 @@
-TARGET = qtmedia_pulse
-QT += multimedia-private
-
-QMAKE_USE += pulseaudio
-
-HEADERS += qpulseaudioplugin.h \
- qaudiodeviceinfo_pulse.h \
- qaudiooutput_pulse.h \
- qaudioinput_pulse.h \
- qpulseaudioengine.h \
- qpulsehelpers.h
-
-SOURCES += qpulseaudioplugin.cpp \
- qaudiodeviceinfo_pulse.cpp \
- qaudiooutput_pulse.cpp \
- qaudioinput_pulse.cpp \
- qpulseaudioengine.cpp \
- qpulsehelpers.cpp
-
-OTHER_FILES += \
- pulseaudio.json
-
-PLUGIN_TYPE = audio
-PLUGIN_CLASS_NAME = QPulseAudioPlugin
-load(qt_plugin)
diff --git a/src/plugins/pulseaudio/qaudiodeviceinfo_pulse.cpp b/src/plugins/pulseaudio/qaudiodeviceinfo_pulse.cpp
deleted file mode 100644
index c9cdfb3c5..000000000
--- a/src/plugins/pulseaudio/qaudiodeviceinfo_pulse.cpp
+++ /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$
-**
-****************************************************************************/
-
-#include "qaudiodeviceinfo_pulse.h"
-#include "qpulseaudioengine.h"
-#include "qpulsehelpers.h"
-
-QT_BEGIN_NAMESPACE
-
-QPulseAudioDeviceInfo::QPulseAudioDeviceInfo(const QByteArray &device, QAudio::Mode mode)
- : m_device(device)
- , m_mode(mode)
-{
-}
-
-bool QPulseAudioDeviceInfo::isFormatSupported(const QAudioFormat &format) const
-{
- pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(format);
- if (!pa_sample_spec_valid(&spec))
- return false;
-
- return true;
-}
-
-QAudioFormat QPulseAudioDeviceInfo::preferredFormat() const
-{
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- QAudioFormat format = pulseEngine->m_preferredFormats.value(m_device);
- return format;
-}
-
-QString QPulseAudioDeviceInfo::deviceName() const
-{
- return m_device;
-}
-
-QStringList QPulseAudioDeviceInfo::supportedCodecs()
-{
- return QStringList() << "audio/pcm";
-}
-
-QList<int> QPulseAudioDeviceInfo::supportedSampleRates()
-{
- return QList<int>() << 8000 << 11025 << 22050 << 44100 << 48000;
-}
-
-QList<int> QPulseAudioDeviceInfo::supportedChannelCounts()
-{
- return QList<int>() << 1 << 2 << 4 << 6 << 8;
-}
-
-QList<int> QPulseAudioDeviceInfo::supportedSampleSizes()
-{
- return QList<int>() << 8 << 16 << 24 << 32;
-}
-
-QList<QAudioFormat::Endian> QPulseAudioDeviceInfo::supportedByteOrders()
-{
- return QList<QAudioFormat::Endian>() << QAudioFormat::BigEndian << QAudioFormat::LittleEndian;
-}
-
-QList<QAudioFormat::SampleType> QPulseAudioDeviceInfo::supportedSampleTypes()
-{
- return QList<QAudioFormat::SampleType>() << QAudioFormat::SignedInt << QAudioFormat::UnSignedInt << QAudioFormat::Float;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/pulseaudio/qaudiodeviceinfo_pulse.h b/src/plugins/pulseaudio/qaudiodeviceinfo_pulse.h
deleted file mode 100644
index 1cec772c0..000000000
--- a/src/plugins/pulseaudio/qaudiodeviceinfo_pulse.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 QAUDIODEVICEINFOPULSE_H
-#define QAUDIODEVICEINFOPULSE_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/qbytearray.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qlist.h>
-
-#include "qaudio.h"
-#include "qaudiodeviceinfo.h"
-#include "qaudiosystem.h"
-
-QT_BEGIN_NAMESPACE
-
-class QPulseAudioDeviceInfo : public QAbstractAudioDeviceInfo
-{
- Q_OBJECT
-
-public:
- QPulseAudioDeviceInfo(const QByteArray &device, QAudio::Mode mode);
- ~QPulseAudioDeviceInfo() {}
-
- QAudioFormat preferredFormat() const override;
- bool isFormatSupported(const QAudioFormat &format) const override;
- QString deviceName() const override;
- QStringList supportedCodecs() override;
- QList<int> supportedSampleRates() override;
- QList<int> supportedChannelCounts() override;
- QList<int> supportedSampleSizes() override;
- QList<QAudioFormat::Endian> supportedByteOrders() override;
- QList<QAudioFormat::SampleType> supportedSampleTypes() override;
-
-private:
- QByteArray m_device;
- QAudio::Mode m_mode;
-};
-
-QT_END_NAMESPACE
-
-#endif
-
diff --git a/src/plugins/pulseaudio/qaudioinput_pulse.cpp b/src/plugins/pulseaudio/qaudioinput_pulse.cpp
deleted file mode 100644
index 3e7b4d79a..000000000
--- a/src/plugins/pulseaudio/qaudioinput_pulse.cpp
+++ /dev/null
@@ -1,682 +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 <QtCore/qcoreapplication.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qmath.h>
-#include <private/qaudiohelpers_p.h>
-
-#include "qaudioinput_pulse.h"
-#include "qaudiodeviceinfo_pulse.h"
-#include "qpulseaudioengine.h"
-#include "qpulsehelpers.h"
-#include <sys/types.h>
-#include <unistd.h>
-
-QT_BEGIN_NAMESPACE
-
-const int PeriodTimeMs = 50;
-
-static void inputStreamReadCallback(pa_stream *stream, size_t length, void *userdata)
-{
- Q_UNUSED(userdata);
- Q_UNUSED(length);
- Q_UNUSED(stream);
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
-}
-
-static void inputStreamStateCallback(pa_stream *stream, void *userdata)
-{
- Q_UNUSED(userdata);
- pa_stream_state_t state = pa_stream_get_state(stream);
-#ifdef DEBUG_PULSE
- qDebug() << "Stream state: " << QPulseAudioInternal::stateToQString(state);
-#endif
- switch (state) {
- case PA_STREAM_CREATING:
- break;
- case PA_STREAM_READY: {
-#ifdef DEBUG_PULSE
- QPulseAudioInput *audioInput = static_cast<QPulseAudioInput*>(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("Stream error: %1").arg(pa_strerror(pa_context_errno(pa_stream_get_context(stream))));
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
- break;
- }
-}
-
-static void inputStreamUnderflowCallback(pa_stream *stream, void *userdata)
-{
- Q_UNUSED(userdata);
- Q_UNUSED(stream);
- qWarning() << "Got a buffer underflow!";
-}
-
-static void inputStreamOverflowCallback(pa_stream *stream, void *userdata)
-{
- Q_UNUSED(stream);
- Q_UNUSED(userdata);
- qWarning() << "Got a buffer overflow!";
-}
-
-static void inputStreamSuccessCallback(pa_stream *stream, int success, void *userdata)
-{
- Q_UNUSED(stream);
- Q_UNUSED(userdata);
- Q_UNUSED(success);
-
- //if (!success)
- //TODO: Is cork success? i->operation_success = success;
-
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
-}
-
-QPulseAudioInput::QPulseAudioInput(const QByteArray &device)
- : m_totalTimeValue(0)
- , m_audioSource(0)
- , 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_intervalTime(1000)
- , m_periodTime(PeriodTimeMs)
- , m_stream(0)
- , m_device(device)
-{
- m_timer = new QTimer(this);
- connect(m_timer, SIGNAL(timeout()), SLOT(userFeed()));
-}
-
-QPulseAudioInput::~QPulseAudioInput()
-{
- close();
- disconnect(m_timer, SIGNAL(timeout()));
- QCoreApplication::processEvents();
- delete m_timer;
-}
-
-void QPulseAudioInput::setError(QAudio::Error error)
-{
- if (m_errorState == error)
- return;
-
- m_errorState = error;
- emit errorChanged(error);
-}
-
-QAudio::Error QPulseAudioInput::error() const
-{
- return m_errorState;
-}
-
-void QPulseAudioInput::setState(QAudio::State state)
-{
- if (m_deviceState == state)
- return;
-
- m_deviceState = state;
- emit stateChanged(state);
-}
-
-QAudio::State QPulseAudioInput::state() const
-{
- return m_deviceState;
-}
-
-void QPulseAudioInput::setFormat(const QAudioFormat &format)
-{
- if (m_deviceState == QAudio::StoppedState)
- m_format = format;
-}
-
-QAudioFormat QPulseAudioInput::format() const
-{
- return m_format;
-}
-
-void QPulseAudioInput::start(QIODevice *device)
-{
- setState(QAudio::StoppedState);
- setError(QAudio::NoError);
-
- if (!m_pullMode && m_audioSource) {
- delete m_audioSource;
- m_audioSource = 0;
- }
-
- close();
-
- if (!open())
- return;
-
- m_pullMode = true;
- m_audioSource = device;
-
- setState(QAudio::ActiveState);
-}
-
-QIODevice *QPulseAudioInput::start()
-{
- setState(QAudio::StoppedState);
- setError(QAudio::NoError);
-
- if (!m_pullMode && m_audioSource) {
- delete m_audioSource;
- m_audioSource = 0;
- }
-
- close();
-
- if (!open())
- return nullptr;
-
- m_pullMode = false;
- m_audioSource = new PulseInputPrivate(this);
- m_audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
-
- setState(QAudio::IdleState);
-
- return m_audioSource;
-}
-
-void QPulseAudioInput::stop()
-{
- if (m_deviceState == QAudio::StoppedState)
- return;
-
- close();
-
- setError(QAudio::NoError);
- setState(QAudio::StoppedState);
-}
-
-bool QPulseAudioInput::open()
-{
- if (m_opened)
- return true;
-
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
-
- if (!pulseEngine->context() || pa_context_get_state(pulseEngine->context()) != PA_CONTEXT_READY) {
- setError(QAudio::FatalError);
- setState(QAudio::StoppedState);
- return false;
- }
-
- pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(m_format);
-
- if (!pa_sample_spec_valid(&spec)) {
- setError(QAudio::OpenError);
- setState(QAudio::StoppedState);
- return false;
- }
-
- m_spec = spec;
-
-#ifdef DEBUG_PULSE
-// QTime now(QTime::currentTime());
-// qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
-#endif
-
- 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
-
- pulseEngine->lock();
- pa_channel_map channel_map;
-
- pa_channel_map_init_extend(&channel_map, spec.channels, PA_CHANNEL_MAP_DEFAULT);
-
- if (!pa_channel_map_compatible(&channel_map, &spec))
- qWarning() << "Channel map doesn't match sample specification!";
-
- m_stream = pa_stream_new(pulseEngine->context(), m_streamName.constData(), &spec, &channel_map);
-
- pa_stream_set_state_callback(m_stream, inputStreamStateCallback, this);
- pa_stream_set_read_callback(m_stream, inputStreamReadCallback, this);
-
- 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);
-
- 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;
- flags |= PA_STREAM_ADJUST_LATENCY;
-
- if (m_bufferSize > 0)
- buffer_attr.fragsize = (uint32_t) m_bufferSize;
- else
- buffer_attr.fragsize = (uint32_t) m_periodSize;
-
- if (pa_stream_connect_record(m_stream, m_device.data(), &buffer_attr, (pa_stream_flags_t)flags) < 0) {
- qWarning() << "pa_stream_connect_record() failed!";
- pa_stream_unref(m_stream);
- m_stream = 0;
- pulseEngine->unlock();
- setError(QAudio::OpenError);
- setState(QAudio::StoppedState);
- return false;
- }
-
- while (pa_stream_get_state(m_stream) != PA_STREAM_READY)
- pa_threaded_mainloop_wait(pulseEngine->mainloop());
-
- 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)
- m_bufferSize = actualBufferAttr->tlength;
-
- pulseEngine->unlock();
-
- connect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioInput::onPulseContextFailed);
-
- m_opened = true;
- m_timer->start(m_periodTime);
-
- m_clockStamp.restart();
- m_timeStamp.restart();
- m_elapsedTimeOffset = 0;
- m_totalTimeValue = 0;
-
- return true;
-}
-
-void QPulseAudioInput::close()
-{
- if (!m_opened)
- return;
-
- m_timer->stop();
-
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
-
- if (m_stream) {
- pulseEngine->lock();
-
- pa_stream_set_state_callback(m_stream, 0, 0);
- pa_stream_set_read_callback(m_stream, 0, 0);
- pa_stream_set_underflow_callback(m_stream, 0, 0);
- pa_stream_set_overflow_callback(m_stream, 0, 0);
-
- pa_stream_disconnect(m_stream);
- pa_stream_unref(m_stream);
- m_stream = 0;
-
- pulseEngine->unlock();
- }
-
- disconnect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioInput::onPulseContextFailed);
-
- if (!m_pullMode && m_audioSource) {
- delete m_audioSource;
- m_audioSource = 0;
- }
- m_opened = false;
-}
-
-int QPulseAudioInput::checkBytesReady()
-{
- if (m_deviceState != QAudio::ActiveState && m_deviceState != QAudio::IdleState) {
- m_bytesAvailable = 0;
- } else {
- m_bytesAvailable = pa_stream_readable_size(m_stream);
- }
-
- return m_bytesAvailable;
-}
-
-int QPulseAudioInput::bytesReady() const
-{
- return qMax(m_bytesAvailable, 0);
-}
-
-qint64 QPulseAudioInput::read(char *data, qint64 len)
-{
- m_bytesAvailable = checkBytesReady();
-
- setError(QAudio::NoError);
- setState(QAudio::ActiveState);
-
- int readBytes = 0;
-
- if (!m_pullMode && !m_tempBuffer.isEmpty()) {
- readBytes = qMin(static_cast<int>(len), m_tempBuffer.size());
- memcpy(data, m_tempBuffer.constData(), readBytes);
- m_totalTimeValue += readBytes;
-
- if (readBytes < m_tempBuffer.size()) {
- m_tempBuffer.remove(0, readBytes);
- return readBytes;
- }
-
- m_tempBuffer.clear();
- }
-
- while (pa_stream_readable_size(m_stream) > 0) {
- size_t readLength = 0;
-
-#ifdef DEBUG_PULSE
- qDebug() << "QPulseAudioInput::read -- " << pa_stream_readable_size(m_stream) << " bytes available from pulse audio";
-#endif
-
- 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.
- if (pa_stream_peek(m_stream, &audioBuffer, &readLength) < 0) {
- qWarning() << QString("pa_stream_peek() failed: %1").arg(pa_strerror(pa_context_errno(pa_stream_get_context(m_stream))));
- pulseEngine->unlock();
- return 0;
- }
-
- qint64 actualLength = 0;
- if (m_pullMode) {
- QByteArray adjusted(readLength, Qt::Uninitialized);
- applyVolume(audioBuffer, adjusted.data(), readLength);
- actualLength = m_audioSource->write(adjusted);
-
- if (actualLength < qint64(readLength)) {
- pulseEngine->unlock();
-
- setError(QAudio::UnderrunError);
- setState(QAudio::IdleState);
-
- return actualLength;
- }
- } else {
- actualLength = qMin(static_cast<int>(len - readBytes), static_cast<int>(readLength));
- applyVolume(audioBuffer, data + readBytes, actualLength);
- }
-
-#ifdef DEBUG_PULSE
- qDebug() << "QPulseAudioInput::read -- wrote " << actualLength << " to client";
-#endif
-
- if (actualLength < qint64(readLength)) {
-#ifdef DEBUG_PULSE
- qDebug() << "QPulseAudioInput::read -- appending " << readLength - actualLength << " bytes of data to temp buffer";
-#endif
- int diff = readLength - actualLength;
- int oldSize = m_tempBuffer.size();
- m_tempBuffer.resize(m_tempBuffer.size() + diff);
- applyVolume(static_cast<const char *>(audioBuffer) + actualLength, m_tempBuffer.data() + oldSize, diff);
- QMetaObject::invokeMethod(this, "userFeed", Qt::QueuedConnection);
- }
-
- m_totalTimeValue += actualLength;
- readBytes += actualLength;
-
- pa_stream_drop(m_stream);
- pulseEngine->unlock();
-
- if (!m_pullMode && readBytes >= len)
- break;
-
- if (m_intervalTime && (m_timeStamp.elapsed() + m_elapsedTimeOffset) > m_intervalTime) {
- emit notify();
- m_elapsedTimeOffset = m_timeStamp.elapsed() + m_elapsedTimeOffset - m_intervalTime;
- m_timeStamp.restart();
- }
- }
-
-#ifdef DEBUG_PULSE
- qDebug() << "QPulseAudioInput::read -- returning after reading " << readBytes << " bytes";
-#endif
-
- return readBytes;
-}
-
-void QPulseAudioInput::applyVolume(const void *src, void *dest, int len)
-{
- if (m_volume < 1.f)
- QAudioHelperInternal::qMultiplySamples(m_volume, m_format, src, dest, len);
- else
- memcpy(dest, src, len);
-}
-
-void QPulseAudioInput::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, 0);
- pulseEngine->wait(operation);
- pa_operation_unref(operation);
-
- pulseEngine->unlock();
-
- m_timer->start(m_periodTime);
-
- setState(QAudio::ActiveState);
- setError(QAudio::NoError);
- }
-}
-
-void QPulseAudioInput::setVolume(qreal vol)
-{
- if (qFuzzyCompare(m_volume, vol))
- return;
-
- m_volume = qBound(qreal(0), vol, qreal(1));
-}
-
-qreal QPulseAudioInput::volume() const
-{
- return m_volume;
-}
-
-void QPulseAudioInput::setBufferSize(int value)
-{
- m_bufferSize = value;
-}
-
-int QPulseAudioInput::bufferSize() const
-{
- return m_bufferSize;
-}
-
-int QPulseAudioInput::periodSize() const
-{
- return m_periodSize;
-}
-
-void QPulseAudioInput::setNotifyInterval(int ms)
-{
- m_intervalTime = qMax(0, ms);
-}
-
-int QPulseAudioInput::notifyInterval() const
-{
- return m_intervalTime;
-}
-
-qint64 QPulseAudioInput::processedUSecs() const
-{
- pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(m_format);
- qint64 result = pa_bytes_to_usec(m_totalTimeValue, &spec);
-
- return result;
-}
-
-void QPulseAudioInput::suspend()
-{
- if (m_deviceState == QAudio::ActiveState) {
- setError(QAudio::NoError);
- setState(QAudio::SuspendedState);
-
- m_timer->stop();
-
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- pa_operation *operation;
-
- pulseEngine->lock();
-
- operation = pa_stream_cork(m_stream, 1, inputStreamSuccessCallback, 0);
- pulseEngine->wait(operation);
- pa_operation_unref(operation);
-
- pulseEngine->unlock();
- }
-}
-
-void QPulseAudioInput::userFeed()
-{
- 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();
-}
-
-bool QPulseAudioInput::deviceReady()
-{
- if (m_pullMode) {
- // reads some audio data and writes it to QIODevice
- read(0,0);
- } else {
- // emits readyRead() so user will call read() on QIODevice to get some audio data
- if (m_audioSource != 0) {
- PulseInputPrivate *a = qobject_cast<PulseInputPrivate*>(m_audioSource);
- a->trigger();
- }
- }
- m_bytesAvailable = checkBytesReady();
-
- if (m_deviceState != QAudio::ActiveState)
- return true;
-
- if (m_intervalTime && (m_timeStamp.elapsed() + m_elapsedTimeOffset) > m_intervalTime) {
- emit notify();
- m_elapsedTimeOffset = m_timeStamp.elapsed() + m_elapsedTimeOffset - m_intervalTime;
- m_timeStamp.restart();
- }
-
- return true;
-}
-
-qint64 QPulseAudioInput::elapsedUSecs() const
-{
- if (m_deviceState == QAudio::StoppedState)
- return 0;
-
- return m_clockStamp.elapsed() * qint64(1000);
-}
-
-void QPulseAudioInput::reset()
-{
- stop();
- m_bytesAvailable = 0;
-}
-
-void QPulseAudioInput::onPulseContextFailed()
-{
- close();
-
- setError(QAudio::FatalError);
- setState(QAudio::StoppedState);
-}
-
-PulseInputPrivate::PulseInputPrivate(QPulseAudioInput *audio)
-{
- m_audioDevice = qobject_cast<QPulseAudioInput*>(audio);
-}
-
-qint64 PulseInputPrivate::readData(char *data, qint64 len)
-{
- return m_audioDevice->read(data, len);
-}
-
-qint64 PulseInputPrivate::writeData(const char *data, qint64 len)
-{
- Q_UNUSED(data);
- Q_UNUSED(len);
- return 0;
-}
-
-void PulseInputPrivate::trigger()
-{
- emit readyRead();
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qaudioinput_pulse.cpp"
diff --git a/src/plugins/pulseaudio/qaudioinput_pulse.h b/src/plugins/pulseaudio/qaudioinput_pulse.h
deleted file mode 100644
index dce212a25..000000000
--- a/src/plugins/pulseaudio/qaudioinput_pulse.h
+++ /dev/null
@@ -1,161 +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$
-**
-****************************************************************************/
-
-//
-// 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.
-//
-
-#ifndef QAUDIOINPUTPULSE_H
-#define QAUDIOINPUTPULSE_H
-
-#include <QtCore/qfile.h>
-#include <QtCore/qtimer.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qelapsedtimer.h>
-#include <QtCore/qiodevice.h>
-
-#include "qaudio.h"
-#include "qaudiodeviceinfo.h"
-#include "qaudiosystem.h"
-
-#include <pulse/pulseaudio.h>
-
-QT_BEGIN_NAMESPACE
-
-class PulseInputPrivate;
-
-class QPulseAudioInput : public QAbstractAudioInput
-{
- Q_OBJECT
-
-public:
- QPulseAudioInput(const QByteArray &device);
- ~QPulseAudioInput();
-
- qint64 read(char *data, qint64 len);
-
- void start(QIODevice *device) override;
- QIODevice *start() override;
- void stop() override;
- void reset() override;
- void suspend() override;
- void resume() override;
- int bytesReady() const override;
- int periodSize() const override;
- void setBufferSize(int value) override;
- int bufferSize() const override;
- void setNotifyInterval(int milliSeconds) override;
- int notifyInterval() const override;
- qint64 processedUSecs() const override;
- qint64 elapsedUSecs() const override;
- QAudio::Error error() const override;
- QAudio::State state() const override;
- void setFormat(const QAudioFormat &format) override;
- QAudioFormat format() const override;
-
- void setVolume(qreal volume) override;
- qreal volume() const override;
-
- qint64 m_totalTimeValue;
- QIODevice *m_audioSource;
- QAudioFormat m_format;
- QAudio::Error m_errorState;
- QAudio::State m_deviceState;
- qreal m_volume;
-
-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;
- int m_intervalTime;
- unsigned int m_periodTime;
- QTimer *m_timer;
- qint64 m_elapsedTimeOffset;
- pa_stream *m_stream;
- QElapsedTimer m_timeStamp;
- QElapsedTimer m_clockStamp;
- QByteArray m_streamName;
- QByteArray m_device;
- QByteArray m_tempBuffer;
- pa_sample_spec m_spec;
-};
-
-class PulseInputPrivate : public QIODevice
-{
- Q_OBJECT
-public:
- PulseInputPrivate(QPulseAudioInput *audio);
- ~PulseInputPrivate() {};
-
- qint64 readData(char *data, qint64 len) override;
- qint64 writeData(const char *data, qint64 len) override;
-
- void trigger();
-
-private:
- QPulseAudioInput *m_audioDevice;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/pulseaudio/qaudiooutput_pulse.cpp b/src/plugins/pulseaudio/qaudiooutput_pulse.cpp
deleted file mode 100644
index 708c3e0e3..000000000
--- a/src/plugins/pulseaudio/qaudiooutput_pulse.cpp
+++ /dev/null
@@ -1,739 +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 <QtCore/qcoreapplication.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qmath.h>
-#include <private/qaudiohelpers_p.h>
-
-#include "qaudiooutput_pulse.h"
-#include "qaudiodeviceinfo_pulse.h"
-#include "qpulseaudioengine.h"
-#include "qpulsehelpers.h"
-#include <sys/types.h>
-#include <unistd.h>
-
-QT_BEGIN_NAMESPACE
-
-const int PeriodTimeMs = 20;
-const int LowLatencyPeriodTimeMs = 10;
-const int LowLatencyBufferSizeMs = 40;
-
-#define LOW_LATENCY_CATEGORY_NAME "game"
-
-static void outputStreamWriteCallback(pa_stream *stream, size_t length, void *userdata)
-{
- Q_UNUSED(stream);
- Q_UNUSED(length);
- Q_UNUSED(userdata);
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
-}
-
-static void outputStreamStateCallback(pa_stream *stream, void *userdata)
-{
- Q_UNUSED(userdata);
- pa_stream_state_t state = pa_stream_get_state(stream);
-#ifdef DEBUG_PULSE
- qDebug() << "Stream state: " << QPulseAudioInternal::stateToQString(state);
-#endif
- switch (state) {
- case PA_STREAM_CREATING:
- case PA_STREAM_READY:
- case PA_STREAM_TERMINATED:
- break;
-
- case PA_STREAM_FAILED:
- default:
- qWarning() << QString("Stream error: %1").arg(pa_strerror(pa_context_errno(pa_stream_get_context(stream))));
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
- break;
- }
-}
-
-static void outputStreamUnderflowCallback(pa_stream *stream, void *userdata)
-{
- Q_UNUSED(stream);
- ((QPulseAudioOutput*)userdata)->streamUnderflowCallback();
-}
-
-static void outputStreamOverflowCallback(pa_stream *stream, void *userdata)
-{
- Q_UNUSED(stream);
- Q_UNUSED(userdata);
- qWarning() << "Got a buffer overflow!";
-}
-
-static void outputStreamLatencyCallback(pa_stream *stream, void *userdata)
-{
- Q_UNUSED(stream);
- Q_UNUSED(userdata);
-
-#ifdef DEBUG_PULSE
- const pa_timing_info *info = pa_stream_get_timing_info(stream);
-
- qDebug() << "Write index corrupt: " << info->write_index_corrupt;
- qDebug() << "Write index: " << info->write_index;
- qDebug() << "Read index corrupt: " << info->read_index_corrupt;
- qDebug() << "Read index: " << info->read_index;
- qDebug() << "Sink usec: " << info->sink_usec;
- qDebug() << "Configured sink usec: " << info->configured_sink_usec;
-#endif
-}
-
-static void outputStreamSuccessCallback(pa_stream *stream, int success, void *userdata)
-{
- Q_UNUSED(stream);
- Q_UNUSED(success);
- Q_UNUSED(userdata);
-
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
-}
-
-static void outputStreamDrainComplete(pa_stream *stream, int success, void *userdata)
-{
- Q_UNUSED(stream);
- Q_UNUSED(success);
- Q_UNUSED(userdata);
-
-#ifdef DEBUG_PULSE
- qDebug() << "Draining completed successfully: " << (bool)success;
-#endif
-}
-
-static void streamAdjustPrebufferCallback(pa_stream *stream, int success, void *userdata)
-{
- Q_UNUSED(stream);
- Q_UNUSED(success);
- Q_UNUSED(userdata);
-
-#ifdef DEBUG_PULSE
- qDebug() << "Adjust prebuffer completed successfully: " << (bool)success;
-#endif
-}
-
-
-QPulseAudioOutput::QPulseAudioOutput(const QByteArray &device)
- : m_device(device)
- , m_errorState(QAudio::NoError)
- , m_deviceState(QAudio::StoppedState)
- , m_pullMode(true)
- , m_opened(false)
- , m_audioSource(0)
- , m_periodTime(0)
- , m_stream(0)
- , m_notifyInterval(1000)
- , m_periodSize(0)
- , m_bufferSize(0)
- , m_maxBufferSize(0)
- , m_totalTimeValue(0)
- , m_tickTimer(new QTimer(this))
- , m_audioBuffer(0)
- , m_resuming(false)
- , m_volume(1.0)
-{
- connect(m_tickTimer, SIGNAL(timeout()), SLOT(userFeed()));
-}
-
-QPulseAudioOutput::~QPulseAudioOutput()
-{
- close();
- disconnect(m_tickTimer, SIGNAL(timeout()));
- QCoreApplication::processEvents();
-}
-
-void QPulseAudioOutput::setError(QAudio::Error error)
-{
- if (m_errorState == error)
- return;
-
- m_errorState = error;
- emit errorChanged(error);
-}
-
-QAudio::Error QPulseAudioOutput::error() const
-{
- return m_errorState;
-}
-
-void QPulseAudioOutput::setState(QAudio::State state)
-{
- if (m_deviceState == state)
- return;
-
- m_deviceState = state;
- emit stateChanged(state);
-}
-
-QAudio::State QPulseAudioOutput::state() const
-{
- return m_deviceState;
-}
-
-void QPulseAudioOutput::streamUnderflowCallback()
-{
- if (m_deviceState != QAudio::IdleState && !m_resuming) {
- setError(QAudio::UnderrunError);
- setState(QAudio::IdleState);
- }
-}
-
-void QPulseAudioOutput::start(QIODevice *device)
-{
- setState(QAudio::StoppedState);
- setError(QAudio::NoError);
-
- // Handle change of mode
- if (m_audioSource && !m_pullMode) {
- delete m_audioSource;
- }
- m_audioSource = 0;
-
- close();
-
- m_pullMode = true;
- m_audioSource = device;
-
- if (!open()) {
- m_audioSource = 0;
- return;
- }
-
- setState(QAudio::ActiveState);
-}
-
-QIODevice *QPulseAudioOutput::start()
-{
- setState(QAudio::StoppedState);
- setError(QAudio::NoError);
-
- // Handle change of mode
- if (m_audioSource && !m_pullMode) {
- delete m_audioSource;
- }
- m_audioSource = 0;
-
- close();
-
- m_pullMode = false;
-
- if (!open())
- return nullptr;
-
- m_audioSource = new PulseOutputPrivate(this);
- m_audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered);
-
- setState(QAudio::IdleState);
-
- return m_audioSource;
-}
-
-bool QPulseAudioOutput::open()
-{
- if (m_opened)
- return true;
-
- 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);
- return false;
- }
-
- pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(m_format);
-
- if (!pa_sample_spec_valid(&spec)) {
- setError(QAudio::OpenError);
- setState(QAudio::StoppedState);
- emit stateChanged(m_deviceState);
- return false;
- }
-
- m_spec = spec;
- m_totalTimeValue = 0;
-
- 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
-
- pulseEngine->lock();
-
- qint64 bytesPerSecond = m_format.sampleRate() * m_format.channelCount() * m_format.sampleSize() / 8;
-
- pa_proplist *propList = pa_proplist_new();
- if (!m_category.isNull())
- pa_proplist_sets(propList, PA_PROP_MEDIA_ROLE, m_category.toLatin1().constData());
-
- static const auto mapName = qEnvironmentVariable("QT_PA_CHANNEL_MAP");
- pa_channel_map_def_t mapDef = PA_CHANNEL_MAP_DEFAULT;
- if (mapName == QLatin1String("ALSA"))
- mapDef = PA_CHANNEL_MAP_ALSA;
- else if (mapName == QLatin1String("AUX"))
- mapDef = PA_CHANNEL_MAP_AUX;
- else if (mapName == QLatin1String("WAVEEX"))
- mapDef = PA_CHANNEL_MAP_WAVEEX;
- else if (mapName == QLatin1String("OSS"))
- mapDef = PA_CHANNEL_MAP_OSS;
- else if (!mapName.isEmpty())
- qWarning() << "Unknown pulse audio channel mapping definition:" << mapName;
-
- pa_channel_map m;
- auto channelMap = pa_channel_map_init_extend(&m, m_spec.channels, mapDef);
- if (!channelMap)
- qWarning() << "QAudioOutput: pa_channel_map_init_extend() Could not initialize channel map";
-
- m_stream = pa_stream_new_with_proplist(pulseEngine->context(), m_streamName.constData(), &m_spec, channelMap, propList);
- if (!m_stream) {
- qWarning() << "QAudioOutput: pa_stream_new_with_proplist() failed!";
- pulseEngine->unlock();
- setError(QAudio::OpenError);
- setState(QAudio::StoppedState);
- emit stateChanged(m_deviceState);
- return false;
- }
-
- pa_proplist_free(propList);
-
- pa_stream_set_state_callback(m_stream, outputStreamStateCallback, this);
- pa_stream_set_write_callback(m_stream, outputStreamWriteCallback, this);
-
- pa_stream_set_underflow_callback(m_stream, outputStreamUnderflowCallback, this);
- pa_stream_set_overflow_callback(m_stream, outputStreamOverflowCallback, this);
- pa_stream_set_latency_update_callback(m_stream, outputStreamLatencyCallback, this);
-
- if (m_bufferSize <= 0 && m_category == LOW_LATENCY_CATEGORY_NAME) {
- m_bufferSize = bytesPerSecond * LowLatencyBufferSizeMs / qint64(1000);
- }
-
- 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;
-
- if (pa_stream_connect_playback(m_stream, m_device.data(), (m_bufferSize > 0) ? &requestedBuffer : NULL, (pa_stream_flags_t)0, NULL, NULL) < 0) {
- qWarning() << "pa_stream_connect_playback() failed!";
- pa_stream_unref(m_stream);
- m_stream = 0;
- pulseEngine->unlock();
- setError(QAudio::OpenError);
- setState(QAudio::StoppedState);
- emit stateChanged(m_deviceState);
- return false;
- }
-
- while (pa_stream_get_state(m_stream) != PA_STREAM_READY)
- pa_threaded_mainloop_wait(pulseEngine->mainloop());
-
- const pa_buffer_attr *buffer = pa_stream_get_buffer_attr(m_stream);
- m_periodTime = (m_category == LOW_LATENCY_CATEGORY_NAME) ? LowLatencyPeriodTimeMs : 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];
-
- 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, NULL);
- if (o)
- pa_operation_unref(o);
- }
-
-#ifdef DEBUG_PULSE
- qDebug() << "Buffering info:";
- qDebug() << "\tMax length: " << buffer->maxlength;
- qDebug() << "\tTarget length: " << buffer->tlength;
- qDebug() << "\tPre-buffering: " << buffer->prebuf;
- qDebug() << "\tMinimum request: " << buffer->minreq;
- qDebug() << "\tFragment size: " << buffer->fragsize;
-#endif
-
- pulseEngine->unlock();
-
- connect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioOutput::onPulseContextFailed);
-
- m_opened = true;
-
- m_tickTimer->start(m_periodTime);
-
- m_elapsedTimeOffset = 0;
- m_timeStamp.restart();
- m_clockStamp.restart();
-
- return true;
-}
-
-void QPulseAudioOutput::close()
-{
- if (!m_opened)
- return;
-
- m_tickTimer->stop();
-
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
-
- if (m_stream) {
- pulseEngine->lock();
-
- pa_stream_set_state_callback(m_stream, 0, 0);
- pa_stream_set_write_callback(m_stream, 0, 0);
- pa_stream_set_underflow_callback(m_stream, 0, 0);
- pa_stream_set_overflow_callback(m_stream, 0, 0);
- pa_stream_set_latency_update_callback(m_stream, 0, 0);
-
- pa_operation *o = pa_stream_drain(m_stream, outputStreamDrainComplete, NULL);
- if (o)
- pa_operation_unref(o);
-
- pa_stream_disconnect(m_stream);
- pa_stream_unref(m_stream);
- m_stream = NULL;
-
- pulseEngine->unlock();
- }
-
- disconnect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioOutput::onPulseContextFailed);
-
- if (!m_pullMode && m_audioSource) {
- delete m_audioSource;
- m_audioSource = 0;
- }
- m_opened = false;
- if (m_audioBuffer) {
- delete[] m_audioBuffer;
- m_audioBuffer = 0;
- }
-}
-
-void QPulseAudioOutput::userFeed()
-{
- if (m_deviceState == QAudio::StoppedState || m_deviceState == QAudio::SuspendedState)
- return;
-
- m_resuming = false;
-
- if (m_pullMode) {
- 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;
-
- int audioBytesPulled = m_audioSource->read(m_audioBuffer, input);
- Q_ASSERT(audioBytesPulled <= input);
- if (m_audioBuffer && audioBytesPulled > 0) {
- if (audioBytesPulled > input) {
- qWarning() << "QPulseAudioOutput::userFeed() - Invalid audio data size provided from user:"
- << 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);
- }
- }
- }
-
- if (m_deviceState != QAudio::ActiveState)
- return;
-
- if (m_notifyInterval && (m_timeStamp.elapsed() + m_elapsedTimeOffset) > m_notifyInterval) {
- emit notify();
- m_elapsedTimeOffset = m_timeStamp.restart() + m_elapsedTimeOffset - m_notifyInterval;
- }
-}
-
-qint64 QPulseAudioOutput::write(const char *data, qint64 len)
-{
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
-
- pulseEngine->lock();
-
- len = qMin(len, static_cast<qint64>(pa_stream_writable_size(m_stream)));
-
- if (m_volume < 1.0f) {
- // Don't use PulseAudio volume, as it might affect all other streams of the same category
- // or even affect the system volume if flat volumes are enabled
- void *dest = NULL;
- size_t nbytes = len;
- if (pa_stream_begin_write(m_stream, &dest, &nbytes) < 0) {
- qWarning("QAudioOutput(pulseaudio): pa_stream_begin_write, error = %s",
- pa_strerror(pa_context_errno(pulseEngine->context())));
- setError(QAudio::IOError);
- return 0;
- }
-
- len = int(nbytes);
- QAudioHelperInternal::qMultiplySamples(m_volume, m_format, data, dest, len);
- data = reinterpret_cast<char *>(dest);
- }
-
- if (pa_stream_write(m_stream, data, len, NULL, 0, PA_SEEK_RELATIVE) < 0) {
- qWarning("QAudioOutput(pulseaudio): pa_stream_write, error = %s",
- pa_strerror(pa_context_errno(pulseEngine->context())));
- setError(QAudio::IOError);
- return 0;
- }
-
- pulseEngine->unlock();
- m_totalTimeValue += len;
-
- setError(QAudio::NoError);
- setState(QAudio::ActiveState);
-
- return len;
-}
-
-void QPulseAudioOutput::stop()
-{
- if (m_deviceState == QAudio::StoppedState)
- return;
-
- close();
-
- setError(QAudio::NoError);
- setState(QAudio::StoppedState);
-}
-
-int QPulseAudioOutput::bytesFree() const
-{
- if (m_deviceState != QAudio::ActiveState && m_deviceState != QAudio::IdleState)
- return 0;
-
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- pulseEngine->lock();
- int writableSize = pa_stream_writable_size(m_stream);
- pulseEngine->unlock();
- return writableSize;
-}
-
-int QPulseAudioOutput::periodSize() const
-{
- return m_periodSize;
-}
-
-void QPulseAudioOutput::setBufferSize(int value)
-{
- m_bufferSize = value;
-}
-
-int QPulseAudioOutput::bufferSize() const
-{
- return m_bufferSize;
-}
-
-void QPulseAudioOutput::setNotifyInterval(int ms)
-{
- m_notifyInterval = qMax(0, ms);
-}
-
-int QPulseAudioOutput::notifyInterval() const
-{
- return m_notifyInterval;
-}
-
-qint64 QPulseAudioOutput::processedUSecs() const
-{
- qint64 result = qint64(1000000) * m_totalTimeValue /
- (m_format.channelCount() * (m_format.sampleSize() / 8)) /
- m_format.sampleRate();
-
- return result;
-}
-
-void QPulseAudioOutput::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, NULL);
- pulseEngine->wait(operation);
- pa_operation_unref(operation);
-
- operation = pa_stream_trigger(m_stream, outputStreamSuccessCallback, NULL);
- pulseEngine->wait(operation);
- pa_operation_unref(operation);
-
- pulseEngine->unlock();
-
- m_tickTimer->start(m_periodTime);
-
- setState(m_pullMode ? QAudio::ActiveState : QAudio::IdleState);
- setError(QAudio::NoError);
- }
-}
-
-void QPulseAudioOutput::setFormat(const QAudioFormat &format)
-{
- m_format = format;
-}
-
-QAudioFormat QPulseAudioOutput::format() const
-{
- return m_format;
-}
-
-void QPulseAudioOutput::suspend()
-{
- if (m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::IdleState) {
- setError(QAudio::NoError);
- setState(QAudio::SuspendedState);
-
- m_tickTimer->stop();
-
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- pa_operation *operation;
-
- pulseEngine->lock();
-
- operation = pa_stream_cork(m_stream, 1, outputStreamSuccessCallback, NULL);
- pulseEngine->wait(operation);
- pa_operation_unref(operation);
-
- pulseEngine->unlock();
- }
-}
-
-qint64 QPulseAudioOutput::elapsedUSecs() const
-{
- if (m_deviceState == QAudio::StoppedState)
- return 0;
-
- return m_clockStamp.elapsed() * qint64(1000);
-}
-
-void QPulseAudioOutput::reset()
-{
- stop();
-}
-
-PulseOutputPrivate::PulseOutputPrivate(QPulseAudioOutput *audio)
-{
- m_audioDevice = qobject_cast<QPulseAudioOutput*>(audio);
-}
-
-qint64 PulseOutputPrivate::readData(char *data, qint64 len)
-{
- Q_UNUSED(data);
- Q_UNUSED(len);
-
- return 0;
-}
-
-qint64 PulseOutputPrivate::writeData(const char *data, qint64 len)
-{
- int retry = 0;
- 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));
- if (chunk <= 0)
- retry++;
- written+=chunk;
- if (retry > 10)
- return written;
- }
- }
-
- return written;
-}
-
-void QPulseAudioOutput::setVolume(qreal vol)
-{
- if (qFuzzyCompare(m_volume, vol))
- return;
-
- m_volume = qBound(qreal(0), vol, qreal(1));
-}
-
-qreal QPulseAudioOutput::volume() const
-{
- return m_volume;
-}
-
-void QPulseAudioOutput::setCategory(const QString &category)
-{
- if (m_category != category) {
- m_category = category;
- }
-}
-
-QString QPulseAudioOutput::category() const
-{
- return m_category;
-}
-
-void QPulseAudioOutput::onPulseContextFailed()
-{
- close();
-
- setError(QAudio::FatalError);
- setState(QAudio::StoppedState);
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qaudiooutput_pulse.cpp"
diff --git a/src/plugins/pulseaudio/qaudiooutput_pulse.h b/src/plugins/pulseaudio/qaudiooutput_pulse.h
deleted file mode 100644
index e11f2ab2f..000000000
--- a/src/plugins/pulseaudio/qaudiooutput_pulse.h
+++ /dev/null
@@ -1,166 +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 QAUDIOOUTPUTPULSE_H
-#define QAUDIOOUTPUTPULSE_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/qfile.h>
-#include <QtCore/qtimer.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qelapsedtimer.h>
-#include <QtCore/qiodevice.h>
-
-#include "qaudio.h"
-#include "qaudiodeviceinfo.h"
-#include "qaudiosystem.h"
-
-#include <pulse/pulseaudio.h>
-
-QT_BEGIN_NAMESPACE
-
-class QPulseAudioOutput : public QAbstractAudioOutput
-{
- friend class PulseOutputPrivate;
- Q_OBJECT
-
-public:
- QPulseAudioOutput(const QByteArray &device);
- ~QPulseAudioOutput();
-
- void start(QIODevice *device) override;
- QIODevice *start() override;
- void stop() override;
- void reset() override;
- void suspend() override;
- void resume() override;
- int bytesFree() const override;
- int periodSize() const override;
- void setBufferSize(int value) override;
- int bufferSize() const override;
- void setNotifyInterval(int milliSeconds) override;
- int notifyInterval() const override;
- qint64 processedUSecs() const override;
- qint64 elapsedUSecs() const override;
- QAudio::Error error() const override;
- QAudio::State state() const override;
- void setFormat(const QAudioFormat &format) override;
- QAudioFormat format() const override;
-
- void setVolume(qreal volume) override;
- qreal volume() const override;
-
- void setCategory(const QString &category) override;
- QString category() const override;
-
-public:
- void streamUnderflowCallback();
-
-private:
- void setState(QAudio::State state);
- void setError(QAudio::Error error);
-
- bool open();
- void close();
- qint64 write(const char *data, qint64 len);
-
-private Q_SLOTS:
- void userFeed();
- void onPulseContextFailed();
-
-private:
- QByteArray m_device;
- QByteArray m_streamName;
- QAudioFormat m_format;
- QAudio::Error m_errorState;
- QAudio::State m_deviceState;
- bool m_pullMode;
- bool m_opened;
- QIODevice *m_audioSource;
- QTimer m_periodTimer;
- int m_periodTime;
- pa_stream *m_stream;
- int m_notifyInterval;
- int m_periodSize;
- int m_bufferSize;
- int m_maxBufferSize;
- QElapsedTimer m_clockStamp;
- qint64 m_totalTimeValue;
- QTimer *m_tickTimer;
- char *m_audioBuffer;
- QElapsedTimer m_timeStamp;
- qint64 m_elapsedTimeOffset;
- bool m_resuming;
- QString m_category;
-
- qreal m_volume;
- pa_sample_spec m_spec;
-};
-
-class PulseOutputPrivate : public QIODevice
-{
- friend class QPulseAudioOutput;
- Q_OBJECT
-
-public:
- PulseOutputPrivate(QPulseAudioOutput *audio);
- virtual ~PulseOutputPrivate() {}
-
-protected:
- qint64 readData(char *data, qint64 len) override;
- qint64 writeData(const char *data, qint64 len) override;
-
-private:
- QPulseAudioOutput *m_audioDevice;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/pulseaudio/qpulseaudioengine.cpp b/src/plugins/pulseaudio/qpulseaudioengine.cpp
deleted file mode 100644
index 653fea57e..000000000
--- a/src/plugins/pulseaudio/qpulseaudioengine.cpp
+++ /dev/null
@@ -1,482 +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 <QtCore/qdebug.h>
-
-#include <qaudiodeviceinfo.h>
-#include "qpulseaudioengine.h"
-#include "qaudiodeviceinfo_pulse.h"
-#include "qaudiooutput_pulse.h"
-#include "qpulsehelpers.h"
-#include <sys/types.h>
-#include <unistd.h>
-
-QT_BEGIN_NAMESPACE
-
-static void serverInfoCallback(pa_context *context, const pa_server_info *info, void *userdata)
-{
- if (!info) {
- qWarning() << QString("Failed to get server information: %s").arg(pa_strerror(pa_context_errno(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
-
- QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine*>(userdata);
- pulseEngine->m_serverLock.lockForWrite();
- pulseEngine->m_defaultSink = info->default_sink_name;
- pulseEngine->m_defaultSource = info->default_source_name;
- pulseEngine->m_serverLock.unlock();
-
- 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);
-
- if (isLast < 0) {
- qWarning() << QString("Failed to get sink information: %s").arg(pa_strerror(pa_context_errno(context)));
- return;
- }
-
- if (isLast) {
- pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
- return;
- }
-
- 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
-
- QAudioFormat format = QPulseAudioInternal::sampleSpecToAudioFormat(info->sample_spec);
-
- QWriteLocker locker(&pulseEngine->m_sinkLock);
- pulseEngine->m_preferredFormats.insert(info->name, format);
- pulseEngine->m_sinks.insert(info->index, info->name);
-}
-
-static void sourceInfoCallback(pa_context *context, const pa_source_info *info, int isLast, void *userdata)
-{
- Q_UNUSED(context);
- QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine*>(userdata);
-
- if (isLast) {
- pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
- return;
- }
-
- 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
-
- QAudioFormat format = QPulseAudioInternal::sampleSpecToAudioFormat(info->sample_spec);
-
- QWriteLocker locker(&pulseEngine->m_sourceLock);
- pulseEngine->m_preferredFormats.insert(info->name, format);
- pulseEngine->m_sources.insert(info->index, info->name);
-}
-
-static void event_cb(pa_context* context, pa_subscription_event_type_t t, uint32_t index, void* userdata)
-{
- QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine*>(userdata);
-
- int type = t & PA_SUBSCRIPTION_EVENT_TYPE_MASK;
- int facility = t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
-
- switch (type) {
- case PA_SUBSCRIPTION_EVENT_NEW:
- 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");
- 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");
- 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");
- break;
- }
- default:
- break;
- }
- break;
- case PA_SUBSCRIPTION_EVENT_REMOVE:
- switch (facility) {
- case PA_SUBSCRIPTION_EVENT_SINK:
- pulseEngine->m_sinkLock.lockForWrite();
- pulseEngine->m_preferredFormats.remove(pulseEngine->m_sinks.value(index));
- pulseEngine->m_sinks.remove(index);
- pulseEngine->m_sinkLock.unlock();
- break;
- case PA_SUBSCRIPTION_EVENT_SOURCE:
- pulseEngine->m_sourceLock.lockForWrite();
- pulseEngine->m_preferredFormats.remove(pulseEngine->m_sources.value(index));
- pulseEngine->m_sources.remove(index);
- pulseEngine->m_sourceLock.unlock();
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
-}
-
-static void contextStateCallbackInit(pa_context *context, void *userdata)
-{
- Q_UNUSED(context);
-#ifdef DEBUG_PULSE
- qDebug() << QPulseAudioInternal::stateToQString(pa_context_get_state(context));
-#endif
- QPulseAudioEngine *pulseEngine = reinterpret_cast<QPulseAudioEngine*>(userdata);
- pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
-}
-
-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 (state == PA_CONTEXT_FAILED)
- QMetaObject::invokeMethod(self, "onContextFailed", Qt::QueuedConnection);
-}
-
-Q_GLOBAL_STATIC(QPulseAudioEngine, pulseEngine);
-
-QPulseAudioEngine::QPulseAudioEngine(QObject *parent)
- : QObject(parent)
- , m_mainLoopApi(0)
- , m_context(0)
- , m_prepared(false)
-{
- prepare();
-}
-
-QPulseAudioEngine::~QPulseAudioEngine()
-{
- if (m_prepared)
- release();
-}
-
-void QPulseAudioEngine::prepare()
-{
- bool keepGoing = true;
- bool ok = true;
-
- m_mainLoop = pa_threaded_mainloop_new();
- if (m_mainLoop == 0) {
- qWarning("PulseAudioService: unable to create pulseaudio mainloop");
- return;
- }
-
- if (pa_threaded_mainloop_start(m_mainLoop) != 0) {
- qWarning("PulseAudioService: unable to start pulseaudio mainloop");
- pa_threaded_mainloop_free(m_mainLoop);
- m_mainLoop = 0;
- return;
- }
-
- m_mainLoopApi = pa_threaded_mainloop_get_api(m_mainLoop);
-
- lock();
-
- m_context = pa_context_new(m_mainLoopApi, QString(QLatin1String("QtPulseAudio:%1")).arg(::getpid()).toLatin1().constData());
-
- if (m_context == 0) {
- qWarning("PulseAudioService: Unable to create new pulseaudio context");
- pa_threaded_mainloop_unlock(m_mainLoop);
- pa_threaded_mainloop_free(m_mainLoop);
- m_mainLoop = 0;
- onContextFailed();
- return;
- }
-
- pa_context_set_state_callback(m_context, contextStateCallbackInit, this);
-
- if (pa_context_connect(m_context, 0, (pa_context_flags_t)0, 0) < 0) {
- qWarning("PulseAudioService: pa_context_connect() failed");
- pa_context_unref(m_context);
- pa_threaded_mainloop_unlock(m_mainLoop);
- pa_threaded_mainloop_free(m_mainLoop);
- m_mainLoop = 0;
- m_context = 0;
- return;
- }
-
- pa_threaded_mainloop_wait(m_mainLoop);
-
- while (keepGoing) {
- switch (pa_context_get_state(m_context)) {
- case PA_CONTEXT_CONNECTING:
- case PA_CONTEXT_AUTHORIZING:
- case PA_CONTEXT_SETTING_NAME:
- break;
-
- case PA_CONTEXT_READY:
-#ifdef DEBUG_PULSE
- qDebug("Connection established.");
-#endif
- keepGoing = false;
- break;
-
- case PA_CONTEXT_TERMINATED:
- qCritical("PulseAudioService: Context terminated.");
- keepGoing = false;
- ok = false;
- break;
-
- case PA_CONTEXT_FAILED:
- default:
- qCritical() << QString("PulseAudioService: Connection failure: %1").arg(pa_strerror(pa_context_errno(m_context)));
- keepGoing = false;
- ok = false;
- }
-
- if (keepGoing)
- pa_threaded_mainloop_wait(m_mainLoop);
- }
-
- if (ok) {
- 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),
- NULL, NULL);
- if (op)
- pa_operation_unref(op);
- else
- qWarning("PulseAudioService: failed to subscribe to context notifications");
- } else {
- pa_context_unref(m_context);
- m_context = 0;
- }
-
- unlock();
-
- if (ok) {
- updateDevices();
- m_prepared = true;
- } else {
- pa_threaded_mainloop_free(m_mainLoop);
- m_mainLoop = 0;
- onContextFailed();
- }
-}
-
-void QPulseAudioEngine::release()
-{
- if (!m_prepared)
- return;
-
- if (m_context) {
- pa_context_disconnect(m_context);
- pa_context_unref(m_context);
- m_context = 0;
- }
-
- if (m_mainLoop) {
- pa_threaded_mainloop_stop(m_mainLoop);
- pa_threaded_mainloop_free(m_mainLoop);
- m_mainLoop = 0;
- }
-
- m_prepared = false;
-}
-
-void QPulseAudioEngine::updateDevices()
-{
- lock();
-
- // Get default input and output devices
- pa_operation *operation = pa_context_get_server_info(m_context, serverInfoCallback, this);
- if (operation) {
- while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
- pa_threaded_mainloop_wait(m_mainLoop);
- pa_operation_unref(operation);
- } else {
- qWarning("PulseAudioService: failed to get server info");
- }
-
- // Get output devices
- operation = pa_context_get_sink_info_list(m_context, sinkInfoCallback, this);
- if (operation) {
- while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
- pa_threaded_mainloop_wait(m_mainLoop);
- pa_operation_unref(operation);
- } else {
- qWarning("PulseAudioService: failed to get sink info");
- }
-
- // Get input devices
- operation = pa_context_get_source_info_list(m_context, sourceInfoCallback, this);
- if (operation) {
- while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
- pa_threaded_mainloop_wait(m_mainLoop);
- pa_operation_unref(operation);
- } else {
- qWarning("PulseAudioService: failed to get source info");
- }
-
- unlock();
-}
-
-void QPulseAudioEngine::onContextFailed()
-{
- // Give a chance to the connected slots to still use the Pulse main loop before releasing it.
- emit contextFailed();
-
- release();
-
- // Try to reconnect later
- QTimer::singleShot(3000, this, SLOT(prepare()));
-}
-
-QPulseAudioEngine *QPulseAudioEngine::instance()
-{
- return pulseEngine();
-}
-
-QList<QByteArray> QPulseAudioEngine::availableDevices(QAudio::Mode mode) const
-{
- QList<QByteArray> devices;
- QByteArray defaultDevice;
-
- m_serverLock.lockForRead();
-
- if (mode == QAudio::AudioOutput) {
- QReadLocker locker(&m_sinkLock);
- devices = m_sinks.values();
- defaultDevice = m_defaultSink;
- } else {
- QReadLocker locker(&m_sourceLock);
- devices = m_sources.values();
- defaultDevice = m_defaultSource;
- }
-
- m_serverLock.unlock();
-
- // Swap the default device to index 0
- devices.removeOne(defaultDevice);
- devices.prepend(defaultDevice);
-
- return devices;
-}
-
-QByteArray QPulseAudioEngine::defaultDevice(QAudio::Mode mode) const
-{
- return (mode == QAudio::AudioOutput) ? m_defaultSink : m_defaultSource;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/pulseaudio/qpulseaudioengine.h b/src/plugins/pulseaudio/qpulseaudioengine.h
deleted file mode 100644
index a19be1841..000000000
--- a/src/plugins/pulseaudio/qpulseaudioengine.h
+++ /dev/null
@@ -1,129 +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 QPULSEAUDIOENGINE_H
-#define QPULSEAUDIOENGINE_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/qmap.h>
-#include <QtCore/qbytearray.h>
-#include <QtCore/qreadwritelock.h>
-#include <QtMultimedia/qaudiosystemplugin.h>
-#include <pulse/pulseaudio.h>
-#include "qpulsehelpers.h"
-#include <qaudioformat.h>
-
-QT_BEGIN_NAMESPACE
-
-class QPulseAudioEngine : public QObject
-{
- Q_OBJECT
-
-public:
- QPulseAudioEngine(QObject *parent = 0);
- ~QPulseAudioEngine();
-
- static QPulseAudioEngine *instance();
- pa_threaded_mainloop *mainloop() { return m_mainLoop; }
- pa_context *context() { return m_context; }
-
- inline void lock()
- {
- if (m_mainLoop)
- pa_threaded_mainloop_lock(m_mainLoop);
- }
-
- inline void unlock()
- {
- if (m_mainLoop)
- pa_threaded_mainloop_unlock(m_mainLoop);
- }
-
- inline void wait(pa_operation *op)
- {
- while (m_mainLoop && pa_operation_get_state(op) == PA_OPERATION_RUNNING)
- pa_threaded_mainloop_wait(m_mainLoop);
- }
-
- QList<QByteArray> availableDevices(QAudio::Mode mode) const;
- QByteArray defaultDevice(QAudio::Mode mode) const;
-
-Q_SIGNALS:
- void contextFailed();
-
-private Q_SLOTS:
- void prepare();
- void onContextFailed();
-
-private:
- void updateDevices();
- void release();
-
-public:
- QMap<int, QByteArray> m_sinks;
- QMap<int, QByteArray> m_sources;
- QMap<QByteArray, QAudioFormat> m_preferredFormats;
-
- QByteArray m_defaultSink;
- QByteArray m_defaultSource;
-
- mutable QReadWriteLock m_sinkLock;
- mutable QReadWriteLock m_sourceLock;
- mutable QReadWriteLock m_serverLock;
-
-private:
- pa_mainloop_api *m_mainLoopApi;
- pa_threaded_mainloop *m_mainLoop;
- pa_context *m_context;
- bool m_prepared;
- };
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/pulseaudio/qpulseaudioplugin.cpp b/src/plugins/pulseaudio/qpulseaudioplugin.cpp
deleted file mode 100644
index 6b3019279..000000000
--- a/src/plugins/pulseaudio/qpulseaudioplugin.cpp
+++ /dev/null
@@ -1,85 +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 <qaudiodeviceinfo.h>
-
-#include "qpulseaudioplugin.h"
-#include "qaudiodeviceinfo_pulse.h"
-#include "qaudiooutput_pulse.h"
-#include "qaudioinput_pulse.h"
-#include "qpulseaudioengine.h"
-
-QT_BEGIN_NAMESPACE
-
-QPulseAudioPlugin::QPulseAudioPlugin(QObject *parent)
- : QAudioSystemPlugin(parent)
- , m_pulseEngine(QPulseAudioEngine::instance())
-{
-}
-
-QByteArray QPulseAudioPlugin::defaultDevice(QAudio::Mode mode) const
-{
- return m_pulseEngine->defaultDevice(mode);
-}
-
-QList<QByteArray> QPulseAudioPlugin::availableDevices(QAudio::Mode mode) const
-{
- return m_pulseEngine->availableDevices(mode);
-}
-
-QAbstractAudioInput *QPulseAudioPlugin::createInput(const QByteArray &device)
-{
- QPulseAudioInput *input = new QPulseAudioInput(device);
- return input;
-}
-
-QAbstractAudioOutput *QPulseAudioPlugin::createOutput(const QByteArray &device)
-{
-
- QPulseAudioOutput *output = new QPulseAudioOutput(device);
- return output;
-}
-
-QAbstractAudioDeviceInfo *QPulseAudioPlugin::createDeviceInfo(const QByteArray &device, QAudio::Mode mode)
-{
- QPulseAudioDeviceInfo *deviceInfo = new QPulseAudioDeviceInfo(device, mode);
- return deviceInfo;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/pulseaudio/qpulseaudioplugin.h b/src/plugins/pulseaudio/qpulseaudioplugin.h
deleted file mode 100644
index 7d27cad48..000000000
--- a/src/plugins/pulseaudio/qpulseaudioplugin.h
+++ /dev/null
@@ -1,73 +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 QPULSEAUDIOPLUGIN_H
-#define QPULSEAUDIOPLUGIN_H
-
-#include <QtMultimedia/qaudiosystemplugin.h>
-#include <QtMultimedia/private/qaudiosystempluginext_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class QPulseAudioEngine;
-
-class QPulseAudioPlugin : public QAudioSystemPlugin, public QAudioSystemPluginExtension
-{
- Q_OBJECT
-
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.audiosystemfactory/5.0" FILE "pulseaudio.json")
- Q_INTERFACES(QAudioSystemPluginExtension)
-
-public:
- QPulseAudioPlugin(QObject *parent = 0);
- ~QPulseAudioPlugin() {}
-
- QByteArray defaultDevice(QAudio::Mode mode) const override;
- QList<QByteArray> availableDevices(QAudio::Mode mode) const override;
- QAbstractAudioInput *createInput(const QByteArray &device) override;
- QAbstractAudioOutput *createOutput(const QByteArray &device) override;
- QAbstractAudioDeviceInfo *createDeviceInfo(const QByteArray &device, QAudio::Mode mode) override;
-
-private:
- QPulseAudioEngine *m_pulseEngine;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/pulseaudio/qpulsehelpers.cpp b/src/plugins/pulseaudio/qpulsehelpers.cpp
deleted file mode 100644
index 0604c97f5..000000000
--- a/src/plugins/pulseaudio/qpulsehelpers.cpp
+++ /dev/null
@@ -1,211 +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 "qpulsehelpers.h"
-
-QT_BEGIN_NAMESPACE
-
-namespace QPulseAudioInternal
-{
-pa_sample_spec audioFormatToSampleSpec(const QAudioFormat &format)
-{
- pa_sample_spec spec;
-
- spec.rate = format.sampleRate();
- spec.channels = format.channelCount();
- spec.format = PA_SAMPLE_INVALID;
- const bool isBigEndian = (format.byteOrder() == QAudioFormat::BigEndian);
-
- if (format.sampleType() == QAudioFormat::UnSignedInt) {
- if (format.sampleSize() == 8)
- spec.format = PA_SAMPLE_U8;
- } else if (format.sampleType() == QAudioFormat::SignedInt) {
- if (format.sampleSize() == 16) {
- spec.format = isBigEndian ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
- } else if (format.sampleSize() == 24) {
- spec.format = isBigEndian ? PA_SAMPLE_S24BE : PA_SAMPLE_S24LE;
- } else if (format.sampleSize() == 32) {
- spec.format = isBigEndian ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
- }
- } else if (format.sampleType() == QAudioFormat::Float) {
- if (format.sampleSize() == 32)
- spec.format = isBigEndian ? PA_SAMPLE_FLOAT32BE : PA_SAMPLE_FLOAT32LE;
- }
-
- return spec;
-}
-
-#ifdef DEBUG_PULSE
-QString stateToQString(pa_stream_state_t state)
-{
- switch (state)
- {
- case PA_STREAM_UNCONNECTED: return "Unconnected";
- case PA_STREAM_CREATING: return "Creating";
- case PA_STREAM_READY: return "Ready";
- case PA_STREAM_FAILED: return "Failed";
- case PA_STREAM_TERMINATED: return "Terminated";
- }
-
- return QString("Unknown state: %0").arg(state);
-}
-
-QString sampleFormatToQString(pa_sample_format format)
-{
- switch (format)
- {
- case PA_SAMPLE_U8: return "Unsigned 8 Bit PCM.";
- case PA_SAMPLE_ALAW: return "8 Bit a-Law ";
- case PA_SAMPLE_ULAW: return "8 Bit mu-Law";
- case PA_SAMPLE_S16LE: return "Signed 16 Bit PCM, little endian (PC).";
- case PA_SAMPLE_S16BE: return "Signed 16 Bit PCM, big endian.";
- case PA_SAMPLE_FLOAT32LE: return "32 Bit IEEE floating point, little endian (PC), range -1.0 to 1.0";
- case PA_SAMPLE_FLOAT32BE: return "32 Bit IEEE floating point, big endian, range -1.0 to 1.0";
- case PA_SAMPLE_S32LE: return "Signed 32 Bit PCM, little endian (PC).";
- case PA_SAMPLE_S32BE: return "Signed 32 Bit PCM, big endian.";
- case PA_SAMPLE_S24LE: return "Signed 24 Bit PCM packed, little endian (PC).";
- case PA_SAMPLE_S24BE: return "Signed 24 Bit PCM packed, big endian.";
- case PA_SAMPLE_S24_32LE: return "Signed 24 Bit PCM in LSB of 32 Bit words, little endian (PC).";
- case PA_SAMPLE_S24_32BE: return "Signed 24 Bit PCM in LSB of 32 Bit words, big endian.";
- case PA_SAMPLE_MAX: return "Upper limit of valid sample types.";
- case PA_SAMPLE_INVALID: return "Invalid sample format";
- }
-
- return QString("Invalid value: %0").arg(format);
-}
-
-QString stateToQString(pa_context_state_t state)
-{
- switch (state)
- {
- case PA_CONTEXT_UNCONNECTED: return "Unconnected";
- case PA_CONTEXT_CONNECTING: return "Connecting";
- case PA_CONTEXT_AUTHORIZING: return "Authorizing";
- case PA_CONTEXT_SETTING_NAME: return "Setting Name";
- case PA_CONTEXT_READY: return "Ready";
- case PA_CONTEXT_FAILED: return "Failed";
- case PA_CONTEXT_TERMINATED: return "Terminated";
- }
-
- return QString("Unknown state: %0").arg(state);
-}
-#endif
-
-QAudioFormat sampleSpecToAudioFormat(pa_sample_spec spec)
-{
- QAudioFormat format;
- format.setSampleRate(spec.rate);
- format.setChannelCount(spec.channels);
- format.setCodec("audio/pcm");
-
- switch (spec.format) {
- case PA_SAMPLE_U8:
- format.setByteOrder(QAudioFormat::LittleEndian);
- format.setSampleType(QAudioFormat::UnSignedInt);
- format.setSampleSize(8);
- break;
- case PA_SAMPLE_ALAW:
- // TODO:
- break;
- case PA_SAMPLE_ULAW:
- // TODO:
- break;
- case PA_SAMPLE_S16LE:
- format.setByteOrder(QAudioFormat::LittleEndian);
- format.setSampleType(QAudioFormat::SignedInt);
- format.setSampleSize(16);
- break;
- case PA_SAMPLE_S16BE:
- format.setByteOrder(QAudioFormat::BigEndian);
- format.setSampleType(QAudioFormat::SignedInt);
- format.setSampleSize(16);
- break;
- case PA_SAMPLE_FLOAT32LE:
- format.setByteOrder(QAudioFormat::LittleEndian);
- format.setSampleType(QAudioFormat::Float);
- format.setSampleSize(32);
- break;
- case PA_SAMPLE_FLOAT32BE:
- format.setByteOrder(QAudioFormat::BigEndian);
- format.setSampleType(QAudioFormat::Float);
- format.setSampleSize(32);
- break;
- case PA_SAMPLE_S32LE:
- format.setByteOrder(QAudioFormat::LittleEndian);
- format.setSampleType(QAudioFormat::SignedInt);
- format.setSampleSize(32);
- break;
- case PA_SAMPLE_S32BE:
- format.setByteOrder(QAudioFormat::BigEndian);
- format.setSampleType(QAudioFormat::SignedInt);
- format.setSampleSize(32);
- break;
- case PA_SAMPLE_S24LE:
- format.setByteOrder(QAudioFormat::LittleEndian);
- format.setSampleType(QAudioFormat::SignedInt);
- format.setSampleSize(24);
- break;
- case PA_SAMPLE_S24BE:
- format.setByteOrder(QAudioFormat::BigEndian);
- format.setSampleType(QAudioFormat::SignedInt);
- format.setSampleSize(24);
- break;
- case PA_SAMPLE_S24_32LE:
- format.setByteOrder(QAudioFormat::LittleEndian);
- format.setSampleType(QAudioFormat::SignedInt);
- format.setSampleSize(24);
- break;
- case PA_SAMPLE_S24_32BE:
- format.setByteOrder(QAudioFormat::BigEndian);
- format.setSampleType(QAudioFormat::SignedInt);
- format.setSampleSize(24);
- break;
- case PA_SAMPLE_MAX:
- case PA_SAMPLE_INVALID:
- default:
- format.setByteOrder(QAudioFormat::LittleEndian);
- format.setSampleType(QAudioFormat::Unknown);
- format.setSampleSize(0);
- }
-
- return format;
-}
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/pulseaudio/qpulsehelpers.h b/src/plugins/pulseaudio/qpulsehelpers.h
deleted file mode 100644
index 279cecc2f..000000000
--- a/src/plugins/pulseaudio/qpulsehelpers.h
+++ /dev/null
@@ -1,71 +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 QPULSEHELPER_H
-#define QPULSEHELPER_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 "qaudiodeviceinfo.h"
-#include <qaudioformat.h>
-#include <pulse/pulseaudio.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace QPulseAudioInternal
-{
-pa_sample_spec audioFormatToSampleSpec(const QAudioFormat &format);
-QString stateToQString(pa_stream_state_t state);
-QString stateToQString(pa_context_state_t state);
-QString sampleFormatToQString(pa_sample_format format);
-QAudioFormat sampleSpecToAudioFormat(pa_sample_spec spec);
-}
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx-audio/audio/audio.pro b/src/plugins/qnx-audio/audio/audio.pro
deleted file mode 100644
index bd69dfe1e..000000000
--- a/src/plugins/qnx-audio/audio/audio.pro
+++ /dev/null
@@ -1,22 +0,0 @@
-TARGET = qtmedia_qnx_audio
-
-QT += multimedia-private
-
-LIBS += -lasound
-
-HEADERS += qnxaudioplugin.h \
- qnxaudiodeviceinfo.h \
- qnxaudioinput.h \
- qnxaudiooutput.h \
- qnxaudioutils.h
-
-SOURCES += qnxaudioplugin.cpp \
- qnxaudiodeviceinfo.cpp \
- qnxaudioinput.cpp \
- qnxaudiooutput.cpp \
- qnxaudioutils.cpp
-
-OTHER_FILES += qnx_audio.json
-
-PLUGIN_TYPE = audio
-load(qt_plugin)
diff --git a/src/plugins/qnx-audio/audio/qnx_audio.json b/src/plugins/qnx-audio/audio/qnx_audio.json
deleted file mode 100644
index a31d52107..000000000
--- a/src/plugins/qnx-audio/audio/qnx_audio.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "Keys": ["default"]
-}
diff --git a/src/plugins/qnx-audio/audio/qnxaudiodeviceinfo.cpp b/src/plugins/qnx-audio/audio/qnxaudiodeviceinfo.cpp
deleted file mode 100644
index 7e73ca264..000000000
--- a/src/plugins/qnx-audio/audio/qnxaudiodeviceinfo.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "qnxaudiodeviceinfo.h"
-
-#include "qnxaudioutils.h"
-
-#include <sys/asoundlib.h>
-
-QT_BEGIN_NAMESPACE
-
-QnxAudioDeviceInfo::QnxAudioDeviceInfo(const QString &deviceName, QAudio::Mode mode)
- : m_name(deviceName),
- m_mode(mode)
-{
-}
-
-QnxAudioDeviceInfo::~QnxAudioDeviceInfo()
-{
-}
-
-QAudioFormat QnxAudioDeviceInfo::preferredFormat() const
-{
- QAudioFormat format;
- if (m_mode == QAudio::AudioOutput) {
- format.setSampleRate(44100);
- format.setChannelCount(2);
- format.setByteOrder(QAudioFormat::LittleEndian);
- format.setSampleType(QAudioFormat::SignedInt);
- format.setSampleSize(16);
- format.setCodec(QLatin1String("audio/pcm"));
- } else {
- format.setSampleRate(8000);
- format.setChannelCount(1);
- format.setSampleType(QAudioFormat::UnSignedInt);
- format.setSampleSize(8);
- format.setCodec(QLatin1String("audio/pcm"));
- if (!isFormatSupported(format)) {
- format.setChannelCount(2);
- format.setSampleSize(16);
- format.setSampleType(QAudioFormat::SignedInt);
- }
- }
- return format;
-}
-
-bool QnxAudioDeviceInfo::isFormatSupported(const QAudioFormat &format) const
-{
- if (!format.codec().startsWith(QLatin1String("audio/pcm")))
- return false;
-
- const int pcmMode = (m_mode == QAudio::AudioOutput) ? SND_PCM_OPEN_PLAYBACK : SND_PCM_OPEN_CAPTURE;
- snd_pcm_t *handle;
-
- int card = 0;
- int device = 0;
- if (snd_pcm_open_preferred(&handle, &card, &device, pcmMode) < 0)
- return false;
-
- snd_pcm_channel_info_t info;
- memset (&info, 0, sizeof(info));
- info.channel = (m_mode == QAudio::AudioOutput) ? SND_PCM_CHANNEL_PLAYBACK : SND_PCM_CHANNEL_CAPTURE;
-
- if (snd_pcm_plugin_info(handle, &info) < 0) {
- qWarning("QAudioDeviceInfo: couldn't get channel info");
- snd_pcm_close(handle);
- return false;
- }
-
- snd_pcm_channel_params_t params = QnxAudioUtils::formatToChannelParams(format, m_mode, info.max_fragment_size);
- const int errorCode = snd_pcm_plugin_params(handle, &params);
- snd_pcm_close(handle);
-
- return errorCode == 0;
-}
-
-QString QnxAudioDeviceInfo::deviceName() const
-{
- return m_name;
-}
-
-QStringList QnxAudioDeviceInfo::supportedCodecs()
-{
- return QStringList() << QLatin1String("audio/pcm");
-}
-
-QList<int> QnxAudioDeviceInfo::supportedSampleRates()
-{
- return QList<int>() << 8000 << 11025 << 22050 << 44100 << 48000;
-}
-
-QList<int> QnxAudioDeviceInfo::supportedChannelCounts()
-{
- return QList<int>() << 1 << 2;
-}
-
-QList<int> QnxAudioDeviceInfo::supportedSampleSizes()
-{
- return QList<int>() << 8 << 16 << 32;
-}
-
-QList<QAudioFormat::Endian> QnxAudioDeviceInfo::supportedByteOrders()
-{
- return QList<QAudioFormat::Endian>() << QAudioFormat::LittleEndian << QAudioFormat::BigEndian;
-}
-
-QList<QAudioFormat::SampleType> QnxAudioDeviceInfo::supportedSampleTypes()
-{
- return QList<QAudioFormat::SampleType>() << QAudioFormat::SignedInt << QAudioFormat::UnSignedInt << QAudioFormat::Float;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx-audio/audio/qnxaudiodeviceinfo.h b/src/plugins/qnx-audio/audio/qnxaudiodeviceinfo.h
deleted file mode 100644
index cb620be3b..000000000
--- a/src/plugins/qnx-audio/audio/qnxaudiodeviceinfo.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/****************************************************************************
-**
-** 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 QNXAUDIODEVICEINFO_H
-#define QNXAUDIODEVICEINFO_H
-
-#include "qaudiosystem.h"
-
-QT_BEGIN_NAMESPACE
-
-class QnxAudioDeviceInfo : public QAbstractAudioDeviceInfo
-{
- Q_OBJECT
-
-public:
- QnxAudioDeviceInfo(const QString &deviceName, QAudio::Mode mode);
- ~QnxAudioDeviceInfo();
-
- QAudioFormat preferredFormat() const override;
- bool isFormatSupported(const QAudioFormat &format) const override;
- QString deviceName() const override;
- QStringList supportedCodecs() override;
- QList<int> supportedSampleRates() override;
- QList<int> supportedChannelCounts() override;
- QList<int> supportedSampleSizes() override;
- QList<QAudioFormat::Endian> supportedByteOrders() override;
- QList<QAudioFormat::SampleType> supportedSampleTypes() override;
-
-private:
- const QString m_name;
- const QAudio::Mode m_mode;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx-audio/audio/qnxaudioinput.cpp b/src/plugins/qnx-audio/audio/qnxaudioinput.cpp
deleted file mode 100644
index 31347b7f3..000000000
--- a/src/plugins/qnx-audio/audio/qnxaudioinput.cpp
+++ /dev/null
@@ -1,453 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "qnxaudioinput.h"
-
-#include "qnxaudioutils.h"
-
-#include <private/qaudiohelpers_p.h>
-
-#include <QDebug>
-
-QT_BEGIN_NAMESPACE
-
-QnxAudioInput::QnxAudioInput()
- : m_audioSource(0)
- , m_pcmHandle(0)
- , m_pcmNotifier(0)
- , m_error(QAudio::NoError)
- , m_state(QAudio::StoppedState)
- , m_bytesRead(0)
- , m_elapsedTimeOffset(0)
- , m_totalTimeValue(0)
- , m_volume(qreal(1.0f))
- , m_bytesAvailable(0)
- , m_bufferSize(0)
- , m_periodSize(0)
- , m_intervalTime(1000)
- , m_pullMode(true)
-{
-}
-
-QnxAudioInput::~QnxAudioInput()
-{
- close();
-}
-
-void QnxAudioInput::start(QIODevice *device)
-{
- if (m_state != QAudio::StoppedState)
- close();
-
- if (!m_pullMode && m_audioSource)
- delete m_audioSource;
-
- m_pullMode = true;
- m_audioSource = device;
-
- if (open()) {
- setError(QAudio::NoError);
- setState(QAudio::ActiveState);
- } else {
- setError(QAudio::OpenError);
- setState(QAudio::StoppedState);
- }
-}
-
-QIODevice *QnxAudioInput::start()
-{
- if (m_state != QAudio::StoppedState)
- close();
-
- if (!m_pullMode && m_audioSource)
- delete m_audioSource;
-
- m_pullMode = false;
- m_audioSource = new InputPrivate(this);
- m_audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
-
- if (open()) {
- setError(QAudio::NoError);
- setState(QAudio::IdleState);
- } else {
- delete m_audioSource;
- m_audioSource = 0;
-
- setError(QAudio::OpenError);
- setState(QAudio::StoppedState);
- }
-
- return m_audioSource;
-}
-
-void QnxAudioInput::stop()
-{
- if (m_state == QAudio::StoppedState)
- return;
-
- setError(QAudio::NoError);
- setState(QAudio::StoppedState);
- close();
-}
-
-void QnxAudioInput::reset()
-{
- stop();
- m_bytesAvailable = 0;
-}
-
-void QnxAudioInput::suspend()
-{
- snd_pcm_capture_pause(m_pcmHandle);
-
- if (m_pcmNotifier)
- m_pcmNotifier->setEnabled(false);
-
- setState(QAudio::SuspendedState);
-}
-
-void QnxAudioInput::resume()
-{
- snd_pcm_capture_resume(m_pcmHandle);
-
- if (m_pcmNotifier)
- m_pcmNotifier->setEnabled(true);
-
- if (m_pullMode) {
- setState(QAudio::ActiveState);
- } else {
- setState(QAudio::IdleState);
- }
-}
-
-int QnxAudioInput::bytesReady() const
-{
- return qMax(m_bytesAvailable, 0);
-}
-
-int QnxAudioInput::periodSize() const
-{
- return m_periodSize;
-}
-
-void QnxAudioInput::setBufferSize(int bufferSize)
-{
- m_bufferSize = bufferSize;
-}
-
-int QnxAudioInput::bufferSize() const
-{
- return m_bufferSize;
-}
-
-void QnxAudioInput::setNotifyInterval(int milliSeconds)
-{
- m_intervalTime = qMax(0, milliSeconds);
-}
-
-int QnxAudioInput::notifyInterval() const
-{
- return m_intervalTime;
-}
-
-qint64 QnxAudioInput::processedUSecs() const
-{
- return qint64(1000000) * m_format.framesForBytes(m_bytesRead) / m_format.sampleRate();
-}
-
-qint64 QnxAudioInput::elapsedUSecs() const
-{
- if (m_state == QAudio::StoppedState)
- return 0;
-
- return m_clockStamp.elapsed() * qint64(1000);
-}
-
-QAudio::Error QnxAudioInput::error() const
-{
- return m_error;
-}
-
-QAudio::State QnxAudioInput::state() const
-{
- return m_state;
-}
-
-void QnxAudioInput::setFormat(const QAudioFormat &format)
-{
- if (m_state == QAudio::StoppedState)
- m_format = format;
-}
-
-QAudioFormat QnxAudioInput::format() const
-{
- return m_format;
-}
-
-void QnxAudioInput::setVolume(qreal volume)
-{
- m_volume = qBound(qreal(0.0), volume, qreal(1.0));
-}
-
-qreal QnxAudioInput::volume() const
-{
- return m_volume;
-}
-
-void QnxAudioInput::userFeed()
-{
- if (m_state == QAudio::StoppedState || m_state == QAudio::SuspendedState)
- return;
-
- deviceReady();
-}
-
-bool QnxAudioInput::deviceReady()
-{
- if (m_pullMode) {
- // reads some audio data and writes it to QIODevice
- read(0, 0);
- } else {
- m_bytesAvailable = m_periodSize;
-
- // emits readyRead() so user will call read() on QIODevice to get some audio data
- if (m_audioSource != 0) {
- InputPrivate *input = qobject_cast<InputPrivate*>(m_audioSource);
- input->trigger();
- }
- }
-
- if (m_state != QAudio::ActiveState)
- return true;
-
- if (m_intervalTime && (m_timeStamp.elapsed() + m_elapsedTimeOffset) > m_intervalTime) {
- emit notify();
- m_elapsedTimeOffset = m_timeStamp.elapsed() + m_elapsedTimeOffset - m_intervalTime;
- m_timeStamp.restart();
- }
-
- return true;
-}
-
-bool QnxAudioInput::open()
-{
- if (!m_format.isValid() || m_format.sampleRate() <= 0) {
- if (!m_format.isValid())
- qWarning("QnxAudioInput: open error, invalid format.");
- else
- qWarning("QnxAudioInput: open error, invalid sample rate (%d).", m_format.sampleRate());
-
- return false;
- }
-
- int errorCode = 0;
-
- int card = 0;
- int device = 0;
- if ((errorCode = snd_pcm_open_preferred(&m_pcmHandle, &card, &device, SND_PCM_OPEN_CAPTURE)) < 0) {
- qWarning("QnxAudioInput: open error, couldn't open card (0x%x)", -errorCode);
- return false;
- }
-
- // Necessary so that bytesFree() which uses the "free" member of the status struct works
- snd_pcm_plugin_set_disable(m_pcmHandle, PLUGIN_MMAP);
-
- snd_pcm_channel_info_t info;
- memset(&info, 0, sizeof(info));
- info.channel = SND_PCM_CHANNEL_CAPTURE;
- if ((errorCode = snd_pcm_plugin_info(m_pcmHandle, &info)) < 0) {
- qWarning("QnxAudioInput: open error, couldn't get channel info (0x%x)", -errorCode);
- close();
- return false;
- }
-
- snd_pcm_channel_params_t params = QnxAudioUtils::formatToChannelParams(m_format, QAudio::AudioInput, info.max_fragment_size);
-
- if ((errorCode = snd_pcm_plugin_params(m_pcmHandle, &params)) < 0) {
- qWarning("QnxAudioInput: open error, couldn't set channel params (0x%x)", -errorCode);
- close();
- return false;
- }
-
- if ((errorCode = snd_pcm_plugin_prepare(m_pcmHandle, SND_PCM_CHANNEL_CAPTURE)) < 0) {
- qWarning("QnxAudioInput: open error, couldn't prepare channel (0x%x)", -errorCode);
- close();
- return false;
- }
-
- snd_pcm_channel_setup_t setup;
-
- memset(&setup, 0, sizeof(setup));
- setup.channel = SND_PCM_CHANNEL_CAPTURE;
- if ((errorCode = snd_pcm_plugin_setup(m_pcmHandle, &setup)) < 0) {
- qWarning("QnxAudioInput: open error, couldn't get channel setup (0x%x)", -errorCode);
- close();
- return false;
- }
-
- m_periodSize = qMin(2048, setup.buf.block.frag_size);
-
- m_clockStamp.restart();
- m_timeStamp.restart();
- m_elapsedTimeOffset = 0;
- m_totalTimeValue = 0;
- m_bytesRead = 0;
-
- m_pcmNotifier = new QSocketNotifier(snd_pcm_file_descriptor(m_pcmHandle, SND_PCM_CHANNEL_CAPTURE),
- QSocketNotifier::Read, this);
- connect(m_pcmNotifier, SIGNAL(activated(QSocketDescriptor)), SLOT(userFeed()));
-
- return true;
-}
-
-void QnxAudioInput::close()
-{
- if (m_pcmHandle)
-#if SND_PCM_VERSION < SND_PROTOCOL_VERSION('P',3,0,2)
- snd_pcm_plugin_flush(m_pcmHandle, SND_PCM_CHANNEL_CAPTURE);
-#else
- snd_pcm_plugin_drop(m_pcmHandle, SND_PCM_CHANNEL_CAPTURE);
-#endif
-
- if (m_pcmNotifier) {
- delete m_pcmNotifier;
- m_pcmNotifier = 0;
- }
-
- if (m_pcmHandle) {
- snd_pcm_close(m_pcmHandle);
- m_pcmHandle = 0;
- }
-
- if (!m_pullMode && m_audioSource) {
- delete m_audioSource;
- m_audioSource = 0;
- }
-}
-
-qint64 QnxAudioInput::read(char *data, qint64 len)
-{
- int errorCode = 0;
- QByteArray tempBuffer(m_periodSize, 0);
-
- const int actualRead = snd_pcm_plugin_read(m_pcmHandle, tempBuffer.data(), m_periodSize);
- if (actualRead < 1) {
- snd_pcm_channel_status_t status;
- memset(&status, 0, sizeof(status));
- status.channel = SND_PCM_CHANNEL_CAPTURE;
- if ((errorCode = snd_pcm_plugin_status(m_pcmHandle, &status)) < 0) {
- qWarning("QnxAudioInput: read error, couldn't get plugin status (0x%x)", -errorCode);
- close();
- setError(QAudio::FatalError);
- setState(QAudio::StoppedState);
- return -1;
- }
-
- if (status.status == SND_PCM_STATUS_READY
- || status.status == SND_PCM_STATUS_OVERRUN) {
- if ((errorCode = snd_pcm_plugin_prepare(m_pcmHandle, SND_PCM_CHANNEL_CAPTURE)) < 0) {
- qWarning("QnxAudioInput: read error, couldn't prepare plugin (0x%x)", -errorCode);
- close();
- setError(QAudio::FatalError);
- setState(QAudio::StoppedState);
- return -1;
- }
- }
- } else {
- setError(QAudio::NoError);
- setState(QAudio::ActiveState);
- }
-
- if (m_volume < 1.0f)
- QAudioHelperInternal::qMultiplySamples(m_volume, m_format, tempBuffer.data(), tempBuffer.data(), actualRead);
-
- m_bytesRead += actualRead;
-
- if (m_pullMode) {
- m_audioSource->write(tempBuffer.data(), actualRead);
- } else {
- memcpy(data, tempBuffer.data(), qMin(static_cast<qint64>(actualRead), len));
- }
-
- m_bytesAvailable = 0;
-
- return actualRead;
-}
-
-void QnxAudioInput::setError(QAudio::Error error)
-{
- if (m_error == error)
- return;
-
- m_error = error;
- emit errorChanged(m_error);
-}
-
-void QnxAudioInput::setState(QAudio::State state)
-{
- if (m_state == state)
- return;
-
- m_state = state;
- emit stateChanged(m_state);
-}
-
-InputPrivate::InputPrivate(QnxAudioInput *audio)
- : m_audioDevice(audio)
-{
-}
-
-qint64 InputPrivate::readData(char *data, qint64 len)
-{
- return m_audioDevice->read(data, len);
-}
-
-qint64 InputPrivate::writeData(const char *data, qint64 len)
-{
- Q_UNUSED(data);
- Q_UNUSED(len);
- return 0;
-}
-
-void InputPrivate::trigger()
-{
- emit readyRead();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx-audio/audio/qnxaudioinput.h b/src/plugins/qnx-audio/audio/qnxaudioinput.h
deleted file mode 100644
index a08491321..000000000
--- a/src/plugins/qnx-audio/audio/qnxaudioinput.h
+++ /dev/null
@@ -1,138 +0,0 @@
-/****************************************************************************
-**
-** 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 QNXAUDIOINPUT_H
-#define QNXAUDIOINPUT_H
-
-#include "qaudiosystem.h"
-
-#include <QSocketNotifier>
-#include <QIODevice>
-#include <QElapsedTimer>
-#include <QTimer>
-
-#include <sys/asoundlib.h>
-
-QT_BEGIN_NAMESPACE
-
-class QnxAudioInput : public QAbstractAudioInput
-{
- Q_OBJECT
-
-public:
- QnxAudioInput();
- ~QnxAudioInput();
-
- void start(QIODevice*) override;
- QIODevice* start() override;
- void stop() override;
- void reset() override;
- void suspend() override;
- void resume() override;
- int bytesReady() const override;
- int periodSize() const override;
- void setBufferSize(int ) override;
- int bufferSize() const override;
- void setNotifyInterval(int ) override;
- int notifyInterval() const override;
- qint64 processedUSecs() const override;
- qint64 elapsedUSecs() const override;
- QAudio::Error error() const override;
- QAudio::State state() const override;
- void setFormat(const QAudioFormat&) override;
- QAudioFormat format() const override;
- void setVolume(qreal) override;
- qreal volume() const override;
-
-private slots:
- void userFeed();
- bool deviceReady();
-
-private:
- friend class InputPrivate;
-
- bool open();
- void close();
- qint64 read(char *data, qint64 len);
- void setError(QAudio::Error error);
- void setState(QAudio::State state);
-
- QElapsedTimer m_timeStamp;
- QElapsedTimer m_clockStamp;
- QAudioFormat m_format;
-
- QIODevice *m_audioSource;
- snd_pcm_t *m_pcmHandle;
- QSocketNotifier *m_pcmNotifier;
-
- QAudio::Error m_error;
- QAudio::State m_state;
-
- qint64 m_bytesRead;
- qint64 m_elapsedTimeOffset;
- qint64 m_totalTimeValue;
-
- qreal m_volume;
-
- int m_bytesAvailable;
- int m_bufferSize;
- int m_periodSize;
- int m_intervalTime;
-
- bool m_pullMode;
-};
-
-class InputPrivate : public QIODevice
-{
- Q_OBJECT
-public:
- InputPrivate(QnxAudioInput *audio);
-
- qint64 readData(char *data, qint64 len) override;
- qint64 writeData(const char *data, qint64 len) override;
-
- void trigger();
-
-private:
- QnxAudioInput *m_audioDevice;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx-audio/audio/qnxaudiooutput.cpp b/src/plugins/qnx-audio/audio/qnxaudiooutput.cpp
deleted file mode 100644
index 34ea23604..000000000
--- a/src/plugins/qnx-audio/audio/qnxaudiooutput.cpp
+++ /dev/null
@@ -1,578 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "qnxaudiooutput.h"
-
-#include "qnxaudioutils.h"
-
-#include <private/qaudiohelpers_p.h>
-
-#pragma GCC diagnostic ignored "-Wvla"
-
-QT_BEGIN_NAMESPACE
-
-QnxAudioOutput::QnxAudioOutput()
- : m_source(0)
- , m_pushSource(false)
- , m_notifyInterval(1000)
- , m_error(QAudio::NoError)
- , m_state(QAudio::StoppedState)
- , m_volume(1.0)
- , m_periodSize(0)
- , m_pcmHandle(0)
- , m_bytesWritten(0)
- , m_intervalOffset(0)
-#if _NTO_VERSION >= 700
- , m_pcmNotifier(0)
-#endif
-{
- m_timer.setSingleShot(false);
- m_timer.setInterval(20);
- connect(&m_timer, SIGNAL(timeout()), this, SLOT(pullData()));
-}
-
-QnxAudioOutput::~QnxAudioOutput()
-{
- stop();
-}
-
-void QnxAudioOutput::start(QIODevice *source)
-{
- if (m_state != QAudio::StoppedState)
- stop();
-
- m_error = QAudio::NoError;
- m_source = source;
- m_pushSource = false;
-
- if (open()) {
- setState(QAudio::ActiveState);
- m_timer.start();
- } else {
- setError(QAudio::OpenError);
- setState(QAudio::StoppedState);
- }
-}
-
-QIODevice *QnxAudioOutput::start()
-{
- if (m_state != QAudio::StoppedState)
- stop();
-
- m_error = QAudio::NoError;
- m_source = new QnxPushIODevice(this);
- m_source->open(QIODevice::WriteOnly|QIODevice::Unbuffered);
- m_pushSource = true;
-
- if (open())
- setState(QAudio::IdleState);
- else {
- setError(QAudio::OpenError);
- setState(QAudio::StoppedState);
- }
-
- return m_source;
-}
-
-void QnxAudioOutput::stop()
-{
- if (m_state == QAudio::StoppedState)
- return;
-
- setError(QAudio::NoError);
- setState(QAudio::StoppedState);
- close();
-}
-
-void QnxAudioOutput::reset()
-{
- if (m_pcmHandle)
-#if SND_PCM_VERSION < SND_PROTOCOL_VERSION('P',3,0,2)
- snd_pcm_playback_drain(m_pcmHandle);
-#else
- snd_pcm_channel_drain(m_pcmHandle, SND_PCM_CHANNEL_PLAYBACK);
-#endif
- stop();
-}
-
-void QnxAudioOutput::suspend()
-{
- snd_pcm_playback_pause(m_pcmHandle);
- if (state() != QAudio::InterruptedState)
- suspendInternal(QAudio::SuspendedState);
-}
-
-void QnxAudioOutput::resume()
-{
- snd_pcm_playback_resume(m_pcmHandle);
- if (state() != QAudio::InterruptedState)
- resumeInternal();
-}
-
-int QnxAudioOutput::bytesFree() const
-{
- if (m_state != QAudio::ActiveState && m_state != QAudio::IdleState)
- return 0;
-
- snd_pcm_channel_status_t status;
- memset(&status, 0, sizeof(status));
- status.channel = SND_PCM_CHANNEL_PLAYBACK;
- const int errorCode = snd_pcm_plugin_status(m_pcmHandle, &status);
-
- if (errorCode)
- return 0;
- else
- return status.free;
-}
-
-int QnxAudioOutput::periodSize() const
-{
- return m_periodSize;
-}
-
-void QnxAudioOutput::setNotifyInterval(int ms)
-{
- m_notifyInterval = ms;
-}
-
-int QnxAudioOutput::notifyInterval() const
-{
- return m_notifyInterval;
-}
-
-qint64 QnxAudioOutput::processedUSecs() const
-{
- return qint64(1000000) * m_format.framesForBytes(m_bytesWritten) / m_format.sampleRate();
-}
-
-qint64 QnxAudioOutput::elapsedUSecs() const
-{
- if (m_state == QAudio::StoppedState)
- return 0;
- else
- return m_startTimeStamp.elapsed() * qint64(1000);
-}
-
-QAudio::Error QnxAudioOutput::error() const
-{
- return m_error;
-}
-
-QAudio::State QnxAudioOutput::state() const
-{
- return m_state;
-}
-
-void QnxAudioOutput::setFormat(const QAudioFormat &format)
-{
- if (m_state == QAudio::StoppedState)
- m_format = format;
-}
-
-QAudioFormat QnxAudioOutput::format() const
-{
- return m_format;
-}
-
-void QnxAudioOutput::setVolume(qreal volume)
-{
- m_volume = qBound(qreal(0.0), volume, qreal(1.0));
-}
-
-qreal QnxAudioOutput::volume() const
-{
- return m_volume;
-}
-
-void QnxAudioOutput::setCategory(const QString &category)
-{
- m_category = category;
-}
-
-QString QnxAudioOutput::category() const
-{
- return m_category;
-}
-
-void QnxAudioOutput::pullData()
-{
- if (m_state == QAudio::StoppedState
- || m_state == QAudio::SuspendedState
- || m_state == QAudio::InterruptedState)
- return;
-
- const int bytesAvailable = bytesFree();
- const int frames = m_format.framesForBytes(bytesAvailable);
-
- if (frames == 0 || bytesAvailable < periodSize())
- return;
-
- // The buffer is placed on the stack so no more than 64K or 1 frame
- // whichever is larger.
- const int maxFrames = qMax(m_format.framesForBytes(64 * 1024), 1);
- const int bytesRequested = m_format.bytesForFrames(qMin(frames, maxFrames));
-
- char buffer[bytesRequested];
- const int bytesRead = m_source->read(buffer, bytesRequested);
-
- // reading can take a while and stream may have been stopped
- if (!m_pcmHandle)
- return;
-
- if (bytesRead > 0) {
- // Got some data to output
- if (m_state != QAudio::ActiveState)
- return;
-
- const qint64 bytesWritten = write(buffer, bytesRead);
- if (bytesWritten != bytesRead)
- m_source->seek(m_source->pos()-(bytesRead-bytesWritten));
-
- } else {
- // We're done
- close();
- if (bytesRead != 0)
- setError(QAudio::IOError);
- setState(QAudio::StoppedState);
- }
-
- if (m_state != QAudio::ActiveState)
- return;
-
- if (m_notifyInterval > 0 && (m_intervalTimeStamp.elapsed() + m_intervalOffset) > m_notifyInterval) {
- emit notify();
- m_intervalOffset = m_intervalTimeStamp.elapsed() + m_intervalOffset - m_notifyInterval;
- m_intervalTimeStamp.restart();
- }
-}
-
-bool QnxAudioOutput::open()
-{
- if (!m_format.isValid() || m_format.sampleRate() <= 0) {
- if (!m_format.isValid())
- qWarning("QnxAudioOutput: open error, invalid format.");
- else
- qWarning("QnxAudioOutput: open error, invalid sample rate (%d).", m_format.sampleRate());
-
- return false;
- }
-
- int errorCode = 0;
-
- int card = 0;
- int device = 0;
- if ((errorCode = snd_pcm_open_preferred(&m_pcmHandle, &card, &device, SND_PCM_OPEN_PLAYBACK)) < 0) {
- qWarning("QnxAudioOutput: open error, couldn't open card (0x%x)", -errorCode);
- return false;
- }
-
- if ((errorCode = snd_pcm_nonblock_mode(m_pcmHandle, 0)) < 0) {
- qWarning("QnxAudioOutput: open error, couldn't set non block mode (0x%x)", -errorCode);
- close();
- return false;
- }
-
- addPcmEventFilter();
-
- // Necessary so that bytesFree() which uses the "free" member of the status struct works
- snd_pcm_plugin_set_disable(m_pcmHandle, PLUGIN_MMAP);
-
- snd_pcm_channel_info_t info;
- memset(&info, 0, sizeof(info));
- info.channel = SND_PCM_CHANNEL_PLAYBACK;
- if ((errorCode = snd_pcm_plugin_info(m_pcmHandle, &info)) < 0) {
- qWarning("QnxAudioOutput: open error, couldn't get channel info (0x%x)", -errorCode);
- close();
- return false;
- }
-
- snd_pcm_channel_params_t params = QnxAudioUtils::formatToChannelParams(m_format, QAudio::AudioOutput, info.max_fragment_size);
- setTypeName(&params);
-
- if ((errorCode = snd_pcm_plugin_params(m_pcmHandle, &params)) < 0) {
- qWarning("QnxAudioOutput: open error, couldn't set channel params (0x%x)", -errorCode);
- close();
- return false;
- }
-
- if ((errorCode = snd_pcm_plugin_prepare(m_pcmHandle, SND_PCM_CHANNEL_PLAYBACK)) < 0) {
- qWarning("QnxAudioOutput: open error, couldn't prepare channel (0x%x)", -errorCode);
- close();
- return false;
- }
-
- snd_pcm_channel_setup_t setup;
- memset(&setup, 0, sizeof(setup));
- setup.channel = SND_PCM_CHANNEL_PLAYBACK;
- if ((errorCode = snd_pcm_plugin_setup(m_pcmHandle, &setup)) < 0) {
- qWarning("QnxAudioOutput: open error, couldn't get channel setup (0x%x)", -errorCode);
- close();
- return false;
- }
-
- m_periodSize = qMin(2048, setup.buf.block.frag_size);
- m_startTimeStamp.restart();
- m_intervalTimeStamp.restart();
- m_intervalOffset = 0;
- m_bytesWritten = 0;
-
- createPcmNotifiers();
-
- return true;
-}
-
-void QnxAudioOutput::close()
-{
- m_timer.stop();
-
- destroyPcmNotifiers();
-
- if (m_pcmHandle) {
-#if SND_PCM_VERSION < SND_PROTOCOL_VERSION('P',3,0,2)
- snd_pcm_plugin_flush(m_pcmHandle, SND_PCM_CHANNEL_PLAYBACK);
-#else
- snd_pcm_plugin_drop(m_pcmHandle, SND_PCM_CHANNEL_PLAYBACK);
-#endif
- snd_pcm_close(m_pcmHandle);
- m_pcmHandle = 0;
- }
-
- if (m_pushSource) {
- delete m_source;
- m_source = 0;
- }
-}
-
-void QnxAudioOutput::setError(QAudio::Error error)
-{
- if (m_error != error) {
- m_error = error;
- emit errorChanged(error);
- }
-}
-
-void QnxAudioOutput::setState(QAudio::State state)
-{
- if (m_state != state) {
- m_state = state;
- emit stateChanged(state);
- }
-}
-
-qint64 QnxAudioOutput::write(const char *data, qint64 len)
-{
- if (!m_pcmHandle)
- return 0;
-
- // Make sure we're writing (N * frame) worth of bytes
- const int size = m_format.bytesForFrames(qBound(qint64(0), qint64(bytesFree()), len) / m_format.bytesPerFrame());
-
- if (size == 0)
- return 0;
-
- int written = 0;
-
- if (m_volume < 1.0f) {
- char out[size];
- QAudioHelperInternal::qMultiplySamples(m_volume, m_format, data, out, size);
- written = snd_pcm_plugin_write(m_pcmHandle, out, size);
- } else {
- written = snd_pcm_plugin_write(m_pcmHandle, data, size);
- }
-
- if (written > 0) {
- m_bytesWritten += written;
- setError(QAudio::NoError);
- setState(QAudio::ActiveState);
- return written;
- } else {
- close();
- setError(QAudio::FatalError);
- setState(QAudio::StoppedState);
- return 0;
- }
-}
-
-void QnxAudioOutput::suspendInternal(QAudio::State suspendState)
-{
- m_timer.stop();
- setState(suspendState);
-}
-
-void QnxAudioOutput::resumeInternal()
-{
- if (m_pushSource) {
- setState(QAudio::IdleState);
- } else {
- setState(QAudio::ActiveState);
- m_timer.start();
- }
-}
-
-#if _NTO_VERSION >= 700
-
-QAudio::State suspendState(const snd_pcm_event_t &event)
-{
- Q_ASSERT(event.type == SND_PCM_EVENT_AUDIOMGMT_STATUS);
- Q_ASSERT(event.data.audiomgmt_status.new_status == SND_PCM_STATUS_SUSPENDED);
- return event.data.audiomgmt_status.flags & SND_PCM_STATUS_EVENT_HARD_SUSPEND
- ? QAudio::InterruptedState : QAudio::SuspendedState;
-}
-
-void QnxAudioOutput::addPcmEventFilter()
-{
- /* Enable PCM events */
- snd_pcm_filter_t filter;
- memset(&filter, 0, sizeof(filter));
- filter.enable = (1<<SND_PCM_EVENT_AUDIOMGMT_STATUS) |
- (1<<SND_PCM_EVENT_AUDIOMGMT_MUTE) |
- (1<<SND_PCM_EVENT_OUTPUTCLASS);
- snd_pcm_set_filter(m_pcmHandle, SND_PCM_CHANNEL_PLAYBACK, &filter);
-}
-
-void QnxAudioOutput::createPcmNotifiers()
-{
- // QSocketNotifier::Read for poll based event dispatcher. Exception for
- // select based event dispatcher.
- m_pcmNotifier = new QSocketNotifier(snd_pcm_file_descriptor(m_pcmHandle,
- SND_PCM_CHANNEL_PLAYBACK),
- QSocketNotifier::Read, this);
- connect(m_pcmNotifier, &QSocketNotifier::activated,
- this, &QnxAudioOutput::pcmNotifierActivated);
-}
-
-void QnxAudioOutput::destroyPcmNotifiers()
-{
- if (m_pcmNotifier) {
- delete m_pcmNotifier;
- m_pcmNotifier = 0;
- }
-}
-
-void QnxAudioOutput::setTypeName(snd_pcm_channel_params_t *params)
-{
- if (m_category.isEmpty())
- return;
-
- QByteArray latin1Category = m_category.toLatin1();
-
- if (QString::fromLatin1(latin1Category) != m_category) {
- qWarning("QnxAudioOutput: audio category name isn't a Latin1 string.");
- return;
- }
-
- if (latin1Category.size() >= static_cast<int>(sizeof(params->audio_type_name))) {
- qWarning("QnxAudioOutput: audio category name too long.");
- return;
- }
-
- strcpy(params->audio_type_name, latin1Category.constData());
-}
-
-void QnxAudioOutput::pcmNotifierActivated(int socket)
-{
- Q_UNUSED(socket);
-
- snd_pcm_event_t pcm_event;
- memset(&pcm_event, 0, sizeof(pcm_event));
- while (snd_pcm_channel_read_event(m_pcmHandle, SND_PCM_CHANNEL_PLAYBACK, &pcm_event) == 0) {
- if (pcm_event.type == SND_PCM_EVENT_AUDIOMGMT_STATUS) {
- if (pcm_event.data.audiomgmt_status.new_status == SND_PCM_STATUS_SUSPENDED)
- suspendInternal(suspendState(pcm_event));
- else if (pcm_event.data.audiomgmt_status.new_status == SND_PCM_STATUS_RUNNING)
- resumeInternal();
- else if (pcm_event.data.audiomgmt_status.new_status == SND_PCM_STATUS_PAUSED)
- suspendInternal(QAudio::SuspendedState);
- }
- }
-}
-
-#else
-
-void QnxAudioOutput::addPcmEventFilter() {}
-void QnxAudioOutput::createPcmNotifiers() {}
-void QnxAudioOutput::destroyPcmNotifiers() {}
-void QnxAudioOutput::setTypeName(snd_pcm_channel_params_t *) {}
-
-#endif
-
-QnxPushIODevice::QnxPushIODevice(QnxAudioOutput *output)
- : QIODevice(output),
- m_output(output)
-{
-}
-
-QnxPushIODevice::~QnxPushIODevice()
-{
-}
-
-qint64 QnxPushIODevice::readData(char *data, qint64 len)
-{
- Q_UNUSED(data);
- Q_UNUSED(len);
- return 0;
-}
-
-qint64 QnxPushIODevice::writeData(const char *data, qint64 len)
-{
- int retry = 0;
- qint64 written = 0;
-
- if (m_output->state() == QAudio::ActiveState
- || m_output->state() == QAudio::IdleState) {
- while (written < len) {
- const int writeSize = m_output->write(data + written, len - written);
-
- if (writeSize <= 0) {
- retry++;
- if (retry > 10)
- return written;
- else
- continue;
- }
-
- retry = 0;
- written += writeSize;
- }
- }
-
- return written;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx-audio/audio/qnxaudiooutput.h b/src/plugins/qnx-audio/audio/qnxaudiooutput.h
deleted file mode 100644
index bbbd40b17..000000000
--- a/src/plugins/qnx-audio/audio/qnxaudiooutput.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/****************************************************************************
-**
-** 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 QNXAUDIOOUTPUT_H
-#define QNXAUDIOOUTPUT_H
-
-#include "qaudiosystem.h"
-
-#include <QElapsedTimer>
-#include <QTimer>
-#include <QIODevice>
-#include <QSocketNotifier>
-
-#include <sys/asoundlib.h>
-#include <sys/neutrino.h>
-
-QT_BEGIN_NAMESPACE
-
-class QnxPushIODevice;
-
-class QnxAudioOutput : public QAbstractAudioOutput
-{
- Q_OBJECT
-
-public:
- QnxAudioOutput();
- ~QnxAudioOutput();
-
- void start(QIODevice *source) override;
- QIODevice *start() override;
- void stop() override;
- void reset() override;
- void suspend() override;
- void resume() override;
- int bytesFree() const override;
- int periodSize() const override;
- void setBufferSize(int) override {}
- int bufferSize() const override { return 0; }
- void setNotifyInterval(int ms) override;
- int notifyInterval() const override;
- qint64 processedUSecs() const override;
- qint64 elapsedUSecs() const override;
- QAudio::Error error() const override;
- QAudio::State state() const override;
- void setFormat(const QAudioFormat &format) override;
- QAudioFormat format() const override;
- void setVolume(qreal volume) override;
- qreal volume() const override;
- void setCategory(const QString &category) override;
- QString category() const override;
-
-private slots:
- void pullData();
-
-private:
- bool open();
- void close();
- void setError(QAudio::Error error);
- void setState(QAudio::State state);
-
- void addPcmEventFilter();
- void createPcmNotifiers();
- void destroyPcmNotifiers();
- void setTypeName(snd_pcm_channel_params_t *params);
-
- void suspendInternal(QAudio::State suspendState);
- void resumeInternal();
-
- friend class QnxPushIODevice;
- qint64 write(const char *data, qint64 len);
-
- QIODevice *m_source;
- bool m_pushSource;
- QTimer m_timer;
-
- int m_notifyInterval;
- QAudio::Error m_error;
- QAudio::State m_state;
- QAudioFormat m_format;
- qreal m_volume;
- QString m_category;
- int m_periodSize;
-
- snd_pcm_t *m_pcmHandle;
- qint64 m_bytesWritten;
- QElapsedTimer m_startTimeStamp;
- QElapsedTimer m_intervalTimeStamp;
- qint64 m_intervalOffset;
-
-#if _NTO_VERSION >= 700
- QSocketNotifier *m_pcmNotifier;
-
-private slots:
- void pcmNotifierActivated(int socket);
-#endif
-};
-
-class QnxPushIODevice : public QIODevice
-{
- Q_OBJECT
-public:
- explicit QnxPushIODevice(QnxAudioOutput *output);
- ~QnxPushIODevice();
-
- qint64 readData(char *data, qint64 len);
- qint64 writeData(const char *data, qint64 len);
-
-private:
- QnxAudioOutput *m_output;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx-audio/audio/qnxaudioplugin.cpp b/src/plugins/qnx-audio/audio/qnxaudioplugin.cpp
deleted file mode 100644
index 5d26c8954..000000000
--- a/src/plugins/qnx-audio/audio/qnxaudioplugin.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "qnxaudioplugin.h"
-
-#include "qnxaudiodeviceinfo.h"
-#include "qnxaudioinput.h"
-#include "qnxaudiooutput.h"
-
-#include <sys/asoundlib.h>
-
-static const char *INPUT_ID = "QnxAudioInput";
-static const char *OUTPUT_ID = "QnxAudioOutput";
-
-QT_BEGIN_NAMESPACE
-
-QnxAudioPlugin::QnxAudioPlugin(QObject *parent)
- : QAudioSystemPlugin(parent)
-{
-}
-
-QByteArray QnxAudioPlugin::defaultDevice(QAudio::Mode mode) const
-{
- return (mode == QAudio::AudioOutput) ? OUTPUT_ID : INPUT_ID;
-}
-
-QList<QByteArray> QnxAudioPlugin::availableDevices(QAudio::Mode mode) const
-{
- if (mode == QAudio::AudioOutput)
- return QList<QByteArray>() << OUTPUT_ID;
- else
- return QList<QByteArray>() << INPUT_ID;
-}
-
-QAbstractAudioInput *QnxAudioPlugin::createInput(const QByteArray &device)
-{
- Q_ASSERT(device == INPUT_ID);
- Q_UNUSED(device);
- return new QnxAudioInput();
-}
-
-QAbstractAudioOutput *QnxAudioPlugin::createOutput(const QByteArray &device)
-{
- Q_ASSERT(device == OUTPUT_ID);
- Q_UNUSED(device);
- return new QnxAudioOutput();
-}
-
-QAbstractAudioDeviceInfo *QnxAudioPlugin::createDeviceInfo(const QByteArray &device, QAudio::Mode mode)
-{
- Q_ASSERT(device == OUTPUT_ID || device == INPUT_ID);
- return new QnxAudioDeviceInfo(device, mode);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx-audio/audio/qnxaudioplugin.h b/src/plugins/qnx-audio/audio/qnxaudioplugin.h
deleted file mode 100644
index d41f4020a..000000000
--- a/src/plugins/qnx-audio/audio/qnxaudioplugin.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/****************************************************************************
-**
-** 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 QNXAUDIOPLUGIN_H
-#define QNXAUDIOPLUGIN_H
-
-#include <QtMultimedia/qaudiosystemplugin.h>
-#include <QtMultimedia/private/qaudiosystempluginext_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class QnxAudioPlugin : public QAudioSystemPlugin, public QAudioSystemPluginExtension
-{
- Q_OBJECT
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.audiosystemfactory/5.0" FILE "qnx_audio.json")
- Q_INTERFACES(QAudioSystemPluginExtension)
-
-public:
- explicit QnxAudioPlugin(QObject *parent = 0);
- ~QnxAudioPlugin() {}
-
- QByteArray defaultDevice(QAudio::Mode mode) const override;
- QList<QByteArray> availableDevices(QAudio::Mode mode) const override;
- QAbstractAudioInput *createInput(const QByteArray &device) override;
- QAbstractAudioOutput *createOutput(const QByteArray &device) override;
- QAbstractAudioDeviceInfo *createDeviceInfo(const QByteArray &device, QAudio::Mode mode) override;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx-audio/audio/qnxaudioutils.cpp b/src/plugins/qnx-audio/audio/qnxaudioutils.cpp
deleted file mode 100644
index ee7bccbda..000000000
--- a/src/plugins/qnx-audio/audio/qnxaudioutils.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "qnxaudioutils.h"
-
-QT_BEGIN_NAMESPACE
-
-snd_pcm_channel_params_t QnxAudioUtils::formatToChannelParams(const QAudioFormat &format, QAudio::Mode mode, int fragmentSize)
-{
- snd_pcm_channel_params_t params;
- memset(&params, 0, sizeof(params));
- params.channel = (mode == QAudio::AudioOutput) ? SND_PCM_CHANNEL_PLAYBACK : SND_PCM_CHANNEL_CAPTURE;
- params.mode = SND_PCM_MODE_BLOCK;
- params.start_mode = SND_PCM_START_DATA;
- params.stop_mode = SND_PCM_STOP_ROLLOVER;
- params.buf.block.frag_size = fragmentSize;
- params.buf.block.frags_min = 1;
- params.buf.block.frags_max = 1;
- strcpy(params.sw_mixer_subchn_name, "QAudio Channel");
-
- params.format.interleave = 1;
- params.format.rate = format.sampleRate();
- params.format.voices = format.channelCount();
-
- switch (format.sampleSize()) {
- case 8:
- switch (format.sampleType()) {
- case QAudioFormat::SignedInt:
- params.format.format = SND_PCM_SFMT_S8;
- break;
- case QAudioFormat::UnSignedInt:
- params.format.format = SND_PCM_SFMT_U8;
- break;
- default:
- break;
- }
- break;
-
- case 16:
- switch (format.sampleType()) {
- case QAudioFormat::SignedInt:
- if (format.byteOrder() == QAudioFormat::LittleEndian) {
- params.format.format = SND_PCM_SFMT_S16_LE;
- } else {
- params.format.format = SND_PCM_SFMT_S16_BE;
- }
- break;
- case QAudioFormat::UnSignedInt:
- if (format.byteOrder() == QAudioFormat::LittleEndian) {
- params.format.format = SND_PCM_SFMT_U16_LE;
- } else {
- params.format.format = SND_PCM_SFMT_U16_BE;
- }
- break;
- default:
- break;
- }
- break;
-
- case 32:
- switch (format.sampleType()) {
- case QAudioFormat::SignedInt:
- if (format.byteOrder() == QAudioFormat::LittleEndian) {
- params.format.format = SND_PCM_SFMT_S32_LE;
- } else {
- params.format.format = SND_PCM_SFMT_S32_BE;
- }
- break;
- case QAudioFormat::UnSignedInt:
- if (format.byteOrder() == QAudioFormat::LittleEndian) {
- params.format.format = SND_PCM_SFMT_U32_LE;
- } else {
- params.format.format = SND_PCM_SFMT_U32_BE;
- }
- break;
- case QAudioFormat::Float:
- if (format.byteOrder() == QAudioFormat::LittleEndian) {
- params.format.format = SND_PCM_SFMT_FLOAT_LE;
- } else {
- params.format.format = SND_PCM_SFMT_FLOAT_BE;
- }
- break;
- default:
- break;
- }
- break;
- }
-
- return params;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx-audio/audio/qnxaudioutils.h b/src/plugins/qnx-audio/audio/qnxaudioutils.h
deleted file mode 100644
index 2f3db63c1..000000000
--- a/src/plugins/qnx-audio/audio/qnxaudioutils.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** 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 QNXAUDIOUTILS_H
-#define QNXAUDIOUTILS_H
-
-#include "qaudiosystem.h"
-#include <sys/asoundlib.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace QnxAudioUtils
-{
- snd_pcm_channel_params_t formatToChannelParams(const QAudioFormat &format, QAudio::Mode mode, int fragmentSize);
-}
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx-audio/qnx-audio.pro b/src/plugins/qnx-audio/qnx-audio.pro
deleted file mode 100644
index 3049729b8..000000000
--- a/src/plugins/qnx-audio/qnx-audio.pro
+++ /dev/null
@@ -1,3 +0,0 @@
-TEMPLATE = subdirs
-
-SUBDIRS = audio
diff --git a/src/plugins/qnx/camera/bbcameraaudioencodersettingscontrol.cpp b/src/plugins/qnx/camera/bbcameraaudioencodersettingscontrol.cpp
deleted file mode 100644
index 96686830f..000000000
--- a/src/plugins/qnx/camera/bbcameraaudioencodersettingscontrol.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbcameraaudioencodersettingscontrol.h"
-
-#include "bbcamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-BbCameraAudioEncoderSettingsControl::BbCameraAudioEncoderSettingsControl(BbCameraSession *session, QObject *parent)
- : QAudioEncoderSettingsControl(parent)
- , m_session(session)
-{
-}
-
-QStringList BbCameraAudioEncoderSettingsControl::supportedAudioCodecs() const
-{
- return QStringList() << QLatin1String("none") << QLatin1String("aac") << QLatin1String("raw");
-}
-
-QString BbCameraAudioEncoderSettingsControl::codecDescription(const QString &codecName) const
-{
- if (codecName == QLatin1String("none"))
- return tr("No compression");
- else if (codecName == QLatin1String("aac"))
- return tr("AAC compression");
- else if (codecName == QLatin1String("raw"))
- return tr("PCM uncompressed");
-
- return QString();
-}
-
-QList<int> BbCameraAudioEncoderSettingsControl::supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous) const
-{
- Q_UNUSED(settings);
- Q_UNUSED(continuous);
-
- // no API provided by BB10 yet
- return QList<int>();
-}
-
-QAudioEncoderSettings BbCameraAudioEncoderSettingsControl::audioSettings() const
-{
- return m_session->audioSettings();
-}
-
-void BbCameraAudioEncoderSettingsControl::setAudioSettings(const QAudioEncoderSettings &settings)
-{
- m_session->setAudioSettings(settings);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbcameraaudioencodersettingscontrol.h b/src/plugins/qnx/camera/bbcameraaudioencodersettingscontrol.h
deleted file mode 100644
index 38c01f86c..000000000
--- a/src/plugins/qnx/camera/bbcameraaudioencodersettingscontrol.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************************
-**
-** 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 BBCAMERAAUDIOENCODERSETTINGSCONTROL_H
-#define BBCAMERAAUDIOENCODERSETTINGSCONTROL_H
-
-#include <qaudioencodersettingscontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraSession;
-
-class BbCameraAudioEncoderSettingsControl : public QAudioEncoderSettingsControl
-{
- Q_OBJECT
-public:
- explicit BbCameraAudioEncoderSettingsControl(BbCameraSession *session, QObject *parent = 0);
-
- QStringList supportedAudioCodecs() const override;
- QString codecDescription(const QString &codecName) const override;
- QList<int> supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous = 0) const override;
- QAudioEncoderSettings audioSettings() const override;
- void setAudioSettings(const QAudioEncoderSettings &settings) override;
-
-private:
- BbCameraSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbcameracapturebufferformatcontrol.cpp b/src/plugins/qnx/camera/bbcameracapturebufferformatcontrol.cpp
deleted file mode 100644
index a2607ca93..000000000
--- a/src/plugins/qnx/camera/bbcameracapturebufferformatcontrol.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbcameracapturebufferformatcontrol.h"
-
-QT_BEGIN_NAMESPACE
-
-BbCameraCaptureBufferFormatControl::BbCameraCaptureBufferFormatControl(QObject *parent)
- : QCameraCaptureBufferFormatControl(parent)
-{
-}
-
-QList<QVideoFrame::PixelFormat> BbCameraCaptureBufferFormatControl::supportedBufferFormats() const
-{
- return (QList<QVideoFrame::PixelFormat>() << QVideoFrame::Format_Jpeg);
-}
-
-QVideoFrame::PixelFormat BbCameraCaptureBufferFormatControl::bufferFormat() const
-{
- return QVideoFrame::Format_Jpeg;
-}
-
-void BbCameraCaptureBufferFormatControl::setBufferFormat(QVideoFrame::PixelFormat format)
-{
- Q_UNUSED(format);
- // Do nothing, we support only Jpeg for now
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbcameracapturebufferformatcontrol.h b/src/plugins/qnx/camera/bbcameracapturebufferformatcontrol.h
deleted file mode 100644
index 4aac740c8..000000000
--- a/src/plugins/qnx/camera/bbcameracapturebufferformatcontrol.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/****************************************************************************
-**
-** 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 BBCAMERACAPTUREBUFFERFORMATCONTROL_H
-#define BBCAMERACAPTUREBUFFERFORMATCONTROL_H
-
-#include <qcameracapturebufferformatcontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraCaptureBufferFormatControl : public QCameraCaptureBufferFormatControl
-{
- Q_OBJECT
-public:
- explicit BbCameraCaptureBufferFormatControl(QObject *parent = 0);
-
- QList<QVideoFrame::PixelFormat> supportedBufferFormats() const override;
- QVideoFrame::PixelFormat bufferFormat() const override;
- void setBufferFormat(QVideoFrame::PixelFormat format) override;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbcameracapturedestinationcontrol.cpp b/src/plugins/qnx/camera/bbcameracapturedestinationcontrol.cpp
deleted file mode 100644
index 8dad17208..000000000
--- a/src/plugins/qnx/camera/bbcameracapturedestinationcontrol.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbcameracapturedestinationcontrol.h"
-
-#include "bbcamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-BbCameraCaptureDestinationControl::BbCameraCaptureDestinationControl(BbCameraSession *session, QObject *parent)
- : QCameraCaptureDestinationControl(parent)
- , m_session(session)
-{
- connect(m_session, SIGNAL(captureDestinationChanged(QCameraImageCapture::CaptureDestinations)),
- this, SIGNAL(captureDestinationChanged(QCameraImageCapture::CaptureDestinations)));
-}
-
-bool BbCameraCaptureDestinationControl::isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const
-{
- return m_session->isCaptureDestinationSupported(destination);
-}
-
-QCameraImageCapture::CaptureDestinations BbCameraCaptureDestinationControl::captureDestination() const
-{
- return m_session->captureDestination();;
-}
-
-void BbCameraCaptureDestinationControl::setCaptureDestination(QCameraImageCapture::CaptureDestinations destination)
-{
- m_session->setCaptureDestination(destination);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbcameracapturedestinationcontrol.h b/src/plugins/qnx/camera/bbcameracapturedestinationcontrol.h
deleted file mode 100644
index 9586ec56a..000000000
--- a/src/plugins/qnx/camera/bbcameracapturedestinationcontrol.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/****************************************************************************
-**
-** 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 BBCAMERACAPTUREDESTINATIONCONTROL_H
-#define BBCAMERACAPTUREDESTINATIONCONTROL_H
-
-#include <qcameracapturedestinationcontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraSession;
-
-class BbCameraCaptureDestinationControl : public QCameraCaptureDestinationControl
-{
- Q_OBJECT
-public:
- explicit BbCameraCaptureDestinationControl(BbCameraSession *session, QObject *parent = 0);
-
- bool isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const override;
- QCameraImageCapture::CaptureDestinations captureDestination() const override;
- void setCaptureDestination(QCameraImageCapture::CaptureDestinations destination) override;
-
-private:
- BbCameraSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbcameracontrol.cpp b/src/plugins/qnx/camera/bbcameracontrol.cpp
deleted file mode 100644
index 8652c0528..000000000
--- a/src/plugins/qnx/camera/bbcameracontrol.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbcameracontrol.h"
-
-#include "bbcamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-BbCameraControl::BbCameraControl(BbCameraSession *session, QObject *parent)
- : QCameraControl(parent)
- , m_session(session)
-{
- connect(m_session, SIGNAL(statusChanged(QCamera::Status)), this, SIGNAL(statusChanged(QCamera::Status)));
- connect(m_session, SIGNAL(stateChanged(QCamera::State)), this, SIGNAL(stateChanged(QCamera::State)));
- connect(m_session, SIGNAL(error(int,QString)), this, SIGNAL(error(int,QString)));
- connect(m_session, SIGNAL(captureModeChanged(QCamera::CaptureModes)), this, SIGNAL(captureModeChanged(QCamera::CaptureModes)));
-}
-
-QCamera::State BbCameraControl::state() const
-{
- return m_session->state();
-}
-
-void BbCameraControl::setState(QCamera::State state)
-{
- m_session->setState(state);
-}
-
-QCamera::CaptureModes BbCameraControl::captureMode() const
-{
- return m_session->captureMode();
-}
-
-void BbCameraControl::setCaptureMode(QCamera::CaptureModes mode)
-{
- m_session->setCaptureMode(mode);
-}
-
-QCamera::Status BbCameraControl::status() const
-{
- return m_session->status();
-}
-
-bool BbCameraControl::isCaptureModeSupported(QCamera::CaptureModes mode) const
-{
- return m_session->isCaptureModeSupported(mode);
-}
-
-bool BbCameraControl::canChangeProperty(PropertyChangeType /* changeType */, QCamera::Status /* status */) const
-{
- return false;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbcameracontrol.h b/src/plugins/qnx/camera/bbcameracontrol.h
deleted file mode 100644
index 0c97bd83c..000000000
--- a/src/plugins/qnx/camera/bbcameracontrol.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/****************************************************************************
-**
-** 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 BBCAMERACONTROL_H
-#define BBCAMERACONTROL_H
-
-#include <qcameracontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraSession;
-
-class BbCameraControl : public QCameraControl
-{
- Q_OBJECT
-public:
- explicit BbCameraControl(BbCameraSession *session, QObject *parent = 0);
-
- QCamera::State state() const override;
- void setState(QCamera::State state) override;
-
- QCamera::Status status() const override;
-
- QCamera::CaptureModes captureMode() const override;
- void setCaptureMode(QCamera::CaptureModes) override;
- bool isCaptureModeSupported(QCamera::CaptureModes mode) const override;
-
- bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const override;
-
-private:
- BbCameraSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbcameraexposurecontrol.cpp b/src/plugins/qnx/camera/bbcameraexposurecontrol.cpp
deleted file mode 100644
index 795e42877..000000000
--- a/src/plugins/qnx/camera/bbcameraexposurecontrol.cpp
+++ /dev/null
@@ -1,232 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbcameraexposurecontrol.h"
-
-#include "bbcamerasession.h"
-
-#include <QDebug>
-
-QT_BEGIN_NAMESPACE
-
-BbCameraExposureControl::BbCameraExposureControl(BbCameraSession *session, QObject *parent)
- : QCameraExposureControl(parent)
- , m_session(session)
- , m_requestedExposureMode(QCameraExposure::ExposureAuto)
-{
- connect(m_session, SIGNAL(statusChanged(QCamera::Status)), this, SLOT(statusChanged(QCamera::Status)));
-}
-
-bool BbCameraExposureControl::isParameterSupported(ExposureParameter parameter) const
-{
- switch (parameter) {
- case QCameraExposureControl::ISO:
- return false;
- case QCameraExposureControl::Aperture:
- return false;
- case QCameraExposureControl::ShutterSpeed:
- return false;
- case QCameraExposureControl::ExposureCompensation:
- return false;
- case QCameraExposureControl::FlashPower:
- return false;
- case QCameraExposureControl::FlashCompensation:
- return false;
- case QCameraExposureControl::TorchPower:
- return false;
- case QCameraExposureControl::SpotMeteringPoint:
- return false;
- case QCameraExposureControl::ExposureMode:
- return true;
- case QCameraExposureControl::MeteringMode:
- return false;
- default:
- return false;
- }
-}
-
-QVariantList BbCameraExposureControl::supportedParameterRange(ExposureParameter parameter, bool *continuous) const
-{
- if (parameter != QCameraExposureControl::ExposureMode) // no other parameter supported by BB10 API at the moment
- return QVariantList();
-
- if (m_session->status() != QCamera::ActiveStatus) // we can query supported exposure modes only with active viewfinder
- return QVariantList();
-
- if (continuous)
- *continuous = false;
-
- int supported = 0;
- camera_scenemode_t modes[20];
- const camera_error_t result = camera_get_scene_modes(m_session->handle(), 20, &supported, modes);
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to retrieve supported scene modes:" << result;
- return QVariantList();
- }
-
- QVariantList exposureModes;
- for (int i = 0; i < supported; ++i) {
- switch (modes[i]) {
- case CAMERA_SCENE_AUTO:
- exposureModes << QVariant::fromValue(QCameraExposure::ExposureAuto);
- break;
- case CAMERA_SCENE_SPORTS:
- exposureModes << QVariant::fromValue(QCameraExposure::ExposureSports);
- break;
- case CAMERA_SCENE_CLOSEUP:
- exposureModes << QVariant::fromValue(QCameraExposure::ExposurePortrait);
- break;
- case CAMERA_SCENE_ACTION:
- exposureModes << QVariant::fromValue(QCameraExposure::ExposureSports);
- break;
- case CAMERA_SCENE_BEACHANDSNOW:
- exposureModes << QVariant::fromValue(QCameraExposure::ExposureBeach) << QVariant::fromValue(QCameraExposure::ExposureSnow);
- break;
- case CAMERA_SCENE_NIGHT:
- exposureModes << QVariant::fromValue(QCameraExposure::ExposureNight);
- break;
- default: break;
- }
- }
-
- return exposureModes;
-}
-
-QVariant BbCameraExposureControl::requestedValue(ExposureParameter parameter) const
-{
- if (parameter != QCameraExposureControl::ExposureMode) // no other parameter supported by BB10 API at the moment
- return QVariant();
-
- return QVariant::fromValue(m_requestedExposureMode);
-}
-
-QVariant BbCameraExposureControl::actualValue(ExposureParameter parameter) const
-{
- if (parameter != QCameraExposureControl::ExposureMode) // no other parameter supported by BB10 API at the moment
- return QVariantList();
-
- if (m_session->status() != QCamera::ActiveStatus) // we can query actual scene modes only with active viewfinder
- return QVariantList();
-
- camera_scenemode_t sceneMode = CAMERA_SCENE_DEFAULT;
- const camera_error_t result = camera_get_scene_mode(m_session->handle(), &sceneMode);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to retrieve scene mode:" << result;
- return QVariant();
- }
-
- switch (sceneMode) {
- case CAMERA_SCENE_AUTO:
- return QVariant::fromValue(QCameraExposure::ExposureAuto);
- case CAMERA_SCENE_SPORTS:
- return QVariant::fromValue(QCameraExposure::ExposureSports);
- case CAMERA_SCENE_CLOSEUP:
- return QVariant::fromValue(QCameraExposure::ExposurePortrait);
- case CAMERA_SCENE_ACTION:
- return QVariant::fromValue(QCameraExposure::ExposureSports);
- case CAMERA_SCENE_BEACHANDSNOW:
- return (m_requestedExposureMode == QCameraExposure::ExposureBeach ? QVariant::fromValue(QCameraExposure::ExposureBeach)
- : QVariant::fromValue(QCameraExposure::ExposureSnow));
- case CAMERA_SCENE_NIGHT:
- return QVariant::fromValue(QCameraExposure::ExposureNight);
- default:
- break;
- }
-
- return QVariant();
-}
-
-bool BbCameraExposureControl::setValue(ExposureParameter parameter, const QVariant& value)
-{
- if (parameter != QCameraExposureControl::ExposureMode) // no other parameter supported by BB10 API at the moment
- return false;
-
- if (m_session->status() != QCamera::ActiveStatus) // we can set actual scene modes only with active viewfinder
- return false;
-
- camera_scenemode_t sceneMode = CAMERA_SCENE_DEFAULT;
-
- if (value.isValid()) {
- m_requestedExposureMode = value.value<QCameraExposure::ExposureMode>();
- emit requestedValueChanged(QCameraExposureControl::ExposureMode);
-
- switch (m_requestedExposureMode) {
- case QCameraExposure::ExposureAuto:
- sceneMode = CAMERA_SCENE_AUTO;
- break;
- case QCameraExposure::ExposureSports:
- sceneMode = CAMERA_SCENE_SPORTS;
- break;
- case QCameraExposure::ExposurePortrait:
- sceneMode = CAMERA_SCENE_CLOSEUP;
- break;
- case QCameraExposure::ExposureBeach:
- sceneMode = CAMERA_SCENE_BEACHANDSNOW;
- break;
- case QCameraExposure::ExposureSnow:
- sceneMode = CAMERA_SCENE_BEACHANDSNOW;
- break;
- case QCameraExposure::ExposureNight:
- sceneMode = CAMERA_SCENE_NIGHT;
- break;
- default:
- sceneMode = CAMERA_SCENE_DEFAULT;
- break;
- }
- }
-
- const camera_error_t result = camera_set_scene_mode(m_session->handle(), sceneMode);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to set scene mode:" << result;
- return false;
- }
-
- emit actualValueChanged(QCameraExposureControl::ExposureMode);
-
- return true;
-}
-
-void BbCameraExposureControl::statusChanged(QCamera::Status status)
-{
- if (status == QCamera::ActiveStatus || status == QCamera::LoadedStatus)
- emit parameterRangeChanged(QCameraExposureControl::ExposureMode);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbcameraexposurecontrol.h b/src/plugins/qnx/camera/bbcameraexposurecontrol.h
deleted file mode 100644
index 2d0da29bd..000000000
--- a/src/plugins/qnx/camera/bbcameraexposurecontrol.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/****************************************************************************
-**
-** 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 BBCAMERAEXPOSURECONTROL_H
-#define BBCAMERAEXPOSURECONTROL_H
-
-#include <qcameraexposurecontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraSession;
-
-class BbCameraExposureControl : public QCameraExposureControl
-{
- Q_OBJECT
-public:
- explicit BbCameraExposureControl(BbCameraSession *session, QObject *parent = 0);
-
- bool isParameterSupported(ExposureParameter parameter) const override;
- QVariantList supportedParameterRange(ExposureParameter parameter, bool *continuous) const override;
-
- QVariant requestedValue(ExposureParameter parameter) const override;
- QVariant actualValue(ExposureParameter parameter) const override;
- bool setValue(ExposureParameter parameter, const QVariant& value) override;
-
-private Q_SLOTS:
- void statusChanged(QCamera::Status status);
-
-private:
- BbCameraSession *m_session;
- QCameraExposure::ExposureMode m_requestedExposureMode;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbcameraflashcontrol.cpp b/src/plugins/qnx/camera/bbcameraflashcontrol.cpp
deleted file mode 100644
index 1cf8b38f9..000000000
--- a/src/plugins/qnx/camera/bbcameraflashcontrol.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbcameraflashcontrol.h"
-
-#include "bbcamerasession.h"
-
-#include <QDebug>
-
-QT_BEGIN_NAMESPACE
-
-BbCameraFlashControl::BbCameraFlashControl(BbCameraSession *session, QObject *parent)
- : QCameraFlashControl(parent)
- , m_session(session)
- , m_flashMode(QCameraExposure::FlashAuto)
-{
-}
-
-QCameraExposure::FlashModes BbCameraFlashControl::flashMode() const
-{
- return m_flashMode;
-}
-
-void BbCameraFlashControl::setFlashMode(QCameraExposure::FlashModes mode)
-{
- if (m_flashMode == mode)
- return;
-
- if (m_session->status() != QCamera::ActiveStatus) // can only be changed when viewfinder is active
- return;
-
- if (m_flashMode == QCameraExposure::FlashVideoLight) {
- const camera_error_t result = camera_config_videolight(m_session->handle(), CAMERA_VIDEOLIGHT_OFF);
- if (result != CAMERA_EOK)
- qWarning() << "Unable to switch off video light:" << result;
- }
-
- m_flashMode = mode;
-
- if (m_flashMode == QCameraExposure::FlashVideoLight) {
- const camera_error_t result = camera_config_videolight(m_session->handle(), CAMERA_VIDEOLIGHT_ON);
- if (result != CAMERA_EOK)
- qWarning() << "Unable to switch on video light:" << result;
- } else {
- camera_flashmode_t flashMode = CAMERA_FLASH_AUTO;
-
- if (m_flashMode.testFlag(QCameraExposure::FlashAuto)) flashMode = CAMERA_FLASH_AUTO;
- else if (mode.testFlag(QCameraExposure::FlashOff)) flashMode = CAMERA_FLASH_OFF;
- else if (mode.testFlag(QCameraExposure::FlashOn)) flashMode = CAMERA_FLASH_ON;
-
- const camera_error_t result = camera_config_flash(m_session->handle(), flashMode);
- if (result != CAMERA_EOK)
- qWarning() << "Unable to configure flash:" << result;
- }
-}
-
-bool BbCameraFlashControl::isFlashModeSupported(QCameraExposure::FlashModes mode) const
-{
- bool supportsVideoLight = false;
- if (m_session->handle() != CAMERA_HANDLE_INVALID) {
- supportsVideoLight = camera_has_feature(m_session->handle(), CAMERA_FEATURE_VIDEOLIGHT);
- }
-
- return (mode == QCameraExposure::FlashOff ||
- mode == QCameraExposure::FlashOn ||
- mode == QCameraExposure::FlashAuto ||
- ((mode == QCameraExposure::FlashVideoLight) && supportsVideoLight));
-}
-
-bool BbCameraFlashControl::isFlashReady() const
-{
- //TODO: check for flash charge-level here?!?
- return true;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbcameraflashcontrol.h b/src/plugins/qnx/camera/bbcameraflashcontrol.h
deleted file mode 100644
index 8ce9c1f41..000000000
--- a/src/plugins/qnx/camera/bbcameraflashcontrol.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************************
-**
-** 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 BBCAMERAFLASHCONTROL_H
-#define BBCAMERAFLASHCONTROL_H
-
-#include <qcameraflashcontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraSession;
-
-class BbCameraFlashControl : public QCameraFlashControl
-{
- Q_OBJECT
-public:
- explicit BbCameraFlashControl(BbCameraSession *session, QObject *parent = 0);
-
- QCameraExposure::FlashModes flashMode() const override;
- void setFlashMode(QCameraExposure::FlashModes mode) override;
- bool isFlashModeSupported(QCameraExposure::FlashModes mode) const override;
- bool isFlashReady() const override;
-
-private:
- BbCameraSession *m_session;
- QCameraExposure::FlashModes m_flashMode;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbcamerafocuscontrol.cpp b/src/plugins/qnx/camera/bbcamerafocuscontrol.cpp
deleted file mode 100644
index a815882d2..000000000
--- a/src/plugins/qnx/camera/bbcamerafocuscontrol.cpp
+++ /dev/null
@@ -1,329 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbcamerafocuscontrol.h"
-
-#include "bbcamerasession.h"
-
-#include <QDebug>
-
-QT_BEGIN_NAMESPACE
-
-BbCameraFocusControl::BbCameraFocusControl(BbCameraSession *session, QObject *parent)
- : QCameraFocusControl(parent)
- , m_session(session)
- , m_focusMode(QCameraFocus::FocusModes())
- , m_focusPointMode(QCameraFocus::FocusPointAuto)
- , m_customFocusPoint(QPointF(0, 0))
-{
-}
-
-QCameraFocus::FocusModes BbCameraFocusControl::focusMode() const
-{
- camera_focusmode_t focusMode = CAMERA_FOCUSMODE_OFF;
-
- const camera_error_t result = camera_get_focus_mode(m_session->handle(), &focusMode);
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to retrieve focus mode from camera:" << result;
- return QCameraFocus::FocusModes();
- }
-
- switch (focusMode) {
- case CAMERA_FOCUSMODE_EDOF:
- return QCameraFocus::HyperfocalFocus;
- case CAMERA_FOCUSMODE_MANUAL:
- return QCameraFocus::ManualFocus;
- case CAMERA_FOCUSMODE_AUTO:
- return QCameraFocus::AutoFocus;
- case CAMERA_FOCUSMODE_MACRO:
- return QCameraFocus::MacroFocus;
- case CAMERA_FOCUSMODE_CONTINUOUS_AUTO:
- return QCameraFocus::ContinuousFocus;
- case CAMERA_FOCUSMODE_CONTINUOUS_MACRO: // fall through
- case CAMERA_FOCUSMODE_OFF: // fall through
- default:
- return QCameraFocus::FocusModes();
- }
-}
-
-void BbCameraFocusControl::setFocusMode(QCameraFocus::FocusModes mode)
-{
- if (m_focusMode == mode)
- return;
-
- camera_focusmode_t focusMode = CAMERA_FOCUSMODE_OFF;
-
- if (mode == QCameraFocus::HyperfocalFocus)
- focusMode = CAMERA_FOCUSMODE_EDOF;
- else if (mode == QCameraFocus::ManualFocus)
- focusMode = CAMERA_FOCUSMODE_MANUAL;
- else if (mode == QCameraFocus::AutoFocus)
- focusMode = CAMERA_FOCUSMODE_AUTO;
- else if (mode == QCameraFocus::MacroFocus)
- focusMode = CAMERA_FOCUSMODE_MACRO;
- else if (mode == QCameraFocus::ContinuousFocus)
- focusMode = CAMERA_FOCUSMODE_CONTINUOUS_AUTO;
-
- const camera_error_t result = camera_set_focus_mode(m_session->handle(), focusMode);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to set focus mode:" << result;
- return;
- }
-
- m_focusMode = mode;
- emit focusModeChanged(m_focusMode);
-}
-
-bool BbCameraFocusControl::isFocusModeSupported(QCameraFocus::FocusModes mode) const
-{
- if (m_session->state() == QCamera::UnloadedState)
- return false;
-
- if (mode == QCameraFocus::HyperfocalFocus)
- return false; //TODO how to check?
- else if (mode == QCameraFocus::ManualFocus)
- return camera_has_feature(m_session->handle(), CAMERA_FEATURE_MANUALFOCUS);
- else if (mode == QCameraFocus::AutoFocus)
- return camera_has_feature(m_session->handle(), CAMERA_FEATURE_AUTOFOCUS);
- else if (mode == QCameraFocus::MacroFocus)
- return camera_has_feature(m_session->handle(), CAMERA_FEATURE_MACROFOCUS);
- else if (mode == QCameraFocus::ContinuousFocus)
- return camera_has_feature(m_session->handle(), CAMERA_FEATURE_AUTOFOCUS);
-
- return false;
-}
-
-QCameraFocus::FocusPointMode BbCameraFocusControl::focusPointMode() const
-{
- return m_focusPointMode;
-}
-
-void BbCameraFocusControl::setFocusPointMode(QCameraFocus::FocusPointMode mode)
-{
- if (m_session->status() != QCamera::ActiveStatus)
- return;
-
- if (m_focusPointMode == mode)
- return;
-
- m_focusPointMode = mode;
- emit focusPointModeChanged(m_focusPointMode);
-
- if (m_focusPointMode == QCameraFocus::FocusPointAuto) {
- //TODO: is this correct?
- const camera_error_t result = camera_set_focus_regions(m_session->handle(), 0, 0);
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to set focus region:" << result;
- return;
- }
-
- emit focusZonesChanged();
- } else if (m_focusPointMode == QCameraFocus::FocusPointCenter) {
- // get the size of the viewfinder
- int viewfinderWidth = 0;
- int viewfinderHeight = 0;
-
- if (!retrieveViewfinderSize(&viewfinderWidth, &viewfinderHeight))
- return;
-
- // define a 40x40 pixel focus region in the center of the viewfinder
- camera_region_t focusRegion;
- focusRegion.left = (viewfinderWidth / 2) - 20;
- focusRegion.top = (viewfinderHeight / 2) - 20;
- focusRegion.width = 40;
- focusRegion.height = 40;
-
- camera_error_t result = camera_set_focus_regions(m_session->handle(), 1, &focusRegion);
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to set focus region:" << result;
- return;
- }
-
- // re-set focus mode to apply focus region changes
- camera_focusmode_t focusMode = CAMERA_FOCUSMODE_OFF;
- result = camera_get_focus_mode(m_session->handle(), &focusMode);
- camera_set_focus_mode(m_session->handle(), focusMode);
-
- emit focusZonesChanged();
-
- } else if (m_focusPointMode == QCameraFocus::FocusPointFaceDetection) {
- //TODO: implement later
- } else if (m_focusPointMode == QCameraFocus::FocusPointCustom) {
- updateCustomFocusRegion();
- }
-}
-
-bool BbCameraFocusControl::isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const
-{
- if (m_session->state() == QCamera::UnloadedState)
- return false;
-
- if (mode == QCameraFocus::FocusPointAuto) {
- return camera_has_feature(m_session->handle(), CAMERA_FEATURE_AUTOFOCUS);
- } else if (mode == QCameraFocus::FocusPointCenter) {
- return camera_has_feature(m_session->handle(), CAMERA_FEATURE_REGIONFOCUS);
- } else if (mode == QCameraFocus::FocusPointFaceDetection) {
- return false; //TODO: implement via custom region in combination with face detection in viewfinder
- } else if (mode == QCameraFocus::FocusPointCustom) {
- return camera_has_feature(m_session->handle(), CAMERA_FEATURE_REGIONFOCUS);
- }
-
- return false;
-}
-
-QPointF BbCameraFocusControl::customFocusPoint() const
-{
- return m_customFocusPoint;
-}
-
-void BbCameraFocusControl::setCustomFocusPoint(const QPointF &point)
-{
- if (m_customFocusPoint == point)
- return;
-
- m_customFocusPoint = point;
- emit customFocusPointChanged(m_customFocusPoint);
-
- updateCustomFocusRegion();
-}
-
-QCameraFocusZoneList BbCameraFocusControl::focusZones() const
-{
- if (m_session->state() == QCamera::UnloadedState)
- return QCameraFocusZoneList();
-
- camera_region_t regions[20];
- int supported = 0;
- int asked = 0;
- camera_error_t result = camera_get_focus_regions(m_session->handle(), 20, &supported, &asked, regions);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to retrieve focus regions:" << result;
- return QCameraFocusZoneList();
- }
-
- // retrieve width and height of viewfinder
- int viewfinderWidth = 0;
- int viewfinderHeight = 0;
- if (m_session->captureMode() & QCamera::CaptureStillImage)
- result = camera_get_photovf_property(m_session->handle(),
- CAMERA_IMGPROP_WIDTH, &viewfinderWidth,
- CAMERA_IMGPROP_HEIGHT, &viewfinderHeight);
- else if (m_session->captureMode() & QCamera::CaptureVideo)
- result = camera_get_videovf_property(m_session->handle(),
- CAMERA_IMGPROP_WIDTH, &viewfinderWidth,
- CAMERA_IMGPROP_HEIGHT, &viewfinderHeight);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to retrieve viewfinder size:" << result;
- return QCameraFocusZoneList();
- }
-
- QCameraFocusZoneList list;
- for (int i = 0; i < asked; ++i) {
- const int x = regions[i].left;
- const int y = regions[i].top;
- const int width = regions[i].width;
- const int height = regions[i].height;
-
- QRectF rect(static_cast<float>(x)/static_cast<float>(viewfinderWidth),
- static_cast<float>(y)/static_cast<float>(viewfinderHeight),
- static_cast<float>(width)/static_cast<float>(viewfinderWidth),
- static_cast<float>(height)/static_cast<float>(viewfinderHeight));
-
- list << QCameraFocusZone(rect, QCameraFocusZone::Focused); //TODO: how to know if a zone is unused/selected/focused?!?
- }
-
- return list;
-}
-
-void BbCameraFocusControl::updateCustomFocusRegion()
-{
- // get the size of the viewfinder
- int viewfinderWidth = 0;
- int viewfinderHeight = 0;
-
- if (!retrieveViewfinderSize(&viewfinderWidth, &viewfinderHeight))
- return;
-
- // define a 40x40 pixel focus region around the custom focus point
- camera_region_t focusRegion;
- focusRegion.left = qMax(0, static_cast<int>(m_customFocusPoint.x() * viewfinderWidth) - 20);
- focusRegion.top = qMax(0, static_cast<int>(m_customFocusPoint.y() * viewfinderHeight) - 20);
- focusRegion.width = 40;
- focusRegion.height = 40;
-
- camera_error_t result = camera_set_focus_regions(m_session->handle(), 1, &focusRegion);
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to set focus region:" << result;
- return;
- }
-
- // re-set focus mode to apply focus region changes
- camera_focusmode_t focusMode = CAMERA_FOCUSMODE_OFF;
- result = camera_get_focus_mode(m_session->handle(), &focusMode);
- camera_set_focus_mode(m_session->handle(), focusMode);
-
- emit focusZonesChanged();
-}
-
-bool BbCameraFocusControl::retrieveViewfinderSize(int *width, int *height)
-{
- if (!width || !height)
- return false;
-
- camera_error_t result = CAMERA_EOK;
- if (m_session->captureMode() & QCamera::CaptureStillImage)
- result = camera_get_photovf_property(m_session->handle(),
- CAMERA_IMGPROP_WIDTH, width,
- CAMERA_IMGPROP_HEIGHT, height);
- else if (m_session->captureMode() & QCamera::CaptureVideo)
- result = camera_get_videovf_property(m_session->handle(),
- CAMERA_IMGPROP_WIDTH, width,
- CAMERA_IMGPROP_HEIGHT, height);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to retrieve viewfinder size:" << result;
- return false;
- }
-
- return true;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbcamerafocuscontrol.h b/src/plugins/qnx/camera/bbcamerafocuscontrol.h
deleted file mode 100644
index bf11ea03e..000000000
--- a/src/plugins/qnx/camera/bbcamerafocuscontrol.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/****************************************************************************
-**
-** 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 BBCAMERAFOCUSCONTROL_H
-#define BBCAMERAFOCUSCONTROL_H
-
-#include <qcamerafocuscontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraSession;
-
-class BbCameraFocusControl : public QCameraFocusControl
-{
- Q_OBJECT
-public:
- explicit BbCameraFocusControl(BbCameraSession *session, QObject *parent = 0);
-
- QCameraFocus::FocusModes focusMode() const override;
- void setFocusMode(QCameraFocus::FocusModes mode) override;
- bool isFocusModeSupported(QCameraFocus::FocusModes mode) const override;
- QCameraFocus::FocusPointMode focusPointMode() const override;
- void setFocusPointMode(QCameraFocus::FocusPointMode mode) override;
- bool isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const override;
- QPointF customFocusPoint() const override;
- void setCustomFocusPoint(const QPointF &point) override;
- QCameraFocusZoneList focusZones() const override;
-
-private:
- void updateCustomFocusRegion();
- bool retrieveViewfinderSize(int *width, int *height);
-
- BbCameraSession *m_session;
-
- QCameraFocus::FocusModes m_focusMode;
- QCameraFocus::FocusPointMode m_focusPointMode;
- QPointF m_customFocusPoint;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbcameraimagecapturecontrol.cpp b/src/plugins/qnx/camera/bbcameraimagecapturecontrol.cpp
deleted file mode 100644
index 2fcd2baed..000000000
--- a/src/plugins/qnx/camera/bbcameraimagecapturecontrol.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbcameraimagecapturecontrol.h"
-
-#include "bbcamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-BbCameraImageCaptureControl::BbCameraImageCaptureControl(BbCameraSession *session, QObject *parent)
- : QCameraImageCaptureControl(parent)
- , m_session(session)
-{
- connect(m_session, SIGNAL(readyForCaptureChanged(bool)), this, SIGNAL(readyForCaptureChanged(bool)));
- connect(m_session, SIGNAL(imageExposed(int)), this, SIGNAL(imageExposed(int)));
- connect(m_session, SIGNAL(imageCaptured(int,QImage)), this, SIGNAL(imageCaptured(int,QImage)));
- connect(m_session, SIGNAL(imageMetadataAvailable(int,QString,QVariant)), this, SIGNAL(imageMetadataAvailable(int,QString,QVariant)));
- connect(m_session, SIGNAL(imageAvailable(int,QVideoFrame)), this, SIGNAL(imageAvailable(int,QVideoFrame)));
- connect(m_session, SIGNAL(imageSaved(int,QString)), this, SIGNAL(imageSaved(int,QString)));
- connect(m_session, SIGNAL(imageCaptureError(int,int,QString)), this, SIGNAL(error(int,int,QString)));
-}
-
-bool BbCameraImageCaptureControl::isReadyForCapture() const
-{
- return m_session->isReadyForCapture();
-}
-
-QCameraImageCapture::DriveMode BbCameraImageCaptureControl::driveMode() const
-{
- return m_session->driveMode();
-}
-
-void BbCameraImageCaptureControl::setDriveMode(QCameraImageCapture::DriveMode mode)
-{
- m_session->setDriveMode(mode);
-}
-
-int BbCameraImageCaptureControl::capture(const QString &fileName)
-{
- return m_session->capture(fileName);
-}
-
-void BbCameraImageCaptureControl::cancelCapture()
-{
- m_session->cancelCapture();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbcameraimagecapturecontrol.h b/src/plugins/qnx/camera/bbcameraimagecapturecontrol.h
deleted file mode 100644
index b8013ba1f..000000000
--- a/src/plugins/qnx/camera/bbcameraimagecapturecontrol.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/****************************************************************************
-**
-** 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 BBCAMERAIMAGECAPTURECONTROL_H
-#define BBCAMERAIMAGECAPTURECONTROL_H
-
-#include <qcameraimagecapturecontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraSession;
-
-class BbCameraImageCaptureControl : public QCameraImageCaptureControl
-{
- Q_OBJECT
-public:
- explicit BbCameraImageCaptureControl(BbCameraSession *session, QObject *parent = 0);
-
- bool isReadyForCapture() const override;
-
- QCameraImageCapture::DriveMode driveMode() const override;
- void setDriveMode(QCameraImageCapture::DriveMode mode) override;
-
- int capture(const QString &fileName) override;
- void cancelCapture() override;
-
-private:
- BbCameraSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbcameraimageprocessingcontrol.cpp b/src/plugins/qnx/camera/bbcameraimageprocessingcontrol.cpp
deleted file mode 100644
index 250a85ca0..000000000
--- a/src/plugins/qnx/camera/bbcameraimageprocessingcontrol.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbcameraimageprocessingcontrol.h"
-
-#include "bbcamerasession.h"
-
-#include <QDebug>
-
-QT_BEGIN_NAMESPACE
-
-BbCameraImageProcessingControl::BbCameraImageProcessingControl(BbCameraSession *session, QObject *parent)
- : QCameraImageProcessingControl(parent)
- , m_session(session)
-{
-}
-
-bool BbCameraImageProcessingControl::isParameterSupported(ProcessingParameter parameter) const
-{
- return (parameter == QCameraImageProcessingControl::WhiteBalancePreset);
-}
-
-bool BbCameraImageProcessingControl::isParameterValueSupported(ProcessingParameter parameter, const QVariant &value) const
-{
- if (parameter != QCameraImageProcessingControl::WhiteBalancePreset)
- return false;
-
- if (m_session->handle() == CAMERA_HANDLE_INVALID)
- return false;
-
- int supported = 0;
- camera_whitebalancemode_t modes[20];
- const camera_error_t result = camera_get_whitebalance_modes(m_session->handle(), 20, &supported, modes);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to retrieve supported whitebalance modes:" << result;
- return false;
- }
-
- QSet<QCameraImageProcessing::WhiteBalanceMode> supportedModes;
- for (int i = 0; i < supported; ++i) {
- switch (modes[i]) {
- case CAMERA_WHITEBALANCEMODE_AUTO:
- supportedModes.insert(QCameraImageProcessing::WhiteBalanceAuto);
- break;
- case CAMERA_WHITEBALANCEMODE_MANUAL:
- supportedModes.insert(QCameraImageProcessing::WhiteBalanceManual);
- break;
- default:
- break;
- }
- }
-
- return supportedModes.contains(value.value<QCameraImageProcessing::WhiteBalanceMode>());
-}
-
-QVariant BbCameraImageProcessingControl::parameter(ProcessingParameter parameter) const
-{
- if (parameter != QCameraImageProcessingControl::WhiteBalancePreset)
- return QVariant();
-
- if (m_session->handle() == CAMERA_HANDLE_INVALID)
- return QVariant();
-
- camera_whitebalancemode_t mode;
- const camera_error_t result = camera_get_whitebalance_mode(m_session->handle(), &mode);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to retrieve current whitebalance mode:" << result;
- return QVariant();
- }
-
- switch (mode) {
- case CAMERA_WHITEBALANCEMODE_AUTO:
- return QVariant::fromValue(QCameraImageProcessing::WhiteBalanceAuto);
- case CAMERA_WHITEBALANCEMODE_MANUAL:
- return QVariant::fromValue(QCameraImageProcessing::WhiteBalanceManual);
- default:
- return QVariant();
- }
-}
-
-void BbCameraImageProcessingControl::setParameter(ProcessingParameter parameter, const QVariant &value)
-{
- if (parameter != QCameraImageProcessingControl::WhiteBalancePreset)
- return;
-
- if (m_session->handle() == CAMERA_HANDLE_INVALID)
- return;
-
- camera_whitebalancemode_t mode = CAMERA_WHITEBALANCEMODE_DEFAULT;
- switch (value.value<QCameraImageProcessing::WhiteBalanceMode>()) {
- case QCameraImageProcessing::WhiteBalanceAuto:
- mode = CAMERA_WHITEBALANCEMODE_AUTO;
- break;
- case QCameraImageProcessing::WhiteBalanceManual:
- mode = CAMERA_WHITEBALANCEMODE_MANUAL;
- break;
- default:
- break;
- }
-
- const camera_error_t result = camera_set_whitebalance_mode(m_session->handle(), mode);
-
- if (result != CAMERA_EOK)
- qWarning() << "Unable to set whitebalance mode:" << result;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbcameraimageprocessingcontrol.h b/src/plugins/qnx/camera/bbcameraimageprocessingcontrol.h
deleted file mode 100644
index 0eefdd2a1..000000000
--- a/src/plugins/qnx/camera/bbcameraimageprocessingcontrol.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/****************************************************************************
-**
-** 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 BBCAMERAIMAGEPROCESSINGCONTROL_H
-#define BBCAMERAIMAGEPROCESSINGCONTROL_H
-
-#include <qcameraimageprocessingcontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraSession;
-
-class BbCameraImageProcessingControl : public QCameraImageProcessingControl
-{
- Q_OBJECT
-public:
- explicit BbCameraImageProcessingControl(BbCameraSession *session, QObject *parent = 0);
-
- bool isParameterSupported(ProcessingParameter) const override;
- bool isParameterValueSupported(ProcessingParameter parameter, const QVariant &value) const override;
- QVariant parameter(ProcessingParameter parameter) const override;
- void setParameter(ProcessingParameter parameter, const QVariant &value) override;
-
-private:
- BbCameraSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbcamerainfocontrol.cpp b/src/plugins/qnx/camera/bbcamerainfocontrol.cpp
deleted file mode 100644
index 3d0481408..000000000
--- a/src/plugins/qnx/camera/bbcamerainfocontrol.cpp
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "bbcamerainfocontrol.h"
-
-#include "bbcamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-BbCameraInfoControl::BbCameraInfoControl(QObject *parent)
- : QCameraInfoControl(parent)
-{
-}
-
-QCamera::Position BbCameraInfoControl::position(const QString &deviceName)
-{
- if (deviceName == QString::fromUtf8(BbCameraSession::cameraIdentifierFront()))
- return QCamera::FrontFace;
- else if (deviceName == QString::fromUtf8(BbCameraSession::cameraIdentifierRear()))
- return QCamera::BackFace;
- else
- return QCamera::UnspecifiedPosition;
-}
-
-int BbCameraInfoControl::orientation(const QString &deviceName)
-{
- // The camera sensor orientation could be retrieved with camera_get_native_orientation()
- // but since the sensor angular offset is compensated with camera_set_videovf_property() and
- // camera_set_photovf_property() we should always return 0 here.
- Q_UNUSED(deviceName);
- return 0;
-}
-
-QCamera::Position BbCameraInfoControl::cameraPosition(const QString &deviceName) const
-{
- return position(deviceName);
-}
-
-int BbCameraInfoControl::cameraOrientation(const QString &deviceName) const
-{
- return orientation(deviceName);
-}
-
-QT_END_NAMESPACE
-
diff --git a/src/plugins/qnx/camera/bbcamerainfocontrol.h b/src/plugins/qnx/camera/bbcamerainfocontrol.h
deleted file mode 100644
index 1c51410f7..000000000
--- a/src/plugins/qnx/camera/bbcamerainfocontrol.h
+++ /dev/null
@@ -1,63 +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 BBCAMERAINFOCONTROL_H
-#define BBCAMERAINFOCONTROL_H
-
-#include <qcamerainfocontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraInfoControl : public QCameraInfoControl
-{
- Q_OBJECT
-public:
- explicit BbCameraInfoControl(QObject *parent = 0);
-
- QCamera::Position cameraPosition(const QString &deviceName) const;
- int cameraOrientation(const QString &deviceName) const;
-
- static QCamera::Position position(const QString &deviceName);
- static int orientation(const QString &deviceName);
-};
-
-QT_END_NAMESPACE
-
-#endif // BBCAMERAINFOCONTROL_H
-
diff --git a/src/plugins/qnx/camera/bbcameralockscontrol.cpp b/src/plugins/qnx/camera/bbcameralockscontrol.cpp
deleted file mode 100644
index 5802064e6..000000000
--- a/src/plugins/qnx/camera/bbcameralockscontrol.cpp
+++ /dev/null
@@ -1,256 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbcameralockscontrol.h"
-
-#include "bbcamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-BbCameraLocksControl::BbCameraLocksControl(BbCameraSession *session, QObject *parent)
- : QCameraLocksControl(parent)
- , m_session(session)
- , m_locksApplyMode(IndependentMode)
- , m_focusLockStatus(QCamera::Unlocked)
- , m_exposureLockStatus(QCamera::Unlocked)
- , m_whiteBalanceLockStatus(QCamera::Unlocked)
- , m_currentLockTypes(QCamera::NoLock)
- , m_supportedLockTypes(QCamera::NoLock)
-{
- connect(m_session, SIGNAL(cameraOpened()), SLOT(cameraOpened()));
- connect(m_session, SIGNAL(focusStatusChanged(int)), SLOT(focusStatusChanged(int)));
-}
-
-QCamera::LockTypes BbCameraLocksControl::supportedLocks() const
-{
- return (QCamera::LockFocus | QCamera::LockExposure | QCamera::LockWhiteBalance);
-}
-
-QCamera::LockStatus BbCameraLocksControl::lockStatus(QCamera::LockType lock) const
-{
- if (!m_supportedLockTypes.testFlag(lock) || (m_session->handle() == CAMERA_HANDLE_INVALID))
- return QCamera::Locked;
-
- switch (lock) {
- case QCamera::LockExposure:
- return m_exposureLockStatus;
- case QCamera::LockWhiteBalance:
- return m_whiteBalanceLockStatus;
- case QCamera::LockFocus:
- return m_focusLockStatus;
- default:
- return QCamera::Locked;
- }
-}
-
-void BbCameraLocksControl::searchAndLock(QCamera::LockTypes locks)
-{
- if (m_session->handle() == CAMERA_HANDLE_INVALID)
- return;
-
- // filter out unsupported locks
- locks &= m_supportedLockTypes;
-
- m_currentLockTypes |= locks;
-
- uint32_t lockModes = CAMERA_3A_NONE;
-
- switch (m_locksApplyMode) {
- case IndependentMode:
- if (m_currentLockTypes & QCamera::LockExposure)
- lockModes |= CAMERA_3A_AUTOEXPOSURE;
- if (m_currentLockTypes & QCamera::LockWhiteBalance)
- lockModes |= CAMERA_3A_AUTOWHITEBALANCE;
- if (m_currentLockTypes & QCamera::LockFocus)
- lockModes |= CAMERA_3A_AUTOFOCUS;
- break;
- case FocusExposureBoundMode:
- if ((m_currentLockTypes & QCamera::LockExposure) || (m_currentLockTypes & QCamera::LockFocus))
- lockModes = (CAMERA_3A_AUTOEXPOSURE | CAMERA_3A_AUTOFOCUS);
- break;
- case AllBoundMode:
- lockModes = (CAMERA_3A_AUTOEXPOSURE | CAMERA_3A_AUTOFOCUS | CAMERA_3A_AUTOWHITEBALANCE);
- break;
- case FocusOnlyMode:
- lockModes = CAMERA_3A_AUTOFOCUS;
- break;
- }
-
- const camera_error_t result = camera_set_3a_lock(m_session->handle(), lockModes);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to set lock modes:" << result;
- } else {
- if (lockModes & CAMERA_3A_AUTOFOCUS) {
- // handled by focusStatusChanged()
- }
-
- if (lockModes & CAMERA_3A_AUTOEXPOSURE) {
- m_exposureLockStatus = QCamera::Locked;
- emit lockStatusChanged(QCamera::LockExposure, QCamera::Locked, QCamera::LockAcquired);
- }
-
- if (lockModes & CAMERA_3A_AUTOWHITEBALANCE) {
- m_whiteBalanceLockStatus = QCamera::Locked;
- emit lockStatusChanged(QCamera::LockWhiteBalance, QCamera::Locked, QCamera::LockAcquired);
- }
- }
-}
-
-void BbCameraLocksControl::unlock(QCamera::LockTypes locks)
-{
- // filter out unsupported locks
- locks &= m_supportedLockTypes;
-
- m_currentLockTypes &= ~locks;
-
- uint32_t lockModes = CAMERA_3A_NONE;
-
- switch (m_locksApplyMode) {
- case IndependentMode:
- if (m_currentLockTypes & QCamera::LockExposure)
- lockModes |= CAMERA_3A_AUTOEXPOSURE;
- if (m_currentLockTypes & QCamera::LockWhiteBalance)
- lockModes |= CAMERA_3A_AUTOWHITEBALANCE;
- if (m_currentLockTypes & QCamera::LockFocus)
- lockModes |= CAMERA_3A_AUTOFOCUS;
- break;
- case FocusExposureBoundMode:
- if ((m_currentLockTypes & QCamera::LockExposure) || (m_currentLockTypes & QCamera::LockFocus))
- lockModes = (CAMERA_3A_AUTOEXPOSURE | CAMERA_3A_AUTOFOCUS);
- break;
- case AllBoundMode:
- lockModes = (CAMERA_3A_AUTOEXPOSURE | CAMERA_3A_AUTOFOCUS | CAMERA_3A_AUTOWHITEBALANCE);
- break;
- case FocusOnlyMode:
- lockModes = CAMERA_3A_AUTOFOCUS;
- break;
- }
-
- const camera_error_t result = camera_set_3a_lock(m_session->handle(), lockModes);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to set lock modes:" << result;
- } else {
- if (locks.testFlag(QCamera::LockFocus)) {
- // handled by focusStatusChanged()
- }
-
- if (locks.testFlag(QCamera::LockExposure)) {
- m_exposureLockStatus = QCamera::Unlocked;
- emit lockStatusChanged(QCamera::LockExposure, QCamera::Unlocked, QCamera::UserRequest);
- }
-
- if (locks.testFlag(QCamera::LockWhiteBalance)) {
- m_whiteBalanceLockStatus = QCamera::Unlocked;
- emit lockStatusChanged(QCamera::LockWhiteBalance, QCamera::Unlocked, QCamera::UserRequest);
- }
- }
-}
-
-void BbCameraLocksControl::cameraOpened()
-{
- // retrieve information about lock apply modes
- int supported = 0;
- uint32_t modes[20];
-
- const camera_error_t result = camera_get_3a_lock_modes(m_session->handle(), 20, &supported, modes);
-
- if (result == CAMERA_EOK) {
- // see API documentation of camera_get_3a_lock_modes for explanation of case discrimination below
- if (supported == 4) {
- m_locksApplyMode = IndependentMode;
- } else if (supported == 3) {
- m_locksApplyMode = FocusExposureBoundMode;
- } else if (supported == 2) {
- if (modes[0] == (CAMERA_3A_AUTOFOCUS | CAMERA_3A_AUTOEXPOSURE | CAMERA_3A_AUTOWHITEBALANCE))
- m_locksApplyMode = AllBoundMode;
- else
- m_locksApplyMode = FocusOnlyMode;
- }
- }
-
- // retrieve information about supported lock types
- m_supportedLockTypes = QCamera::NoLock;
-
- if (camera_has_feature(m_session->handle(), CAMERA_FEATURE_AUTOFOCUS))
- m_supportedLockTypes |= QCamera::LockFocus;
-
- if (camera_has_feature(m_session->handle(), CAMERA_FEATURE_AUTOEXPOSURE))
- m_supportedLockTypes |= QCamera::LockExposure;
-
- if (camera_has_feature(m_session->handle(), CAMERA_FEATURE_AUTOWHITEBALANCE))
- m_supportedLockTypes |= QCamera::LockWhiteBalance;
-
- m_focusLockStatus = QCamera::Unlocked;
- m_exposureLockStatus = QCamera::Unlocked;
- m_whiteBalanceLockStatus = QCamera::Unlocked;
-}
-
-void BbCameraLocksControl::focusStatusChanged(int value)
-{
- const camera_focusstate_t focusState = static_cast<camera_focusstate_t>(value);
-
- switch (focusState) {
- case CAMERA_FOCUSSTATE_NONE:
- m_focusLockStatus = QCamera::Unlocked;
- emit lockStatusChanged(QCamera::LockFocus, QCamera::Unlocked, QCamera::UserRequest);
- break;
- case CAMERA_FOCUSSTATE_WAITING:
- case CAMERA_FOCUSSTATE_SEARCHING:
- m_focusLockStatus = QCamera::Searching;
- emit lockStatusChanged(QCamera::LockFocus, QCamera::Searching, QCamera::UserRequest);
- break;
- case CAMERA_FOCUSSTATE_FAILED:
- m_focusLockStatus = QCamera::Unlocked;
- emit lockStatusChanged(QCamera::LockFocus, QCamera::Unlocked, QCamera::LockFailed);
- break;
- case CAMERA_FOCUSSTATE_LOCKED:
- m_focusLockStatus = QCamera::Locked;
- emit lockStatusChanged(QCamera::LockFocus, QCamera::Locked, QCamera::LockAcquired);
- break;
- case CAMERA_FOCUSSTATE_SCENECHANGE:
- m_focusLockStatus = QCamera::Unlocked;
- emit lockStatusChanged(QCamera::LockFocus, QCamera::Unlocked, QCamera::LockTemporaryLost);
- break;
- default:
- break;
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbcameralockscontrol.h b/src/plugins/qnx/camera/bbcameralockscontrol.h
deleted file mode 100644
index be3d1bd02..000000000
--- a/src/plugins/qnx/camera/bbcameralockscontrol.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/****************************************************************************
-**
-** 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 BBCAMERALOCKSCONTROL_H
-#define BBCAMERALOCKSCONTROL_H
-
-#include <qcameralockscontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraSession;
-
-class BbCameraLocksControl : public QCameraLocksControl
-{
- Q_OBJECT
-public:
- enum LocksApplyMode
- {
- IndependentMode,
- FocusExposureBoundMode,
- AllBoundMode,
- FocusOnlyMode
- };
-
- explicit BbCameraLocksControl(BbCameraSession *session, QObject *parent = 0);
-
- QCamera::LockTypes supportedLocks() const override;
- QCamera::LockStatus lockStatus(QCamera::LockType lock) const override;
- void searchAndLock(QCamera::LockTypes locks) override;
- void unlock(QCamera::LockTypes locks) override;
-
-private Q_SLOTS:
- void cameraOpened();
- void focusStatusChanged(int value);
-
-private:
- BbCameraSession *m_session;
-
- LocksApplyMode m_locksApplyMode;
- QCamera::LockStatus m_focusLockStatus;
- QCamera::LockStatus m_exposureLockStatus;
- QCamera::LockStatus m_whiteBalanceLockStatus;
- QCamera::LockTypes m_currentLockTypes;
- QCamera::LockTypes m_supportedLockTypes;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbcameramediarecordercontrol.cpp b/src/plugins/qnx/camera/bbcameramediarecordercontrol.cpp
deleted file mode 100644
index 3cb9ed38d..000000000
--- a/src/plugins/qnx/camera/bbcameramediarecordercontrol.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbcameramediarecordercontrol.h"
-
-#include "bbcamerasession.h"
-
-#include <QDebug>
-#include <QUrl>
-
-#include <audio/audio_manager_device.h>
-#include <audio/audio_manager_volume.h>
-
-QT_BEGIN_NAMESPACE
-
-static audio_manager_device_t currentAudioInputDevice()
-{
- audio_manager_device_t device = AUDIO_DEVICE_HEADSET;
-
- const int result = audio_manager_get_default_input_device(&device);
- if (result != EOK) {
- qWarning() << "Unable to retrieve default audio input device:" << result;
- return AUDIO_DEVICE_HEADSET;
- }
-
- return device;
-}
-
-BbCameraMediaRecorderControl::BbCameraMediaRecorderControl(BbCameraSession *session, QObject *parent)
- : QMediaRecorderControl(parent)
- , m_session(session)
-{
- connect(m_session, SIGNAL(videoStateChanged(QMediaRecorder::State)), this, SIGNAL(stateChanged(QMediaRecorder::State)));
- connect(m_session, SIGNAL(videoStatusChanged(QMediaRecorder::Status)), this, SIGNAL(statusChanged(QMediaRecorder::Status)));
- connect(m_session, SIGNAL(durationChanged(qint64)), this, SIGNAL(durationChanged(qint64)));
- connect(m_session, SIGNAL(actualLocationChanged(QUrl)), this, SIGNAL(actualLocationChanged(QUrl)));
- connect(m_session, SIGNAL(videoError(int,QString)), this, SIGNAL(error(int,QString)));
-}
-
-QUrl BbCameraMediaRecorderControl::outputLocation() const
-{
- return m_session->outputLocation();
-}
-
-bool BbCameraMediaRecorderControl::setOutputLocation(const QUrl &location)
-{
- return m_session->setOutputLocation(location);
-}
-
-QMediaRecorder::State BbCameraMediaRecorderControl::state() const
-{
- return m_session->videoState();
-}
-
-QMediaRecorder::Status BbCameraMediaRecorderControl::status() const
-{
- return m_session->videoStatus();
-}
-
-qint64 BbCameraMediaRecorderControl::duration() const
-{
- return m_session->duration();
-}
-
-bool BbCameraMediaRecorderControl::isMuted() const
-{
- bool muted = false;
-
- const int result = audio_manager_get_input_mute(currentAudioInputDevice(), &muted);
- if (result != EOK) {
- emit const_cast<BbCameraMediaRecorderControl*>(this)->error(QMediaRecorder::ResourceError, tr("Unable to retrieve mute status"));
- return false;
- }
-
- return muted;
-}
-
-qreal BbCameraMediaRecorderControl::volume() const
-{
- double level = 0.0;
-
- const int result = audio_manager_get_input_level(currentAudioInputDevice(), &level);
- if (result != EOK) {
- emit const_cast<BbCameraMediaRecorderControl*>(this)->error(QMediaRecorder::ResourceError, tr("Unable to retrieve audio input volume"));
- return 0.0;
- }
-
- return (level / 100);
-}
-
-void BbCameraMediaRecorderControl::applySettings()
-{
- m_session->applyVideoSettings();
-}
-
-void BbCameraMediaRecorderControl::setState(QMediaRecorder::State state)
-{
- m_session->setVideoState(state);
-}
-
-void BbCameraMediaRecorderControl::setMuted(bool muted)
-{
- const int result = audio_manager_set_input_mute(currentAudioInputDevice(), muted);
- if (result != EOK) {
- emit error(QMediaRecorder::ResourceError, tr("Unable to set mute status"));
- } else {
- emit mutedChanged(muted);
- }
-}
-
-void BbCameraMediaRecorderControl::setVolume(qreal volume)
-{
- const int result = audio_manager_set_input_level(currentAudioInputDevice(), (volume * 100));
- if (result != EOK) {
- emit error(QMediaRecorder::ResourceError, tr("Unable to set audio input volume"));
- } else {
- emit volumeChanged(volume);
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbcameramediarecordercontrol.h b/src/plugins/qnx/camera/bbcameramediarecordercontrol.h
deleted file mode 100644
index af46479a5..000000000
--- a/src/plugins/qnx/camera/bbcameramediarecordercontrol.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/****************************************************************************
-**
-** 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 BBCAMERAMEDIARECORDERCONTROL_H
-#define BBCAMERAMEDIARECORDERCONTROL_H
-
-#include <qmediarecordercontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraSession;
-
-class BbCameraMediaRecorderControl : public QMediaRecorderControl
-{
- Q_OBJECT
-public:
- explicit BbCameraMediaRecorderControl(BbCameraSession *session, QObject *parent = 0);
-
- QUrl outputLocation() const override;
- bool setOutputLocation(const QUrl &location) override;
- QMediaRecorder::State state() const override;
- QMediaRecorder::Status status() const override;
- qint64 duration() const override;
- bool isMuted() const override;
- qreal volume() const override;
- void applySettings() override;
-
-public Q_SLOTS:
- void setState(QMediaRecorder::State state) override;
- void setMuted(bool muted) override;
- void setVolume(qreal volume) override;
-
-private:
- BbCameraSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbcameraorientationhandler.cpp b/src/plugins/qnx/camera/bbcameraorientationhandler.cpp
deleted file mode 100644
index d600f3db0..000000000
--- a/src/plugins/qnx/camera/bbcameraorientationhandler.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbcameraorientationhandler.h"
-
-#include <QAbstractEventDispatcher>
-#include <QGuiApplication>
-#include <QScreen>
-#include <QDebug>
-
-#include <bps/orientation.h>
-
-QT_BEGIN_NAMESPACE
-
-BbCameraOrientationHandler::BbCameraOrientationHandler(QObject *parent)
- : QObject(parent)
- , m_orientation(0)
-{
- QCoreApplication::eventDispatcher()->installNativeEventFilter(this);
- int result = orientation_request_events(0);
- if (result == BPS_FAILURE)
- qWarning() << "Unable to register for orientation change events";
-
- orientation_direction_t direction = ORIENTATION_FACE_UP;
- int angle = 0;
-
- result = orientation_get(&direction, &angle);
- if (result == BPS_FAILURE) {
- qWarning() << "Unable to retrieve initial orientation";
- } else {
- m_orientation = angle;
- }
-}
-
-BbCameraOrientationHandler::~BbCameraOrientationHandler()
-{
- const int result = orientation_stop_events(0);
- if (result == BPS_FAILURE)
- qWarning() << "Unable to unregister for orientation change events";
-
- QCoreApplication::eventDispatcher()->removeNativeEventFilter(this);
-}
-
-bool BbCameraOrientationHandler::nativeEventFilter(const QByteArray&, void *message, qintptr *)
-{
- bps_event_t* const event = static_cast<bps_event_t*>(message);
- if (!event || bps_event_get_domain(event) != orientation_get_domain())
- return false;
-
- const int angle = orientation_event_get_angle(event);
- if (angle != m_orientation) {
- if (angle == 180) // The screen does not rotate at 180 degrees
- return false;
-
- m_orientation = angle;
- emit orientationChanged(m_orientation);
- }
-
- return false; // do not drop the event
-}
-
-int BbCameraOrientationHandler::viewfinderOrientation() const
-{
- // On a keyboard device we do not rotate the screen at all
- if (qGuiApp->primaryScreen()->nativeOrientation()
- != qGuiApp->primaryScreen()->primaryOrientation()) {
- return m_orientation;
- }
-
- return 0;
-}
-
-int BbCameraOrientationHandler::orientation() const
-{
- return m_orientation;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbcameraorientationhandler.h b/src/plugins/qnx/camera/bbcameraorientationhandler.h
deleted file mode 100644
index af80bd4e1..000000000
--- a/src/plugins/qnx/camera/bbcameraorientationhandler.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/****************************************************************************
-**
-** 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 BBCAMERAORIENTATIONHANDLER_H
-#define BBCAMERAORIENTATIONHANDLER_H
-
-#include <QAbstractNativeEventFilter>
-#include <QObject>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraOrientationHandler : public QObject, public QAbstractNativeEventFilter
-{
- Q_OBJECT
-public:
- explicit BbCameraOrientationHandler(QObject *parent = 0);
- ~BbCameraOrientationHandler();
-
- bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
-
- int orientation() const;
-
- int viewfinderOrientation() const;
-
-Q_SIGNALS:
- void orientationChanged(int degree);
-
-private:
- int m_orientation;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbcameraservice.cpp b/src/plugins/qnx/camera/bbcameraservice.cpp
deleted file mode 100644
index 2879f6981..000000000
--- a/src/plugins/qnx/camera/bbcameraservice.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbcameraservice.h"
-
-#include "bbcameraaudioencodersettingscontrol.h"
-#include "bbcameracapturebufferformatcontrol.h"
-#include "bbcameracapturedestinationcontrol.h"
-#include "bbcameracontrol.h"
-#include "bbcameraexposurecontrol.h"
-#include "bbcameraflashcontrol.h"
-#include "bbcamerafocuscontrol.h"
-#include "bbcameraimagecapturecontrol.h"
-#include "bbcameraimageprocessingcontrol.h"
-#include "bbcamerainfocontrol.h"
-#include "bbcameralockscontrol.h"
-#include "bbcameramediarecordercontrol.h"
-#include "bbcamerasession.h"
-#include "bbcameravideoencodersettingscontrol.h"
-#include "bbcameraviewfindersettingscontrol.h"
-#include "bbcamerazoomcontrol.h"
-#include "bbimageencodercontrol.h"
-#include "bbvideodeviceselectorcontrol.h"
-#include "bbvideorenderercontrol.h"
-
-#include <QDebug>
-#include <QVariant>
-
-QT_BEGIN_NAMESPACE
-
-BbCameraService::BbCameraService(QObject *parent)
- : QMediaService(parent)
- , m_cameraSession(new BbCameraSession(this))
- , m_cameraAudioEncoderSettingsControl(new BbCameraAudioEncoderSettingsControl(m_cameraSession, this))
- , m_cameraCaptureBufferFormatControl(new BbCameraCaptureBufferFormatControl(this))
- , m_cameraCaptureDestinationControl(new BbCameraCaptureDestinationControl(m_cameraSession, this))
- , m_cameraControl(new BbCameraControl(m_cameraSession, this))
- , m_cameraExposureControl(new BbCameraExposureControl(m_cameraSession, this))
- , m_cameraFlashControl(new BbCameraFlashControl(m_cameraSession, this))
- , m_cameraFocusControl(new BbCameraFocusControl(m_cameraSession, this))
- , m_cameraImageCaptureControl(new BbCameraImageCaptureControl(m_cameraSession, this))
- , m_cameraImageProcessingControl(new BbCameraImageProcessingControl(m_cameraSession, this))
- , m_cameraInfoControl(new BbCameraInfoControl(this))
- , m_cameraLocksControl(new BbCameraLocksControl(m_cameraSession, this))
- , m_cameraMediaRecorderControl(new BbCameraMediaRecorderControl(m_cameraSession, this))
- , m_cameraVideoEncoderSettingsControl(new BbCameraVideoEncoderSettingsControl(m_cameraSession, this))
- , m_cameraViewfinderSettingsControl(new BbCameraViewfinderSettingsControl(m_cameraSession, this))
- , m_cameraZoomControl(new BbCameraZoomControl(m_cameraSession, this))
- , m_imageEncoderControl(new BbImageEncoderControl(m_cameraSession, this))
- , m_videoDeviceSelectorControl(new BbVideoDeviceSelectorControl(m_cameraSession, this))
- , m_videoRendererControl(new BbVideoRendererControl(m_cameraSession, this))
-{
-}
-
-BbCameraService::~BbCameraService()
-{
-}
-
-QMediaControl* BbCameraService::requestControl(const char *name)
-{
- if (qstrcmp(name, QAudioEncoderSettingsControl_iid) == 0)
- return m_cameraAudioEncoderSettingsControl;
- else if (qstrcmp(name, QCameraCaptureBufferFormatControl_iid) == 0)
- return m_cameraCaptureBufferFormatControl;
- else if (qstrcmp(name, QCameraCaptureDestinationControl_iid) == 0)
- return m_cameraCaptureDestinationControl;
- else if (qstrcmp(name, QCameraControl_iid) == 0)
- return m_cameraControl;
- else if (qstrcmp(name, QCameraInfoControl_iid) == 0)
- return m_cameraInfoControl;
- else if (qstrcmp(name, QCameraExposureControl_iid) == 0)
- return m_cameraExposureControl;
- else if (qstrcmp(name, QCameraFlashControl_iid) == 0)
- return m_cameraFlashControl;
- else if (qstrcmp(name, QCameraFocusControl_iid) == 0)
- return m_cameraFocusControl;
- else if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0)
- return m_cameraImageCaptureControl;
- else if (qstrcmp(name, QCameraImageProcessingControl_iid) == 0)
- return m_cameraImageProcessingControl;
- else if (qstrcmp(name, QCameraLocksControl_iid) == 0)
- return m_cameraLocksControl;
- else if (qstrcmp(name, QMediaRecorderControl_iid) == 0)
- return m_cameraMediaRecorderControl;
- else if (qstrcmp(name, QVideoEncoderSettingsControl_iid) == 0)
- return m_cameraVideoEncoderSettingsControl;
- else if (qstrcmp(name, QCameraViewfinderSettingsControl_iid) == 0)
- return m_cameraViewfinderSettingsControl;
- else if (qstrcmp(name, QCameraZoomControl_iid) == 0)
- return m_cameraZoomControl;
- else if (qstrcmp(name, QImageEncoderControl_iid) == 0)
- return m_imageEncoderControl;
- else if (qstrcmp(name, QVideoDeviceSelectorControl_iid) == 0)
- return m_videoDeviceSelectorControl;
- else if (qstrcmp(name, QVideoRendererControl_iid) == 0)
- return m_videoRendererControl;
-
- return 0;
-}
-
-void BbCameraService::releaseControl(QMediaControl *control)
-{
- Q_UNUSED(control);
-
- // Implemented as a singleton, so we do nothing.
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbcameraservice.h b/src/plugins/qnx/camera/bbcameraservice.h
deleted file mode 100644
index b13da6a4a..000000000
--- a/src/plugins/qnx/camera/bbcameraservice.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/****************************************************************************
-**
-** 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 BBCAMERASERVICE_H
-#define BBCAMERASERVICE_H
-
-#include <QObject>
-
-#include <qmediaservice.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraAudioEncoderSettingsControl;
-class BbCameraCaptureBufferFormatControl;
-class BbCameraCaptureDestinationControl;
-class BbCameraControl;
-class BbCameraExposureControl;
-class BbCameraFlashControl;
-class BbCameraFocusControl;
-class BbCameraImageCaptureControl;
-class BbCameraImageProcessingControl;
-class BbCameraInfoControl;
-class BbCameraLocksControl;
-class BbCameraMediaRecorderControl;
-class BbCameraSession;
-class BbCameraVideoEncoderSettingsControl;
-class BbCameraViewfinderSettingsControl;
-class BbCameraZoomControl;
-class BbImageEncoderControl;
-class BbVideoDeviceSelectorControl;
-class BbVideoRendererControl;
-
-class BbCameraService : public QMediaService
-{
- Q_OBJECT
-
-public:
- explicit BbCameraService(QObject *parent = 0);
- ~BbCameraService();
-
- virtual QMediaControl* requestControl(const char *name);
- virtual void releaseControl(QMediaControl *control);
-
-private:
- BbCameraSession* m_cameraSession;
-
- BbCameraAudioEncoderSettingsControl* m_cameraAudioEncoderSettingsControl;
- BbCameraCaptureBufferFormatControl* m_cameraCaptureBufferFormatControl;
- BbCameraCaptureDestinationControl* m_cameraCaptureDestinationControl;
- BbCameraControl* m_cameraControl;
- BbCameraExposureControl* m_cameraExposureControl;
- BbCameraFlashControl* m_cameraFlashControl;
- BbCameraFocusControl* m_cameraFocusControl;
- BbCameraImageCaptureControl* m_cameraImageCaptureControl;
- BbCameraImageProcessingControl* m_cameraImageProcessingControl;
- BbCameraInfoControl* m_cameraInfoControl;
- BbCameraLocksControl* m_cameraLocksControl;
- BbCameraMediaRecorderControl* m_cameraMediaRecorderControl;
- BbCameraVideoEncoderSettingsControl* m_cameraVideoEncoderSettingsControl;
- BbCameraViewfinderSettingsControl* m_cameraViewfinderSettingsControl;
- BbCameraZoomControl* m_cameraZoomControl;
- BbImageEncoderControl* m_imageEncoderControl;
- BbVideoDeviceSelectorControl* m_videoDeviceSelectorControl;
- BbVideoRendererControl* m_videoRendererControl;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbcamerasession.cpp b/src/plugins/qnx/camera/bbcamerasession.cpp
deleted file mode 100644
index 7b3974084..000000000
--- a/src/plugins/qnx/camera/bbcamerasession.cpp
+++ /dev/null
@@ -1,1154 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbcamerasession.h"
-
-#include "bbcameraorientationhandler.h"
-#include "bbcameraviewfindersettingscontrol.h"
-#include "windowgrabber.h"
-
-#include <QAbstractVideoSurface>
-#include <QBuffer>
-#include <QDebug>
-#include <QImage>
-#include <QUrl>
-#include <QVideoSurfaceFormat>
-#include <qmath.h>
-
-#include <algorithm>
-
-QT_BEGIN_NAMESPACE
-
-static QString errorToString(camera_error_t error)
-{
- switch (error) {
- case CAMERA_EOK:
- return QLatin1String("No error");
- case CAMERA_EAGAIN:
- return QLatin1String("Camera unavailable");
- case CAMERA_EINVAL:
- return QLatin1String("Invalid argument");
- case CAMERA_ENODEV:
- return QLatin1String("Camera not found");
- case CAMERA_EMFILE:
- return QLatin1String("File table overflow");
- case CAMERA_EBADF:
- return QLatin1String("Invalid handle passed");
- case CAMERA_EACCESS:
- return QLatin1String("No permission");
- case CAMERA_EBADR:
- return QLatin1String("Invalid file descriptor");
- case CAMERA_ENOENT:
- return QLatin1String("File or directory does not exists");
- case CAMERA_ENOMEM:
- return QLatin1String("Memory allocation failed");
- case CAMERA_EOPNOTSUPP:
- return QLatin1String("Operation not supported");
- case CAMERA_ETIMEDOUT:
- return QLatin1String("Communication timeout");
- case CAMERA_EALREADY:
- return QLatin1String("Operation already in progress");
- case CAMERA_EUNINIT:
- return QLatin1String("Camera library not initialized");
- case CAMERA_EREGFAULT:
- return QLatin1String("Callback registration failed");
- case CAMERA_EMICINUSE:
- return QLatin1String("Microphone in use already");
- case CAMERA_ENODATA:
- return QLatin1String("Data does not exist");
- case CAMERA_EBUSY:
- return QLatin1String("Camera busy");
- case CAMERA_EDESKTOPCAMERAINUSE:
- return QLatin1String("Desktop camera in use already");
- case CAMERA_ENOSPC:
- return QLatin1String("Disk is full");
- case CAMERA_EPOWERDOWN:
- return QLatin1String("Camera in power down state");
- case CAMERA_3ALOCKED:
- return QLatin1String("3A have been locked");
-// case CAMERA_EVIEWFINDERFROZEN: // not yet available in 10.2 NDK
-// return QLatin1String("Freeze flag set");
- default:
- return QLatin1String("Unknown error");
- }
-}
-
-QDebug operator<<(QDebug debug, camera_error_t error)
-{
- debug.nospace() << errorToString(error);
- return debug.space();
-}
-
-BbCameraSession::BbCameraSession(QObject *parent)
- : QObject(parent)
- , m_nativeCameraOrientation(0)
- , m_orientationHandler(new BbCameraOrientationHandler(this))
- , m_status(QCamera::UnloadedStatus)
- , m_state(QCamera::UnloadedState)
- , m_captureMode(QCamera::CaptureStillImage)
- , m_device("bb:RearCamera")
- , m_previewIsVideo(true)
- , m_surface(0)
- , m_captureImageDriveMode(QCameraImageCapture::SingleImageCapture)
- , m_lastImageCaptureId(0)
- , m_captureDestination(QCameraImageCapture::CaptureToFile)
- , m_videoState(QMediaRecorder::StoppedState)
- , m_videoStatus(QMediaRecorder::LoadedStatus)
- , m_handle(CAMERA_HANDLE_INVALID)
- , m_windowGrabber(new WindowGrabber(this))
-{
- connect(this, SIGNAL(statusChanged(QCamera::Status)), SLOT(updateReadyForCapture()));
- connect(this, SIGNAL(captureModeChanged(QCamera::CaptureModes)), SLOT(updateReadyForCapture()));
- connect(m_orientationHandler, SIGNAL(orientationChanged(int)), SLOT(deviceOrientationChanged(int)));
-
- connect(m_windowGrabber, SIGNAL(frameGrabbed(QImage, int)), SLOT(viewfinderFrameGrabbed(QImage)));
-}
-
-BbCameraSession::~BbCameraSession()
-{
- stopViewFinder();
- closeCamera();
-}
-
-camera_handle_t BbCameraSession::handle() const
-{
- return m_handle;
-}
-
-QCamera::State BbCameraSession::state() const
-{
- return m_state;
-}
-
-void BbCameraSession::setState(QCamera::State state)
-{
- if (m_state == state)
- return;
-
- const QCamera::State previousState = m_state;
-
- if (previousState == QCamera::UnloadedState) {
- if (state == QCamera::LoadedState) {
- if (openCamera()) {
- m_state = state;
- }
- } else if (state == QCamera::ActiveState) {
- if (openCamera()) {
- QMetaObject::invokeMethod(this, "applyConfiguration", Qt::QueuedConnection);
- m_state = state;
- }
- }
- } else if (previousState == QCamera::LoadedState) {
- if (state == QCamera::UnloadedState) {
- closeCamera();
- m_state = state;
- } else if (state == QCamera::ActiveState) {
- QMetaObject::invokeMethod(this, "applyConfiguration", Qt::QueuedConnection);
- m_state = state;
- }
- } else if (previousState == QCamera::ActiveState) {
- if (state == QCamera::LoadedState) {
- stopViewFinder();
- m_state = state;
- } else if (state == QCamera::UnloadedState) {
- stopViewFinder();
- closeCamera();
- m_state = state;
- }
- }
-
- if (m_state != previousState)
- emit stateChanged(m_state);
-}
-
-QCamera::Status BbCameraSession::status() const
-{
- return m_status;
-}
-
-QCamera::CaptureModes BbCameraSession::captureMode() const
-{
- return m_captureMode;
-}
-
-void BbCameraSession::setCaptureMode(QCamera::CaptureModes captureMode)
-{
- if (m_captureMode == captureMode)
- return;
-
- m_captureMode = captureMode;
- emit captureModeChanged(m_captureMode);
-}
-
-bool BbCameraSession::isCaptureModeSupported(QCamera::CaptureModes mode) const
-{
- if (m_handle == CAMERA_HANDLE_INVALID) {
- // the camera has not been loaded yet via QCamera::load(), so
- // we open it temporarily to peek for the supported capture modes
-
- camera_unit_t unit = CAMERA_UNIT_REAR;
- if (m_device == cameraIdentifierFront())
- unit = CAMERA_UNIT_FRONT;
- else if (m_device == cameraIdentifierRear())
- unit = CAMERA_UNIT_REAR;
- else if (m_device == cameraIdentifierDesktop())
- unit = CAMERA_UNIT_DESKTOP;
-
- camera_handle_t handle;
- const camera_error_t result = camera_open(unit, CAMERA_MODE_RW, &handle);
- if (result != CAMERA_EOK)
- return true;
-
- const bool supported = isCaptureModeSupported(handle, mode);
-
- camera_close(handle);
-
- return supported;
- } else {
- return isCaptureModeSupported(m_handle, mode);
- }
-}
-
-QByteArray BbCameraSession::cameraIdentifierFront()
-{
- return "bb:FrontCamera";
-}
-
-QByteArray BbCameraSession::cameraIdentifierRear()
-{
- return "bb:RearCamera";
-}
-
-QByteArray BbCameraSession::cameraIdentifierDesktop()
-{
- return "bb:DesktopCamera";
-}
-
-void BbCameraSession::setDevice(const QByteArray &device)
-{
- m_device = device;
-}
-
-QByteArray BbCameraSession::device() const
-{
- return m_device;
-}
-
-QAbstractVideoSurface* BbCameraSession::surface() const
-{
- return m_surface;
-}
-
-void BbCameraSession::setSurface(QAbstractVideoSurface *surface)
-{
- QMutexLocker locker(&m_surfaceMutex);
-
- if (m_surface == surface)
- return;
-
- m_surface = surface;
-}
-
-bool BbCameraSession::isReadyForCapture() const
-{
- if (m_captureMode & QCamera::CaptureStillImage)
- return (m_status == QCamera::ActiveStatus);
-
- if (m_captureMode & QCamera::CaptureVideo)
- return (m_status == QCamera::ActiveStatus);
-
- return false;
-}
-
-QCameraImageCapture::DriveMode BbCameraSession::driveMode() const
-{
- return m_captureImageDriveMode;
-}
-
-void BbCameraSession::setDriveMode(QCameraImageCapture::DriveMode mode)
-{
- m_captureImageDriveMode = mode;
-}
-
-/**
- * A helper structure that keeps context data for image capture callbacks.
- */
-struct ImageCaptureData
-{
- int requestId;
- QString fileName;
- BbCameraSession *session;
-};
-
-static void imageCaptureShutterCallback(camera_handle_t handle, void *context)
-{
- Q_UNUSED(handle);
-
- const ImageCaptureData *data = static_cast<ImageCaptureData*>(context);
-
- // We are inside a worker thread here, so emit imageExposed inside the main thread
- QMetaObject::invokeMethod(data->session, "imageExposed", Qt::QueuedConnection,
- Q_ARG(int, data->requestId));
-}
-
-static void imageCaptureImageCallback(camera_handle_t handle, camera_buffer_t *buffer, void *context)
-{
- Q_UNUSED(handle);
-
- QScopedPointer<ImageCaptureData> data(static_cast<ImageCaptureData*>(context));
-
- if (buffer->frametype != CAMERA_FRAMETYPE_JPEG) {
- // We are inside a worker thread here, so emit error signal inside the main thread
- QMetaObject::invokeMethod(data->session, "imageCaptureError", Qt::QueuedConnection,
- Q_ARG(int, data->requestId),
- Q_ARG(QCameraImageCapture::Error, QCameraImageCapture::FormatError),
- Q_ARG(QString, BbCameraSession::tr("Camera provides image in unsupported format")));
- return;
- }
-
- const QByteArray rawData((const char*)buffer->framebuf, buffer->framedesc.jpeg.bufsize);
-
- QImage image;
- const bool ok = image.loadFromData(rawData, "JPG");
- if (!ok) {
- const QString errorMessage = BbCameraSession::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->session, "imageCaptureError", Qt::QueuedConnection,
- Q_ARG(int, data->requestId),
- Q_ARG(QCameraImageCapture::Error, QCameraImageCapture::FormatError),
- Q_ARG(QString, errorMessage));
- return;
- }
-
-
- // We are inside a worker thread here, so invoke imageCaptured inside the main thread
- QMetaObject::invokeMethod(data->session, "imageCaptured", Qt::QueuedConnection,
- Q_ARG(int, data->requestId),
- Q_ARG(QImage, image),
- Q_ARG(QString, data->fileName));
-}
-
-int BbCameraSession::capture(const QString &fileName)
-{
- m_lastImageCaptureId++;
-
- if (!isReadyForCapture()) {
- emit imageCaptureError(m_lastImageCaptureId, QCameraImageCapture::NotReadyError, tr("Camera not ready"));
- return m_lastImageCaptureId;
- }
-
- if (m_captureImageDriveMode == QCameraImageCapture::SingleImageCapture) {
- // prepare context object for callback
- ImageCaptureData *context = new ImageCaptureData;
- context->requestId = m_lastImageCaptureId;
- context->fileName = fileName;
- context->session = this;
-
- const camera_error_t result = camera_take_photo(m_handle,
- imageCaptureShutterCallback,
- 0,
- 0,
- imageCaptureImageCallback,
- context, false);
-
- if (result != CAMERA_EOK)
- qWarning() << "Unable to take photo:" << result;
- } else {
- // TODO: implement burst mode when available in Qt API
- }
-
- return m_lastImageCaptureId;
-}
-
-void BbCameraSession::cancelCapture()
-{
- // BB10 API doesn't provide functionality for that
-}
-
-bool BbCameraSession::isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const
-{
- // capture to buffer, file and both are supported.
- return destination & (QCameraImageCapture::CaptureToFile | QCameraImageCapture::CaptureToBuffer);
-}
-
-QCameraImageCapture::CaptureDestinations BbCameraSession::captureDestination() const
-{
- return m_captureDestination;
-}
-
-void BbCameraSession::setCaptureDestination(QCameraImageCapture::CaptureDestinations destination)
-{
- if (m_captureDestination != destination) {
- m_captureDestination = destination;
- emit captureDestinationChanged(m_captureDestination);
- }
-}
-
-QList<QSize> BbCameraSession::supportedResolutions(const QImageEncoderSettings&, bool *continuous) const
-{
- if (continuous)
- *continuous = false;
-
- if (m_status == QCamera::UnloadedStatus)
- return QList<QSize>();
-
- if (m_captureMode & QCamera::CaptureStillImage) {
- return supportedResolutions(QCamera::CaptureStillImage);
- } else if (m_captureMode & QCamera::CaptureVideo) {
- return supportedResolutions(QCamera::CaptureVideo);
- }
-
- return QList<QSize>();
-}
-
-QImageEncoderSettings BbCameraSession::imageSettings() const
-{
- return m_imageEncoderSettings;
-}
-
-void BbCameraSession::setImageSettings(const QImageEncoderSettings &settings)
-{
- m_imageEncoderSettings = settings;
- if (m_imageEncoderSettings.codec().isEmpty())
- m_imageEncoderSettings.setCodec(QLatin1String("jpeg"));
-}
-
-QUrl BbCameraSession::outputLocation() const
-{
- return QUrl::fromLocalFile(m_videoOutputLocation);
-}
-
-bool BbCameraSession::setOutputLocation(const QUrl &location)
-{
- m_videoOutputLocation = location.toLocalFile();
-
- return true;
-}
-
-QMediaRecorder::State BbCameraSession::videoState() const
-{
- return m_videoState;
-}
-
-void BbCameraSession::setVideoState(QMediaRecorder::State state)
-{
- if (m_videoState == state)
- return;
-
- const QMediaRecorder::State previousState = m_videoState;
-
- if (previousState == QMediaRecorder::StoppedState) {
- if (state == QMediaRecorder::RecordingState) {
- if (startVideoRecording()) {
- m_videoState = state;
- }
- } else if (state == QMediaRecorder::PausedState) {
- // do nothing
- }
- } else if (previousState == QMediaRecorder::RecordingState) {
- if (state == QMediaRecorder::StoppedState) {
- stopVideoRecording();
- m_videoState = state;
- } else if (state == QMediaRecorder::PausedState) {
- //TODO: (pause) not supported by BB10 API yet
- }
- } else if (previousState == QMediaRecorder::PausedState) {
- if (state == QMediaRecorder::StoppedState) {
- stopVideoRecording();
- m_videoState = state;
- } else if (state == QMediaRecorder::RecordingState) {
- //TODO: (resume) not supported by BB10 API yet
- }
- }
-
- emit videoStateChanged(m_videoState);
-}
-
-QMediaRecorder::Status BbCameraSession::videoStatus() const
-{
- return m_videoStatus;
-}
-
-qint64 BbCameraSession::duration() const
-{
- return (m_videoRecordingDuration.isValid() ? m_videoRecordingDuration.elapsed() : 0);
-}
-
-void BbCameraSession::applyVideoSettings()
-{
- if (m_handle == CAMERA_HANDLE_INVALID)
- return;
-
- // apply viewfinder configuration
- const QList<QSize> videoOutputResolutions = supportedResolutions(QCamera::CaptureVideo);
-
- if (!m_videoEncoderSettings.resolution().isValid() || !videoOutputResolutions.contains(m_videoEncoderSettings.resolution()))
- m_videoEncoderSettings.setResolution(videoOutputResolutions.first());
-
- QSize viewfinderResolution;
-
- if (m_previewIsVideo) {
- // The viewfinder is responsible for encoding the video frames, so the resolutions must match.
- viewfinderResolution = m_videoEncoderSettings.resolution();
- } else {
- // The frames are encoded separately from the viewfinder, so only the aspect ratio must match.
- const QSize videoResolution = m_videoEncoderSettings.resolution();
- const qreal aspectRatio = static_cast<qreal>(videoResolution.width())/static_cast<qreal>(videoResolution.height());
-
- QList<QSize> sizes = supportedViewfinderResolutions(QCamera::CaptureVideo);
- std::reverse(sizes.begin(), sizes.end()); // use smallest possible resolution
- for (const QSize &size : qAsConst(sizes)) {
- // search for viewfinder resolution with the same aspect ratio
- if (qFuzzyCompare(aspectRatio, (static_cast<qreal>(size.width())/static_cast<qreal>(size.height())))) {
- viewfinderResolution = size;
- break;
- }
- }
- }
-
- Q_ASSERT(viewfinderResolution.isValid());
-
- const QByteArray windowId = QString().sprintf("qcamera_vf_%p", this).toLatin1();
- m_windowGrabber->setWindowId(windowId);
-
- const QByteArray windowGroupId = m_windowGrabber->windowGroupId();
-
- const int rotationAngle = (360 - m_nativeCameraOrientation);
-
- camera_error_t result = CAMERA_EOK;
- result = camera_set_videovf_property(m_handle,
- CAMERA_IMGPROP_WIN_GROUPID, windowGroupId.data(),
- CAMERA_IMGPROP_WIN_ID, windowId.data(),
- CAMERA_IMGPROP_WIDTH, viewfinderResolution.width(),
- CAMERA_IMGPROP_HEIGHT, viewfinderResolution.height(),
- CAMERA_IMGPROP_ROTATION, rotationAngle);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to apply video viewfinder settings:" << result;
- return;
- }
-
- const QSize resolution = m_videoEncoderSettings.resolution();
-
- QString videoCodec = m_videoEncoderSettings.codec();
- if (videoCodec.isEmpty())
- videoCodec = QLatin1String("h264");
-
- camera_videocodec_t cameraVideoCodec = CAMERA_VIDEOCODEC_H264;
- if (videoCodec == QLatin1String("none"))
- cameraVideoCodec = CAMERA_VIDEOCODEC_NONE;
- else if (videoCodec == QLatin1String("avc1"))
- cameraVideoCodec = CAMERA_VIDEOCODEC_AVC1;
- else if (videoCodec == QLatin1String("h264"))
- cameraVideoCodec = CAMERA_VIDEOCODEC_H264;
-
- qreal frameRate = m_videoEncoderSettings.frameRate();
- if (frameRate == 0) {
- const QList<qreal> frameRates = supportedFrameRates(QVideoEncoderSettings(), 0);
- if (!frameRates.isEmpty())
- frameRate = frameRates.last();
- }
-
- QString audioCodec = m_audioEncoderSettings.codec();
- if (audioCodec.isEmpty())
- audioCodec = QLatin1String("aac");
-
- camera_audiocodec_t cameraAudioCodec = CAMERA_AUDIOCODEC_AAC;
- if (audioCodec == QLatin1String("none"))
- cameraAudioCodec = CAMERA_AUDIOCODEC_NONE;
- else if (audioCodec == QLatin1String("aac"))
- cameraAudioCodec = CAMERA_AUDIOCODEC_AAC;
- else if (audioCodec == QLatin1String("raw"))
- cameraAudioCodec = CAMERA_AUDIOCODEC_RAW;
-
- result = camera_set_video_property(m_handle,
- CAMERA_IMGPROP_WIDTH, resolution.width(),
- CAMERA_IMGPROP_HEIGHT, resolution.height(),
- CAMERA_IMGPROP_ROTATION, rotationAngle,
- CAMERA_IMGPROP_VIDEOCODEC, cameraVideoCodec,
- CAMERA_IMGPROP_AUDIOCODEC, cameraAudioCodec);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to apply video settings:" << result;
- emit videoError(QMediaRecorder::ResourceError, tr("Unable to apply video settings"));
- }
-}
-
-QList<QSize> BbCameraSession::supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous) const
-{
- Q_UNUSED(settings);
-
- if (continuous)
- *continuous = false;
-
- return supportedResolutions(QCamera::CaptureVideo);
-}
-
-QList<qreal> BbCameraSession::supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous) const
-{
- Q_UNUSED(settings);
-
- if (m_handle == CAMERA_HANDLE_INVALID)
- return QList<qreal>();
-
- int supported = 0;
- double rates[20];
- bool maxmin = false;
-
- /**
- * Since in current version of the BB10 platform the video viewfinder encodes the video frames, we use
- * the values as returned by camera_get_video_vf_framerates().
- */
- const camera_error_t result = camera_get_video_vf_framerates(m_handle, 20, &supported, rates, &maxmin);
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to retrieve supported viewfinder framerates:" << result;
- return QList<qreal>();
- }
-
- QList<qreal> frameRates;
- for (int i = 0; i < supported; ++i)
- frameRates << rates[i];
-
- if (continuous)
- *continuous = maxmin;
-
- return frameRates;
-}
-
-QVideoEncoderSettings BbCameraSession::videoSettings() const
-{
- return m_videoEncoderSettings;
-}
-
-void BbCameraSession::setVideoSettings(const QVideoEncoderSettings &settings)
-{
- m_videoEncoderSettings = settings;
-}
-
-QAudioEncoderSettings BbCameraSession::audioSettings() const
-{
- return m_audioEncoderSettings;
-}
-
-void BbCameraSession::setAudioSettings(const QAudioEncoderSettings &settings)
-{
- m_audioEncoderSettings = settings;
-}
-
-void BbCameraSession::updateReadyForCapture()
-{
- emit readyForCaptureChanged(isReadyForCapture());
-}
-
-void BbCameraSession::imageCaptured(int requestId, const QImage &rawImage, const QString &fileName)
-{
- QTransform transform;
-
- // subtract out the native rotation
- transform.rotate(m_nativeCameraOrientation);
-
- // subtract out the current device orientation
- if (m_device == cameraIdentifierRear())
- transform.rotate(360 - m_orientationHandler->orientation());
- else
- transform.rotate(m_orientationHandler->orientation());
-
- const QImage image = rawImage.transformed(transform);
-
- // Generate snap preview as downscaled image
- {
- QSize previewSize = image.size();
- int downScaleSteps = 0;
- while (previewSize.width() > 800 && downScaleSteps < 8) {
- previewSize.rwidth() /= 2;
- previewSize.rheight() /= 2;
- downScaleSteps++;
- }
-
- const QImage snapPreview = image.scaled(previewSize);
-
- emit imageCaptured(requestId, snapPreview);
- }
-
- if (m_captureDestination & QCameraImageCapture::CaptureToBuffer) {
- QVideoFrame frame(image);
-
- emit imageAvailable(requestId, frame);
- }
-
- if (m_captureDestination & QCameraImageCapture::CaptureToFile) {
- const QString actualFileName = m_mediaStorageLocation.generateFileName(fileName,
- QCamera::CaptureStillImage,
- QLatin1String("IMG_"),
- QLatin1String("jpg"));
-
- QFile file(actualFileName);
- if (file.open(QFile::WriteOnly)) {
- if (image.save(&file, "JPG")) {
- emit imageSaved(requestId, actualFileName);
- } else {
- emit imageCaptureError(requestId, QCameraImageCapture::OutOfSpaceError, file.errorString());
- }
- } else {
- const QString errorMessage = tr("Could not open destination file:\n%1").arg(actualFileName);
- emit imageCaptureError(requestId, QCameraImageCapture::ResourceError, errorMessage);
- }
- }
-}
-
-void BbCameraSession::handleVideoRecordingPaused()
-{
- //TODO: implement once BB10 API supports pausing a video
-}
-
-void BbCameraSession::handleVideoRecordingResumed()
-{
- if (m_videoStatus == QMediaRecorder::StartingStatus) {
- m_videoStatus = QMediaRecorder::RecordingStatus;
- emit videoStatusChanged(m_videoStatus);
-
- m_videoRecordingDuration.restart();
- }
-}
-
-void BbCameraSession::deviceOrientationChanged(int angle)
-{
- if (m_handle != CAMERA_HANDLE_INVALID)
- camera_set_device_orientation(m_handle, angle);
-}
-
-void BbCameraSession::handleCameraPowerUp()
-{
- stopViewFinder();
- startViewFinder();
-}
-
-void BbCameraSession::viewfinderFrameGrabbed(const QImage &image)
-{
- QTransform transform;
-
- // subtract out the native rotation
- transform.rotate(m_nativeCameraOrientation);
-
- // subtract out the current device orientation
- if (m_device == cameraIdentifierRear())
- transform.rotate(360 - m_orientationHandler->viewfinderOrientation());
- else
- transform.rotate(m_orientationHandler->viewfinderOrientation());
-
- QImage frame = image.copy().transformed(transform);
-
- QMutexLocker locker(&m_surfaceMutex);
- if (m_surface) {
- if (frame.size() != m_surface->surfaceFormat().frameSize()) {
- m_surface->stop();
- m_surface->start(QVideoSurfaceFormat(frame.size(), QVideoFrame::Format_ARGB32));
- }
-
- QVideoFrame videoFrame(frame);
-
- m_surface->present(videoFrame);
- }
-}
-
-bool BbCameraSession::openCamera()
-{
- if (m_handle != CAMERA_HANDLE_INVALID) // camera is already open
- return true;
-
- m_status = QCamera::LoadingStatus;
- emit statusChanged(m_status);
-
- camera_unit_t unit = CAMERA_UNIT_REAR;
- if (m_device == cameraIdentifierFront())
- unit = CAMERA_UNIT_FRONT;
- else if (m_device == cameraIdentifierRear())
- unit = CAMERA_UNIT_REAR;
- else if (m_device == cameraIdentifierDesktop())
- unit = CAMERA_UNIT_DESKTOP;
-
- camera_error_t result = camera_open(unit, CAMERA_MODE_RW, &m_handle);
- if (result != CAMERA_EOK) {
- m_handle = CAMERA_HANDLE_INVALID;
- m_status = QCamera::UnloadedStatus;
- emit statusChanged(m_status);
-
- qWarning() << "Unable to open camera:" << result;
- emit error(QCamera::CameraError, tr("Unable to open camera"));
- return false;
- }
-
- result = camera_get_native_orientation(m_handle, &m_nativeCameraOrientation);
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to retrieve native camera orientation:" << result;
- emit error(QCamera::CameraError, tr("Unable to retrieve native camera orientation"));
- return false;
- }
-
- m_previewIsVideo = camera_has_feature(m_handle, CAMERA_FEATURE_PREVIEWISVIDEO);
-
- m_status = QCamera::LoadedStatus;
- emit statusChanged(m_status);
-
- emit cameraOpened();
-
- return true;
-}
-
-void BbCameraSession::closeCamera()
-{
- if (m_handle == CAMERA_HANDLE_INVALID) // camera is closed already
- return;
-
- m_status = QCamera::UnloadingStatus;
- emit statusChanged(m_status);
-
- const camera_error_t result = camera_close(m_handle);
- if (result != CAMERA_EOK) {
- m_status = QCamera::LoadedStatus;
- emit statusChanged(m_status);
-
- qWarning() << "Unable to close camera:" << result;
- emit error(QCamera::CameraError, tr("Unable to close camera"));
- return;
- }
-
- m_handle = CAMERA_HANDLE_INVALID;
-
- m_status = QCamera::UnloadedStatus;
- emit statusChanged(m_status);
-}
-
-static void viewFinderStatusCallback(camera_handle_t handle, camera_devstatus_t status, uint16_t value, void *context)
-{
- Q_UNUSED(handle);
-
- if (status == CAMERA_STATUS_FOCUS_CHANGE) {
- BbCameraSession *session = static_cast<BbCameraSession*>(context);
- QMetaObject::invokeMethod(session, "focusStatusChanged", Qt::QueuedConnection, Q_ARG(int, value));
- return;
- } else if (status == CAMERA_STATUS_POWERUP) {
- BbCameraSession *session = static_cast<BbCameraSession*>(context);
- QMetaObject::invokeMethod(session, "handleCameraPowerUp", Qt::QueuedConnection);
- }
-}
-
-bool BbCameraSession::startViewFinder()
-{
- m_status = QCamera::StartingStatus;
- emit statusChanged(m_status);
-
- QSize viewfinderResolution;
- camera_error_t result = CAMERA_EOK;
- if (m_captureMode & QCamera::CaptureStillImage) {
- result = camera_start_photo_viewfinder(m_handle, 0, viewFinderStatusCallback, this);
- viewfinderResolution = currentViewfinderResolution(QCamera::CaptureStillImage);
- } else if (m_captureMode & QCamera::CaptureVideo) {
- result = camera_start_video_viewfinder(m_handle, 0, viewFinderStatusCallback, this);
- viewfinderResolution = currentViewfinderResolution(QCamera::CaptureVideo);
- }
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to start viewfinder:" << result;
- return false;
- }
-
- const int angle = m_orientationHandler->viewfinderOrientation();
-
- const QSize rotatedSize = ((angle == 0 || angle == 180) ? viewfinderResolution
- : viewfinderResolution.transposed());
-
- m_surfaceMutex.lock();
- if (m_surface) {
- const bool ok = m_surface->start(QVideoSurfaceFormat(rotatedSize, QVideoFrame::Format_ARGB32));
- if (!ok)
- qWarning() << "Unable to start camera viewfinder surface";
- }
- m_surfaceMutex.unlock();
-
- m_status = QCamera::ActiveStatus;
- emit statusChanged(m_status);
-
- return true;
-}
-
-void BbCameraSession::stopViewFinder()
-{
- m_windowGrabber->stop();
-
- m_status = QCamera::StoppingStatus;
- emit statusChanged(m_status);
-
- m_surfaceMutex.lock();
- if (m_surface) {
- m_surface->stop();
- }
- m_surfaceMutex.unlock();
-
- camera_error_t result = CAMERA_EOK;
- if (m_captureMode & QCamera::CaptureStillImage)
- result = camera_stop_photo_viewfinder(m_handle);
- else if (m_captureMode & QCamera::CaptureVideo)
- result = camera_stop_video_viewfinder(m_handle);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to stop viewfinder:" << result;
- return;
- }
-
- m_status = QCamera::LoadedStatus;
- emit statusChanged(m_status);
-}
-
-void BbCameraSession::applyConfiguration()
-{
- if (m_captureMode & QCamera::CaptureStillImage) {
- const QList<QSize> photoOutputResolutions = supportedResolutions(QCamera::CaptureStillImage);
-
- if (!m_imageEncoderSettings.resolution().isValid() || !photoOutputResolutions.contains(m_imageEncoderSettings.resolution()))
- m_imageEncoderSettings.setResolution(photoOutputResolutions.first());
-
- const QSize photoResolution = m_imageEncoderSettings.resolution();
- const qreal aspectRatio = static_cast<qreal>(photoResolution.width())/static_cast<qreal>(photoResolution.height());
-
- // apply viewfinder configuration
- QSize viewfinderResolution;
- QList<QSize> sizes = supportedViewfinderResolutions(QCamera::CaptureStillImage);
- std::reverse(sizes.begin(), sizes.end()); // use smallest possible resolution
- for (const QSize &size : qAsConst(sizes)) {
- // search for viewfinder resolution with the same aspect ratio
- if (qFuzzyCompare(aspectRatio, (static_cast<qreal>(size.width())/static_cast<qreal>(size.height())))) {
- viewfinderResolution = size;
- break;
- }
- }
-
- Q_ASSERT(viewfinderResolution.isValid());
-
- const QByteArray windowId = QString().sprintf("qcamera_vf_%p", this).toLatin1();
- m_windowGrabber->setWindowId(windowId);
-
- const QByteArray windowGroupId = m_windowGrabber->windowGroupId();
-
- camera_error_t result = camera_set_photovf_property(m_handle,
- CAMERA_IMGPROP_WIN_GROUPID, windowGroupId.data(),
- CAMERA_IMGPROP_WIN_ID, windowId.data(),
- CAMERA_IMGPROP_WIDTH, viewfinderResolution.width(),
- CAMERA_IMGPROP_HEIGHT, viewfinderResolution.height(),
- CAMERA_IMGPROP_FORMAT, CAMERA_FRAMETYPE_NV12,
- CAMERA_IMGPROP_ROTATION, 360 - m_nativeCameraOrientation);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to apply photo viewfinder settings:" << result;
- return;
- }
-
-
- int jpegQuality = 100;
- switch (m_imageEncoderSettings.quality()) {
- case QMultimedia::VeryLowQuality:
- jpegQuality = 20;
- break;
- case QMultimedia::LowQuality:
- jpegQuality = 40;
- break;
- case QMultimedia::NormalQuality:
- jpegQuality = 60;
- break;
- case QMultimedia::HighQuality:
- jpegQuality = 80;
- break;
- case QMultimedia::VeryHighQuality:
- jpegQuality = 100;
- break;
- }
-
- // apply photo configuration
- result = camera_set_photo_property(m_handle,
- CAMERA_IMGPROP_WIDTH, photoResolution.width(),
- CAMERA_IMGPROP_HEIGHT, photoResolution.height(),
- CAMERA_IMGPROP_JPEGQFACTOR, jpegQuality,
- CAMERA_IMGPROP_ROTATION, 360 - m_nativeCameraOrientation);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to apply photo settings:" << result;
- return;
- }
-
- } else if (m_captureMode & QCamera::CaptureVideo) {
- applyVideoSettings();
- }
-
- startViewFinder();
-}
-
-static void videoRecordingStatusCallback(camera_handle_t handle, camera_devstatus_t status, uint16_t value, void *context)
-{
- Q_UNUSED(handle);
- Q_UNUSED(value);
-
- if (status == CAMERA_STATUS_VIDEO_PAUSE) {
- BbCameraSession *session = static_cast<BbCameraSession*>(context);
- QMetaObject::invokeMethod(session, "handleVideoRecordingPaused", Qt::QueuedConnection);
- } else if (status == CAMERA_STATUS_VIDEO_RESUME) {
- BbCameraSession *session = static_cast<BbCameraSession*>(context);
- QMetaObject::invokeMethod(session, "handleVideoRecordingResumed", Qt::QueuedConnection);
- }
-}
-
-bool BbCameraSession::startVideoRecording()
-{
- m_videoRecordingDuration.invalidate();
-
- m_videoStatus = QMediaRecorder::StartingStatus;
- emit videoStatusChanged(m_videoStatus);
-
- if (m_videoOutputLocation.isEmpty())
- m_videoOutputLocation = m_mediaStorageLocation.generateFileName(QLatin1String("VID_"), m_mediaStorageLocation.defaultDir(QCamera::CaptureVideo), QLatin1String("mp4"));
-
- emit actualLocationChanged(m_videoOutputLocation);
-
- const camera_error_t result = camera_start_video(m_handle, QFile::encodeName(m_videoOutputLocation), 0, videoRecordingStatusCallback, this);
- if (result != CAMERA_EOK) {
- m_videoStatus = QMediaRecorder::LoadedStatus;
- emit videoStatusChanged(m_videoStatus);
-
- emit videoError(QMediaRecorder::ResourceError, tr("Unable to start video recording"));
- return false;
- }
-
- return true;
-}
-
-void BbCameraSession::stopVideoRecording()
-{
- m_videoStatus = QMediaRecorder::FinalizingStatus;
- emit videoStatusChanged(m_videoStatus);
-
- const camera_error_t result = camera_stop_video(m_handle);
- if (result != CAMERA_EOK) {
- emit videoError(QMediaRecorder::ResourceError, tr("Unable to stop video recording"));
- }
-
- m_videoStatus = QMediaRecorder::LoadedStatus;
- emit videoStatusChanged(m_videoStatus);
-
- m_videoRecordingDuration.invalidate();
-}
-
-bool BbCameraSession::isCaptureModeSupported(camera_handle_t handle, QCamera::CaptureModes mode) const
-{
- if (mode & QCamera::CaptureStillImage)
- return camera_has_feature(handle, CAMERA_FEATURE_PHOTO);
-
- if (mode & QCamera::CaptureVideo)
- return camera_has_feature(handle, CAMERA_FEATURE_VIDEO);
-
- return false;
-}
-
-QList<QSize> BbCameraSession::supportedResolutions(QCamera::CaptureMode mode) const
-{
- Q_ASSERT(m_handle != CAMERA_HANDLE_INVALID);
-
- QList<QSize> list;
-
- camera_error_t result = CAMERA_EOK;
- camera_res_t resolutions[20];
- unsigned int supported = 0;
-
- if (mode == QCamera::CaptureStillImage)
- result = camera_get_photo_output_resolutions(m_handle, CAMERA_FRAMETYPE_JPEG, 20, &supported, resolutions);
- else if (mode == QCamera::CaptureVideo)
- result = camera_get_video_output_resolutions(m_handle, 20, &supported, resolutions);
-
- if (result != CAMERA_EOK)
- return list;
-
- for (unsigned int i = 0; i < supported; ++i)
- list << QSize(resolutions[i].width, resolutions[i].height);
-
- return list;
-}
-
-QList<QSize> BbCameraSession::supportedViewfinderResolutions(QCamera::CaptureMode mode) const
-{
- Q_ASSERT(m_handle != CAMERA_HANDLE_INVALID);
-
- QList<QSize> list;
-
- camera_error_t result = CAMERA_EOK;
- camera_res_t resolutions[20];
- unsigned int supported = 0;
-
- if (mode == QCamera::CaptureStillImage)
- result = camera_get_photo_vf_resolutions(m_handle, 20, &supported, resolutions);
- else if (mode == QCamera::CaptureVideo)
- result = camera_get_video_vf_resolutions(m_handle, 20, &supported, resolutions);
-
- if (result != CAMERA_EOK)
- return list;
-
- for (unsigned int i = 0; i < supported; ++i)
- list << QSize(resolutions[i].width, resolutions[i].height);
-
- return list;
-}
-
-QSize BbCameraSession::currentViewfinderResolution(QCamera::CaptureMode mode) const
-{
- Q_ASSERT(m_handle != CAMERA_HANDLE_INVALID);
-
- camera_error_t result = CAMERA_EOK;
- int width = 0;
- int height = 0;
-
- if (mode == QCamera::CaptureStillImage)
- result = camera_get_photovf_property(m_handle, CAMERA_IMGPROP_WIDTH, &width,
- CAMERA_IMGPROP_HEIGHT, &height);
- else if (mode == QCamera::CaptureVideo)
- result = camera_get_videovf_property(m_handle, CAMERA_IMGPROP_WIDTH, &width,
- CAMERA_IMGPROP_HEIGHT, &height);
-
- if (result != CAMERA_EOK)
- return QSize();
-
- return QSize(width, height);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbcamerasession.h b/src/plugins/qnx/camera/bbcamerasession.h
deleted file mode 100644
index 70523f1e3..000000000
--- a/src/plugins/qnx/camera/bbcamerasession.h
+++ /dev/null
@@ -1,215 +0,0 @@
-/****************************************************************************
-**
-** 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 BBCAMERASESSION_H
-#define BBCAMERASESSION_H
-
-#include "bbmediastoragelocation.h"
-
-#include <QCamera>
-#include <QCameraImageCapture>
-#include <QCameraViewfinderSettingsControl>
-#include <QElapsedTimer>
-#include <QMediaRecorder>
-#include <QMutex>
-#include <QObject>
-#include <QPointer>
-
-#include <camera/camera_api.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraOrientationHandler;
-class WindowGrabber;
-
-class BbCameraSession : public QObject
-{
- Q_OBJECT
-public:
- explicit BbCameraSession(QObject *parent = 0);
- ~BbCameraSession();
-
- camera_handle_t handle() const;
-
- // camera control
- QCamera::State state() const;
- void setState(QCamera::State state);
- QCamera::Status status() const;
- QCamera::CaptureModes captureMode() const;
- void setCaptureMode(QCamera::CaptureModes);
- bool isCaptureModeSupported(QCamera::CaptureModes mode) const;
-
- // video device selector control
- static QByteArray cameraIdentifierFront();
- static QByteArray cameraIdentifierRear();
- static QByteArray cameraIdentifierDesktop();
-
- void setDevice(const QByteArray &device);
- QByteArray device() const;
-
- // video renderer control
- QAbstractVideoSurface *surface() const;
- void setSurface(QAbstractVideoSurface *surface);
-
- // image capture control
- bool isReadyForCapture() const;
- QCameraImageCapture::DriveMode driveMode() const;
- void setDriveMode(QCameraImageCapture::DriveMode mode);
- int capture(const QString &fileName);
- void cancelCapture();
-
- // capture destination control
- bool isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const;
- QCameraImageCapture::CaptureDestinations captureDestination() const;
- void setCaptureDestination(QCameraImageCapture::CaptureDestinations destination);
-
- // image encoder control
- QList<QSize> supportedResolutions(const QImageEncoderSettings &settings, bool *continuous) const;
- QImageEncoderSettings imageSettings() const;
- void setImageSettings(const QImageEncoderSettings &settings);
-
- // media recorder control
- QUrl outputLocation() const;
- bool setOutputLocation(const QUrl &location);
- QMediaRecorder::State videoState() const;
- void setVideoState(QMediaRecorder::State state);
- QMediaRecorder::Status videoStatus() const;
- qint64 duration() const;
- void applyVideoSettings();
-
- // video encoder settings control
- QList<QSize> supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous) const;
- QList<qreal> supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous) const;
- QVideoEncoderSettings videoSettings() const;
- void setVideoSettings(const QVideoEncoderSettings &settings);
-
- // audio encoder settings control
- QAudioEncoderSettings audioSettings() const;
- void setAudioSettings(const QAudioEncoderSettings &settings);
-
-Q_SIGNALS:
- // camera control
- void statusChanged(QCamera::Status);
- void stateChanged(QCamera::State);
- void error(int error, const QString &errorString);
- void captureModeChanged(QCamera::CaptureModes);
-
- // image capture control
- void readyForCaptureChanged(bool);
- void imageExposed(int id);
- void imageCaptured(int id, const QImage &preview);
- void imageMetadataAvailable(int id, const QString &key, const QVariant &value);
- void imageAvailable(int id, const QVideoFrame &buffer);
- void imageSaved(int id, const QString &fileName);
- void imageCaptureError(int id, int error, const QString &errorString);
-
- // capture destination control
- void captureDestinationChanged(QCameraImageCapture::CaptureDestinations destination);
-
- // media recorder control
- void videoStateChanged(QMediaRecorder::State state);
- void videoStatusChanged(QMediaRecorder::Status status);
- void durationChanged(qint64 duration);
- void actualLocationChanged(const QUrl &location);
- void videoError(int error, const QString &errorString);
-
- void cameraOpened();
- void focusStatusChanged(int status);
-
-private slots:
- void updateReadyForCapture();
- void imageCaptured(int, const QImage&, const QString&);
- void handleVideoRecordingPaused();
- void handleVideoRecordingResumed();
- void deviceOrientationChanged(int);
- void handleCameraPowerUp();
- void viewfinderFrameGrabbed(const QImage &image);
- void applyConfiguration();
-
-private:
- bool openCamera();
- void closeCamera();
- bool startViewFinder();
- void stopViewFinder();
- bool startVideoRecording();
- void stopVideoRecording();
-
- bool isCaptureModeSupported(camera_handle_t handle, QCamera::CaptureModes mode) const;
- QList<QSize> supportedResolutions(QCamera::CaptureMode mode) const;
- QList<QSize> supportedViewfinderResolutions(QCamera::CaptureMode mode) const;
- QSize currentViewfinderResolution(QCamera::CaptureMode mode) const;
-
- quint32 m_nativeCameraOrientation;
- BbCameraOrientationHandler* m_orientationHandler;
-
- QCamera::Status m_status;
- QCamera::State m_state;
- QCamera::CaptureModes m_captureMode;
-
- QByteArray m_device;
- bool m_previewIsVideo;
-
- QPointer<QAbstractVideoSurface> m_surface;
- QMutex m_surfaceMutex;
-
- QCameraImageCapture::DriveMode m_captureImageDriveMode;
- int m_lastImageCaptureId;
- QCameraImageCapture::CaptureDestinations m_captureDestination;
-
- QImageEncoderSettings m_imageEncoderSettings;
-
- QString m_videoOutputLocation;
- QMediaRecorder::State m_videoState;
- QMediaRecorder::Status m_videoStatus;
- QElapsedTimer m_videoRecordingDuration;
-
- QVideoEncoderSettings m_videoEncoderSettings;
- QAudioEncoderSettings m_audioEncoderSettings;
-
- BbMediaStorageLocation m_mediaStorageLocation;
-
- camera_handle_t m_handle;
-
- WindowGrabber* m_windowGrabber;
-};
-
-QDebug operator<<(QDebug debug, camera_error_t error);
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbcameravideoencodersettingscontrol.cpp b/src/plugins/qnx/camera/bbcameravideoencodersettingscontrol.cpp
deleted file mode 100644
index 5e3a902c2..000000000
--- a/src/plugins/qnx/camera/bbcameravideoencodersettingscontrol.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbcameravideoencodersettingscontrol.h"
-
-#include "bbcamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-BbCameraVideoEncoderSettingsControl::BbCameraVideoEncoderSettingsControl(BbCameraSession *session, QObject *parent)
- : QVideoEncoderSettingsControl(parent)
- , m_session(session)
-{
-}
-
-QList<QSize> BbCameraVideoEncoderSettingsControl::supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous) const
-{
- return m_session->supportedResolutions(settings, continuous);
-}
-
-QList<qreal> BbCameraVideoEncoderSettingsControl::supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous) const
-{
- return m_session->supportedFrameRates(settings, continuous);
-}
-
-QStringList BbCameraVideoEncoderSettingsControl::supportedVideoCodecs() const
-{
- return QStringList() << QLatin1String("none") << QLatin1String("avc1") << QLatin1String("h264");
-}
-
-QString BbCameraVideoEncoderSettingsControl::videoCodecDescription(const QString &codecName) const
-{
- if (codecName == QLatin1String("none"))
- return tr("No compression");
- else if (codecName == QLatin1String("avc1"))
- return tr("AVC1 compression");
- else if (codecName == QLatin1String("h264"))
- return tr("H264 compression");
-
- return QString();
-}
-
-QVideoEncoderSettings BbCameraVideoEncoderSettingsControl::videoSettings() const
-{
- return m_session->videoSettings();
-}
-
-void BbCameraVideoEncoderSettingsControl::setVideoSettings(const QVideoEncoderSettings &settings)
-{
- m_session->setVideoSettings(settings);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbcameravideoencodersettingscontrol.h b/src/plugins/qnx/camera/bbcameravideoencodersettingscontrol.h
deleted file mode 100644
index f67196be1..000000000
--- a/src/plugins/qnx/camera/bbcameravideoencodersettingscontrol.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/****************************************************************************
-**
-** 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 BBCAMERAVIDEOENCODERSETTINGSCONTROL_H
-#define BBCAMERAVIDEOENCODERSETTINGSCONTROL_H
-
-#include <qvideoencodersettingscontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraSession;
-
-class BbCameraVideoEncoderSettingsControl : public QVideoEncoderSettingsControl
-{
- Q_OBJECT
-public:
- explicit BbCameraVideoEncoderSettingsControl(BbCameraSession *session, QObject *parent = 0);
-
- QList<QSize> supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous = 0) const override;
- QList<qreal> supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous = 0) const override;
- QStringList supportedVideoCodecs() const override;
- QString videoCodecDescription(const QString &codecName) const override;
- QVideoEncoderSettings videoSettings() const override;
- void setVideoSettings(const QVideoEncoderSettings &settings) override;
-
-private:
- BbCameraSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbcameraviewfindersettingscontrol.cpp b/src/plugins/qnx/camera/bbcameraviewfindersettingscontrol.cpp
deleted file mode 100644
index d800ffe13..000000000
--- a/src/plugins/qnx/camera/bbcameraviewfindersettingscontrol.cpp
+++ /dev/null
@@ -1,245 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbcameraviewfindersettingscontrol.h"
-
-#include "bbcamerasession.h"
-
-#include <QDebug>
-
-QT_BEGIN_NAMESPACE
-
-BbCameraViewfinderSettingsControl::BbCameraViewfinderSettingsControl(BbCameraSession *session, QObject *parent)
- : QCameraViewfinderSettingsControl(parent)
- , m_session(session)
-{
-}
-
-bool BbCameraViewfinderSettingsControl::isViewfinderParameterSupported(ViewfinderParameter parameter) const
-{
- switch (parameter) {
- case QCameraViewfinderSettingsControl::Resolution:
- return true;
- case QCameraViewfinderSettingsControl::PixelAspectRatio:
- return false;
- case QCameraViewfinderSettingsControl::MinimumFrameRate:
- return true;
- case QCameraViewfinderSettingsControl::MaximumFrameRate:
- return true;
- case QCameraViewfinderSettingsControl::PixelFormat:
- return true;
- default:
- return false;
- }
-}
-
-QVariant BbCameraViewfinderSettingsControl::viewfinderParameter(ViewfinderParameter parameter) const
-{
- if (parameter == QCameraViewfinderSettingsControl::Resolution) {
- camera_error_t result = CAMERA_EOK;
- unsigned int width = 0;
- unsigned int height = 0;
-
- if (m_session->captureMode() & QCamera::CaptureStillImage) {
- result = camera_get_photovf_property(m_session->handle(), CAMERA_IMGPROP_WIDTH, &width,
- CAMERA_IMGPROP_HEIGHT, &height);
- } else if (m_session->captureMode() & QCamera::CaptureVideo) {
- result = camera_get_videovf_property(m_session->handle(), CAMERA_IMGPROP_WIDTH, &width,
- CAMERA_IMGPROP_HEIGHT, &height);
- }
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to retrieve resolution of viewfinder:" << result;
- return QVariant();
- }
-
- return QSize(width, height);
-
- } else if (parameter == QCameraViewfinderSettingsControl::MinimumFrameRate) {
- camera_error_t result = CAMERA_EOK;
- double minimumFrameRate = 0;
-
- if (m_session->captureMode() & QCamera::CaptureStillImage)
- result = camera_get_photovf_property(m_session->handle(), CAMERA_IMGPROP_MINFRAMERATE, &minimumFrameRate);
- else if (m_session->captureMode() & QCamera::CaptureVideo)
- result = camera_get_videovf_property(m_session->handle(), CAMERA_IMGPROP_MINFRAMERATE, &minimumFrameRate);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to retrieve minimum framerate of viewfinder:" << result;
- return QVariant();
- }
-
- return QVariant(static_cast<qreal>(minimumFrameRate));
-
- } else if (parameter == QCameraViewfinderSettingsControl::MaximumFrameRate) {
- camera_error_t result = CAMERA_EOK;
- double maximumFrameRate = 0;
-
- if (m_session->captureMode() & QCamera::CaptureStillImage)
- result = camera_get_photovf_property(m_session->handle(), CAMERA_IMGPROP_FRAMERATE, &maximumFrameRate);
- else if (m_session->captureMode() & QCamera::CaptureVideo)
- result = camera_get_videovf_property(m_session->handle(), CAMERA_IMGPROP_FRAMERATE, &maximumFrameRate);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to retrieve maximum framerate of viewfinder:" << result;
- return QVariant();
- }
-
- return QVariant(static_cast<qreal>(maximumFrameRate));
- } else if (parameter == QCameraViewfinderSettingsControl::PixelFormat) {
- camera_error_t result = CAMERA_EOK;
- camera_frametype_t format = CAMERA_FRAMETYPE_UNSPECIFIED;
-
- if (m_session->captureMode() & QCamera::CaptureStillImage)
- result = camera_get_photovf_property(m_session->handle(), CAMERA_IMGPROP_FORMAT, &format);
- else if (m_session->captureMode() & QCamera::CaptureVideo)
- result = camera_get_videovf_property(m_session->handle(), CAMERA_IMGPROP_FORMAT, &format);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to retrieve pixel format of viewfinder:" << result;
- return QVariant();
- }
-
- switch (format) {
- case CAMERA_FRAMETYPE_UNSPECIFIED:
- return QVideoFrame::Format_Invalid;
- case CAMERA_FRAMETYPE_NV12:
- return QVideoFrame::Format_NV12;
- case CAMERA_FRAMETYPE_RGB8888:
- return QVideoFrame::Format_ARGB32;
- case CAMERA_FRAMETYPE_RGB888:
- return QVideoFrame::Format_RGB24;
- case CAMERA_FRAMETYPE_JPEG:
- return QVideoFrame::Format_Jpeg;
- case CAMERA_FRAMETYPE_GRAY8:
- return QVideoFrame::Format_Y8;
- case CAMERA_FRAMETYPE_METADATA:
- return QVideoFrame::Format_Invalid;
- case CAMERA_FRAMETYPE_BAYER:
- return QVideoFrame::Format_Invalid;
- case CAMERA_FRAMETYPE_CBYCRY:
- return QVideoFrame::Format_Invalid;
- case CAMERA_FRAMETYPE_COMPRESSEDVIDEO:
- return QVideoFrame::Format_Invalid;
- case CAMERA_FRAMETYPE_COMPRESSEDAUDIO:
- return QVideoFrame::Format_Invalid;
- default:
- return QVideoFrame::Format_Invalid;
- }
- }
-
- return QVariant();
-}
-
-void BbCameraViewfinderSettingsControl::setViewfinderParameter(ViewfinderParameter parameter, const QVariant &value)
-{
- if (parameter == QCameraViewfinderSettingsControl::Resolution) {
- camera_error_t result = CAMERA_EOK;
- const QSize size = value.toSize();
-
- if (m_session->captureMode() & QCamera::CaptureStillImage) {
- result = camera_set_photovf_property(m_session->handle(), CAMERA_IMGPROP_WIDTH, size.width(),
- CAMERA_IMGPROP_HEIGHT, size.height());
- } else if (m_session->captureMode() & QCamera::CaptureVideo) {
- result = camera_set_videovf_property(m_session->handle(), CAMERA_IMGPROP_WIDTH, size.width(),
- CAMERA_IMGPROP_HEIGHT, size.height());
- }
-
- if (result != CAMERA_EOK)
- qWarning() << "Unable to set resolution of viewfinder:" << result;
-
- } else if (parameter == QCameraViewfinderSettingsControl::MinimumFrameRate) {
- camera_error_t result = CAMERA_EOK;
- const double minimumFrameRate = value.toReal();
-
- if (m_session->captureMode() & QCamera::CaptureStillImage)
- result = camera_set_photovf_property(m_session->handle(), CAMERA_IMGPROP_MINFRAMERATE, minimumFrameRate);
- else if (m_session->captureMode() & QCamera::CaptureVideo)
- result = camera_set_videovf_property(m_session->handle(), CAMERA_IMGPROP_MINFRAMERATE, minimumFrameRate);
-
- if (result != CAMERA_EOK)
- qWarning() << "Unable to set minimum framerate of viewfinder:" << result;
-
- } else if (parameter == QCameraViewfinderSettingsControl::MaximumFrameRate) {
- camera_error_t result = CAMERA_EOK;
- const double maximumFrameRate = value.toReal();
-
- if (m_session->captureMode() & QCamera::CaptureStillImage)
- result = camera_set_photovf_property(m_session->handle(), CAMERA_IMGPROP_FRAMERATE, maximumFrameRate);
- else if (m_session->captureMode() & QCamera::CaptureVideo)
- result = camera_set_videovf_property(m_session->handle(), CAMERA_IMGPROP_FRAMERATE, maximumFrameRate);
-
- if (result != CAMERA_EOK)
- qWarning() << "Unable to set maximum framerate of viewfinder:" << result;
-
- } else if (parameter == QCameraViewfinderSettingsControl::PixelFormat) {
- camera_error_t result = CAMERA_EOK;
- camera_frametype_t format = CAMERA_FRAMETYPE_UNSPECIFIED;
-
- switch (value.value<QVideoFrame::PixelFormat>()) {
- case QVideoFrame::Format_NV12:
- format = CAMERA_FRAMETYPE_NV12;
- break;
- case QVideoFrame::Format_ARGB32:
- format = CAMERA_FRAMETYPE_RGB8888;
- break;
- case QVideoFrame::Format_RGB24:
- format = CAMERA_FRAMETYPE_RGB888;
- break;
- case QVideoFrame::Format_Jpeg:
- format = CAMERA_FRAMETYPE_JPEG;
- break;
- case QVideoFrame::Format_Y8:
- format = CAMERA_FRAMETYPE_GRAY8;
- break;
- default:
- format = CAMERA_FRAMETYPE_UNSPECIFIED;
- break;
- }
-
- if (m_session->captureMode() & QCamera::CaptureStillImage)
- result = camera_set_photovf_property(m_session->handle(), CAMERA_IMGPROP_FORMAT, format);
- else if (m_session->captureMode() & QCamera::CaptureVideo)
- result = camera_set_videovf_property(m_session->handle(), CAMERA_IMGPROP_FORMAT, format);
-
- if (result != CAMERA_EOK)
- qWarning() << "Unable to set pixel format of viewfinder:" << result;
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbcameraviewfindersettingscontrol.h b/src/plugins/qnx/camera/bbcameraviewfindersettingscontrol.h
deleted file mode 100644
index 7a8e57a13..000000000
--- a/src/plugins/qnx/camera/bbcameraviewfindersettingscontrol.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/****************************************************************************
-**
-** 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 BBCAMERAVIEWVINDERSETTINGSCONTROL_H
-#define BBCAMERAVIEWVINDERSETTINGSCONTROL_H
-
-#include <qcameraviewfindersettingscontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraSession;
-
-class BbCameraViewfinderSettingsControl : public QCameraViewfinderSettingsControl
-{
- Q_OBJECT
-public:
- explicit BbCameraViewfinderSettingsControl(BbCameraSession *session, QObject *parent = 0);
-
- bool isViewfinderParameterSupported(ViewfinderParameter parameter) const override;
- QVariant viewfinderParameter(ViewfinderParameter parameter) const override;
- void setViewfinderParameter(ViewfinderParameter parameter, const QVariant &value) override;
-
-private:
- BbCameraSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbcamerazoomcontrol.cpp b/src/plugins/qnx/camera/bbcamerazoomcontrol.cpp
deleted file mode 100644
index 21f328b4f..000000000
--- a/src/plugins/qnx/camera/bbcamerazoomcontrol.cpp
+++ /dev/null
@@ -1,155 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbcamerazoomcontrol.h"
-
-#include "bbcamerasession.h"
-
-#include <QDebug>
-
-QT_BEGIN_NAMESPACE
-
-BbCameraZoomControl::BbCameraZoomControl(BbCameraSession *session, QObject *parent)
- : QCameraZoomControl(parent)
- , m_session(session)
- , m_minimumZoomFactor(1.0)
- , m_maximumZoomFactor(1.0)
- , m_supportsSmoothZoom(false)
- , m_requestedZoomFactor(1.0)
-{
- connect(m_session, SIGNAL(statusChanged(QCamera::Status)), this, SLOT(statusChanged(QCamera::Status)));
-}
-
-qreal BbCameraZoomControl::maximumOpticalZoom() const
-{
- //TODO: optical zoom support not available in BB10 API yet
- return 1.0;
-}
-
-qreal BbCameraZoomControl::maximumDigitalZoom() const
-{
- return m_maximumZoomFactor;
-}
-
-qreal BbCameraZoomControl::requestedOpticalZoom() const
-{
- //TODO: optical zoom support not available in BB10 API yet
- return 1.0;
-}
-
-qreal BbCameraZoomControl::requestedDigitalZoom() const
-{
- return currentDigitalZoom();
-}
-
-qreal BbCameraZoomControl::currentOpticalZoom() const
-{
- //TODO: optical zoom support not available in BB10 API yet
- return 1.0;
-}
-
-qreal BbCameraZoomControl::currentDigitalZoom() const
-{
- if (m_session->status() != QCamera::ActiveStatus)
- return 1.0;
-
- unsigned int zoomFactor = 0;
- camera_error_t result = CAMERA_EOK;
-
- if (m_session->captureMode() & QCamera::CaptureStillImage)
- result = camera_get_photovf_property(m_session->handle(), CAMERA_IMGPROP_ZOOMFACTOR, &zoomFactor);
- else if (m_session->captureMode() & QCamera::CaptureVideo)
- result = camera_get_videovf_property(m_session->handle(), CAMERA_IMGPROP_ZOOMFACTOR, &zoomFactor);
-
- if (result != CAMERA_EOK)
- return 1.0;
-
- return zoomFactor;
-}
-
-void BbCameraZoomControl::zoomTo(qreal optical, qreal digital)
-{
- Q_UNUSED(optical);
-
- if (m_session->status() != QCamera::ActiveStatus)
- return;
-
- const qreal actualZoom = qBound(m_minimumZoomFactor, digital, m_maximumZoomFactor);
-
- const camera_error_t result = camera_set_zoom(m_session->handle(), actualZoom, false);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to change zoom factor:" << result;
- return;
- }
-
- if (m_requestedZoomFactor != digital) {
- m_requestedZoomFactor = digital;
- emit requestedDigitalZoomChanged(m_requestedZoomFactor);
- }
-
- emit currentDigitalZoomChanged(actualZoom);
-}
-
-void BbCameraZoomControl::statusChanged(QCamera::Status status)
-{
- if (status == QCamera::ActiveStatus) {
- // retrieve information about zoom limits
- unsigned int maximumZoomLimit = 0;
- unsigned int minimumZoomLimit = 0;
- bool smoothZoom = false;
-
- const camera_error_t result = camera_get_zoom_limits(m_session->handle(), &maximumZoomLimit, &minimumZoomLimit, &smoothZoom);
- if (result == CAMERA_EOK) {
- const qreal oldMaximumZoomFactor = m_maximumZoomFactor;
- m_maximumZoomFactor = maximumZoomLimit;
-
- if (oldMaximumZoomFactor != m_maximumZoomFactor)
- emit maximumDigitalZoomChanged(m_maximumZoomFactor);
-
- m_minimumZoomFactor = minimumZoomLimit;
- m_supportsSmoothZoom = smoothZoom;
- } else {
- m_maximumZoomFactor = 1.0;
- m_minimumZoomFactor = 1.0;
- m_supportsSmoothZoom = false;
- }
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbcamerazoomcontrol.h b/src/plugins/qnx/camera/bbcamerazoomcontrol.h
deleted file mode 100644
index 7b5e06f8d..000000000
--- a/src/plugins/qnx/camera/bbcamerazoomcontrol.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/****************************************************************************
-**
-** 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 BBCAMERAZOOMCONTROL_H
-#define BBCAMERAZOOMCONTROL_H
-
-#include <qcamera.h>
-#include <qcamerazoomcontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraSession;
-
-class BbCameraZoomControl : public QCameraZoomControl
-{
- Q_OBJECT
-public:
- explicit BbCameraZoomControl(BbCameraSession *session, QObject *parent = 0);
-
- qreal maximumOpticalZoom() const override;
- qreal maximumDigitalZoom() const override;
- qreal requestedOpticalZoom() const override;
- qreal requestedDigitalZoom() const override;
- qreal currentOpticalZoom() const override;
- qreal currentDigitalZoom() const override;
- void zoomTo(qreal optical, qreal digital) override;
-
-private Q_SLOTS:
- void statusChanged(QCamera::Status status);
-
-private:
- BbCameraSession *m_session;
-
- qreal m_minimumZoomFactor;
- qreal m_maximumZoomFactor;
- bool m_supportsSmoothZoom;
- qreal m_requestedZoomFactor;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbimageencodercontrol.cpp b/src/plugins/qnx/camera/bbimageencodercontrol.cpp
deleted file mode 100644
index cd564a1d4..000000000
--- a/src/plugins/qnx/camera/bbimageencodercontrol.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbimageencodercontrol.h"
-
-#include "bbcamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-BbImageEncoderControl::BbImageEncoderControl(BbCameraSession *session, QObject *parent)
- : QImageEncoderControl(parent)
- , m_session(session)
-{
-}
-
-QStringList BbImageEncoderControl::supportedImageCodecs() const
-{
- return QStringList() << QLatin1String("jpeg");
-}
-
-QString BbImageEncoderControl::imageCodecDescription(const QString &codecName) const
-{
- if (codecName == QLatin1String("jpeg"))
- return tr("JPEG image");
-
- return QString();
-}
-
-QList<QSize> BbImageEncoderControl::supportedResolutions(const QImageEncoderSettings &settings, bool *continuous) const
-{
- return m_session->supportedResolutions(settings, continuous);
-}
-
-QImageEncoderSettings BbImageEncoderControl::imageSettings() const
-{
- return m_session->imageSettings();
-}
-
-void BbImageEncoderControl::setImageSettings(const QImageEncoderSettings &settings)
-{
- m_session->setImageSettings(settings);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbimageencodercontrol.h b/src/plugins/qnx/camera/bbimageencodercontrol.h
deleted file mode 100644
index bb246def6..000000000
--- a/src/plugins/qnx/camera/bbimageencodercontrol.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************************
-**
-** 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 BBIMAGEENCODERCONTROL_H
-#define BBIMAGEENCODERCONTROL_H
-
-#include <qimageencodercontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraSession;
-
-class BbImageEncoderControl : public QImageEncoderControl
-{
- Q_OBJECT
-public:
- explicit BbImageEncoderControl(BbCameraSession *session, QObject *parent = 0);
-
- QStringList supportedImageCodecs() const override;
- QString imageCodecDescription(const QString &codecName) const override;
- QList<QSize> supportedResolutions(const QImageEncoderSettings &settings, bool *continuous = 0) const override;
- QImageEncoderSettings imageSettings() const override;
- void setImageSettings(const QImageEncoderSettings &settings) override;
-
-private:
- BbCameraSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbmediastoragelocation.cpp b/src/plugins/qnx/camera/bbmediastoragelocation.cpp
deleted file mode 100644
index c3aaed55d..000000000
--- a/src/plugins/qnx/camera/bbmediastoragelocation.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbmediastoragelocation.h"
-
-#include <QStandardPaths>
-
-QT_BEGIN_NAMESPACE
-
-BbMediaStorageLocation::BbMediaStorageLocation()
-{
-}
-
-QDir BbMediaStorageLocation::defaultDir(QCamera::CaptureMode mode) const
-{
- QStringList dirCandidates;
-
- dirCandidates << QLatin1String("shared/camera");
-
- if (mode == QCamera::CaptureVideo) {
- dirCandidates << QStandardPaths::writableLocation(QStandardPaths::MoviesLocation);
- } else {
- dirCandidates << QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
- }
-
- dirCandidates << QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
- dirCandidates << QDir::homePath();
- dirCandidates << QDir::currentPath();
- dirCandidates << QDir::tempPath();
-
- for (const QString &path : qAsConst(dirCandidates)) {
- if (QFileInfo(path).isWritable())
- return QDir(path);
- }
-
- return QDir();
-}
-
-QString BbMediaStorageLocation::generateFileName(const QString &requestedName, QCamera::CaptureMode mode, const QString &prefix, const QString &extension) const
-{
- if (requestedName.isEmpty())
- return generateFileName(prefix, defaultDir(mode), extension);
-
- if (QFileInfo(requestedName).isDir())
- return generateFileName(prefix, QDir(requestedName), extension);
-
- return requestedName;
-}
-
-QString BbMediaStorageLocation::generateFileName(const QString &prefix, const QDir &dir, const QString &extension) const
-{
- const QString lastMediaKey = dir.absolutePath() + QLatin1Char(' ') + prefix + QLatin1Char(' ') + extension;
- qint64 lastMediaIndex = m_lastUsedIndex.value(lastMediaKey, 0);
-
- if (lastMediaIndex == 0) {
- // first run, find the maximum media number during the fist capture
- const auto list = dir.entryList(QStringList() << QString("%1*.%2").arg(prefix).arg(extension));
- for (const QString &fileName : list) {
- const qint64 mediaIndex = QStringView{fileName}.mid(prefix.length(), fileName.size() - prefix.length() - extension.length() - 1).toInt();
- lastMediaIndex = qMax(lastMediaIndex, mediaIndex);
- }
- }
-
- // don't just rely on cached lastMediaIndex value,
- // someone else may create a file after camera started
- while (true) {
- const QString name = QString("%1%2.%3").arg(prefix)
- .arg(lastMediaIndex + 1, 8, 10, QLatin1Char('0'))
- .arg(extension);
-
- const QString path = dir.absoluteFilePath(name);
- if (!QFileInfo(path).exists()) {
- m_lastUsedIndex[lastMediaKey] = lastMediaIndex + 1;
- return path;
- }
-
- lastMediaIndex++;
- }
-
- return QString();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbmediastoragelocation.h b/src/plugins/qnx/camera/bbmediastoragelocation.h
deleted file mode 100644
index 8a953c27d..000000000
--- a/src/plugins/qnx/camera/bbmediastoragelocation.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/****************************************************************************
-**
-** 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 BBMEDIASTORAGELOCATION_H
-#define BBMEDIASTORAGELOCATION_H
-
-#include <QCamera>
-#include <QDir>
-#include <QHash>
-
-QT_BEGIN_NAMESPACE
-
-class BbMediaStorageLocation
-{
-public:
- BbMediaStorageLocation();
-
- QDir defaultDir(QCamera::CaptureMode mode) const;
-
- QString generateFileName(const QString &requestedName, QCamera::CaptureMode mode, const QString &prefix, const QString &extension) const;
- QString generateFileName(const QString &prefix, const QDir &dir, const QString &extension) const;
-
-private:
- mutable QHash<QString, qint64> m_lastUsedIndex;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbvideodeviceselectorcontrol.cpp b/src/plugins/qnx/camera/bbvideodeviceselectorcontrol.cpp
deleted file mode 100644
index fd7deb179..000000000
--- a/src/plugins/qnx/camera/bbvideodeviceselectorcontrol.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "bbvideodeviceselectorcontrol.h"
-
-#include "bbcamerasession.h"
-
-#include <QDebug>
-
-QT_BEGIN_NAMESPACE
-
-BbVideoDeviceSelectorControl::BbVideoDeviceSelectorControl(BbCameraSession *session, QObject *parent)
- : QVideoDeviceSelectorControl(parent)
- , m_session(session)
- , m_default(0)
- , m_selected(0)
-{
- enumerateDevices(&m_devices, &m_descriptions);
-
- // pre-select the rear camera
- const int index = m_devices.indexOf(BbCameraSession::cameraIdentifierRear());
- if (index != -1)
- m_default = m_selected = index;
-}
-
-int BbVideoDeviceSelectorControl::deviceCount() const
-{
- return m_devices.count();
-}
-
-QString BbVideoDeviceSelectorControl::deviceName(int index) const
-{
- if (index < 0 || index >= m_devices.count())
- return QString();
-
- return QString::fromUtf8(m_devices.at(index));
-}
-
-QString BbVideoDeviceSelectorControl::deviceDescription(int index) const
-{
- if (index < 0 || index >= m_descriptions.count())
- return QString();
-
- return m_descriptions.at(index);
-}
-
-int BbVideoDeviceSelectorControl::defaultDevice() const
-{
- return m_default;
-}
-
-int BbVideoDeviceSelectorControl::selectedDevice() const
-{
- return m_selected;
-}
-
-void BbVideoDeviceSelectorControl::enumerateDevices(QList<QByteArray> *devices, QStringList *descriptions)
-{
- devices->clear();
- descriptions->clear();
-
- camera_unit_t cameras[10];
-
- unsigned int knownCameras = 0;
- const camera_error_t result = camera_get_supported_cameras(10, &knownCameras, cameras);
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to retrieve supported camera types:" << result;
- return;
- }
-
- for (unsigned int i = 0; i < knownCameras; ++i) {
- switch (cameras[i]) {
- case CAMERA_UNIT_FRONT:
- devices->append(BbCameraSession::cameraIdentifierFront());
- descriptions->append(tr("Front Camera"));
- break;
- case CAMERA_UNIT_REAR:
- devices->append(BbCameraSession::cameraIdentifierRear());
- descriptions->append(tr("Rear Camera"));
- break;
- case CAMERA_UNIT_DESKTOP:
- devices->append(BbCameraSession::cameraIdentifierDesktop());
- descriptions->append(tr("Desktop Camera"));
- break;
- default:
- break;
- }
- }
-}
-
-void BbVideoDeviceSelectorControl::setSelectedDevice(int index)
-{
- if (index < 0 || index >= m_devices.count())
- return;
-
- if (!m_session)
- return;
-
- const QByteArray device = m_devices.at(index);
- if (device == m_session->device())
- return;
-
- m_session->setDevice(device);
- m_selected = index;
-
- emit selectedDeviceChanged(QString::fromUtf8(device));
- emit selectedDeviceChanged(index);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbvideodeviceselectorcontrol.h b/src/plugins/qnx/camera/bbvideodeviceselectorcontrol.h
deleted file mode 100644
index 08d6cbb14..000000000
--- a/src/plugins/qnx/camera/bbvideodeviceselectorcontrol.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/****************************************************************************
-**
-** 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 BBVIDEODEVICESELECTORCONTROL_H
-#define BBVIDEODEVICESELECTORCONTROL_H
-
-#include <qvideodeviceselectorcontrol.h>
-#include <QStringList>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraSession;
-
-class BbVideoDeviceSelectorControl : public QVideoDeviceSelectorControl
-{
- Q_OBJECT
-public:
- explicit BbVideoDeviceSelectorControl(BbCameraSession *session, QObject *parent = 0);
-
- int deviceCount() const override;
- QString deviceName(int index) const override;
- QString deviceDescription(int index) const override;
- int defaultDevice() const override;
- int selectedDevice() const override;
-
- static void enumerateDevices(QList<QByteArray> *devices, QStringList *descriptions);
-
-public Q_SLOTS:
- void setSelectedDevice(int index) override;
-
-private:
- BbCameraSession* m_session;
-
- QList<QByteArray> m_devices;
- QStringList m_descriptions;
-
- int m_default;
- int m_selected;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/bbvideorenderercontrol.cpp b/src/plugins/qnx/camera/bbvideorenderercontrol.cpp
deleted file mode 100644
index fd271c9de..000000000
--- a/src/plugins/qnx/camera/bbvideorenderercontrol.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "bbvideorenderercontrol.h"
-
-#include "bbcamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-BbVideoRendererControl::BbVideoRendererControl(BbCameraSession *session, QObject *parent)
- : QVideoRendererControl(parent)
- , m_session(session)
-{
-}
-
-QAbstractVideoSurface* BbVideoRendererControl::surface() const
-{
- return m_session->surface();
-}
-
-void BbVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
-{
- m_session->setSurface(surface);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/camera/bbvideorenderercontrol.h b/src/plugins/qnx/camera/bbvideorenderercontrol.h
deleted file mode 100644
index 441ff369d..000000000
--- a/src/plugins/qnx/camera/bbvideorenderercontrol.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/****************************************************************************
-**
-** 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 BBVIDEORENDERERCONTROL_H
-#define BBVIDEORENDERERCONTROL_H
-
-#include <qvideorenderercontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class BbCameraSession;
-
-class BbVideoRendererControl : public QVideoRendererControl
-{
- Q_OBJECT
-public:
- explicit BbVideoRendererControl(BbCameraSession *session, QObject *parent = 0);
-
- QAbstractVideoSurface *surface() const override;
- void setSurface(QAbstractVideoSurface *surface) override;
-
-private:
- BbCameraSession *m_session;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/camera/camera.pri b/src/plugins/qnx/camera/camera.pri
deleted file mode 100644
index 886351862..000000000
--- a/src/plugins/qnx/camera/camera.pri
+++ /dev/null
@@ -1,52 +0,0 @@
-INCLUDEPATH += $$PWD
-
-HEADERS += \
- $$PWD/bbcameraaudioencodersettingscontrol.h \
- $$PWD/bbcameracapturebufferformatcontrol.h \
- $$PWD/bbcameracapturedestinationcontrol.h \
- $$PWD/bbcameracontrol.h \
- $$PWD/bbcameraexposurecontrol.h \
- $$PWD/bbcameraflashcontrol.h \
- $$PWD/bbcamerafocuscontrol.h \
- $$PWD/bbcameraimagecapturecontrol.h \
- $$PWD/bbcameraimageprocessingcontrol.h \
- $$PWD/bbcamerainfocontrol.h \
- $$PWD/bbcameralockscontrol.h \
- $$PWD/bbcameramediarecordercontrol.h \
- $$PWD/bbcameraorientationhandler.h \
- $$PWD/bbcameraservice.h \
- $$PWD/bbcamerasession.h \
- $$PWD/bbcameravideoencodersettingscontrol.h \
- $$PWD/bbcameraviewfindersettingscontrol.h \
- $$PWD/bbcamerazoomcontrol.h \
- $$PWD/bbimageencodercontrol.h \
- $$PWD/bbmediastoragelocation.h \
- $$PWD/bbvideodeviceselectorcontrol.h \
- $$PWD/bbvideorenderercontrol.h
-
-SOURCES += \
- $$PWD/bbcameraaudioencodersettingscontrol.cpp \
- $$PWD/bbcameracapturebufferformatcontrol.cpp \
- $$PWD/bbcameracapturedestinationcontrol.cpp \
- $$PWD/bbcameracontrol.cpp \
- $$PWD/bbcameraexposurecontrol.cpp \
- $$PWD/bbcameraflashcontrol.cpp \
- $$PWD/bbcamerafocuscontrol.cpp \
- $$PWD/bbcameraimagecapturecontrol.cpp \
- $$PWD/bbcameraimageprocessingcontrol.cpp \
- $$PWD/bbcamerainfocontrol.cpp \
- $$PWD/bbcameralockscontrol.cpp \
- $$PWD/bbcameramediarecordercontrol.cpp \
- $$PWD/bbcameraorientationhandler.cpp \
- $$PWD/bbcameraservice.cpp \
- $$PWD/bbcamerasession.cpp \
- $$PWD/bbcameravideoencodersettingscontrol.cpp \
- $$PWD/bbcameraviewfindersettingscontrol.cpp \
- $$PWD/bbcamerazoomcontrol.cpp \
- $$PWD/bbimageencodercontrol.cpp \
- $$PWD/bbmediastoragelocation.cpp \
- $$PWD/bbvideodeviceselectorcontrol.cpp \
- $$PWD/bbvideorenderercontrol.cpp
-
-LIBS += -lcamapi -laudio_manager
-
diff --git a/src/plugins/qnx/common/common.pri b/src/plugins/qnx/common/common.pri
deleted file mode 100644
index 1a6693474..000000000
--- a/src/plugins/qnx/common/common.pri
+++ /dev/null
@@ -1,7 +0,0 @@
-INCLUDEPATH += $$PWD
-
-HEADERS += \
- $$PWD/windowgrabber.h
-
-SOURCES += \
- $$PWD/windowgrabber.cpp
diff --git a/src/plugins/qnx/common/windowgrabber.cpp b/src/plugins/qnx/common/windowgrabber.cpp
deleted file mode 100644
index 65037fcce..000000000
--- a/src/plugins/qnx/common/windowgrabber.cpp
+++ /dev/null
@@ -1,419 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "windowgrabber.h"
-
-#include <QAbstractEventDispatcher>
-#include <QDebug>
-#include <QGuiApplication>
-#include <QImage>
-#include <QThread>
-#include <qpa/qplatformnativeinterface.h>
-
-#include <QOpenGLContext>
-#include <QOpenGLFunctions>
-
-#include <errno.h>
-
-QT_BEGIN_NAMESPACE
-
-static PFNEGLCREATEIMAGEKHRPROC s_eglCreateImageKHR;
-static PFNEGLDESTROYIMAGEKHRPROC s_eglDestroyImageKHR;
-
-WindowGrabber::WindowGrabber(QObject *parent)
- : QObject(parent),
- m_windowParent(nullptr),
- m_screenContext(0),
- m_active(false),
- m_currentFrame(0),
- m_eglImageSupported(false),
- m_eglImageCheck(false)
-{
- // grab the window frame with 60 frames per second
- m_timer.setInterval(1000/60);
-
- connect(&m_timer, SIGNAL(timeout()), SLOT(triggerUpdate()));
-
- QCoreApplication::eventDispatcher()->installNativeEventFilter(this);
-
- for ( int i = 0; i < 2; ++i )
- m_images[i] = 0;
-
- // Use of EGL images can be disabled by setting QQNX_MM_DISABLE_EGLIMAGE_SUPPORT to something
- // non-zero. This is probably useful only to test that this path still works since it results
- // in a high CPU load.
- if (!s_eglCreateImageKHR && qgetenv("QQNX_MM_DISABLE_EGLIMAGE_SUPPORT").toInt() == 0) {
- s_eglCreateImageKHR = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"));
- s_eglDestroyImageKHR = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR"));
- }
-
- QPlatformNativeInterface *const nativeInterface = QGuiApplication::platformNativeInterface();
- if (nativeInterface) {
- m_screenContext = static_cast<screen_context_t>(
- nativeInterface->nativeResourceForIntegration("screenContext"));
- }
-
- // Create a parent window for the window whose content will be grabbed. Since the
- // window is only a buffer conduit, the characteristics of the parent window are
- // irrelevant. The contents of the window can be grabbed so long as the window
- // joins the parent window's group and the parent window is in this process.
- // Using the window that displays this content isn't possible because there's no
- // way to reliably retrieve it from this code or any calling code.
- screen_create_window(&m_windowParent, m_screenContext);
- screen_create_window_group(m_windowParent, nullptr);
-}
-
-WindowGrabber::~WindowGrabber()
-{
- screen_destroy_window(m_windowParent);
- QCoreApplication::eventDispatcher()->removeNativeEventFilter(this);
- cleanup();
-}
-
-void WindowGrabber::setFrameRate(int frameRate)
-{
- m_timer.setInterval(1000/frameRate);
-}
-
-
-void WindowGrabber::setWindowId(const QByteArray &windowId)
-{
- m_windowId = windowId;
-}
-
-void WindowGrabber::start()
-{
- if (m_active)
- return;
-
- if (!m_screenContext)
- screen_get_window_property_pv(m_window, SCREEN_PROPERTY_CONTEXT, reinterpret_cast<void**>(&m_screenContext));
-
- m_timer.start();
-
- m_active = true;
-}
-
-void WindowGrabber::stop()
-{
- if (!m_active)
- return;
-
- cleanup();
-
- m_timer.stop();
-
- m_active = false;
-}
-
-void WindowGrabber::pause()
-{
- m_timer.stop();
-}
-
-void WindowGrabber::resume()
-{
- if (!m_active)
- return;
-
- m_timer.start();
-}
-
-bool WindowGrabber::handleScreenEvent(screen_event_t screen_event)
-{
-
- int eventType;
- if (screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TYPE, &eventType) != 0) {
- qWarning() << "WindowGrabber: Failed to query screen event type";
- return false;
- }
-
- if (eventType != SCREEN_EVENT_CREATE)
- return false;
-
- screen_window_t window = 0;
- if (screen_get_event_property_pv(screen_event, SCREEN_PROPERTY_WINDOW, (void**)&window) != 0) {
- qWarning() << "WindowGrabber: Failed to query window property";
- return false;
- }
-
- const int maxIdStrLength = 128;
- char idString[maxIdStrLength];
- if (screen_get_window_property_cv(window, SCREEN_PROPERTY_ID_STRING, maxIdStrLength, idString) != 0) {
- qWarning() << "WindowGrabber: Failed to query window ID string";
- return false;
- }
-
- // Grab windows that have a non-empty ID string and a matching window id to grab
- if (idString[0] != '\0' && m_windowId == idString) {
- m_window = window;
- start();
- }
-
- return false;
-}
-
-bool WindowGrabber::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *)
-{
- if (eventType == "screen_event_t") {
- const screen_event_t event = static_cast<screen_event_t>(message);
- return handleScreenEvent(event);
- }
-
- return false;
-}
-
-QByteArray WindowGrabber::windowGroupId() const
-{
- char groupName[256];
- memset(groupName, 0, sizeof(groupName));
- screen_get_window_property_cv(m_windowParent,
- SCREEN_PROPERTY_GROUP,
- sizeof(groupName) - 1,
- groupName);
- return QByteArray(groupName);
-}
-
-bool WindowGrabber::eglImageSupported()
-{
- return m_eglImageSupported;
-}
-
-void WindowGrabber::checkForEglImageExtension()
-{
- QOpenGLContext *m_context = QOpenGLContext::currentContext();
- if (!m_context) //Should not happen, because we are called from the render thread
- return;
-
- QByteArray eglExtensions = QByteArray(eglQueryString(eglGetDisplay(EGL_DEFAULT_DISPLAY),
- EGL_EXTENSIONS));
- m_eglImageSupported = m_context->hasExtension(QByteArrayLiteral("GL_OES_EGL_image"))
- && eglExtensions.contains(QByteArrayLiteral("EGL_KHR_image"))
- && s_eglCreateImageKHR && s_eglDestroyImageKHR;
-
- if (strstr(reinterpret_cast<const char*>(glGetString(GL_VENDOR)), "VMware"))
- m_eglImageSupported = false;
-
- m_eglImageCheck = true;
-}
-
-void WindowGrabber::triggerUpdate()
-{
- if (!m_eglImageCheck) // We did not check for egl images yet
- return;
-
- int size[2] = { 0, 0 };
-
- int result = screen_get_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, size);
- if (result != 0) {
- cleanup();
- qWarning() << "WindowGrabber: cannot get window size:" << strerror(errno);
- return;
- }
-
- if (m_size.width() != size[0] || m_size.height() != size[1])
- m_size = QSize(size[0], size[1]);
-
- emit updateScene(m_size);
-}
-
-bool WindowGrabber::selectBuffer()
-{
- // If we're using egl images we need to double buffer since the gpu may still be using the last
- // video frame. If we're not, it doesn't matter since the data is immediately copied.
- if (eglImageSupported())
- m_currentFrame = (m_currentFrame + 1) % 2;
-
- if (!m_images[m_currentFrame]) {
- m_images[m_currentFrame] = new WindowGrabberImage();
- if (!m_images[m_currentFrame]->initialize(m_screenContext)) {
- delete m_images[m_currentFrame];
- m_images[m_currentFrame] = 0;
- return false;
- }
- }
- return true;
-}
-
-int WindowGrabber::getNextTextureId()
-{
- if (!selectBuffer())
- return 0;
- return m_images[m_currentFrame]->getTexture(m_window, m_size);
-}
-
-QImage WindowGrabber::getNextImage()
-{
- if (!selectBuffer())
- return QImage();
-
- return m_images[m_currentFrame]->getImage(m_window, m_size);
-}
-
-void WindowGrabber::cleanup()
-{
- for ( int i = 0; i < 2; ++i ) {
- if (m_images[i]) {
- m_images[i]->destroy();
- m_images[i] = 0;
- }
- }
-}
-
-
-WindowGrabberImage::WindowGrabberImage()
- : m_pixmap(0), m_pixmapBuffer(0), m_eglImage(0), m_glTexture(0), m_bufferAddress(0), m_bufferStride(0)
-{
-}
-
-WindowGrabberImage::~WindowGrabberImage()
-{
- if (m_glTexture)
- glDeleteTextures(1, &m_glTexture);
- if (m_eglImage)
- s_eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), m_eglImage);
- if (m_pixmap)
- screen_destroy_pixmap(m_pixmap);
-}
-
-bool
-WindowGrabberImage::initialize(screen_context_t screenContext)
-{
- if (screen_create_pixmap(&m_pixmap, screenContext) != 0) {
- qWarning() << "WindowGrabber: cannot create pixmap:" << strerror(errno);
- return false;
- }
- const int usage = SCREEN_USAGE_WRITE | SCREEN_USAGE_READ | SCREEN_USAGE_NATIVE;
- screen_set_pixmap_property_iv(m_pixmap, SCREEN_PROPERTY_USAGE, &usage);
-
- const int format = SCREEN_FORMAT_RGBX8888;
- screen_set_pixmap_property_iv(m_pixmap, SCREEN_PROPERTY_FORMAT, &format);
-
- return true;
-}
-
-void
-WindowGrabberImage::destroy()
-{
- // We want to delete in the thread we were created in since we need the thread that
- // has called eglMakeCurrent on the right EGL context. This doesn't actually guarantee
- // this but that would be hard to achieve and in practice it works.
- if (QThread::currentThread() == thread())
- delete this;
- else
- deleteLater();
-}
-
-bool
-WindowGrabberImage::resize(const QSize &newSize)
-{
- if (m_pixmapBuffer) {
- screen_destroy_pixmap_buffer(m_pixmap);
- m_pixmapBuffer = 0;
- m_bufferAddress = 0;
- m_bufferStride = 0;
- }
-
- int size[2] = { newSize.width(), newSize.height() };
-
- screen_set_pixmap_property_iv(m_pixmap, SCREEN_PROPERTY_BUFFER_SIZE, size);
-
- if (screen_create_pixmap_buffer(m_pixmap) == 0) {
- screen_get_pixmap_property_pv(m_pixmap, SCREEN_PROPERTY_RENDER_BUFFERS,
- reinterpret_cast<void**>(&m_pixmapBuffer));
- screen_get_buffer_property_pv(m_pixmapBuffer, SCREEN_PROPERTY_POINTER,
- reinterpret_cast<void**>(&m_bufferAddress));
- screen_get_buffer_property_iv(m_pixmapBuffer, SCREEN_PROPERTY_STRIDE, &m_bufferStride);
- m_size = newSize;
-
- return true;
- } else {
- m_size = QSize();
- return false;
- }
-}
-
-bool
-WindowGrabberImage::grab(screen_window_t window)
-{
- const int rect[] = { 0, 0, m_size.width(), m_size.height() };
- return screen_read_window(window, m_pixmapBuffer, 1, rect, 0) == 0;
-}
-
-QImage
-WindowGrabberImage::getImage(screen_window_t window, const QSize &size)
-{
- if (size != m_size) {
- if (!resize(size))
- return QImage();
- }
- if (!m_bufferAddress || !grab(window))
- return QImage();
-
- return QImage(m_bufferAddress, m_size.width(), m_size.height(), m_bufferStride, QImage::Format_ARGB32);
-}
-
-GLuint
-WindowGrabberImage::getTexture(screen_window_t window, const QSize &size)
-{
- if (size != m_size) {
- if (!m_glTexture)
- glGenTextures(1, &m_glTexture);
- glBindTexture(GL_TEXTURE_2D, m_glTexture);
- if (m_eglImage) {
- glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, 0);
- s_eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), m_eglImage);
- m_eglImage = 0;
- }
- if (!resize(size))
- return 0;
- m_eglImage = s_eglCreateImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), EGL_NO_CONTEXT,
- EGL_NATIVE_PIXMAP_KHR, m_pixmap, 0);
- glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
- }
-
- if (!m_pixmap || !grab(window))
- return 0;
-
- return m_glTexture;
-}
-
-
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/common/windowgrabber.h b/src/plugins/qnx/common/windowgrabber.h
deleted file mode 100644
index c4c1f6a53..000000000
--- a/src/plugins/qnx/common/windowgrabber.h
+++ /dev/null
@@ -1,144 +0,0 @@
-/****************************************************************************
-**
-** 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 WINDOWGRABBER_H
-#define WINDOWGRABBER_H
-
-#define EGL_EGLEXT_PROTOTYPES
-#define GL_GLEXT_PROTOTYPES
-#include <EGL/egl.h>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <EGL/eglext.h>
-#include <QAbstractNativeEventFilter>
-#include <QObject>
-#include <QSize>
-#include <QTimer>
-
-#include <screen/screen.h>
-
-QT_BEGIN_NAMESPACE
-
-class WindowGrabberImage : public QObject
-{
- Q_OBJECT
-
-public:
- WindowGrabberImage();
- ~WindowGrabberImage();
-
- bool initialize(screen_context_t screenContext);
-
- void destroy();
-
- QImage getImage(screen_window_t window, const QSize &size);
- GLuint getTexture(screen_window_t window, const QSize &size);
-
-private:
- bool grab(screen_window_t window);
- bool resize(const QSize &size);
-
- QSize m_size;
- screen_pixmap_t m_pixmap;
- screen_buffer_t m_pixmapBuffer;
- EGLImageKHR m_eglImage;
- GLuint m_glTexture;
- unsigned char *m_bufferAddress;
- int m_bufferStride;
-};
-
-class WindowGrabber : public QObject, public QAbstractNativeEventFilter
-{
- Q_OBJECT
-
-public:
- explicit WindowGrabber(QObject *parent = 0);
- ~WindowGrabber();
-
- void setFrameRate(int frameRate);
-
- void setWindowId(const QByteArray &windowId);
-
- void start();
- void stop();
-
- void pause();
- void resume();
-
- bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
-
- bool handleScreenEvent(screen_event_t event);
-
- QByteArray windowGroupId() const;
-
- bool eglImageSupported();
- void checkForEglImageExtension();
-
- int getNextTextureId();
- QImage getNextImage();
-
-signals:
- void updateScene(const QSize &size);
-
-private slots:
- void triggerUpdate();
-
-private:
- bool selectBuffer();
- void cleanup();
-
- QTimer m_timer;
-
- QByteArray m_windowId;
-
- screen_window_t m_windowParent;
- screen_window_t m_window;
- screen_context_t m_screenContext;
-
- WindowGrabberImage *m_images[2];
- QSize m_size;
-
- bool m_active;
- int m_currentFrame;
- bool m_eglImageSupported;
- bool m_eglImageCheck; // We must not send a grabed frame before this is true
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/mediaplayer/mediaplayer.pri b/src/plugins/qnx/mediaplayer/mediaplayer.pri
deleted file mode 100644
index f39b542cc..000000000
--- a/src/plugins/qnx/mediaplayer/mediaplayer.pri
+++ /dev/null
@@ -1,28 +0,0 @@
-INCLUDEPATH += $$PWD
-
-HEADERS += \
- $$PWD/mmrendereraudiorolecontrol.h \
- $$PWD/mmrenderermediaplayercontrol.h \
- $$PWD/mmrenderermediaplayerservice.h \
- $$PWD/mmrenderermetadata.h \
- $$PWD/mmrenderermetadatareadercontrol.h \
- $$PWD/mmrendererplayervideorenderercontrol.h \
- $$PWD/mmrendererutil.h \
- $$PWD/mmrenderervideowindowcontrol.h \
- $$PWD/mmreventmediaplayercontrol.h \
- $$PWD/mmreventthread.h \
- $$PWD/mmrenderercustomaudiorolecontrol.h
-SOURCES += \
- $$PWD/mmrendereraudiorolecontrol.cpp \
- $$PWD/mmrenderermediaplayercontrol.cpp \
- $$PWD/mmrenderermediaplayerservice.cpp \
- $$PWD/mmrenderermetadata.cpp \
- $$PWD/mmrenderermetadatareadercontrol.cpp \
- $$PWD/mmrendererplayervideorenderercontrol.cpp \
- $$PWD/mmrendererutil.cpp \
- $$PWD/mmrenderervideowindowcontrol.cpp \
- $$PWD/mmreventmediaplayercontrol.cpp \
- $$PWD/mmreventthread.cpp \
- $$PWD/mmrenderercustomaudiorolecontrol.cpp
-
-QMAKE_USE += mmrenderer
diff --git a/src/plugins/qnx/mediaplayer/mmrendereraudiorolecontrol.cpp b/src/plugins/qnx/mediaplayer/mmrendereraudiorolecontrol.cpp
deleted file mode 100644
index e470ed4c5..000000000
--- a/src/plugins/qnx/mediaplayer/mmrendereraudiorolecontrol.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 QNX Software Systems. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "mmrendereraudiorolecontrol.h"
-#include "mmrendererutil.h"
-
-QT_BEGIN_NAMESPACE
-
-MmRendererAudioRoleControl::MmRendererAudioRoleControl(QObject *parent)
- : QAudioRoleControl(parent)
- , m_role(QAudio::UnknownRole)
-{
-}
-
-QAudio::Role MmRendererAudioRoleControl::audioRole() const
-{
- return m_role;
-}
-
-void MmRendererAudioRoleControl::setAudioRole(QAudio::Role role)
-{
- if (m_role != role) {
- m_role = role;
- emit audioRoleChanged(m_role);
- }
-}
-
-QList<QAudio::Role> MmRendererAudioRoleControl::supportedAudioRoles() const
-{
- return qnxSupportedAudioRoles();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/mediaplayer/mmrendereraudiorolecontrol.h b/src/plugins/qnx/mediaplayer/mmrendereraudiorolecontrol.h
deleted file mode 100644
index d0d2165eb..000000000
--- a/src/plugins/qnx/mediaplayer/mmrendereraudiorolecontrol.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 QNX Software Systems. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 MMRENDERERAUDIOROLECONTROL_H
-#define MMRENDERERAUDIOROLECONTROL_H
-
-#include <qaudiorolecontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class MmRendererAudioRoleControl : public QAudioRoleControl
-{
- Q_OBJECT
-public:
- explicit MmRendererAudioRoleControl(QObject *parent = 0);
-
- QAudio::Role audioRole() const override;
- void setAudioRole(QAudio::Role role) override;
-
- QList<QAudio::Role> supportedAudioRoles() const override;
-
-private:
- QAudio::Role m_role;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/mediaplayer/mmrenderercustomaudiorolecontrol.cpp b/src/plugins/qnx/mediaplayer/mmrenderercustomaudiorolecontrol.cpp
deleted file mode 100644
index c8971d41c..000000000
--- a/src/plugins/qnx/mediaplayer/mmrenderercustomaudiorolecontrol.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 QNX Software Systems. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "mmrenderercustomaudiorolecontrol.h"
-#include "mmrendererutil.h"
-
-QT_BEGIN_NAMESPACE
-
-MmRendererCustomAudioRoleControl::MmRendererCustomAudioRoleControl(QObject *parent)
- : QCustomAudioRoleControl(parent)
-{
-}
-
-QString MmRendererCustomAudioRoleControl::customAudioRole() const
-{
- return m_role;
-}
-
-void MmRendererCustomAudioRoleControl::setCustomAudioRole(const QString &role)
-{
- if (m_role != role) {
- m_role = role;
- emit customAudioRoleChanged(m_role);
- }
-}
-
-QStringList MmRendererCustomAudioRoleControl::supportedCustomAudioRoles() const
-{
- return QStringList();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/mediaplayer/mmrenderercustomaudiorolecontrol.h b/src/plugins/qnx/mediaplayer/mmrenderercustomaudiorolecontrol.h
deleted file mode 100644
index ff16f9355..000000000
--- a/src/plugins/qnx/mediaplayer/mmrenderercustomaudiorolecontrol.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 QNX Software Systems. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 MMRENDERERCUSTOMAUDIOROLECONTROL_H
-#define MMRENDERERCUSTOMAUDIOROLECONTROL_H
-
-#include <qcustomaudiorolecontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class MmRendererCustomAudioRoleControl : public QCustomAudioRoleControl
-{
- Q_OBJECT
-public:
- explicit MmRendererCustomAudioRoleControl(QObject *parent = 0);
-
- QString customAudioRole() const Q_DECL_OVERRIDE;
- void setCustomAudioRole(const QString &role) Q_DECL_OVERRIDE;
-
- QStringList supportedCustomAudioRoles() const Q_DECL_OVERRIDE;
-
-private:
- QString m_role;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp b/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp
deleted file mode 100644
index fc48ed818..000000000
--- a/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp
+++ /dev/null
@@ -1,653 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "mmrendereraudiorolecontrol.h"
-#include "mmrenderercustomaudiorolecontrol.h"
-#include "mmrenderermediaplayercontrol.h"
-#include "mmrenderermetadatareadercontrol.h"
-#include "mmrendererplayervideorenderercontrol.h"
-#include "mmrendererutil.h"
-#include "mmrenderervideowindowcontrol.h"
-#include <QtCore/qabstracteventdispatcher.h>
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qdir.h>
-#include <QtCore/qfileinfo.h>
-#include <QtCore/quuid.h>
-#include <mm/renderer.h>
-
-#include <errno.h>
-#include <sys/strm.h>
-#include <sys/stat.h>
-
-QT_BEGIN_NAMESPACE
-
-static int idCounter = 0;
-
-MmRendererMediaPlayerControl::MmRendererMediaPlayerControl(QObject *parent)
- : QMediaPlayerControl(parent),
- m_context(0),
- m_id(-1),
- m_connection(0),
- m_audioId(-1),
- m_state(QMediaPlayer::StoppedState),
- m_volume(100),
- m_muted(false),
- m_rate(1),
- m_position(0),
- m_mediaStatus(QMediaPlayer::NoMedia),
- m_playAfterMediaLoaded(false),
- m_inputAttached(false),
- m_bufferLevel(0)
-{
- m_loadingTimer.setSingleShot(true);
- m_loadingTimer.setInterval(0);
- connect(&m_loadingTimer, SIGNAL(timeout()), this, SLOT(continueLoadMedia()));
- QCoreApplication::eventDispatcher()->installNativeEventFilter(this);
-}
-
-void MmRendererMediaPlayerControl::destroy()
-{
- stop();
- detach();
- closeConnection();
- QCoreApplication::eventDispatcher()->removeNativeEventFilter(this);
-}
-
-void MmRendererMediaPlayerControl::openConnection()
-{
- m_connection = mmr_connect(NULL);
- if (!m_connection) {
- emitPError("Unable to connect to the multimedia renderer");
- return;
- }
-
- m_id = idCounter++;
- m_contextName = QString("MmRendererMediaPlayerControl_%1_%2").arg(m_id)
- .arg(QCoreApplication::applicationPid());
- m_context = mmr_context_create(m_connection, m_contextName.toLatin1(),
- 0, S_IRWXU|S_IRWXG|S_IRWXO);
- if (!m_context) {
- emitPError("Unable to create context");
- closeConnection();
- return;
- }
-
- startMonitoring();
-}
-
-void MmRendererMediaPlayerControl::handleMmStopped()
-{
- // Only react to stop events that happen when the end of the stream is reached and
- // playback is stopped because of this.
- // Ignore other stop event sources, such as calling mmr_stop() ourselves.
- if (m_state != QMediaPlayer::StoppedState) {
- setMediaStatus(QMediaPlayer::EndOfMedia);
- stopInternal(IgnoreMmRenderer);
- }
-}
-
-void MmRendererMediaPlayerControl::handleMmSuspend(const QString &reason)
-{
- if (m_state == QMediaPlayer::StoppedState)
- return;
-
- Q_UNUSED(reason);
- setMediaStatus(QMediaPlayer::StalledMedia);
-}
-
-void MmRendererMediaPlayerControl::handleMmSuspendRemoval(const QString &bufferStatus)
-{
- if (m_state == QMediaPlayer::StoppedState)
- return;
-
- if (bufferStatus == QLatin1String("buffering"))
- setMediaStatus(QMediaPlayer::BufferingMedia);
- else
- setMediaStatus(QMediaPlayer::BufferedMedia);
-}
-
-void MmRendererMediaPlayerControl::handleMmPause()
-{
- if (m_state == QMediaPlayer::PlayingState) {
- setState(QMediaPlayer::PausedState);
- }
-}
-
-void MmRendererMediaPlayerControl::handleMmPlay()
-{
- if (m_state == QMediaPlayer::PausedState) {
- setState(QMediaPlayer::PlayingState);
- }
-}
-
-void MmRendererMediaPlayerControl::closeConnection()
-{
- stopMonitoring();
-
- if (m_context) {
- mmr_context_destroy(m_context);
- m_context = 0;
- m_contextName.clear();
- }
-
- if (m_connection) {
- mmr_disconnect(m_connection);
- m_connection = 0;
- }
-}
-
-QByteArray MmRendererMediaPlayerControl::resourcePathForUrl(const QUrl &url)
-{
- // If this is a local file, mmrenderer expects the file:// prefix and an absolute path.
- // We treat URLs without scheme as local files, most likely someone just forgot to set the
- // file:// prefix when constructing the URL.
- if (url.isLocalFile() || url.scheme().isEmpty()) {
- QString relativeFilePath;
- if (!url.scheme().isEmpty())
- relativeFilePath = url.toLocalFile();
- else
- relativeFilePath = url.path();
- const QFileInfo fileInfo(relativeFilePath);
- return QFile::encodeName(QStringLiteral("file://") + fileInfo.absoluteFilePath());
-
- // HTTP or similar URL
- } else {
- return url.toEncoded();
- }
-}
-
-void MmRendererMediaPlayerControl::attach()
-{
- // Should only be called in detached state
- Q_ASSERT(m_audioId == -1 && !m_inputAttached);
-
- if (m_media.isNull() || !m_context) {
- setMediaStatus(QMediaPlayer::NoMedia);
- return;
- }
-
- resetMonitoring();
-
- if (m_videoRendererControl)
- m_videoRendererControl->attachDisplay(m_context);
-
- if (m_videoWindowControl)
- m_videoWindowControl->attachDisplay(m_context);
-
- const QByteArray defaultAudioDevice = qgetenv("QQNX_RENDERER_DEFAULT_AUDIO_SINK");
- m_audioId = mmr_output_attach(m_context, defaultAudioDevice.isEmpty() ? "audio:default" : defaultAudioDevice.constData(), "audio");
- if (m_audioId == -1) {
- emitMmError("mmr_output_attach() for audio failed");
- return;
- }
-
- if (m_audioId != -1 && m_audioRoleControl) {
- QAudio::Role audioRole = m_audioRoleControl->audioRole();
- QString audioType = (audioRole == QAudio::CustomRole && m_customAudioRoleControl)
- ? m_customAudioRoleControl->customAudioRole()
- : qnxAudioType(audioRole);
- QByteArray latin1AudioType = audioType.toLatin1();
- if (!audioType.isEmpty() && latin1AudioType == audioType) {
- strm_dict_t *dict = strm_dict_new();
- dict = strm_dict_set(dict, "audio_type", latin1AudioType.constData());
- if (mmr_output_parameters(m_context, m_audioId, dict) != 0)
- emitMmError("mmr_output_parameters: Setting audio_type failed");
- }
- }
-
- const QByteArray resourcePath = resourcePathForUrl(m_media.request().url());
- if (resourcePath.isEmpty()) {
- detach();
- return;
- }
-
- if (mmr_input_attach(m_context, resourcePath.constData(), "track") != 0) {
- emitMmError(QStringLiteral("mmr_input_attach() failed for ") + QString(resourcePath));
- setMediaStatus(QMediaPlayer::InvalidMedia);
- detach();
- return;
- }
-
- m_inputAttached = true;
- setMediaStatus(QMediaPlayer::LoadedMedia);
-
- // mm-renderer has buffer properties "status" and "level"
- // QMediaPlayer's buffer status maps to mm-renderer's buffer level
- m_bufferLevel = 0;
- emit bufferStatusChanged(m_bufferLevel);
-}
-
-void MmRendererMediaPlayerControl::detach()
-{
- if (m_context) {
- if (m_inputAttached) {
- mmr_input_detach(m_context);
- m_inputAttached = false;
- }
- if (m_videoRendererControl)
- m_videoRendererControl->detachDisplay();
- if (m_videoWindowControl)
- m_videoWindowControl->detachDisplay();
- if (m_audioId != -1 && m_context) {
- mmr_output_detach(m_context, m_audioId);
- m_audioId = -1;
- }
- }
-
- m_loadingTimer.stop();
-}
-
-QMediaPlayer::State MmRendererMediaPlayerControl::state() const
-{
- return m_state;
-}
-
-QMediaPlayer::MediaStatus MmRendererMediaPlayerControl::mediaStatus() const
-{
- return m_mediaStatus;
-}
-
-qint64 MmRendererMediaPlayerControl::duration() const
-{
- return m_metaData.duration();
-}
-
-qint64 MmRendererMediaPlayerControl::position() const
-{
- return m_position;
-}
-
-void MmRendererMediaPlayerControl::setPosition(qint64 position)
-{
- if (m_position != position) {
- m_position = position;
-
- // Don't update in stopped state, it would not have any effect. Instead, the position is
- // updated in play().
- if (m_state != QMediaPlayer::StoppedState)
- setPositionInternal(m_position);
-
- emit positionChanged(m_position);
- }
-}
-
-int MmRendererMediaPlayerControl::volume() const
-{
- return m_volume;
-}
-
-void MmRendererMediaPlayerControl::setVolumeInternal(int newVolume)
-{
- if (!m_context)
- return;
-
- newVolume = qBound(0, newVolume, 100);
- if (m_audioId != -1) {
- strm_dict_t * dict = strm_dict_new();
- dict = strm_dict_set(dict, "volume", QString::number(newVolume).toLatin1());
- if (mmr_output_parameters(m_context, m_audioId, dict) != 0)
- emitMmError("mmr_output_parameters: Setting volume failed");
- }
-}
-
-void MmRendererMediaPlayerControl::setPlaybackRateInternal(qreal rate)
-{
- if (!m_context)
- return;
-
- const int mmRate = rate * 1000;
- if (mmr_speed_set(m_context, mmRate) != 0)
- emitMmError("mmr_speed_set failed");
-}
-
-void MmRendererMediaPlayerControl::setPositionInternal(qint64 position)
-{
- if (!m_context)
- return;
-
- if (m_metaData.isSeekable()) {
- if (mmr_seek(m_context, QString::number(position).toLatin1()) != 0)
- emitMmError("Seeking failed");
- }
-}
-
-void MmRendererMediaPlayerControl::setMediaStatus(QMediaPlayer::MediaStatus status)
-{
- if (m_mediaStatus != status) {
- m_mediaStatus = status;
- emit mediaStatusChanged(m_mediaStatus);
- }
-}
-
-void MmRendererMediaPlayerControl::setState(QMediaPlayer::State state)
-{
- if (m_state != state) {
- if (m_videoRendererControl) {
- if (state == QMediaPlayer::PausedState || state == QMediaPlayer::StoppedState) {
- m_videoRendererControl->pause();
- } else if ((state == QMediaPlayer::PlayingState)
- && (m_state == QMediaPlayer::PausedState
- || m_state == QMediaPlayer::StoppedState)) {
- m_videoRendererControl->resume();
- }
- }
-
- m_state = state;
- emit stateChanged(m_state);
- }
-}
-
-void MmRendererMediaPlayerControl::stopInternal(StopCommand stopCommand)
-{
- resetMonitoring();
- setPosition(0);
-
- if (m_state != QMediaPlayer::StoppedState) {
-
- if (stopCommand == StopMmRenderer) {
- mmr_stop(m_context);
- }
-
- setState(QMediaPlayer::StoppedState);
- }
-}
-
-void MmRendererMediaPlayerControl::setVolume(int volume)
-{
- const int newVolume = qBound(0, volume, 100);
- if (m_volume != newVolume) {
- m_volume = newVolume;
- if (!m_muted)
- setVolumeInternal(m_volume);
- emit volumeChanged(m_volume);
- }
-}
-
-bool MmRendererMediaPlayerControl::isMuted() const
-{
- return m_muted;
-}
-
-void MmRendererMediaPlayerControl::setMuted(bool muted)
-{
- if (m_muted != muted) {
- m_muted = muted;
- setVolumeInternal(muted ? 0 : m_volume);
- emit mutedChanged(muted);
- }
-}
-
-int MmRendererMediaPlayerControl::bufferStatus() const
-{
- // mm-renderer has buffer properties "status" and "level"
- // QMediaPlayer's buffer status maps to mm-renderer's buffer level
- return m_bufferLevel;
-}
-
-bool MmRendererMediaPlayerControl::isAudioAvailable() const
-{
- return m_metaData.hasAudio();
-}
-
-bool MmRendererMediaPlayerControl::isVideoAvailable() const
-{
- return m_metaData.hasVideo();
-}
-
-bool MmRendererMediaPlayerControl::isSeekable() const
-{
- return m_metaData.isSeekable();
-}
-
-QMediaTimeRange MmRendererMediaPlayerControl::availablePlaybackRanges() const
-{
- // We can't get this information from the mmrenderer API yet, so pretend we can seek everywhere
- return QMediaTimeRange(0, m_metaData.duration());
-}
-
-qreal MmRendererMediaPlayerControl::playbackRate() const
-{
- return m_rate;
-}
-
-void MmRendererMediaPlayerControl::setPlaybackRate(qreal rate)
-{
- if (m_rate != rate) {
- m_rate = rate;
- setPlaybackRateInternal(m_rate);
- emit playbackRateChanged(m_rate);
- }
-}
-
-QMediaContent MmRendererMediaPlayerControl::media() const
-{
- return m_media;
-}
-
-const QIODevice *MmRendererMediaPlayerControl::mediaStream() const
-{
- // Always 0, we don't support QIODevice streams
- return 0;
-}
-
-void MmRendererMediaPlayerControl::setMedia(const QMediaContent &media, QIODevice *stream)
-{
- Q_UNUSED(stream); // not supported
-
- stop();
- detach();
-
- m_media = media;
- emit mediaChanged(m_media);
-
- // Slight hack: With MediaPlayer QtQuick elements that have autoPlay set to true, playback
- // would start before the QtQuick canvas is propagated to all elements, and therefore our
- // video output would not work. Therefore, delay actually playing the media a bit so that the
- // canvas is ready.
- // The mmrenderer doesn't allow to attach video outputs after playing has started, otherwise
- // this would be unnecessary.
- if (!m_media.isNull()) {
- setMediaStatus(QMediaPlayer::LoadingMedia);
- m_loadingTimer.start(); // singleshot timer to continueLoadMedia()
- } else {
- continueLoadMedia(); // still needed, as it will update the media status and clear metadata
- }
-}
-
-void MmRendererMediaPlayerControl::continueLoadMedia()
-{
- updateMetaData(nullptr);
- attach();
- if (m_playAfterMediaLoaded)
- play();
-}
-
-MmRendererVideoWindowControl *MmRendererMediaPlayerControl::videoWindowControl() const
-{
- return m_videoWindowControl;
-}
-
-void MmRendererMediaPlayerControl::play()
-{
- if (m_playAfterMediaLoaded)
- m_playAfterMediaLoaded = false;
-
- // No-op if we are already playing, except if we were called from continueLoadMedia(), in which
- // case m_playAfterMediaLoaded is true (hence the 'else').
- else if (m_state == QMediaPlayer::PlayingState)
- return;
-
- if (m_mediaStatus == QMediaPlayer::LoadingMedia) {
-
- // State changes are supposed to be synchronous
- setState(QMediaPlayer::PlayingState);
-
- // Defer playing to later, when the timer triggers continueLoadMedia()
- m_playAfterMediaLoaded = true;
- return;
- }
-
- // Un-pause the state when it is paused
- if (m_state == QMediaPlayer::PausedState) {
- setPlaybackRateInternal(m_rate);
- setState(QMediaPlayer::PlayingState);
- return;
- }
-
- if (m_media.isNull() || !m_connection || !m_context || m_audioId == -1) {
- setState(QMediaPlayer::StoppedState);
- return;
- }
-
- if (m_mediaStatus == QMediaPlayer::EndOfMedia)
- m_position = 0;
-
- resetMonitoring();
- setPositionInternal(m_position);
- setVolumeInternal(m_muted ? 0 : m_volume);
- setPlaybackRateInternal(m_rate);
-
- if (mmr_play(m_context) != 0) {
- setState(QMediaPlayer::StoppedState);
- emitMmError("mmr_play() failed");
- return;
- }
-
- setState( QMediaPlayer::PlayingState);
-}
-
-void MmRendererMediaPlayerControl::pause()
-{
- if (m_state == QMediaPlayer::PlayingState) {
- setPlaybackRateInternal(0);
- setState(QMediaPlayer::PausedState);
- }
-}
-
-void MmRendererMediaPlayerControl::stop()
-{
- stopInternal(StopMmRenderer);
-}
-
-MmRendererPlayerVideoRendererControl *MmRendererMediaPlayerControl::videoRendererControl() const
-{
- return m_videoRendererControl;
-}
-
-void MmRendererMediaPlayerControl::setVideoRendererControl(MmRendererPlayerVideoRendererControl *videoControl)
-{
- m_videoRendererControl = videoControl;
-}
-
-void MmRendererMediaPlayerControl::setVideoWindowControl(MmRendererVideoWindowControl *videoControl)
-{
- m_videoWindowControl = videoControl;
-}
-
-void MmRendererMediaPlayerControl::setMetaDataReaderControl(MmRendererMetaDataReaderControl *metaDataReaderControl)
-{
- m_metaDataReaderControl = metaDataReaderControl;
-}
-
-void MmRendererMediaPlayerControl::setAudioRoleControl(MmRendererAudioRoleControl *audioRoleControl)
-{
- m_audioRoleControl = audioRoleControl;
-}
-
-void MmRendererMediaPlayerControl::setCustomAudioRoleControl(MmRendererCustomAudioRoleControl *customAudioRoleControl)
-{
- m_customAudioRoleControl = customAudioRoleControl;
-}
-
-void MmRendererMediaPlayerControl::setMmPosition(qint64 newPosition)
-{
- if (newPosition != 0 && newPosition != m_position) {
- m_position = newPosition;
- emit positionChanged(m_position);
- }
-}
-
-void MmRendererMediaPlayerControl::setMmBufferStatus(const QString &bufferStatus)
-{
- if (bufferStatus == QLatin1String("buffering"))
- setMediaStatus(QMediaPlayer::BufferingMedia);
- else if (bufferStatus == QLatin1String("playing"))
- setMediaStatus(QMediaPlayer::BufferedMedia);
- // ignore "idle" buffer status
-}
-
-void MmRendererMediaPlayerControl::setMmBufferLevel(int level, int capacity)
-{
- m_bufferLevel = capacity == 0 ? 0 : level / static_cast<float>(capacity) * 100.0f;
- m_bufferLevel = qBound(0, m_bufferLevel, 100);
- emit bufferStatusChanged(m_bufferLevel);
-}
-
-void MmRendererMediaPlayerControl::updateMetaData(const strm_dict *dict)
-{
- m_metaData.update(dict);
-
- if (m_videoWindowControl)
- m_videoWindowControl->setMetaData(m_metaData);
-
- if (m_metaDataReaderControl)
- m_metaDataReaderControl->setMetaData(m_metaData);
-
- emit durationChanged(m_metaData.duration());
- emit audioAvailableChanged(m_metaData.hasAudio());
- emit videoAvailableChanged(m_metaData.hasVideo());
- emit availablePlaybackRangesChanged(availablePlaybackRanges());
- emit seekableChanged(m_metaData.isSeekable());
-}
-
-void MmRendererMediaPlayerControl::emitMmError(const QString &msg)
-{
- int errorCode = MMR_ERROR_NONE;
- const QString errorMessage = mmErrorMessage(msg, m_context, &errorCode);
- qDebug() << errorMessage;
- emit error(errorCode, errorMessage);
-}
-
-void MmRendererMediaPlayerControl::emitPError(const QString &msg)
-{
- const QString errorMessage = QString("%1: %2").arg(msg).arg(strerror(errno));
- qDebug() << errorMessage;
- emit error(errno, errorMessage);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.h b/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.h
deleted file mode 100644
index 3426ef2f2..000000000
--- a/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.h
+++ /dev/null
@@ -1,180 +0,0 @@
-/****************************************************************************
-**
-** 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 MMRENDERERMEDIAPLAYERCONTROL_H
-#define MMRENDERERMEDIAPLAYERCONTROL_H
-
-#include "mmrenderermetadata.h"
-#include <qmediaplayercontrol.h>
-#include <QtCore/qabstractnativeeventfilter.h>
-#include <QtCore/qpointer.h>
-#include <QtCore/qtimer.h>
-
-typedef struct mmr_connection mmr_connection_t;
-typedef struct mmr_context mmr_context_t;
-typedef struct mmrenderer_monitor mmrenderer_monitor_t;
-typedef struct strm_dict strm_dict_t;
-
-QT_BEGIN_NAMESPACE
-
-class MmRendererAudioRoleControl;
-class MmRendererCustomAudioRoleControl;
-class MmRendererMetaDataReaderControl;
-class MmRendererPlayerVideoRendererControl;
-class MmRendererVideoWindowControl;
-
-class MmRendererMediaPlayerControl : public QMediaPlayerControl, public QAbstractNativeEventFilter
-{
- Q_OBJECT
-public:
- explicit MmRendererMediaPlayerControl(QObject *parent = 0);
-
- QMediaPlayer::State state() const override;
-
- QMediaPlayer::MediaStatus mediaStatus() const override;
-
- qint64 duration() const override;
-
- qint64 position() const override;
- void setPosition(qint64 position) override;
-
- int volume() const override;
- void setVolume(int volume) override;
-
- bool isMuted() const override;
- void setMuted(bool muted) override;
-
- int bufferStatus() const override;
-
- bool isAudioAvailable() const override;
- bool isVideoAvailable() const override;
-
- bool isSeekable() const override;
-
- QMediaTimeRange availablePlaybackRanges() const override;
-
- qreal playbackRate() const override;
- void setPlaybackRate(qreal rate) override;
-
- QMediaContent media() const override;
- const QIODevice *mediaStream() const override;
- void setMedia(const QMediaContent &media, QIODevice *stream) override;
-
- void play() override;
- void pause() override;
- void stop() override;
-
- MmRendererPlayerVideoRendererControl *videoRendererControl() const;
- void setVideoRendererControl(MmRendererPlayerVideoRendererControl *videoControl);
-
- MmRendererVideoWindowControl *videoWindowControl() const;
- void setVideoWindowControl(MmRendererVideoWindowControl *videoControl);
- void setMetaDataReaderControl(MmRendererMetaDataReaderControl *metaDataReaderControl);
- void setAudioRoleControl(MmRendererAudioRoleControl *audioRoleControl);
- void setCustomAudioRoleControl(MmRendererCustomAudioRoleControl *customAudioRoleControl);
-
-protected:
- virtual void startMonitoring() = 0;
- virtual void stopMonitoring() = 0;
- virtual void resetMonitoring() = 0;
-
- void openConnection();
- void emitMmError(const QString &msg);
- void emitPError(const QString &msg);
- void setMmPosition(qint64 newPosition);
- void setMmBufferStatus(const QString &bufferStatus);
- void setMmBufferLevel(int level, int capacity);
- void handleMmStopped();
- void handleMmSuspend(const QString &reason);
- void handleMmSuspendRemoval(const QString &bufferStatus);
- void handleMmPause();
- void handleMmPlay();
- void updateMetaData(const strm_dict_t *dict);
-
- // must be called from subclass dtors (calls virtual function stopMonitoring())
- void destroy();
-
- mmr_context_t *m_context;
- int m_id;
- QString m_contextName;
-
-private Q_SLOTS:
- void continueLoadMedia();
-
-private:
- QByteArray resourcePathForUrl(const QUrl &url);
- void closeConnection();
- void attach();
- void detach();
-
- // All these set the specified value to the backend, but neither emit changed signals
- // nor change the member value.
- void setVolumeInternal(int newVolume);
- void setPlaybackRateInternal(qreal rate);
- void setPositionInternal(qint64 position);
-
- void setMediaStatus(QMediaPlayer::MediaStatus status);
- void setState(QMediaPlayer::State state);
-
- enum StopCommand { StopMmRenderer, IgnoreMmRenderer };
- void stopInternal(StopCommand stopCommand);
-
- QMediaContent m_media;
- mmr_connection_t *m_connection;
- int m_audioId;
- QMediaPlayer::State m_state;
- int m_volume;
- bool m_muted;
- qreal m_rate;
- QPointer<MmRendererPlayerVideoRendererControl> m_videoRendererControl;
- QPointer<MmRendererVideoWindowControl> m_videoWindowControl;
- QPointer<MmRendererMetaDataReaderControl> m_metaDataReaderControl;
- QPointer<MmRendererAudioRoleControl> m_audioRoleControl;
- QPointer<MmRendererCustomAudioRoleControl> m_customAudioRoleControl;
- MmRendererMetaData m_metaData;
- qint64 m_position;
- QMediaPlayer::MediaStatus m_mediaStatus;
- bool m_playAfterMediaLoaded;
- bool m_inputAttached;
- int m_bufferLevel;
- QTimer m_loadingTimer;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/mediaplayer/mmrenderermediaplayerservice.cpp b/src/plugins/qnx/mediaplayer/mmrenderermediaplayerservice.cpp
deleted file mode 100644
index 190cb8b80..000000000
--- a/src/plugins/qnx/mediaplayer/mmrenderermediaplayerservice.cpp
+++ /dev/null
@@ -1,164 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "mmrenderermediaplayerservice.h"
-
-#include "mmrendereraudiorolecontrol.h"
-#include "mmrenderercustomaudiorolecontrol.h"
-#include "mmrenderermediaplayercontrol.h"
-#include "mmrenderermetadatareadercontrol.h"
-#include "mmrendererplayervideorenderercontrol.h"
-#include "mmrendererutil.h"
-#include "mmrenderervideowindowcontrol.h"
-
-#include "mmreventmediaplayercontrol.h"
-
-QT_BEGIN_NAMESPACE
-
-MmRendererMediaPlayerService::MmRendererMediaPlayerService(QObject *parent)
- : QMediaService(parent),
- m_videoRendererControl(0),
- m_videoWindowControl(0),
- m_mediaPlayerControl(0),
- m_metaDataReaderControl(0),
- m_appHasDrmPermission(false),
- m_appHasDrmPermissionChecked(false)
-{
-}
-
-MmRendererMediaPlayerService::~MmRendererMediaPlayerService()
-{
- // Someone should have called releaseControl(), but better be safe
- delete m_videoRendererControl;
- delete m_videoWindowControl;
- delete m_mediaPlayerControl;
- delete m_metaDataReaderControl;
- delete m_audioRoleControl;
- delete m_customAudioRoleControl;
-}
-
-QMediaControl *MmRendererMediaPlayerService::requestControl(const char *name)
-{
- if (qstrcmp(name, QMediaPlayerControl_iid) == 0) {
- if (!m_mediaPlayerControl) {
- m_mediaPlayerControl = new MmrEventMediaPlayerControl;
- updateControls();
- }
- return m_mediaPlayerControl;
- } else if (qstrcmp(name, QMetaDataReaderControl_iid) == 0) {
- if (!m_metaDataReaderControl) {
- m_metaDataReaderControl = new MmRendererMetaDataReaderControl();
- updateControls();
- }
- return m_metaDataReaderControl;
- } else if (qstrcmp(name, QAudioRoleControl_iid) == 0) {
- if (!m_audioRoleControl) {
- m_audioRoleControl = new MmRendererAudioRoleControl();
- updateControls();
- }
- return m_audioRoleControl;
- } else if (qstrcmp(name, QCustomAudioRoleControl_iid) == 0) {
- if (!m_customAudioRoleControl) {
- m_customAudioRoleControl = new MmRendererCustomAudioRoleControl();
- updateControls();
- }
- return m_customAudioRoleControl;
- } else if (qstrcmp(name, QVideoRendererControl_iid) == 0) {
- if (!m_appHasDrmPermissionChecked) {
- m_appHasDrmPermission = checkForDrmPermission();
- m_appHasDrmPermissionChecked = true;
- }
-
- if (m_appHasDrmPermission) {
- // When the application wants to play back DRM secured media, we can't use
- // the QVideoRendererControl, because we won't have access to the pixel data
- // in this case.
- return 0;
- }
-
- if (!m_videoRendererControl) {
- m_videoRendererControl = new MmRendererPlayerVideoRendererControl();
- updateControls();
- }
- return m_videoRendererControl;
- } else if (qstrcmp(name, QVideoWindowControl_iid) == 0) {
- if (!m_videoWindowControl) {
- m_videoWindowControl = new MmRendererVideoWindowControl();
- updateControls();
- }
- return m_videoWindowControl;
- }
- return 0;
-}
-
-void MmRendererMediaPlayerService::releaseControl(QMediaControl *control)
-{
- if (control == m_videoRendererControl)
- m_videoRendererControl = 0;
- if (control == m_videoWindowControl)
- m_videoWindowControl = 0;
- if (control == m_mediaPlayerControl)
- m_mediaPlayerControl = 0;
- if (control == m_metaDataReaderControl)
- m_metaDataReaderControl = 0;
- if (control == m_audioRoleControl)
- m_audioRoleControl = 0;
- if (control == m_customAudioRoleControl)
- m_customAudioRoleControl = 0;
- delete control;
-}
-
-void MmRendererMediaPlayerService::updateControls()
-{
- if (m_videoRendererControl && m_mediaPlayerControl)
- m_mediaPlayerControl->setVideoRendererControl(m_videoRendererControl);
-
- if (m_videoWindowControl && m_mediaPlayerControl)
- m_mediaPlayerControl->setVideoWindowControl(m_videoWindowControl);
-
- if (m_metaDataReaderControl && m_mediaPlayerControl)
- m_mediaPlayerControl->setMetaDataReaderControl(m_metaDataReaderControl);
-
- if (m_audioRoleControl && m_mediaPlayerControl)
- m_mediaPlayerControl->setAudioRoleControl(m_audioRoleControl);
-
- if (m_customAudioRoleControl && m_mediaPlayerControl)
- m_mediaPlayerControl->setCustomAudioRoleControl(m_customAudioRoleControl);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/mediaplayer/mmrenderermediaplayerservice.h b/src/plugins/qnx/mediaplayer/mmrenderermediaplayerservice.h
deleted file mode 100644
index ab3054af5..000000000
--- a/src/plugins/qnx/mediaplayer/mmrenderermediaplayerservice.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/****************************************************************************
-**
-** 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 MMRENDERERMEDIAPLAYERSERVICE_H
-#define MMRENDERERMEDIAPLAYERSERVICE_H
-
-#include <qmediaservice.h>
-#include <QtCore/qpointer.h>
-
-QT_BEGIN_NAMESPACE
-
-class MmRendererAudioRoleControl;
-class MmRendererCustomAudioRoleControl;
-class MmRendererMediaPlayerControl;
-class MmRendererMetaDataReaderControl;
-class MmRendererPlayerVideoRendererControl;
-class MmRendererVideoWindowControl;
-
-class MmRendererMediaPlayerService : public QMediaService
-{
- Q_OBJECT
-public:
- explicit MmRendererMediaPlayerService(QObject *parent = 0);
- ~MmRendererMediaPlayerService();
-
- QMediaControl *requestControl(const char *name) override;
- void releaseControl(QMediaControl *control) override;
-
-private:
- void updateControls();
-
- QPointer<MmRendererPlayerVideoRendererControl> m_videoRendererControl;
- QPointer<MmRendererVideoWindowControl> m_videoWindowControl;
- QPointer<MmRendererMediaPlayerControl> m_mediaPlayerControl;
- QPointer<MmRendererMetaDataReaderControl> m_metaDataReaderControl;
- QPointer<MmRendererAudioRoleControl> m_audioRoleControl;
- QPointer<MmRendererCustomAudioRoleControl> m_customAudioRoleControl;
-
- bool m_appHasDrmPermission : 1;
- bool m_appHasDrmPermissionChecked : 1;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/mediaplayer/mmrenderermetadata.cpp b/src/plugins/qnx/mediaplayer/mmrenderermetadata.cpp
deleted file mode 100644
index a8b92c267..000000000
--- a/src/plugins/qnx/mediaplayer/mmrenderermetadata.cpp
+++ /dev/null
@@ -1,298 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "mmrenderermetadata.h"
-
-#include <QtCore/qdebug.h>
-#include <QtCore/qfile.h>
-#include <QtCore/qstringlist.h>
-
-#include <mm/renderer/events.h>
-#include <sys/neutrino.h>
-#include <sys/strm.h>
-
-static const char *strm_string_getx(const strm_string_t *sstr, const char *defaultValue)
-{
- return sstr ? strm_string_get(sstr) : defaultValue;
-}
-
-#if _NTO_VERSION < 700
-static strm_dict_t *mmr_metadata_split(strm_dict_t const *, const char *, unsigned)
-{
- return nullptr;
-}
-#endif
-
-QT_BEGIN_NAMESPACE
-
-MmRendererMetaData::MmRendererMetaData()
-{
- clear();
-}
-
-static const char * titleKey = "md_title_name";
-static const char * artistKey = "md_title_artist";
-static const char * commentKey = "md_title_comment";
-static const char * genreKey = "md_title_genre";
-static const char * yearKey = "md_title_year";
-static const char * durationKey = "md_title_duration";
-static const char * bitRateKey = "md_title_bitrate";
-static const char * sampleKey = "md_title_samplerate";
-static const char * albumKey = "md_title_album";
-static const char * trackKey = "md_title_track";
-static const char * widthKey = "md_video_width";
-static const char * heightKey = "md_video_height";
-static const char * mediaTypeKey = "md_title_mediatype";
-static const char * pixelWidthKey = "md_video_pixel_width";
-static const char * pixelHeightKey = "md_video_pixel_height";
-static const char * seekableKey = "md_title_seekable";
-static const char * trackSampleKey = "sample_rate";
-static const char * trackBitRateKey = "bitrate";
-static const char * trackWidthKey = "width";
-static const char * trackHeightKey = "height";
-static const char * trackPixelWidthKey = "pixel_width";
-static const char * trackPixelHeightKey = "pixel_height";
-
-static const int mediaTypeAudioFlag = 4;
-static const int mediaTypeVideoFlag = 2;
-
-bool MmRendererMetaData::update(const strm_dict_t *dict)
-{
- if (!dict) {
- clear();
- return true;
- }
-
- const strm_string_t *value;
-
- value = strm_dict_find_rstr(dict, durationKey);
- m_duration = QByteArray(strm_string_getx(value, "0")).toLongLong();
-
- value = strm_dict_find_rstr(dict, mediaTypeKey);
- m_mediaType = QByteArray(strm_string_getx(value, "-1")).toInt();
-
- value = strm_dict_find_rstr(dict, titleKey);
- m_title = QString::fromLatin1(QByteArray(strm_string_getx(value, nullptr)));
-
- value = strm_dict_find_rstr(dict, seekableKey);
- m_seekable = (strcmp(strm_string_getx(value, "1"), "0") != 0);
-
- value = strm_dict_find_rstr(dict, artistKey);
- m_artist = QString::fromLatin1(QByteArray(strm_string_getx(value, nullptr)));
-
- value = strm_dict_find_rstr(dict, commentKey);
- m_comment = QString::fromLatin1(QByteArray(strm_string_getx(value, nullptr)));
-
- value = strm_dict_find_rstr(dict, genreKey);
- m_genre = QString::fromLatin1(QByteArray(strm_string_getx(value, nullptr)));
-
- value = strm_dict_find_rstr(dict, yearKey);
- m_year = QByteArray(strm_string_getx(value, "0")).toInt();
-
- value = strm_dict_find_rstr(dict, albumKey);
- m_album = QString::fromLatin1(QByteArray(strm_string_getx(value, nullptr)));
-
- value = strm_dict_find_rstr(dict, trackKey);
- m_track = QByteArray(strm_string_getx(value, "0")).toInt();
-
- strm_dict_t *at = mmr_metadata_split(dict, "audio", 0);
- if (at) {
- value = strm_dict_find_rstr(at, trackSampleKey);
- m_sampleRate = QByteArray(strm_string_getx(value, "0")).toInt();
-
- value = strm_dict_find_rstr(at, trackBitRateKey);
- m_audioBitRate = QByteArray(strm_string_getx(value, "0")).toInt();
-
- strm_dict_destroy(at);
- } else {
- value = strm_dict_find_rstr(dict, sampleKey);
- m_sampleRate = QByteArray(strm_string_getx(value, "0")).toInt();
-
- value = strm_dict_find_rstr(dict, bitRateKey);
- m_audioBitRate = QByteArray(strm_string_getx(value, "0")).toInt();
- }
-
- strm_dict_t *vt = mmr_metadata_split(dict, "video", 0);
- if (vt) {
- value = strm_dict_find_rstr(vt, trackWidthKey);
- m_width = QByteArray(strm_string_getx(value, "0")).toInt();
-
- value = strm_dict_find_rstr(vt, trackHeightKey);
- m_height = QByteArray(strm_string_getx(value, "0")).toInt();
-
- value = strm_dict_find_rstr(vt, trackPixelWidthKey);
- m_pixelWidth = QByteArray(strm_string_getx(value, "1")).toFloat();
-
- value = strm_dict_find_rstr(vt, trackPixelHeightKey);
- m_pixelHeight = QByteArray(strm_string_getx(value, "1")).toFloat();
-
- strm_dict_destroy(vt);
- } else {
- value = strm_dict_find_rstr(dict, widthKey);
- m_width = QByteArray(strm_string_getx(value, "0")).toInt();
-
- value = strm_dict_find_rstr(dict, heightKey);
- m_height = QByteArray(strm_string_getx(value, "0")).toInt();
-
- value = strm_dict_find_rstr(dict, pixelWidthKey);
- m_pixelWidth = QByteArray(strm_string_getx(value, "1")).toFloat();
-
- value = strm_dict_find_rstr(dict, pixelHeightKey);
- m_pixelHeight = QByteArray(strm_string_getx(value, "1")).toFloat();
- }
-
- return true;
-}
-
-void MmRendererMetaData::clear()
-{
- strm_dict_t *dict;
- dict = strm_dict_new();
- update(dict);
- strm_dict_destroy(dict);
-}
-
-qlonglong MmRendererMetaData::duration() const
-{
- return m_duration;
-}
-
-// Handling of pixel aspect ratio
-//
-// If the pixel aspect ratio is different from 1:1, it means the video needs to be stretched in
-// order to look natural.
-// For example, if the pixel width is 2, and the pixel height is 1, it means a video of 300x200
-// pixels needs to be displayed as 600x200 to look correct.
-// In order to support this the easiest way, we simply pretend that the actual size of the video
-// is 600x200, which will cause the video to be displayed in an aspect ratio of 3:1 instead of 3:2,
-// and therefore look correct.
-
-int MmRendererMetaData::height() const
-{
- return m_height * m_pixelHeight;
-}
-
-int MmRendererMetaData::width() const
-{
- return m_width * m_pixelWidth;
-}
-
-bool MmRendererMetaData::hasVideo() const
-{
- // By default, assume no video if we can't extract the information
- if (m_mediaType == -1)
- return false;
-
- return (m_mediaType & mediaTypeVideoFlag);
-}
-
-bool MmRendererMetaData::hasAudio() const
-{
- // By default, assume audio only if we can't extract the information
- if (m_mediaType == -1)
- return true;
-
- return (m_mediaType & mediaTypeAudioFlag);
-}
-
-QString MmRendererMetaData::title() const
-{
- return m_title;
-}
-
-bool MmRendererMetaData::isSeekable() const
-{
- return m_seekable;
-}
-
-QString MmRendererMetaData::artist() const
-{
- return m_artist;
-}
-
-QString MmRendererMetaData::comment() const
-{
- return m_comment;
-}
-
-QString MmRendererMetaData::genre() const
-{
- return m_genre;
-}
-
-int MmRendererMetaData::year() const
-{
- return m_year;
-}
-
-QString MmRendererMetaData::mediaType() const
-{
- if (hasVideo())
- return QLatin1String("video");
- else if (hasAudio())
- return QLatin1String("audio");
- else
- return QString();
-}
-
-int MmRendererMetaData::audioBitRate() const
-{
- return m_audioBitRate;
-}
-
-int MmRendererMetaData::sampleRate() const
-{
- return m_sampleRate;
-}
-
-QString MmRendererMetaData::album() const
-{
- return m_album;
-}
-
-int MmRendererMetaData::track() const
-{
- return m_track;
-}
-
-QSize MmRendererMetaData::resolution() const
-{
- return QSize(width(), height());
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/mediaplayer/mmrenderermetadata.h b/src/plugins/qnx/mediaplayer/mmrenderermetadata.h
deleted file mode 100644
index ad2193d29..000000000
--- a/src/plugins/qnx/mediaplayer/mmrenderermetadata.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/****************************************************************************
-**
-** 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 MMRENDERERMETADATA_H
-#define MMRENDERERMETADATA_H
-
-#include <QtCore/qglobal.h>
-#include <QtCore/QSize>
-#include <QtCore/QString>
-
-typedef struct strm_dict strm_dict_t;
-
-QT_BEGIN_NAMESPACE
-
-class MmRendererMetaData
-{
-public:
- MmRendererMetaData();
- bool update(const strm_dict_t *dict);
- void clear();
-
- // Duration in milliseconds
- qlonglong duration() const;
-
- int height() const;
- int width() const;
- bool hasVideo() const;
- bool hasAudio() const;
- bool isSeekable() const;
-
- QString title() const;
- QString artist() const;
- QString comment() const;
- QString genre() const;
- int year() const;
- QString mediaType() const;
- int audioBitRate() const;
- int sampleRate() const;
- QString album() const;
- int track() const;
- QSize resolution() const;
-
-private:
- qlonglong m_duration;
- int m_height;
- int m_width;
- int m_mediaType;
- float m_pixelWidth;
- float m_pixelHeight;
- bool m_seekable;
- QString m_title;
- QString m_artist;
- QString m_comment;
- QString m_genre;
- int m_year;
- int m_audioBitRate;
- int m_sampleRate;
- QString m_album;
- int m_track;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/mediaplayer/mmrenderermetadatareadercontrol.cpp b/src/plugins/qnx/mediaplayer/mmrenderermetadatareadercontrol.cpp
deleted file mode 100644
index 5367e0c54..000000000
--- a/src/plugins/qnx/mediaplayer/mmrenderermetadatareadercontrol.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "mmrenderermetadatareadercontrol.h"
-#include <QtMultimedia/qmediametadata.h>
-
-QT_BEGIN_NAMESPACE
-
-MmRendererMetaDataReaderControl::MmRendererMetaDataReaderControl(QObject *parent)
- : QMetaDataReaderControl(parent)
-{
-}
-
-bool MmRendererMetaDataReaderControl::isMetaDataAvailable() const
-{
- return !availableMetaData().isEmpty();
-}
-
-QVariant MmRendererMetaDataReaderControl::metaData(const QString &key) const
-{
- if (key == QMediaMetaData::Title)
- return m_metaData.title();
- else if (key == QMediaMetaData::AlbumArtist)
- return m_metaData.artist();
- else if (key == QMediaMetaData::Comment)
- return m_metaData.comment();
- else if (key == QMediaMetaData::Genre)
- return m_metaData.genre();
- else if (key == QMediaMetaData::Year)
- return m_metaData.year();
- else if (key == QMediaMetaData::MediaType)
- return m_metaData.mediaType();
- else if (key == QMediaMetaData::Duration)
- return m_metaData.duration();
- else if (key == QMediaMetaData::AudioBitRate)
- return m_metaData.audioBitRate();
- else if (key == QMediaMetaData::SampleRate)
- return m_metaData.sampleRate();
- else if (key == QMediaMetaData::AlbumTitle)
- return m_metaData.album();
- else if (key == QMediaMetaData::TrackNumber)
- return m_metaData.track();
- else if (key == QMediaMetaData::Resolution)
- return m_metaData.resolution();
-
- return QVariant();
-}
-
-QStringList MmRendererMetaDataReaderControl::availableMetaData() const
-{
- QStringList metaData;
-
- if (!m_metaData.title().isEmpty())
- metaData << QMediaMetaData::Title;
- if (!m_metaData.artist().isEmpty())
- metaData << QMediaMetaData::Author;
- if (!m_metaData.comment().isEmpty())
- metaData << QMediaMetaData::Comment;
- if (!m_metaData.genre().isEmpty())
- metaData << QMediaMetaData::Genre;
- if (m_metaData.year() != 0)
- metaData << QMediaMetaData::Year;
- if (!m_metaData.mediaType().isEmpty())
- metaData << QMediaMetaData::MediaType;
- if (m_metaData.duration() != 0)
- metaData << QMediaMetaData::Duration;
- if (m_metaData.audioBitRate() != 0)
- metaData << QMediaMetaData::AudioBitRate;
- if (m_metaData.sampleRate() != 0)
- metaData << QMediaMetaData::SampleRate;
- if (!m_metaData.album().isEmpty())
- metaData << QMediaMetaData::AlbumTitle;
- if (m_metaData.track() != 0)
- metaData << QMediaMetaData::TrackNumber;
- if (m_metaData.resolution().isValid())
- metaData << QMediaMetaData::Resolution;
-
- return metaData;
-}
-
-void MmRendererMetaDataReaderControl::setMetaData(const MmRendererMetaData &data)
-{
- const MmRendererMetaData oldMetaData = m_metaData;
- const bool oldMetaDataAvailable = isMetaDataAvailable();
-
- m_metaData = data;
-
- bool changed = false;
- if (m_metaData.title() != oldMetaData.title()) {
- changed = true;
- emit metaDataChanged(QMediaMetaData::Title, m_metaData.title());
- } else if (m_metaData.artist() != oldMetaData.artist()) {
- changed = true;
- emit metaDataChanged(QMediaMetaData::Author, m_metaData.artist());
- } else if (m_metaData.comment() != oldMetaData.comment()) {
- changed = true;
- emit metaDataChanged(QMediaMetaData::Comment, m_metaData.comment());
- } else if (m_metaData.genre() != oldMetaData.genre()) {
- changed = true;
- emit metaDataChanged(QMediaMetaData::Genre, m_metaData.genre());
- } else if (m_metaData.year() != oldMetaData.year()) {
- changed = true;
- emit metaDataChanged(QMediaMetaData::Year, m_metaData.year());
- } else if (m_metaData.mediaType() != oldMetaData.mediaType()) {
- changed = true;
- emit metaDataChanged(QMediaMetaData::MediaType, m_metaData.mediaType());
- } else if (m_metaData.duration() != oldMetaData.duration()) {
- changed = true;
- emit metaDataChanged(QMediaMetaData::Duration, m_metaData.duration());
- } else if (m_metaData.audioBitRate() != oldMetaData.audioBitRate()) {
- changed = true;
- emit metaDataChanged(QMediaMetaData::AudioBitRate, m_metaData.audioBitRate());
- } else if (m_metaData.sampleRate() != oldMetaData.sampleRate()) {
- changed = true;
- emit metaDataChanged(QMediaMetaData::SampleRate, m_metaData.sampleRate());
- } else if (m_metaData.album() != oldMetaData.album()) {
- changed = true;
- emit metaDataChanged(QMediaMetaData::AlbumTitle, m_metaData.album());
- } else if (m_metaData.track() != oldMetaData.track()) {
- changed = true;
- emit metaDataChanged(QMediaMetaData::TrackNumber, m_metaData.track());
- } else if (m_metaData.resolution() != oldMetaData.resolution()) {
- changed = true;
- emit metaDataChanged(QMediaMetaData::Resolution, m_metaData.resolution());
- }
-
- if (changed)
- emit metaDataChanged();
-
- const bool metaDataAvailable = isMetaDataAvailable();
- if (metaDataAvailable != oldMetaDataAvailable)
- emit metaDataAvailableChanged(metaDataAvailable);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/mediaplayer/mmrenderermetadatareadercontrol.h b/src/plugins/qnx/mediaplayer/mmrenderermetadatareadercontrol.h
deleted file mode 100644
index 878420460..000000000
--- a/src/plugins/qnx/mediaplayer/mmrenderermetadatareadercontrol.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************************
-**
-** 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 MMRENDERERMETADATAREADERCONTROL_H
-#define MMRENDERERMETADATAREADERCONTROL_H
-
-#include "mmrenderermetadata.h"
-#include <qmetadatareadercontrol.h>
-
-QT_BEGIN_NAMESPACE
-
-class MmRendererMetaDataReaderControl : public QMetaDataReaderControl
-{
- Q_OBJECT
-public:
- explicit MmRendererMetaDataReaderControl(QObject *parent = 0);
-
- bool isMetaDataAvailable() const override;
-
- QVariant metaData(const QString &key) const override;
- QStringList availableMetaData() const override;
-
- void setMetaData(const MmRendererMetaData &data);
-
-private:
- MmRendererMetaData m_metaData;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/mediaplayer/mmrendererplayervideorenderercontrol.cpp b/src/plugins/qnx/mediaplayer/mmrendererplayervideorenderercontrol.cpp
deleted file mode 100644
index 3b5715157..000000000
--- a/src/plugins/qnx/mediaplayer/mmrendererplayervideorenderercontrol.cpp
+++ /dev/null
@@ -1,218 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "mmrendererplayervideorenderercontrol.h"
-
-#include "windowgrabber.h"
-
-#include <QCoreApplication>
-#include <QDebug>
-#include <QVideoSurfaceFormat>
-#include <QOpenGLContext>
-
-#include <mm/renderer.h>
-
-QT_BEGIN_NAMESPACE
-
-static int winIdCounter = 0;
-
-MmRendererPlayerVideoRendererControl::MmRendererPlayerVideoRendererControl(QObject *parent)
- : QVideoRendererControl(parent)
- , m_windowGrabber(new WindowGrabber(this))
- , m_context(0)
- , m_videoId(-1)
-{
- connect(m_windowGrabber, SIGNAL(updateScene(const QSize &)), SLOT(updateScene(const QSize &)));
-}
-
-MmRendererPlayerVideoRendererControl::~MmRendererPlayerVideoRendererControl()
-{
- detachDisplay();
-}
-
-QAbstractVideoSurface *MmRendererPlayerVideoRendererControl::surface() const
-{
- return m_surface;
-}
-
-void MmRendererPlayerVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
-{
- m_surface = QPointer<QAbstractVideoSurface>(surface);
- if (QOpenGLContext::currentContext())
- m_windowGrabber->checkForEglImageExtension();
- else if (m_surface)
- m_surface->setProperty("_q_GLThreadCallback", QVariant::fromValue<QObject*>(this));
-}
-
-void MmRendererPlayerVideoRendererControl::attachDisplay(mmr_context_t *context)
-{
- if (m_videoId != -1) {
- qWarning() << "MmRendererPlayerVideoRendererControl: Video output already attached!";
- return;
- }
-
- if (!context) {
- qWarning() << "MmRendererPlayerVideoRendererControl: No media player context!";
- return;
- }
-
- const QByteArray windowGroupId = m_windowGrabber->windowGroupId();
- if (windowGroupId.isEmpty()) {
- qWarning() << "MmRendererPlayerVideoRendererControl: Unable to find window group";
- return;
- }
-
- const QString windowName = QStringLiteral("MmRendererPlayerVideoRendererControl_%1_%2")
- .arg(winIdCounter++)
- .arg(QCoreApplication::applicationPid());
-
- m_windowGrabber->setWindowId(windowName.toLatin1());
-
- // Start with an invisible window, because we just want to grab the frames from it.
- const QString videoDeviceUrl = QStringLiteral("screen:?winid=%1&wingrp=%2&initflags=invisible&nodstviewport=1")
- .arg(windowName)
- .arg(QString::fromLatin1(windowGroupId));
-
- m_videoId = mmr_output_attach(context, videoDeviceUrl.toLatin1(), "video");
- if (m_videoId == -1) {
- qWarning() << "mmr_output_attach() for video failed";
- return;
- }
-
- m_context = context;
-}
-
-void MmRendererPlayerVideoRendererControl::detachDisplay()
-{
- m_windowGrabber->stop();
-
- if (m_surface)
- m_surface->stop();
-
- if (m_context && m_videoId != -1)
- mmr_output_detach(m_context, m_videoId);
-
- m_context = 0;
- m_videoId = -1;
-}
-
-void MmRendererPlayerVideoRendererControl::pause()
-{
- m_windowGrabber->pause();
-}
-
-void MmRendererPlayerVideoRendererControl::resume()
-{
- m_windowGrabber->resume();
-}
-
-class QnxTextureBuffer : public QAbstractVideoBuffer
-{
-public:
- QnxTextureBuffer(WindowGrabber *windowGrabber) :
- QAbstractVideoBuffer(QAbstractVideoBuffer::GLTextureHandle)
- {
- m_windowGrabber = windowGrabber;
- m_handle = 0;
- }
- MapMode mapMode() const {
- return QAbstractVideoBuffer::ReadWrite;
- }
- void unmap() {
-
- }
- uchar *map(MapMode mode, int * numBytes, int * bytesPerLine) {
- Q_UNUSED(mode);
- Q_UNUSED(numBytes);
- Q_UNUSED(bytesPerLine);
- return 0;
- }
- QVariant handle() const {
- if (!m_handle) {
- const_cast<QnxTextureBuffer*>(this)->m_handle = m_windowGrabber->getNextTextureId();
- }
- return m_handle;
- }
-private:
- WindowGrabber *m_windowGrabber;
- int m_handle;
-};
-
-void MmRendererPlayerVideoRendererControl::updateScene(const QSize &size)
-{
- if (m_surface) {
- if (!m_surface->isActive()) {
- if (m_windowGrabber->eglImageSupported()) {
- m_surface->start(QVideoSurfaceFormat(size, QVideoFrame::Format_BGR32,
- QAbstractVideoBuffer::GLTextureHandle));
- } else {
- m_surface->start(QVideoSurfaceFormat(size, QVideoFrame::Format_ARGB32));
- }
- } else {
- if (m_surface->surfaceFormat().frameSize() != size) {
- m_surface->stop();
- if (m_windowGrabber->eglImageSupported()) {
- m_surface->start(QVideoSurfaceFormat(size, QVideoFrame::Format_BGR32,
- QAbstractVideoBuffer::GLTextureHandle));
- } else {
- m_surface->start(QVideoSurfaceFormat(size, QVideoFrame::Format_ARGB32));
- }
- }
- }
-
- // Depending on the support of EGL images on the current platform we either pass a texture
- // handle or a copy of the image data
- if (m_windowGrabber->eglImageSupported()) {
- QnxTextureBuffer *textBuffer = new QnxTextureBuffer(m_windowGrabber);
- QVideoFrame actualFrame(textBuffer, size, QVideoFrame::Format_BGR32);
- m_surface->present(actualFrame);
- } else {
- m_surface->present(m_windowGrabber->getNextImage().copy());
- }
- }
-}
-
-void MmRendererPlayerVideoRendererControl::customEvent(QEvent *e)
-{
- // This is running in the render thread (OpenGL enabled)
- if (e->type() == QEvent::User)
- m_windowGrabber->checkForEglImageExtension();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/mediaplayer/mmrendererplayervideorenderercontrol.h b/src/plugins/qnx/mediaplayer/mmrendererplayervideorenderercontrol.h
deleted file mode 100644
index c547ef534..000000000
--- a/src/plugins/qnx/mediaplayer/mmrendererplayervideorenderercontrol.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/****************************************************************************
-**
-** 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 MMRENDERERPLAYERVIDEORENDERERCONTROL_H
-#define MMRENDERERPLAYERVIDEORENDERERCONTROL_H
-
-#include <QPointer>
-#include <qabstractvideosurface.h>
-#include <qvideorenderercontrol.h>
-
-typedef struct mmr_context mmr_context_t;
-
-QT_BEGIN_NAMESPACE
-
-class WindowGrabber;
-
-class MmRendererPlayerVideoRendererControl : public QVideoRendererControl
-{
- Q_OBJECT
-public:
- explicit MmRendererPlayerVideoRendererControl(QObject *parent = 0);
- ~MmRendererPlayerVideoRendererControl();
-
- QAbstractVideoSurface *surface() const override;
- void setSurface(QAbstractVideoSurface *surface) override;
-
- // Called by media control
- void attachDisplay(mmr_context_t *context);
- void detachDisplay();
- void pause();
- void resume();
-
- void customEvent(QEvent *) override;
-
-private Q_SLOTS:
- void updateScene(const QSize &size);
-
-private:
- QPointer<QAbstractVideoSurface> m_surface;
-
- WindowGrabber* m_windowGrabber;
- mmr_context_t *m_context;
-
- int m_videoId;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/mediaplayer/mmrendererutil.cpp b/src/plugins/qnx/mediaplayer/mmrendererutil.cpp
deleted file mode 100644
index d8af4a746..000000000
--- a/src/plugins/qnx/mediaplayer/mmrendererutil.cpp
+++ /dev/null
@@ -1,246 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "mmrendererutil.h"
-
-#include <QDebug>
-#include <QDir>
-#include <QFile>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonValue>
-#include <QMutex>
-#include <QMutex>
-#include <QString>
-#include <QXmlStreamReader>
-
-#include <mm/renderer.h>
-
-QT_BEGIN_NAMESPACE
-
-struct MmError {
- int errorCode;
- const char *name;
-};
-
-#define MM_ERROR_ENTRY(error) { error, #error }
-static const MmError mmErrors[] = {
- MM_ERROR_ENTRY(MMR_ERROR_NONE),
- MM_ERROR_ENTRY(MMR_ERROR_UNKNOWN ),
- MM_ERROR_ENTRY(MMR_ERROR_INVALID_PARAMETER ),
- MM_ERROR_ENTRY(MMR_ERROR_INVALID_STATE),
- MM_ERROR_ENTRY(MMR_ERROR_UNSUPPORTED_VALUE),
- MM_ERROR_ENTRY(MMR_ERROR_UNSUPPORTED_MEDIA_TYPE),
- MM_ERROR_ENTRY(MMR_ERROR_MEDIA_PROTECTED),
- MM_ERROR_ENTRY(MMR_ERROR_UNSUPPORTED_OPERATION),
- MM_ERROR_ENTRY(MMR_ERROR_READ),
- MM_ERROR_ENTRY(MMR_ERROR_WRITE),
- MM_ERROR_ENTRY(MMR_ERROR_MEDIA_UNAVAILABLE),
- MM_ERROR_ENTRY(MMR_ERROR_MEDIA_CORRUPTED),
- MM_ERROR_ENTRY(MMR_ERROR_OUTPUT_UNAVAILABLE),
- MM_ERROR_ENTRY(MMR_ERROR_NO_MEMORY),
- MM_ERROR_ENTRY(MMR_ERROR_RESOURCE_UNAVAILABLE),
- MM_ERROR_ENTRY(MMR_ERROR_MEDIA_DRM_NO_RIGHTS),
- MM_ERROR_ENTRY(MMR_ERROR_DRM_CORRUPTED_DATA_STORE),
- MM_ERROR_ENTRY(MMR_ERROR_DRM_OUTPUT_PROTECTION),
- MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_HDMI),
- MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_DISPLAYPORT),
- MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_DVI),
- MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_ANALOG_VIDEO),
- MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_ANALOG_AUDIO),
- MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_TOSLINK),
- MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_SPDIF),
- MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_BLUETOOTH),
- MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_WIRELESSHD),
-};
-static const unsigned int numMmErrors = sizeof(mmErrors) / sizeof(MmError);
-
-static QBasicMutex roleMapMutex;
-static bool roleMapInitialized = false;
-static QString roleMap[QAudio::CustomRole + 1];
-
-template <typename T, size_t N>
-constexpr size_t countof(T (&)[N])
-{
- return N;
-}
-
-constexpr bool inBounds(QAudio::Role r)
-{
- return r >= 0 && r < countof(roleMap);
-}
-
-QString keyValueMapsLocation()
-{
- QByteArray qtKeyValueMaps = qgetenv("QT_KEY_VALUE_MAPS");
- if (qtKeyValueMaps.isNull())
- return QStringLiteral("/etc/qt/keyvaluemaps");
- else
- return qtKeyValueMaps;
-}
-
-QJsonObject loadMapObject(const QString &keyValueMapPath)
-{
- QFile mapFile(keyValueMapsLocation() + keyValueMapPath);
- if (mapFile.open(QIODevice::ReadOnly)) {
- QByteArray mapFileContents = mapFile.readAll();
- QJsonDocument mapDocument = QJsonDocument::fromJson(mapFileContents);
- if (mapDocument.isObject()) {
- QJsonObject mapObject = mapDocument.object();
- return mapObject;
- }
- }
- return QJsonObject();
-}
-
-static void loadRoleMap()
-{
- QMutexLocker locker(&roleMapMutex);
-
- if (!roleMapInitialized) {
- QJsonObject mapObject = loadMapObject("/QAudio/Role.json");
- if (!mapObject.isEmpty()) {
- // Wrapping the loads in a switch like this ensures that anyone adding
- // a new enumerator will be notified that this code must be updated. A
- // compile error will occur because the enumerator is missing from the
- // switch. A compile error will also occur if the enumerator used to
- // size the mapping table isn't updated when a new enumerator is added.
- // One or more enumerators will be outside the bounds of the array when
- // the wrong enumerator is used to size the array.
- //
- // The code loads a mapping for each enumerator because role is set
- // to UnknownRole and all the cases drop through to the next case.
-#pragma GCC diagnostic push
-#pragma GCC diagnostic error "-Wswitch"
-#define loadRoleMapping(r) \
- case QAudio::r: \
- static_assert(inBounds(QAudio::r), #r " out-of-bounds." \
- " Do you need to change the enumerator used to size the mapping table" \
- " because you added new QAudio::Role enumerators?"); \
- roleMap[QAudio::r] = mapObject.value(QLatin1String(#r)).toString();
-
- QAudio::Role role = QAudio::UnknownRole;
- switch (role) {
- loadRoleMapping(UnknownRole);
- loadRoleMapping(MusicRole);
- loadRoleMapping(VideoRole);
- loadRoleMapping(VoiceCommunicationRole);
- loadRoleMapping(AlarmRole);
- loadRoleMapping(NotificationRole);
- loadRoleMapping(RingtoneRole);
- loadRoleMapping(AccessibilityRole);
- loadRoleMapping(SonificationRole);
- loadRoleMapping(GameRole);
- loadRoleMapping(CustomRole);
- }
-#undef loadRoleMapping
-#pragma GCC diagnostic pop
-
- if (!roleMap[QAudio::CustomRole].isEmpty()) {
- qWarning("CustomRole mapping ignored");
- roleMap[QAudio::CustomRole].clear();
- }
- }
-
- roleMapInitialized = true;
- }
-}
-
-QString mmErrorMessage(const QString &msg, mmr_context_t *context, int *errorCode)
-{
- const mmr_error_info_t * const mmError = mmr_error_info(context);
-
- if (errorCode)
- *errorCode = mmError->error_code;
-
- if (mmError->error_code < numMmErrors) {
- return QString("%1: %2 (code %3)").arg(msg).arg(mmErrors[mmError->error_code].name)
- .arg(mmError->error_code);
- } else {
- return QString("%1: Unknown error code %2").arg(msg).arg(mmError->error_code);
- }
-}
-
-bool checkForDrmPermission()
-{
- QDir sandboxDir = QDir::home(); // always returns 'data' directory
- sandboxDir.cdUp(); // change to app sandbox directory
-
- QFile file(sandboxDir.filePath("app/native/bar-descriptor.xml"));
- if (!file.open(QIODevice::ReadOnly)) {
- qWarning() << "checkForDrmPermission: Unable to open bar-descriptor.xml";
- return false;
- }
-
- QXmlStreamReader reader(&file);
- while (!reader.atEnd()) {
- reader.readNextStartElement();
- if (reader.name() == QLatin1String("action")
- || reader.name() == QLatin1String("permission")) {
- if (reader.readElementText().trimmed() == QLatin1String("access_protected_media"))
- return true;
- }
- }
-
- return false;
-}
-
-QString qnxAudioType(QAudio::Role role)
-{
- loadRoleMap();
-
- if (role >= 0 && role < countof(roleMap))
- return roleMap[role];
- else
- return QString();
-}
-
-QList<QAudio::Role> qnxSupportedAudioRoles()
-{
- loadRoleMap();
-
- QList<QAudio::Role> result;
- for (size_t i = 0; i < countof(roleMap); ++i) {
- if (!roleMap[i].isEmpty() || (i == QAudio::UnknownRole))
- result.append(static_cast<QAudio::Role>(i));
- }
-
- return result;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/mediaplayer/mmrendererutil.h b/src/plugins/qnx/mediaplayer/mmrendererutil.h
deleted file mode 100644
index ac6f73a7d..000000000
--- a/src/plugins/qnx/mediaplayer/mmrendererutil.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/****************************************************************************
-**
-** 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 MMRENDERERUTIL_H
-#define MMRENDERERUTIL_H
-
-#include <QtCore/qglobal.h>
-#include <QtMultimedia/qaudio.h>
-
-typedef struct mmr_context mmr_context_t;
-
-QT_BEGIN_NAMESPACE
-
-class QString;
-
-QString mmErrorMessage(const QString &msg, mmr_context_t *context, int * errorCode = 0);
-
-bool checkForDrmPermission();
-
-QString qnxAudioType(QAudio::Role role);
-QList<QAudio::Role> qnxSupportedAudioRoles();
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/mediaplayer/mmrenderervideowindowcontrol.cpp b/src/plugins/qnx/mediaplayer/mmrenderervideowindowcontrol.cpp
deleted file mode 100644
index fbd698eea..000000000
--- a/src/plugins/qnx/mediaplayer/mmrenderervideowindowcontrol.cpp
+++ /dev/null
@@ -1,413 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "mmrenderervideowindowcontrol.h"
-#include "mmrendererutil.h"
-#include <QtCore/qdebug.h>
-#include <QtGui/qguiapplication.h>
-#include <QtGui/qpa/qplatformnativeinterface.h>
-#include <QtGui/qscreen.h>
-#include <QtGui/qwindow.h>
-#include <mm/renderer.h>
-
-QT_BEGIN_NAMESPACE
-
-static int winIdCounter = 0;
-
-MmRendererVideoWindowControl::MmRendererVideoWindowControl(QObject *parent)
- : QVideoWindowControl(parent),
- m_videoId(-1),
- m_winId(0),
- m_context(0),
- m_fullscreen(false),
- m_aspectRatioMode(Qt::IgnoreAspectRatio),
- m_window(0),
- m_hue(0),
- m_brightness(0),
- m_contrast(0),
- m_saturation(0)
-{
-}
-
-MmRendererVideoWindowControl::~MmRendererVideoWindowControl()
-{
-}
-
-WId MmRendererVideoWindowControl::winId() const
-{
- return m_winId;
-}
-
-void MmRendererVideoWindowControl::setWinId(WId id)
-{
- m_winId = id;
-}
-
-QRect MmRendererVideoWindowControl::displayRect() const
-{
- return m_displayRect ;
-}
-
-void MmRendererVideoWindowControl::setDisplayRect(const QRect &rect)
-{
- if (m_displayRect != rect) {
- m_displayRect = rect;
- updateVideoPosition();
- }
-}
-
-bool MmRendererVideoWindowControl::isFullScreen() const
-{
- return m_fullscreen;
-}
-
-void MmRendererVideoWindowControl::setFullScreen(bool fullScreen)
-{
- if (m_fullscreen != fullScreen) {
- m_fullscreen = fullScreen;
- updateVideoPosition();
- emit fullScreenChanged(m_fullscreen);
- }
-}
-
-void MmRendererVideoWindowControl::repaint()
-{
- // Nothing we can or should do here
-}
-
-QSize MmRendererVideoWindowControl::nativeSize() const
-{
- return QSize(m_metaData.width(), m_metaData.height());
-}
-
-Qt::AspectRatioMode MmRendererVideoWindowControl::aspectRatioMode() const
-{
- return m_aspectRatioMode;
-}
-
-void MmRendererVideoWindowControl::setAspectRatioMode(Qt::AspectRatioMode mode)
-{
- m_aspectRatioMode = mode;
-}
-
-int MmRendererVideoWindowControl::brightness() const
-{
- return m_brightness;
-}
-
-void MmRendererVideoWindowControl::setBrightness(int brightness)
-{
- if (m_brightness != brightness) {
- m_brightness = brightness;
- updateBrightness();
- emit brightnessChanged(m_brightness);
- }
-}
-
-int MmRendererVideoWindowControl::contrast() const
-{
- return m_contrast;
-}
-
-void MmRendererVideoWindowControl::setContrast(int contrast)
-{
- if (m_contrast != contrast) {
- m_contrast = contrast;
- updateContrast();
- emit contrastChanged(m_contrast);
- }
-}
-
-int MmRendererVideoWindowControl::hue() const
-{
- return m_hue;
-}
-
-void MmRendererVideoWindowControl::setHue(int hue)
-{
- if (m_hue != hue) {
- m_hue = hue;
- updateHue();
- emit hueChanged(m_hue);
- }
-}
-
-int MmRendererVideoWindowControl::saturation() const
-{
- return m_saturation;
-}
-
-void MmRendererVideoWindowControl::setSaturation(int saturation)
-{
- if (m_saturation != saturation) {
- m_saturation = saturation;
- updateSaturation();
- emit saturationChanged(m_saturation);
- }
-}
-
-void MmRendererVideoWindowControl::attachDisplay(mmr_context_t *context)
-{
- if (m_videoId != -1) {
- qDebug() << "MmRendererVideoWindowControl: Video output already attached!";
- return;
- }
-
- if (!context) {
- qDebug() << "MmRendererVideoWindowControl: No media player context!";
- return;
- }
-
- QWindow *window = findWindow(m_winId);
- if (!window) {
- qDebug() << "MmRendererVideoWindowControl: No video window!";
- return;
- }
-
- QPlatformNativeInterface * const nativeInterface = QGuiApplication::platformNativeInterface();
- if (!nativeInterface) {
- qDebug() << "MmRendererVideoWindowControl: Unable to get platform native interface";
- return;
- }
-
- const char * const groupNameData = static_cast<const char *>(
- nativeInterface->nativeResourceForWindow("windowGroup", window));
- if (!groupNameData) {
- qDebug() << "MmRendererVideoWindowControl: Unable to find window group for window"
- << window;
- return;
- }
-
- const QString groupName = QString::fromLatin1(groupNameData);
- m_windowName = QString("MmRendererVideoWindowControl_%1_%2").arg(winIdCounter++)
- .arg(QCoreApplication::applicationPid());
-
- nativeInterface->setWindowProperty(window->handle(),
- QStringLiteral("mmRendererWindowName"), m_windowName);
-
- // Start with an invisible window. If it would be visible right away, it would be at the wrong
- // position, and we can only change the position once we get the window handle.
- const QString videoDeviceUrl =
- QString("screen:?winid=%1&wingrp=%2&initflags=invisible&nodstviewport=1").arg(m_windowName).arg(groupName);
-
- m_videoId = mmr_output_attach(context, videoDeviceUrl.toLatin1(), "video");
- if (m_videoId == -1) {
- qDebug() << mmErrorMessage("mmr_output_attach() for video failed", context);
- return;
- }
-
- m_context = context;
- updateVideoPosition();
- updateHue();
- updateContrast();
- updateBrightness();
- updateSaturation();
-}
-
-void MmRendererVideoWindowControl::updateVideoPosition()
-{
- QWindow * const window = findWindow(m_winId);
- if (m_context && m_videoId != -1 && window) {
- QPoint topLeft = m_displayRect.topLeft();
-
- QScreen * const screen = window->screen();
- int width = m_fullscreen ?
- screen->size().width() :
- m_displayRect.width();
- int height = m_fullscreen ?
- screen->size().height() :
- m_displayRect.height();
-
- if (m_metaData.hasVideo() && m_metaData.width() > 0 && m_metaData.height() > 0) {
- // We need the source size to do aspect ratio scaling
- const qreal sourceRatio = m_metaData.width() / static_cast<float>(m_metaData.height());
- const qreal targetRatio = width / static_cast<float>(height);
-
- if (m_aspectRatioMode == Qt::KeepAspectRatio) {
- if (targetRatio < sourceRatio) {
- // Need to make height smaller
- const int newHeight = width / sourceRatio;
- const int heightDiff = height - newHeight;
- topLeft.ry() += heightDiff / 2;
- height = newHeight;
- } else {
- // Need to make width smaller
- const int newWidth = sourceRatio * height;
- const int widthDiff = width - newWidth;
- topLeft.rx() += widthDiff / 2;
- width = newWidth;
- }
-
- } else if (m_aspectRatioMode == Qt::KeepAspectRatioByExpanding) {
- if (targetRatio < sourceRatio) {
- // Need to make width larger
- const int newWidth = sourceRatio * height;
- const int widthDiff = newWidth - width;
- topLeft.rx() -= widthDiff / 2;
- width = newWidth;
- } else {
- // Need to make height larger
- const int newHeight = width / sourceRatio;
- const int heightDiff = newHeight - height;
- topLeft.ry() -= heightDiff / 2;
- height = newHeight;
- }
- }
- }
-
- if (m_window != 0) {
- const int position[2] = { topLeft.x(), topLeft.y() };
- const int size[2] = { width, height };
- const int visible = m_displayRect.isValid();
- if (screen_set_window_property_iv(m_window, SCREEN_PROPERTY_POSITION, position) != 0)
- perror("Setting video position failed");
- if (screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SIZE, size) != 0)
- perror("Setting video size failed");
- if (screen_set_window_property_iv(m_window, SCREEN_PROPERTY_VISIBLE, &visible) != 0)
- perror("Setting video visibility failed");
- }
- }
-}
-
-void MmRendererVideoWindowControl::updateBrightness()
-{
- if (m_window != 0) {
- const int backendValue = m_brightness * 2.55f;
- if (screen_set_window_property_iv(m_window, SCREEN_PROPERTY_BRIGHTNESS, &backendValue) != 0)
- perror("Setting brightness failed");
- }
-}
-
-void MmRendererVideoWindowControl::updateContrast()
-{
- if (m_window != 0) {
- const int backendValue = m_contrast * 1.27f;
- if (screen_set_window_property_iv(m_window, SCREEN_PROPERTY_CONTRAST, &backendValue) != 0)
- perror("Setting contrast failed");
- }
-}
-
-void MmRendererVideoWindowControl::updateHue()
-{
- if (m_window != 0) {
- const int backendValue = m_hue * 1.27f;
- if (screen_set_window_property_iv(m_window, SCREEN_PROPERTY_HUE, &backendValue) != 0)
- perror("Setting hue failed");
- }
-}
-
-void MmRendererVideoWindowControl::updateSaturation()
-{
- if (m_window != 0) {
- const int backendValue = m_saturation * 1.27f;
- if (screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SATURATION, &backendValue) != 0)
- perror("Setting saturation failed");
- }
-}
-
-void MmRendererVideoWindowControl::detachDisplay()
-{
- if (m_context && m_videoId != -1)
- mmr_output_detach(m_context, m_videoId);
-
- m_context = 0;
- m_videoId = -1;
- m_metaData.clear();
- m_windowName.clear();
- m_window = 0;
- m_hue = 0;
- m_brightness = 0;
- m_contrast = 0;
- m_saturation = 0;
-}
-
-void MmRendererVideoWindowControl::setMetaData(const MmRendererMetaData &metaData)
-{
- m_metaData = metaData;
- emit nativeSizeChanged();
-
- // To handle the updated source size data
- updateVideoPosition();
-}
-
-void MmRendererVideoWindowControl::screenEventHandler(const screen_event_t &screen_event)
-{
- int eventType;
- if (screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TYPE, &eventType) != 0) {
- perror("MmRendererVideoWindowControl: Failed to query screen event type");
- return;
- }
-
- if (eventType != SCREEN_EVENT_CREATE)
- return;
-
- screen_window_t window = 0;
- if (screen_get_event_property_pv(screen_event, SCREEN_PROPERTY_WINDOW, (void**)&window) != 0) {
- perror("MmRendererVideoWindowControl: Failed to query window property");
- return;
- }
-
- const int maxIdStrLength = 128;
- char idString[maxIdStrLength];
- if (screen_get_window_property_cv(window, SCREEN_PROPERTY_ID_STRING, maxIdStrLength, idString) != 0) {
- perror("MmRendererVideoWindowControl: Failed to query window ID string");
- return;
- }
-
- if (m_windowName == idString) {
- m_window = window;
- updateVideoPosition();
-
- const int visibleFlag = 1;
- if (screen_set_window_property_iv(m_window, SCREEN_PROPERTY_VISIBLE, &visibleFlag) != 0) {
- perror("MmRendererVideoWindowControl: Failed to make window visible");
- return;
- }
- }
-}
-
-QWindow *MmRendererVideoWindowControl::findWindow(WId id) const
-{
- const auto allWindows = QGuiApplication::allWindows();
- for (QWindow *window : allWindows)
- if (window->winId() == id)
- return window;
- return 0;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/mediaplayer/mmrenderervideowindowcontrol.h b/src/plugins/qnx/mediaplayer/mmrenderervideowindowcontrol.h
deleted file mode 100644
index 8327e259d..000000000
--- a/src/plugins/qnx/mediaplayer/mmrenderervideowindowcontrol.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/****************************************************************************
-**
-** 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 MMRENDERERVIDEOWINDOWCONTROL_H
-#define MMRENDERERVIDEOWINDOWCONTROL_H
-
-#include "mmrenderermetadata.h"
-#include <qvideowindowcontrol.h>
-#include <screen/screen.h>
-
-typedef struct mmr_context mmr_context_t;
-
-QT_BEGIN_NAMESPACE
-
-class MmRendererVideoWindowControl : public QVideoWindowControl
-{
- Q_OBJECT
-public:
- explicit MmRendererVideoWindowControl(QObject *parent = 0);
- ~MmRendererVideoWindowControl();
-
- WId winId() const override;
- void setWinId(WId id) override;
-
- QRect displayRect() const override;
- void setDisplayRect(const QRect &rect) override;
-
- bool isFullScreen() const override;
- void setFullScreen(bool fullScreen) override;
-
- void repaint() override;
-
- QSize nativeSize() const override;
-
- Qt::AspectRatioMode aspectRatioMode() const override;
- void setAspectRatioMode(Qt::AspectRatioMode mode) override;
-
- int brightness() const override;
- void setBrightness(int brightness) override;
-
- int contrast() const override;
- void setContrast(int contrast) override;
-
- int hue() const override;
- void setHue(int hue) override;
-
- int saturation() const override;
- void setSaturation(int saturation) override;
-
- //
- // Called by media control
- //
- void detachDisplay();
- void attachDisplay(mmr_context_t *context);
- void setMetaData(const MmRendererMetaData &metaData);
- void screenEventHandler(const screen_event_t &event);
-
-private:
- QWindow *findWindow(WId id) const;
- void updateVideoPosition();
- void updateBrightness();
- void updateContrast();
- void updateHue();
- void updateSaturation();
-
- int m_videoId;
- WId m_winId;
- QRect m_displayRect;
- mmr_context_t *m_context;
- bool m_fullscreen;
- MmRendererMetaData m_metaData;
- Qt::AspectRatioMode m_aspectRatioMode;
- QString m_windowName;
- screen_window_t m_window;
- int m_hue;
- int m_brightness;
- int m_contrast;
- int m_saturation;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/mediaplayer/mmreventmediaplayercontrol.cpp b/src/plugins/qnx/mediaplayer/mmreventmediaplayercontrol.cpp
deleted file mode 100644
index b5c9804ab..000000000
--- a/src/plugins/qnx/mediaplayer/mmreventmediaplayercontrol.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 QNX Software Systems. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "mmreventmediaplayercontrol.h"
-#include "mmreventthread.h"
-#include "mmrenderervideowindowcontrol.h"
-
-#include <mm/renderer.h>
-#include <tuple>
-
-QT_BEGIN_NAMESPACE
-
-static std::tuple<int, int, bool> parseBufferLevel(const QByteArray &value)
-{
- const int slashPos = value.indexOf('/');
- if (slashPos <= 0)
- return std::make_tuple(0, 0, false);
-
- bool ok = false;
- const int level = value.left(slashPos).toInt(&ok);
- if (!ok || level < 0)
- return std::make_tuple(0, 0, false);
-
- const int capacity = value.mid(slashPos + 1).toInt(&ok);
- if (!ok || capacity < 0)
- return std::make_tuple(0, 0, false);
-
- return std::make_tuple(level, capacity, true);
-}
-
-MmrEventMediaPlayerControl::MmrEventMediaPlayerControl(QObject *parent)
- : MmRendererMediaPlayerControl(parent)
- , m_eventThread(nullptr)
- , m_bufferStatus("")
- , m_bufferLevel(0)
- , m_bufferCapacity(0)
- , m_position(0)
- , m_suspended(false)
- , m_suspendedReason("unknown")
- , m_state(MMR_STATE_IDLE)
- , m_speed(0)
-{
- openConnection();
-}
-
-MmrEventMediaPlayerControl::~MmrEventMediaPlayerControl()
-{
- destroy();
-}
-
-void MmrEventMediaPlayerControl::startMonitoring()
-{
- m_eventThread = new MmrEventThread(m_context);
-
- connect(m_eventThread, &MmrEventThread::eventPending,
- this, &MmrEventMediaPlayerControl::readEvents);
-
- m_eventThread->setObjectName(QStringLiteral("MmrEventThread-") + QString::number(m_id));
- m_eventThread->start();
-}
-
-void MmrEventMediaPlayerControl::stopMonitoring()
-{
- delete m_eventThread;
- m_eventThread = nullptr;
-}
-
-void MmrEventMediaPlayerControl::resetMonitoring()
-{
- m_bufferStatus = "";
- m_bufferLevel = 0;
- m_bufferCapacity = 0;
- m_position = 0;
- m_suspended = false;
- m_suspendedReason = "unknown";
- m_state = MMR_STATE_IDLE;
- m_speed = 0;
-}
-
-bool MmrEventMediaPlayerControl::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)
-{
- Q_UNUSED(result);
- if (eventType == "screen_event_t") {
- screen_event_t event = static_cast<screen_event_t>(message);
- if (MmRendererVideoWindowControl *control = videoWindowControl())
- control->screenEventHandler(event);
- }
-
- return false;
-}
-
-void MmrEventMediaPlayerControl::readEvents()
-{
- const mmr_event_t *event;
-
- while ((event = mmr_event_get(m_context))) {
- if (event->type == MMR_EVENT_NONE)
- break;
-
- switch (event->type) {
- case MMR_EVENT_STATUS: {
- if (event->data) {
- const strm_string_t *value;
- value = strm_dict_find_rstr(event->data, "bufferstatus");
- if (value) {
- m_bufferStatus = QByteArray(strm_string_get(value));
- if (!m_suspended)
- setMmBufferStatus(m_bufferStatus);
- }
-
- value = strm_dict_find_rstr(event->data, "bufferlevel");
- if (value) {
- const char *cstrValue = strm_string_get(value);
- int level;
- int capacity;
- bool ok;
- std::tie(level, capacity, ok) = parseBufferLevel(QByteArray(cstrValue));
- if (!ok) {
- qCritical("Could not parse buffer capacity from '%s'", cstrValue);
- } else {
- m_bufferLevel = level;
- m_bufferCapacity = capacity;
- setMmBufferLevel(level, capacity);
- }
- }
-
- value = strm_dict_find_rstr(event->data, "suspended");
- if (value) {
- if (!m_suspended) {
- m_suspended = true;
- m_suspendedReason = strm_string_get(value);
- handleMmSuspend(m_suspendedReason);
- }
- } else if (m_suspended) {
- m_suspended = false;
- handleMmSuspendRemoval(m_bufferStatus);
- }
- }
-
- if (event->pos_str) {
- const QByteArray valueBa = QByteArray(event->pos_str);
- bool ok;
- m_position = valueBa.toLongLong(&ok);
- if (!ok) {
- qCritical("Could not parse position from '%s'", valueBa.constData());
- } else {
- setMmPosition(m_position);
- }
- }
- break;
- }
- case MMR_EVENT_STATE: {
- if (event->state == MMR_STATE_PLAYING && m_speed != event->speed) {
- m_speed = event->speed;
- if (m_speed == 0)
- handleMmPause();
- else
- handleMmPlay();
- }
- break;
- }
- case MMR_EVENT_METADATA: {
- updateMetaData(event->data);
- break;
- }
- case MMR_EVENT_ERROR:
- case MMR_EVENT_NONE:
- case MMR_EVENT_OVERFLOW:
- case MMR_EVENT_WARNING:
- case MMR_EVENT_PLAYLIST:
- case MMR_EVENT_INPUT:
- case MMR_EVENT_OUTPUT:
- case MMR_EVENT_CTXTPAR:
- case MMR_EVENT_TRKPAR:
- case MMR_EVENT_OTHER: {
- break;
- }
- }
-
- // Currently, any exit from the playing state is considered a stop (end-of-media).
- // If you ever need to separate end-of-media from things like "stopped unexpectedly"
- // or "stopped because of an error", you'll find that end-of-media is signaled by an
- // MMR_EVENT_ERROR of MMR_ERROR_NONE with state changed to MMR_STATE_STOPPED.
- if (event->state != m_state && m_state == MMR_STATE_PLAYING)
- handleMmStopped();
- m_state = event->state;
- }
-
- if (m_eventThread)
- m_eventThread->signalRead();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/mediaplayer/mmreventmediaplayercontrol.h b/src/plugins/qnx/mediaplayer/mmreventmediaplayercontrol.h
deleted file mode 100644
index e6c138f89..000000000
--- a/src/plugins/qnx/mediaplayer/mmreventmediaplayercontrol.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 QNX Software Systems. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 MMREVENTMEDIAPLAYERCONTROL_H
-#define MMREVENTMEDIAPLAYERCONTROL_H
-
-#include "mmrenderermediaplayercontrol.h"
-
-#include <mm/renderer/events.h>
-
-QT_BEGIN_NAMESPACE
-
-class MmrEventThread;
-
-class MmrEventMediaPlayerControl final : public MmRendererMediaPlayerControl
-{
- Q_OBJECT
-public:
- explicit MmrEventMediaPlayerControl(QObject *parent = 0);
- ~MmrEventMediaPlayerControl() override;
-
- void startMonitoring() override;
- void stopMonitoring() override;
- void resetMonitoring() override;
-
- bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
-
-private Q_SLOTS:
- void readEvents();
-
-private:
- MmrEventThread *m_eventThread;
-
- // status properties.
- QByteArray m_bufferStatus;
- int m_bufferLevel;
- int m_bufferCapacity;
- qint64 m_position;
- bool m_suspended;
- QByteArray m_suspendedReason;
-
- // state properties.
- mmr_state_t m_state;
- int m_speed;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/mediaplayer/mmreventthread.cpp b/src/plugins/qnx/mediaplayer/mmreventthread.cpp
deleted file mode 100644
index 25f26e216..000000000
--- a/src/plugins/qnx/mediaplayer/mmreventthread.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 QNX Software Systems. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "mmreventthread.h"
-
-#include <QtCore/QDebug>
-
-#include <errno.h>
-#include <mm/renderer/events.h>
-#include <sys/neutrino.h>
-
-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;
-
-MmrEventThread::MmrEventThread(mmr_context_t *context)
- : QThread(),
- m_mmrContext(context)
-{
- if (Q_UNLIKELY((m_channelId = ChannelCreate(_NTO_CHF_DISCONNECT
- | _NTO_CHF_UNBLOCK
- | _NTO_CHF_PRIVATE)) == -1)) {
- qFatal("MmrEventThread: Can't continue without a channel");
- }
-
- if (Q_UNLIKELY((m_connectionId = ConnectAttach(0, 0, m_channelId,
- _NTO_SIDE_CHANNEL, 0)) == -1)) {
- ChannelDestroy(m_channelId);
- qFatal("MmrEventThread: Can't continue without a channel connection");
- }
-
- SIGEV_PULSE_INIT(&m_mmrEvent, m_connectionId, SIGEV_PULSE_PRIO_INHERIT, c_mmrCode, 0);
-}
-
-MmrEventThread::~MmrEventThread()
-{
- // block until thread terminates
- shutdown();
-
- ConnectDetach(m_connectionId);
- ChannelDestroy(m_channelId);
-}
-
-void MmrEventThread::run()
-{
- int armResult = mmr_event_arm(m_mmrContext, &m_mmrEvent);
- if (armResult > 0)
- emit eventPending();
-
- while (1) {
- struct _pulse msg;
- memset(&msg, 0, sizeof(msg));
- int receiveId = MsgReceive(m_channelId, &msg, sizeof(msg), nullptr);
- if (receiveId == 0) {
- if (msg.code == c_mmrCode) {
- emit eventPending();
- } else if (msg.code == c_readCode) {
- armResult = mmr_event_arm(m_mmrContext, &m_mmrEvent);
- if (armResult > 0)
- emit eventPending();
- } else if (msg.code == c_quitCode) {
- break;
- } else {
- qWarning() << Q_FUNC_INFO << "Unexpected pulse" << msg.code;
- }
- } else if (receiveId > 0) {
- qWarning() << Q_FUNC_INFO << "Unexpected message" << msg.code;
- } else {
- qWarning() << Q_FUNC_INFO << "MsgReceive error" << strerror(errno);
- }
- }
-}
-
-void MmrEventThread::signalRead()
-{
- MsgSendPulse(m_connectionId, SIGEV_PULSE_PRIO_INHERIT, c_readCode, 0);
-}
-
-void MmrEventThread::shutdown()
-{
- MsgSendPulse(m_connectionId, SIGEV_PULSE_PRIO_INHERIT, c_quitCode, 0);
-
- // block until thread terminates
- wait();
-}
diff --git a/src/plugins/qnx/mediaplayer/mmreventthread.h b/src/plugins/qnx/mediaplayer/mmreventthread.h
deleted file mode 100644
index f7bc5cf5e..000000000
--- a/src/plugins/qnx/mediaplayer/mmreventthread.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 QNX Software Systems. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 MMREVENTTHREAD_H
-#define MMREVENTTHREAD_H
-
-#include <QtCore/QThread>
-
-#include <sys/neutrino.h>
-#include <sys/siginfo.h>
-
-QT_BEGIN_NAMESPACE
-
-typedef struct mmr_context mmr_context_t;
-
-class MmrEventThread : public QThread
-{
- Q_OBJECT
-
-public:
- MmrEventThread(mmr_context_t *context);
- ~MmrEventThread() override;
-
- void signalRead();
-
-protected:
- void run() override;
-
-Q_SIGNALS:
- void eventPending();
-
-private:
- void shutdown();
-
- int m_channelId;
- int m_connectionId;
- struct sigevent m_mmrEvent;
- mmr_context_t *m_mmrContext;
-};
-
-QT_END_NAMESPACE
-
-#endif // MMREVENTTHREAD_H
diff --git a/src/plugins/qnx/neutrino_mediaservice.json b/src/plugins/qnx/neutrino_mediaservice.json
deleted file mode 100644
index 919368d73..000000000
--- a/src/plugins/qnx/neutrino_mediaservice.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Keys": ["neutrinomultimedia"],
- "Services": ["org.qt-project.qt.mediaplayer"]
-}
diff --git a/src/plugins/qnx/neutrinoserviceplugin.cpp b/src/plugins/qnx/neutrinoserviceplugin.cpp
deleted file mode 100644
index 842796c83..000000000
--- a/src/plugins/qnx/neutrinoserviceplugin.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-#include "neutrinoserviceplugin.h"
-
-#include "mmrenderermediaplayerservice.h"
-
-QT_BEGIN_NAMESPACE
-
-NeutrinoServicePlugin::NeutrinoServicePlugin()
-{
-}
-
-QMediaService *NeutrinoServicePlugin::create(const QString &key)
-{
- if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER))
- return new MmRendererMediaPlayerService();
-
- return 0;
-}
-
-void NeutrinoServicePlugin::release(QMediaService *service)
-{
- delete service;
-}
-
-QMediaServiceProviderHint::Features NeutrinoServicePlugin::supportedFeatures(const QByteArray &service) const
-{
- Q_UNUSED(service);
- return QMediaServiceProviderHint::Features();
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/qnx/neutrinoserviceplugin.h b/src/plugins/qnx/neutrinoserviceplugin.h
deleted file mode 100644
index 62ed7f0bd..000000000
--- a/src/plugins/qnx/neutrinoserviceplugin.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/****************************************************************************
-**
-** 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 NEUTRINOSERVICEPLUGIN_H
-#define NEUTRINOSERVICEPLUGIN_H
-
-#include <qmediaserviceproviderplugin.h>
-
-QT_BEGIN_NAMESPACE
-
-class NeutrinoServicePlugin
- : public QMediaServiceProviderPlugin,
- public QMediaServiceFeaturesInterface
-{
- Q_OBJECT
- Q_INTERFACES(QMediaServiceFeaturesInterface)
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "neutrino_mediaservice.json")
-public:
- NeutrinoServicePlugin();
-
- QMediaService *create(const QString &key) override;
- void release(QMediaService *service) override;
- QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const override;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/qnx/qnx.pro b/src/plugins/qnx/qnx.pro
deleted file mode 100644
index 960c614e0..000000000
--- a/src/plugins/qnx/qnx.pro
+++ /dev/null
@@ -1,15 +0,0 @@
-TARGET = qtmedia_qnx
-QT += multimedia-private gui-private core-private
-
-LIBS += -lscreen
-
-include(common/common.pri)
-include(mediaplayer/mediaplayer.pri)
-
-HEADERS += neutrinoserviceplugin.h
-SOURCES += neutrinoserviceplugin.cpp
-OTHER_FILES += neutrino_mediaservice.json
-PLUGIN_CLASS_NAME = NeutrinoServicePlugin
-
-PLUGIN_TYPE = mediaservice
-load(qt_plugin)
diff --git a/src/plugins/resourcepolicy/resourcepolicy.json b/src/plugins/resourcepolicy/resourcepolicy.json
deleted file mode 100644
index d751b849c..000000000
--- a/src/plugins/resourcepolicy/resourcepolicy.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Keys": ["resourcepolicy"],
- "Services": ["org.qt-project.qt.mediaplayerresourcesetfactory", "default"]
-}
diff --git a/src/plugins/resourcepolicy/resourcepolicy.pro b/src/plugins/resourcepolicy/resourcepolicy.pro
deleted file mode 100644
index 89e75be1b..000000000
--- a/src/plugins/resourcepolicy/resourcepolicy.pro
+++ /dev/null
@@ -1,22 +0,0 @@
-TARGET = resourceqt
-
-QT += multimedia-private
-
-QMAKE_USE += libresourceqt5
-
-INCLUDEPATH += $$PWD \
- $${SOURCE_DIR}/src/multimedia
-
-HEADERS += \
- $$PWD/resourcepolicyplugin.h \
- $$PWD/resourcepolicyimpl.h \
- $$PWD/resourcepolicyint.h
-
-SOURCES += \
- $$PWD/resourcepolicyplugin.cpp \
- $$PWD/resourcepolicyimpl.cpp \
- $$PWD/resourcepolicyint.cpp
-
-PLUGIN_TYPE = resourcepolicy
-PLUGIN_CLASS_NAME = ResourceQtPolicyPlugin
-load(qt_plugin)
diff --git a/src/plugins/resourcepolicy/resourcepolicyimpl.cpp b/src/plugins/resourcepolicy/resourcepolicyimpl.cpp
deleted file mode 100644
index 0ce02e9b4..000000000
--- a/src/plugins/resourcepolicy/resourcepolicyimpl.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Jolla Ltd, author: <robin.burchell@jollamobile.com>
-** 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 <QGlobalStatic>
-
-#include <policy/resource.h>
-#include <policy/resources.h>
-#include <policy/resource-set.h>
-
-#include "resourcepolicyimpl.h"
-#include "resourcepolicyint.h"
-
-Q_GLOBAL_STATIC(ResourcePolicyInt, globalResourcePolicyInt);
-
-ResourcePolicyImpl::ResourcePolicyImpl(QObject *parent)
- : QMediaPlayerResourceSetInterface(parent)
-{
- ResourcePolicyInt *set = globalResourcePolicyInt;
- set->addClient(this);
-}
-
-ResourcePolicyImpl::~ResourcePolicyImpl()
-{
- ResourcePolicyInt *set = globalResourcePolicyInt;
- if (!globalResourcePolicyInt.isDestroyed())
- set->removeClient(this);
-}
-
-bool ResourcePolicyImpl::isVideoEnabled() const
-{
- ResourcePolicyInt *set = globalResourcePolicyInt;
- return set->isVideoEnabled(this);
-}
-
-void ResourcePolicyImpl::setVideoEnabled(bool videoEnabled)
-{
- ResourcePolicyInt *set = globalResourcePolicyInt;
- set->setVideoEnabled(this, videoEnabled);
-}
-
-void ResourcePolicyImpl::acquire()
-{
- ResourcePolicyInt *set = globalResourcePolicyInt;
- set->acquire(this);
-}
-
-void ResourcePolicyImpl::release()
-{
- ResourcePolicyInt *set = globalResourcePolicyInt;
- set->release(this);
-}
-
-bool ResourcePolicyImpl::isGranted() const
-{
- ResourcePolicyInt *set = globalResourcePolicyInt;
- return set->isGranted(this);
-}
-
-bool ResourcePolicyImpl::isAvailable() const
-{
- ResourcePolicyInt *set = globalResourcePolicyInt;
- return set->isAvailable();
-}
diff --git a/src/plugins/resourcepolicy/resourcepolicyimpl.h b/src/plugins/resourcepolicy/resourcepolicyimpl.h
deleted file mode 100644
index 27eb32dc8..000000000
--- a/src/plugins/resourcepolicy/resourcepolicyimpl.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Jolla Ltd, author: <robin.burchell@jollamobile.com>
-** 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 RESOURCEPOLICYIMPL_H
-#define RESOURCEPOLICYIMPL_H
-
-#include <QObject>
-
-#include <private/qmediaresourceset_p.h>
-
-namespace ResourcePolicy {
- class ResourceSet;
-};
-
-class ResourcePolicyImpl : public QMediaPlayerResourceSetInterface
-{
- Q_OBJECT
-public:
- ResourcePolicyImpl(QObject *parent = 0);
- ~ResourcePolicyImpl();
-
- bool isVideoEnabled() const;
- void setVideoEnabled(bool videoEnabled);
- void acquire();
- void release();
- bool isGranted() const;
- bool isAvailable() const;
-};
-
-#endif // RESOURCEPOLICYIMPL_H
diff --git a/src/plugins/resourcepolicy/resourcepolicyint.cpp b/src/plugins/resourcepolicy/resourcepolicyint.cpp
deleted file mode 100644
index fc6a3d874..000000000
--- a/src/plugins/resourcepolicy/resourcepolicyint.cpp
+++ /dev/null
@@ -1,405 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Jolla Ltd, author: <robin.burchell@jollamobile.com>
-** 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 <policy/resource.h>
-#include <policy/resources.h>
-#include <policy/resource-set.h>
-
-#include "resourcepolicyint.h"
-#include "resourcepolicyimpl.h"
-
-#include <QMap>
-#include <QByteArray>
-#include <QString>
-
-static int clientid = 0;
-
-ResourcePolicyInt::ResourcePolicyInt(QObject *parent)
- : QObject(parent)
- , m_acquired(0)
- , m_status(Initial)
- , m_video(0)
- , m_available(false)
- , m_resourceSet(0)
-{
- const char *resourceClass = "player";
-
- QByteArray envVar = qgetenv("NEMO_RESOURCE_CLASS_OVERRIDE");
- if (!envVar.isEmpty()) {
- QString data(envVar);
- // Only allow few resource classes
- if (data == "navigator" ||
- data == "call" ||
- data == "camera" ||
- data == "game" ||
- data == "player" ||
- data == "event")
- resourceClass = envVar.constData();
- }
-
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### Using resource class " << resourceClass;
-#endif
-
- m_resourceSet = new ResourcePolicy::ResourceSet(resourceClass, this);
- m_resourceSet->setAlwaysReply();
-
- connect(m_resourceSet, SIGNAL(resourcesGranted(QList<ResourcePolicy::ResourceType>)),
- this, SLOT(handleResourcesGranted()));
- connect(m_resourceSet, SIGNAL(resourcesDenied()),
- this, SLOT(handleResourcesDenied()));
- connect(m_resourceSet, SIGNAL(resourcesReleased()),
- this, SLOT(handleResourcesReleased()));
- connect(m_resourceSet, SIGNAL(lostResources()),
- this, SLOT(handleResourcesLost()));
- connect(m_resourceSet, SIGNAL(resourcesReleasedByManager()),
- this, SLOT(handleResourcesReleasedByManager()));
-
- connect(m_resourceSet, SIGNAL(resourcesBecameAvailable(const QList<ResourcePolicy::ResourceType>)),
- this, SLOT(handleResourcesBecameAvailable(const QList<ResourcePolicy::ResourceType>)));
-
- ResourcePolicy::AudioResource *audioResource = new ResourcePolicy::AudioResource(resourceClass);
-
- audioResource->setProcessID(QCoreApplication::applicationPid());
- audioResource->setStreamTag("media.name", "*");
- m_resourceSet->addResourceObject(audioResource);
- m_resourceSet->update();
-}
-
-ResourcePolicyInt::~ResourcePolicyInt()
-{
- delete m_resourceSet;
- m_resourceSet = 0;
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### Tearing down singleton.";
-#endif
-}
-
-void ResourcePolicyInt::addClient(ResourcePolicyImpl *client)
-{
- clientEntry entry;
- entry.id = clientid++;
- entry.client = client;
- entry.status = Initial;
- entry.videoEnabled = false;
- m_clients.insert(entry.client, entry);
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### Add client " << client << " : " << entry.id;
-#endif
-}
-
-void ResourcePolicyInt::removeClient(ResourcePolicyImpl *client)
-{
- QMap<const ResourcePolicyImpl*, clientEntry>::iterator i = m_clients.find(client);
- if (i != m_clients.end()) {
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### Remove client " << client << " : " << i.value().id;
-#endif
- if (i.value().status == GrantedResource)
- --m_acquired;
- m_clients.erase(i);
- }
-
- if (m_acquired == 0 && m_status != Initial) {
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### Remove client, acquired = 0, release";
-#endif
- m_resourceSet->release();
- m_status = Initial;
- }
-}
-
-bool ResourcePolicyInt::isVideoEnabled(const ResourcePolicyImpl *client) const
-{
- QMap<const ResourcePolicyImpl*, clientEntry>::const_iterator i = m_clients.find(client);
- if (i != m_clients.constEnd())
- return i.value().videoEnabled;
-
- return false;
-}
-
-void ResourcePolicyInt::setVideoEnabled(const ResourcePolicyImpl *client, bool videoEnabled)
-{
- bool update = false;
-
- QMap<const ResourcePolicyImpl*, clientEntry>::iterator i = m_clients.find(client);
- if (i != m_clients.end()) {
- if (videoEnabled == i.value().videoEnabled)
- return;
-
- if (videoEnabled) {
- if (m_video > 0) {
- i.value().videoEnabled = true;
- } else {
- m_resourceSet->addResource(ResourcePolicy::VideoPlaybackType);
- update = true;
- }
- ++m_video;
- } else {
- --m_video;
- i.value().videoEnabled = false;
- if (m_video == 0) {
- m_resourceSet->deleteResource(ResourcePolicy::VideoPlaybackType);
- update = true;
- }
- }
- }
-
- if (update)
- m_resourceSet->update();
-}
-
-void ResourcePolicyInt::acquire(const ResourcePolicyImpl *client)
-{
- QMap<const ResourcePolicyImpl*, clientEntry>::iterator i = m_clients.find(client);
- if (i != m_clients.end()) {
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### " << i.value().id << ": ACQUIRE";
-#endif
- if (i.value().status == Initial) {
-
- if (m_status == RequestedResource) {
- i.value().status = RequestedResource;
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### " << i.value().id << ": Already requesting, set client as RequestResource and return";
-#endif
- return;
- }
-
- if (m_status == GrantedResource) {
- ++m_acquired;
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### " << i.value().id << ": Already granted, set as GrantedResource and return";
-#endif
- i.value().status = GrantedResource;
- emit i.value().client->resourcesGranted();
- return;
- }
- } else if (i.value().status == RequestedResource) {
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### " << i.value().id << ": Already requesting, return";
-#endif
- return;
- } else {
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### " << i.value().id << ": Already granted, return ";
-#endif
- return;
- }
- i.value().status = RequestedResource;
- m_status = RequestedResource;
-
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### " << i.value().id << ": ACQUIRE call resourceSet->acquire()";
-#endif
- // If m_status was Initial this is the first time resources are requested,
- // so let's actually do the acquiring
- m_resourceSet->acquire();
- }
-}
-
-void ResourcePolicyInt::release(const ResourcePolicyImpl *client)
-{
- QMap<const ResourcePolicyImpl*, clientEntry>::iterator i = m_clients.find(client);
- if (i != m_clients.end()) {
- if (i.value().status == GrantedResource) {
- i.value().status = Initial;
- --m_acquired;
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### " << i.value().id << ": RELEASE, acquired (" << m_acquired << ")";
-#endif
- emit i.value().client->resourcesReleased();
- }
- }
-
- if (m_acquired == 0 && m_status != Initial) {
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### " << i.value().id << ": RELEASE call resourceSet->release()";
-#endif
- m_resourceSet->release();
- m_status = Initial;
- }
-}
-
-bool ResourcePolicyInt::isGranted(const ResourcePolicyImpl *client) const
-{
- QMap<const ResourcePolicyImpl*, clientEntry>::const_iterator i = m_clients.find(client);
- if (i != m_clients.constEnd()) {
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### " << i.value().id << ": IS GRANTED, status: " << i.value().status;
-#endif
- return i.value().status == GrantedResource;
- }
-
- return false;
-}
-
-bool ResourcePolicyInt::isAvailable() const
-{
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### isAvailable " << m_available;
-#endif
- return m_available;
-}
-
-void ResourcePolicyInt::handleResourcesGranted()
-{
- m_status = GrantedResource;
- QMap<const ResourcePolicyImpl*, clientEntry>::iterator i = m_clients.begin();
- while (i != m_clients.end()) {
- if (i.value().status == RequestedResource) {
- ++m_acquired;
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### " << i.value().id << ": HANDLE GRANTED, acquired (" << m_acquired << ") emitting resourcesGranted()";
-#endif
- i.value().status = GrantedResource;
- emit i.value().client->resourcesGranted();
- }
- ++i;
- }
-}
-
-void ResourcePolicyInt::handleResourcesDenied()
-{
- m_status = Initial;
- m_acquired = 0;
- QMap<const ResourcePolicyImpl*, clientEntry>::iterator i = m_clients.begin();
- while (i != m_clients.end()) {
- if (i.value().status == RequestedResource) {
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### " << i.value().id << ": HANDLE DENIED, acquired (" << m_acquired << ") emitting resourcesDenied()";
-#endif
- i.value().status = Initial;
- emit i.value().client->resourcesDenied();
- }
- // Do we need to act for clients that are in granted state?
- ++i;
- }
-}
-
-void ResourcePolicyInt::handleResourcesReleased()
-{
- m_status = Initial;
- m_acquired = 0;
- QMap<const ResourcePolicyImpl*, clientEntry>::iterator i = m_clients.begin();
- while (i != m_clients.end()) {
- if (i.value().status != Initial) {
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### " << i.value().id << ": HANDLE RELEASED, acquired (" << m_acquired << ") emitting resourcesReleased()";
-#endif
- i.value().status = Initial;
- emit i.value().client->resourcesReleased();
- }
- ++i;
- }
-}
-
-void ResourcePolicyInt::handleResourcesLost()
-{
- // If resources were granted switch to acquiring state,
- // so that if the resources are freed elsewhere we
- // will acquire them again properly.
- if (m_status == GrantedResource)
- m_status = RequestedResource;
-
- m_acquired = 0;
-
- QMap<const ResourcePolicyImpl*, clientEntry>::iterator i = m_clients.begin();
- while (i != m_clients.end()) {
- if (i.value().status == GrantedResource) {
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### " << i.value().id << ": HANDLE LOST, acquired (" << m_acquired << ") emitting resourcesLost()";
-#endif
- i.value().status = RequestedResource;
- emit i.value().client->resourcesLost();
- }
- ++i;
- }
-}
-
-void ResourcePolicyInt::handleResourcesReleasedByManager()
-{
- if (m_status != Initial)
- m_status = Initial;
-
- m_acquired = 0;
-
- QMap<const ResourcePolicyImpl*, clientEntry>::iterator i = m_clients.begin();
- while (i != m_clients.end()) {
- if (i.value().status != Initial) {
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### " << i.value().id << ": HANDLE RELEASEDBYMANAGER, acquired (" << m_acquired << ") emitting resourcesLost()";
-#endif
- i.value().status = Initial;
- emit i.value().client->resourcesLost();
- }
- ++i;
- }
-}
-
-void ResourcePolicyInt::handleResourcesBecameAvailable(const QList<ResourcePolicy::ResourceType> &resources)
-{
- bool available = false;
-
- for (int i = 0; i < resources.size(); ++i) {
- if (resources.at(i) == ResourcePolicy::AudioPlaybackType)
- available = true;
- }
-
- availabilityChanged(available);
-}
-
-void ResourcePolicyInt::availabilityChanged(bool available)
-{
- if (available == m_available)
- return;
-
- m_available = available;
-
- QMap<const ResourcePolicyImpl*, clientEntry>::const_iterator i = m_clients.constBegin();
- while (i != m_clients.constEnd()) {
-#ifdef RESOURCE_DEBUG
- qDebug() << "##### " << i.value().id << ": emitting availabilityChanged(" << m_available << ")";
-#endif
- emit i.value().client->availabilityChanged(m_available);
- ++i;
- }
-}
diff --git a/src/plugins/resourcepolicy/resourcepolicyint.h b/src/plugins/resourcepolicy/resourcepolicyint.h
deleted file mode 100644
index 9ba79c1bc..000000000
--- a/src/plugins/resourcepolicy/resourcepolicyint.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Jolla Ltd, author: <robin.burchell@jollamobile.com>
-** 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 RESOURCEPOLICYINT_H
-#define RESOURCEPOLICYINT_H
-
-#include <QObject>
-#include <QMap>
-
-#include <policy/resource-set.h>
-#include <policy/resource.h>
-#include <private/qmediaresourceset_p.h>
-#include "resourcepolicyimpl.h"
-
-namespace ResourcePolicy {
- class ResourceSet;
-};
-
-enum ResourceStatus {
- Initial = 0,
- RequestedResource,
- GrantedResource
-};
-
-struct clientEntry {
- int id;
- ResourcePolicyImpl *client;
- ResourceStatus status;
- bool videoEnabled;
-};
-
-class ResourcePolicyInt : public QObject
-{
- Q_OBJECT
-public:
- ResourcePolicyInt(QObject *parent = 0);
- ~ResourcePolicyInt();
-
- bool isVideoEnabled(const ResourcePolicyImpl *client) const;
- void setVideoEnabled(const ResourcePolicyImpl *client, bool videoEnabled);
- void acquire(const ResourcePolicyImpl *client);
- void release(const ResourcePolicyImpl *client);
- bool isGranted(const ResourcePolicyImpl *client) const;
- bool isAvailable() const;
-
- void addClient(ResourcePolicyImpl *client);
- void removeClient(ResourcePolicyImpl *client);
-
-private slots:
- void handleResourcesGranted();
- void handleResourcesDenied();
- void handleResourcesReleased();
- void handleResourcesLost();
- void handleResourcesReleasedByManager();
- void handleResourcesBecameAvailable(const QList<ResourcePolicy::ResourceType> &resources);
-
-private:
- void availabilityChanged(bool available);
-
- QMap<const ResourcePolicyImpl*, clientEntry> m_clients;
-
- int m_acquired;
- ResourceStatus m_status;
- int m_video;
- bool m_available;
- ResourcePolicy::ResourceSet *m_resourceSet;
-};
-
-#endif // RESOURCEPOLICYINT_H
diff --git a/src/plugins/resourcepolicy/resourcepolicyplugin.cpp b/src/plugins/resourcepolicy/resourcepolicyplugin.cpp
deleted file mode 100644
index bc78d6fbe..000000000
--- a/src/plugins/resourcepolicy/resourcepolicyplugin.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Jolla Ltd, author: <robin.burchell@jollamobile.com>
-** 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 <QDebug>
-
-#include "resourcepolicyplugin.h"
-#include "resourcepolicyimpl.h"
-
-ResourcePolicyPlugin::ResourcePolicyPlugin(QObject *parent)
- : QMediaResourcePolicyPlugin(parent)
-{
-}
-
-QObject *ResourcePolicyPlugin::create(const QString &interfaceId)
-{
- // TODO: what is interfaceId for?
- return new ResourcePolicyImpl(this);
-}
-
-void ResourcePolicyPlugin::destroy(QObject *resourceSet)
-{
- // TODO: do we need to do anything more elaborate here?
- delete resourceSet;
-}
-
diff --git a/src/plugins/resourcepolicy/resourcepolicyplugin.h b/src/plugins/resourcepolicy/resourcepolicyplugin.h
deleted file mode 100644
index 6661c73de..000000000
--- a/src/plugins/resourcepolicy/resourcepolicyplugin.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Jolla Ltd, author: <robin.burchell@jollamobile.com>
-** 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 RESOURCEPOLICYPLUGIN_H
-#define RESOURCEPOLICYPLUGIN_H
-
-#include <private/qmediaresourcepolicyplugin_p.h>
-#include <QObject>
-
-class ResourcePolicyPlugin : public QMediaResourcePolicyPlugin
-{
- Q_OBJECT
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaresourcesetfactory/5.0" FILE "resourcepolicy.json")
- Q_INTERFACES(QMediaResourceSetFactoryInterface)
-public:
- ResourcePolicyPlugin(QObject *parent = 0);
-
- QObject *create(const QString &interfaceId);
- void destroy(QObject *resourceSet);
-};
-
-#endif // RESOURCEPOLICYPLUGIN_H
diff --git a/src/plugins/videonode/CMakeLists.txt b/src/plugins/videonode/CMakeLists.txt
new file mode 100644
index 000000000..176628276
--- /dev/null
+++ b/src/plugins/videonode/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+# Generated from videonode.pro.
+
+if(QT_FEATURE_gpu_vivante)
+ add_subdirectory(imx6)
+endif()
diff --git a/src/plugins/videonode/imx6/CMakeLists.txt b/src/plugins/videonode/imx6/CMakeLists.txt
new file mode 100644
index 000000000..659f73469
--- /dev/null
+++ b/src/plugins/videonode/imx6/CMakeLists.txt
@@ -0,0 +1,43 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+# Generated from imx6.pro.
+
+#####################################################################
+## QSGVivanteVideoNodeFactory Plugin:
+#####################################################################
+
+qt_internal_add_plugin(QSGVivanteVideoNodeFactoryPlugin
+ OUTPUT_NAME imx6vivantevideonode
+ PLUGIN_TYPE video/videonode
+ CLASS_NAME QSGVivanteVideoNodeFactory
+ SOURCES
+ qsgvivantevideomaterial.cpp qsgvivantevideomaterial.h
+ qsgvivantevideomaterialshader.cpp qsgvivantevideomaterialshader.h
+ qsgvivantevideonode.cpp qsgvivantevideonode.h
+ qsgvivantevideonodefactory.cpp qsgvivantevideonodefactory.h
+ LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::MultimediaPrivate
+ Qt::MultimediaQuickPrivate
+ GStreamer::GStreamer # special case
+)
+
+# Resources:
+set(imx6_resource_files
+ "shaders/rgba.frag.qsb"
+ "shaders/rgba.vert.qsb"
+)
+
+qt_internal_add_resource(QSGVivanteVideoNodeFactoryPlugin "imx6"
+ PREFIX
+ "/imx6"
+ FILES
+ ${imx6_resource_files}
+)
+
+
+#### Keys ignored in scope 1:.:.:imx6.pro:<TRUE>:
+# OTHER_FILES = "imx6.json"
+# PLUGIN_EXTENDS = "quick"
diff --git a/src/plugins/videonode/imx6/imx6.pro b/src/plugins/videonode/imx6/imx6.pro
deleted file mode 100644
index 4b2a2e720..000000000
--- a/src/plugins/videonode/imx6/imx6.pro
+++ /dev/null
@@ -1,28 +0,0 @@
-TARGET = imx6vivantevideonode
-
-QT += multimedia-private qtmultimediaquicktools-private multimediagsttools-private
-
-QMAKE_USE += gstreamer
-
-HEADERS += \
- qsgvivantevideonode.h \
- qsgvivantevideomaterialshader.h \
- qsgvivantevideomaterial.h \
- qsgvivantevideonodefactory.h
-
-SOURCES += \
- qsgvivantevideonode.cpp \
- qsgvivantevideomaterialshader.cpp \
- qsgvivantevideomaterial.cpp \
- qsgvivantevideonodefactory.cpp
-
-OTHER_FILES += \
- imx6.json
-
-RESOURCES += \
- imx6.qrc
-
-PLUGIN_TYPE = video/videonode
-PLUGIN_EXTENDS = quick
-PLUGIN_CLASS_NAME = QSGVivanteVideoNodeFactory
-load(qt_plugin)
diff --git a/src/plugins/videonode/imx6/imx6.qrc b/src/plugins/videonode/imx6/imx6.qrc
deleted file mode 100644
index c64fa4c64..000000000
--- a/src/plugins/videonode/imx6/imx6.qrc
+++ /dev/null
@@ -1,6 +0,0 @@
-<RCC>
- <qresource prefix="/imx6">
- <file>shaders/rgba.vert.qsb</file>
- <file>shaders/rgba.frag.qsb</file>
- </qresource>
-</RCC>
diff --git a/src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp b/src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp
index 2e6c712e3..599dbb9e2 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>
@@ -44,6 +8,7 @@
#include "qsgvivantevideomaterialshader.h"
#include "qsgvivantevideonode.h"
#include "private/qsgvideotexture_p.h"
+#include "private/qvideoframe_p.h"
#include <QOpenGLContext>
#include <QThread>
@@ -52,7 +17,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
@@ -60,7 +27,7 @@ QSGVivanteVideoMaterial::QSGVivanteVideoMaterial() :
mOpacity(1.0),
mWidth(0),
mHeight(0),
- mFormat(QVideoFrame::Format_Invalid),
+ mFormat(QVideoFrameFormat::Format_Invalid),
mCurrentTexture(0),
mMappable(true),
mTexDirectTexture(0)
@@ -107,7 +74,7 @@ void QSGVivanteVideoMaterial::setCurrentFrame(const QVideoFrame &frame, QSGVideo
{
QMutexLocker lock(&mFrameMutex);
mCurrentFrame = frame;
- mMappable = mMapError == GL_NO_ERROR && !flags.testFlag(QSGVideoNode::FrameFiltered);
+ mMappable = mMapError == GL_NO_ERROR;
#ifdef QT_VIVANTE_VIDEO_DEBUG
qDebug() << Q_FUNC_INFO << " new frame: " << frame;
@@ -176,7 +143,7 @@ GLuint QSGVivanteVideoMaterial::vivanteMapping(QVideoFrame vF)
clearTextures();
}
- if (vF.map(QAbstractVideoBuffer::ReadOnly)) {
+ if (vF.map(QtVideo::MapMode::ReadOnly)) {
if (mMappable) {
if (!mBitsToTextureMap.contains(vF.bits())) {
@@ -225,7 +192,7 @@ GLuint QSGVivanteVideoMaterial::vivanteMapping(QVideoFrame vF)
GLuint physical = ~0U;
#if GST_CHECK_VERSION(1,14,0)
- auto buffer = reinterpret_cast<QGstVideoBuffer *>(vF.buffer());
+ auto buffer = reinterpret_cast<QGstVideoBuffer *>(QVideoFramePrivate::buffer(vF));
auto mem = gst_buffer_peek_memory(buffer->buffer(), 0);
auto phys_addr = gst_is_phys_memory(mem) ? gst_phys_memory_get_phys_addr(mem) : 0;
if (phys_addr)
@@ -273,14 +240,14 @@ GLuint QSGVivanteVideoMaterial::vivanteMapping(QVideoFrame vF)
glBindTexture(GL_TEXTURE_2D, mTexDirectTexture);
}
switch (mCurrentFrame.pixelFormat()) {
- case QVideoFrame::Format_YUV420P:
- case QVideoFrame::Format_YV12:
+ case QVideoFrameFormat::Format_YUV420P:
+ case QVideoFrameFormat::Format_YV12:
memcpy(mTexDirectPlanes[0], mCurrentFrame.bits(0), mCurrentFrame.height() * mCurrentFrame.bytesPerLine(0));
memcpy(mTexDirectPlanes[1], mCurrentFrame.bits(1), mCurrentFrame.height() / 2 * mCurrentFrame.bytesPerLine(1));
memcpy(mTexDirectPlanes[2], mCurrentFrame.bits(2), mCurrentFrame.height() / 2 * mCurrentFrame.bytesPerLine(2));
break;
- case QVideoFrame::Format_NV12:
- case QVideoFrame::Format_NV21:
+ case QVideoFrameFormat::Format_NV12:
+ case QVideoFrameFormat::Format_NV21:
memcpy(mTexDirectPlanes[0], mCurrentFrame.bits(0), mCurrentFrame.height() * mCurrentFrame.bytesPerLine(0));
memcpy(mTexDirectPlanes[1], mCurrentFrame.bits(1), mCurrentFrame.height() / 2 * mCurrentFrame.bytesPerLine(1));
break;
diff --git a/src/plugins/videonode/imx6/qsgvivantevideomaterial.h b/src/plugins/videonode/imx6/qsgvivantevideomaterial.h
index 10fd5447f..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
@@ -75,7 +39,7 @@ private:
int mWidth;
int mHeight;
- QVideoFrame::PixelFormat mFormat;
+ QVideoFrameFormat::PixelFormat mFormat;
QMap<const uchar*, GLuint> mBitsToTextureMap;
QVideoFrame mCurrentFrame;
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 c8d83b4b2..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>
@@ -44,9 +8,9 @@
#include "qsgvivantevideomaterialshader.h"
#include "qsgvivantevideomaterial.h"
-QMap<QVideoFrame::PixelFormat, GLenum> QSGVivanteVideoNode::static_VideoFormat2GLFormatMap = QMap<QVideoFrame::PixelFormat, GLenum>();
+QMap<QVideoFrameFormat::PixelFormat, GLenum> QSGVivanteVideoNode::static_VideoFormat2GLFormatMap = QMap<QVideoFrameFormat::PixelFormat, GLenum>();
-QSGVivanteVideoNode::QSGVivanteVideoNode(const QVideoSurfaceFormat &format) :
+QSGVivanteVideoNode::QSGVivanteVideoNode(const QVideoFrameFormat &format) :
mFormat(format)
{
setFlag(QSGNode::OwnsMaterial, true);
@@ -64,40 +28,38 @@ void QSGVivanteVideoNode::setCurrentFrame(const QVideoFrame &frame, FrameFlags f
markDirty(DirtyMaterial);
}
-const QMap<QVideoFrame::PixelFormat, GLenum>& QSGVivanteVideoNode::getVideoFormat2GLFormatMap()
+const QMap<QVideoFrameFormat::PixelFormat, GLenum>& QSGVivanteVideoNode::getVideoFormat2GLFormatMap()
{
if (static_VideoFormat2GLFormatMap.isEmpty()) {
- static_VideoFormat2GLFormatMap.insert(QVideoFrame::Format_YUV420P, GL_VIV_I420);
- static_VideoFormat2GLFormatMap.insert(QVideoFrame::Format_YV12, GL_VIV_YV12);
- static_VideoFormat2GLFormatMap.insert(QVideoFrame::Format_NV12, GL_VIV_NV12);
- static_VideoFormat2GLFormatMap.insert(QVideoFrame::Format_NV21, GL_VIV_NV21);
- static_VideoFormat2GLFormatMap.insert(QVideoFrame::Format_UYVY, GL_VIV_UYVY);
- static_VideoFormat2GLFormatMap.insert(QVideoFrame::Format_YUYV, GL_VIV_YUY2);
- static_VideoFormat2GLFormatMap.insert(QVideoFrame::Format_RGB32, GL_BGRA_EXT);
- static_VideoFormat2GLFormatMap.insert(QVideoFrame::Format_ARGB32, GL_BGRA_EXT);
- static_VideoFormat2GLFormatMap.insert(QVideoFrame::Format_BGR32, GL_RGBA);
- static_VideoFormat2GLFormatMap.insert(QVideoFrame::Format_BGRA32, GL_RGBA);
- static_VideoFormat2GLFormatMap.insert(QVideoFrame::Format_RGB565, GL_RGB565);
+ static_VideoFormat2GLFormatMap.insert(QVideoFrameFormat::Format_YUV420P, GL_VIV_I420);
+ static_VideoFormat2GLFormatMap.insert(QVideoFrameFormat::Format_YV12, GL_VIV_YV12);
+ static_VideoFormat2GLFormatMap.insert(QVideoFrameFormat::Format_NV12, GL_VIV_NV12);
+ static_VideoFormat2GLFormatMap.insert(QVideoFrameFormat::Format_NV21, GL_VIV_NV21);
+ static_VideoFormat2GLFormatMap.insert(QVideoFrameFormat::Format_UYVY, GL_VIV_UYVY);
+ static_VideoFormat2GLFormatMap.insert(QVideoFrameFormat::Format_YUYV, GL_VIV_YUY2);
+ static_VideoFormat2GLFormatMap.insert(QVideoFrameFormat::Format_RGB32, GL_BGRA_EXT);
+ static_VideoFormat2GLFormatMap.insert(QVideoFrameFormat::Format_ARGB32, GL_BGRA_EXT);
+ static_VideoFormat2GLFormatMap.insert(QVideoFrameFormat::Format_BGR32, GL_RGBA);
+ static_VideoFormat2GLFormatMap.insert(QVideoFrameFormat::Format_BGRA32, GL_RGBA);
}
return static_VideoFormat2GLFormatMap;
}
-int QSGVivanteVideoNode::getBytesForPixelFormat(QVideoFrame::PixelFormat pixelformat)
+int QSGVivanteVideoNode::getBytesForPixelFormat(QVideoFrameFormat::PixelFormat pixelformat)
{
switch (pixelformat) {
- case QVideoFrame::Format_YUV420P: return 1;
- case QVideoFrame::Format_YV12: return 1;
- case QVideoFrame::Format_NV12: return 1;
- case QVideoFrame::Format_NV21: return 1;
- case QVideoFrame::Format_UYVY: return 2;
- case QVideoFrame::Format_YUYV: return 2;
- case QVideoFrame::Format_RGB32: return 4;
- case QVideoFrame::Format_ARGB32: return 4;
- case QVideoFrame::Format_BGR32: return 4;
- case QVideoFrame::Format_BGRA32: return 4;
- case QVideoFrame::Format_RGB565: return 2;
+ case QVideoFrameFormat::Format_YUV420P: return 1;
+ case QVideoFrameFormat::Format_YV12: return 1;
+ case QVideoFrameFormat::Format_NV12: return 1;
+ case QVideoFrameFormat::Format_NV21: return 1;
+ case QVideoFrameFormat::Format_UYVY: return 2;
+ case QVideoFrameFormat::Format_YUYV: return 2;
+ case QVideoFrameFormat::Format_RGB32: return 4;
+ case QVideoFrameFormat::Format_ARGB32: return 4;
+ case QVideoFrameFormat::Format_BGR32: return 4;
+ case QVideoFrameFormat::Format_BGRA32: return 4;
default: return 1;
}
}
diff --git a/src/plugins/videonode/imx6/qsgvivantevideonode.h b/src/plugins/videonode/imx6/qsgvivantevideonode.h
index 2796f1e44..05ab4f25b 100644
--- a/src/plugins/videonode/imx6/qsgvivantevideonode.h
+++ b/src/plugins/videonode/imx6/qsgvivantevideonode.h
@@ -1,67 +1,31 @@
-/****************************************************************************
-**
-** 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
#include <private/qsgvideonode_p.h>
-#include <QVideoSurfaceFormat>
+#include <QVideoFrameFormat>
class QSGVivanteVideoMaterial;
class QSGVivanteVideoNode : public QSGVideoNode
{
public:
- QSGVivanteVideoNode(const QVideoSurfaceFormat &format);
+ QSGVivanteVideoNode(const QVideoFrameFormat &format);
~QSGVivanteVideoNode();
- QVideoFrame::PixelFormat pixelFormat() const { return mFormat.pixelFormat(); }
- QAbstractVideoBuffer::HandleType handleType() const { return QAbstractVideoBuffer::NoHandle; }
+ QVideoFrameFormat::PixelFormat pixelFormat() const { return mFormat.pixelFormat(); }
+ QVideoFrame::HandleType handleType() const { return QVideoFrame::NoHandle; }
void setCurrentFrame(const QVideoFrame &frame, FrameFlags flags);
- static const QMap<QVideoFrame::PixelFormat, GLenum>& getVideoFormat2GLFormatMap();
- static int getBytesForPixelFormat(QVideoFrame::PixelFormat pixelformat);
+ static const QMap<QVideoFrameFormat::PixelFormat, GLenum>& getVideoFormat2GLFormatMap();
+ static int getBytesForPixelFormat(QVideoFrameFormat::PixelFormat pixelformat);
private:
- QVideoSurfaceFormat mFormat;
+ QVideoFrameFormat mFormat;
QSGVivanteVideoMaterial *mMaterial;
- static QMap<QVideoFrame::PixelFormat, GLenum> static_VideoFormat2GLFormatMap;
+ static QMap<QVideoFrameFormat::PixelFormat, GLenum> static_VideoFormat2GLFormatMap;
};
#endif // QSGVIDEONODE_VIVANTE_H
diff --git a/src/plugins/videonode/imx6/qsgvivantevideonodefactory.cpp b/src/plugins/videonode/imx6/qsgvivantevideonodefactory.cpp
index 55ed57c10..ca6550776 100644
--- a/src/plugins/videonode/imx6/qsgvivantevideonodefactory.cpp
+++ b/src/plugins/videonode/imx6/qsgvivantevideonodefactory.cpp
@@ -1,57 +1,21 @@
-/****************************************************************************
-**
-** 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"
#include <QtGui/QGuiApplication>
-QList<QVideoFrame::PixelFormat> QSGVivanteVideoNodeFactory::supportedPixelFormats(
- QAbstractVideoBuffer::HandleType handleType) const
+QList<QVideoFrameFormat::PixelFormat> QSGVivanteVideoNodeFactory::supportedPixelFormats(
+ QVideoFrame::HandleType handleType) const
{
const bool isWebGl = QGuiApplication::platformName() == QLatin1String("webgl");
- if (!isWebGl && handleType == QAbstractVideoBuffer::NoHandle)
+ if (!isWebGl && handleType == QVideoFrame::NoHandle)
return QSGVivanteVideoNode::getVideoFormat2GLFormatMap().keys();
else
- return QList<QVideoFrame::PixelFormat>();
+ return QList<QVideoFrameFormat::PixelFormat>();
}
-QSGVideoNode *QSGVivanteVideoNodeFactory::createNode(const QVideoSurfaceFormat &format)
+QSGVideoNode *QSGVivanteVideoNodeFactory::createNode(const QVideoFrameFormat &format)
{
if (supportedPixelFormats(format.handleType()).contains(format.pixelFormat())) {
return new QSGVivanteVideoNode(format);
diff --git a/src/plugins/videonode/imx6/qsgvivantevideonodefactory.h b/src/plugins/videonode/imx6/qsgvivantevideonodefactory.h
index dfb6a0123..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
@@ -50,7 +14,7 @@ public:
Q_PLUGIN_METADATA(IID QSGVideoNodeFactoryInterface_iid FILE "imx6.json")
Q_INTERFACES(QSGVideoNodeFactoryInterface)
- QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const;
- QSGVideoNode *createNode(const QVideoSurfaceFormat &format);
+ QList<QVideoFrameFormat::PixelFormat> supportedPixelFormats(QVideoFrame::HandleType handleType) const;
+ QSGVideoNode *createNode(const QVideoFrameFormat &format);
};
#endif // 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/plugins/videonode/videonode.pro b/src/plugins/videonode/videonode.pro
deleted file mode 100644
index b8bd43329..000000000
--- a/src/plugins/videonode/videonode.pro
+++ /dev/null
@@ -1,6 +0,0 @@
-TEMPLATE = subdirs
-QT_FOR_CONFIG += gui-private multimedia-private
-
-qtConfig(gpu_vivante) {
- SUBDIRS += imx6
-}
diff --git a/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.cpp b/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.cpp
deleted file mode 100644
index 72d142da9..000000000
--- a/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.cpp
+++ /dev/null
@@ -1,469 +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$
-**
-****************************************************************************/
-
-//
-// 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.
-//
-// INTERNAL USE ONLY: Do NOT use for any other purpose.
-//
-
-
-#include <QtCore/qt_windows.h>
-#include <QtCore/QDataStream>
-#include <QtCore/QIODevice>
-#include <mmsystem.h>
-#include "qwindowsaudiodeviceinfo.h"
-#include "qwindowsaudioutils.h"
-
-#if defined(Q_CC_MINGW) && !defined(__MINGW64_VERSION_MAJOR)
-struct IBaseFilter; // Needed for strmif.h from stock MinGW.
-struct _DDPIXELFORMAT;
-typedef struct _DDPIXELFORMAT* LPDDPIXELFORMAT;
-#endif
-
-#include <strmif.h>
-#if !defined(Q_CC_MINGW) || defined(__MINGW64_VERSION_MAJOR)
-# include <uuids.h>
-#else
-
-extern GUID CLSID_AudioInputDeviceCategory;
-extern GUID CLSID_AudioRendererCategory;
-extern GUID IID_ICreateDevEnum;
-extern GUID CLSID_SystemDeviceEnum;
-
-#ifndef __ICreateDevEnum_INTERFACE_DEFINED__
-#define __ICreateDevEnum_INTERFACE_DEFINED__
-
-DECLARE_INTERFACE_(ICreateDevEnum, IUnknown)
-{
- STDMETHOD(CreateClassEnumerator)(REFCLSID clsidDeviceClass,
- IEnumMoniker **ppEnumMoniker,
- DWORD dwFlags) PURE;
-};
-
-#endif // __ICreateDevEnum_INTERFACE_DEFINED__
-
-#ifndef __IErrorLog_INTERFACE_DEFINED__
-#define __IErrorLog_INTERFACE_DEFINED__
-
-DECLARE_INTERFACE_(IErrorLog, IUnknown)
-{
- STDMETHOD(AddError)(THIS_ LPCOLESTR, EXCEPINFO *) PURE;
-};
-
-#endif /* __IErrorLog_INTERFACE_DEFINED__ */
-
-#ifndef __IPropertyBag_INTERFACE_DEFINED__
-#define __IPropertyBag_INTERFACE_DEFINED__
-
-const GUID IID_IPropertyBag = {0x55272A00, 0x42CB, 0x11CE, {0x81, 0x35, 0x00, 0xAA, 0x00, 0x4B, 0xB8, 0x51}};
-
-DECLARE_INTERFACE_(IPropertyBag, IUnknown)
-{
- STDMETHOD(Read)(THIS_ LPCOLESTR, VARIANT *, IErrorLog *) PURE;
- STDMETHOD(Write)(THIS_ LPCOLESTR, VARIANT *) PURE;
-};
-
-#endif /* __IPropertyBag_INTERFACE_DEFINED__ */
-
-#endif // defined(Q_CC_MINGW) && !defined(__MINGW64_VERSION_MAJOR)
-
-QT_BEGIN_NAMESPACE
-
-// For mingw toolchain mmsystem.h only defines half the defines, so add if needed.
-#ifndef WAVE_FORMAT_44M08
-#define WAVE_FORMAT_44M08 0x00000100
-#define WAVE_FORMAT_44S08 0x00000200
-#define WAVE_FORMAT_44M16 0x00000400
-#define WAVE_FORMAT_44S16 0x00000800
-#define WAVE_FORMAT_48M08 0x00001000
-#define WAVE_FORMAT_48S08 0x00002000
-#define WAVE_FORMAT_48M16 0x00004000
-#define WAVE_FORMAT_48S16 0x00008000
-#define WAVE_FORMAT_96M08 0x00010000
-#define WAVE_FORMAT_96S08 0x00020000
-#define WAVE_FORMAT_96M16 0x00040000
-#define WAVE_FORMAT_96S16 0x00080000
-#endif
-
-
-QWindowsAudioDeviceInfo::QWindowsAudioDeviceInfo(QByteArray dev, QAudio::Mode mode)
-{
- QDataStream ds(&dev, QIODevice::ReadOnly);
- ds >> devId >> device;
- this->mode = mode;
-
- updateLists();
-}
-
-QWindowsAudioDeviceInfo::~QWindowsAudioDeviceInfo()
-{
- close();
-}
-
-bool QWindowsAudioDeviceInfo::isFormatSupported(const QAudioFormat& format) const
-{
- return testSettings(format);
-}
-
-QAudioFormat QWindowsAudioDeviceInfo::preferredFormat() const
-{
- QAudioFormat nearest;
- if (mode == QAudio::AudioOutput) {
- nearest.setSampleRate(44100);
- nearest.setChannelCount(2);
- nearest.setByteOrder(QAudioFormat::LittleEndian);
- nearest.setSampleType(QAudioFormat::SignedInt);
- nearest.setSampleSize(16);
- nearest.setCodec(QLatin1String("audio/pcm"));
- } else {
- nearest.setSampleRate(11025);
- nearest.setChannelCount(1);
- nearest.setByteOrder(QAudioFormat::LittleEndian);
- nearest.setSampleType(QAudioFormat::SignedInt);
- nearest.setSampleSize(8);
- nearest.setCodec(QLatin1String("audio/pcm"));
- }
- return nearest;
-}
-
-QString QWindowsAudioDeviceInfo::deviceName() const
-{
- return device;
-}
-
-QStringList QWindowsAudioDeviceInfo::supportedCodecs()
-{
- return QStringList() << QStringLiteral("audio/pcm");
-}
-
-QList<int> QWindowsAudioDeviceInfo::supportedSampleRates()
-{
- updateLists();
- return sampleRatez;
-}
-
-QList<int> QWindowsAudioDeviceInfo::supportedChannelCounts()
-{
- updateLists();
- return channelz;
-}
-
-QList<int> QWindowsAudioDeviceInfo::supportedSampleSizes()
-{
- updateLists();
- return sizez;
-}
-
-QList<QAudioFormat::Endian> QWindowsAudioDeviceInfo::supportedByteOrders()
-{
- return QList<QAudioFormat::Endian>() << QAudioFormat::LittleEndian;
-}
-
-QList<QAudioFormat::SampleType> QWindowsAudioDeviceInfo::supportedSampleTypes()
-{
- updateLists();
- return typez;
-}
-
-
-bool QWindowsAudioDeviceInfo::open()
-{
- return true;
-}
-
-void QWindowsAudioDeviceInfo::close()
-{
-}
-
-bool QWindowsAudioDeviceInfo::testSettings(const QAudioFormat& format) const
-{
- WAVEFORMATEXTENSIBLE wfx;
- if (qt_convertFormat(format, &wfx)) {
- // query only, do not open device
- if (mode == QAudio::AudioOutput) {
- return (waveOutOpen(NULL, UINT_PTR(devId), &wfx.Format, 0, 0,
- WAVE_FORMAT_QUERY) == MMSYSERR_NOERROR);
- } else { // AudioInput
- return (waveInOpen(NULL, UINT_PTR(devId), &wfx.Format, 0, 0,
- WAVE_FORMAT_QUERY) == MMSYSERR_NOERROR);
- }
- }
-
- return false;
-}
-
-void QWindowsAudioDeviceInfo::updateLists()
-{
- if (!sizez.isEmpty())
- return;
-
- DWORD fmt = 0;
-
- if(mode == QAudio::AudioOutput) {
- WAVEOUTCAPS woc;
- if (waveOutGetDevCaps(devId, &woc, sizeof(WAVEOUTCAPS)) == MMSYSERR_NOERROR)
- fmt = woc.dwFormats;
- } else {
- WAVEINCAPS woc;
- if (waveInGetDevCaps(devId, &woc, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR)
- fmt = woc.dwFormats;
- }
-
- sizez.clear();
- sampleRatez.clear();
- channelz.clear();
- typez.clear();
-
- if (fmt) {
- // Check sample size
- if ((fmt & WAVE_FORMAT_1M08)
- || (fmt & WAVE_FORMAT_1S08)
- || (fmt & WAVE_FORMAT_2M08)
- || (fmt & WAVE_FORMAT_2S08)
- || (fmt & WAVE_FORMAT_4M08)
- || (fmt & WAVE_FORMAT_4S08)
- || (fmt & WAVE_FORMAT_48M08)
- || (fmt & WAVE_FORMAT_48S08)
- || (fmt & WAVE_FORMAT_96M08)
- || (fmt & WAVE_FORMAT_96S08)) {
- sizez.append(8);
- }
- if ((fmt & WAVE_FORMAT_1M16)
- || (fmt & WAVE_FORMAT_1S16)
- || (fmt & WAVE_FORMAT_2M16)
- || (fmt & WAVE_FORMAT_2S16)
- || (fmt & WAVE_FORMAT_4M16)
- || (fmt & WAVE_FORMAT_4S16)
- || (fmt & WAVE_FORMAT_48M16)
- || (fmt & WAVE_FORMAT_48S16)
- || (fmt & WAVE_FORMAT_96M16)
- || (fmt & WAVE_FORMAT_96S16)) {
- sizez.append(16);
- }
-
- // Check sample rate
- if ((fmt & WAVE_FORMAT_1M08)
- || (fmt & WAVE_FORMAT_1S08)
- || (fmt & WAVE_FORMAT_1M16)
- || (fmt & WAVE_FORMAT_1S16)) {
- sampleRatez.append(11025);
- }
- if ((fmt & WAVE_FORMAT_2M08)
- || (fmt & WAVE_FORMAT_2S08)
- || (fmt & WAVE_FORMAT_2M16)
- || (fmt & WAVE_FORMAT_2S16)) {
- sampleRatez.append(22050);
- }
- if ((fmt & WAVE_FORMAT_4M08)
- || (fmt & WAVE_FORMAT_4S08)
- || (fmt & WAVE_FORMAT_4M16)
- || (fmt & WAVE_FORMAT_4S16)) {
- sampleRatez.append(44100);
- }
- if ((fmt & WAVE_FORMAT_48M08)
- || (fmt & WAVE_FORMAT_48S08)
- || (fmt & WAVE_FORMAT_48M16)
- || (fmt & WAVE_FORMAT_48S16)) {
- sampleRatez.append(48000);
- }
- if ((fmt & WAVE_FORMAT_96M08)
- || (fmt & WAVE_FORMAT_96S08)
- || (fmt & WAVE_FORMAT_96M16)
- || (fmt & WAVE_FORMAT_96S16)) {
- sampleRatez.append(96000);
- }
-
- // Check channel count
- if (fmt & WAVE_FORMAT_1M08
- || fmt & WAVE_FORMAT_1M16
- || fmt & WAVE_FORMAT_2M08
- || fmt & WAVE_FORMAT_2M16
- || fmt & WAVE_FORMAT_4M08
- || fmt & WAVE_FORMAT_4M16
- || fmt & WAVE_FORMAT_48M08
- || fmt & WAVE_FORMAT_48M16
- || fmt & WAVE_FORMAT_96M08
- || fmt & WAVE_FORMAT_96M16) {
- channelz.append(1);
- }
- if (fmt & WAVE_FORMAT_1S08
- || fmt & WAVE_FORMAT_1S16
- || fmt & WAVE_FORMAT_2S08
- || fmt & WAVE_FORMAT_2S16
- || fmt & WAVE_FORMAT_4S08
- || fmt & WAVE_FORMAT_4S16
- || fmt & WAVE_FORMAT_48S08
- || fmt & WAVE_FORMAT_48S16
- || fmt & WAVE_FORMAT_96S08
- || fmt & WAVE_FORMAT_96S16) {
- channelz.append(2);
- }
-
- typez.append(QAudioFormat::SignedInt);
- typez.append(QAudioFormat::UnSignedInt);
-
- // WAVEOUTCAPS and WAVEINCAPS contains information only for the previously tested parameters.
- // WaveOut and WaveInt might actually support more formats, the only way to know is to try
- // opening the device with it.
- QAudioFormat testFormat;
- testFormat.setCodec(QStringLiteral("audio/pcm"));
- testFormat.setByteOrder(QAudioFormat::LittleEndian);
- testFormat.setSampleType(QAudioFormat::SignedInt);
- testFormat.setChannelCount(channelz.first());
- testFormat.setSampleRate(sampleRatez.at(sampleRatez.size() / 2));
- testFormat.setSampleSize(sizez.last());
- const QAudioFormat defaultTestFormat(testFormat);
-
- // Check if float samples are supported
- testFormat.setSampleType(QAudioFormat::Float);
- testFormat.setSampleSize(32);
- if (testSettings(testFormat))
- typez.append(QAudioFormat::Float);
-
- // Check channel counts > 2
- testFormat = defaultTestFormat;
- for (int i = 3; i < 19; ++i) { // <mmreg.h> defines 18 different channels
- testFormat.setChannelCount(i);
- if (testSettings(testFormat))
- channelz.append(i);
- }
-
- // Check more sample sizes
- testFormat = defaultTestFormat;
- const QList<int> testSampleSizes = QList<int>() << 24 << 32 << 48 << 64;
- for (int s : testSampleSizes) {
- testFormat.setSampleSize(s);
- if (testSettings(testFormat))
- sizez.append(s);
- }
-
- // Check more sample rates
- testFormat = defaultTestFormat;
- const QList<int> testSampleRates = QList<int>() << 8000 << 16000 << 32000 << 88200 << 192000;
- for (int r : testSampleRates) {
- testFormat.setSampleRate(r);
- if (testSettings(testFormat))
- sampleRatez.append(r);
- }
- std::sort(sampleRatez.begin(), sampleRatez.end());
- }
-}
-
-QList<QByteArray> QWindowsAudioDeviceInfo::availableDevices(QAudio::Mode mode)
-{
- Q_UNUSED(mode);
-
- QList<QByteArray> devices;
- //enumerate device fullnames through directshow api
- auto hrCoInit = CoInitialize(nullptr);
- ICreateDevEnum *pDevEnum = NULL;
- IEnumMoniker *pEnum = NULL;
- // Create the System device enumerator
- HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
- CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
- reinterpret_cast<void **>(&pDevEnum));
-
- unsigned long iNumDevs = mode == QAudio::AudioOutput ? waveOutGetNumDevs() : waveInGetNumDevs();
- if (SUCCEEDED(hr)) {
- // Create the enumerator for the audio input/output category
- if (pDevEnum->CreateClassEnumerator(
- mode == QAudio::AudioOutput ? CLSID_AudioRendererCategory : CLSID_AudioInputDeviceCategory,
- &pEnum, 0) == S_OK) {
- pEnum->Reset();
- // go through and find all audio devices
- IMoniker *pMoniker = NULL;
- while (pEnum->Next(1, &pMoniker, NULL) == S_OK) {
- IPropertyBag *pPropBag;
- hr = pMoniker->BindToStorage(0,0,IID_IPropertyBag,
- reinterpret_cast<void **>(&pPropBag));
- if (FAILED(hr)) {
- pMoniker->Release();
- continue; // skip this one
- }
- // Find if it is a wave device
- VARIANT var;
- VariantInit(&var);
- hr = pPropBag->Read(mode == QAudio::AudioOutput ? L"WaveOutID" : L"WaveInID", &var, 0);
- if (SUCCEEDED(hr)) {
- LONG waveID = var.lVal;
- if (waveID >= 0 && waveID < LONG(iNumDevs)) {
- VariantClear(&var);
- // Find the description
- hr = pPropBag->Read(L"FriendlyName", &var, 0);
- if (SUCCEEDED(hr)) {
- QByteArray device;
- QDataStream ds(&device, QIODevice::WriteOnly);
- ds << quint32(waveID) << QString::fromWCharArray(var.bstrVal);
- devices.append(device);
- }
- }
- }
-
- pPropBag->Release();
- pMoniker->Release();
- }
- pEnum->Release();
- }
- pDevEnum->Release();
- }
- if (SUCCEEDED(hrCoInit))
- CoUninitialize();
-
- return devices;
-}
-
-QByteArray QWindowsAudioDeviceInfo::defaultDevice(QAudio::Mode mode)
-{
- const QString &name = (mode == QAudio::AudioOutput) ? QStringLiteral("Default Output Device")
- : QStringLiteral("Default Input Device");
- QByteArray defaultDevice;
- QDataStream ds(&defaultDevice, QIODevice::WriteOnly);
- ds << quint32(WAVE_MAPPER) // device ID for default device
- << name;
-
- return defaultDevice;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.h b/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.h
deleted file mode 100644
index d84eb8acf..000000000
--- a/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** 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 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.
-//
-
-
-#ifndef QWINDOWSAUDIODEVICEINFO_H
-#define QWINDOWSAUDIODEVICEINFO_H
-
-#include <QtCore/qbytearray.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qlist.h>
-#include <QtCore/qdebug.h>
-
-#include <QtMultimedia/qaudiodeviceinfo.h>
-#include <QtMultimedia/qaudiosystem.h>
-
-
-QT_BEGIN_NAMESPACE
-
-const unsigned int MAX_SAMPLE_RATES = 5;
-const unsigned int SAMPLE_RATES[] = { 8000, 11025, 22050, 44100, 48000 };
-
-class QWindowsAudioDeviceInfo : public QAbstractAudioDeviceInfo
-{
- Q_OBJECT
-
-public:
- QWindowsAudioDeviceInfo(QByteArray dev,QAudio::Mode mode);
- ~QWindowsAudioDeviceInfo();
-
- bool open();
- void close();
-
- bool testSettings(const QAudioFormat& format) const;
- void updateLists();
- QAudioFormat preferredFormat() const;
- bool isFormatSupported(const QAudioFormat& format) const;
- QString deviceName() const;
- QStringList supportedCodecs();
- QList<int> supportedSampleRates();
- QList<int> supportedChannelCounts();
- QList<int> supportedSampleSizes();
- QList<QAudioFormat::Endian> supportedByteOrders();
- QList<QAudioFormat::SampleType> supportedSampleTypes();
- static QByteArray defaultDevice(QAudio::Mode mode);
- static QList<QByteArray> availableDevices(QAudio::Mode);
-
-private:
- QAudio::Mode mode;
- QString device;
- quint32 devId;
- QList<int> sampleRatez;
- QList<int> channelz;
- QList<int> sizez;
- QList<QAudioFormat::SampleType> typez;
-};
-
-
-
-QT_END_NAMESPACE
-
-
-#endif // QWINDOWSAUDIODEVICEINFO_H
diff --git a/src/plugins/windowsaudio/qwindowsaudioinput.cpp b/src/plugins/windowsaudio/qwindowsaudioinput.cpp
deleted file mode 100644
index addabbd32..000000000
--- a/src/plugins/windowsaudio/qwindowsaudioinput.cpp
+++ /dev/null
@@ -1,732 +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$
-**
-****************************************************************************/
-
-//
-// 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.
-//
-// INTERNAL USE ONLY: Do NOT use for any other purpose.
-//
-
-
-#include "qwindowsaudioinput.h"
-
-#include <QtCore/QDataStream>
-#include <QtCore/qtimer.h>
-
-QT_BEGIN_NAMESPACE
-
-//#define DEBUG_AUDIO 1
-
-QWindowsAudioInput::QWindowsAudioInput(const QByteArray &device)
-{
- bytesAvailable = 0;
- buffer_size = 0;
- period_size = 0;
- m_device = device;
- totalTimeValue = 0;
- intervalTime = 1000;
- 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));
-}
-
-QWindowsAudioInput::~QWindowsAudioInput()
-{
- stop();
-}
-
-void QT_WIN_CALLBACK QWindowsAudioInput::waveInProc( HWAVEIN hWaveIn, UINT uMsg,
- DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
-{
- Q_UNUSED(dwParam1);
- Q_UNUSED(dwParam2);
- Q_UNUSED(hWaveIn);
-
- QWindowsAudioInput* qAudio;
- qAudio = (QWindowsAudioInput*)(dwInstance);
- if(!qAudio)
- return;
-
- QMutexLocker locker(&qAudio->mutex);
-
- 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;
- }
-}
-
-WAVEHDR* QWindowsAudioInput::allocateBlocks(int size, int count)
-{
- 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("QAudioInput: 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("QAudioInput: Can't prepare block %d",i);
- return 0;
- }
- buffer += size;
- }
- return blocks;
-}
-
-void QWindowsAudioInput::freeBlocks(WAVEHDR* blockArray)
-{
- WAVEHDR* blocks = blockArray;
-
- int count = buffer_size/period_size;
-
- for(int i = 0; i < count; i++) {
- waveInUnprepareHeader(hWaveIn,blocks, sizeof(WAVEHDR));
- blocks++;
- }
- HeapFree(GetProcessHeap(), 0, blockArray);
-}
-
-QAudio::Error QWindowsAudioInput::error() const
-{
- return errorState;
-}
-
-QAudio::State QWindowsAudioInput::state() const
-{
- return 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 QWindowsAudioInput::setVolume(qreal volume)
-{
- 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);
- }
- }
-}
-
-qreal QWindowsAudioInput::volume() const
-{
- for (DWORD i = 0; i < mixerLineControls.cControls; i++) {
- if ((mixerLineControls.pamxctrl[i].dwControlType != MIXERCONTROL_CONTROLTYPE_FADER) &&
- (mixerLineControls.pamxctrl[i].dwControlType != MIXERCONTROL_CONTROLTYPE_VOLUME)) {
- continue;
- }
-
- 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;
- }
-
- return qFuzzyCompare(cachedVolume, qreal(-1.0f)) ? 1.0f : cachedVolume;
-}
-
-void QWindowsAudioInput::setFormat(const QAudioFormat& fmt)
-{
- if (deviceState == QAudio::StoppedState)
- settings = fmt;
-}
-
-QAudioFormat QWindowsAudioInput::format() const
-{
- return settings;
-}
-
-void QWindowsAudioInput::start(QIODevice* device)
-{
- if(deviceState != QAudio::StoppedState)
- close();
-
- if(!pullMode && audioSource)
- delete audioSource;
-
- pullMode = true;
- audioSource = device;
-
- deviceState = QAudio::ActiveState;
-
- if(!open())
- return;
-
- emit stateChanged(deviceState);
-}
-
-QIODevice* QWindowsAudioInput::start()
-{
- if(deviceState != QAudio::StoppedState)
- close();
-
- if(!pullMode && audioSource)
- delete audioSource;
-
- pullMode = false;
- audioSource = new InputPrivate(this);
- audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
-
- deviceState = QAudio::IdleState;
-
- if(!open())
- return 0;
-
- emit stateChanged(deviceState);
-
- return audioSource;
-}
-
-void QWindowsAudioInput::stop()
-{
- if(deviceState == QAudio::StoppedState)
- return;
-
- close();
- emit stateChanged(deviceState);
-}
-
-bool QWindowsAudioInput::open()
-{
-#ifdef DEBUG_AUDIO
- QTime now(QTime::currentTime());
- qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
-#endif
- header = 0;
-
- period_size = 0;
-
- if (!qt_convertFormat(settings, &wfx)) {
- qWarning("QAudioInput: open error, invalid format.");
- } else if (buffer_size == 0) {
- buffer_size
- = (settings.sampleRate()
- * settings.channelCount()
- * settings.sampleSize()
- + 39) / 40;
- period_size = buffer_size / 5;
- } else {
- period_size = buffer_size / 5;
- }
-
- if (period_size == 0) {
- errorState = QAudio::OpenError;
- deviceState = QAudio::StoppedState;
- emit stateChanged(deviceState);
- return false;
- }
-
- timeStamp.restart();
- elapsedTimeOffset = 0;
-
- QDataStream ds(&m_device, QIODevice::ReadOnly);
- quint32 deviceId;
- ds >> deviceId;
-
- if (waveInOpen(&hWaveIn, UINT_PTR(deviceId), &wfx.Format,
- (DWORD_PTR)&waveInProc,
- (DWORD_PTR) this,
- CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
- errorState = QAudio::OpenError;
- deviceState = QAudio::StoppedState;
- emit stateChanged(deviceState);
- qWarning("QAudioInput: failed to open audio device");
- 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("QAudioInput: failed to allocate blocks. open failed");
- 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("QAudioInput: failed to setup block %d,err=%d",i,result);
- errorState = QAudio::OpenError;
- deviceState = QAudio::StoppedState;
- emit stateChanged(deviceState);
- return false;
- }
- }
- result = waveInStart(hWaveIn);
- if(result) {
- qWarning("QAudioInput: failed to start audio input");
- errorState = QAudio::OpenError;
- deviceState = QAudio::StoppedState;
- emit stateChanged(deviceState);
- return false;
- }
- timeStampOpened.restart();
- elapsedTimeOffset = 0;
- totalTimeValue = 0;
- errorState = QAudio::NoError;
- initMixer();
- return true;
-}
-
-void QWindowsAudioInput::close()
-{
- if(deviceState == QAudio::StoppedState)
- return;
-
- deviceState = QAudio::StoppedState;
- waveInReset(hWaveIn);
-
- 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);
- }
-}
-
-void QWindowsAudioInput::initMixer()
-{
- // 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));
-
- // 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;
-
- // 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);
- }
-}
-
-void QWindowsAudioInput::closeMixer()
-{
- delete[] mixerLineControls.pamxctrl;
- memset(&mixerLineControls, 0, sizeof(mixerLineControls));
-}
-
-int QWindowsAudioInput::bytesReady() const
-{
- if(period_size == 0 || buffer_size == 0)
- return 0;
-
- int buf = ((buffer_size/period_size)-waveFreeBlockCount)*period_size;
- if(buf < 0)
- buf = 0;
- return buf;
-}
-
-qint64 QWindowsAudioInput::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("QAudioInput: IOError");
- errorState = QAudio::IOError;
-
- } else if(l == 0) {
- // cant write to IODevice
- qWarning("QAudioInput: 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;
- }
-
- 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("QAudioInput: 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("QAudioInput: 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();
- }
-
- written+=l;
- }
-#ifdef DEBUG_AUDIO
- qDebug()<<"read in len="<<written;
-#endif
- return written;
-}
-
-void QWindowsAudioInput::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("QAudioInput: 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);
- }
-}
-
-void QWindowsAudioInput::setBufferSize(int value)
-{
- buffer_size = value;
-}
-
-int QWindowsAudioInput::bufferSize() const
-{
- return buffer_size;
-}
-
-int QWindowsAudioInput::periodSize() const
-{
- return period_size;
-}
-
-void QWindowsAudioInput::setNotifyInterval(int ms)
-{
- intervalTime = qMax(0, ms);
-}
-
-int QWindowsAudioInput::notifyInterval() const
-{
- return intervalTime;
-}
-
-qint64 QWindowsAudioInput::processedUSecs() const
-{
- if (deviceState == QAudio::StoppedState)
- return 0;
- qint64 result = qint64(1000000) * totalTimeValue /
- (settings.channelCount()*(settings.sampleSize()/8)) /
- settings.sampleRate();
-
- return result;
-}
-
-void QWindowsAudioInput::suspend()
-{
- if(deviceState == QAudio::ActiveState) {
- waveInReset(hWaveIn);
- deviceState = QAudio::SuspendedState;
- emit stateChanged(deviceState);
- }
-}
-
-void QWindowsAudioInput::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 QWindowsAudioInput::deviceReady()
-{
- bytesAvailable = bytesReady();
-#ifdef DEBUG_AUDIO
- QTime now(QTime::currentTime());
- qDebug()<<now.second()<<"s "<<now.msec()<<"ms :deviceReady() INPUT";
-#endif
- if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
- return true;
-
- if(pullMode) {
- // reads some audio data and writes it to QIODevice
- read(0, buffer_size);
- } else {
- // emits readyRead() so user will call read() on QIODevice to get some audio data
- InputPrivate* a = qobject_cast<InputPrivate*>(audioSource);
- a->trigger();
- }
-
- if(intervalTime && (timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) {
- emit notify();
- elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime;
- timeStamp.restart();
- }
- return true;
-}
-
-qint64 QWindowsAudioInput::elapsedUSecs() const
-{
- if (deviceState == QAudio::StoppedState)
- return 0;
-
- return timeStampOpened.elapsed() * qint64(1000);
-}
-
-void QWindowsAudioInput::reset()
-{
- stop();
- if (period_size > 0)
- waveFreeBlockCount = buffer_size / period_size;
-}
-
-InputPrivate::InputPrivate(QWindowsAudioInput* audio)
-{
- audioDevice = qobject_cast<QWindowsAudioInput*>(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_qwindowsaudioinput.cpp"
diff --git a/src/plugins/windowsaudio/qwindowsaudioinput.h b/src/plugins/windowsaudio/qwindowsaudioinput.h
deleted file mode 100644
index 817176731..000000000
--- a/src/plugins/windowsaudio/qwindowsaudioinput.h
+++ /dev/null
@@ -1,177 +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$
-**
-****************************************************************************/
-
-//
-// 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.
-//
-
-#ifndef QWINDOWSAUDIOINPUT_H
-#define QWINDOWSAUDIOINPUT_H
-
-#include "qwindowsaudioutils.h"
-
-#include <QtCore/qfile.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qelapsedtimer.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qdatetime.h>
-#include <QtCore/qmutex.h>
-
-#include <QtMultimedia/qaudio.h>
-#include <QtMultimedia/qaudiodeviceinfo.h>
-#include <QtMultimedia/qaudiosystem.h>
-
-
-QT_BEGIN_NAMESPACE
-
-
-// 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
-
-class QWindowsAudioInput : public QAbstractAudioInput
-{
- Q_OBJECT
-public:
- QWindowsAudioInput(const QByteArray &device);
- ~QWindowsAudioInput();
-
- qint64 read(char* data, qint64 len);
-
- void setFormat(const QAudioFormat& fmt);
- QAudioFormat format() const;
- QIODevice* start();
- void start(QIODevice* device);
- void stop();
- void reset();
- void suspend();
- void resume();
- int bytesReady() const;
- int periodSize() const;
- void setBufferSize(int value);
- int bufferSize() const;
- void setNotifyInterval(int milliSeconds);
- int notifyInterval() const;
- qint64 processedUSecs() const;
- qint64 elapsedUSecs() const;
- QAudio::Error error() const;
- QAudio::State state() const;
- void setVolume(qreal volume);
- qreal volume() const;
-
- QIODevice* audioSource;
- QAudioFormat settings;
- QAudio::Error errorState;
- QAudio::State deviceState;
-
-private:
- qint32 buffer_size;
- qint32 period_size;
- qint32 header;
- QByteArray m_device;
- int bytesAvailable;
- int intervalTime;
- QElapsedTimer timeStamp;
- qint64 elapsedTimeOffset;
- QElapsedTimer timeStampOpened;
- 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);
- 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(QWindowsAudioInput* audio);
- ~InputPrivate();
-
- qint64 readData( char* data, qint64 len);
- qint64 writeData(const char* data, qint64 len);
-
- void trigger();
-private:
- QWindowsAudioInput *audioDevice;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/windowsaudio/qwindowsaudiooutput.cpp b/src/plugins/windowsaudio/qwindowsaudiooutput.cpp
deleted file mode 100644
index e0635307e..000000000
--- a/src/plugins/windowsaudio/qwindowsaudiooutput.cpp
+++ /dev/null
@@ -1,676 +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$
-**
-****************************************************************************/
-
-//
-// 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.
-//
-// INTERNAL USE ONLY: Do NOT use for any other purpose.
-//
-
-#include "qwindowsaudiooutput.h"
-#include "qwindowsaudiodeviceinfo.h"
-#include "qwindowsaudioutils.h"
-#include <QtEndian>
-#include <QtCore/QDataStream>
-#include <QtCore/qtimer.h>
-#include <private/qaudiohelpers_p.h>
-
-//#define DEBUG_AUDIO 1
-
-QT_BEGIN_NAMESPACE
-
-QWindowsAudioOutput::QWindowsAudioOutput(const QByteArray &device)
-{
- bytesAvailable = 0;
- buffer_size = 0;
- period_size = 0;
- m_device = device;
- totalTimeValue = 0;
- intervalTime = 1000;
- audioBuffer = 0;
- errorState = QAudio::NoError;
- deviceState = QAudio::StoppedState;
- audioSource = 0;
- pullMode = true;
- finished = false;
- volumeCache = qreal(1.0);
- blocks_count = 5;
-}
-
-QWindowsAudioOutput::~QWindowsAudioOutput()
-{
- mutex.lock();
- finished = true;
- mutex.unlock();
-
- close();
-}
-
-void CALLBACK QWindowsAudioOutput::waveOutProc( HWAVEOUT hWaveOut, UINT uMsg,
- DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
-{
- Q_UNUSED(dwParam1);
- Q_UNUSED(dwParam2);
- Q_UNUSED(hWaveOut);
-
- QWindowsAudioOutput* qAudio;
- qAudio = (QWindowsAudioOutput*)(dwInstance);
- if(!qAudio)
- return;
-
- QMutexLocker locker(&qAudio->mutex);
-
- switch(uMsg) {
- case WOM_OPEN:
- qAudio->feedback();
- break;
- case WOM_CLOSE:
- return;
- case WOM_DONE:
- if(qAudio->finished || qAudio->buffer_size == 0 || qAudio->period_size == 0) {
- return;
- }
- qAudio->waveFreeBlockCount++;
- if (qAudio->waveFreeBlockCount >= qAudio->blocks_count)
- qAudio->waveFreeBlockCount = qAudio->blocks_count;
-
- qAudio->feedback();
- break;
- default:
- return;
- }
-}
-
-WAVEHDR* QWindowsAudioOutput::allocateBlocks(int size, int count)
-{
- 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("QAudioOutput: 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;
- buffer += size;
- }
- return blocks;
-}
-
-void QWindowsAudioOutput::freeBlocks(WAVEHDR* blockArray)
-{
- WAVEHDR* blocks = blockArray;
- for (int i = 0; i < blocks_count; ++i) {
- waveOutUnprepareHeader(hWaveOut,blocks, sizeof(WAVEHDR));
- blocks++;
- }
- HeapFree(GetProcessHeap(), 0, blockArray);
-}
-
-QAudioFormat QWindowsAudioOutput::format() const
-{
- return settings;
-}
-
-void QWindowsAudioOutput::setFormat(const QAudioFormat& fmt)
-{
- if (deviceState == QAudio::StoppedState)
- settings = fmt;
-}
-
-void QWindowsAudioOutput::start(QIODevice* device)
-{
- if(deviceState != QAudio::StoppedState)
- close();
-
- if(!pullMode && audioSource)
- delete audioSource;
-
- pullMode = true;
- audioSource = device;
-
- deviceState = QAudio::ActiveState;
-
- if(!open())
- return;
-
- emit stateChanged(deviceState);
-}
-
-QIODevice* QWindowsAudioOutput::start()
-{
- if(deviceState != QAudio::StoppedState)
- close();
-
- if(!pullMode && audioSource)
- delete audioSource;
-
- pullMode = false;
- audioSource = new OutputPrivate(this);
- audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered);
-
- deviceState = QAudio::IdleState;
-
- if(!open())
- return 0;
-
- emit stateChanged(deviceState);
-
- return audioSource;
-}
-
-void QWindowsAudioOutput::stop()
-{
- if(deviceState == QAudio::StoppedState)
- return;
- close();
- if(!pullMode && audioSource) {
- delete audioSource;
- audioSource = 0;
- }
- emit stateChanged(deviceState);
-}
-
-bool QWindowsAudioOutput::open()
-{
-#ifdef DEBUG_AUDIO
- QTime now(QTime::currentTime());
- qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
-#endif
-
- period_size = 0;
-
- if (!qt_convertFormat(settings, &wfx)) {
- qWarning("QAudioOutput: open error, invalid format.");
- } else if (buffer_size == 0) {
- // Default buffer size, 200ms, default period size is 40ms
- buffer_size
- = (settings.sampleRate()
- * settings.channelCount()
- * settings.sampleSize()
- + 39) / 40;
- period_size = buffer_size / 5;
- } else {
- period_size = buffer_size / 5;
- }
-
- // Make even size of wave block to prevent crackling
- // due to waveOutWrite() does not like odd buffer length
- period_size &= ~1;
-
- if (period_size == 0) {
- errorState = QAudio::OpenError;
- deviceState = QAudio::StoppedState;
- emit stateChanged(deviceState);
- return false;
- }
-
- const int periods = buffer_size / period_size;
- bool ok = false;
- static int wave_buffers = qEnvironmentVariableIntValue("QT_WAVE_BUFFERS", &ok);
- if (wave_buffers < periods) {
- if (ok)
- qWarning("Number of WAVE buffers (QT_WAVE_BUFFERS=%d) cannot be less than %d.", wave_buffers, periods);
- wave_buffers = periods;
- }
-
- blocks_count = wave_buffers;
- waveBlocks = allocateBlocks(period_size, blocks_count);
-
- mutex.lock();
- waveFreeBlockCount = blocks_count;
- mutex.unlock();
-
- waveCurrentBlock = 0;
-
- if(audioBuffer == 0)
- audioBuffer = new char[blocks_count * period_size];
-
- timeStamp.restart();
- elapsedTimeOffset = 0;
-
- QDataStream ds(&m_device, QIODevice::ReadOnly);
- quint32 deviceId;
- ds >> deviceId;
-
- if (waveOutOpen(&hWaveOut, UINT_PTR(deviceId), &wfx.Format,
- (DWORD_PTR)&waveOutProc,
- (DWORD_PTR) this,
- CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
- errorState = QAudio::OpenError;
- deviceState = QAudio::StoppedState;
- emit stateChanged(deviceState);
- qWarning("QAudioOutput: open error");
- return false;
- }
-
- totalTimeValue = 0;
- timeStampOpened.restart();
- elapsedTimeOffset = 0;
-
- errorState = QAudio::NoError;
- if(pullMode) {
- deviceState = QAudio::ActiveState;
- QTimer::singleShot(10, this, SLOT(feedback()));
- } else
- deviceState = QAudio::IdleState;
-
- return true;
-}
-
-void QWindowsAudioOutput::pauseAndSleep()
-{
- waveOutPause(hWaveOut);
- int bitrate = settings.sampleRate() * settings.channelCount() * settings.sampleSize() / 8;
- // Time of written data.
- int delay = (buffer_size - bytesFree()) * 1000 / bitrate;
- Sleep(delay + 10);
-}
-
-void QWindowsAudioOutput::close()
-{
- if(deviceState == QAudio::StoppedState)
- return;
-
- // Pause playback before reset to avoid uneeded crackling at the end.
- pauseAndSleep();
- deviceState = QAudio::StoppedState;
- errorState = QAudio::NoError;
- waveOutReset(hWaveOut);
-
- freeBlocks(waveBlocks);
- waveOutClose(hWaveOut);
- delete [] audioBuffer;
- audioBuffer = 0;
- buffer_size = 0;
-}
-
-int QWindowsAudioOutput::bytesFree() const
-{
- int buf;
- buf = waveFreeBlockCount*period_size;
-
- return buf;
-}
-
-int QWindowsAudioOutput::periodSize() const
-{
- return period_size;
-}
-
-void QWindowsAudioOutput::setBufferSize(int value)
-{
- if(deviceState == QAudio::StoppedState)
- buffer_size = value;
-}
-
-int QWindowsAudioOutput::bufferSize() const
-{
- return buffer_size;
-}
-
-void QWindowsAudioOutput::setNotifyInterval(int ms)
-{
- intervalTime = qMax(0, ms);
-}
-
-int QWindowsAudioOutput::notifyInterval() const
-{
- return intervalTime;
-}
-
-qint64 QWindowsAudioOutput::processedUSecs() const
-{
- if (deviceState == QAudio::StoppedState)
- return 0;
- qint64 result = qint64(1000000) * totalTimeValue /
- (settings.channelCount()*(settings.sampleSize()/8)) /
- settings.sampleRate();
-
- return result;
-}
-
-qint64 QWindowsAudioOutput::write( const char *data, qint64 len )
-{
- // Write out some audio data
- if (deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
- return 0;
-
- char* p = (char*)data;
- int l = (int)len;
-
- QByteArray reverse;
- if (settings.byteOrder() == QAudioFormat::BigEndian) {
-
- switch (settings.sampleSize()) {
- case 8:
- // No need to convert
- break;
-
- case 16:
- reverse.resize(l);
- for (qint64 i = 0; i < (l >> 1); i++)
- *((qint16*)reverse.data() + i) = qFromBigEndian(*((qint16*)data + i));
- p = reverse.data();
- break;
-
- case 32:
- reverse.resize(l);
- for (qint64 i = 0; i < (l >> 2); i++)
- *((qint32*)reverse.data() + i) = qFromBigEndian(*((qint32*)data + i));
- p = reverse.data();
- break;
- }
- }
-
- WAVEHDR* current;
- int remain;
- current = &waveBlocks[waveCurrentBlock];
- while(l > 0) {
- mutex.lock();
- if(waveFreeBlockCount==0) {
- mutex.unlock();
- break;
- }
- mutex.unlock();
-
- if(current->dwFlags & WHDR_PREPARED)
- waveOutUnprepareHeader(hWaveOut, current, sizeof(WAVEHDR));
-
- if(l < period_size)
- remain = l;
- else
- remain = period_size;
-
- if (volumeCache < qreal(1.0))
- QAudioHelperInternal::qMultiplySamples(volumeCache, settings, p, current->lpData, remain);
- else
- memcpy(current->lpData, p, remain);
-
- l -= remain;
- p += remain;
- current->dwBufferLength = remain;
- waveOutPrepareHeader(hWaveOut, current, sizeof(WAVEHDR));
- waveOutWrite(hWaveOut, current, sizeof(WAVEHDR));
-
- mutex.lock();
- waveFreeBlockCount--;
-#ifdef DEBUG_AUDIO
- qDebug("write out l=%d, waveFreeBlockCount=%d",
- current->dwBufferLength,waveFreeBlockCount);
-#endif
- mutex.unlock();
-
- totalTimeValue += current->dwBufferLength;
- waveCurrentBlock++;
- waveCurrentBlock %= blocks_count;
- current = &waveBlocks[waveCurrentBlock];
- current->dwUser = 0;
- errorState = QAudio::NoError;
- if (deviceState != QAudio::ActiveState) {
- deviceState = QAudio::ActiveState;
- emit stateChanged(deviceState);
- }
- }
- return (len-l);
-}
-
-void QWindowsAudioOutput::resume()
-{
- if(deviceState == QAudio::SuspendedState) {
- deviceState = pullMode ? QAudio::ActiveState : QAudio::IdleState;
- errorState = QAudio::NoError;
- waveOutRestart(hWaveOut);
- QTimer::singleShot(10, this, SLOT(feedback()));
- emit stateChanged(deviceState);
- }
-}
-
-void QWindowsAudioOutput::suspend()
-{
- if(deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState) {
- pauseAndSleep();
- deviceState = QAudio::SuspendedState;
- errorState = QAudio::NoError;
- emit stateChanged(deviceState);
- }
-}
-
-void QWindowsAudioOutput::feedback()
-{
-#ifdef DEBUG_AUDIO
- QTime now(QTime::currentTime());
- qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback()";
-#endif
- bytesAvailable = bytesFree();
-
- if(!(deviceState==QAudio::StoppedState||deviceState==QAudio::SuspendedState)) {
- if(bytesAvailable >= period_size)
- QMetaObject::invokeMethod(this, "deviceReady", Qt::QueuedConnection);
- }
-}
-
-bool QWindowsAudioOutput::deviceReady()
-{
- if(deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState)
- return false;
-
- if(pullMode) {
- int chunks = bytesAvailable/period_size;
-#ifdef DEBUG_AUDIO
- qDebug()<<"deviceReady() avail="<<bytesAvailable<<" bytes, period size="<<period_size<<" bytes";
- qDebug()<<"deviceReady() no. of chunks that can fit ="<<chunks<<", chunks in bytes ="<<chunks*period_size;
-#endif
- bool startup = false;
- if(totalTimeValue == 0)
- startup = true;
-
- bool full=false;
-
- mutex.lock();
- if (waveFreeBlockCount==0) full = true;
- mutex.unlock();
-
- if (full) {
-#ifdef DEBUG_AUDIO
- qDebug() << "Skipping data as unable to write";
-#endif
- if ((timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) {
- emit notify();
- elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime;
- timeStamp.restart();
- }
- return true;
- }
-
- if(startup)
- waveOutPause(hWaveOut);
- int input = period_size*chunks;
- int l = audioSource->read(audioBuffer,input);
- if(l > 0) {
- int out= write(audioBuffer,l);
- if(out > 0) {
- if (deviceState != QAudio::ActiveState) {
- deviceState = QAudio::ActiveState;
- emit stateChanged(deviceState);
- }
- }
- if ( out < l) {
- // Didn't write all data
- audioSource->seek(audioSource->pos()-(l-out));
- }
- if (startup)
- waveOutRestart(hWaveOut);
- } else if(l == 0) {
- bytesAvailable = bytesFree();
-
- int check = 0;
-
- mutex.lock();
- check = waveFreeBlockCount;
- mutex.unlock();
-
- if (check == blocks_count) {
- if (deviceState != QAudio::IdleState) {
- errorState = QAudio::UnderrunError;
- deviceState = QAudio::IdleState;
- emit stateChanged(deviceState);
- }
- }
-
- } else if(l < 0) {
- bytesAvailable = bytesFree();
- if (errorState != QAudio::IOError)
- errorState = QAudio::IOError;
- }
- } else {
- int buffered;
-
- mutex.lock();
- buffered = waveFreeBlockCount;
- mutex.unlock();
-
- if (buffered >= blocks_count && deviceState == QAudio::ActiveState) {
- if (deviceState != QAudio::IdleState) {
- errorState = QAudio::UnderrunError;
- deviceState = QAudio::IdleState;
- emit stateChanged(deviceState);
- }
- }
- }
- if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
- return true;
-
- if(intervalTime && (timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) {
- emit notify();
- elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime;
- timeStamp.restart();
- }
-
- return true;
-}
-
-qint64 QWindowsAudioOutput::elapsedUSecs() const
-{
- if (deviceState == QAudio::StoppedState)
- return 0;
-
- return timeStampOpened.elapsed() * qint64(1000);
-}
-
-QAudio::Error QWindowsAudioOutput::error() const
-{
- return errorState;
-}
-
-QAudio::State QWindowsAudioOutput::state() const
-{
- return deviceState;
-}
-
-void QWindowsAudioOutput::setVolume(qreal v)
-{
- if (qFuzzyCompare(volumeCache, v))
- return;
-
- volumeCache = qBound(qreal(0), v, qreal(1));
-}
-
-qreal QWindowsAudioOutput::volume() const
-{
- return volumeCache;
-}
-
-void QWindowsAudioOutput::reset()
-{
- stop();
-}
-
-OutputPrivate::OutputPrivate(QWindowsAudioOutput* audio)
-{
- audioDevice = qobject_cast<QWindowsAudioOutput*>(audio);
-}
-
-OutputPrivate::~OutputPrivate() {}
-
-qint64 OutputPrivate::readData( char* data, qint64 len)
-{
- Q_UNUSED(data);
- Q_UNUSED(len);
-
- return 0;
-}
-
-qint64 OutputPrivate::writeData(const char* data, qint64 len)
-{
- int retry = 0;
- qint64 written = 0;
-
- if((audioDevice->deviceState == QAudio::ActiveState)
- ||(audioDevice->deviceState == QAudio::IdleState)) {
- qint64 l = len;
- while(written < l) {
- int chunk = audioDevice->write(data+written,(l-written));
- if(chunk <= 0)
- retry++;
- else
- written+=chunk;
-
- if(retry > 10)
- return written;
- }
- audioDevice->deviceState = QAudio::ActiveState;
- }
- return written;
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qwindowsaudiooutput.cpp"
diff --git a/src/plugins/windowsaudio/qwindowsaudiooutput.h b/src/plugins/windowsaudio/qwindowsaudiooutput.h
deleted file mode 100644
index e81121810..000000000
--- a/src/plugins/windowsaudio/qwindowsaudiooutput.h
+++ /dev/null
@@ -1,168 +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$
-**
-****************************************************************************/
-
-//
-// 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.
-//
-
-#ifndef QWINDOWSAUDIOOUTPUT_H
-#define QWINDOWSAUDIOOUTPUT_H
-
-#include "qwindowsaudioutils.h"
-
-#include <QtCore/qdebug.h>
-#include <QtCore/qelapsedtimer.h>
-#include <QtCore/qiodevice.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qdatetime.h>
-#include <QtCore/qmutex.h>
-
-#include <QtMultimedia/qaudio.h>
-#include <QtMultimedia/qaudiodeviceinfo.h>
-#include <QtMultimedia/qaudiosystem.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 QWindowsAudioOutput : public QAbstractAudioOutput
-{
- Q_OBJECT
-public:
- QWindowsAudioOutput(const QByteArray &device);
- ~QWindowsAudioOutput();
-
- qint64 write( const char *data, qint64 len );
-
- void setFormat(const QAudioFormat& fmt);
- QAudioFormat format() const;
- QIODevice* start();
- void start(QIODevice* device);
- void stop();
- void reset();
- void suspend();
- void resume();
- int bytesFree() const;
- int periodSize() const;
- void setBufferSize(int value);
- int bufferSize() const;
- void setNotifyInterval(int milliSeconds);
- int notifyInterval() const;
- qint64 processedUSecs() const;
- qint64 elapsedUSecs() const;
- QAudio::Error error() const;
- QAudio::State state() const;
- void setVolume(qreal);
- qreal volume() const;
-
- QIODevice* audioSource;
- QAudioFormat settings;
- QAudio::Error errorState;
- QAudio::State deviceState;
-
-private slots:
- void feedback();
- bool deviceReady();
-
-private:
- void pauseAndSleep();
- QByteArray m_device;
- int bytesAvailable;
- QElapsedTimer timeStamp;
- qint64 elapsedTimeOffset;
- QElapsedTimer timeStampOpened;
- qint32 buffer_size;
- qint32 period_size;
- qint32 blocks_count;
- qint64 totalTimeValue;
- bool pullMode;
- int intervalTime;
- qreal volumeCache;
- static void QT_WIN_CALLBACK waveOutProc( HWAVEOUT hWaveOut, UINT uMsg,
- DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 );
-
- QMutex mutex;
-
- WAVEHDR* allocateBlocks(int size, int count);
- void freeBlocks(WAVEHDR* blockArray);
- bool open();
- void close();
-
- WAVEFORMATEXTENSIBLE wfx;
- HWAVEOUT hWaveOut;
- WAVEHDR* waveBlocks;
- volatile bool finished;
- volatile int waveFreeBlockCount;
- int waveCurrentBlock;
- char* audioBuffer;
-};
-
-class OutputPrivate : public QIODevice
-{
- Q_OBJECT
-public:
- OutputPrivate(QWindowsAudioOutput* audio);
- ~OutputPrivate();
-
- qint64 readData( char* data, qint64 len);
- qint64 writeData(const char* data, qint64 len);
-
-private:
- QWindowsAudioOutput *audioDevice;
-};
-
-QT_END_NAMESPACE
-
-
-#endif // QWINDOWSAUDIOOUTPUT_H
diff --git a/src/plugins/windowsaudio/qwindowsaudioplugin.cpp b/src/plugins/windowsaudio/qwindowsaudioplugin.cpp
deleted file mode 100644
index 8f532fa70..000000000
--- a/src/plugins/windowsaudio/qwindowsaudioplugin.cpp
+++ /dev/null
@@ -1,77 +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 "qwindowsaudioplugin.h"
-#include "qwindowsaudiodeviceinfo.h"
-#include "qwindowsaudioinput.h"
-#include "qwindowsaudiooutput.h"
-
-QT_BEGIN_NAMESPACE
-
-QWindowsAudioPlugin::QWindowsAudioPlugin(QObject *parent)
- : QAudioSystemPlugin(parent)
-{
-}
-
-QByteArray QWindowsAudioPlugin::defaultDevice(QAudio::Mode mode) const
-{
- return QWindowsAudioDeviceInfo::defaultDevice(mode);
-}
-
-QList<QByteArray> QWindowsAudioPlugin::availableDevices(QAudio::Mode mode) const
-{
- return QWindowsAudioDeviceInfo::availableDevices(mode);
-}
-
-QAbstractAudioInput *QWindowsAudioPlugin::createInput(const QByteArray &device)
-{
- return new QWindowsAudioInput(device);
-}
-
-QAbstractAudioOutput *QWindowsAudioPlugin::createOutput(const QByteArray &device)
-{
- return new QWindowsAudioOutput(device);
-}
-
-QAbstractAudioDeviceInfo *QWindowsAudioPlugin::createDeviceInfo(const QByteArray &device, QAudio::Mode mode)
-{
- return new QWindowsAudioDeviceInfo(device, mode);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/windowsaudio/qwindowsaudioplugin.h b/src/plugins/windowsaudio/qwindowsaudioplugin.h
deleted file mode 100644
index a155ae082..000000000
--- a/src/plugins/windowsaudio/qwindowsaudioplugin.h
+++ /dev/null
@@ -1,68 +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 QWINDOWSAUDIOPLUGIN_H
-#define QWINDOWSAUDIOPLUGIN_H
-
-#include <QtMultimedia/qaudiosystemplugin.h>
-#include <QtMultimedia/private/qaudiosystempluginext_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class QWindowsAudioPlugin : public QAudioSystemPlugin, public QAudioSystemPluginExtension
-{
- Q_OBJECT
-
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.audiosystemfactory/5.0" FILE "windowsaudio.json")
- Q_INTERFACES(QAudioSystemPluginExtension)
-
-public:
- QWindowsAudioPlugin(QObject *parent = 0);
- ~QWindowsAudioPlugin() {}
-
- QByteArray defaultDevice(QAudio::Mode mode) const override;
- QList<QByteArray> availableDevices(QAudio::Mode mode) const override;
- QAbstractAudioInput *createInput(const QByteArray &device) override;
- QAbstractAudioOutput *createOutput(const QByteArray &device) override;
- QAbstractAudioDeviceInfo *createDeviceInfo(const QByteArray &device, QAudio::Mode mode) override;
-};
-
-QT_END_NAMESPACE
-
-#endif // QWINDOWSAUDIOPLUGIN_H
diff --git a/src/plugins/windowsaudio/qwindowsaudioutils.cpp b/src/plugins/windowsaudio/qwindowsaudioutils.cpp
deleted file mode 100644
index 4eaa80b41..000000000
--- a/src/plugins/windowsaudio/qwindowsaudioutils.cpp
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "qwindowsaudioutils.h"
-
-#ifndef SPEAKER_FRONT_LEFT
- #define SPEAKER_FRONT_LEFT 0x00000001
- #define SPEAKER_FRONT_RIGHT 0x00000002
- #define SPEAKER_FRONT_CENTER 0x00000004
- #define SPEAKER_LOW_FREQUENCY 0x00000008
- #define SPEAKER_BACK_LEFT 0x00000010
- #define SPEAKER_BACK_RIGHT 0x00000020
- #define SPEAKER_FRONT_LEFT_OF_CENTER 0x00000040
- #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x00000080
- #define SPEAKER_BACK_CENTER 0x00000100
- #define SPEAKER_SIDE_LEFT 0x00000200
- #define SPEAKER_SIDE_RIGHT 0x00000400
- #define SPEAKER_TOP_CENTER 0x00000800
- #define SPEAKER_TOP_FRONT_LEFT 0x00001000
- #define SPEAKER_TOP_FRONT_CENTER 0x00002000
- #define SPEAKER_TOP_FRONT_RIGHT 0x00004000
- #define SPEAKER_TOP_BACK_LEFT 0x00008000
- #define SPEAKER_TOP_BACK_CENTER 0x00010000
- #define SPEAKER_TOP_BACK_RIGHT 0x00020000
- #define SPEAKER_RESERVED 0x7FFC0000
- #define SPEAKER_ALL 0x80000000
-#endif
-
-#ifndef WAVE_FORMAT_EXTENSIBLE
- #define WAVE_FORMAT_EXTENSIBLE 0xFFFE
-#endif
-
-#ifndef WAVE_FORMAT_IEEE_FLOAT
- #define WAVE_FORMAT_IEEE_FLOAT 0x0003
-#endif
-
-static const GUID _KSDATAFORMAT_SUBTYPE_PCM = {
- 0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
-
-static const GUID _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {
- 0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
-
-QT_BEGIN_NAMESPACE
-
-bool qt_convertFormat(const QAudioFormat &format, WAVEFORMATEXTENSIBLE *wfx)
-{
- if (!wfx
- || !format.isValid()
- || format.codec() != QStringLiteral("audio/pcm")
- || format.sampleRate() <= 0
- || format.channelCount() <= 0
- || format.sampleSize() <= 0
- || format.byteOrder() != QAudioFormat::LittleEndian) {
- return false;
- }
-
- wfx->Format.nSamplesPerSec = format.sampleRate();
- wfx->Format.wBitsPerSample = wfx->Samples.wValidBitsPerSample = format.sampleSize();
- wfx->Format.nChannels = format.channelCount();
- wfx->Format.nBlockAlign = (wfx->Format.wBitsPerSample / 8) * wfx->Format.nChannels;
- wfx->Format.nAvgBytesPerSec = wfx->Format.nBlockAlign * wfx->Format.nSamplesPerSec;
- wfx->Format.cbSize = 0;
-
- if (format.sampleType() == QAudioFormat::Float) {
- wfx->Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
- wfx->SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- } else {
- wfx->Format.wFormatTag = WAVE_FORMAT_PCM;
- wfx->SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
- }
-
- if (format.channelCount() > 2) {
- wfx->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- wfx->Format.cbSize = 22;
- wfx->dwChannelMask = 0xFFFFFFFF >> (32 - format.channelCount());
- }
-
- return true;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/windowsaudio/qwindowsaudioutils.h b/src/plugins/windowsaudio/qwindowsaudioutils.h
deleted file mode 100644
index b8567d85c..000000000
--- a/src/plugins/windowsaudio/qwindowsaudioutils.h
+++ /dev/null
@@ -1,73 +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 QWINDOWSAUDIOUTILS_H
-#define QWINDOWSAUDIOUTILS_H
-
-#include <qaudioformat.h>
-#include <QtCore/qt_windows.h>
-#include <mmsystem.h>
-
-#ifndef _WAVEFORMATEXTENSIBLE_
-
- #define _WAVEFORMATEXTENSIBLE_
- typedef struct
- {
- WAVEFORMATEX Format; // Base WAVEFORMATEX data
- union
- {
- WORD wValidBitsPerSample; // Valid bits in each sample container
- WORD wSamplesPerBlock; // Samples per block of audio data; valid
- // if wBitsPerSample=0 (but rarely used).
- WORD wReserved; // Zero if neither case above applies.
- } Samples;
- DWORD dwChannelMask; // Positions of the audio channels
- GUID SubFormat; // Format identifier GUID
- } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE, *LPPWAVEFORMATEXTENSIBLE;
- typedef const WAVEFORMATEXTENSIBLE* LPCWAVEFORMATEXTENSIBLE;
-
-#endif
-
-QT_BEGIN_NAMESPACE
-
-bool qt_convertFormat(const QAudioFormat &format, WAVEFORMATEXTENSIBLE *wfx);
-
-QT_END_NAMESPACE
-
-#endif // QWINDOWSAUDIOUTILS_H
diff --git a/src/plugins/windowsaudio/windowsaudio.json b/src/plugins/windowsaudio/windowsaudio.json
deleted file mode 100644
index a31d52107..000000000
--- a/src/plugins/windowsaudio/windowsaudio.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "Keys": ["default"]
-}
diff --git a/src/plugins/windowsaudio/windowsaudio.pro b/src/plugins/windowsaudio/windowsaudio.pro
deleted file mode 100644
index 6d5fe9495..000000000
--- a/src/plugins/windowsaudio/windowsaudio.pro
+++ /dev/null
@@ -1,25 +0,0 @@
-TARGET = qtaudio_windows
-QT += multimedia-private
-
-LIBS += -lstrmiids -lole32 -loleaut32 -lwinmm
-
-HEADERS += \
- qwindowsaudioplugin.h \
- qwindowsaudiodeviceinfo.h \
- qwindowsaudioinput.h \
- qwindowsaudiooutput.h \
- qwindowsaudioutils.h
-
-SOURCES += \
- qwindowsaudioplugin.cpp \
- qwindowsaudiodeviceinfo.cpp \
- qwindowsaudioinput.cpp \
- qwindowsaudiooutput.cpp \
- qwindowsaudioutils.cpp
-
-OTHER_FILES += \
- windowsaudio.json
-
-PLUGIN_TYPE = audio
-PLUGIN_CLASS_NAME = QWindowsAudioPlugin
-load(qt_plugin)
diff --git a/src/plugins/wmf/decoder/decoder.pri b/src/plugins/wmf/decoder/decoder.pri
deleted file mode 100644
index 7637ac848..000000000
--- a/src/plugins/wmf/decoder/decoder.pri
+++ /dev/null
@@ -1,14 +0,0 @@
-INCLUDEPATH += $$PWD
-
-LIBS += -lmfreadwrite -lwmcodecdspuuid
-QMAKE_USE += wmf
-
-HEADERS += \
- $$PWD/mfdecoderservice.h \
- $$PWD/mfdecodersourcereader.h \
- $$PWD/mfaudiodecodercontrol.h
-
-SOURCES += \
- $$PWD/mfdecoderservice.cpp \
- $$PWD/mfdecodersourcereader.cpp \
- $$PWD/mfaudiodecodercontrol.cpp
diff --git a/src/plugins/wmf/decoder/mfaudiodecodercontrol.cpp b/src/plugins/wmf/decoder/mfaudiodecodercontrol.cpp
deleted file mode 100644
index c0eada324..000000000
--- a/src/plugins/wmf/decoder/mfaudiodecodercontrol.cpp
+++ /dev/null
@@ -1,486 +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 "Wmcodecdsp.h"
-#include "mfaudiodecodercontrol.h"
-
-MFAudioDecoderControl::MFAudioDecoderControl(QObject *parent)
- : QAudioDecoderControl(parent)
- , m_decoderSourceReader(new MFDecoderSourceReader)
- , m_sourceResolver(new SourceResolver)
- , m_resampler(0)
- , m_state(QAudioDecoder::StoppedState)
- , m_device(0)
- , m_mfInputStreamID(0)
- , m_mfOutputStreamID(0)
- , m_bufferReady(false)
- , m_duration(0)
- , m_position(0)
- , m_loadingSource(false)
- , m_mfOutputType(0)
- , m_convertSample(0)
- , m_sourceReady(false)
- , m_resamplerDirty(false)
-{
- CoCreateInstance(CLSID_CResamplerMediaObject, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform, (LPVOID*)(&m_resampler));
- if (!m_resampler) {
- qCritical("MFAudioDecoderControl: Failed to create resampler(CLSID_CResamplerMediaObject)!");
- return;
- }
- m_resampler->AddInputStreams(1, &m_mfInputStreamID);
-
- connect(m_sourceResolver, SIGNAL(mediaSourceReady()), this, SLOT(handleMediaSourceReady()));
- connect(m_sourceResolver, SIGNAL(error(long)), this, SLOT(handleMediaSourceError(long)));
- connect(m_decoderSourceReader, SIGNAL(finished()), this, SLOT(handleSourceFinished()));
-
- QAudioFormat defaultFormat;
- defaultFormat.setCodec("audio/pcm");
- setAudioFormat(defaultFormat);
-}
-
-MFAudioDecoderControl::~MFAudioDecoderControl()
-{
- if (m_mfOutputType)
- m_mfOutputType->Release();
- m_decoderSourceReader->shutdown();
- m_decoderSourceReader->Release();
- m_sourceResolver->Release();
- if (m_resampler)
- m_resampler->Release();
-}
-
-QAudioDecoder::State MFAudioDecoderControl::state() const
-{
- return m_state;
-}
-
-QString MFAudioDecoderControl::sourceFilename() const
-{
- return m_sourceFilename;
-}
-
-void MFAudioDecoderControl::onSourceCleared()
-{
- bool positionDirty = false;
- bool durationDirty = false;
- if (m_position != 0) {
- m_position = 0;
- positionDirty = true;
- }
- if (m_duration != 0) {
- m_duration = 0;
- durationDirty = true;
- }
- if (positionDirty)
- emit positionChanged(m_position);
- if (durationDirty)
- emit durationChanged(m_duration);
-}
-
-void MFAudioDecoderControl::setSourceFilename(const QString &fileName)
-{
- if (!m_device && m_sourceFilename == fileName)
- return;
- m_sourceReady = false;
- m_sourceResolver->cancel();
- m_decoderSourceReader->setSource(0, m_audioFormat);
- m_device = 0;
- m_sourceFilename = fileName;
- if (!m_sourceFilename.isEmpty()) {
- m_sourceResolver->shutdown();
- QUrl url;
- if (m_sourceFilename.startsWith(':'))
- url = QUrl(QStringLiteral("qrc%1").arg(m_sourceFilename));
- else
- url = QUrl::fromLocalFile(m_sourceFilename);
- m_sourceResolver->load(url, 0);
- m_loadingSource = true;
- } else {
- onSourceCleared();
- }
- emit sourceChanged();
-}
-
-QIODevice* MFAudioDecoderControl::sourceDevice() const
-{
- return m_device;
-}
-
-void MFAudioDecoderControl::setSourceDevice(QIODevice *device)
-{
- if (m_device == device && m_sourceFilename.isEmpty())
- return;
- m_sourceReady = false;
- m_sourceResolver->cancel();
- m_decoderSourceReader->setSource(0, m_audioFormat);
- m_sourceFilename.clear();
- m_device = device;
- if (m_device) {
- m_sourceResolver->shutdown();
- m_sourceResolver->load(QUrl(), m_device);
- m_loadingSource = true;
- } else {
- onSourceCleared();
- }
- emit sourceChanged();
-}
-
-void MFAudioDecoderControl::updateResamplerOutputType()
-{
- m_resamplerDirty = false;
- if (m_audioFormat == m_sourceOutputFormat)
- return;
- HRESULT hr = m_resampler->SetOutputType(m_mfOutputStreamID, m_mfOutputType, 0);
- if (SUCCEEDED(hr)) {
- MFT_OUTPUT_STREAM_INFO streamInfo;
- m_resampler->GetOutputStreamInfo(m_mfOutputStreamID, &streamInfo);
- if ((streamInfo.dwFlags & (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES | MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES)) == 0) {
- //if resampler does not allocate output sample memory, we do it here
- if (m_convertSample) {
- m_convertSample->Release();
- m_convertSample = 0;
- }
- if (SUCCEEDED(MFCreateSample(&m_convertSample))) {
- IMFMediaBuffer *mbuf = 0;;
- if (SUCCEEDED(MFCreateMemoryBuffer(streamInfo.cbSize, &mbuf))) {
- m_convertSample->AddBuffer(mbuf);
- mbuf->Release();
- }
- }
- }
- } else {
- qWarning() << "MFAudioDecoderControl: failed to SetOutputType of resampler" << hr;
- }
-}
-
-void MFAudioDecoderControl::handleMediaSourceReady()
-{
- m_loadingSource = false;
- m_sourceReady = true;
- IMFMediaType *mediaType = m_decoderSourceReader->setSource(m_sourceResolver->mediaSource(), m_audioFormat);
- m_sourceOutputFormat = QAudioFormat();
-
- if (mediaType) {
- m_sourceOutputFormat = m_audioFormat;
- QAudioFormat af = m_audioFormat;
-
- UINT32 val = 0;
- if (SUCCEEDED(mediaType->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &val))) {
- m_sourceOutputFormat.setChannelCount(int(val));
- }
- if (SUCCEEDED(mediaType->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &val))) {
- m_sourceOutputFormat.setSampleRate(int(val));
- }
- if (SUCCEEDED(mediaType->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, &val))) {
- m_sourceOutputFormat.setSampleSize(int(val));
- }
-
- GUID subType;
- if (SUCCEEDED(mediaType->GetGUID(MF_MT_SUBTYPE, &subType))) {
- if (subType == MFAudioFormat_Float) {
- m_sourceOutputFormat.setSampleType(QAudioFormat::Float);
- } else if (m_sourceOutputFormat.sampleSize() == 8) {
- m_sourceOutputFormat.setSampleType(QAudioFormat::UnSignedInt);
- } else {
- m_sourceOutputFormat.setSampleType(QAudioFormat::SignedInt);
- }
- }
- if (m_sourceOutputFormat.sampleType() != QAudioFormat::Float) {
- m_sourceOutputFormat.setByteOrder(QAudioFormat::LittleEndian);
- }
-
- if (m_audioFormat.sampleType() != QAudioFormat::Float
- && m_audioFormat.sampleType() != QAudioFormat::SignedInt) {
- af.setSampleType(m_sourceOutputFormat.sampleType());
- }
- if (af.sampleType() == QAudioFormat::SignedInt) {
- af.setByteOrder(QAudioFormat::LittleEndian);
- }
- if (m_audioFormat.channelCount() <= 0) {
- af.setChannelCount(m_sourceOutputFormat.channelCount());
- }
- if (m_audioFormat.sampleRate() <= 0) {
- af.setSampleRate(m_sourceOutputFormat.sampleRate());
- }
- if (m_audioFormat.sampleSize() <= 0) {
- af.setSampleSize(m_sourceOutputFormat.sampleSize());
- }
- setAudioFormat(af);
- }
-
- if (m_sourceResolver->mediaSource()) {
- if (mediaType && m_resampler) {
- HRESULT hr = S_OK;
- hr = m_resampler->SetInputType(m_mfInputStreamID, mediaType, 0);
- if (SUCCEEDED(hr)) {
- updateResamplerOutputType();
- } else {
- qWarning() << "MFAudioDecoderControl: failed to SetInputType of resampler" << hr;
- }
- }
- IMFPresentationDescriptor *pd;
- if (SUCCEEDED(m_sourceResolver->mediaSource()->CreatePresentationDescriptor(&pd))) {
- UINT64 duration = 0;
- pd->GetUINT64(MF_PD_DURATION, &duration);
- pd->Release();
- duration /= 10000;
- if (m_duration != qint64(duration)) {
- m_duration = qint64(duration);
- emit durationChanged(m_duration);
- }
- }
- if (m_state == QAudioDecoder::DecodingState) {
- activatePipeline();
- }
- } else if (m_state != QAudioDecoder::StoppedState) {
- m_state = QAudioDecoder::StoppedState;
- emit stateChanged(m_state);
- }
-}
-
-void MFAudioDecoderControl::handleMediaSourceError(long hr)
-{
- Q_UNUSED(hr);
- m_loadingSource = false;
- m_decoderSourceReader->setSource(0, m_audioFormat);
- if (m_state != QAudioDecoder::StoppedState) {
- m_state = QAudioDecoder::StoppedState;
- emit stateChanged(m_state);
- }
-}
-
-void MFAudioDecoderControl::activatePipeline()
-{
- Q_ASSERT(!m_bufferReady);
- m_state = QAudioDecoder::DecodingState;
- connect(m_decoderSourceReader, SIGNAL(sampleAdded()), this, SLOT(handleSampleAdded()));
- if (m_resamplerDirty) {
- updateResamplerOutputType();
- }
- m_decoderSourceReader->reset();
- m_decoderSourceReader->readNextSample();
- if (m_position != 0) {
- m_position = 0;
- emit positionChanged(0);
- }
-}
-
-void MFAudioDecoderControl::start()
-{
- if (m_state != QAudioDecoder::StoppedState)
- return;
-
- if (m_loadingSource) {
- //deferred starting
- m_state = QAudioDecoder::DecodingState;
- emit stateChanged(m_state);
- return;
- }
-
- if (!m_decoderSourceReader->mediaSource())
- return;
- activatePipeline();
- emit stateChanged(m_state);
-}
-
-void MFAudioDecoderControl::stop()
-{
- if (m_state == QAudioDecoder::StoppedState)
- return;
- m_state = QAudioDecoder::StoppedState;
- disconnect(m_decoderSourceReader, SIGNAL(sampleAdded()), this, SLOT(handleSampleAdded()));
- if (m_bufferReady) {
- m_bufferReady = false;
- emit bufferAvailableChanged(m_bufferReady);
- }
- emit stateChanged(m_state);
-}
-
-void MFAudioDecoderControl::handleSampleAdded()
-{
- QList<IMFSample*> samples = m_decoderSourceReader->takeSamples();
- Q_ASSERT(samples.count() > 0);
- Q_ASSERT(!m_bufferReady);
- Q_ASSERT(m_resampler);
- LONGLONG sampleStartTime = 0;
- IMFSample *firstSample = samples.first();
- firstSample->GetSampleTime(&sampleStartTime);
- QByteArray abuf;
- if (m_sourceOutputFormat == m_audioFormat) {
- //no need for resampling
- for (IMFSample *s : qAsConst(samples)) {
- IMFMediaBuffer *buffer;
- s->ConvertToContiguousBuffer(&buffer);
- DWORD bufLen = 0;
- BYTE *buf = 0;
- if (SUCCEEDED(buffer->Lock(&buf, NULL, &bufLen))) {
- abuf.push_back(QByteArray(reinterpret_cast<char*>(buf), bufLen));
- buffer->Unlock();
- }
- buffer->Release();
- LONGLONG sampleTime = 0, sampleDuration = 0;
- s->GetSampleTime(&sampleTime);
- s->GetSampleDuration(&sampleDuration);
- m_position = qint64(sampleTime + sampleDuration) / 10000;
- s->Release();
- }
- } else {
- for (IMFSample *s : qAsConst(samples)) {
- HRESULT hr = m_resampler->ProcessInput(m_mfInputStreamID, s, 0);
- if (SUCCEEDED(hr)) {
- MFT_OUTPUT_DATA_BUFFER outputDataBuffer;
- outputDataBuffer.dwStreamID = m_mfOutputStreamID;
- while (true) {
- outputDataBuffer.pEvents = 0;
- outputDataBuffer.dwStatus = 0;
- outputDataBuffer.pSample = m_convertSample;
- DWORD status = 0;
- if (SUCCEEDED(m_resampler->ProcessOutput(0, 1, &outputDataBuffer, &status))) {
- IMFMediaBuffer *buffer;
- outputDataBuffer.pSample->ConvertToContiguousBuffer(&buffer);
- DWORD bufLen = 0;
- BYTE *buf = 0;
- if (SUCCEEDED(buffer->Lock(&buf, NULL, &bufLen))) {
- abuf.push_back(QByteArray(reinterpret_cast<char*>(buf), bufLen));
- buffer->Unlock();
- }
- buffer->Release();
- } else {
- break;
- }
- }
- }
- LONGLONG sampleTime = 0, sampleDuration = 0;
- s->GetSampleTime(&sampleTime);
- s->GetSampleDuration(&sampleDuration);
- m_position = qint64(sampleTime + sampleDuration) / 10000;
- s->Release();
- }
- }
- // WMF uses 100-nanosecond units, QAudioDecoder uses milliseconds, QAudioBuffer uses microseconds...
- m_cachedAudioBuffer = QAudioBuffer(abuf, m_audioFormat, qint64(sampleStartTime / 10));
- m_bufferReady = true;
- emit positionChanged(m_position);
- emit bufferAvailableChanged(m_bufferReady);
- emit bufferReady();
-}
-
-void MFAudioDecoderControl::handleSourceFinished()
-{
- stop();
- emit finished();
-}
-
-QAudioFormat MFAudioDecoderControl::audioFormat() const
-{
- return m_audioFormat;
-}
-
-void MFAudioDecoderControl::setAudioFormat(const QAudioFormat &format)
-{
- if (m_audioFormat == format || !m_resampler)
- return;
- if (format.codec() != QLatin1String("audio/x-wav") && format.codec() != QLatin1String("audio/pcm")) {
- qWarning("MFAudioDecoderControl does not accept non-pcm audio format!");
- return;
- }
- m_audioFormat = format;
-
- if (m_audioFormat.isValid()) {
- IMFMediaType *mediaType = 0;
- MFCreateMediaType(&mediaType);
- mediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
- if (format.sampleType() == QAudioFormat::Float) {
- mediaType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_Float);
- } else {
- mediaType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
- }
-
- mediaType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, UINT32(m_audioFormat.channelCount()));
- mediaType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, UINT32(m_audioFormat.sampleRate()));
- UINT32 alignmentBlock = UINT32(m_audioFormat.channelCount() * m_audioFormat.sampleSize() / 8);
- mediaType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, alignmentBlock);
- UINT32 avgBytesPerSec = UINT32(m_audioFormat.sampleRate() * m_audioFormat.sampleSize() / 8 * m_audioFormat.channelCount());
- mediaType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, avgBytesPerSec);
- mediaType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, UINT32(m_audioFormat.sampleSize()));
- mediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
-
- if (m_mfOutputType)
- m_mfOutputType->Release();
- m_mfOutputType = mediaType;
- } else {
- if (m_mfOutputType)
- m_mfOutputType->Release();
- m_mfOutputType = NULL;
- }
-
- if (m_sourceReady && m_state == QAudioDecoder::StoppedState) {
- updateResamplerOutputType();
- } else {
- m_resamplerDirty = true;
- }
-
- emit formatChanged(m_audioFormat);
-}
-
-QAudioBuffer MFAudioDecoderControl::read()
-{
- if (!m_bufferReady)
- return QAudioBuffer();
- QAudioBuffer buffer = m_cachedAudioBuffer;
- m_bufferReady = false;
- emit bufferAvailableChanged(m_bufferReady);
- m_decoderSourceReader->readNextSample();
- return buffer;
-}
-
-bool MFAudioDecoderControl::bufferAvailable() const
-{
- return m_bufferReady;
-}
-
-qint64 MFAudioDecoderControl::position() const
-{
- return m_position;
-}
-
-qint64 MFAudioDecoderControl::duration() const
-{
- return m_duration;
-}
diff --git a/src/plugins/wmf/decoder/mfaudiodecodercontrol.h b/src/plugins/wmf/decoder/mfaudiodecodercontrol.h
deleted file mode 100644
index cfb8e9091..000000000
--- a/src/plugins/wmf/decoder/mfaudiodecodercontrol.h
+++ /dev/null
@@ -1,108 +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 MFAUDIODECODERCONTROL_H
-#define MFAUDIODECODERCONTROL_H
-
-#include "qaudiodecodercontrol.h"
-#include "mfdecodersourcereader.h"
-#include "sourceresolver.h"
-
-QT_USE_NAMESPACE
-
-class MFAudioDecoderControl : public QAudioDecoderControl
-{
- Q_OBJECT
-public:
- MFAudioDecoderControl(QObject *parent = 0);
- ~MFAudioDecoderControl();
-
- QAudioDecoder::State state() const;
-
- QString sourceFilename() const;
- void setSourceFilename(const QString &fileName);
-
- QIODevice* sourceDevice() const;
- void setSourceDevice(QIODevice *device);
-
- void start();
- void stop();
-
- QAudioFormat audioFormat() const;
- void setAudioFormat(const QAudioFormat &format);
-
- QAudioBuffer read();
- bool bufferAvailable() const;
-
- qint64 position() const;
- qint64 duration() const;
-
-private Q_SLOTS:
- void handleMediaSourceReady();
- void handleMediaSourceError(long hr);
- void handleSampleAdded();
- void handleSourceFinished();
-
-private:
- void updateResamplerOutputType();
- void activatePipeline();
- void onSourceCleared();
-
- MFDecoderSourceReader *m_decoderSourceReader;
- SourceResolver *m_sourceResolver;
- IMFTransform *m_resampler;
- QAudioDecoder::State m_state;
- QString m_sourceFilename;
- QIODevice *m_device;
- QAudioFormat m_audioFormat;
- DWORD m_mfInputStreamID;
- DWORD m_mfOutputStreamID;
- bool m_bufferReady;
- QAudioBuffer m_cachedAudioBuffer;
- qint64 m_duration;
- qint64 m_position;
- bool m_loadingSource;
- IMFMediaType *m_mfOutputType;
- IMFSample *m_convertSample;
- QAudioFormat m_sourceOutputFormat;
- bool m_sourceReady;
- bool m_resamplerDirty;
-};
-
-#endif//MFAUDIODECODERCONTROL_H
diff --git a/src/plugins/wmf/decoder/mfdecoderservice.cpp b/src/plugins/wmf/decoder/mfdecoderservice.cpp
deleted file mode 100644
index c6ed3976a..000000000
--- a/src/plugins/wmf/decoder/mfdecoderservice.cpp
+++ /dev/null
@@ -1,65 +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 "mfdecoderservice.h"
-#include "mfaudiodecodercontrol.h"
-
-MFAudioDecoderService::MFAudioDecoderService(QObject *parent)
- : QMediaService(parent)
-{
-}
-
-MFAudioDecoderService::~MFAudioDecoderService()
-{
-}
-
-QMediaControl* MFAudioDecoderService::requestControl(const char *name)
-{
- if (qstrcmp(name, QAudioDecoderControl_iid) == 0) {
- return new MFAudioDecoderControl(this);
- }
- return 0;
-}
-
-void MFAudioDecoderService::releaseControl(QMediaControl *control)
-{
- if (control && control->inherits("MFAudioDecoderControl")) {
- delete control;
- }
-}
diff --git a/src/plugins/wmf/decoder/mfdecoderservice.h b/src/plugins/wmf/decoder/mfdecoderservice.h
deleted file mode 100644
index 14899ee39..000000000
--- a/src/plugins/wmf/decoder/mfdecoderservice.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 MFDECODERSERVICE_H
-#define MFDECODERSERVICE_H
-
-#include "qmediaservice.h"
-
-class MFAudioDecoderService : public QMediaService
-{
- Q_OBJECT
-public:
- MFAudioDecoderService(QObject *parent = 0);
- ~MFAudioDecoderService();
-
- QMediaControl* requestControl(const char *name);
- void releaseControl(QMediaControl *control);
-};
-
-#endif//MFDECODERSERVICE_H
diff --git a/src/plugins/wmf/decoder/mfdecodersourcereader.cpp b/src/plugins/wmf/decoder/mfdecodersourcereader.cpp
deleted file mode 100644
index e907cfaf0..000000000
--- a/src/plugins/wmf/decoder/mfdecodersourcereader.cpp
+++ /dev/null
@@ -1,197 +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 "mfdecodersourcereader.h"
-
-MFDecoderSourceReader::MFDecoderSourceReader(QObject *parent)
- : m_cRef(1)
- , m_sourceReader(0)
- , m_source(0)
-{
- Q_UNUSED(parent);
-}
-
-void MFDecoderSourceReader::shutdown()
-{
- if (m_source) {
- m_source->Release();
- m_source = NULL;
- }
- if (m_sourceReader) {
- m_sourceReader->Release();
- m_sourceReader = NULL;
- }
-}
-
-IMFMediaSource* MFDecoderSourceReader::mediaSource()
-{
- return m_source;
-}
-
-IMFMediaType* MFDecoderSourceReader::setSource(IMFMediaSource *source, const QAudioFormat &audioFormat)
-{
- IMFMediaType *mediaType = NULL;
- if (m_source == source)
- return mediaType;
- if (m_source) {
- m_source->Release();
- m_source = NULL;
- }
- if (m_sourceReader) {
- m_sourceReader->Release();
- m_sourceReader = NULL;
- }
- if (!source)
- return mediaType;
- IMFAttributes *attr = NULL;
- MFCreateAttributes(&attr, 1);
- if (SUCCEEDED(attr->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, this))) {
- if (SUCCEEDED(MFCreateSourceReaderFromMediaSource(source, attr, &m_sourceReader))) {
- m_source = source;
- m_source->AddRef();
- m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_ALL_STREAMS), FALSE);
- m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), TRUE);
- IMFMediaType *pPartialType = NULL;
- MFCreateMediaType(&pPartialType);
- pPartialType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
-
- if (audioFormat.sampleType() == QAudioFormat::Float) {
- pPartialType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_Float);
- } else {
- pPartialType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
- }
-
- m_sourceReader->SetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), NULL, pPartialType);
- pPartialType->Release();
- m_sourceReader->GetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), &mediaType);
- // Ensure the stream is selected.
- m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), TRUE);
- }
- attr->Release();
- }
- return mediaType;
-}
-
-void MFDecoderSourceReader::reset()
-{
- if (!m_sourceReader)
- return;
- PROPVARIANT vPos;
- PropVariantInit(&vPos);
- vPos.vt = VT_I8;
- vPos.uhVal.QuadPart = 0;
- m_sourceReader->SetCurrentPosition(GUID_NULL, vPos);
-}
-
-void MFDecoderSourceReader::readNextSample()
-{
- if (!m_sourceReader)
- return;
- m_sourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, NULL, NULL, NULL, NULL);
-}
-
-QList<IMFSample*> MFDecoderSourceReader::takeSamples() //internal samples will be cleared after this
-{
- QList<IMFSample*> samples;
- m_samplesMutex.lock();
- samples = m_cachedSamples;
- m_cachedSamples.clear();
- m_samplesMutex.unlock();
- return samples;
-}
-
-//from IUnknown
-STDMETHODIMP MFDecoderSourceReader::QueryInterface(REFIID riid, LPVOID *ppvObject)
-{
- if (!ppvObject)
- return E_POINTER;
- if (riid == IID_IMFSourceReaderCallback) {
- *ppvObject = static_cast<IMFSourceReaderCallback*>(this);
- } else if (riid == IID_IUnknown) {
- *ppvObject = static_cast<IUnknown*>(this);
- } else {
- *ppvObject = NULL;
- return E_NOINTERFACE;
- }
- AddRef();
- return S_OK;
-}
-
-STDMETHODIMP_(ULONG) MFDecoderSourceReader::AddRef(void)
-{
- return InterlockedIncrement(&m_cRef);
-}
-
-STDMETHODIMP_(ULONG) MFDecoderSourceReader::Release(void)
-{
- LONG cRef = InterlockedDecrement(&m_cRef);
- if (cRef == 0) {
- this->deleteLater();
- }
- return cRef;
-}
-
-//from IMFSourceReaderCallback
-STDMETHODIMP MFDecoderSourceReader::OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex,
- DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample)
-{
- Q_UNUSED(hrStatus);
- Q_UNUSED(dwStreamIndex);
- Q_UNUSED(llTimestamp);
- if (pSample) {
- pSample->AddRef();
- m_samplesMutex.lock();
- m_cachedSamples.push_back(pSample);
- m_samplesMutex.unlock();
- emit sampleAdded();
- } else if ((dwStreamFlags & MF_SOURCE_READERF_ENDOFSTREAM) == MF_SOURCE_READERF_ENDOFSTREAM) {
- emit finished();
- }
- return S_OK;
-}
-
-STDMETHODIMP MFDecoderSourceReader::OnFlush(DWORD)
-{
- return S_OK;
-}
-
-STDMETHODIMP MFDecoderSourceReader::OnEvent(DWORD, IMFMediaEvent*)
-{
- return S_OK;
-}
diff --git a/src/plugins/wmf/decoder/mfdecodersourcereader.h b/src/plugins/wmf/decoder/mfdecodersourcereader.h
deleted file mode 100644
index 21c6b5eb3..000000000
--- a/src/plugins/wmf/decoder/mfdecodersourcereader.h
+++ /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:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 MFDECODERSOURCEREADER_H
-#define MFDECODERSOURCEREADER_H
-#include <mfapi.h>
-#include <mfidl.h>
-#include <Mfreadwrite.h>
-
-#include <QtCore/qobject.h>
-#include <QtCore/qmutex.h>
-#include "qaudioformat.h"
-
-QT_USE_NAMESPACE
-
-class MFDecoderSourceReader : public QObject, public IMFSourceReaderCallback
-{
- Q_OBJECT
-public:
- MFDecoderSourceReader(QObject *parent = 0);
- void shutdown();
-
- IMFMediaSource* mediaSource();
- IMFMediaType* setSource(IMFMediaSource *source, const QAudioFormat &audioFormat);
-
- void reset();
- void readNextSample();
- QList<IMFSample*> takeSamples(); //internal samples will be cleared after this
-
- //from IUnknown
- STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject);
- STDMETHODIMP_(ULONG) AddRef(void);
- STDMETHODIMP_(ULONG) Release(void);
-
- //from IMFSourceReaderCallback
- STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex,
- DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample);
- STDMETHODIMP OnFlush(DWORD dwStreamIndex);
- STDMETHODIMP OnEvent(DWORD dwStreamIndex, IMFMediaEvent *pEvent);
-
-Q_SIGNALS:
- void sampleAdded();
- void finished();
-
-private:
- long m_cRef;
- QList<IMFSample*> m_cachedSamples;
- QMutex m_samplesMutex;
-
- IMFSourceReader *m_sourceReader;
- IMFMediaSource *m_source;
-};
-#endif//MFDECODERSOURCEREADER_H
diff --git a/src/plugins/wmf/mfstream.cpp b/src/plugins/wmf/mfstream.cpp
deleted file mode 100644
index a98b5a704..000000000
--- a/src/plugins/wmf/mfstream.cpp
+++ /dev/null
@@ -1,361 +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:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "mfstream.h"
-#include <QtCore/qcoreapplication.h>
-
-//MFStream is added for supporting QIODevice type of media source.
-//It is used to delegate invocations from media foundation(through IMFByteStream) to QIODevice.
-
-MFStream::MFStream(QIODevice *stream, bool ownStream)
- : m_cRef(1)
- , m_stream(stream)
- , m_ownStream(ownStream)
- , m_currentReadResult(0)
-{
- //Move to the thread of the stream object
- //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()
-{
- if (m_currentReadResult)
- m_currentReadResult->Release();
- if (m_ownStream)
- m_stream->deleteLater();
-}
-
-//from IUnknown
-STDMETHODIMP MFStream::QueryInterface(REFIID riid, LPVOID *ppvObject)
-{
- if (!ppvObject)
- return E_POINTER;
- if (riid == IID_IMFByteStream) {
- *ppvObject = static_cast<IMFByteStream*>(this);
- } else if (riid == IID_IUnknown) {
- *ppvObject = static_cast<IUnknown*>(this);
- } else {
- *ppvObject = NULL;
- return E_NOINTERFACE;
- }
- AddRef();
- return S_OK;
-}
-
-STDMETHODIMP_(ULONG) MFStream::AddRef(void)
-{
- return InterlockedIncrement(&m_cRef);
-}
-
-STDMETHODIMP_(ULONG) MFStream::Release(void)
-{
- LONG cRef = InterlockedDecrement(&m_cRef);
- if (cRef == 0) {
- this->deleteLater();
- }
- return cRef;
-}
-
-
-//from IMFByteStream
-STDMETHODIMP MFStream::GetCapabilities(DWORD *pdwCapabilities)
-{
- if (!pdwCapabilities)
- return E_INVALIDARG;
- *pdwCapabilities = MFBYTESTREAM_IS_READABLE;
- if (!m_stream->isSequential())
- *pdwCapabilities |= MFBYTESTREAM_IS_SEEKABLE;
- return S_OK;
-}
-
-STDMETHODIMP MFStream::GetLength(QWORD *pqwLength)
-{
- if (!pqwLength)
- return E_INVALIDARG;
- QMutexLocker locker(&m_mutex);
- *pqwLength = QWORD(m_stream->size());
- return S_OK;
-}
-
-STDMETHODIMP MFStream::SetLength(QWORD)
-{
- return E_NOTIMPL;
-}
-
-STDMETHODIMP MFStream::GetCurrentPosition(QWORD *pqwPosition)
-{
- if (!pqwPosition)
- return E_INVALIDARG;
- QMutexLocker locker(&m_mutex);
- *pqwPosition = m_stream->pos();
- return S_OK;
-}
-
-STDMETHODIMP MFStream::SetCurrentPosition(QWORD qwPosition)
-{
- QMutexLocker locker(&m_mutex);
- //SetCurrentPosition may happend during the BeginRead/EndRead pair,
- //refusing to execute SetCurrentPosition during that time seems to be
- //the simplest workable solution
- if (m_currentReadResult)
- return S_FALSE;
-
- bool seekOK = m_stream->seek(qint64(qwPosition));
- if (seekOK)
- return S_OK;
- else
- return S_FALSE;
-}
-
-STDMETHODIMP MFStream::IsEndOfStream(BOOL *pfEndOfStream)
-{
- if (!pfEndOfStream)
- return E_INVALIDARG;
- QMutexLocker locker(&m_mutex);
- *pfEndOfStream = m_stream->atEnd() ? TRUE : FALSE;
- return S_OK;
-}
-
-STDMETHODIMP MFStream::Read(BYTE *pb, ULONG cb, ULONG *pcbRead)
-{
- QMutexLocker locker(&m_mutex);
- qint64 read = m_stream->read((char*)(pb), qint64(cb));
- if (pcbRead)
- *pcbRead = ULONG(read);
- return S_OK;
-}
-
-STDMETHODIMP MFStream::BeginRead(BYTE *pb, ULONG cb, IMFAsyncCallback *pCallback,
- IUnknown *punkState)
-{
- if (!pCallback || !pb)
- return E_INVALIDARG;
-
- Q_ASSERT(m_currentReadResult == NULL);
-
- AsyncReadState *state = new (std::nothrow) AsyncReadState(pb, cb);
- if (state == NULL)
- return E_OUTOFMEMORY;
-
- HRESULT hr = MFCreateAsyncResult(state, pCallback, punkState, &m_currentReadResult);
- state->Release();
- if (FAILED(hr))
- return hr;
-
- QCoreApplication::postEvent(this, new QEvent(QEvent::User));
- return hr;
-}
-
-STDMETHODIMP MFStream::EndRead(IMFAsyncResult* pResult, ULONG *pcbRead)
-{
- if (!pcbRead)
- return E_INVALIDARG;
- IUnknown *pUnk;
- pResult->GetObject(&pUnk);
- AsyncReadState *state = static_cast<AsyncReadState*>(pUnk);
- *pcbRead = state->bytesRead();
- pUnk->Release();
-
- m_currentReadResult->Release();
- m_currentReadResult = NULL;
-
- return S_OK;
-}
-
-STDMETHODIMP MFStream::Write(const BYTE *, ULONG, ULONG *)
-{
- return E_NOTIMPL;
-}
-
-STDMETHODIMP MFStream::BeginWrite(const BYTE *, ULONG ,
- IMFAsyncCallback *,
- IUnknown *)
-{
- return E_NOTIMPL;
-}
-
-STDMETHODIMP MFStream::EndWrite(IMFAsyncResult *,
- ULONG *)
-{
- return E_NOTIMPL;
-}
-
-STDMETHODIMP MFStream::Seek(
- MFBYTESTREAM_SEEK_ORIGIN SeekOrigin,
- LONGLONG llSeekOffset,
- DWORD,
- QWORD *pqwCurrentPosition)
-{
- QMutexLocker locker(&m_mutex);
- if (m_currentReadResult)
- return S_FALSE;
-
- qint64 pos = qint64(llSeekOffset);
- switch (SeekOrigin) {
- case msoBegin:
- break;
- case msoCurrent:
- pos += m_stream->pos();
- break;
- }
- bool seekOK = m_stream->seek(pos);
- if (pqwCurrentPosition)
- *pqwCurrentPosition = pos;
- if (seekOK)
- return S_OK;
- else
- return S_FALSE;
-}
-
-STDMETHODIMP MFStream::Flush()
-{
- return E_NOTIMPL;
-}
-
-STDMETHODIMP MFStream::Close()
-{
- QMutexLocker locker(&m_mutex);
- if (m_ownStream)
- m_stream->close();
- return S_OK;
-}
-
-void MFStream::doRead()
-{
- bool readDone = true;
- IUnknown *pUnk = NULL;
- HRESULT hr = m_currentReadResult->GetObject(&pUnk);
- if (SUCCEEDED(hr)) {
- //do actual read
- AsyncReadState *state = static_cast<AsyncReadState*>(pUnk);
- ULONG cbRead;
- Read(state->pb(), state->cb() - state->bytesRead(), &cbRead);
- pUnk->Release();
-
- state->setBytesRead(cbRead + state->bytesRead());
- if (state->cb() > state->bytesRead() && !m_stream->atEnd()) {
- readDone = false;
- }
- }
-
- if (readDone) {
- //now inform the original caller
- m_currentReadResult->SetStatus(hr);
- MFInvokeCallback(m_currentReadResult);
- }
-}
-
-
-void MFStream::handleReadyRead()
-{
- doRead();
-}
-
-void MFStream::customEvent(QEvent *event)
-{
- if (event->type() != QEvent::User) {
- QObject::customEvent(event);
- return;
- }
- doRead();
-}
-
-//AsyncReadState is a helper class used in BeginRead for asynchronous operation
-//to record some BeginRead parameters, so these parameters could be
-//used later when actually executing the read operation in another thread.
-MFStream::AsyncReadState::AsyncReadState(BYTE *pb, ULONG cb)
- : m_cRef(1)
- , m_pb(pb)
- , m_cb(cb)
- , m_cbRead(0)
-{
-}
-
-//from IUnknown
-STDMETHODIMP MFStream::AsyncReadState::QueryInterface(REFIID riid, LPVOID *ppvObject)
-{
- if (!ppvObject)
- return E_POINTER;
-
- if (riid == IID_IUnknown) {
- *ppvObject = static_cast<IUnknown*>(this);
- } else {
- *ppvObject = NULL;
- return E_NOINTERFACE;
- }
- AddRef();
- return S_OK;
-}
-
-STDMETHODIMP_(ULONG) MFStream::AsyncReadState::AddRef(void)
-{
- return InterlockedIncrement(&m_cRef);
-}
-
-STDMETHODIMP_(ULONG) MFStream::AsyncReadState::Release(void)
-{
- LONG cRef = InterlockedDecrement(&m_cRef);
- if (cRef == 0)
- delete this;
- // For thread safety, return a temporary variable.
- return cRef;
-}
-
-BYTE* MFStream::AsyncReadState::pb() const
-{
- return m_pb;
-}
-
-ULONG MFStream::AsyncReadState::cb() const
-{
- return m_cb;
-}
-
-ULONG MFStream::AsyncReadState::bytesRead() const
-{
- return m_cbRead;
-}
-
-void MFStream::AsyncReadState::setBytesRead(ULONG cbRead)
-{
- m_cbRead = cbRead;
-}
diff --git a/src/plugins/wmf/mfstream.h b/src/plugins/wmf/mfstream.h
deleted file mode 100644
index f98ab42a9..000000000
--- a/src/plugins/wmf/mfstream.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 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$
-**
-****************************************************************************/
-
-#ifndef MFSTREAM_H
-#define MFSTREAM_H
-
-#include <mfapi.h>
-#include <mfidl.h>
-#include <QtCore/qmutex.h>
-#include <QtCore/qiodevice.h>
-#include <QtCore/qcoreevent.h>
-
-QT_USE_NAMESPACE
-
-class MFStream : public QObject, public IMFByteStream
-{
- Q_OBJECT
-public:
- MFStream(QIODevice *stream, bool ownStream);
-
- ~MFStream();
-
- //from IUnknown
- STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject);
-
- STDMETHODIMP_(ULONG) AddRef(void);
-
- STDMETHODIMP_(ULONG) Release(void);
-
-
- //from IMFByteStream
- STDMETHODIMP GetCapabilities(DWORD *pdwCapabilities);
-
- STDMETHODIMP GetLength(QWORD *pqwLength);
-
- STDMETHODIMP SetLength(QWORD);
-
- STDMETHODIMP GetCurrentPosition(QWORD *pqwPosition);
-
- STDMETHODIMP SetCurrentPosition(QWORD qwPosition);
-
- STDMETHODIMP IsEndOfStream(BOOL *pfEndOfStream);
-
- STDMETHODIMP Read(BYTE *pb, ULONG cb, ULONG *pcbRead);
-
- STDMETHODIMP BeginRead(BYTE *pb, ULONG cb, IMFAsyncCallback *pCallback,
- IUnknown *punkState);
-
- STDMETHODIMP EndRead(IMFAsyncResult* pResult, ULONG *pcbRead);
-
- STDMETHODIMP Write(const BYTE *, ULONG, ULONG *);
-
- STDMETHODIMP BeginWrite(const BYTE *, ULONG ,
- IMFAsyncCallback *,
- IUnknown *);
-
- STDMETHODIMP EndWrite(IMFAsyncResult *,
- ULONG *);
-
- STDMETHODIMP Seek(
- MFBYTESTREAM_SEEK_ORIGIN SeekOrigin,
- LONGLONG llSeekOffset,
- DWORD,
- QWORD *pqwCurrentPosition);
-
- STDMETHODIMP Flush();
-
- STDMETHODIMP Close();
-
-private:
- class AsyncReadState : public IUnknown
- {
- public:
- AsyncReadState(BYTE *pb, ULONG cb);
-
- //from IUnknown
- STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject);
-
- STDMETHODIMP_(ULONG) AddRef(void);
-
- STDMETHODIMP_(ULONG) Release(void);
-
- BYTE* pb() const;
- ULONG cb() const;
- ULONG bytesRead() const;
-
- void setBytesRead(ULONG cbRead);
-
- private:
- long m_cRef;
- BYTE *m_pb;
- ULONG m_cb;
- ULONG m_cbRead;
- };
-
- long m_cRef;
- QIODevice *m_stream;
- bool m_ownStream;
- DWORD m_workQueueId;
- QMutex m_mutex;
-
- void doRead();
-
-private Q_SLOTS:
- void handleReadyRead();
-
-protected:
- void customEvent(QEvent *event);
- IMFAsyncResult *m_currentReadResult;
-};
-
-#endif
diff --git a/src/plugins/wmf/player/mfactivate.cpp b/src/plugins/wmf/player/mfactivate.cpp
deleted file mode 100644
index e06906584..000000000
--- a/src/plugins/wmf/player/mfactivate.cpp
+++ /dev/null
@@ -1,87 +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 "mfactivate.h"
-
-#include <mfapi.h>
-
-MFAbstractActivate::MFAbstractActivate()
- : m_attributes(0)
- , m_cRef(1)
-{
- MFCreateAttributes(&m_attributes, 0);
-}
-
-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/wmf/player/mfactivate.h b/src/plugins/wmf/player/mfactivate.h
deleted file mode 100644
index 3243296e8..000000000
--- a/src/plugins/wmf/player/mfactivate.h
+++ /dev/null
@@ -1,212 +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 MFACTIVATE_H
-#define MFACTIVATE_H
-
-#include <mfidl.h>
-
-class MFAbstractActivate : public IMFActivate
-{
-public:
- explicit MFAbstractActivate();
- virtual ~MFAbstractActivate();
-
- //from IUnknown
- STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject);
- STDMETHODIMP_(ULONG) AddRef(void);
- STDMETHODIMP_(ULONG) Release(void);
-
- //from IMFAttributes
- STDMETHODIMP GetItem(REFGUID guidKey, PROPVARIANT *pValue)
- {
- return m_attributes->GetItem(guidKey, pValue);
- }
-
- STDMETHODIMP GetItemType(REFGUID guidKey, MF_ATTRIBUTE_TYPE *pType)
- {
- return m_attributes->GetItemType(guidKey, pType);
- }
-
- STDMETHODIMP CompareItem(REFGUID guidKey, REFPROPVARIANT Value, BOOL *pbResult)
- {
- return m_attributes->CompareItem(guidKey, Value, pbResult);
- }
-
- STDMETHODIMP Compare(IMFAttributes *pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, BOOL *pbResult)
- {
- return m_attributes->Compare(pTheirs, MatchType, pbResult);
- }
-
- STDMETHODIMP GetUINT32(REFGUID guidKey, UINT32 *punValue)
- {
- return m_attributes->GetUINT32(guidKey, punValue);
- }
-
- STDMETHODIMP GetUINT64(REFGUID guidKey, UINT64 *punValue)
- {
- return m_attributes->GetUINT64(guidKey, punValue);
- }
-
- STDMETHODIMP GetDouble(REFGUID guidKey, double *pfValue)
- {
- return m_attributes->GetDouble(guidKey, pfValue);
- }
-
- STDMETHODIMP GetGUID(REFGUID guidKey, GUID *pguidValue)
- {
- return m_attributes->GetGUID(guidKey, pguidValue);
- }
-
- STDMETHODIMP GetStringLength(REFGUID guidKey, UINT32 *pcchLength)
- {
- return m_attributes->GetStringLength(guidKey, pcchLength);
- }
-
- STDMETHODIMP GetString(REFGUID guidKey, LPWSTR pwszValue, UINT32 cchBufSize, UINT32 *pcchLength)
- {
- return m_attributes->GetString(guidKey, pwszValue, cchBufSize, pcchLength);
- }
-
- STDMETHODIMP GetAllocatedString(REFGUID guidKey, LPWSTR *ppwszValue, UINT32 *pcchLength)
- {
- return m_attributes->GetAllocatedString(guidKey, ppwszValue, pcchLength);
- }
-
- STDMETHODIMP GetBlobSize(REFGUID guidKey, UINT32 *pcbBlobSize)
- {
- return m_attributes->GetBlobSize(guidKey, pcbBlobSize);
- }
-
- STDMETHODIMP GetBlob(REFGUID guidKey, UINT8 *pBuf, UINT32 cbBufSize, UINT32 *pcbBlobSize)
- {
- return m_attributes->GetBlob(guidKey, pBuf, cbBufSize, pcbBlobSize);
- }
-
- STDMETHODIMP GetAllocatedBlob(REFGUID guidKey, UINT8 **ppBuf, UINT32 *pcbSize)
- {
- return m_attributes->GetAllocatedBlob(guidKey, ppBuf, pcbSize);
- }
-
- STDMETHODIMP GetUnknown(REFGUID guidKey, REFIID riid, LPVOID *ppv)
- {
- return m_attributes->GetUnknown(guidKey, riid, ppv);
- }
-
- STDMETHODIMP SetItem(REFGUID guidKey, REFPROPVARIANT Value)
- {
- return m_attributes->SetItem(guidKey, Value);
- }
-
- STDMETHODIMP DeleteItem(REFGUID guidKey)
- {
- return m_attributes->DeleteItem(guidKey);
- }
-
- STDMETHODIMP DeleteAllItems()
- {
- return m_attributes->DeleteAllItems();
- }
-
- STDMETHODIMP SetUINT32(REFGUID guidKey, UINT32 unValue)
- {
- return m_attributes->SetUINT32(guidKey, unValue);
- }
-
- STDMETHODIMP SetUINT64(REFGUID guidKey, UINT64 unValue)
- {
- return m_attributes->SetUINT64(guidKey, unValue);
- }
-
- STDMETHODIMP SetDouble(REFGUID guidKey, double fValue)
- {
- return m_attributes->SetDouble(guidKey, fValue);
- }
-
- STDMETHODIMP SetGUID(REFGUID guidKey, REFGUID guidValue)
- {
- return m_attributes->SetGUID(guidKey, guidValue);
- }
-
- STDMETHODIMP SetString(REFGUID guidKey, LPCWSTR wszValue)
- {
- return m_attributes->SetString(guidKey, wszValue);
- }
-
- STDMETHODIMP SetBlob(REFGUID guidKey, const UINT8 *pBuf, UINT32 cbBufSize)
- {
- return m_attributes->SetBlob(guidKey, pBuf, cbBufSize);
- }
-
- STDMETHODIMP SetUnknown(REFGUID guidKey, IUnknown *pUnknown)
- {
- return m_attributes->SetUnknown(guidKey, pUnknown);
- }
-
- STDMETHODIMP LockStore()
- {
- return m_attributes->LockStore();
- }
-
- STDMETHODIMP UnlockStore()
- {
- return m_attributes->UnlockStore();
- }
-
- STDMETHODIMP GetCount(UINT32 *pcItems)
- {
- return m_attributes->GetCount(pcItems);
- }
-
- STDMETHODIMP GetItemByIndex(UINT32 unIndex, GUID *pguidKey, PROPVARIANT *pValue)
- {
- return m_attributes->GetItemByIndex(unIndex, pguidKey, pValue);
- }
-
- STDMETHODIMP CopyAllItems(IMFAttributes *pDest)
- {
- return m_attributes->CopyAllItems(pDest);
- }
-
-private:
- IMFAttributes *m_attributes;
- ULONG m_cRef;
-};
-
-#endif // MFACTIVATE_H
diff --git a/src/plugins/wmf/player/mfaudioendpointcontrol.cpp b/src/plugins/wmf/player/mfaudioendpointcontrol.cpp
deleted file mode 100644
index 3b86c52b7..000000000
--- a/src/plugins/wmf/player/mfaudioendpointcontrol.cpp
+++ /dev/null
@@ -1,180 +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:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "QtCore/qdebug.h"
-#include "mfaudioendpointcontrol.h"
-
-#include <mmdeviceapi.h>
-
-MFAudioEndpointControl::MFAudioEndpointControl(QObject *parent)
- : QAudioOutputSelectorControl(parent)
- , m_currentActivate(0)
-{
-}
-
-MFAudioEndpointControl::~MFAudioEndpointControl()
-{
- clear();
-}
-
-void MFAudioEndpointControl::clear()
-{
- m_activeEndpoint.clear();
-
- for (auto it = m_devices.cbegin(), end = m_devices.cend(); it != end; ++it)
- CoTaskMemFree(it.value());
-
- m_devices.clear();
-
- if (m_currentActivate)
- m_currentActivate->Release();
- m_currentActivate = NULL;
-}
-
-QList<QString> MFAudioEndpointControl::availableOutputs() const
-{
- return m_devices.keys();
-}
-
-QString MFAudioEndpointControl::outputDescription(const QString &name) const
-{
- return name.section(QLatin1Char('\\'), -1);
-}
-
-QString MFAudioEndpointControl::defaultOutput() const
-{
- return m_defaultEndpoint;
-}
-
-QString MFAudioEndpointControl::activeOutput() const
-{
- return m_activeEndpoint;
-}
-
-void MFAudioEndpointControl::setActiveOutput(const QString &name)
-{
- if (m_activeEndpoint == name)
- return;
- QMap<QString, LPWSTR>::iterator it = m_devices.find(name);
- if (it == m_devices.end())
- return;
-
- LPWSTR wstrID = *it;
- IMFActivate *activate = NULL;
- HRESULT hr = MFCreateAudioRendererActivate(&activate);
- if (FAILED(hr)) {
- qWarning() << "Failed to create audio renderer activate";
- return;
- }
-
- if (wstrID) {
- hr = activate->SetString(MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ID, wstrID);
- } 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);
- }
-
- if (FAILED(hr)) {
- qWarning() << "Failed to set attribute for audio device" << name;
- return;
- }
-
- if (m_currentActivate)
- m_currentActivate->Release();
- m_currentActivate = activate;
- m_activeEndpoint = name;
-}
-
-IMFActivate* MFAudioEndpointControl::createActivate()
-{
- clear();
-
- updateEndpoints();
-
- // Check if an endpoint is available ("Default" is always inserted)
- if (m_devices.count() <= 1)
- return NULL;
-
- setActiveOutput(m_defaultEndpoint);
-
- return m_currentActivate;
-}
-
-void MFAudioEndpointControl::updateEndpoints()
-{
- m_defaultEndpoint = QString::fromLatin1("Default");
- m_devices.insert(m_defaultEndpoint, NULL);
-
- IMMDeviceEnumerator *pEnum = NULL;
- HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
- NULL, CLSCTX_ALL,
- __uuidof(IMMDeviceEnumerator),
- (void**)&pEnum);
- if (SUCCEEDED(hr)) {
- IMMDeviceCollection *pDevices = NULL;
- hr = pEnum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pDevices);
- if (SUCCEEDED(hr)) {
- UINT count;
- hr = pDevices->GetCount(&count);
- if (SUCCEEDED(hr)) {
- for (UINT i = 0; i < count; ++i) {
- IMMDevice *pDevice = NULL;
- hr = pDevices->Item(i, &pDevice);
- if (SUCCEEDED(hr)) {
- LPWSTR wstrID = NULL;
- hr = pDevice->GetId(&wstrID);
- if (SUCCEEDED(hr)) {
- QString deviceId = QString::fromWCharArray(wstrID);
- m_devices.insert(deviceId, wstrID);
- }
- pDevice->Release();
- }
- }
- }
- pDevices->Release();
- }
- pEnum->Release();
- }
-}
diff --git a/src/plugins/wmf/player/mfaudioendpointcontrol.h b/src/plugins/wmf/player/mfaudioendpointcontrol.h
deleted file mode 100644
index a439c31a5..000000000
--- a/src/plugins/wmf/player/mfaudioendpointcontrol.h
+++ /dev/null
@@ -1,82 +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:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 MFAUDIOENDPOINTCONTROL_H
-#define MFAUDIOENDPOINTCONTROL_H
-
-#include <mfapi.h>
-#include <mfidl.h>
-
-#include "qaudiooutputselectorcontrol.h"
-
-class MFPlayerService;
-
-QT_USE_NAMESPACE
-
-class MFAudioEndpointControl : public QAudioOutputSelectorControl
-{
- Q_OBJECT
-public:
- MFAudioEndpointControl(QObject *parent = 0);
- ~MFAudioEndpointControl();
-
- QList<QString> availableOutputs() const;
-
- QString outputDescription(const QString &name) const;
-
- QString defaultOutput() const;
- QString activeOutput() const;
-
- void setActiveOutput(const QString& name);
-
- IMFActivate* createActivate();
-
-private:
- void clear();
- void updateEndpoints();
-
- QString m_defaultEndpoint;
- QString m_activeEndpoint;
- QMap<QString, LPWSTR> m_devices;
- IMFActivate *m_currentActivate;
-
-};
-
-#endif
-
diff --git a/src/plugins/wmf/player/mfaudioprobecontrol.cpp b/src/plugins/wmf/player/mfaudioprobecontrol.cpp
deleted file mode 100644
index c703922c8..000000000
--- a/src/plugins/wmf/player/mfaudioprobecontrol.cpp
+++ /dev/null
@@ -1,75 +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 "mfaudioprobecontrol.h"
-
-MFAudioProbeControl::MFAudioProbeControl(QObject *parent):
- QMediaAudioProbeControl(parent)
-{
-}
-
-MFAudioProbeControl::~MFAudioProbeControl()
-{
-}
-
-void MFAudioProbeControl::bufferProbed(const char *data, quint32 size, const QAudioFormat& format, qint64 startTime)
-{
- if (!format.isValid())
- return;
-
- QAudioBuffer audioBuffer = QAudioBuffer(QByteArray(data, size), format, startTime);
-
- {
- QMutexLocker locker(&m_bufferMutex);
- m_pendingBuffer = audioBuffer;
- QMetaObject::invokeMethod(this, "bufferProbed", Qt::QueuedConnection);
- }
-}
-
-void MFAudioProbeControl::bufferProbed()
-{
- QAudioBuffer audioBuffer;
- {
- QMutexLocker locker(&m_bufferMutex);
- if (!m_pendingBuffer.isValid())
- return;
- audioBuffer = m_pendingBuffer;
- }
- emit audioBufferProbed(audioBuffer);
-}
diff --git a/src/plugins/wmf/player/mfaudioprobecontrol.h b/src/plugins/wmf/player/mfaudioprobecontrol.h
deleted file mode 100644
index c8a06148f..000000000
--- a/src/plugins/wmf/player/mfaudioprobecontrol.h
+++ /dev/null
@@ -1,66 +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 MFAUDIOPROBECONTROL_H
-#define MFAUDIOPROBECONTROL_H
-
-#include <qmediaaudioprobecontrol.h>
-#include <QtCore/qmutex.h>
-#include <qaudiobuffer.h>
-
-QT_USE_NAMESPACE
-
-class MFAudioProbeControl : public QMediaAudioProbeControl
-{
- Q_OBJECT
-public:
- explicit MFAudioProbeControl(QObject *parent);
- virtual ~MFAudioProbeControl();
-
- void bufferProbed(const char *data, quint32 size, const QAudioFormat& format, qint64 startTime);
-
-private slots:
- void bufferProbed();
-
-private:
- QAudioBuffer m_pendingBuffer;
- QMutex m_bufferMutex;
-};
-
-#endif
diff --git a/src/plugins/wmf/player/mfevrvideowindowcontrol.cpp b/src/plugins/wmf/player/mfevrvideowindowcontrol.cpp
deleted file mode 100644
index 4b3e0f303..000000000
--- a/src/plugins/wmf/player/mfevrvideowindowcontrol.cpp
+++ /dev/null
@@ -1,91 +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 "mfevrvideowindowcontrol.h"
-
-#include <qdebug.h>
-
-MFEvrVideoWindowControl::MFEvrVideoWindowControl(QObject *parent)
- : EvrVideoWindowControl(parent)
- , m_currentActivate(NULL)
- , m_evrSink(NULL)
-{
-}
-
-MFEvrVideoWindowControl::~MFEvrVideoWindowControl()
-{
- clear();
-}
-
-void MFEvrVideoWindowControl::clear()
-{
- setEvr(NULL);
-
- if (m_evrSink)
- m_evrSink->Release();
- if (m_currentActivate) {
- m_currentActivate->ShutdownObject();
- m_currentActivate->Release();
- }
- m_evrSink = NULL;
- m_currentActivate = NULL;
-}
-
-IMFActivate* MFEvrVideoWindowControl::createActivate()
-{
- clear();
-
- if (FAILED(MFCreateVideoRendererActivate(0, &m_currentActivate))) {
- qWarning() << "Failed to create evr video renderer activate!";
- return NULL;
- }
- if (FAILED(m_currentActivate->ActivateObject(IID_IMFMediaSink, (LPVOID*)(&m_evrSink)))) {
- qWarning() << "Failed to activate evr media sink!";
- return NULL;
- }
- if (!setEvr(m_evrSink))
- return NULL;
-
- return m_currentActivate;
-}
-
-void MFEvrVideoWindowControl::releaseActivate()
-{
- clear();
-}
diff --git a/src/plugins/wmf/player/mfevrvideowindowcontrol.h b/src/plugins/wmf/player/mfevrvideowindowcontrol.h
deleted file mode 100644
index 96634e6d8..000000000
--- a/src/plugins/wmf/player/mfevrvideowindowcontrol.h
+++ /dev/null
@@ -1,63 +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 MFEVRVIDEOWINDOWCONTROL_H
-#define MFEVRVIDEOWINDOWCONTROL_H
-
-#include "evrvideowindowcontrol.h"
-
-QT_USE_NAMESPACE
-
-class MFEvrVideoWindowControl : public EvrVideoWindowControl
-{
-public:
- MFEvrVideoWindowControl(QObject *parent = 0);
- ~MFEvrVideoWindowControl();
-
- IMFActivate* createActivate();
- void releaseActivate();
-
-private:
- void clear();
-
- IMFActivate *m_currentActivate;
- IMFMediaSink *m_evrSink;
-};
-
-#endif // MFEVRVIDEOWINDOWCONTROL_H
diff --git a/src/plugins/wmf/player/mfmetadatacontrol.cpp b/src/plugins/wmf/player/mfmetadatacontrol.cpp
deleted file mode 100644
index 74063f7d1..000000000
--- a/src/plugins/wmf/player/mfmetadatacontrol.cpp
+++ /dev/null
@@ -1,431 +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:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <qmediametadata.h>
-#include <qdatetime.h>
-#include <qimage.h>
-
-#include "mfmetadatacontrol.h"
-#include "mfplayerservice.h"
-#include "Propkey.h"
-
-//#define DEBUG_MEDIAFOUNDATION
-
-static QString nameForGUID(GUID guid)
-{
- // Audio formats
- if (guid == MFAudioFormat_AAC)
- return QStringLiteral("MPEG AAC Audio");
- else if (guid == MFAudioFormat_ADTS)
- return QStringLiteral("MPEG ADTS AAC Audio");
- else if (guid == MFAudioFormat_Dolby_AC3_SPDIF)
- return QStringLiteral("Dolby AC-3 SPDIF");
- else if (guid == MFAudioFormat_DRM)
- return QStringLiteral("DRM");
- else if (guid == MFAudioFormat_DTS)
- return QStringLiteral("Digital Theater Systems Audio (DTS)");
- else if (guid == MFAudioFormat_Float)
- return QStringLiteral("IEEE Float Audio");
- else if (guid == MFAudioFormat_MP3)
- return QStringLiteral("MPEG Audio Layer-3 (MP3)");
- else if (guid == MFAudioFormat_MPEG)
- return QStringLiteral("MPEG-1 Audio");
- else if (guid == MFAudioFormat_MSP1)
- return QStringLiteral("Windows Media Audio Voice");
- else if (guid == MFAudioFormat_PCM)
- return QStringLiteral("Uncompressed PCM Audio");
- else if (guid == MFAudioFormat_WMASPDIF)
- return QStringLiteral("Windows Media Audio 9 SPDIF");
- else if (guid == MFAudioFormat_WMAudioV8)
- return QStringLiteral("Windows Media Audio 8 (WMA2)");
- else if (guid == MFAudioFormat_WMAudioV9)
- return QStringLiteral("Windows Media Audio 9 (WMA3");
- else if (guid == MFAudioFormat_WMAudio_Lossless)
- return QStringLiteral("Windows Media Audio 9 Lossless");
-
- // Video formats
- if (guid == MFVideoFormat_DV25)
- return QStringLiteral("DVCPRO 25 (DV25)");
- else if (guid == MFVideoFormat_DV50)
- return QStringLiteral("DVCPRO 50 (DV50)");
- else if (guid == MFVideoFormat_DVC)
- return QStringLiteral("DVC/DV Video");
- else if (guid == MFVideoFormat_DVH1)
- return QStringLiteral("DVCPRO 100 (DVH1)");
- else if (guid == MFVideoFormat_DVHD)
- return QStringLiteral("HD-DVCR (DVHD)");
- else if (guid == MFVideoFormat_DVSD)
- return QStringLiteral("SDL-DVCR (DVSD)");
- else if (guid == MFVideoFormat_DVSL)
- return QStringLiteral("SD-DVCR (DVSL)");
- else if (guid == MFVideoFormat_H264)
- return QStringLiteral("H.264 Video");
- else if (guid == MFVideoFormat_M4S2)
- return QStringLiteral("MPEG-4 part 2 Video (M4S2)");
- else if (guid == MFVideoFormat_MJPG)
- return QStringLiteral("Motion JPEG (MJPG)");
- else if (guid == MFVideoFormat_MP43)
- return QStringLiteral("Microsoft MPEG 4 version 3 (MP43)");
- else if (guid == MFVideoFormat_MP4S)
- return QStringLiteral("ISO MPEG 4 version 1 (MP4S)");
- else if (guid == MFVideoFormat_MP4V)
- return QStringLiteral("MPEG-4 part 2 Video (MP4V)");
- else if (guid == MFVideoFormat_MPEG2)
- return QStringLiteral("MPEG-2 Video");
- else if (guid == MFVideoFormat_MPG1)
- return QStringLiteral("MPEG-1 Video");
- else if (guid == MFVideoFormat_MSS1)
- return QStringLiteral("Windows Media Screen 1 (MSS1)");
- else if (guid == MFVideoFormat_MSS2)
- return QStringLiteral("Windows Media Video 9 Screen (MSS2)");
- else if (guid == MFVideoFormat_WMV1)
- return QStringLiteral("Windows Media Video 7 (WMV1)");
- else if (guid == MFVideoFormat_WMV2)
- return QStringLiteral("Windows Media Video 8 (WMV2)");
- else if (guid == MFVideoFormat_WMV3)
- return QStringLiteral("Windows Media Video 9 (WMV3)");
- else if (guid == MFVideoFormat_WVC1)
- return QStringLiteral("Windows Media Video VC1 (WVC1)");
-
- else
- return QStringLiteral("Unknown codec");
-}
-
-MFMetaDataControl::MFMetaDataControl(QObject *parent)
- : QMetaDataReaderControl(parent)
- , m_metaData(0)
- , m_content(0)
-{
-}
-
-MFMetaDataControl::~MFMetaDataControl()
-{
- if (m_metaData)
- m_metaData->Release();
- if (m_content)
- m_content->Release();
-}
-
-bool MFMetaDataControl::isMetaDataAvailable() const
-{
- return m_content || m_metaData;
-}
-
-QVariant MFMetaDataControl::metaData(const QString &key) const
-{
- QVariant value;
- if (!isMetaDataAvailable())
- return value;
-
- int index = m_availableMetaDatas.indexOf(key);
- if (index < 0)
- return value;
-
- PROPVARIANT var;
- PropVariantInit(&var);
- HRESULT hr = S_FALSE;
- if (m_content)
- hr = m_content->GetValue(m_commonKeys[index], &var);
- else if (m_metaData)
- hr = m_metaData->GetProperty(reinterpret_cast<LPCWSTR>(m_commonNames[index].utf16()), &var);
-
- if (SUCCEEDED(hr)) {
- value = convertValue(var);
-
- // some metadata needs to be reformatted
- if (value.isValid() && m_content) {
- if (key == QMediaMetaData::MediaType) {
- QString v = value.toString();
- if (v == QLatin1String("{D1607DBC-E323-4BE2-86A1-48A42A28441E}"))
- value = QStringLiteral("Music");
- else if (v == QLatin1String("{DB9830BD-3AB3-4FAB-8A37-1A995F7FF74B}"))
- value = QStringLiteral("Video");
- else if (v == QLatin1String("{01CD0F29-DA4E-4157-897B-6275D50C4F11}"))
- value = QStringLiteral("Audio");
- else if (v == QLatin1String("{FCF24A76-9A57-4036-990D-E35DD8B244E1}"))
- value = QStringLiteral("Other");
- } else if (key == QMediaMetaData::Duration) {
- // duration is provided in 100-nanosecond units, convert to milliseconds
- value = (value.toLongLong() + 10000) / 10000;
- } else if (key == QMediaMetaData::AudioCodec || key == QMediaMetaData::VideoCodec) {
- GUID guid;
- if (SUCCEEDED(CLSIDFromString((const WCHAR*)value.toString().utf16(), &guid)))
- value = nameForGUID(guid);
- } else if (key == QMediaMetaData::Resolution) {
- QSize res;
- res.setHeight(value.toUInt());
- if (m_content && SUCCEEDED(m_content->GetValue(PKEY_Video_FrameWidth, &var)))
- res.setWidth(convertValue(var).toUInt());
- value = res;
- } else if (key == QMediaMetaData::Orientation) {
- uint orientation = 0;
- if (m_content && SUCCEEDED(m_content->GetValue(PKEY_Video_Orientation, &var)))
- orientation = convertValue(var).toUInt();
- value = orientation;
- } else if (key == QMediaMetaData::PixelAspectRatio) {
- QSize aspectRatio;
- aspectRatio.setWidth(value.toUInt());
- if (m_content && SUCCEEDED(m_content->GetValue(PKEY_Video_VerticalAspectRatio, &var)))
- aspectRatio.setHeight(convertValue(var).toUInt());
- value = aspectRatio;
- } else if (key == QMediaMetaData::VideoFrameRate) {
- value = value.toReal() / 1000.f;
- }
- }
- }
-
- PropVariantClear(&var);
- return value;
-}
-
-QVariant MFMetaDataControl::convertValue(const PROPVARIANT& var) const
-{
- QVariant value;
- switch (var.vt) {
- case VT_LPWSTR:
- value = QString::fromUtf16(reinterpret_cast<const ushort*>(var.pwszVal));
- break;
- case VT_UI4:
- value = uint(var.ulVal);
- break;
- case VT_UI8:
- value = qulonglong(var.uhVal.QuadPart);
- break;
- case VT_BOOL:
- value = bool(var.boolVal);
- break;
- case VT_FILETIME:
- SYSTEMTIME sysDate;
- if (!FileTimeToSystemTime(&var.filetime, &sysDate))
- break;
- value = QDate(sysDate.wYear, sysDate.wMonth, sysDate.wDay);
- break;
- case VT_STREAM:
- {
- STATSTG stat;
- if (FAILED(var.pStream->Stat(&stat, STATFLAG_NONAME)))
- break;
- void *data = malloc(stat.cbSize.QuadPart);
- ULONG read = 0;
- if (FAILED(var.pStream->Read(data, stat.cbSize.QuadPart, &read))) {
- free(data);
- break;
- }
- value = QImage::fromData((const uchar*)data, read);
- free(data);
- }
- break;
- case VT_VECTOR | VT_LPWSTR:
- QStringList vList;
- for (ULONG i = 0; i < var.calpwstr.cElems; ++i)
- vList.append(QString::fromUtf16(reinterpret_cast<const ushort*>(var.calpwstr.pElems[i])));
- value = vList;
- break;
- }
- return value;
-}
-
-QStringList MFMetaDataControl::availableMetaData() const
-{
- return m_availableMetaDatas;
-}
-
-void MFMetaDataControl::updateSource(IMFPresentationDescriptor* sourcePD, IMFMediaSource* mediaSource)
-{
- if (m_metaData) {
- m_metaData->Release();
- m_metaData = 0;
- }
-
- if (m_content) {
- m_content->Release();
- m_content = 0;
- }
-
- m_availableMetaDatas.clear();
- m_commonKeys.clear();
- m_commonNames.clear();
-
- if (SUCCEEDED(MFGetService(mediaSource, MF_PROPERTY_HANDLER_SERVICE, IID_PPV_ARGS(&m_content)))) {
- DWORD cProps;
- if (SUCCEEDED(m_content->GetCount(&cProps))) {
- for (DWORD i = 0; i < cProps; i++)
- {
- PROPERTYKEY key;
- if (FAILED(m_content->GetAt(i, &key)))
- continue;
- bool common = true;
- if (key == PKEY_Author) {
- m_availableMetaDatas.push_back(QMediaMetaData::Author);
- } else if (key == PKEY_Title) {
- m_availableMetaDatas.push_back(QMediaMetaData::Title);
- } else if (key == PKEY_Media_SubTitle) {
- m_availableMetaDatas.push_back(QMediaMetaData::SubTitle);
- } else if (key == PKEY_ParentalRating) {
- m_availableMetaDatas.push_back(QMediaMetaData::ParentalRating);
- } else if (key == PKEY_Media_EncodingSettings) {
- m_availableMetaDatas.push_back(QMediaMetaData::Description);
- } else if (key == PKEY_Copyright) {
- m_availableMetaDatas.push_back(QMediaMetaData::Copyright);
- } else if (key == PKEY_Comment) {
- m_availableMetaDatas.push_back(QMediaMetaData::Comment);
- } else if (key == PKEY_Media_ProviderStyle) {
- m_availableMetaDatas.push_back(QMediaMetaData::Genre);
- } else if (key == PKEY_Media_Year) {
- m_availableMetaDatas.push_back(QMediaMetaData::Year);
- } else if (key == PKEY_Media_DateEncoded) {
- m_availableMetaDatas.push_back(QMediaMetaData::Date);
- } else if (key == PKEY_Rating) {
- m_availableMetaDatas.push_back(QMediaMetaData::UserRating);
- } else if (key == PKEY_Keywords) {
- m_availableMetaDatas.push_back(QMediaMetaData::Keywords);
- } else if (key == PKEY_Language) {
- m_availableMetaDatas.push_back(QMediaMetaData::Language);
- } else if (key == PKEY_Media_Publisher) {
- m_availableMetaDatas.push_back(QMediaMetaData::Publisher);
- } else if (key == PKEY_Media_ClassPrimaryID) {
- m_availableMetaDatas.push_back(QMediaMetaData::MediaType);
- } else if (key == PKEY_Media_Duration) {
- m_availableMetaDatas.push_back(QMediaMetaData::Duration);
- } else if (key == PKEY_Audio_EncodingBitrate) {
- m_availableMetaDatas.push_back(QMediaMetaData::AudioBitRate);
- } else if (key == PKEY_Audio_Format) {
- m_availableMetaDatas.push_back(QMediaMetaData::AudioCodec);
- } else if (key == PKEY_Media_AverageLevel) {
- m_availableMetaDatas.push_back(QMediaMetaData::AverageLevel);
- } else if (key == PKEY_Audio_ChannelCount) {
- m_availableMetaDatas.push_back(QMediaMetaData::ChannelCount);
- } else if (key == PKEY_Audio_PeakValue) {
- m_availableMetaDatas.push_back(QMediaMetaData::PeakValue);
- } else if (key == PKEY_Audio_SampleRate) {
- m_availableMetaDatas.push_back(QMediaMetaData::SampleRate);
- } else if (key == PKEY_Music_AlbumTitle) {
- m_availableMetaDatas.push_back(QMediaMetaData::AlbumTitle);
- } else if (key == PKEY_Music_AlbumArtist) {
- m_availableMetaDatas.push_back(QMediaMetaData::AlbumArtist);
- } else if (key == PKEY_Music_Artist) {
- m_availableMetaDatas.push_back(QMediaMetaData::ContributingArtist);
- } else if (key == PKEY_Music_Composer) {
- m_availableMetaDatas.push_back(QMediaMetaData::Composer);
- } else if (key == PKEY_Music_Conductor) {
- m_availableMetaDatas.push_back(QMediaMetaData::Conductor);
- } else if (key == PKEY_Music_Lyrics) {
- m_availableMetaDatas.push_back(QMediaMetaData::Lyrics);
- } else if (key == PKEY_Music_Mood) {
- m_availableMetaDatas.push_back(QMediaMetaData::Mood);
- } else if (key == PKEY_Music_TrackNumber) {
- m_availableMetaDatas.push_back(QMediaMetaData::TrackNumber);
- } else if (key == PKEY_Music_Genre) {
- m_availableMetaDatas.push_back(QMediaMetaData::Genre);
- } else if (key == PKEY_ThumbnailStream) {
- m_availableMetaDatas.push_back(QMediaMetaData::ThumbnailImage);
- } else if (key == PKEY_Video_FrameHeight) {
- m_availableMetaDatas.push_back(QMediaMetaData::Resolution);
- } else if (key == PKEY_Video_Orientation) {
- m_availableMetaDatas.push_back(QMediaMetaData::Orientation);
- } else if (key == PKEY_Video_HorizontalAspectRatio) {
- m_availableMetaDatas.push_back(QMediaMetaData::PixelAspectRatio);
- } else if (key == PKEY_Video_FrameRate) {
- m_availableMetaDatas.push_back(QMediaMetaData::VideoFrameRate);
- } else if (key == PKEY_Video_EncodingBitrate) {
- m_availableMetaDatas.push_back(QMediaMetaData::VideoBitRate);
- } else if (key == PKEY_Video_Compression) {
- m_availableMetaDatas.push_back(QMediaMetaData::VideoCodec);
- } else if (key == PKEY_Video_Director) {
- m_availableMetaDatas.push_back(QMediaMetaData::Director);
- } else if (key == PKEY_Media_Writer) {
- m_availableMetaDatas.push_back(QMediaMetaData::Writer);
- } else {
- common = false;
- //TODO: add more extended keys
- }
- if (common)
- m_commonKeys.push_back(key);
- }
- } else {
- m_content->Release();
- m_content = NULL;
- }
- }
-
- if (!m_content) {
- //fallback to Vista approach
- IMFMetadataProvider *provider = NULL;
- if (SUCCEEDED(MFGetService(mediaSource, MF_METADATA_PROVIDER_SERVICE, IID_PPV_ARGS(&provider)))) {
- if (SUCCEEDED(provider->GetMFMetadata(sourcePD, 0, 0, &m_metaData))) {
- PROPVARIANT varNames;
- PropVariantInit(&varNames);
- if (SUCCEEDED(m_metaData->GetAllPropertyNames(&varNames)) && varNames.vt == (VT_VECTOR | VT_LPWSTR)) {
- ULONG cElements = varNames.calpwstr.cElems;
- for (ULONG i = 0; i < cElements; i++)
- {
- const WCHAR* sName = varNames.calpwstr.pElems[i];
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "metadata: " << QString::fromUtf16(sName);
-#endif
- if (wcscmp(sName, L"Author") == 0) {
- m_availableMetaDatas.push_back(QMediaMetaData::Author);
- } else if (wcscmp(sName, L"Title") == 0) {
- m_availableMetaDatas.push_back(QMediaMetaData::Title);
- } else if (wcscmp(sName, L"Rating") == 0) {
- m_availableMetaDatas.push_back(QMediaMetaData::ParentalRating);
- } else if (wcscmp(sName, L"Description") == 0) {
- m_availableMetaDatas.push_back(QMediaMetaData::Description);
- } else if (wcscmp(sName, L"Copyright") == 0) {
- m_availableMetaDatas.push_back(QMediaMetaData::Copyright);
- //TODO: add more common keys
- } else {
- m_availableMetaDatas.push_back(QString::fromUtf16(reinterpret_cast<const ushort*>(sName)));
- }
- m_commonNames.push_back(QString::fromUtf16(reinterpret_cast<const ushort*>(sName)));
- }
- }
- PropVariantClear(&varNames);
- } else {
- qWarning("Failed to get IMFMetadata");
- }
- provider->Release();
- } else {
- qWarning("Failed to get IMFMetadataProvider from source");
- }
- }
-
- emit metaDataChanged();
- emit metaDataAvailableChanged(m_metaData || m_content);
-}
diff --git a/src/plugins/wmf/player/mfmetadatacontrol.h b/src/plugins/wmf/player/mfmetadatacontrol.h
deleted file mode 100644
index 7ae06cedb..000000000
--- a/src/plugins/wmf/player/mfmetadatacontrol.h
+++ /dev/null
@@ -1,72 +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:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 MFMETADATACONTROL_H
-#define MFMETADATACONTROL_H
-
-#include <qmetadatareadercontrol.h>
-#include "Mfidl.h"
-
-QT_USE_NAMESPACE
-
-class MFMetaDataControl : public QMetaDataReaderControl
-{
- Q_OBJECT
-public:
- MFMetaDataControl(QObject *parent = 0);
- ~MFMetaDataControl();
-
- bool isMetaDataAvailable() const;
-
- QVariant metaData(const QString &key) const;
- QStringList availableMetaData() const;
-
- void updateSource(IMFPresentationDescriptor* sourcePD, IMFMediaSource* mediaSource);
-
-private:
- QVariant convertValue(const PROPVARIANT& var) const;
- IPropertyStore *m_content; //for Windows7
- IMFMetadata *m_metaData; //for Vista
-
- QStringList m_availableMetaDatas;
- QList<PROPERTYKEY> m_commonKeys; //for Windows7
- QStringList m_commonNames; //for Vista
-};
-
-#endif
diff --git a/src/plugins/wmf/player/mfplayercontrol.cpp b/src/plugins/wmf/player/mfplayercontrol.cpp
deleted file mode 100644
index b070ce482..000000000
--- a/src/plugins/wmf/player/mfplayercontrol.cpp
+++ /dev/null
@@ -1,316 +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:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "mfplayercontrol.h"
-#include <qtcore/qdebug.h>
-
-//#define DEBUG_MEDIAFOUNDATION
-
-MFPlayerControl::MFPlayerControl(MFPlayerSession *session)
-: QMediaPlayerControl(session)
-, m_state(QMediaPlayer::StoppedState)
-, m_stateDirty(false)
-, m_videoAvailable(false)
-, m_audioAvailable(false)
-, m_duration(-1)
-, m_seekable(false)
-, m_session(session)
-{
- QObject::connect(m_session, SIGNAL(statusChanged()), this, SLOT(handleStatusChanged()));
- QObject::connect(m_session, SIGNAL(videoAvailable()), this, SLOT(handleVideoAvailable()));
- QObject::connect(m_session, SIGNAL(audioAvailable()), this, SLOT(handleAudioAvailable()));
- QObject::connect(m_session, SIGNAL(durationUpdate(qint64)), this, SLOT(handleDurationUpdate(qint64)));
- QObject::connect(m_session, SIGNAL(seekableUpdate(bool)), this, SLOT(handleSeekableUpdate(bool)));
- QObject::connect(m_session, SIGNAL(error(QMediaPlayer::Error,QString,bool)), this, SLOT(handleError(QMediaPlayer::Error,QString,bool)));
- QObject::connect(m_session, SIGNAL(positionChanged(qint64)), this, SIGNAL(positionChanged(qint64)));
- QObject::connect(m_session, SIGNAL(volumeChanged(int)), this, SIGNAL(volumeChanged(int)));
- QObject::connect(m_session, SIGNAL(mutedChanged(bool)), this, SIGNAL(mutedChanged(bool)));
- QObject::connect(m_session, SIGNAL(playbackRateChanged(qreal)), this, SIGNAL(playbackRateChanged(qreal)));
- QObject::connect(m_session, SIGNAL(bufferStatusChanged(int)), this, SIGNAL(bufferStatusChanged(int)));
-}
-
-MFPlayerControl::~MFPlayerControl()
-{
-}
-
-void MFPlayerControl::setMedia(const QMediaContent &media, QIODevice *stream)
-{
- if (m_state != QMediaPlayer::StoppedState) {
- changeState(QMediaPlayer::StoppedState);
- m_session->stop(true);
- refreshState();
- }
-
- m_media = media;
- m_stream = stream;
- resetAudioVideoAvailable();
- handleDurationUpdate(-1);
- handleSeekableUpdate(false);
- m_session->load(media, stream);
- emit mediaChanged(m_media);
-}
-
-void MFPlayerControl::play()
-{
- if (m_state == QMediaPlayer::PlayingState)
- return;
- if (QMediaPlayer::InvalidMedia == m_session->status())
- m_session->load(m_media, m_stream);
-
- switch (m_session->status()) {
- case QMediaPlayer::UnknownMediaStatus:
- case QMediaPlayer::NoMedia:
- case QMediaPlayer::InvalidMedia:
- return;
- case QMediaPlayer::LoadedMedia:
- case QMediaPlayer::BufferingMedia:
- case QMediaPlayer::BufferedMedia:
- case QMediaPlayer::EndOfMedia:
- changeState(QMediaPlayer::PlayingState);
- m_session->start();
- break;
- default: //Loading/Stalled
- changeState(QMediaPlayer::PlayingState);
- break;
- }
- refreshState();
-}
-
-void MFPlayerControl::pause()
-{
- if (m_state != QMediaPlayer::PlayingState)
- return;
- changeState(QMediaPlayer::PausedState);
- m_session->pause();
- refreshState();
-}
-
-void MFPlayerControl::stop()
-{
- if (m_state == QMediaPlayer::StoppedState)
- return;
- changeState(QMediaPlayer::StoppedState);
- m_session->stop();
- refreshState();
-}
-
-void MFPlayerControl::changeState(QMediaPlayer::State state)
-{
- if (m_state == state)
- return;
- m_state = state;
- m_stateDirty = true;
-}
-
-void MFPlayerControl::refreshState()
-{
- if (!m_stateDirty)
- return;
- m_stateDirty = false;
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "MFPlayerControl::emit stateChanged" << m_state;
-#endif
- emit stateChanged(m_state);
-}
-
-void MFPlayerControl::handleStatusChanged()
-{
- QMediaPlayer::MediaStatus status = m_session->status();
- switch (status) {
- case QMediaPlayer::EndOfMedia:
- changeState(QMediaPlayer::StoppedState);
- break;
- case QMediaPlayer::InvalidMedia:
- break;
- case QMediaPlayer::LoadedMedia:
- case QMediaPlayer::BufferingMedia:
- case QMediaPlayer::BufferedMedia:
- if (m_state == QMediaPlayer::PlayingState)
- m_session->start();
- break;
- }
- emit mediaStatusChanged(m_session->status());
- refreshState();
-}
-
-void MFPlayerControl::handleVideoAvailable()
-{
- if (m_videoAvailable)
- return;
- m_videoAvailable = true;
- emit videoAvailableChanged(m_videoAvailable);
-}
-
-void MFPlayerControl::handleAudioAvailable()
-{
- if (m_audioAvailable)
- return;
- m_audioAvailable = true;
- emit audioAvailableChanged(m_audioAvailable);
-}
-
-void MFPlayerControl::resetAudioVideoAvailable()
-{
- bool videoDirty = false;
- if (m_videoAvailable) {
- m_videoAvailable = false;
- videoDirty = true;
- }
- if (m_audioAvailable) {
- m_audioAvailable = false;
- emit audioAvailableChanged(m_audioAvailable);
- }
- if (videoDirty)
- emit videoAvailableChanged(m_videoAvailable);
-}
-
-void MFPlayerControl::handleDurationUpdate(qint64 duration)
-{
- if (m_duration == duration)
- return;
- m_duration = duration;
- emit durationChanged(m_duration);
-}
-
-void MFPlayerControl::handleSeekableUpdate(bool seekable)
-{
- if (m_seekable == seekable)
- return;
- m_seekable = seekable;
- emit seekableChanged(m_seekable);
-}
-
-QMediaPlayer::State MFPlayerControl::state() const
-{
- return m_state;
-}
-
-QMediaPlayer::MediaStatus MFPlayerControl::mediaStatus() const
-{
- return m_session->status();
-}
-
-qint64 MFPlayerControl::duration() const
-{
- return m_duration;
-}
-
-qint64 MFPlayerControl::position() const
-{
- return m_session->position();
-}
-
-void MFPlayerControl::setPosition(qint64 position)
-{
- if (!m_seekable || position == m_session->position())
- return;
- m_session->setPosition(position);
-}
-
-int MFPlayerControl::volume() const
-{
- return m_session->volume();
-}
-
-void MFPlayerControl::setVolume(int volume)
-{
- m_session->setVolume(volume);
-}
-
-bool MFPlayerControl::isMuted() const
-{
- return m_session->isMuted();
-}
-
-void MFPlayerControl::setMuted(bool muted)
-{
- m_session->setMuted(muted);
-}
-
-int MFPlayerControl::bufferStatus() const
-{
- return m_session->bufferStatus();
-}
-
-bool MFPlayerControl::isAudioAvailable() const
-{
- return m_audioAvailable;
-}
-
-bool MFPlayerControl::isVideoAvailable() const
-{
- return m_videoAvailable;
-}
-
-bool MFPlayerControl::isSeekable() const
-{
- return m_seekable;
-}
-
-QMediaTimeRange MFPlayerControl::availablePlaybackRanges() const
-{
- return m_session->availablePlaybackRanges();
-}
-
-qreal MFPlayerControl::playbackRate() const
-{
- return m_session->playbackRate();
-}
-
-void MFPlayerControl::setPlaybackRate(qreal rate)
-{
- m_session->setPlaybackRate(rate);
-}
-
-QMediaContent MFPlayerControl::media() const
-{
- return m_media;
-}
-
-const QIODevice* MFPlayerControl::mediaStream() const
-{
- return m_stream;
-}
-
-void MFPlayerControl::handleError(QMediaPlayer::Error errorCode, const QString& errorString, bool isFatal)
-{
- if (isFatal)
- stop();
- emit error(int(errorCode), errorString);
-}
diff --git a/src/plugins/wmf/player/mfplayercontrol.h b/src/plugins/wmf/player/mfplayercontrol.h
deleted file mode 100644
index a75fac493..000000000
--- a/src/plugins/wmf/player/mfplayercontrol.h
+++ /dev/null
@@ -1,122 +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:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 MFPLAYERCONTROL_H
-#define MFPLAYERCONTROL_H
-
-#include "qmediacontent.h"
-#include "qmediaplayercontrol.h"
-
-#include <QtCore/qcoreevent.h>
-
-#include "mfplayersession.h"
-
-QT_USE_NAMESPACE
-
-class MFPlayerControl : public QMediaPlayerControl
-{
- Q_OBJECT
-public:
- MFPlayerControl(MFPlayerSession *session);
- ~MFPlayerControl();
-
- QMediaPlayer::State state() const;
-
- QMediaPlayer::MediaStatus mediaStatus() const;
-
- qint64 duration() const;
-
- qint64 position() const;
- void setPosition(qint64 position);
-
- int volume() const;
- void setVolume(int volume);
-
- bool isMuted() const;
- void setMuted(bool muted);
-
- int bufferStatus() const;
-
- bool isAudioAvailable() const;
- bool isVideoAvailable() const;
-
- bool isSeekable() const;
-
- QMediaTimeRange availablePlaybackRanges() const;
-
- qreal playbackRate() const;
- void setPlaybackRate(qreal rate);
-
- QMediaContent media() const;
- const QIODevice *mediaStream() const;
- void setMedia(const QMediaContent &media, QIODevice *stream);
-
- void play();
- void pause();
- void stop();
-
-private Q_SLOTS:
- void handleStatusChanged();
- void handleVideoAvailable();
- void handleAudioAvailable();
- void handleDurationUpdate(qint64 duration);
- void handleSeekableUpdate(bool seekable);
- void handleError(QMediaPlayer::Error errorCode, const QString& errorString, bool isFatal);
-
-private:
- void changeState(QMediaPlayer::State state);
- void resetAudioVideoAvailable();
- void refreshState();
-
- QMediaPlayer::State m_state;
- bool m_stateDirty;
- QMediaPlayer::MediaStatus m_status;
- QMediaPlayer::Error m_error;
-
- bool m_videoAvailable;
- bool m_audioAvailable;
- qint64 m_duration;
- bool m_seekable;
-
- QIODevice *m_stream;
- QMediaContent m_media;
- MFPlayerSession *m_session;
-};
-
-#endif
diff --git a/src/plugins/wmf/player/mfplayerservice.cpp b/src/plugins/wmf/player/mfplayerservice.cpp
deleted file mode 100644
index b73390ac7..000000000
--- a/src/plugins/wmf/player/mfplayerservice.cpp
+++ /dev/null
@@ -1,167 +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:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "qmediacontent.h"
-
-#include <QtCore/qdebug.h>
-
-#include "mfplayercontrol.h"
-#include "mfevrvideowindowcontrol.h"
-#include "mfvideorenderercontrol.h"
-#include "mfaudioendpointcontrol.h"
-#include "mfaudioprobecontrol.h"
-#include "mfvideoprobecontrol.h"
-#include "mfplayerservice.h"
-#include "mfplayersession.h"
-#include "mfmetadatacontrol.h"
-
-MFPlayerService::MFPlayerService(QObject *parent)
- : QMediaService(parent)
- , m_session(0)
- , m_videoWindowControl(0)
- , m_videoRendererControl(0)
-{
- m_audioEndpointControl = new MFAudioEndpointControl(this);
- m_session = new MFPlayerSession(this);
- m_player = new MFPlayerControl(m_session);
- m_metaDataControl = new MFMetaDataControl(this);
-}
-
-MFPlayerService::~MFPlayerService()
-{
- m_session->close();
-
- if (m_videoWindowControl)
- delete m_videoWindowControl;
-
- if (m_videoRendererControl)
- delete m_videoRendererControl;
-
- m_session->Release();
-}
-
-QMediaControl* MFPlayerService::requestControl(const char *name)
-{
- if (qstrcmp(name, QMediaPlayerControl_iid) == 0) {
- return m_player;
- } else if (qstrcmp(name, QAudioOutputSelectorControl_iid) == 0) {
- return m_audioEndpointControl;
- } else if (qstrcmp(name, QMetaDataReaderControl_iid) == 0) {
- return m_metaDataControl;
- } else if (qstrcmp(name, QVideoRendererControl_iid) == 0) {
- if (!m_videoRendererControl && !m_videoWindowControl) {
- m_videoRendererControl = new MFVideoRendererControl;
- return m_videoRendererControl;
- }
- } else if (qstrcmp(name, QVideoWindowControl_iid) == 0) {
- if (!m_videoRendererControl && !m_videoWindowControl) {
- m_videoWindowControl = new MFEvrVideoWindowControl;
- return m_videoWindowControl;
- }
- } else if (qstrcmp(name,QMediaAudioProbeControl_iid) == 0) {
- if (m_session) {
- MFAudioProbeControl *probe = new MFAudioProbeControl(this);
- m_session->addProbe(probe);
- return probe;
- }
- return 0;
- } else if (qstrcmp(name,QMediaVideoProbeControl_iid) == 0) {
- if (m_session) {
- MFVideoProbeControl *probe = new MFVideoProbeControl(this);
- m_session->addProbe(probe);
- return probe;
- }
- return 0;
- }
-
- return 0;
-}
-
-void MFPlayerService::releaseControl(QMediaControl *control)
-{
- if (!control) {
- qWarning("QMediaService::releaseControl():"
- " Attempted release of null control");
- } else if (control == m_videoRendererControl) {
- m_videoRendererControl->setSurface(0);
- delete m_videoRendererControl;
- m_videoRendererControl = 0;
- return;
- } else if (control == m_videoWindowControl) {
- delete m_videoWindowControl;
- m_videoWindowControl = 0;
- return;
- }
-
- MFAudioProbeControl* audioProbe = qobject_cast<MFAudioProbeControl*>(control);
- if (audioProbe) {
- if (m_session)
- m_session->removeProbe(audioProbe);
- delete audioProbe;
- return;
- }
-
- MFVideoProbeControl* videoProbe = qobject_cast<MFVideoProbeControl*>(control);
- if (videoProbe) {
- if (m_session)
- m_session->removeProbe(videoProbe);
- delete videoProbe;
- return;
- }
-}
-
-MFAudioEndpointControl* MFPlayerService::audioEndpointControl() const
-{
- return m_audioEndpointControl;
-}
-
-MFVideoRendererControl* MFPlayerService::videoRendererControl() const
-{
- return m_videoRendererControl;
-}
-
-MFEvrVideoWindowControl* MFPlayerService::videoWindowControl() const
-{
- return m_videoWindowControl;
-}
-
-MFMetaDataControl* MFPlayerService::metaDataControl() const
-{
- return m_metaDataControl;
-}
diff --git a/src/plugins/wmf/player/mfplayerservice.h b/src/plugins/wmf/player/mfplayerservice.h
deleted file mode 100644
index 78423c0b1..000000000
--- a/src/plugins/wmf/player/mfplayerservice.h
+++ /dev/null
@@ -1,87 +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:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 MFPLAYERSERVICE_H
-#define MFPLAYERSERVICE_H
-
-#include <mfapi.h>
-#include <mfidl.h>
-
-#include "qmediaplayer.h"
-#include "qmediaservice.h"
-#include "qmediatimerange.h"
-
-QT_BEGIN_NAMESPACE
-class QMediaContent;
-QT_END_NAMESPACE
-
-QT_USE_NAMESPACE
-
-class MFEvrVideoWindowControl;
-class MFAudioEndpointControl;
-class MFVideoRendererControl;
-class MFPlayerControl;
-class MFMetaDataControl;
-class MFPlayerSession;
-
-class MFPlayerService : public QMediaService
-{
- Q_OBJECT
-public:
- MFPlayerService(QObject *parent = 0);
- ~MFPlayerService();
-
- QMediaControl* requestControl(const char *name);
- void releaseControl(QMediaControl *control);
-
- MFAudioEndpointControl* audioEndpointControl() const;
- MFVideoRendererControl* videoRendererControl() const;
- MFEvrVideoWindowControl* videoWindowControl() const;
- MFMetaDataControl* metaDataControl() const;
-
-private:
- MFPlayerSession *m_session;
- MFVideoRendererControl *m_videoRendererControl;
- MFAudioEndpointControl *m_audioEndpointControl;
- MFEvrVideoWindowControl *m_videoWindowControl;
- MFPlayerControl *m_player;
- MFMetaDataControl *m_metaDataControl;
-};
-
-#endif
diff --git a/src/plugins/wmf/player/mfplayersession.cpp b/src/plugins/wmf/player/mfplayersession.cpp
deleted file mode 100644
index 1d145edc4..000000000
--- a/src/plugins/wmf/player/mfplayersession.cpp
+++ /dev/null
@@ -1,1818 +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:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "qmediacontent.h"
-#include "qmediaplayercontrol.h"
-
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qdatetime.h>
-#include <QtCore/qthread.h>
-#include <QtCore/qvarlengtharray.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qfile.h>
-#include <QtCore/qbuffer.h>
-
-#include "mfplayercontrol.h"
-#include "mfevrvideowindowcontrol.h"
-#include "mfvideorenderercontrol.h"
-#include "mfaudioendpointcontrol.h"
-
-#include "mfplayersession.h"
-#include "mfplayerservice.h"
-#include "mfmetadatacontrol.h"
-#include <mferror.h>
-#include <nserror.h>
-#include "sourceresolver.h"
-#include "samplegrabber.h"
-#include "mftvideo.h"
-#include <wmcodecdsp.h>
-
-//#define DEBUG_MEDIAFOUNDATION
-
-MFPlayerSession::MFPlayerSession(MFPlayerService *playerService)
- : m_playerService(playerService)
- , m_cRef(1)
- , m_session(0)
- , m_presentationClock(0)
- , m_rateControl(0)
- , m_rateSupport(0)
- , m_volumeControl(0)
- , m_netsourceStatistics(0)
- , m_duration(0)
- , m_sourceResolver(0)
- , m_hCloseEvent(0)
- , m_closing(false)
- , m_pendingRate(1)
- , m_volume(100)
- , m_muted(false)
- , m_status(QMediaPlayer::NoMedia)
- , m_scrubbing(false)
- , m_restoreRate(1)
- , m_mediaTypes(0)
- , m_audioSampleGrabber(0)
- , m_audioSampleGrabberNode(0)
- , m_videoProbeMFT(0)
-{
- QObject::connect(this, SIGNAL(sessionEvent(IMFMediaEvent*)), this, SLOT(handleSessionEvent(IMFMediaEvent*)));
-
- m_pendingState = NoPending;
- ZeroMemory(&m_state, sizeof(m_state));
- m_state.command = CmdStop;
- m_state.prevCmd = CmdNone;
- m_state.rate = 1.0f;
- ZeroMemory(&m_request, sizeof(m_request));
- m_request.command = CmdNone;
- m_request.prevCmd = CmdNone;
- m_request.rate = 1.0f;
-
- m_audioSampleGrabber = new AudioSampleGrabberCallback;
-}
-
-void MFPlayerSession::close()
-{
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "close";
-#endif
-
- clear();
- if (!m_session)
- return;
-
- HRESULT hr = S_OK;
- if (m_session) {
- m_closing = true;
- hr = m_session->Close();
- if (SUCCEEDED(hr)) {
- DWORD dwWaitResult = WaitForSingleObject(m_hCloseEvent, 100);
- if (dwWaitResult == WAIT_TIMEOUT) {
- qWarning() << "session close time out!";
- }
- }
- m_closing = false;
- }
-
- if (SUCCEEDED(hr)) {
- if (m_session)
- m_session->Shutdown();
- if (m_sourceResolver)
- m_sourceResolver->shutdown();
- }
- if (m_sourceResolver) {
- m_sourceResolver->Release();
- m_sourceResolver = 0;
- }
- if (m_videoProbeMFT) {
- m_videoProbeMFT->Release();
- m_videoProbeMFT = 0;
- }
-
- if (m_playerService->videoRendererControl()) {
- m_playerService->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;
-}
-
-void MFPlayerSession::addProbe(MFAudioProbeControl *probe)
-{
- m_audioSampleGrabber->addProbe(probe);
-}
-
-void MFPlayerSession::removeProbe(MFAudioProbeControl *probe)
-{
- m_audioSampleGrabber->removeProbe(probe);
-}
-
-void MFPlayerSession::addProbe(MFVideoProbeControl* probe)
-{
- if (m_videoProbes.contains(probe))
- return;
-
- m_videoProbes.append(probe);
-
- if (m_videoProbeMFT)
- m_videoProbeMFT->addProbe(probe);
-}
-
-void MFPlayerSession::removeProbe(MFVideoProbeControl* probe)
-{
- m_videoProbes.removeOne(probe);
-
- if (m_videoProbeMFT)
- m_videoProbeMFT->removeProbe(probe);
-}
-
-MFPlayerSession::~MFPlayerSession()
-{
- m_audioSampleGrabber->Release();
-}
-
-
-void MFPlayerSession::load(const QMediaContent &media, QIODevice *stream)
-{
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "load";
-#endif
- clear();
- QUrl url = media.request().url();
-
- if (m_status == QMediaPlayer::LoadingMedia && m_sourceResolver)
- m_sourceResolver->cancel();
-
- if (url.isEmpty() && !stream) {
- changeStatus(QMediaPlayer::NoMedia);
- } else if (stream && (!stream->isReadable())) {
- changeStatus(QMediaPlayer::InvalidMedia);
- emit error(QMediaPlayer::ResourceError, tr("Invalid stream source."), true);
- } else {
- createSession();
- changeStatus(QMediaPlayer::LoadingMedia);
- m_sourceResolver->load(url, stream);
- }
- emit positionChanged(position());
-}
-
-void MFPlayerSession::handleSourceError(long hr)
-{
- QString errorString;
- QMediaPlayer::Error errorCode = QMediaPlayer::ResourceError;
- switch (hr) {
- case QMediaPlayer::FormatError:
- errorCode = QMediaPlayer::FormatError;
- errorString = tr("Attempting to play invalid Qt resource.");
- break;
- case NS_E_FILE_NOT_FOUND:
- errorString = tr("The system cannot find the file specified.");
- break;
- case NS_E_SERVER_NOT_FOUND:
- errorString = tr("The specified server could not be found.");
- break;
- case MF_E_UNSUPPORTED_BYTESTREAM_TYPE:
- errorCode = QMediaPlayer::FormatError;
- errorString = tr("Unsupported media type.");
- break;
- default:
- errorString = tr("Failed to load source.");
- break;
- }
- changeStatus(QMediaPlayer::InvalidMedia);
- emit error(errorCode, errorString, true);
-}
-
-void MFPlayerSession::handleMediaSourceReady()
-{
- if (QMediaPlayer::LoadingMedia != m_status || !m_sourceResolver || m_sourceResolver != sender())
- return;
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "handleMediaSourceReady";
-#endif
- HRESULT hr = S_OK;
- IMFMediaSource* mediaSource = m_sourceResolver->mediaSource();
-
- DWORD dwCharacteristics = 0;
- mediaSource->GetCharacteristics(&dwCharacteristics);
- emit seekableUpdate(MFMEDIASOURCE_CAN_SEEK & dwCharacteristics);
-
- IMFPresentationDescriptor* sourcePD;
- hr = mediaSource->CreatePresentationDescriptor(&sourcePD);
- if (SUCCEEDED(hr)) {
- m_duration = 0;
- m_playerService->metaDataControl()->updateSource(sourcePD, mediaSource);
- sourcePD->GetUINT64(MF_PD_DURATION, &m_duration);
- //convert from 100 nanosecond to milisecond
- emit durationUpdate(qint64(m_duration / 10000));
- setupPlaybackTopology(mediaSource, sourcePD);
- sourcePD->Release();
- } else {
- changeStatus(QMediaPlayer::InvalidMedia);
- emit error(QMediaPlayer::ResourceError, tr("Cannot create presentation descriptor."), true);
- }
-}
-
-MFPlayerSession::MediaType MFPlayerSession::getStreamType(IMFStreamDescriptor *stream) const
-{
- if (!stream)
- return Unknown;
-
- struct SafeRelease {
- IMFMediaTypeHandler *ptr = nullptr;
- ~SafeRelease() { if (ptr) ptr->Release(); }
- } typeHandler;
- if (SUCCEEDED(stream->GetMediaTypeHandler(&typeHandler.ptr))) {
- GUID guidMajorType;
- if (SUCCEEDED(typeHandler.ptr->GetMajorType(&guidMajorType))) {
- if (guidMajorType == MFMediaType_Audio)
- return Audio;
- else if (guidMajorType == MFMediaType_Video)
- return Video;
- }
- }
-
- return Unknown;
-}
-
-void MFPlayerSession::setupPlaybackTopology(IMFMediaSource *source, IMFPresentationDescriptor *sourcePD)
-{
- HRESULT hr = S_OK;
- // Get the number of streams in the media source.
- DWORD cSourceStreams = 0;
- hr = sourcePD->GetStreamDescriptorCount(&cSourceStreams);
- if (FAILED(hr)) {
- changeStatus(QMediaPlayer::UnknownMediaStatus);
- emit error(QMediaPlayer::ResourceError, tr("Failed to get stream count."), true);
- return;
- }
-
- IMFTopology *topology;
- hr = MFCreateTopology(&topology);
- if (FAILED(hr)) {
- changeStatus(QMediaPlayer::UnknownMediaStatus);
- emit error(QMediaPlayer::ResourceError, tr("Failed to create topology."), true);
- return;
- }
-
- // Remember output node id for a first video stream
- TOPOID outputNodeId = -1;
-
- // For each stream, create the topology nodes and add them to the topology.
- DWORD succeededCount = 0;
- for (DWORD i = 0; i < cSourceStreams; i++)
- {
- BOOL fSelected = FALSE;
- bool streamAdded = false;
- IMFStreamDescriptor *streamDesc = NULL;
-
- HRESULT hr = sourcePD->GetStreamDescriptorByIndex(i, &fSelected, &streamDesc);
- if (SUCCEEDED(hr)) {
- // The media might have multiple audio and video streams,
- // only use one of each kind, and only if it is selected by default.
- MediaType mediaType = getStreamType(streamDesc);
- if (mediaType != Unknown
- && ((m_mediaTypes & mediaType) == 0) // Check if this type isn't already added
- && fSelected) {
-
- IMFTopologyNode *sourceNode = addSourceNode(topology, source, sourcePD, streamDesc);
- if (sourceNode) {
- IMFTopologyNode *outputNode = addOutputNode(mediaType, topology, 0);
- if (outputNode) {
- bool connected = false;
- if (mediaType == Audio) {
- if (!m_audioSampleGrabberNode)
- connected = setupAudioSampleGrabber(topology, sourceNode, outputNode);
- } else if (mediaType == Video && outputNodeId == -1) {
- // Remember video output node ID.
- outputNode->GetTopoNodeID(&outputNodeId);
- }
-
- if (!connected)
- hr = sourceNode->ConnectOutput(0, outputNode, 0);
-
- if (FAILED(hr)) {
- emit error(QMediaPlayer::FormatError, tr("Unable to play any stream."), false);
- } else {
- streamAdded = true;
- succeededCount++;
- m_mediaTypes |= mediaType;
- switch (mediaType) {
- case Audio:
- emit audioAvailable();
- break;
- case Video:
- emit videoAvailable();
- break;
- }
- }
- outputNode->Release();
- }
- sourceNode->Release();
- }
- }
-
- if (fSelected && !streamAdded)
- sourcePD->DeselectStream(i);
-
- streamDesc->Release();
- }
- }
-
- if (succeededCount == 0) {
- changeStatus(QMediaPlayer::InvalidMedia);
- emit error(QMediaPlayer::ResourceError, tr("Unable to play."), true);
- } else {
- if (outputNodeId != -1) {
- topology = insertMFT(topology, outputNodeId);
- }
-
- hr = m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology);
- if (FAILED(hr)) {
- changeStatus(QMediaPlayer::UnknownMediaStatus);
- emit error(QMediaPlayer::ResourceError, tr("Failed to set topology."), true);
- }
- }
- topology->Release();
-}
-
-IMFTopologyNode* MFPlayerSession::addSourceNode(IMFTopology* topology, IMFMediaSource* source,
- IMFPresentationDescriptor* presentationDesc, IMFStreamDescriptor *streamDesc)
-{
- IMFTopologyNode *node = NULL;
- HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node);
- if (SUCCEEDED(hr)) {
- hr = node->SetUnknown(MF_TOPONODE_SOURCE, source);
- if (SUCCEEDED(hr)) {
- hr = node->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, presentationDesc);
- if (SUCCEEDED(hr)) {
- hr = node->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, streamDesc);
- if (SUCCEEDED(hr)) {
- hr = topology->AddNode(node);
- if (SUCCEEDED(hr))
- return node;
- }
- }
- }
- node->Release();
- }
- return NULL;
-}
-
-IMFTopologyNode* MFPlayerSession::addOutputNode(MediaType mediaType, IMFTopology* topology, DWORD sinkID)
-{
- IMFTopologyNode *node = NULL;
- if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node)))
- return NULL;
-
- IMFActivate *activate = NULL;
- if (mediaType == Audio) {
- activate = m_playerService->audioEndpointControl()->createActivate();
- } else if (mediaType == Video) {
- if (m_playerService->videoRendererControl()) {
- activate = m_playerService->videoRendererControl()->createActivate();
- } else if (m_playerService->videoWindowControl()) {
- activate = m_playerService->videoWindowControl()->createActivate();
- } else {
- qWarning() << "no videoWindowControl or videoRendererControl, unable to add output node for video data";
- }
- } else {
- // Unknown stream type.
- emit 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;
- }
-
- 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;
-}
-
-QAudioFormat MFPlayerSession::audioFormatForMFMediaType(IMFMediaType *mediaType) const
-{
- WAVEFORMATEX *wfx = 0;
- UINT32 size;
- HRESULT hr = MFCreateWaveFormatExFromMFMediaType(mediaType, &wfx, &size, MFWaveFormatExConvertFlag_Normal);
- if (FAILED(hr))
- return QAudioFormat();
-
- if (size < sizeof(WAVEFORMATEX)) {
- CoTaskMemFree(wfx);
- return QAudioFormat();
- }
-
- if (wfx->wFormatTag != WAVE_FORMAT_PCM) {
- CoTaskMemFree(wfx);
- return QAudioFormat();
- }
-
- QAudioFormat format;
- format.setSampleRate(wfx->nSamplesPerSec);
- format.setChannelCount(wfx->nChannels);
- format.setSampleSize(wfx->wBitsPerSample);
- format.setCodec("audio/pcm");
- format.setByteOrder(QAudioFormat::LittleEndian);
- if (format.sampleSize() == 8)
- format.setSampleType(QAudioFormat::UnSignedInt);
- else
- format.setSampleType(QAudioFormat::SignedInt);
-
- CoTaskMemFree(wfx);
- return format;
-}
-
-// 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;
-
- HRESULT hr = pNode->GetObject(&nodeObject);
- if (FAILED(hr))
- return hr;
-
- hr = nodeObject->QueryInterface(IID_PPV_ARGS(&activate));
- if (SUCCEEDED(hr)) {
- DWORD dwStreamID = 0;
-
- // Try to create the media sink.
- hr = activate->ActivateObject(IID_PPV_ARGS(&sink));
- if (SUCCEEDED(hr))
- dwStreamID = MFGetAttributeUINT32(pNode, MF_TOPONODE_STREAMID, 0);
-
- if (SUCCEEDED(hr)) {
- // First check if the media sink already has a stream sink with the requested ID.
- hr = sink->GetStreamSinkById(dwStreamID, &stream);
- if (FAILED(hr)) {
- // Create the stream sink.
- hr = sink->AddStreamSink(dwStreamID, NULL, &stream);
- }
- }
-
- // Replace the node's object pointer with the stream sink.
- if (SUCCEEDED(hr)) {
- hr = pNode->SetObject(stream);
- }
- } 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;
-}
-
-// BindOutputNodes
-// Sets the IMFStreamSink pointers on all of the output nodes in a topology.
-HRESULT BindOutputNodes(IMFTopology *pTopology)
-{
- IMFCollection *collection;
-
- // Get the collection of output nodes.
- HRESULT hr = pTopology->GetOutputNodeCollection(&collection);
-
- // Enumerate all of the nodes in the collection.
- if (SUCCEEDED(hr)) {
- DWORD cNodes;
- hr = collection->GetElementCount(&cNodes);
-
- if (SUCCEEDED(hr)) {
- for (DWORD i = 0; i < cNodes; i++) {
- IUnknown *element;
- hr = collection->GetElement(i, &element);
- if (FAILED(hr))
- break;
-
- IMFTopologyNode *node;
- hr = element->QueryInterface(IID_IMFTopologyNode, (void**)&node);
- element->Release();
- if (FAILED(hr))
- break;
-
- // Bind this node.
- hr = BindOutputNode(node);
- node->Release();
- if (FAILED(hr))
- break;
- }
- }
- collection->Release();
- }
-
- return hr;
-}
-
-// 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)
-{
- bool isNewTopology = false;
-
- IMFTopoLoader *topoLoader = 0;
- IMFTopology *resolvedTopology = 0;
- IMFCollection *outputNodes = 0;
-
- do {
- if (FAILED(BindOutputNodes(topology)))
- break;
-
- if (FAILED(MFCreateTopoLoader(&topoLoader)))
- break;
-
- if (FAILED(topoLoader->Load(topology, &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)))
- break;
- }
-
- if (insertResizer(resolvedTopology))
- 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;
-}
-
-// This method checks if the topology contains a color converter transform (CColorConvertDMO),
-// if it does it inserts a resizer transform (CResizerDMO) to handle dynamic frame size change
-// of the video stream.
-// Returns true if it inserted a resizer
-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;
-
- 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;
- }
-
- if (FAILED(topology->GetNode(i, &node)))
- break;
-
- MF_TOPOLOGY_TYPE nodeType;
- if (FAILED(node->GetNodeType(&nodeType)))
- break;
-
- if (nodeType != MF_TOPOLOGY_TRANSFORM_NODE)
- continue;
-
- if (FAILED(node->GetObject(&object)))
- break;
-
- if (FAILED(object->QueryInterface(&colorConv)))
- continue;
-
- if (FAILED(CoCreateInstance(CLSID_CResizerDMO, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform, (void**)&resizer)))
- break;
-
- if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &resizerNode)))
- break;
-
- if (FAILED(resizerNode->SetObject(resizer)))
- break;
-
- if (FAILED(topology->AddNode(resizerNode)))
- break;
-
- DWORD outputIndex = 0;
- if (FAILED(node->GetInput(0, &inputNode, &outputIndex))) {
- topology->RemoveNode(resizerNode);
- break;
- }
-
- if (FAILED(inputNode->ConnectOutput(0, resizerNode, 0))) {
- topology->RemoveNode(resizerNode);
- break;
- }
-
- if (FAILED(resizerNode->ConnectOutput(0, node, 0))) {
- inputNode->ConnectOutput(0, node, 0);
- topology->RemoveNode(resizerNode);
- break;
- }
-
- inserted = true;
- 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;
-}
-
-// This method inserts a color converter (CColorConvertDMO) in the topology,
-// typically to convert to RGB format.
-// Usually this converter is automatically inserted when the topology is resolved but
-// 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;
-
- if (FAILED(topology->GetOutputNodeCollection(&outputNodes)))
- return;
-
- DWORD elementCount = 0;
- if (FAILED(outputNodes->GetElementCount(&elementCount)))
- goto done;
-
- for (DWORD n = 0; n < elementCount; n++) {
- IUnknown *element = 0;
- IMFTopologyNode *node = 0;
- IMFTopologyNode *inputNode = 0;
- IMFTopologyNode *mftNode = 0;
- IMFTransform *converter = 0;
-
- 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;
-
- DWORD outputIndex = 0;
- if (FAILED(node->GetInput(0, &inputNode, &outputIndex)))
- break;
-
- if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &mftNode)))
- break;
-
- if (FAILED(CoCreateInstance(CLSID_CColorConvertDMO, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform, (void**)&converter)))
- break;
-
- if (FAILED(mftNode->SetObject(converter)))
- break;
-
- if (FAILED(topology->AddNode(mftNode)))
- break;
-
- if (FAILED(inputNode->ConnectOutput(0, mftNode, 0)))
- break;
-
- if (FAILED(mftNode->ConnectOutput(0, node, 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)
-{
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "stop";
-#endif
- if (!immediate && m_pendingState != NoPending) {
- m_request.setCommand(CmdStop);
- } else {
- if (m_state.command == CmdStop)
- return;
-
- if (m_scrubbing)
- scrub(false);
-
- if (SUCCEEDED(m_session->Stop())) {
- m_state.setCommand(CmdStop);
- m_pendingState = CmdPending;
- if (m_status != QMediaPlayer::EndOfMedia) {
- m_varStart.vt = VT_I8;
- m_varStart.hVal.QuadPart = 0;
- }
- } else {
- emit error(QMediaPlayer::ResourceError, tr("Failed to stop."), true);
- }
- }
-}
-
-void MFPlayerSession::start()
-{
- if (m_status == QMediaPlayer::EndOfMedia)
- m_varStart.hVal.QuadPart = 0; // restart from the beginning
-
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "start";
-#endif
-
- if (m_pendingState != NoPending) {
- m_request.setCommand(CmdStart);
- } else {
- if (m_state.command == CmdStart)
- return;
-
- if (m_scrubbing)
- scrub(false);
-
- if (SUCCEEDED(m_session->Start(&GUID_NULL, &m_varStart))) {
- m_state.setCommand(CmdStart);
- m_pendingState = CmdPending;
- PropVariantInit(&m_varStart);
- } else {
- emit error(QMediaPlayer::ResourceError, tr("failed to start playback"), true);
- }
- }
-}
-
-void MFPlayerSession::pause()
-{
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "pause";
-#endif
- if (m_pendingState != NoPending) {
- m_request.setCommand(CmdPause);
- } else {
- if (m_state.command == CmdPause)
- return;
-
- if (SUCCEEDED(m_session->Pause())) {
- m_state.setCommand(CmdPause);
- m_pendingState = CmdPending;
- } else {
- emit error(QMediaPlayer::ResourceError, tr("Failed to pause."), false);
- }
- }
-}
-
-void MFPlayerSession::changeStatus(QMediaPlayer::MediaStatus newStatus)
-{
- if (m_status == newStatus)
- return;
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "MFPlayerSession::changeStatus" << newStatus;
-#endif
- m_status = newStatus;
- emit statusChanged();
-}
-
-QMediaPlayer::MediaStatus MFPlayerSession::status() const
-{
- return m_status;
-}
-
-void MFPlayerSession::createSession()
-{
- close();
-
- m_hCloseEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
-
- m_sourceResolver = new SourceResolver();
- QObject::connect(m_sourceResolver, SIGNAL(mediaSourceReady()), this, SLOT(handleMediaSourceReady()));
- QObject::connect(m_sourceResolver, SIGNAL(error(long)), this, SLOT(handleSourceError(long)));
-
- m_videoProbeMFT = new MFTransform;
- for (int i = 0; i < m_videoProbes.size(); ++i)
- m_videoProbeMFT->addProbe(m_videoProbes.at(i));
-
- Q_ASSERT(m_session == NULL);
- HRESULT hr = MFCreateMediaSession(NULL, &m_session);
- if (FAILED(hr)) {
- changeStatus(QMediaPlayer::UnknownMediaStatus);
- emit error(QMediaPlayer::ResourceError, tr("Unable to create mediasession."), true);
- }
-
- hr = m_session->BeginGetEvent(this, m_session);
-
- if (FAILED(hr)) {
- changeStatus(QMediaPlayer::UnknownMediaStatus);
- emit error(QMediaPlayer::ResourceError, tr("Unable to pull session events."), false);
- }
-
- PropVariantInit(&m_varStart);
- m_varStart.vt = VT_I8;
- m_varStart.hVal.QuadPart = 0;
-}
-
-qint64 MFPlayerSession::position()
-{
- if (m_request.command == CmdSeek || m_request.command == CmdSeekResume)
- return m_request.start;
-
- if (m_pendingState == SeekPending)
- return m_state.start;
-
- if (m_state.command == CmdStop)
- return qint64(m_varStart.hVal.QuadPart / 10000);
-
- if (m_presentationClock) {
- MFTIME time, sysTime;
- if (FAILED(m_presentationClock->GetCorrelatedTime(0, &time, &sysTime)))
- return 0;
- return qint64(time / 10000);
- }
- return 0;
-}
-
-void MFPlayerSession::setPosition(qint64 position)
-{
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "setPosition";
-#endif
- if (m_pendingState != NoPending) {
- m_request.setCommand(CmdSeek);
- m_request.start = position;
- } else {
- setPositionInternal(position, CmdNone);
- }
-}
-
-void MFPlayerSession::setPositionInternal(qint64 position, Command requestCmd)
-{
- if (m_status == QMediaPlayer::EndOfMedia)
- changeStatus(QMediaPlayer::LoadedMedia);
- if (m_state.command == CmdStop && requestCmd != CmdSeekResume) {
- m_varStart.vt = VT_I8;
- m_varStart.hVal.QuadPart = LONGLONG(position * 10000);
- // Even though the position is not actually set on the session yet,
- // report it to have changed anyway for UI controls to be updated
- emit positionChanged(this->position());
- return;
- }
-
- if (m_state.command == CmdPause)
- scrub(true);
-
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "setPositionInternal";
-#endif
-
- PROPVARIANT varStart;
- varStart.vt = VT_I8;
- varStart.hVal.QuadPart = LONGLONG(position * 10000);
- if (SUCCEEDED(m_session->Start(NULL, &varStart)))
- {
- PropVariantInit(&m_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);
- }
-}
-
-qreal MFPlayerSession::playbackRate() const
-{
- if (m_scrubbing)
- return m_restoreRate;
- return m_state.rate;
-}
-
-void MFPlayerSession::setPlaybackRate(qreal rate)
-{
- if (m_scrubbing) {
- m_restoreRate = rate;
- emit playbackRateChanged(rate);
- return;
- }
- setPlaybackRateInternal(rate);
-}
-
-void MFPlayerSession::setPlaybackRateInternal(qreal rate)
-{
- if (rate == m_request.rate)
- return;
-
- m_pendingRate = rate;
- if (!m_rateSupport)
- return;
-
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "setPlaybackRate";
-#endif
- BOOL isThin = FALSE;
-
- //from MSDN http://msdn.microsoft.com/en-us/library/aa965220%28v=vs.85%29.aspx
- //Thinning applies primarily to video streams.
- //In thinned mode, the source drops delta frames and deliver only key frames.
- //At very high playback rates, the source might skip some key frames (for example, deliver every other key frame).
-
- if (FAILED(m_rateSupport->IsRateSupported(FALSE, rate, NULL))) {
- isThin = TRUE;
- if (FAILED(m_rateSupport->IsRateSupported(isThin, rate, NULL))) {
- qWarning() << "unable to set playbackrate = " << rate;
- m_pendingRate = m_request.rate = m_state.rate;
- return;
- }
- }
- if (m_pendingState != NoPending) {
- m_request.rate = rate;
- m_request.isThin = isThin;
- // Remember the current transport state (play, paused, etc), so that we
- // can restore it after the rate change, if necessary. However, if
- // anothercommand is already pending, that one takes precedent.
- if (m_request.command == CmdNone)
- m_request.setCommand(m_state.command);
- } else {
- //No pending operation. Commit the new rate.
- commitRateChange(rate, isThin);
- }
-}
-
-void MFPlayerSession::commitRateChange(qreal rate, BOOL isThin)
-{
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "commitRateChange";
-#endif
- Q_ASSERT(m_pendingState == NoPending);
- MFTIME hnsSystemTime = 0;
- MFTIME hnsClockTime = 0;
- Command cmdNow = m_state.command;
- bool resetPosition = false;
- // Allowed rate transitions:
- // Positive <-> negative: Stopped
- // Negative <-> zero: Stopped
- // Postive <-> zero: Paused or stopped
- if ((rate > 0 && m_state.rate <= 0) || (rate < 0 && m_state.rate >= 0)) {
- if (cmdNow == CmdStart) {
- // Get the current clock position. This will be the restart time.
- m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
- Q_ASSERT(hnsSystemTime != 0);
-
- if (rate < 0 || m_state.rate < 0)
- m_request.setCommand(CmdSeekResume);
- else if (isThin || m_state.isThin)
- m_request.setCommand(CmdStartAndSeek);
- else
- m_request.setCommand(CmdStart);
-
- // We need to stop only when dealing with negative rates
- if (rate >= 0 && m_state.rate >= 0)
- pause();
- else
- stop();
-
- // If we deal with negative rates, we stopped the session and consequently
- // reset the position to zero. We then need to resume to the current position.
- m_request.start = hnsClockTime / 10000;
- } else if (cmdNow == CmdPause) {
- if (rate < 0 || m_state.rate < 0) {
- // The current state is paused.
- // For this rate change, the session must be stopped. However, the
- // session cannot transition back from stopped to paused.
- // Therefore, this rate transition is not supported while paused.
- qWarning() << "Unable to change rate from positive to negative or vice versa in paused state";
- rate = m_state.rate;
- isThin = m_state.isThin;
- goto done;
- }
-
- // This happens when resuming playback after scrubbing in pause mode.
- // This transition requires the session to be paused. Even though our
- // internal state is set to paused, the session might not be so we need
- // to enforce it
- if (rate > 0 && m_state.rate == 0) {
- m_state.setCommand(CmdNone);
- pause();
- }
- }
- } else if (rate == 0 && m_state.rate > 0) {
- if (cmdNow != CmdPause) {
- // Transition to paused.
- // This transisition requires the paused state.
- // Pause and set the rate.
- pause();
-
- // Request: Switch back to current state.
- m_request.setCommand(cmdNow);
- }
- } else if (rate == 0 && m_state.rate < 0) {
- // Changing rate from negative to zero requires to stop the session
- m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
-
- m_request.setCommand(CmdSeekResume);
-
- stop();
-
- // Resume to the current position (stop() will reset the position to 0)
- m_request.start = hnsClockTime / 10000;
- } else if (!isThin && m_state.isThin) {
- if (cmdNow == CmdStart) {
- // When thinning, only key frames are read and presented. Going back
- // to normal playback requires to reset the current position to force
- // the pipeline to decode the actual frame at the current position
- // (which might be earlier than the last decoded key frame)
- resetPosition = true;
- } else if (cmdNow == CmdPause) {
- // If paused, don't reset the position until we resume, otherwise
- // a new frame will be rendered
- m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
- m_request.setCommand(CmdSeekResume);
- m_request.start = hnsClockTime / 10000;
- }
-
- }
-
- // Set the rate.
- if (FAILED(m_rateControl->SetRate(isThin, rate))) {
- qWarning() << "failed to set playbackrate = " << rate;
- rate = m_state.rate;
- isThin = m_state.isThin;
- goto done;
- }
-
- if (resetPosition) {
- m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
- setPosition(hnsClockTime / 10000);
- }
-
-done:
- // Adjust our current rate and requested rate.
- m_pendingRate = m_request.rate = m_state.rate = rate;
- if (rate != 0)
- m_state.isThin = isThin;
- emit playbackRateChanged(rate);
-}
-
-void MFPlayerSession::scrub(bool enableScrub)
-{
- if (m_scrubbing == enableScrub)
- return;
-
- m_scrubbing = enableScrub;
-
- if (!canScrub()) {
- if (!enableScrub)
- m_pendingRate = m_restoreRate;
- return;
- }
-
- if (enableScrub) {
- // Enter scrubbing mode. Cache the rate.
- m_restoreRate = m_request.rate;
- setPlaybackRateInternal(0.0f);
- } else {
- // Leaving scrubbing mode. Restore the old rate.
- setPlaybackRateInternal(m_restoreRate);
- }
-}
-
-int MFPlayerSession::volume() const
-{
- return m_volume;
-}
-
-void MFPlayerSession::setVolume(int volume)
-{
- if (m_volume == volume)
- return;
- m_volume = volume;
-
- if (!m_muted)
- setVolumeInternal(volume);
-
- emit volumeChanged(m_volume);
-}
-
-bool MFPlayerSession::isMuted() const
-{
- return m_muted;
-}
-
-void MFPlayerSession::setMuted(bool muted)
-{
- if (m_muted == muted)
- return;
- m_muted = muted;
-
- setVolumeInternal(muted ? 0 : m_volume);
-
- emit mutedChanged(m_muted);
-}
-
-void MFPlayerSession::setVolumeInternal(int volume)
-{
- if (m_volumeControl) {
- quint32 channelCount = 0;
- if (!SUCCEEDED(m_volumeControl->GetChannelCount(&channelCount))
- || channelCount == 0)
- return;
-
- float scaled = volume * 0.01f;
- for (quint32 i = 0; i < channelCount; ++i)
- m_volumeControl->SetChannelVolume(i, scaled);
- }
-}
-
-int MFPlayerSession::bufferStatus()
-{
- if (!m_netsourceStatistics)
- return 0;
- PROPVARIANT var;
- PropVariantInit(&var);
- PROPERTYKEY key;
- key.fmtid = MFNETSOURCE_STATISTICS;
- key.pid = MFNETSOURCE_BUFFERPROGRESS_ID;
- int progress = -1;
- // GetValue returns S_FALSE if the property is not available, which has
- // a value > 0. We therefore can't use the SUCCEEDED macro here.
- if (m_netsourceStatistics->GetValue(key, &var) == S_OK) {
- progress = var.lVal;
- PropVariantClear(&var);
- }
-
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "bufferStatus: progress = " << progress;
-#endif
-
- return progress;
-}
-
-QMediaTimeRange MFPlayerSession::availablePlaybackRanges()
-{
- // defaults to the whole media
- qint64 start = 0;
- qint64 end = qint64(m_duration / 10000);
-
- if (m_netsourceStatistics) {
- PROPVARIANT var;
- PropVariantInit(&var);
- PROPERTYKEY key;
- key.fmtid = MFNETSOURCE_STATISTICS;
- key.pid = MFNETSOURCE_SEEKRANGESTART_ID;
- // GetValue returns S_FALSE if the property is not available, which has
- // a value > 0. We therefore can't use the SUCCEEDED macro here.
- if (m_netsourceStatistics->GetValue(key, &var) == S_OK) {
- start = qint64(var.uhVal.QuadPart / 10000);
- PropVariantClear(&var);
- PropVariantInit(&var);
- key.pid = MFNETSOURCE_SEEKRANGEEND_ID;
- if (m_netsourceStatistics->GetValue(key, &var) == S_OK) {
- end = qint64(var.uhVal.QuadPart / 10000);
- PropVariantClear(&var);
- }
- }
- }
-
- return QMediaTimeRange(start, end);
-}
-
-HRESULT MFPlayerSession::QueryInterface(REFIID riid, void** ppvObject)
-{
- if (!ppvObject)
- return E_POINTER;
- if (riid == IID_IMFAsyncCallback) {
- *ppvObject = static_cast<IMFAsyncCallback*>(this);
- } else if (riid == IID_IUnknown) {
- *ppvObject = static_cast<IUnknown*>(this);
- } else {
- *ppvObject = NULL;
- return E_NOINTERFACE;
- }
- return S_OK;
-}
-
-ULONG MFPlayerSession::AddRef(void)
-{
- return InterlockedIncrement(&m_cRef);
-}
-
-ULONG MFPlayerSession::Release(void)
-{
- LONG cRef = InterlockedDecrement(&m_cRef);
- if (cRef == 0)
- this->deleteLater();
- return cRef;
-}
-
-HRESULT MFPlayerSession::Invoke(IMFAsyncResult *pResult)
-{
- if (pResult->GetStateNoAddRef() != m_session)
- return S_OK;
-
- IMFMediaEvent *pEvent = NULL;
- // Get the event from the event queue.
- HRESULT hr = m_session->EndGetEvent(pResult, &pEvent);
- if (FAILED(hr)) {
- return S_OK;
- }
-
- MediaEventType meType = MEUnknown;
- hr = pEvent->GetType(&meType);
- if (FAILED(hr)) {
- pEvent->Release();
- return S_OK;
- }
-
- if (meType == MESessionClosed) {
- SetEvent(m_hCloseEvent);
- pEvent->Release();
- return S_OK;
- } else {
- hr = m_session->BeginGetEvent(this, m_session);
- 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)
-{
- HRESULT hrStatus = S_OK;
- HRESULT hr = sessionEvent->GetStatus(&hrStatus);
- if (FAILED(hr) || !m_session) {
- sessionEvent->Release();
- return;
- }
-
- MediaEventType meType = MEUnknown;
- hr = sessionEvent->GetType(&meType);
-
-#ifdef DEBUG_MEDIAFOUNDATION
- if (FAILED(hrStatus))
- qDebug() << "handleSessionEvent: MediaEventType = " << meType << "Failed";
- else
- qDebug() << "handleSessionEvent: MediaEventType = " << meType;
-#endif
-
- switch (meType) {
- case MENonFatalError: {
- PROPVARIANT var;
- PropVariantInit(&var);
- sessionEvent->GetValue(&var);
- qWarning() << "handleSessionEvent: non fatal error = " << var.ulVal;
- PropVariantClear(&var);
- emit error(QMediaPlayer::ResourceError, tr("Media session non-fatal error."), false);
- }
- break;
- case MESourceUnknown:
- changeStatus(QMediaPlayer::InvalidMedia);
- break;
- case MEError:
- changeStatus(QMediaPlayer::UnknownMediaStatus);
- qWarning() << "handleSessionEvent: serious error = " << hrStatus;
- emit error(QMediaPlayer::ResourceError, tr("Media session serious error."), true);
- break;
- case MESessionRateChanged:
- // If the rate change succeeded, we've already got the rate
- // cached. If it failed, try to get the actual rate.
- if (FAILED(hrStatus)) {
- PROPVARIANT var;
- PropVariantInit(&var);
- if (SUCCEEDED(sessionEvent->GetValue(&var)) && (var.vt == VT_R4)) {
- m_state.rate = var.fltVal;
- }
- emit playbackRateChanged(playbackRate());
- }
- break;
- case MESessionScrubSampleComplete :
- if (m_scrubbing)
- updatePendingCommands(CmdStart);
- break;
- case MESessionStarted:
- if (m_status == QMediaPlayer::EndOfMedia
- || m_status == QMediaPlayer::LoadedMedia) {
- // If the session started, then enough data is buffered to play
- changeStatus(QMediaPlayer::BufferedMedia);
- }
-
- updatePendingCommands(CmdStart);
- // playback started, we can now set again the procAmpValues if they have been
- // changed previously (these are lost when loading a new media)
- if (m_playerService->videoWindowControl()) {
- m_playerService->videoWindowControl()->applyImageControls();
- }
- break;
- case MESessionStopped:
- if (m_status != QMediaPlayer::EndOfMedia) {
- m_varStart.vt = VT_I8;
- m_varStart.hVal.QuadPart = 0;
-
- // Reset to Loaded status unless we are loading a new media
- // or changing the playback rate to negative values (stop required)
- if (m_status != QMediaPlayer::LoadingMedia && m_request.command != CmdSeekResume)
- changeStatus(QMediaPlayer::LoadedMedia);
- }
- updatePendingCommands(CmdStop);
- break;
- case MESessionPaused:
- updatePendingCommands(CmdPause);
- break;
- case MEReconnectStart:
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "MEReconnectStart" << ((hrStatus == S_OK) ? "OK" : "Failed");
-#endif
- break;
- case MEReconnectEnd:
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "MEReconnectEnd" << ((hrStatus == S_OK) ? "OK" : "Failed");
-#endif
- break;
- case MESessionTopologySet:
- if (FAILED(hrStatus)) {
- changeStatus(QMediaPlayer::InvalidMedia);
- emit 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(audioFormatForMFMediaType(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_varStart.vt = VT_I8;
- m_varStart.hVal.QuadPart = 0;
-
- changeStatus(QMediaPlayer::LoadedMedia);
- }
- break;
- }
-
- if (FAILED(hrStatus)) {
- sessionEvent->Release();
- return;
- }
-
- switch (meType) {
- case MEBufferingStarted:
- changeStatus(QMediaPlayer::StalledMedia);
- emit bufferStatusChanged(bufferStatus());
- break;
- case MEBufferingStopped:
- changeStatus(QMediaPlayer::BufferedMedia);
- emit bufferStatusChanged(bufferStatus());
- break;
- case MESessionEnded:
- m_pendingState = NoPending;
- m_state.command = CmdStop;
- m_state.prevCmd = CmdNone;
- m_request.command = CmdNone;
- m_request.prevCmd = CmdNone;
-
- m_varStart.vt = VT_I8;
- //keep reporting the final position after end of media
- m_varStart.hVal.QuadPart = m_duration;
- emit positionChanged(position());
-
- changeStatus(QMediaPlayer::EndOfMedia);
- break;
- case MEEndOfPresentationSegment:
- break;
- case MESessionTopologyStatus: {
- UINT32 status;
- if (SUCCEEDED(sessionEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, &status))) {
- if (status == MF_TOPOSTATUS_READY) {
- IMFClock* clock;
- if (SUCCEEDED(m_session->GetClock(&clock))) {
- clock->QueryInterface(IID_IMFPresentationClock, (void**)(&m_presentationClock));
- clock->Release();
- }
-
- 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 ((m_mediaTypes & Video) == Video
- && SUCCEEDED(m_rateSupport->IsRateSupported(TRUE, 0, NULL)))
- m_canScrub = true;
- }
- BOOL isThin = FALSE;
- float rate = 1;
- if (SUCCEEDED(m_rateControl->GetRate(&isThin, &rate))) {
- if (m_pendingRate != rate) {
- m_state.rate = m_request.rate = rate;
- setPlaybackRate(m_pendingRate);
- }
- }
- }
- MFGetService(m_session, MFNETSOURCE_STATISTICS_SERVICE, IID_PPV_ARGS(&m_netsourceStatistics));
-
- if (SUCCEEDED(MFGetService(m_session, MR_STREAM_VOLUME_SERVICE, IID_PPV_ARGS(&m_volumeControl))))
- setVolumeInternal(m_muted ? 0 : m_volume);
- }
- }
- }
- break;
- default:
- break;
- }
-
- sessionEvent->Release();
-}
-
-void MFPlayerSession::updatePendingCommands(Command command)
-{
- emit positionChanged(position());
- if (m_state.command != command || m_pendingState == NoPending)
- return;
-
- // Seek while paused completed
- if (m_pendingState == SeekPending && m_state.prevCmd == CmdPause) {
- m_pendingState = NoPending;
- // A seek operation actually restarts playback. If scrubbing is possible, playback rate
- // is set to 0.0 at this point and we just need to reset the current state to Pause.
- // If scrubbing is not possible, the playback rate was not changed and we explicitly need
- // to re-pause playback.
- if (!canScrub())
- pause();
- else
- m_state.setCommand(CmdPause);
- }
-
- m_pendingState = NoPending;
-
- //First look for rate changes.
- if (m_request.rate != m_state.rate) {
- commitRateChange(m_request.rate, m_request.isThin);
- }
-
- // Now look for new requests.
- if (m_pendingState == NoPending) {
- switch (m_request.command) {
- case CmdStart:
- start();
- break;
- case CmdPause:
- pause();
- break;
- case CmdStop:
- stop();
- break;
- case CmdSeek:
- case CmdSeekResume:
- setPositionInternal(m_request.start, m_request.command);
- break;
- case CmdStartAndSeek:
- start();
- setPositionInternal(m_request.start, m_request.command);
- break;
- }
- m_request.setCommand(CmdNone);
- }
-
-}
-
-bool MFPlayerSession::canScrub() const
-{
- return m_canScrub && m_rateSupport && m_rateControl;
-}
-
-void MFPlayerSession::clear()
-{
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "MFPlayerSession::clear";
-#endif
- m_mediaTypes = 0;
- m_canScrub = false;
-
- m_pendingState = NoPending;
- m_state.command = CmdStop;
- m_state.prevCmd = CmdNone;
- m_request.command = CmdNone;
- m_request.prevCmd = CmdNone;
-
- 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;
- }
-}
diff --git a/src/plugins/wmf/player/mfplayersession.h b/src/plugins/wmf/player/mfplayersession.h
deleted file mode 100644
index c6ffba91f..000000000
--- a/src/plugins/wmf/player/mfplayersession.h
+++ /dev/null
@@ -1,239 +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:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 MFPLAYERSESSION_H
-#define MFPLAYERSESSION_H
-
-#include <mfapi.h>
-#include <mfidl.h>
-
-#include "qmediaplayer.h"
-#include "qmediaservice.h"
-#include "qmediatimerange.h"
-
-#include <QtCore/qcoreevent.h>
-#include <QtCore/qmutex.h>
-#include <QtCore/qurl.h>
-#include <QtCore/qwaitcondition.h>
-#include <QtMultimedia/qaudioformat.h>
-#include <QtMultimedia/qvideosurfaceformat.h>
-
-QT_BEGIN_NAMESPACE
-class QMediaContent;
-QT_END_NAMESPACE
-
-QT_USE_NAMESPACE
-
-class SourceResolver;
-class MFAudioEndpointControl;
-class MFVideoRendererControl;
-class MFPlayerControl;
-class MFMetaDataControl;
-class MFPlayerService;
-class AudioSampleGrabberCallback;
-class MFTransform;
-class MFAudioProbeControl;
-class MFVideoProbeControl;
-
-class MFPlayerSession : public QObject, public IMFAsyncCallback
-{
- Q_OBJECT
- friend class SourceResolver;
-public:
- MFPlayerSession(MFPlayerService *playerService = 0);
- ~MFPlayerSession();
-
- STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject);
-
- STDMETHODIMP_(ULONG) AddRef(void);
-
- STDMETHODIMP_(ULONG) Release(void);
-
- STDMETHODIMP Invoke(IMFAsyncResult *pResult);
-
- STDMETHODIMP GetParameters(DWORD *pdwFlags, DWORD *pdwQueue)
- {
- Q_UNUSED(pdwFlags);
- Q_UNUSED(pdwQueue);
- return E_NOTIMPL;
- }
-
- void load(const QMediaContent &media, QIODevice *stream);
- void stop(bool immediate = false);
- void start();
- void pause();
-
- QMediaPlayer::MediaStatus status() const;
- qint64 position();
- void setPosition(qint64 position);
- qreal playbackRate() const;
- void setPlaybackRate(qreal rate);
- int volume() const;
- void setVolume(int volume);
- bool isMuted() const;
- void setMuted(bool muted);
- int bufferStatus();
- QMediaTimeRange availablePlaybackRanges();
-
- void changeStatus(QMediaPlayer::MediaStatus newStatus);
-
- void close();
-
- void addProbe(MFAudioProbeControl* probe);
- void removeProbe(MFAudioProbeControl* probe);
- void addProbe(MFVideoProbeControl* probe);
- void removeProbe(MFVideoProbeControl* probe);
-
-Q_SIGNALS:
- void error(QMediaPlayer::Error error, QString errorString, bool isFatal);
- void sessionEvent(IMFMediaEvent *sessionEvent);
- void statusChanged();
- void audioAvailable();
- void videoAvailable();
- void durationUpdate(qint64 duration);
- void seekableUpdate(bool seekable);
- void positionChanged(qint64 position);
- void playbackRateChanged(qreal rate);
- void volumeChanged(int volume);
- void mutedChanged(bool muted);
- void bufferStatusChanged(int percentFilled);
-
-private Q_SLOTS:
- void handleMediaSourceReady();
- void handleSessionEvent(IMFMediaEvent *sessionEvent);
- void handleSourceError(long hr);
-
-private:
- long m_cRef;
- MFPlayerService *m_playerService;
- IMFMediaSession *m_session;
- IMFPresentationClock *m_presentationClock;
- IMFRateControl *m_rateControl;
- IMFRateSupport *m_rateSupport;
- IMFAudioStreamVolume *m_volumeControl;
- IPropertyStore *m_netsourceStatistics;
- PROPVARIANT m_varStart;
- UINT64 m_duration;
-
- enum Command
- {
- CmdNone = 0,
- CmdStop,
- CmdStart,
- CmdPause,
- CmdSeek,
- CmdSeekResume,
- CmdStartAndSeek
- };
-
- void clear();
- void setPositionInternal(qint64 position, Command requestCmd);
- void setPlaybackRateInternal(qreal rate);
- void commitRateChange(qreal rate, BOOL isThin);
- bool canScrub() const;
- void scrub(bool enableScrub);
- bool m_scrubbing;
- float m_restoreRate;
-
- SourceResolver *m_sourceResolver;
- HANDLE m_hCloseEvent;
- bool m_closing;
-
- enum MediaType
- {
- Unknown = 0,
- Audio = 1,
- Video = 2,
- };
- DWORD m_mediaTypes;
-
- enum PendingState
- {
- NoPending = 0,
- CmdPending,
- SeekPending,
- };
-
- struct SeekState
- {
- void setCommand(Command cmd) {
- prevCmd = command;
- command = cmd;
- }
- Command command;
- Command prevCmd;
- float rate; // Playback rate
- BOOL isThin; // Thinned playback?
- qint64 start; // Start position
- };
- SeekState m_state; // Current nominal state.
- SeekState m_request; // Pending request.
- PendingState m_pendingState;
- float m_pendingRate;
- void updatePendingCommands(Command command);
-
- QMediaPlayer::MediaStatus m_status;
- bool m_canScrub;
- int m_volume;
- bool m_muted;
-
- void setVolumeInternal(int volume);
-
- void createSession();
- void setupPlaybackTopology(IMFMediaSource *source, IMFPresentationDescriptor *sourcePD);
- MediaType getStreamType(IMFStreamDescriptor *stream) const;
- IMFTopologyNode* addSourceNode(IMFTopology* topology, IMFMediaSource* source,
- IMFPresentationDescriptor* presentationDesc, IMFStreamDescriptor *streamDesc);
- 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;
- AudioSampleGrabberCallback *m_audioSampleGrabber;
- IMFTopologyNode *m_audioSampleGrabberNode;
-
- IMFTopology *insertMFT(IMFTopology *topology, TOPOID outputNodeId);
- bool insertResizer(IMFTopology *topology);
- void insertColorConverter(IMFTopology *topology, TOPOID outputNodeId);
- MFTransform *m_videoProbeMFT;
- QList<MFVideoProbeControl*> m_videoProbes;
-};
-
-
-#endif
diff --git a/src/plugins/wmf/player/mftvideo.cpp b/src/plugins/wmf/player/mftvideo.cpp
deleted file mode 100644
index 9dce654f2..000000000
--- a/src/plugins/wmf/player/mftvideo.cpp
+++ /dev/null
@@ -1,753 +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.h"
-#include "mfvideoprobecontrol.h"
-#include <private/qmemoryvideobuffer_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;
- }
-
- 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;
-}
-
-QVideoFrame::PixelFormat MFTransform::formatFromSubtype(const GUID& subtype)
-{
- if (subtype == MFVideoFormat_ARGB32)
- return QVideoFrame::Format_ARGB32;
- else if (subtype == MFVideoFormat_RGB32)
- return QVideoFrame::Format_RGB32;
- else if (subtype == MFVideoFormat_RGB24)
- return QVideoFrame::Format_RGB24;
- else if (subtype == MFVideoFormat_RGB565)
- return QVideoFrame::Format_RGB565;
- else if (subtype == MFVideoFormat_RGB555)
- return QVideoFrame::Format_RGB555;
- else if (subtype == MFVideoFormat_AYUV)
- return QVideoFrame::Format_AYUV444;
- else if (subtype == MFVideoFormat_I420)
- return QVideoFrame::Format_YUV420P;
- else if (subtype == MFVideoFormat_UYVY)
- return QVideoFrame::Format_UYVY;
- else if (subtype == MFVideoFormat_YV12)
- return QVideoFrame::Format_YV12;
- else if (subtype == MFVideoFormat_NV12)
- return QVideoFrame::Format_NV12;
-
- return QVideoFrame::Format_Invalid;
-}
-
-QVideoSurfaceFormat MFTransform::videoFormatForMFMediaType(IMFMediaType *mediaType, int *bytesPerLine)
-{
- UINT32 stride;
- if (FAILED(mediaType->GetUINT32(MF_MT_DEFAULT_STRIDE, &stride))) {
- *bytesPerLine = 0;
- return QVideoSurfaceFormat();
- }
-
- *bytesPerLine = (int)stride;
-
- QSize size;
- UINT32 width, height;
- if (FAILED(MFGetAttributeSize(mediaType, MF_MT_FRAME_SIZE, &width, &height)))
- return QVideoSurfaceFormat();
-
- size.setWidth(width);
- size.setHeight(height);
-
- GUID subtype = GUID_NULL;
- if (FAILED(mediaType->GetGUID(MF_MT_SUBTYPE, &subtype)))
- return QVideoSurfaceFormat();
-
- QVideoFrame::PixelFormat pixelFormat = formatFromSubtype(subtype);
- QVideoSurfaceFormat format(size, pixelFormat);
-
- UINT32 num, den;
- if (SUCCEEDED(MFGetAttributeRatio(mediaType, MF_MT_PIXEL_ASPECT_RATIO, &num, &den))) {
- format.setPixelAspectRatio(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.frameSize(), m_format.pixelFormat());
-
- // 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/wmf/player/mftvideo.h b/src/plugins/wmf/player/mftvideo.h
deleted file mode 100644
index ffcb80b32..000000000
--- a/src/plugins/wmf/player/mftvideo.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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
-
-#include <mfapi.h>
-#include <mfidl.h>
-#include <QtCore/qlist.h>
-#include <QtCore/qmutex.h>
-#include <QtMultimedia/qvideosurfaceformat.h>
-
-QT_USE_NAMESPACE
-
-class MFVideoProbeControl;
-
-class MFTransform: public IMFTransform
-{
-public:
- MFTransform();
- ~MFTransform();
-
- void addProbe(MFVideoProbeControl* probe);
- void removeProbe(MFVideoProbeControl* probe);
-
- void setVideoSink(IUnknown *videoSink);
-
- // IUnknown methods
- STDMETHODIMP QueryInterface(REFIID iid, void** ppv);
- STDMETHODIMP_(ULONG) AddRef();
- STDMETHODIMP_(ULONG) Release();
-
- // IMFTransform methods
- STDMETHODIMP GetStreamLimits(DWORD *pdwInputMinimum, DWORD *pdwInputMaximum, DWORD *pdwOutputMinimum, DWORD *pdwOutputMaximum);
- STDMETHODIMP GetStreamCount(DWORD *pcInputStreams, DWORD *pcOutputStreams);
- STDMETHODIMP GetStreamIDs(DWORD dwInputIDArraySize, DWORD *pdwInputIDs, DWORD dwOutputIDArraySize, DWORD *pdwOutputIDs);
- STDMETHODIMP GetInputStreamInfo(DWORD dwInputStreamID, MFT_INPUT_STREAM_INFO *pStreamInfo);
- STDMETHODIMP GetOutputStreamInfo(DWORD dwOutputStreamID, MFT_OUTPUT_STREAM_INFO *pStreamInfo);
- STDMETHODIMP GetAttributes(IMFAttributes **pAttributes);
- STDMETHODIMP GetInputStreamAttributes(DWORD dwInputStreamID, IMFAttributes **pAttributes);
- STDMETHODIMP GetOutputStreamAttributes(DWORD dwOutputStreamID, IMFAttributes **pAttributes);
- STDMETHODIMP DeleteInputStream(DWORD dwStreamID);
- STDMETHODIMP AddInputStreams(DWORD cStreams, DWORD *adwStreamIDs);
- STDMETHODIMP GetInputAvailableType(DWORD dwInputStreamID, DWORD dwTypeIndex, IMFMediaType **ppType);
- STDMETHODIMP GetOutputAvailableType(DWORD dwOutputStreamID,DWORD dwTypeIndex, IMFMediaType **ppType);
- STDMETHODIMP SetInputType(DWORD dwInputStreamID, IMFMediaType *pType, DWORD dwFlags);
- STDMETHODIMP SetOutputType(DWORD dwOutputStreamID, IMFMediaType *pType, DWORD dwFlags);
- STDMETHODIMP GetInputCurrentType(DWORD dwInputStreamID, IMFMediaType **ppType);
- STDMETHODIMP GetOutputCurrentType(DWORD dwOutputStreamID, IMFMediaType **ppType);
- STDMETHODIMP GetInputStatus(DWORD dwInputStreamID, DWORD *pdwFlags);
- STDMETHODIMP GetOutputStatus(DWORD *pdwFlags);
- STDMETHODIMP SetOutputBounds(LONGLONG hnsLowerBound, LONGLONG hnsUpperBound);
- STDMETHODIMP ProcessEvent(DWORD dwInputStreamID, IMFMediaEvent *pEvent);
- STDMETHODIMP ProcessMessage(MFT_MESSAGE_TYPE eMessage, ULONG_PTR ulParam);
- STDMETHODIMP ProcessInput(DWORD dwInputStreamID, IMFSample *pSample, DWORD dwFlags);
- STDMETHODIMP ProcessOutput(DWORD dwFlags, DWORD cOutputBufferCount, MFT_OUTPUT_DATA_BUFFER *pOutputSamples, DWORD *pdwStatus);
-
-private:
- HRESULT OnFlush();
- static QVideoFrame::PixelFormat formatFromSubtype(const GUID& subtype);
- static QVideoSurfaceFormat 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;
-
- QVideoSurfaceFormat m_format;
- int m_bytesPerLine;
-};
-
-#endif
diff --git a/src/plugins/wmf/player/mfvideoprobecontrol.cpp b/src/plugins/wmf/player/mfvideoprobecontrol.cpp
deleted file mode 100644
index 13d9a1bfe..000000000
--- a/src/plugins/wmf/player/mfvideoprobecontrol.cpp
+++ /dev/null
@@ -1,54 +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 "mfvideoprobecontrol.h"
-
-MFVideoProbeControl::MFVideoProbeControl(QObject *parent)
- : QMediaVideoProbeControl(parent)
-{
-}
-
-MFVideoProbeControl::~MFVideoProbeControl()
-{
-}
-
-void MFVideoProbeControl::bufferProbed(const QVideoFrame& frame)
-{
- QMetaObject::invokeMethod(this, "videoFrameProbed", Qt::QueuedConnection, Q_ARG(QVideoFrame, frame));
-}
diff --git a/src/plugins/wmf/player/mfvideoprobecontrol.h b/src/plugins/wmf/player/mfvideoprobecontrol.h
deleted file mode 100644
index 2b238725a..000000000
--- a/src/plugins/wmf/player/mfvideoprobecontrol.h
+++ /dev/null
@@ -1,59 +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 MFVIDEOPROBECONTROL_H
-#define MFVIDEOPROBECONTROL_H
-
-#include <qmediavideoprobecontrol.h>
-#include <QtCore/qmutex.h>
-#include <qvideoframe.h>
-
-QT_USE_NAMESPACE
-
-class MFVideoProbeControl : public QMediaVideoProbeControl
-{
- Q_OBJECT
-public:
- explicit MFVideoProbeControl(QObject *parent);
- virtual ~MFVideoProbeControl();
-
- void bufferProbed(const QVideoFrame& frame);
-};
-
-#endif
diff --git a/src/plugins/wmf/player/mfvideorenderercontrol.cpp b/src/plugins/wmf/player/mfvideorenderercontrol.cpp
deleted file mode 100644
index 7b121255f..000000000
--- a/src/plugins/wmf/player/mfvideorenderercontrol.cpp
+++ /dev/null
@@ -1,2426 +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:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "mfvideorenderercontrol.h"
-#include "mfactivate.h"
-
-#include "evrcustompresenter.h"
-
-#include <qabstractvideosurface.h>
-#include <qvideosurfaceformat.h>
-#include <qtcore/qtimer.h>
-#include <qtcore/qmutex.h>
-#include <qtcore/qcoreevent.h>
-#include <qtcore/qcoreapplication.h>
-#include <qtcore/qthread.h>
-#include "guiddef.h"
-#include <qtcore/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(NoHandle)
- , m_buffer(buffer)
- , m_bytesPerLine(bytesPerLine)
- , m_mapMode(NotMapped)
- {
- buffer->AddRef();
- }
-
- ~MediaSampleVideoBuffer()
- {
- m_buffer->Release();
- }
-
- uchar *map(MapMode mode, int *numBytes, int *bytesPerLine)
- {
- if (m_mapMode == NotMapped && mode != NotMapped) {
- BYTE *bytes;
- DWORD length;
- HRESULT hr = m_buffer->Lock(&bytes, NULL, &length);
- if (SUCCEEDED(hr)) {
- if (numBytes)
- *numBytes = int(length);
-
- if (bytesPerLine)
- *bytesPerLine = m_bytesPerLine;
-
- m_mapMode = mode;
- return reinterpret_cast<uchar *>(bytes);
- } else {
- qWarning("Faild to lock mf buffer!");
- }
- }
- return 0;
- }
-
- void unmap()
- {
- if (m_mapMode == NotMapped)
- return;
- m_mapMode = NotMapped;
- m_buffer->Unlock();
- }
-
- MapMode mapMode() const
- {
- return m_mapMode;
- }
-
- private:
- IMFMediaBuffer *m_buffer;
- int m_bytesPerLine;
- MapMode m_mapMode;
- };
-
- // Custom interface for handling IMFStreamSink::PlaceMarker calls asynchronously.
- 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)
- {
- if (!ppv)
- return E_POINTER;
- if (iid == IID_IUnknown) {
- *ppv = static_cast<IUnknown*>(this);
- } else if (iid == __uuidof(IMarker)) {
- *ppv = static_cast<IMarker*>(this);
- } else {
- *ppv = NULL;
- return E_NOINTERFACE;
- }
- AddRef();
- return S_OK;
- }
-
- STDMETHODIMP_(ULONG) AddRef()
- {
- return InterlockedIncrement(&m_cRef);
- }
-
- STDMETHODIMP_(ULONG) Release()
- {
- 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)
- {
- if (pType == NULL)
- return E_POINTER;
- *pType = m_eMarkerType;
- return S_OK;
- }
-
- STDMETHODIMP GetMarkerValue(PROPVARIANT *pvar)
- {
- if (pvar == NULL)
- return E_POINTER;
- return PropVariantCopy(pvar, &m_varMarkerValue);
- }
-
- STDMETHODIMP GetContext(PROPVARIANT *pvar)
- {
- 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;
-
- Marker(MFSTREAMSINK_MARKER_TYPE eMarkerType) : m_cRef(1), 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_cRef(1)
- , m_eventQueue(0)
- , m_shutdown(false)
- , m_surface(0)
- , m_state(State_TypeNotSet)
- , m_currentFormatIndex(-1)
- , m_bytesPerLine(0)
- , m_workQueueId(0)
- , m_workQueueCB(this, &MediaStream::onDispatchWorkItem)
- , m_finalizeResult(0)
- , m_scheduledBuffer(0)
- , m_bufferStartTime(-1)
- , m_bufferDuration(-1)
- , m_presentationClock(0)
- , m_sampleRequested(false)
- , m_currentMediaType(0)
- , m_prerolling(false)
- , m_prerollTargetTime(0)
- , m_startTime(0)
- , m_rendererControl(rendererControl)
- , m_rate(1.f)
- {
- 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)
- {
- 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)
- {
- return InterlockedIncrement(&m_cRef);
- }
-
- STDMETHODIMP_(ULONG) Release(void)
- {
- 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)
- {
- // 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)
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- return m_eventQueue->BeginGetEvent(pCallback, punkState);
- }
-
- STDMETHODIMP EndGetEvent(
- IMFAsyncResult *pResult,
- IMFMediaEvent **ppEvent)
- {
- 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)
- {
-#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)
- {
- 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)
- {
- *pdwIdentifier = MediaStream::DEFAULT_MEDIA_STREAM_ID;
- return S_OK;
- }
-
- STDMETHODIMP GetMediaTypeHandler(
- IMFMediaTypeHandler **ppHandler)
- {
- LPVOID handler = NULL;
- HRESULT hr = QueryInterface(IID_IMFMediaTypeHandler, &handler);
- *ppHandler = (IMFMediaTypeHandler*)(handler);
- return hr;
- }
-
- STDMETHODIMP ProcessSample(
- IMFSample *pSample)
- {
- 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)
- {
- 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)
- {
-#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)
- {
- 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)
- {
- if (pdwTypeCount == NULL)
- return E_INVALIDARG;
- QMutexLocker locker(&m_mutex);
- *pdwTypeCount = DWORD(m_mediaTypes.size());
- return S_OK;
- }
-
- STDMETHODIMP GetMediaTypeByIndex(
- DWORD dwIndex,
- IMFMediaType **ppType)
- {
- 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)
- {
- 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));
- QVideoSurfaceFormat 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)
- {
- 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)
- {
- if (pguidMajorType == NULL)
- return E_INVALIDARG;
- *pguidMajorType = MFMediaType_Video;
- return S_OK;
- }
-
- //
- void setSurface(QAbstractVideoSurface *surface)
- {
- m_mutex.lock();
- m_surface = surface;
- 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 != 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_surface)
- return;
- const QList<QVideoFrame::PixelFormat> formats = m_surface->supportedPixelFormats();
- for (QVideoFrame::PixelFormat format : formats) {
- 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 QVideoFrame::Format_ARGB32:
- case QVideoFrame::Format_ARGB32_Premultiplied:
- mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_ARGB32);
- break;
- case QVideoFrame::Format_RGB32:
- mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
- break;
- case QVideoFrame::Format_BGR24: // MFVideoFormat_RGB24 has a BGR layout
- mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB24);
- break;
- case QVideoFrame::Format_RGB565:
- mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB565);
- break;
- case QVideoFrame::Format_RGB555:
- mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB555);
- break;
- case QVideoFrame::Format_AYUV444:
- case QVideoFrame::Format_AYUV444_Premultiplied:
- mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_AYUV);
- break;
- case QVideoFrame::Format_YUV420P:
- mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_I420);
- break;
- case QVideoFrame::Format_UYVY:
- mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_UYVY);
- break;
- case QVideoFrame::Format_YV12:
- mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YV12);
- break;
- case QVideoFrame::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.frameSize(),
- m_surfaceFormat.pixelFormat());
- frame.setStartTime(m_bufferStartTime * 0.1);
- frame.setEndTime((m_bufferStartTime + m_bufferDuration) * 0.1);
- m_surface->present(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
- {
- StartSurface = QEvent::User,
- StopSurface,
- FlushSurface,
- 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:
- void customEvent(QEvent *event)
- {
- QMutexLocker locker(&m_mutex);
- if (event->type() == StartSurface) {
- if (m_state == State_WaitForSurfaceStart) {
- m_startResult = startSurface();
- queueAsyncOperation(OpStart);
- }
- } else if (event->type() == StopSurface) {
- stopSurface();
- } else {
- QObject::customEvent(event);
- }
- }
- HRESULT m_startResult;
-
- private:
- HRESULT startSurface()
- {
- if (!m_surface->isFormatSupported(m_surfaceFormat))
- return S_FALSE;
- if (!m_surface->start(m_surfaceFormat))
- return S_FALSE;
- return S_OK;
- }
-
- void stopSurface()
- {
- m_surface->stop();
- }
-
- 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_cRef(1), m_op(op)
- {
- }
-
- StreamOperation m_op; // The operation to perform.
-
- //from IUnknown
- STDMETHODIMP QueryInterface(REFIID iid, void** ppv)
- {
- 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()
- {
- return InterlockedIncrement(&m_cRef);
- }
- STDMETHODIMP_(ULONG) Release()
- {
- ULONG uCount = InterlockedDecrement(&m_cRef);
- if (uCount == 0)
- delete this;
- // For thread safety, return a temporary variable.
- return uCount;
- }
-
- private:
- long m_cRef;
- 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;
- QMutex m_mutex;
-
- IMFMediaType *m_currentMediaType;
- State m_state;
- IMFMediaSink *m_sink;
- IMFMediaEventQueue *m_eventQueue;
- DWORD m_workQueueId;
- AsyncCallback<MediaStream> m_workQueueCB;
- QList<IUnknown*> m_sampleQueue;
- IMFAsyncResult *m_finalizeResult; // Result object for Finalize operation.
- MFTIME m_startTime; // Presentation time when the clock started.
-
- bool m_shutdown;
- QList<IMFMediaType*> m_mediaTypes;
- QList<QVideoFrame::PixelFormat> m_pixelFormats;
- int m_currentFormatIndex;
- int m_bytesPerLine;
- QVideoSurfaceFormat m_surfaceFormat;
- QAbstractVideoSurface* m_surface;
- MFVideoRendererControl *m_rendererControl;
-
- 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 QVideoSurfaceFormat &format)
- {
- switch (format.pixelFormat()) {
- // 32 bpp packed formats.
- case QVideoFrame::Format_RGB32:
- case QVideoFrame::Format_AYUV444:
- return format.frameWidth() * 4;
- // 24 bpp packed formats.
- case QVideoFrame::Format_RGB24:
- case QVideoFrame::Format_BGR24:
- return PAD_TO_DWORD(format.frameWidth() * 3);
- // 16 bpp packed formats.
- case QVideoFrame::Format_RGB565:
- case QVideoFrame::Format_RGB555:
- case QVideoFrame::Format_YUYV:
- case QVideoFrame::Format_UYVY:
- return PAD_TO_DWORD(format.frameWidth() * 2);
- // Planar formats.
- case QVideoFrame::Format_IMC1:
- case QVideoFrame::Format_IMC2:
- case QVideoFrame::Format_IMC3:
- case QVideoFrame::Format_IMC4:
- case QVideoFrame::Format_YV12:
- case QVideoFrame::Format_NV12:
- case QVideoFrame::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);
- if (m_state == State_WaitForSurfaceStart) {
- hr = m_startResult;
- m_state = State_Started;
- } else if (!m_surface->isActive()) {
- if (thread() == QThread::currentThread()) {
- hr = startSurface();
- }
- else {
- m_state = State_WaitForSurfaceStart;
- QCoreApplication::postEvent(m_rendererControl, new QChildEvent(QEvent::Type(StartSurface), this));
- break;
- }
- }
-
- if (m_state == State_Started)
- schedulePresentation(true);
- 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);
- if (m_surface->isActive()) {
- if (thread() == QThread::currentThread()) {
- stopSurface();
- }
- else {
- QCoreApplication::postEvent(m_rendererControl, new QChildEvent(QEvent::Type(StopSurface), this));
- }
- }
- 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;
- }
- }
-
- 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(__uuidof(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;
- bool m_prerolling;
-
- 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;
- MFTIME m_bufferStartTime;
- MFTIME m_bufferDuration;
- IMFPresentationClock *m_presentationClock;
- bool m_sampleRequested;
- float m_rate;
- };
-
- 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_cRef(1)
- , m_shutdown(false)
- , m_presentationClock(0)
- , m_playRate(1)
- {
- m_stream = new MediaStream(this, rendererControl);
- }
-
- ~MediaSink()
- {
- Q_ASSERT(m_shutdown);
- }
-
- void setSurface(QAbstractVideoSurface *surface)
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return;
- m_stream->setSurface(surface);
- }
-
- void supportedFormatsChanged()
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return;
- m_stream->supportedFormatsChanged();
- }
-
- 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)
- {
- 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)
- {
- return InterlockedIncrement(&m_cRef);
- }
-
- STDMETHODIMP_(ULONG) Release(void)
- {
- 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)
- {
- 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)
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- return m_stream->startPreroll(hnsUpcomingStartTime);
- }
-
- //from IMFFinalizableMediaSink
- STDMETHODIMP BeginFinalize(IMFAsyncCallback *pCallback, IUnknown *punkState)
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- return m_stream->finalize(pCallback, punkState);
- }
-
- STDMETHODIMP EndFinalize(IMFAsyncResult *pResult)
- {
- 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)
- {
- 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 **)
- {
- QMutexLocker locker(&m_mutex);
- return m_shutdown ? MF_E_SHUTDOWN : MF_E_STREAMSINKS_FIXED;
- }
-
- STDMETHODIMP RemoveStreamSink(
- DWORD)
- {
- QMutexLocker locker(&m_mutex);
- return m_shutdown ? MF_E_SHUTDOWN : MF_E_STREAMSINKS_FIXED;
- }
-
- STDMETHODIMP GetStreamSinkCount(
- DWORD *pcStreamSinkCount)
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- *pcStreamSinkCount = 1;
- return S_OK;
- }
-
- STDMETHODIMP GetStreamSinkByIndex(
- DWORD dwIndex,
- IMFStreamSink **ppStreamSink)
- {
- 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)
- {
- 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)
- {
- 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)
- {
- 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)
- {
- 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)
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- return m_stream->start(llClockStartOffset);
- }
-
- STDMETHODIMP OnClockStop(MFTIME)
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- return m_stream->stop();
- }
-
- STDMETHODIMP OnClockPause(MFTIME)
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- return m_stream->pause();
- }
-
- STDMETHODIMP OnClockRestart(MFTIME)
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- return m_stream->restart();
- }
-
- STDMETHODIMP OnClockSetRate(MFTIME, float flRate)
- {
- 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)
- {
- 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)
- {
- 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)
- {
- 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;
- QMutex m_mutex;
- bool m_shutdown;
- IMFPresentationClock *m_presentationClock;
- MediaStream *m_stream;
- float m_playRate;
- };
-
- class VideoRendererActivate : public IMFActivate
- {
- public:
- VideoRendererActivate(MFVideoRendererControl *rendererControl)
- : m_cRef(1)
- , m_sink(0)
- , m_rendererControl(rendererControl)
- , m_attributes(0)
- , m_surface(0)
- {
- MFCreateAttributes(&m_attributes, 0);
- m_sink = new MediaSink(rendererControl);
- }
-
- ~VideoRendererActivate()
- {
- m_attributes->Release();
- }
-
- //from IUnknown
- STDMETHODIMP QueryInterface(REFIID riid, void** 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;
- }
-
- STDMETHODIMP_(ULONG) AddRef(void)
- {
- return InterlockedIncrement(&m_cRef);
- }
-
- STDMETHODIMP_(ULONG) Release(void)
- {
- 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)
- {
- if (!ppv)
- return E_INVALIDARG;
- QMutexLocker locker(&m_mutex);
- if (!m_sink) {
- m_sink = new MediaSink(m_rendererControl);
- if (m_surface)
- m_sink->setSurface(m_surface);
- }
- return m_sink->QueryInterface(riid, ppv);
- }
-
- STDMETHODIMP ShutdownObject(void)
- {
- 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)
- {
- QMutexLocker locker(&m_mutex);
- if (m_sink) {
- m_sink->Release();
- m_sink = NULL;
- }
- return S_OK;
- }
-
- //from IMFAttributes
- STDMETHODIMP GetItem(
- REFGUID guidKey,
- PROPVARIANT *pValue)
- {
- return m_attributes->GetItem(guidKey, pValue);
- }
-
- STDMETHODIMP GetItemType(
- REFGUID guidKey,
- MF_ATTRIBUTE_TYPE *pType)
- {
- return m_attributes->GetItemType(guidKey, pType);
- }
-
- STDMETHODIMP CompareItem(
- REFGUID guidKey,
- REFPROPVARIANT Value,
- BOOL *pbResult)
- {
- return m_attributes->CompareItem(guidKey, Value, pbResult);
- }
-
- STDMETHODIMP Compare(
- IMFAttributes *pTheirs,
- MF_ATTRIBUTES_MATCH_TYPE MatchType,
- BOOL *pbResult)
- {
- return m_attributes->Compare(pTheirs, MatchType, pbResult);
- }
-
- STDMETHODIMP GetUINT32(
- REFGUID guidKey,
- UINT32 *punValue)
- {
- return m_attributes->GetUINT32(guidKey, punValue);
- }
-
- STDMETHODIMP GetUINT64(
- REFGUID guidKey,
- UINT64 *punValue)
- {
- return m_attributes->GetUINT64(guidKey, punValue);
- }
-
- STDMETHODIMP GetDouble(
- REFGUID guidKey,
- double *pfValue)
- {
- return m_attributes->GetDouble(guidKey, pfValue);
- }
-
- STDMETHODIMP GetGUID(
- REFGUID guidKey,
- GUID *pguidValue)
- {
- return m_attributes->GetGUID(guidKey, pguidValue);
- }
-
- STDMETHODIMP GetStringLength(
- REFGUID guidKey,
- UINT32 *pcchLength)
- {
- return m_attributes->GetStringLength(guidKey, pcchLength);
- }
-
- STDMETHODIMP GetString(
- REFGUID guidKey,
- LPWSTR pwszValue,
- UINT32 cchBufSize,
- UINT32 *pcchLength)
- {
- return m_attributes->GetString(guidKey, pwszValue, cchBufSize, pcchLength);
- }
-
- STDMETHODIMP GetAllocatedString(
- REFGUID guidKey,
- LPWSTR *ppwszValue,
- UINT32 *pcchLength)
- {
- return m_attributes->GetAllocatedString(guidKey, ppwszValue, pcchLength);
- }
-
- STDMETHODIMP GetBlobSize(
- REFGUID guidKey,
- UINT32 *pcbBlobSize)
- {
- return m_attributes->GetBlobSize(guidKey, pcbBlobSize);
- }
-
- STDMETHODIMP GetBlob(
- REFGUID guidKey,
- UINT8 *pBuf,
- UINT32 cbBufSize,
- UINT32 *pcbBlobSize)
- {
- return m_attributes->GetBlob(guidKey, pBuf, cbBufSize, pcbBlobSize);
- }
-
- STDMETHODIMP GetAllocatedBlob(
- REFGUID guidKey,
- UINT8 **ppBuf,
- UINT32 *pcbSize)
- {
- return m_attributes->GetAllocatedBlob(guidKey, ppBuf, pcbSize);
- }
-
- STDMETHODIMP GetUnknown(
- REFGUID guidKey,
- REFIID riid,
- LPVOID *ppv)
- {
- return m_attributes->GetUnknown(guidKey, riid, ppv);
- }
-
- STDMETHODIMP SetItem(
- REFGUID guidKey,
- REFPROPVARIANT Value)
- {
- return m_attributes->SetItem(guidKey, Value);
- }
-
- STDMETHODIMP DeleteItem(
- REFGUID guidKey)
- {
- return m_attributes->DeleteItem(guidKey);
- }
-
- STDMETHODIMP DeleteAllItems(void)
- {
- return m_attributes->DeleteAllItems();
- }
-
- STDMETHODIMP SetUINT32(
- REFGUID guidKey,
- UINT32 unValue)
- {
- return m_attributes->SetUINT32(guidKey, unValue);
- }
-
- STDMETHODIMP SetUINT64(
- REFGUID guidKey,
- UINT64 unValue)
- {
- return m_attributes->SetUINT64(guidKey, unValue);
- }
-
- STDMETHODIMP SetDouble(
- REFGUID guidKey,
- double fValue)
- {
- return m_attributes->SetDouble(guidKey, fValue);
- }
-
- STDMETHODIMP SetGUID(
- REFGUID guidKey,
- REFGUID guidValue)
- {
- return m_attributes->SetGUID(guidKey, guidValue);
- }
-
- STDMETHODIMP SetString(
- REFGUID guidKey,
- LPCWSTR wszValue)
- {
- return m_attributes->SetString(guidKey, wszValue);
- }
-
- STDMETHODIMP SetBlob(
- REFGUID guidKey,
- const UINT8 *pBuf,
- UINT32 cbBufSize)
- {
- return m_attributes->SetBlob(guidKey, pBuf, cbBufSize);
- }
-
- STDMETHODIMP SetUnknown(
- REFGUID guidKey,
- IUnknown *pUnknown)
- {
- return m_attributes->SetUnknown(guidKey, pUnknown);
- }
-
- STDMETHODIMP LockStore(void)
- {
- return m_attributes->LockStore();
- }
-
- STDMETHODIMP UnlockStore(void)
- {
- return m_attributes->UnlockStore();
- }
-
- STDMETHODIMP GetCount(
- UINT32 *pcItems)
- {
- return m_attributes->GetCount(pcItems);
- }
-
- STDMETHODIMP GetItemByIndex(
- UINT32 unIndex,
- GUID *pguidKey,
- PROPVARIANT *pValue)
- {
- return m_attributes->GetItemByIndex(unIndex, pguidKey, pValue);
- }
-
- STDMETHODIMP CopyAllItems(
- IMFAttributes *pDest)
- {
- return m_attributes->CopyAllItems(pDest);
- }
-
- /////////////////////////////////
- void setSurface(QAbstractVideoSurface *surface)
- {
- QMutexLocker locker(&m_mutex);
- if (m_surface == surface)
- return;
-
- m_surface = surface;
-
- if (!m_sink)
- return;
- m_sink->setSurface(m_surface);
- }
-
- void supportedFormatsChanged()
- {
- QMutexLocker locker(&m_mutex);
- if (!m_sink)
- return;
- m_sink->supportedFormatsChanged();
- }
-
- 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;
- QAbstractVideoSurface *m_surface;
- QMutex m_mutex;
- };
-}
-
-
-class EVRCustomPresenterActivate : public MFAbstractActivate
-{
-public:
- EVRCustomPresenterActivate();
- ~EVRCustomPresenterActivate()
- { }
-
- STDMETHODIMP ActivateObject(REFIID riid, void **ppv);
- STDMETHODIMP ShutdownObject();
- STDMETHODIMP DetachObject();
-
- void setSurface(QAbstractVideoSurface *surface);
-
-private:
- EVRCustomPresenter *m_presenter;
- QAbstractVideoSurface *m_surface;
- QMutex m_mutex;
-};
-
-
-MFVideoRendererControl::MFVideoRendererControl(QObject *parent)
- : QVideoRendererControl(parent)
- , m_surface(0)
- , m_currentActivate(0)
- , m_callback(0)
- , m_presenterActivate(0)
-{
-}
-
-MFVideoRendererControl::~MFVideoRendererControl()
-{
- clear();
-}
-
-void MFVideoRendererControl::clear()
-{
- if (m_surface)
- m_surface->stop();
-
- if (m_presenterActivate) {
- m_presenterActivate->ShutdownObject();
- m_presenterActivate->Release();
- m_presenterActivate = NULL;
- }
-
- if (m_currentActivate) {
- m_currentActivate->ShutdownObject();
- m_currentActivate->Release();
- }
- m_currentActivate = NULL;
-}
-
-void MFVideoRendererControl::releaseActivate()
-{
- clear();
-}
-
-QAbstractVideoSurface *MFVideoRendererControl::surface() const
-{
- return m_surface;
-}
-
-void MFVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
-{
- if (m_surface)
- disconnect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(supportedFormatsChanged()));
- m_surface = surface;
-
- if (m_surface) {
- connect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(supportedFormatsChanged()));
- }
-
- if (m_presenterActivate)
- m_presenterActivate->setSurface(m_surface);
- else if (m_currentActivate)
- static_cast<VideoRendererActivate*>(m_currentActivate)->setSurface(m_surface);
-}
-
-void MFVideoRendererControl::customEvent(QEvent *event)
-{
- if (m_presenterActivate)
- return;
-
- if (!m_currentActivate)
- return;
-
- if (event->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;
- }
- if (event->type() >= MediaStream::StartSurface) {
- QChildEvent *childEvent = static_cast<QChildEvent*>(event);
- static_cast<MediaStream*>(childEvent->child())->customEvent(event);
- } else {
- QObject::customEvent(event);
- }
-}
-
-void MFVideoRendererControl::supportedFormatsChanged()
-{
- if (m_presenterActivate)
- return;
-
- if (m_currentActivate)
- static_cast<VideoRendererActivate*>(m_currentActivate)->supportedFormatsChanged();
-}
-
-void MFVideoRendererControl::present()
-{
- if (m_presenterActivate)
- return;
-
- if (m_currentActivate)
- static_cast<VideoRendererActivate*>(m_currentActivate)->present();
-}
-
-IMFActivate* MFVideoRendererControl::createActivate()
-{
- Q_ASSERT(m_surface);
-
- clear();
-
- // Create the EVR media sink, but replace the presenter with our own
- if (SUCCEEDED(MFCreateVideoRendererActivate(::GetShellWindow(), &m_currentActivate))) {
- m_presenterActivate = new EVRCustomPresenterActivate;
- m_currentActivate->SetUnknown(MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_ACTIVATE, m_presenterActivate);
- } else {
- m_currentActivate = new VideoRendererActivate(this);
- }
-
- setSurface(m_surface);
-
- return m_currentActivate;
-}
-
-
-EVRCustomPresenterActivate::EVRCustomPresenterActivate()
- : MFAbstractActivate()
- , m_presenter(0)
- , m_surface(0)
-{ }
-
-HRESULT EVRCustomPresenterActivate::ActivateObject(REFIID riid, void **ppv)
-{
- if (!ppv)
- return E_INVALIDARG;
- QMutexLocker locker(&m_mutex);
- if (!m_presenter) {
- m_presenter = new EVRCustomPresenter;
- if (m_surface)
- m_presenter->setSurface(m_surface);
- }
- return m_presenter->QueryInterface(riid, ppv);
-}
-
-HRESULT EVRCustomPresenterActivate::ShutdownObject()
-{
- // The presenter does not implement IMFShutdown so
- // this function is the same as DetachObject()
- return DetachObject();
-}
-
-HRESULT EVRCustomPresenterActivate::DetachObject()
-{
- QMutexLocker locker(&m_mutex);
- if (m_presenter) {
- m_presenter->Release();
- m_presenter = 0;
- }
- return S_OK;
-}
-
-void EVRCustomPresenterActivate::setSurface(QAbstractVideoSurface *surface)
-{
- QMutexLocker locker(&m_mutex);
- if (m_surface == surface)
- return;
-
- m_surface = surface;
-
- if (m_presenter)
- m_presenter->setSurface(surface);
-}
-
-#include "moc_mfvideorenderercontrol.cpp"
-#include "mfvideorenderercontrol.moc"
diff --git a/src/plugins/wmf/player/mfvideorenderercontrol.h b/src/plugins/wmf/player/mfvideorenderercontrol.h
deleted file mode 100644
index da9e97ba9..000000000
--- a/src/plugins/wmf/player/mfvideorenderercontrol.h
+++ /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:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 MFVIDEORENDERERCONTROL_H
-#define MFVIDEORENDERERCONTROL_H
-
-#include "qvideorenderercontrol.h"
-#include <mfapi.h>
-#include <mfidl.h>
-
-QT_USE_NAMESPACE
-
-class EVRCustomPresenterActivate;
-
-class MFVideoRendererControl : public QVideoRendererControl
-{
- Q_OBJECT
-public:
- MFVideoRendererControl(QObject *parent = 0);
- ~MFVideoRendererControl();
-
- QAbstractVideoSurface *surface() const;
- void setSurface(QAbstractVideoSurface *surface);
-
- IMFActivate* createActivate();
- void releaseActivate();
-
-protected:
- void customEvent(QEvent *event);
-
-private Q_SLOTS:
- void supportedFormatsChanged();
- void present();
-
-private:
- void clear();
-
- QAbstractVideoSurface *m_surface;
- IMFActivate *m_currentActivate;
- IMFSampleGrabberSinkCallback *m_callback;
-
- EVRCustomPresenterActivate *m_presenterActivate;
-};
-
-#endif
diff --git a/src/plugins/wmf/player/player.pri b/src/plugins/wmf/player/player.pri
deleted file mode 100644
index af2f7458b..000000000
--- a/src/plugins/wmf/player/player.pri
+++ /dev/null
@@ -1,34 +0,0 @@
-INCLUDEPATH += $$PWD
-
-LIBS += -lgdi32 -luser32
-QMAKE_USE += wmf
-
-HEADERS += \
- $$PWD/mfplayerservice.h \
- $$PWD/mfplayersession.h \
- $$PWD/mfplayercontrol.h \
- $$PWD/mfvideorenderercontrol.h \
- $$PWD/mfaudioendpointcontrol.h \
- $$PWD/mfmetadatacontrol.h \
- $$PWD/mfaudioprobecontrol.h \
- $$PWD/mfvideoprobecontrol.h \
- $$PWD/mfevrvideowindowcontrol.h \
- $$PWD/samplegrabber.h \
- $$PWD/mftvideo.h \
- $$PWD/mfactivate.h
-
-SOURCES += \
- $$PWD/mfplayerservice.cpp \
- $$PWD/mfplayersession.cpp \
- $$PWD/mfplayercontrol.cpp \
- $$PWD/mfvideorenderercontrol.cpp \
- $$PWD/mfaudioendpointcontrol.cpp \
- $$PWD/mfmetadatacontrol.cpp \
- $$PWD/mfaudioprobecontrol.cpp \
- $$PWD/mfvideoprobecontrol.cpp \
- $$PWD/mfevrvideowindowcontrol.cpp \
- $$PWD/samplegrabber.cpp \
- $$PWD/mftvideo.cpp \
- $$PWD/mfactivate.cpp
-
-include($$PWD/../../common/evr.pri)
diff --git a/src/plugins/wmf/player/samplegrabber.cpp b/src/plugins/wmf/player/samplegrabber.cpp
deleted file mode 100644
index d137335f3..000000000
--- a/src/plugins/wmf/player/samplegrabber.cpp
+++ /dev/null
@@ -1,173 +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.h"
-#include "mfaudioprobecontrol.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);
-
- 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/wmf/player/samplegrabber.h b/src/plugins/wmf/player/samplegrabber.h
deleted file mode 100644
index 9ca673a1b..000000000
--- a/src/plugins/wmf/player/samplegrabber.h
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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
-
-#include <QtCore/qmutex.h>
-#include <QtCore/qlist.h>
-#include <QtMultimedia/qaudioformat.h>
-#include <mfapi.h>
-#include <mfidl.h>
-
-class MFAudioProbeControl;
-
-class SampleGrabberCallback : public IMFSampleGrabberSinkCallback
-{
-public:
- // IUnknown methods
- STDMETHODIMP QueryInterface(REFIID iid, void** ppv);
- STDMETHODIMP_(ULONG) AddRef();
- STDMETHODIMP_(ULONG) Release();
-
- // IMFClockStateSink methods
- STDMETHODIMP OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset);
- STDMETHODIMP OnClockStop(MFTIME hnsSystemTime);
- STDMETHODIMP OnClockPause(MFTIME hnsSystemTime);
- STDMETHODIMP OnClockRestart(MFTIME hnsSystemTime);
- STDMETHODIMP OnClockSetRate(MFTIME hnsSystemTime, float flRate);
-
- // IMFSampleGrabberSinkCallback methods
- STDMETHODIMP OnSetPresentationClock(IMFPresentationClock* pClock);
- STDMETHODIMP OnShutdown();
-
-protected:
- SampleGrabberCallback() : m_cRef(1) {}
-
-public:
- virtual ~SampleGrabberCallback() {}
-
-private:
- long m_cRef;
-};
-
-class AudioSampleGrabberCallback: public SampleGrabberCallback {
-public:
- void addProbe(MFAudioProbeControl* probe);
- void removeProbe(MFAudioProbeControl* probe);
- void setFormat(const QAudioFormat& format);
-
- STDMETHODIMP OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags,
- LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer,
- DWORD dwSampleSize);
-
-private:
- QList<MFAudioProbeControl*> m_audioProbes;
- QMutex m_audioProbeMutex;
- QAudioFormat m_format;
-};
-
-#endif // SAMPLEGRABBER_H
diff --git a/src/plugins/wmf/sourceresolver.cpp b/src/plugins/wmf/sourceresolver.cpp
deleted file mode 100644
index 15ef6f0ab..000000000
--- a/src/plugins/wmf/sourceresolver.cpp
+++ /dev/null
@@ -1,325 +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 "mfstream.h"
-#include "sourceresolver.h"
-#include <mferror.h>
-#include <nserror.h>
-#include <QtCore/qfile.h>
-#include <QtCore/qdebug.h>
-#include <QtMultimedia/qmediaplayer.h>
-
-/*
- 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,
- and it will emit mediaSourceReady() when resolving is done. You can call SourceResolver::cancel to
- stop the previous load operation if there is any.
-*/
-
-SourceResolver::SourceResolver()
- : m_cRef(1)
- , m_cancelCookie(0)
- , m_sourceResolver(0)
- , m_mediaSource(0)
- , m_stream(0)
-{
-}
-
-SourceResolver::~SourceResolver()
-{
- shutdown();
- if (m_mediaSource) {
- m_mediaSource->Release();
- m_mediaSource = NULL;
- }
-
- if (m_cancelCookie)
- m_cancelCookie->Release();
- if (m_sourceResolver)
- m_sourceResolver->Release();
-}
-
-STDMETHODIMP SourceResolver::QueryInterface(REFIID riid, LPVOID *ppvObject)
-{
- if (!ppvObject)
- return E_POINTER;
- if (riid == IID_IUnknown) {
- *ppvObject = static_cast<IUnknown*>(this);
- } else if (riid == IID_IMFAsyncCallback) {
- *ppvObject = static_cast<IMFAsyncCallback*>(this);
- } else {
- *ppvObject = NULL;
- return E_NOINTERFACE;
- }
- AddRef();
- return S_OK;
-}
-
-STDMETHODIMP_(ULONG) SourceResolver::AddRef(void)
-{
- return InterlockedIncrement(&m_cRef);
-}
-
-STDMETHODIMP_(ULONG) SourceResolver::Release(void)
-{
- LONG cRef = InterlockedDecrement(&m_cRef);
- if (cRef == 0)
- this->deleteLater();
- return cRef;
-}
-
-HRESULT STDMETHODCALLTYPE SourceResolver::Invoke(IMFAsyncResult *pAsyncResult)
-{
- QMutexLocker locker(&m_mutex);
-
- if (!m_sourceResolver)
- return S_OK;
-
- MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;
- IUnknown* pSource = NULL;
- State *state = static_cast<State*>(pAsyncResult->GetStateNoAddRef());
-
- HRESULT hr = S_OK;
- if (state->fromStream())
- hr = m_sourceResolver->EndCreateObjectFromByteStream(pAsyncResult, &ObjectType, &pSource);
- else
- hr = m_sourceResolver->EndCreateObjectFromURL(pAsyncResult, &ObjectType, &pSource);
-
- if (state->sourceResolver() != m_sourceResolver) {
- //This is a cancelled one
- return S_OK;
- }
-
- if (m_cancelCookie) {
- m_cancelCookie->Release();
- m_cancelCookie = NULL;
- }
-
- if (FAILED(hr)) {
- emit error(hr);
- return S_OK;
- }
-
- if (m_mediaSource) {
- m_mediaSource->Release();
- m_mediaSource = NULL;
- }
-
- hr = pSource->QueryInterface(IID_PPV_ARGS(&m_mediaSource));
- pSource->Release();
- if (FAILED(hr)) {
- emit error(hr);
- return S_OK;
- }
-
- emit mediaSourceReady();
-
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE SourceResolver::GetParameters(DWORD*, DWORD*)
-{
- return E_NOTIMPL;
-}
-
-void SourceResolver::load(const QUrl &url, QIODevice* stream)
-{
- QMutexLocker locker(&m_mutex);
- HRESULT hr = S_OK;
- if (!m_sourceResolver)
- hr = MFCreateSourceResolver(&m_sourceResolver);
-
- if (m_stream) {
- m_stream->Release();
- m_stream = NULL;
- }
-
- if (FAILED(hr)) {
- qWarning() << "Failed to create Source Resolver!";
- emit error(hr);
- } else if (stream) {
- QString urlString = url.toString();
- m_stream = new MFStream(stream, false);
- hr = m_sourceResolver->BeginCreateObjectFromByteStream(
- m_stream, urlString.isEmpty() ? 0 : reinterpret_cast<LPCWSTR>(urlString.utf16()),
- MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE
- , NULL, &m_cancelCookie, this, new State(m_sourceResolver, true));
- if (FAILED(hr)) {
- qWarning() << "Unsupported stream!";
- emit error(hr);
- }
- } else {
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "loading :" << url;
- qDebug() << "url path =" << url.path().mid(1);
-#endif
-#ifdef TEST_STREAMING
- //Testing stream function
- if (url.scheme() == QLatin1String("file")) {
- stream = new QFile(url.path().mid(1));
- if (stream->open(QIODevice::ReadOnly)) {
- m_stream = new MFStream(stream, true);
- hr = m_sourceResolver->BeginCreateObjectFromByteStream(
- m_stream, reinterpret_cast<const OLECHAR *>(url.toString().utf16()),
- MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE,
- NULL, &m_cancelCookie, this, new State(m_sourceResolver, true));
- if (FAILED(hr)) {
- qWarning() << "Unsupported stream!";
- emit error(hr);
- }
- } else {
- delete stream;
- emit error(QMediaPlayer::FormatError);
- }
- } else
-#endif
- if (url.scheme() == QLatin1String("qrc")) {
- // If the canonical URL refers to a Qt resource, open with QFile and use
- // the stream playback capability to play.
- stream = new QFile(QLatin1Char(':') + url.path());
- if (stream->open(QIODevice::ReadOnly)) {
- m_stream = new MFStream(stream, true);
- hr = m_sourceResolver->BeginCreateObjectFromByteStream(
- m_stream, reinterpret_cast<const OLECHAR *>(url.toString().utf16()),
- MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE,
- NULL, &m_cancelCookie, this, new State(m_sourceResolver, true));
- if (FAILED(hr)) {
- qWarning() << "Unsupported stream!";
- emit error(hr);
- }
- } else {
- delete stream;
- emit error(QMediaPlayer::FormatError);
- }
- } else {
- hr = m_sourceResolver->BeginCreateObjectFromURL(
- reinterpret_cast<const OLECHAR *>(url.toString().utf16()),
- MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE,
- NULL, &m_cancelCookie, this, new State(m_sourceResolver, false));
- if (FAILED(hr)) {
- qWarning() << "Unsupported url scheme!";
- emit error(hr);
- }
- }
- }
-}
-
-void SourceResolver::cancel()
-{
- QMutexLocker locker(&m_mutex);
- if (m_cancelCookie) {
- m_sourceResolver->CancelObjectCreation(m_cancelCookie);
- m_cancelCookie->Release();
- m_cancelCookie = NULL;
- m_sourceResolver->Release();
- m_sourceResolver = NULL;
- }
-}
-
-void SourceResolver::shutdown()
-{
- if (m_mediaSource) {
- m_mediaSource->Shutdown();
- m_mediaSource->Release();
- m_mediaSource = NULL;
- }
-
- if (m_stream) {
- m_stream->Release();
- m_stream = NULL;
- }
-}
-
-IMFMediaSource* SourceResolver::mediaSource() const
-{
- return m_mediaSource;
-}
-
-/////////////////////////////////////////////////////////////////////////////////
-SourceResolver::State::State(IMFSourceResolver *sourceResolver, bool fromStream)
- : m_cRef(0)
- , m_sourceResolver(sourceResolver)
- , m_fromStream(fromStream)
-{
- sourceResolver->AddRef();
-}
-
-SourceResolver::State::~State()
-{
- m_sourceResolver->Release();
-}
-
-STDMETHODIMP SourceResolver::State::QueryInterface(REFIID riid, LPVOID *ppvObject)
-{
- if (!ppvObject)
- return E_POINTER;
- if (riid == IID_IUnknown) {
- *ppvObject = static_cast<IUnknown*>(this);
- } else {
- *ppvObject = NULL;
- return E_NOINTERFACE;
- }
- AddRef();
- return S_OK;
-}
-
-STDMETHODIMP_(ULONG) SourceResolver::State::AddRef(void)
-{
- return InterlockedIncrement(&m_cRef);
-}
-
-STDMETHODIMP_(ULONG) SourceResolver::State::Release(void)
-{
- LONG cRef = InterlockedDecrement(&m_cRef);
- if (cRef == 0)
- delete this;
- // For thread safety, return a temporary variable.
- return cRef;
-}
-
-IMFSourceResolver* SourceResolver::State::sourceResolver() const
-{
- return m_sourceResolver;
-}
-
-bool SourceResolver::State::fromStream() const
-{
- return m_fromStream;
-}
-
diff --git a/src/plugins/wmf/sourceresolver.h b/src/plugins/wmf/sourceresolver.h
deleted file mode 100644
index 007552cb0..000000000
--- a/src/plugins/wmf/sourceresolver.h
+++ /dev/null
@@ -1,104 +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 SOURCERESOLVER_H
-#define SOURCERESOLVER_H
-
-#include "mfstream.h"
-#include <QUrl>
-
-class SourceResolver: public QObject, public IMFAsyncCallback
-{
- Q_OBJECT
-public:
- SourceResolver();
-
- ~SourceResolver();
-
- STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject);
- STDMETHODIMP_(ULONG) AddRef(void);
- STDMETHODIMP_(ULONG) Release(void);
-
- HRESULT STDMETHODCALLTYPE Invoke(IMFAsyncResult *pAsyncResult);
-
- HRESULT STDMETHODCALLTYPE GetParameters(DWORD*, DWORD*);
-
- void load(const QUrl &url, QIODevice* stream);
-
- void cancel();
-
- void shutdown();
-
- IMFMediaSource* mediaSource() const;
-
-Q_SIGNALS:
- void error(long hr);
- void mediaSourceReady();
-
-private:
- class State : public IUnknown
- {
- public:
- State(IMFSourceResolver *sourceResolver, bool fromStream);
- ~State();
-
- STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject);
-
- STDMETHODIMP_(ULONG) AddRef(void);
-
- STDMETHODIMP_(ULONG) Release(void);
-
- IMFSourceResolver* sourceResolver() const;
- bool fromStream() const;
-
- private:
- long m_cRef;
- IMFSourceResolver *m_sourceResolver;
- bool m_fromStream;
- };
-
- long m_cRef;
- IUnknown *m_cancelCookie;
- IMFSourceResolver *m_sourceResolver;
- IMFMediaSource *m_mediaSource;
- MFStream *m_stream;
- QMutex m_mutex;
-};
-
-#endif
diff --git a/src/plugins/wmf/wmf.json b/src/plugins/wmf/wmf.json
deleted file mode 100644
index e70736480..000000000
--- a/src/plugins/wmf/wmf.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "Keys": ["windowsmediafoundation"],
- "Services": ["org.qt-project.qt.mediaplayer", "org.qt-project.qt.audiodecode"]
-}
diff --git a/src/plugins/wmf/wmf.pro b/src/plugins/wmf/wmf.pro
deleted file mode 100644
index 7c712233d..000000000
--- a/src/plugins/wmf/wmf.pro
+++ /dev/null
@@ -1,28 +0,0 @@
-TARGET = wmfengine
-QT += multimedia-private network
-
-win32:!qtHaveModule(opengl) {
- LIBS_PRIVATE += -lgdi32 -luser32
-}
-
-INCLUDEPATH += .
-
-HEADERS += \
- wmfserviceplugin.h \
- mfstream.h \
- sourceresolver.h
-
-SOURCES += \
- wmfserviceplugin.cpp \
- mfstream.cpp \
- sourceresolver.cpp
-
-include (player/player.pri)
-include (decoder/decoder.pri)
-
-OTHER_FILES += \
- wmf.json
-
-PLUGIN_TYPE = mediaservice
-PLUGIN_CLASS_NAME = WMFServicePlugin
-load(qt_plugin)
diff --git a/src/plugins/wmf/wmfserviceplugin.cpp b/src/plugins/wmf/wmfserviceplugin.cpp
deleted file mode 100644
index 740067600..000000000
--- a/src/plugins/wmf/wmfserviceplugin.cpp
+++ /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 Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <QtCore/qstring.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/QFile>
-
-#include "wmfserviceplugin.h"
-#include "mfplayerservice.h"
-#include "mfdecoderservice.h"
-
-#include <mfapi.h>
-
-namespace
-{
-static int g_refCount = 0;
-void addRefCount()
-{
- g_refCount++;
- if (g_refCount == 1) {
- CoInitialize(NULL);
- MFStartup(MF_VERSION);
- }
-}
-
-void releaseRefCount()
-{
- g_refCount--;
- if (g_refCount == 0) {
- MFShutdown();
- CoUninitialize();
- }
-}
-
-}
-
-QMediaService* WMFServicePlugin::create(QString const& key)
-{
- if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) {
- addRefCount();
- return new MFPlayerService;
- }
-
- if (key == QLatin1String(Q_MEDIASERVICE_AUDIODECODER)) {
- addRefCount();
- return new MFAudioDecoderService;
- }
- //qDebug() << "unsupported key:" << key;
- return 0;
-}
-
-void WMFServicePlugin::release(QMediaService *service)
-{
- delete service;
- releaseRefCount();
-}
-
-QMediaServiceProviderHint::Features WMFServicePlugin::supportedFeatures(
- const QByteArray &service) const
-{
- if (service == Q_MEDIASERVICE_MEDIAPLAYER)
- return QMediaServiceProviderHint::StreamPlayback;
- else
- return QMediaServiceProviderHint::Features();
-}
-
-QByteArray WMFServicePlugin::defaultDevice(const QByteArray &) const
-{
- return QByteArray();
-}
-
-QList<QByteArray> WMFServicePlugin::devices(const QByteArray &) const
-{
- return QList<QByteArray>();
-}
-
-QString WMFServicePlugin::deviceDescription(const QByteArray &, const QByteArray &)
-{
- return QString();
-}
-
diff --git a/src/plugins/wmf/wmfserviceplugin.h b/src/plugins/wmf/wmfserviceplugin.h
deleted file mode 100644
index 493a0b08c..000000000
--- a/src/plugins/wmf/wmfserviceplugin.h
+++ /dev/null
@@ -1,71 +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 WMFSERVICEPLUGIN_H
-#define WMFSERVICEPLUGIN_H
-
-#include <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include "qmediaserviceproviderplugin.h"
-
-QT_USE_NAMESPACE
-
-class WMFServicePlugin
- : public QMediaServiceProviderPlugin
- , public QMediaServiceSupportedDevicesInterface
- , public QMediaServiceDefaultDeviceInterface
- , public QMediaServiceFeaturesInterface
-{
- Q_OBJECT
- Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
- Q_INTERFACES(QMediaServiceDefaultDeviceInterface)
- Q_INTERFACES(QMediaServiceFeaturesInterface)
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "wmf.json")
-
-public:
- QMediaService* create(QString const& key);
- void release(QMediaService *service);
-
- QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const;
-
- QByteArray defaultDevice(const QByteArray &service) const;
- QList<QByteArray> devices(const QByteArray &service) const;
- QString deviceDescription(const QByteArray &service, const QByteArray &device);
-};
-
-#endif // WMFSERVICEPLUGIN_H