summaryrefslogtreecommitdiffstats
path: root/src/multimedia/platform
diff options
context:
space:
mode:
Diffstat (limited to 'src/multimedia/platform')
-rw-r--r--src/multimedia/platform/alsa/alsa.json3
-rw-r--r--src/multimedia/platform/alsa/alsa.pri16
-rw-r--r--src/multimedia/platform/android/android.pri12
-rw-r--r--src/multimedia/platform/android/common/common.pri10
-rw-r--r--src/multimedia/platform/android/common/qandroidglobal_p.h64
-rw-r--r--src/multimedia/platform/android/common/qandroidmultimediautils.cpp152
-rw-r--r--src/multimedia/platform/android/common/qandroidmultimediautils_p.h74
-rw-r--r--src/multimedia/platform/android/common/qandroidvideooutput.cpp502
-rw-r--r--src/multimedia/platform/android/common/qandroidvideooutput_p.h156
-rw-r--r--src/multimedia/platform/android/mediacapture/mediacapture.pri39
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidaudioencodersettingscontrol.cpp96
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidaudioencodersettingscontrol_p.h78
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidaudioinputselectorcontrol.cpp111
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidaudioinputselectorcontrol_p.h82
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameracontrol.cpp343
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameracontrol_p.h112
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameraexposurecontrol.cpp355
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameraexposurecontrol_p.h102
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcamerafocuscontrol.cpp383
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcamerafocuscontrol_p.h133
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameraimagecapturecontrol.cpp94
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameraimagecapturecontrol_p.h83
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameraimageprocessingcontrol.cpp140
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameraimageprocessingcontrol_p.h86
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcamerasession.cpp939
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcamerasession_p.h216
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameravideorenderercontrol.cpp281
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameravideorenderercontrol_p.h83
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcaptureservice.cpp200
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcaptureservice_p.h108
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp594
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcapturesession_p.h193
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidimageencodercontrol.cpp93
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidimageencodercontrol_p.h83
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidmediacontainercontrol.cpp84
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidmediacontainercontrol_p.h77
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidmediarecordercontrol.cpp118
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidmediarecordercontrol_p.h86
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidmediavideoprobecontrol.cpp61
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidmediavideoprobecontrol_p.h72
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidvideodeviceselectorcontrol.cpp115
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidvideodeviceselectorcontrol_p.h87
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidvideoencodersettingscontrol.cpp97
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidvideoencodersettingscontrol_p.h79
-rw-r--r--src/multimedia/platform/android/mediaplayer/mediaplayer.pri13
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmediaplayercontrol.cpp835
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmediaplayercontrol_p.h154
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp76
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmediaplayervideorenderercontrol_p.h79
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmediaservice.cpp95
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmediaservice_p.h84
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmetadatareadercontrol.cpp248
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmetadatareadercontrol_p.h90
-rw-r--r--src/multimedia/platform/android/qandroidmediaserviceplugin.cpp159
-rw-r--r--src/multimedia/platform/android/qandroidmediaserviceplugin_p.h81
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidcamera.cpp1712
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidcamera_p.h248
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever.cpp193
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever_p.h100
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidmediaplayer.cpp435
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidmediaplayer_p.h152
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidmediarecorder.cpp405
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidmediarecorder_p.h187
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidmultimediautils.cpp78
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidmultimediautils_p.h77
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidsurfacetexture.cpp211
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidsurfacetexture_p.h95
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidsurfaceview.cpp195
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidsurfaceview_p.h113
-rw-r--r--src/multimedia/platform/android/wrappers/jni/jni.pri21
-rw-r--r--src/multimedia/platform/avfoundation/avfoundation.pri18
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfaudioencodersettingscontrol.mm226
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfaudioencodersettingscontrol_p.h86
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfaudioinputselectorcontrol.mm119
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfaudioinputselectorcontrol_p.h94
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameracontrol.mm530
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameracontrol_p.h126
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameradebug_p.h68
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameradevicecontrol.mm142
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameradevicecontrol_p.h98
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameraexposurecontrol.mm831
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameraexposurecontrol_p.h112
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerafocuscontrol.mm422
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerafocuscontrol_p.h118
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerametadatacontrol.mm83
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerametadatacontrol_p.h81
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerarenderercontrol.mm409
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerarenderercontrol_p.h119
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameraservice.mm223
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameraservice_p.h126
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameraserviceplugin.mm103
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcameraserviceplugin_p.h79
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerasession.mm491
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerasession_p.h155
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerautility.mm575
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerautility_p.h190
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerawindowcontrol.mm262
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfcamerawindowcontrol_p.h129
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfimagecapturecontrol.mm278
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfimagecapturecontrol_p.h116
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfimageencodercontrol.mm239
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfimageencodercontrol_p.h96
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediaassetwriter.mm514
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediaassetwriter_p.h86
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediacontainercontrol.mm113
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediacontainercontrol_p.h80
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol.mm430
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_ios.mm414
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_ios_p.h126
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_p.h131
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediavideoprobecontrol.mm61
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfmediavideoprobecontrol_p.h71
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfstoragelocation.mm136
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfstoragelocation_p.h82
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfvideoencodersettingscontrol.mm385
-rw-r--r--src/multimedia/platform/avfoundation/camera/avfvideoencodersettingscontrol_p.h99
-rw-r--r--src/multimedia/platform/avfoundation/camera/camera.pri60
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfdisplaylink.mm241
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfdisplaylink_p.h101
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayercontrol.mm192
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayercontrol_p.h110
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.mm161
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayermetadatacontrol_p.h86
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayerservice.mm122
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayerservice_p.h81
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayerserviceplugin.mm99
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayerserviceplugin_p.h84
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayersession.mm1067
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayersession_p.h172
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfvideoframerenderer.mm464
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfvideoframerenderer_ios.mm307
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfvideoframerenderer_ios_p.h131
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfvideoframerenderer_p.h137
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfvideooutput.mm42
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfvideooutput_p.h71
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfvideorenderercontrol.mm301
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfvideorenderercontrol_p.h103
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfvideowindowcontrol.mm255
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/avfvideowindowcontrol_p.h129
-rw-r--r--src/multimedia/platform/avfoundation/mediaplayer/mediaplayer.pri48
-rw-r--r--src/multimedia/platform/coreaudio/coreaudio.pri24
-rw-r--r--src/multimedia/platform/gstreamer/audio/audio.pri15
-rw-r--r--src/multimedia/platform/gstreamer/audio/qaudiodeviceinfo_gstreamer.cpp (renamed from src/multimedia/platform/gstreamer/qaudiodeviceinfo_gstreamer.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/audio/qaudiodeviceinfo_gstreamer_p.h (renamed from src/multimedia/platform/gstreamer/qaudiodeviceinfo_gstreamer_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/audio/qaudioengine_gstreamer.cpp (renamed from src/multimedia/platform/gstreamer/qaudioengine_gstreamer.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/audio/qaudioengine_gstreamer_p.h (renamed from src/multimedia/platform/gstreamer/qaudioengine_gstreamer_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/audio/qaudioinput_gstreamer.cpp (renamed from src/multimedia/platform/gstreamer/qaudioinput_gstreamer.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/audio/qaudioinput_gstreamer_p.h (renamed from src/multimedia/platform/gstreamer/qaudioinput_gstreamer_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/audio/qaudiointerface_gstreamer.cpp (renamed from src/multimedia/platform/gstreamer/qaudiointerface_gstreamer.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/audio/qaudiointerface_gstreamer_p.h (renamed from src/multimedia/platform/gstreamer/qaudiointerface_gstreamer_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/audio/qaudiooutput_gstreamer.cpp (renamed from src/multimedia/platform/gstreamer/qaudiooutput_gstreamer.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/audio/qaudiooutput_gstreamer_p.h (renamed from src/multimedia/platform/gstreamer/qaudiooutput_gstreamer_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/audio/qgstreameraudiodecodercontrol.cpp599
-rw-r--r--src/multimedia/platform/gstreamer/audio/qgstreameraudiodecodercontrol_p.h157
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabin.pri51
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinaudioencoder.cpp165
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinaudioencoder_p.h120
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabincontainer.cpp151
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabincontainer_p.h105
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabincontrol.cpp432
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabincontrol_p.h136
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinexposure.cpp322
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinexposure_p.h92
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinfocus.cpp607
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinfocus_p.h153
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinimagecapture.cpp303
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinimagecapture_p.h128
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinimageencoder.cpp88
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinimageencoder_p.h97
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinimageprocessing.cpp409
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinimageprocessing_p.h115
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinmetadata.cpp226
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinmetadata_p.h84
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinrecorder.cpp289
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinrecorder_p.h105
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinservice.cpp206
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinservice_p.h105
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinserviceplugin.cpp144
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinserviceplugin_p.h89
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinsession.cpp1510
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinsession_p.h291
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinv4limageprocessing.cpp316
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinv4limageprocessing_p.h101
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinvideoencoder.cpp239
-rw-r--r--src/multimedia/platform/gstreamer/camerabin/camerabinvideoencoder_p.h120
-rw-r--r--src/multimedia/platform/gstreamer/common/common.pri41
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstappsrc.cpp (renamed from src/multimedia/platform/gstreamer/qgstappsrc.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstappsrc_p.h (renamed from src/multimedia/platform/gstreamer/qgstappsrc_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstcodecsinfo.cpp (renamed from src/multimedia/platform/gstreamer/qgstcodecsinfo.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstcodecsinfo_p.h (renamed from src/multimedia/platform/gstreamer/qgstcodecsinfo_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreameraudioinputselector.cpp (renamed from src/multimedia/platform/gstreamer/qgstreameraudioinputselector.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreameraudioinputselector_p.h (renamed from src/multimedia/platform/gstreamer/qgstreameraudioinputselector_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreameraudioprobecontrol.cpp (renamed from src/multimedia/platform/gstreamer/qgstreameraudioprobecontrol.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreameraudioprobecontrol_p.h (renamed from src/multimedia/platform/gstreamer/qgstreameraudioprobecontrol_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamerbufferprobe.cpp (renamed from src/multimedia/platform/gstreamer/qgstreamerbufferprobe.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamerbufferprobe_p.h (renamed from src/multimedia/platform/gstreamer/qgstreamerbufferprobe_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamerbushelper.cpp (renamed from src/multimedia/platform/gstreamer/qgstreamerbushelper.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamerbushelper_p.h (renamed from src/multimedia/platform/gstreamer/qgstreamerbushelper_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamermessage.cpp (renamed from src/multimedia/platform/gstreamer/qgstreamermessage.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamermessage_p.h (renamed from src/multimedia/platform/gstreamer/qgstreamermessage_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamerplayercontrol.cpp (renamed from src/multimedia/platform/gstreamer/qgstreamerplayercontrol.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamerplayercontrol_p.h (renamed from src/multimedia/platform/gstreamer/qgstreamerplayercontrol_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamerplayersession.cpp (renamed from src/multimedia/platform/gstreamer/qgstreamerplayersession.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamerplayersession_p.h (renamed from src/multimedia/platform/gstreamer/qgstreamerplayersession_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamervideoinputdevicecontrol.cpp (renamed from src/multimedia/platform/gstreamer/qgstreamervideoinputdevicecontrol.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamervideoinputdevicecontrol_p.h (renamed from src/multimedia/platform/gstreamer/qgstreamervideoinputdevicecontrol_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamervideooverlay.cpp (renamed from src/multimedia/platform/gstreamer/qgstreamervideooverlay.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamervideooverlay_p.h (renamed from src/multimedia/platform/gstreamer/qgstreamervideooverlay_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamervideoprobecontrol.cpp (renamed from src/multimedia/platform/gstreamer/qgstreamervideoprobecontrol.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamervideoprobecontrol_p.h (renamed from src/multimedia/platform/gstreamer/qgstreamervideoprobecontrol_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamervideorenderer.cpp (renamed from src/multimedia/platform/gstreamer/qgstreamervideorenderer.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamervideorenderer_p.h (renamed from src/multimedia/platform/gstreamer/qgstreamervideorenderer_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamervideorendererinterface.cpp (renamed from src/multimedia/platform/gstreamer/qgstreamervideorendererinterface.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamervideorendererinterface_p.h (renamed from src/multimedia/platform/gstreamer/qgstreamervideorendererinterface_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamervideowindow.cpp (renamed from src/multimedia/platform/gstreamer/qgstreamervideowindow.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstreamervideowindow_p.h (renamed from src/multimedia/platform/gstreamer/qgstreamervideowindow_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstutils.cpp (renamed from src/multimedia/platform/gstreamer/qgstutils.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstutils_p.h (renamed from src/multimedia/platform/gstreamer/qgstutils_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstvideobuffer.cpp (renamed from src/multimedia/platform/gstreamer/qgstvideobuffer.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstvideobuffer_p.h (renamed from src/multimedia/platform/gstreamer/qgstvideobuffer_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstvideorendererplugin.cpp (renamed from src/multimedia/platform/gstreamer/qgstvideorendererplugin.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstvideorendererplugin_p.h (renamed from src/multimedia/platform/gstreamer/qgstvideorendererplugin_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstvideorenderersink.cpp (renamed from src/multimedia/platform/gstreamer/qgstvideorenderersink.cpp)0
-rw-r--r--src/multimedia/platform/gstreamer/common/qgstvideorenderersink_p.h (renamed from src/multimedia/platform/gstreamer/qgstvideorenderersink_p.h)0
-rw-r--r--src/multimedia/platform/gstreamer/gstreamer.pri64
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/mediacapture.pri37
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreameraudioencode.cpp239
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreameraudioencode_p.h105
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamercameracontrol.cpp195
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamercameracontrol_p.h116
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.cpp158
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturemetadatacontrol_p.h83
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamercaptureservice.cpp205
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamercaptureservice_p.h109
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamercaptureserviceplugin.cpp126
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamercaptureserviceplugin_p.h95
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturesession.cpp1020
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturesession_p.h253
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamerimagecapturecontrol.cpp113
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamerimagecapturecontrol_p.h88
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamerimageencode.cpp88
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamerimageencode_p.h92
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamermediacontainercontrol.cpp60
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamermediacontainercontrol_p.h91
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp372
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamerrecordercontrol_p.h108
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamerv4l2input.cpp286
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamerv4l2input_p.h94
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamervideoencode.cpp296
-rw-r--r--src/multimedia/platform/gstreamer/mediacapture/qgstreamervideoencode_p.h108
-rw-r--r--src/multimedia/platform/gstreamer/mediaplayer/mediaplayer.pri13
-rw-r--r--src/multimedia/platform/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp184
-rw-r--r--src/multimedia/platform/gstreamer/mediaplayer/qgstreamermetadataprovider_p.h84
-rw-r--r--src/multimedia/platform/gstreamer/mediaplayer/qgstreamerplayerservice.cpp157
-rw-r--r--src/multimedia/platform/gstreamer/mediaplayer/qgstreamerplayerservice_p.h103
-rw-r--r--src/multimedia/platform/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.cpp93
-rw-r--r--src/multimedia/platform/gstreamer/mediaplayer/qgstreamerplayerserviceplugin_p.h84
-rw-r--r--src/multimedia/platform/gstreamer/mediaplayer/qgstreamerstreamscontrol.cpp87
-rw-r--r--src/multimedia/platform/gstreamer/mediaplayer/qgstreamerstreamscontrol_p.h82
-rw-r--r--src/multimedia/platform/opensles/opensles.pri20
-rw-r--r--src/multimedia/platform/platform.pri15
-rw-r--r--src/multimedia/platform/pulseaudio/pulseaudio.pri24
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameraaudioencodersettingscontrol.cpp87
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameraaudioencodersettingscontrol_p.h77
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameracontrol.cpp290
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameracontrol_p.h109
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameraexposurecontrol.cpp287
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameraexposurecontrol_p.h89
-rw-r--r--src/multimedia/platform/qnx/camera/bbcamerafocuscontrol.cpp428
-rw-r--r--src/multimedia/platform/qnx/camera/bbcamerafocuscontrol_p.h104
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameraimagecapturecontrol.cpp93
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameraimagecapturecontrol_p.h82
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameraimageprocessingcontrol.cpp144
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameraimageprocessingcontrol_p.h76
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameramediarecordercontrol.cpp156
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameramediarecordercontrol_p.h85
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameraorientationhandler.cpp113
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameraorientationhandler_p.h80
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameraservice.cpp119
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameraservice_p.h103
-rw-r--r--src/multimedia/platform/qnx/camera/bbcamerasession.cpp1154
-rw-r--r--src/multimedia/platform/qnx/camera/bbcamerasession_p.h226
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameravideoencodersettingscontrol.cpp88
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameravideoencodersettingscontrol_p.h78
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameraviewfindersettingscontrol.cpp245
-rw-r--r--src/multimedia/platform/qnx/camera/bbcameraviewfindersettingscontrol_p.h75
-rw-r--r--src/multimedia/platform/qnx/camera/bbimageencodercontrol.cpp79
-rw-r--r--src/multimedia/platform/qnx/camera/bbimageencodercontrol_p.h77
-rw-r--r--src/multimedia/platform/qnx/camera/bbmediastoragelocation.cpp118
-rw-r--r--src/multimedia/platform/qnx/camera/bbmediastoragelocation_p.h75
-rw-r--r--src/multimedia/platform/qnx/camera/bbvideodeviceselectorcontrol.cpp160
-rw-r--r--src/multimedia/platform/qnx/camera/bbvideodeviceselectorcontrol_p.h92
-rw-r--r--src/multimedia/platform/qnx/camera/bbvideorenderercontrol.cpp62
-rw-r--r--src/multimedia/platform/qnx/camera/bbvideorenderercontrol_p.h74
-rw-r--r--src/multimedia/platform/qnx/camera/camera.pri40
-rw-r--r--src/multimedia/platform/qnx/common/common.pri7
-rw-r--r--src/multimedia/platform/qnx/common/windowgrabber.cpp419
-rw-r--r--src/multimedia/platform/qnx/common/windowgrabber_p.h155
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mediaplayer.pri24
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp664
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayercontrol_p.h193
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayerservice.cpp138
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayerservice_p.h87
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mmrenderermetadata.cpp298
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mmrenderermetadata_p.h110
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mmrenderermetadatareadercontrol.cpp170
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mmrenderermetadatareadercontrol_p.h77
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mmrendererplayervideorenderercontrol.cpp211
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mmrendererplayervideorenderercontrol_p.h95
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mmrendererutil.cpp246
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mmrendererutil_p.h71
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mmrenderervideowindowcontrol.cpp413
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mmrenderervideowindowcontrol_p.h129
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mmreventmediaplayercontrol.cpp229
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mmreventmediaplayercontrol_p.h95
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mmreventthread.cpp121
-rw-r--r--src/multimedia/platform/qnx/mediaplayer/mmreventthread_p.h90
-rw-r--r--src/multimedia/platform/qnx/neutrinoserviceplugin.cpp62
-rw-r--r--src/multimedia/platform/qnx/neutrinoserviceplugin_p.h71
-rw-r--r--src/multimedia/platform/qnx/qnx.pri26
-rw-r--r--src/multimedia/platform/qnx/qnxaudiodeviceinfo_p.h11
-rw-r--r--src/multimedia/platform/qnx/qnxaudioinput_p.h11
-rw-r--r--src/multimedia/platform/qnx/qnxaudiointerface_p.h11
-rw-r--r--src/multimedia/platform/qnx/qnxaudiooutput_p.h11
-rw-r--r--src/multimedia/platform/qnx/qnxaudioutils_p.h11
-rw-r--r--src/multimedia/platform/wasapi/wasapi.pri20
-rw-r--r--src/multimedia/platform/wmf/decoder/decoder.pri12
-rw-r--r--src/multimedia/platform/wmf/decoder/mfaudiodecodercontrol.cpp486
-rw-r--r--src/multimedia/platform/wmf/decoder/mfaudiodecodercontrol_p.h119
-rw-r--r--src/multimedia/platform/wmf/decoder/mfdecodersourcereader.cpp197
-rw-r--r--src/multimedia/platform/wmf/decoder/mfdecodersourcereader_p.h101
-rw-r--r--src/multimedia/platform/wmf/evr/evr.pri20
-rw-r--r--src/multimedia/platform/wmf/evr/evrcustompresenter.cpp2062
-rw-r--r--src/multimedia/platform/wmf/evr/evrcustompresenter_p.h388
-rw-r--r--src/multimedia/platform/wmf/evr/evrd3dpresentengine.cpp412
-rw-r--r--src/multimedia/platform/wmf/evr/evrd3dpresentengine_p.h163
-rw-r--r--src/multimedia/platform/wmf/evr/evrdefs.cpp48
-rw-r--r--src/multimedia/platform/wmf/evr/evrdefs_p.h364
-rw-r--r--src/multimedia/platform/wmf/evr/evrhelpers.cpp186
-rw-r--r--src/multimedia/platform/wmf/evr/evrhelpers_p.h112
-rw-r--r--src/multimedia/platform/wmf/evr/evrvideowindowcontrol.cpp350
-rw-r--r--src/multimedia/platform/wmf/evr/evrvideowindowcontrol_p.h120
-rw-r--r--src/multimedia/platform/wmf/mfstream.cpp361
-rw-r--r--src/multimedia/platform/wmf/mfstream_p.h159
-rw-r--r--src/multimedia/platform/wmf/player/mfactivate.cpp87
-rw-r--r--src/multimedia/platform/wmf/player/mfactivate_p.h223
-rw-r--r--src/multimedia/platform/wmf/player/mfaudioendpointcontrol.cpp180
-rw-r--r--src/multimedia/platform/wmf/player/mfaudioendpointcontrol_p.h93
-rw-r--r--src/multimedia/platform/wmf/player/mfaudioprobecontrol.cpp75
-rw-r--r--src/multimedia/platform/wmf/player/mfaudioprobecontrol_p.h77
-rw-r--r--src/multimedia/platform/wmf/player/mfevrvideowindowcontrol.cpp91
-rw-r--r--src/multimedia/platform/wmf/player/mfevrvideowindowcontrol_p.h74
-rw-r--r--src/multimedia/platform/wmf/player/mfmetadatacontrol.cpp431
-rw-r--r--src/multimedia/platform/wmf/player/mfmetadatacontrol_p.h83
-rw-r--r--src/multimedia/platform/wmf/player/mfplayercontrol.cpp316
-rw-r--r--src/multimedia/platform/wmf/player/mfplayercontrol_p.h136
-rw-r--r--src/multimedia/platform/wmf/player/mfplayerservice.cpp167
-rw-r--r--src/multimedia/platform/wmf/player/mfplayerservice_p.h98
-rw-r--r--src/multimedia/platform/wmf/player/mfplayersession.cpp1816
-rw-r--r--src/multimedia/platform/wmf/player/mfplayersession_p.h250
-rw-r--r--src/multimedia/platform/wmf/player/mftvideo.cpp753
-rw-r--r--src/multimedia/platform/wmf/player/mftvideo_p.h128
-rw-r--r--src/multimedia/platform/wmf/player/mfvideoprobecontrol.cpp54
-rw-r--r--src/multimedia/platform/wmf/player/mfvideoprobecontrol_p.h70
-rw-r--r--src/multimedia/platform/wmf/player/mfvideorenderercontrol.cpp2424
-rw-r--r--src/multimedia/platform/wmf/player/mfvideorenderercontrol_p.h92
-rw-r--r--src/multimedia/platform/wmf/player/player.pri32
-rw-r--r--src/multimedia/platform/wmf/player/samplegrabber.cpp173
-rw-r--r--src/multimedia/platform/wmf/player/samplegrabber_p.h107
-rw-r--r--src/multimedia/platform/wmf/sourceresolver.cpp325
-rw-r--r--src/multimedia/platform/wmf/sourceresolver_p.h115
-rw-r--r--src/multimedia/platform/wmf/wmf.pri21
-rw-r--r--src/multimedia/platform/wmf/wmfserviceplugin.cpp103
-rw-r--r--src/multimedia/platform/wmf/wmfserviceplugin_p.h76
374 files changed, 64396 insertions, 121 deletions
diff --git a/src/multimedia/platform/alsa/alsa.json b/src/multimedia/platform/alsa/alsa.json
deleted file mode 100644
index c2b22dfec..000000000
--- a/src/multimedia/platform/alsa/alsa.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "Keys": ["alsa"]
-}
diff --git a/src/multimedia/platform/alsa/alsa.pri b/src/multimedia/platform/alsa/alsa.pri
index 354e2c03d..aeec7da8a 100644
--- a/src/multimedia/platform/alsa/alsa.pri
+++ b/src/multimedia/platform/alsa/alsa.pri
@@ -1,11 +1,11 @@
QMAKE_USE_PRIVATE += alsa
-HEADERS += platform/alsa/qalsaaudiodeviceinfo_p.h \
- platform/alsa/qalsaaudioinput_p.h \
- platform/alsa/qalsaaudiooutput_p.h \
- platform/alsa/qalsainterface_p.h
+HEADERS += $$PWD/qalsaaudiodeviceinfo_p.h \
+ $$PWD/qalsaaudioinput_p.h \
+ $$PWD/qalsaaudiooutput_p.h \
+ $$PWD/qalsainterface_p.h
-SOURCES += platform/alsa/qalsaaudiodeviceinfo.cpp \
- platform/alsa/qalsaaudioinput.cpp \
- platform/alsa/qalsaaudiooutput.cpp \
- platform/alsa/qalsainterface.cpp
+SOURCES += $$PWD/qalsaaudiodeviceinfo.cpp \
+ $$PWD/qalsaaudioinput.cpp \
+ $$PWD/qalsaaudiooutput.cpp \
+ $$PWD/qalsainterface.cpp
diff --git a/src/multimedia/platform/android/android.pri b/src/multimedia/platform/android/android.pri
new file mode 100644
index 000000000..ce9fa093e
--- /dev/null
+++ b/src/multimedia/platform/android/android.pri
@@ -0,0 +1,12 @@
+QT += opengl core-private network
+
+HEADERS += \
+ $$PWD/qandroidmediaserviceplugin_p.h
+
+SOURCES += \
+ $$PWD/qandroidmediaserviceplugin.cpp
+
+include(wrappers/jni/jni.pri)
+include(common/common.pri)
+include(mediaplayer/mediaplayer.pri)
+include(mediacapture/mediacapture.pri)
diff --git a/src/multimedia/platform/android/common/common.pri b/src/multimedia/platform/android/common/common.pri
new file mode 100644
index 000000000..67a5116d2
--- /dev/null
+++ b/src/multimedia/platform/android/common/common.pri
@@ -0,0 +1,10 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/qandroidglobal_p.h \
+ $$PWD/qandroidvideooutput_p.h \
+ $$PWD/qandroidmultimediautils_p.h
+
+SOURCES += \
+ $$PWD/qandroidvideooutput.cpp \
+ $$PWD/qandroidmultimediautils.cpp
diff --git a/src/multimedia/platform/android/common/qandroidglobal_p.h b/src/multimedia/platform/android/common/qandroidglobal_p.h
new file mode 100644
index 000000000..45bd22ffb
--- /dev/null
+++ b/src/multimedia/platform/android/common/qandroidglobal_p.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/multimedia/platform/android/common/qandroidmultimediautils.cpp b/src/multimedia/platform/android/common/qandroidmultimediautils.cpp
new file mode 100644
index 000000000..850b3d7ea
--- /dev/null
+++ b/src/multimedia/platform/android/common/qandroidmultimediautils.cpp
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+#include "qandroidglobal_p.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/multimedia/platform/android/common/qandroidmultimediautils_p.h b/src/multimedia/platform/android/common/qandroidmultimediautils_p.h
new file mode 100644
index 000000000..205244eb5
--- /dev/null
+++ b/src/multimedia/platform/android/common/qandroidmultimediautils_p.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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);
+
+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/multimedia/platform/android/common/qandroidvideooutput.cpp b/src/multimedia/platform/android/common/qandroidvideooutput.cpp
new file mode 100644
index 000000000..0fa6cb84a
--- /dev/null
+++ b/src/multimedia/platform/android/common/qandroidvideooutput.cpp
@@ -0,0 +1,502 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "androidsurfacetexture_p.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 override { return m_mapMode; }
+
+ MapData map(MapMode mode) override
+ {
+ MapData mapData;
+ if (m_mapMode == NotMapped && mode == ReadOnly && updateFrame()) {
+ m_mapMode = mode;
+ m_image = m_output->m_fbo->toImage();
+
+ mapData.nBytes = static_cast<int>(m_image.sizeInBytes());
+ mapData.nPlanes = 1;
+ mapData.bytesPerLine[0] = m_image.bytesPerLine();
+ mapData.data[0] = m_image.bits();
+ }
+
+ return mapData;
+ }
+
+ void unmap() override
+ {
+ m_image = QImage();
+ m_mapMode = NotMapped;
+ }
+
+ QVariant handle() const override
+ {
+ 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/multimedia/platform/android/common/qandroidvideooutput_p.h b/src/multimedia/platform/android/common/qandroidvideooutput_p.h
new file mode 100644
index 000000000..dbc53ca44
--- /dev/null
+++ b/src/multimedia/platform/android/common/qandroidvideooutput_p.h
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <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/multimedia/platform/android/mediacapture/mediacapture.pri b/src/multimedia/platform/android/mediacapture/mediacapture.pri
new file mode 100644
index 000000000..a1f4b41a6
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/mediacapture.pri
@@ -0,0 +1,39 @@
+INCLUDEPATH += $$PWD
+
+SOURCES += \
+ $$PWD/qandroidcaptureservice.cpp \
+ $$PWD/qandroidcameracontrol.cpp \
+ $$PWD/qandroidvideodeviceselectorcontrol.cpp \
+ $$PWD/qandroidcamerasession.cpp \
+ $$PWD/qandroidcameraexposurecontrol.cpp \
+ $$PWD/qandroidcameraimageprocessingcontrol.cpp \
+ $$PWD/qandroidimageencodercontrol.cpp \
+ $$PWD/qandroidcameraimagecapturecontrol.cpp \
+ $$PWD/qandroidcamerafocuscontrol.cpp \
+ $$PWD/qandroidcapturesession.cpp \
+ $$PWD/qandroidmediarecordercontrol.cpp \
+ $$PWD/qandroidaudioencodersettingscontrol.cpp \
+ $$PWD/qandroidmediacontainercontrol.cpp \
+ $$PWD/qandroidvideoencodersettingscontrol.cpp \
+ $$PWD/qandroidaudioinputselectorcontrol.cpp \
+ $$PWD/qandroidmediavideoprobecontrol.cpp \
+ $$PWD/qandroidcameravideorenderercontrol.cpp
+
+HEADERS += \
+ $$PWD/qandroidcaptureservice_p.h \
+ $$PWD/qandroidcameracontrol_p.h \
+ $$PWD/qandroidvideodeviceselectorcontrol_p.h \
+ $$PWD/qandroidcamerasession_p.h \
+ $$PWD/qandroidcameraexposurecontrol_p.h \
+ $$PWD/qandroidcameraimageprocessingcontrol_p.h \
+ $$PWD/qandroidimageencodercontrol_p.h \
+ $$PWD/qandroidcameraimagecapturecontrol_p.h \
+ $$PWD/qandroidcamerafocuscontrol_p.h \
+ $$PWD/qandroidcapturesession_p.h \
+ $$PWD/qandroidmediarecordercontrol_p.h \
+ $$PWD/qandroidaudioencodersettingscontrol_p.h \
+ $$PWD/qandroidmediacontainercontrol_p.h \
+ $$PWD/qandroidvideoencodersettingscontrol_p.h \
+ $$PWD/qandroidaudioinputselectorcontrol_p.h \
+ $$PWD/qandroidmediavideoprobecontrol_p.h \
+ $$PWD/qandroidcameravideorenderercontrol_p.h
diff --git a/src/multimedia/platform/android/mediacapture/qandroidaudioencodersettingscontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidaudioencodersettingscontrol.cpp
new file mode 100644
index 000000000..c32a93cee
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidaudioencodersettingscontrol.cpp
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.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/multimedia/platform/android/mediacapture/qandroidaudioencodersettingscontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidaudioencodersettingscontrol_p.h
new file mode 100644
index 000000000..7259ff685
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidaudioencodersettingscontrol_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qaudioencodersettingscontrol_p.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/multimedia/platform/android/mediacapture/qandroidaudioinputselectorcontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidaudioinputselectorcontrol.cpp
new file mode 100644
index 000000000..9745d0725
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidaudioinputselectorcontrol.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.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/multimedia/platform/android/mediacapture/qandroidaudioinputselectorcontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidaudioinputselectorcontrol_p.h
new file mode 100644
index 000000000..ef53dbdae
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidaudioinputselectorcontrol_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qaudioinputselectorcontrol_p.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/multimedia/platform/android/mediacapture/qandroidcameracontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidcameracontrol.cpp
new file mode 100644
index 000000000..18f6de7c1
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameracontrol.cpp
@@ -0,0 +1,343 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+#include "qandroidcamerasession_p.h"
+#include <qtimer.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)));
+
+ connect(m_cameraSession, 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()));
+}
+
+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;
+ }
+}
+
+
+QCamera::LockTypes QAndroidCameraControl::supportedLocks() const
+{
+ return m_supportedLocks;
+}
+
+QCamera::LockStatus QAndroidCameraControl::lockStatus(QCamera::LockType lock) const
+{
+ if (!m_supportedLocks.testFlag(lock) || !m_cameraSession->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 QAndroidCameraControl::searchAndLock(QCamera::LockTypes locks)
+{
+ if (!m_cameraSession->camera())
+ return;
+
+ // filter out unsupported locks
+ locks &= m_supportedLocks;
+
+ if (locks.testFlag(QCamera::LockFocus)) {
+ QString focusMode = m_cameraSession->camera()->getFocusMode();
+ if (focusMode == QLatin1String("auto")
+ || focusMode == QLatin1String("macro")
+ || focusMode == QLatin1String("continuous-picture")
+ || focusMode == QLatin1String("continuous-video")) {
+
+ if (m_focusLockStatus == QCamera::Searching)
+ m_cameraSession->camera()->cancelAutoFocus();
+ else
+ setFocusLockStatus(QCamera::Searching, QCamera::UserRequest);
+
+ m_cameraSession->camera()->autoFocus();
+
+ } else {
+ setFocusLockStatus(QCamera::Locked, QCamera::LockAcquired);
+ }
+ }
+
+ if (locks.testFlag(QCamera::LockExposure) && m_exposureLockStatus != QCamera::Searching) {
+ if (m_cameraSession->camera()->getAutoExposureLock()) {
+ // if already locked, unlock and give some time to recalculate exposure
+ m_cameraSession->camera()->setAutoExposureLock(false);
+ setExposureLockStatus(QCamera::Searching, QCamera::UserRequest);
+ } else {
+ m_cameraSession->camera()->setAutoExposureLock(true);
+ setExposureLockStatus(QCamera::Locked, QCamera::LockAcquired);
+ }
+ }
+
+ if (locks.testFlag(QCamera::LockWhiteBalance) && m_whiteBalanceLockStatus != QCamera::Searching) {
+ if (m_cameraSession->camera()->getAutoWhiteBalanceLock()) {
+ // if already locked, unlock and give some time to recalculate white balance
+ m_cameraSession->camera()->setAutoWhiteBalanceLock(false);
+ setWhiteBalanceLockStatus(QCamera::Searching, QCamera::UserRequest);
+ } else {
+ m_cameraSession->camera()->setAutoWhiteBalanceLock(true);
+ setWhiteBalanceLockStatus(QCamera::Locked, QCamera::LockAcquired);
+ }
+ }
+
+ if (m_exposureLockStatus == QCamera::Searching || m_whiteBalanceLockStatus == QCamera::Searching)
+ m_recalculateTimer->start();
+}
+
+void QAndroidCameraControl::unlock(QCamera::LockTypes locks)
+{
+ if (!m_cameraSession->camera())
+ return;
+
+ if (m_recalculateTimer->isActive())
+ m_recalculateTimer->stop();
+
+ // filter out unsupported locks
+ locks &= m_supportedLocks;
+
+ if (locks.testFlag(QCamera::LockFocus)) {
+ m_cameraSession->camera()->cancelAutoFocus();
+ setFocusLockStatus(QCamera::Unlocked, QCamera::UserRequest);
+ }
+
+ if (locks.testFlag(QCamera::LockExposure)) {
+ m_cameraSession->camera()->setAutoExposureLock(false);
+ setExposureLockStatus(QCamera::Unlocked, QCamera::UserRequest);
+ }
+
+ if (locks.testFlag(QCamera::LockWhiteBalance)) {
+ m_cameraSession->camera()->setAutoWhiteBalanceLock(false);
+ setWhiteBalanceLockStatus(QCamera::Unlocked, QCamera::UserRequest);
+ }
+}
+
+void QAndroidCameraControl::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_cameraSession->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_cameraSession->camera(), SIGNAL(autoFocusComplete(bool)),
+ this, SLOT(onCameraAutoFocusComplete(bool)));
+
+ break;
+ }
+ }
+
+ if (m_cameraSession->camera()->isAutoExposureLockSupported()) {
+ m_supportedLocks |= QCamera::LockExposure;
+ setExposureLockStatus(QCamera::Unlocked, QCamera::UserRequest);
+ }
+
+ if (m_cameraSession->camera()->isAutoWhiteBalanceLockSupported()) {
+ m_supportedLocks |= QCamera::LockWhiteBalance;
+ setWhiteBalanceLockStatus(QCamera::Unlocked, QCamera::UserRequest);
+
+ connect(m_cameraSession->camera(), SIGNAL(whiteBalanceChanged()),
+ this, SLOT(onWhiteBalanceChanged()));
+ }
+}
+
+void QAndroidCameraControl::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 QAndroidCameraControl::onRecalculateTimeOut()
+{
+ if (m_exposureLockStatus == QCamera::Searching) {
+ m_cameraSession->camera()->setAutoExposureLock(true);
+ setExposureLockStatus(QCamera::Locked, QCamera::LockAcquired);
+ }
+
+ if (m_whiteBalanceLockStatus == QCamera::Searching) {
+ m_cameraSession->camera()->setAutoWhiteBalanceLock(true);
+ setWhiteBalanceLockStatus(QCamera::Locked, QCamera::LockAcquired);
+ }
+}
+
+void QAndroidCameraControl::onWhiteBalanceChanged()
+{
+ // changing the white balance mode releases the white balance lock
+ if (m_whiteBalanceLockStatus != QCamera::Unlocked)
+ setWhiteBalanceLockStatus(QCamera::Unlocked, QCamera::LockLost);
+}
+
+void QAndroidCameraControl::setFocusLockStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason)
+{
+ m_focusLockStatus = status;
+ emit lockStatusChanged(QCamera::LockFocus, m_focusLockStatus, reason);
+}
+
+void QAndroidCameraControl::setWhiteBalanceLockStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason)
+{
+ m_whiteBalanceLockStatus = status;
+ emit lockStatusChanged(QCamera::LockWhiteBalance, m_whiteBalanceLockStatus, reason);
+}
+
+void QAndroidCameraControl::setExposureLockStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason)
+{
+ m_exposureLockStatus = status;
+ emit lockStatusChanged(QCamera::LockExposure, m_exposureLockStatus, reason);
+}
+
+QList<QCameraViewfinderSettings> QAndroidCameraControl::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 QAndroidCameraControl::viewfinderSettings() const
+{
+ return m_cameraSession->viewfinderSettings();
+}
+
+void QAndroidCameraControl::setViewfinderSettings(const QCameraViewfinderSettings &settings)
+{
+ m_cameraSession->setViewfinderSettings(settings);
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcameracontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidcameracontrol_p.h
new file mode 100644
index 000000000..23fbc2dc6
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameracontrol_p.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qcameracontrol_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCameraSession;
+
+class QAndroidCameraControl : public QCameraControl
+{
+ Q_OBJECT
+public:
+ explicit QAndroidCameraControl(QAndroidCameraSession *session);
+ virtual ~QAndroidCameraControl();
+
+ 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;
+
+ 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;
+
+ QList<QCameraViewfinderSettings> supportedViewfinderSettings() const override;
+ QCameraViewfinderSettings viewfinderSettings() const override;
+ void setViewfinderSettings(const QCameraViewfinderSettings &settings) 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_cameraSession;
+
+ QTimer *m_recalculateTimer;
+
+ QCamera::LockTypes m_supportedLocks;
+
+ QCamera::LockStatus m_focusLockStatus;
+ QCamera::LockStatus m_exposureLockStatus;
+ QCamera::LockStatus m_whiteBalanceLockStatus;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAMERACONTROL_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcameraexposurecontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidcameraexposurecontrol.cpp
new file mode 100644
index 000000000..e2c1d09c1
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameraexposurecontrol.cpp
@@ -0,0 +1,355 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "qandroidcamerasession_p.h"
+#include "androidcamera_p.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::TorchPower:
+ return false;
+ case QCameraExposureControl::ExposureMode:
+ return !m_supportedExposureModes.isEmpty();
+ 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));
+
+ 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);
+}
+
+
+QCameraExposure::FlashModes QAndroidCameraExposureControl::flashMode() const
+{
+ return m_flashMode;
+}
+
+void QAndroidCameraExposureControl::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 QAndroidCameraExposureControl::isFlashModeSupported(QCameraExposure::FlashModes mode) const
+{
+ return m_session->camera() ? m_supportedFlashModes.contains(mode) : false;
+}
+
+bool QAndroidCameraExposureControl::isFlashReady() const
+{
+ // Android doesn't have an API for that
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcameraexposurecontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidcameraexposurecontrol_p.h
new file mode 100644
index 000000000..133221b13
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameraexposurecontrol_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qcameraexposurecontrol_p.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;
+
+ 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;
+
+ 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;
+
+ QList<QCameraExposure::FlashModes> m_supportedFlashModes;
+ QCameraExposure::FlashModes m_flashMode;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAMERAEXPOSURECONTROL_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcamerafocuscontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidcamerafocuscontrol.cpp
new file mode 100644
index 000000000..0fa9b055a
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcamerafocuscontrol.cpp
@@ -0,0 +1,383 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "qandroidcamerasession_p.h"
+#include "androidcamera_p.h"
+
+#include "qandroidmultimediautils_p.h"
+#include <qmath.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);
+
+ if (m_session->camera()->isZoomSupported()) {
+ m_zoomRatios = m_session->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);
+ }
+ }
+}
+
+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);
+}
+
+
+qreal QAndroidCameraFocusControl::maximumOpticalZoom() const
+{
+ // Optical zoom not supported
+ return 1.0;
+}
+
+qreal QAndroidCameraFocusControl::maximumDigitalZoom() const
+{
+ return m_maximumZoom;
+}
+
+qreal QAndroidCameraFocusControl::requestedOpticalZoom() const
+{
+ // Optical zoom not supported
+ return 1.0;
+}
+
+qreal QAndroidCameraFocusControl::requestedDigitalZoom() const
+{
+ return m_requestedZoom;
+}
+
+qreal QAndroidCameraFocusControl::currentOpticalZoom() const
+{
+ // Optical zoom not supported
+ return 1.0;
+}
+
+qreal QAndroidCameraFocusControl::currentDigitalZoom() const
+{
+ return m_currentZoom;
+}
+
+void QAndroidCameraFocusControl::zoomTo(qreal optical, qreal digital)
+{
+ Q_UNUSED(optical);
+
+ if (!qFuzzyCompare(m_requestedZoom, digital)) {
+ m_requestedZoom = digital;
+ emit requestedDigitalZoomChanged(m_requestedZoom);
+ }
+
+ if (m_session->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_session->camera()->setZoom(validZoomIndex);
+ m_currentZoom = newZoom;
+ emit currentDigitalZoomChanged(m_currentZoom);
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcamerafocuscontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidcamerafocuscontrol_p.h
new file mode 100644
index 000000000..4cc931993
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcamerafocuscontrol_p.h
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qcamerafocuscontrol_p.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;
+
+ 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();
+ 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;
+
+ qreal m_maximumZoom;
+ QList<int> m_zoomRatios;
+ qreal m_requestedZoom;
+ qreal m_currentZoom;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAMERAFOCUSCONTROL_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcameraimagecapturecontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidcameraimagecapturecontrol.cpp
new file mode 100644
index 000000000..d230daa5c
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameraimagecapturecontrol.cpp
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "qandroidcamerasession_p.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();
+}
+
+QCameraImageCapture::CaptureDestinations QAndroidCameraImageCaptureControl::captureDestination() const
+{
+ return m_session->captureDestination();;
+}
+
+void QAndroidCameraImageCaptureControl::setCaptureDestination(QCameraImageCapture::CaptureDestinations destination)
+{
+ m_session->setCaptureDestination(destination);
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcameraimagecapturecontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidcameraimagecapturecontrol_p.h
new file mode 100644
index 000000000..071e8a314
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameraimagecapturecontrol_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qcameraimagecapturecontrol_p.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;
+
+ QCameraImageCapture::CaptureDestinations captureDestination() const override;
+ void setCaptureDestination(QCameraImageCapture::CaptureDestinations destination) override;
+
+private:
+ QAndroidCameraSession *m_session;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAMERAIMAGECAPTURECONTROL_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcameraimageprocessingcontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidcameraimageprocessingcontrol.cpp
new file mode 100644
index 000000000..f35e6cf6e
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameraimageprocessingcontrol.cpp
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "qandroidcamerasession_p.h"
+#include "androidcamera_p.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/multimedia/platform/android/mediacapture/qandroidcameraimageprocessingcontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidcameraimageprocessingcontrol_p.h
new file mode 100644
index 000000000..370c8dfb6
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameraimageprocessingcontrol_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qcameraimageprocessingcontrol_p.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/multimedia/platform/android/mediacapture/qandroidcamerasession.cpp b/src/multimedia/platform/android/mediacapture/qandroidcamerasession.cpp
new file mode 100644
index 000000000..f261d86e2
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcamerasession.cpp
@@ -0,0 +1,939 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include "androidcamera_p.h"
+#include "androidmultimediautils_p.h"
+#include "qandroidvideooutput_p.h"
+#include "qandroidmediavideoprobecontrol_p.h"
+#include "qandroidmultimediautils_p.h"
+#include "qandroidcameravideorenderercontrol_p.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)
+{
+ m_captureDestination = destination;
+}
+
+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/multimedia/platform/android/mediacapture/qandroidcamerasession_p.h b/src/multimedia/platform/android/mediacapture/qandroidcamerasession_p.h
new file mode 100644
index 000000000..5d4c06738
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcamerasession_p.h
@@ -0,0 +1,216 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <qmediaencodersettings.h>
+#include <QCameraImageCapture>
+#include <QSet>
+#include <QMutex>
+#include <private/qmediastoragelocation_p.h>
+#include "androidcamera_p.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/multimedia/platform/android/mediacapture/qandroidcameravideorenderercontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidcameravideorenderercontrol.cpp
new file mode 100644
index 000000000..6084ed43f
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameravideorenderercontrol.cpp
@@ -0,0 +1,281 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "qandroidcamerasession_p.h"
+#include "qandroidvideooutput_p.h"
+#include "androidsurfaceview_p.h"
+#include "qandroidmultimediautils_p.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/multimedia/platform/android/mediacapture/qandroidcameravideorenderercontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidcameravideorenderercontrol_p.h
new file mode 100644
index 000000000..0deaac943
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameravideorenderercontrol_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/android/mediacapture/qandroidcaptureservice.cpp b/src/multimedia/platform/android/mediacapture/qandroidcaptureservice.cpp
new file mode 100644
index 000000000..79808956e
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcaptureservice.cpp
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include "qandroidmediarecordercontrol_p.h"
+#include "qandroidcapturesession_p.h"
+#include "qandroidcameracontrol_p.h"
+#include "qandroidvideodeviceselectorcontrol_p.h"
+#include "qandroidaudioinputselectorcontrol_p.h"
+#include "qandroidcamerasession_p.h"
+#include "qandroidcameravideorenderercontrol_p.h"
+#include "qandroidcameraexposurecontrol_p.h"
+#include "qandroidcamerafocuscontrol_p.h"
+#include "qandroidcameraimageprocessingcontrol_p.h"
+#include "qandroidimageencodercontrol_p.h"
+#include "qandroidcameraimagecapturecontrol_p.h"
+#include "qandroidaudioencodersettingscontrol_p.h"
+#include "qandroidvideoencodersettingscontrol_p.h"
+#include "qandroidmediacontainercontrol_p.h"
+#include "qandroidmediavideoprobecontrol_p.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_videoInputControl = new QAndroidVideoDeviceSelectorControl(m_cameraSession);
+ m_cameraExposureControl = new QAndroidCameraExposureControl(m_cameraSession);
+ m_cameraFocusControl = new QAndroidCameraFocusControl(m_cameraSession);
+ m_cameraImageProcessingControl = new QAndroidCameraImageProcessingControl(m_cameraSession);
+ m_imageEncoderControl = new QAndroidImageEncoderControl(m_cameraSession);
+ m_imageCaptureControl = new QAndroidCameraImageCaptureControl(m_cameraSession);
+ m_audioInputControl = 0;
+ } else {
+ m_cameraSession = 0;
+ m_cameraControl = 0;
+ m_videoInputControl = 0;
+ m_cameraExposureControl = 0;
+ m_cameraFocusControl = 0;
+ m_cameraImageProcessingControl = 0;
+ m_imageEncoderControl = 0;
+ m_imageCaptureControl = 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_audioInputControl;
+ delete m_videoInputControl;
+ delete m_videoRendererControl;
+ delete m_cameraExposureControl;
+ delete m_cameraFocusControl;
+ delete m_cameraImageProcessingControl;
+ delete m_imageEncoderControl;
+ delete m_imageCaptureControl;
+ delete m_cameraSession;
+}
+
+QObject *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, QAudioInputSelectorControl_iid) == 0)
+ return m_audioInputControl;
+
+ if (qstrcmp(name, QVideoDeviceSelectorControl_iid) == 0)
+ return m_videoInputControl;
+
+ if (qstrcmp(name, QCameraExposureControl_iid) == 0)
+ return m_cameraExposureControl;
+
+ if (qstrcmp(name, QCameraFocusControl_iid) == 0)
+ return m_cameraFocusControl;
+
+ 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, 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(QObject *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/multimedia/platform/android/mediacapture/qandroidcaptureservice_p.h b/src/multimedia/platform/android/mediacapture/qandroidcaptureservice_p.h
new file mode 100644
index 000000000..e202c40c2
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcaptureservice_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmediaservice.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidMediaRecorderControl;
+class QAndroidCaptureSession;
+class QAndroidCameraControl;
+class QAndroidVideoDeviceSelectorControl;
+class QAndroidAudioInputSelectorControl;
+class QAndroidCameraSession;
+class QAndroidCameraVideoRendererControl;
+class QAndroidCameraExposureControl;
+class QAndroidCameraFocusControl;
+class QAndroidCameraImageProcessingControl;
+class QAndroidImageEncoderControl;
+class QAndroidCameraImageCaptureControl;
+class QAndroidAudioEncoderSettingsControl;
+class QAndroidVideoEncoderSettingsControl;
+class QAndroidMediaContainerControl;
+
+class QAndroidCaptureService : public QMediaService
+{
+ Q_OBJECT
+
+public:
+ explicit QAndroidCaptureService(const QString &service, QObject *parent = 0);
+ virtual ~QAndroidCaptureService();
+
+ QObject *requestControl(const char *name);
+ void releaseControl(QObject *);
+
+private:
+ QString m_service;
+
+ QAndroidMediaRecorderControl *m_recorderControl;
+ QAndroidCaptureSession *m_captureSession;
+ QAndroidCameraControl *m_cameraControl;
+ QAndroidVideoDeviceSelectorControl *m_videoInputControl;
+ QAndroidAudioInputSelectorControl *m_audioInputControl;
+ QAndroidCameraSession *m_cameraSession;
+ QAndroidCameraVideoRendererControl *m_videoRendererControl;
+ QAndroidCameraExposureControl *m_cameraExposureControl;
+ QAndroidCameraFocusControl *m_cameraFocusControl;
+ QAndroidCameraImageProcessingControl *m_cameraImageProcessingControl;
+ QAndroidImageEncoderControl *m_imageEncoderControl;
+ QAndroidCameraImageCaptureControl *m_imageCaptureControl;
+ QAndroidAudioEncoderSettingsControl *m_audioEncoderSettingsControl;
+ QAndroidVideoEncoderSettingsControl *m_videoEncoderSettingsControl;
+ QAndroidMediaContainerControl *m_mediaContainerControl;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAPTURESERVICE_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp b/src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp
new file mode 100644
index 000000000..67f748994
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp
@@ -0,0 +1,594 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "androidcamera_p.h"
+#include "qandroidcamerasession_p.h"
+#include "androidmultimediautils_p.h"
+#include "qandroidmultimediautils_p.h"
+#include "qandroidvideooutput_p.h"
+#include "qandroidglobal_p.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/multimedia/platform/android/mediacapture/qandroidcapturesession_p.h b/src/multimedia/platform/android/mediacapture/qandroidcapturesession_p.h
new file mode 100644
index 000000000..7ea469334
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcapturesession_p.h
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <private/qmediastoragelocation_p.h>
+#include "androidmediarecorder_p.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/multimedia/platform/android/mediacapture/qandroidimageencodercontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidimageencodercontrol.cpp
new file mode 100644
index 000000000..1f30ea95f
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidimageencodercontrol.cpp
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "qandroidcamerasession_p.h"
+#include "androidcamera_p.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/multimedia/platform/android/mediacapture/qandroidimageencodercontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidimageencodercontrol_p.h
new file mode 100644
index 000000000..9e65660ff
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidimageencodercontrol_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/android/mediacapture/qandroidmediacontainercontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidmediacontainercontrol.cpp
new file mode 100644
index 000000000..45fe7092c
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidmediacontainercontrol.cpp
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "qandroidcapturesession_p.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/multimedia/platform/android/mediacapture/qandroidmediacontainercontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidmediacontainercontrol_p.h
new file mode 100644
index 000000000..00f09970f
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidmediacontainercontrol_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDMEDIACONTAINERCONTROL_H
+#define QANDROIDMEDIACONTAINERCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/android/mediacapture/qandroidmediarecordercontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidmediarecordercontrol.cpp
new file mode 100644
index 000000000..7a1913afc
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidmediarecordercontrol.cpp
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "qandroidcapturesession_p.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/multimedia/platform/android/mediacapture/qandroidmediarecordercontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidmediarecordercontrol_p.h
new file mode 100644
index 000000000..4f6685d28
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidmediarecordercontrol_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/android/mediacapture/qandroidmediavideoprobecontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidmediavideoprobecontrol.cpp
new file mode 100644
index 000000000..86321271a
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidmediavideoprobecontrol.cpp
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** 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_p.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/multimedia/platform/android/mediacapture/qandroidmediavideoprobecontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidmediavideoprobecontrol_p.h
new file mode 100644
index 000000000..324370e97
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidmediavideoprobecontrol_p.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/android/mediacapture/qandroidvideodeviceselectorcontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidvideodeviceselectorcontrol.cpp
new file mode 100644
index 000000000..5403647f7
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidvideodeviceselectorcontrol.cpp
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "qandroidcamerasession_p.h"
+#include "androidcamera_p.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;
+}
+
+QCamera::Position QAndroidVideoDeviceSelectorControl::cameraPosition(int index) const
+{
+ if (index < 0 || index >= QAndroidCameraSession::availableCameras().count())
+ return QCamera::UnspecifiedPosition;
+
+ return QAndroidCameraSession::availableCameras().at(index).position;
+}
+
+int QAndroidVideoDeviceSelectorControl::cameraOrientation(int index) const
+{
+ if (index < 0 || index >= QAndroidCameraSession::availableCameras().count())
+ return QCamera::UnspecifiedPosition;
+
+ return QAndroidCameraSession::availableCameras().at(index).orientation;
+}
+
+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/multimedia/platform/android/mediacapture/qandroidvideodeviceselectorcontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidvideodeviceselectorcontrol_p.h
new file mode 100644
index 000000000..d074e7441
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidvideodeviceselectorcontrol_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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;
+ QCamera::Position cameraPosition(int index) const;
+ int cameraOrientation(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/multimedia/platform/android/mediacapture/qandroidvideoencodersettingscontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidvideoencodersettingscontrol.cpp
new file mode 100644
index 000000000..ef3559c0d
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidvideoencodersettingscontrol.cpp
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "qandroidcapturesession_p.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/multimedia/platform/android/mediacapture/qandroidvideoencodersettingscontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidvideoencodersettingscontrol_p.h
new file mode 100644
index 000000000..1488790a8
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidvideoencodersettingscontrol_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/android/mediaplayer/mediaplayer.pri b/src/multimedia/platform/android/mediaplayer/mediaplayer.pri
new file mode 100644
index 000000000..33348b38b
--- /dev/null
+++ b/src/multimedia/platform/android/mediaplayer/mediaplayer.pri
@@ -0,0 +1,13 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/qandroidmediaplayercontrol_p.h \
+ $$PWD/qandroidmediaservice_p.h \
+ $$PWD/qandroidmetadatareadercontrol_p.h \
+ $$PWD/qandroidmediaplayervideorenderercontrol_p.h
+
+SOURCES += \
+ $$PWD/qandroidmediaplayercontrol.cpp \
+ $$PWD/qandroidmediaservice.cpp \
+ $$PWD/qandroidmetadatareadercontrol.cpp \
+ $$PWD/qandroidmediaplayervideorenderercontrol.cpp
diff --git a/src/multimedia/platform/android/mediaplayer/qandroidmediaplayercontrol.cpp b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayercontrol.cpp
new file mode 100644
index 000000000..7e4f32e4a
--- /dev/null
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayercontrol.cpp
@@ -0,0 +1,835 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+#include "androidmediaplayer_p.h"
+#include "qandroidvideooutput_p.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);
+}
+
+QList<QAudio::Role> QAndroidMediaPlayerControl::supportedAudioRoles() const
+{
+ return QList<QAudio::Role>()
+ << QAudio::VoiceCommunicationRole
+ << QAudio::MusicRole
+ << QAudio::VideoRole
+ << QAudio::SonificationRole
+ << QAudio::AlarmRole
+ << QAudio::NotificationRole
+ << QAudio::RingtoneRole
+ << QAudio::AccessibilityRole
+ << QAudio::GameRole;
+}
+
+void QAndroidMediaPlayerControl::setCustomAudioRole(const QString &role)
+{
+ mMediaPlayer->setCustomAudioRole(role);
+}
+
+QStringList QAndroidMediaPlayerControl::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";
+}
+
+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);
+ }
+}
+
+QUrl QAndroidMediaPlayerControl::media() const
+{
+ return mMediaContent;
+}
+
+const QIODevice *QAndroidMediaPlayerControl::mediaStream() const
+{
+ return mMediaStream;
+}
+
+void QAndroidMediaPlayerControl::setMedia(const QUrl &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/multimedia/platform/android/mediaplayer/qandroidmediaplayercontrol_p.h b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayercontrol_p.h
new file mode 100644
index 000000000..b7246acb4
--- /dev/null
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayercontrol_p.h
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <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;
+ QUrl media() const override;
+ const QIODevice *mediaStream() const override;
+ void setMedia(const QUrl &mediaContent, QIODevice *stream) override;
+
+ void setAudioRole(QAudio::Role role) override;
+ QList<QAudio::Role> supportedAudioRoles() const override;
+ void setCustomAudioRole(const QString &role) override;
+ QStringList supportedCustomAudioRoles() const 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;
+
+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;
+ QUrl 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/multimedia/platform/android/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp
new file mode 100644
index 000000000..aed7ba671
--- /dev/null
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "qandroidmediaplayercontrol_p.h"
+#include "qandroidvideooutput_p.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/multimedia/platform/android/mediaplayer/qandroidmediaplayervideorenderercontrol_p.h b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayervideorenderercontrol_p.h
new file mode 100644
index 000000000..91aed30fb
--- /dev/null
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayervideorenderercontrol_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/android/mediaplayer/qandroidmediaservice.cpp b/src/multimedia/platform/android/mediaplayer/qandroidmediaservice.cpp
new file mode 100644
index 000000000..7ccf1dc3a
--- /dev/null
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmediaservice.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "qandroidmediaplayercontrol_p.h"
+#include "qandroidmetadatareadercontrol_p.h"
+#include "qandroidmediaplayervideorenderercontrol_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QAndroidMediaService::QAndroidMediaService(QObject *parent)
+ : QMediaService(parent)
+ , mAudioRoleControl(nullptr)
+ , mCustomAudioRoleControl(nullptr)
+ , mVideoRendererControl(0)
+{
+ mMediaControl = new QAndroidMediaPlayerControl;
+ mMetadataControl = new QAndroidMetaDataReaderControl;
+ connect(mMediaControl, SIGNAL(mediaChanged(QUrl)),
+ mMetadataControl, SLOT(onMediaChanged(QUrl)));
+ connect(mMediaControl, SIGNAL(metaDataUpdated()),
+ mMetadataControl, SLOT(onUpdateMetaData()));
+}
+
+QAndroidMediaService::~QAndroidMediaService()
+{
+ delete mVideoRendererControl;
+ delete mMetadataControl;
+ delete mMediaControl;
+}
+
+QObject *QAndroidMediaService::requestControl(const char *name)
+{
+ if (qstrcmp(name, QMediaPlayerControl_iid) == 0)
+ return mMediaControl;
+
+ if (qstrcmp(name, QMetaDataReaderControl_iid) == 0)
+ return mMetadataControl;
+
+ if (qstrcmp(name, QVideoRendererControl_iid) == 0) {
+ if (!mVideoRendererControl) {
+ mVideoRendererControl = new QAndroidMediaPlayerVideoRendererControl(mMediaControl);
+ return mVideoRendererControl;
+ }
+ }
+
+ return 0;
+}
+
+void QAndroidMediaService::releaseControl(QObject *control)
+{
+ if (control == mVideoRendererControl) {
+ delete mVideoRendererControl;
+ mVideoRendererControl = 0;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediaplayer/qandroidmediaservice_p.h b/src/multimedia/platform/android/mediaplayer/qandroidmediaservice_p.h
new file mode 100644
index 000000000..984c95ba3
--- /dev/null
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmediaservice_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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;
+
+ QObject *requestControl(const char *name) override;
+ void releaseControl(QObject *control) override;
+
+private:
+ QAndroidMediaPlayerControl *mMediaControl;
+ QAndroidMetaDataReaderControl *mMetadataControl;
+ QAndroidAudioRoleControl *mAudioRoleControl;
+ QAndroidCustomAudioRoleControl *mCustomAudioRoleControl;
+ QAndroidMediaPlayerVideoRendererControl *mVideoRendererControl;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMEDIASERVICE_H
diff --git a/src/multimedia/platform/android/mediaplayer/qandroidmetadatareadercontrol.cpp b/src/multimedia/platform/android/mediaplayer/qandroidmetadatareadercontrol.cpp
new file mode 100644
index 000000000..31263335b
--- /dev/null
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmetadatareadercontrol.cpp
@@ -0,0 +1,248 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "androidmediametadataretriever_p.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 QUrl &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/multimedia/platform/android/mediaplayer/qandroidmetadatareadercontrol_p.h b/src/multimedia/platform/android/mediaplayer/qandroidmetadatareadercontrol_p.h
new file mode 100644
index 000000000..2ee857fa1
--- /dev/null
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmetadatareadercontrol_p.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QMetaDataReaderControl>
+#include <QUrl.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 QUrl &media);
+ void onUpdateMetaData();
+
+private:
+ void updateData(const QVariantMap &metadata, const QUrl &url);
+ static void extractMetadata(QAndroidMetaDataReaderControl *caller, const QUrl &url);
+
+ mutable QMutex m_mtx;
+ QUrl m_mediaContent;
+ bool m_available;
+ QVariantMap m_metadata;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMETADATAREADERCONTROL_H
diff --git a/src/multimedia/platform/android/qandroidmediaserviceplugin.cpp b/src/multimedia/platform/android/qandroidmediaserviceplugin.cpp
new file mode 100644
index 000000000..52cedb5bd
--- /dev/null
+++ b/src/multimedia/platform/android/qandroidmediaserviceplugin.cpp
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "qandroidmediaservice_p.h"
+#include "qandroidcaptureservice_p.h"
+#include "qandroidaudioinputselectorcontrol_p.h"
+#include "qandroidcamerasession_p.h"
+#include "androidmediaplayer_p.h"
+#include "androidsurfacetexture_p.h"
+#include "androidcamera_p.h"
+#include "androidmultimediautils_p.h"
+#include "androidmediarecorder_p.h"
+#include "androidsurfaceview_p.h"
+#include "qandroidglobal_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qtAndroidMediaPlugin, "qt.multimedia.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;
+}
+
+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();
+}
+
+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/multimedia/platform/android/qandroidmediaserviceplugin_p.h b/src/multimedia/platform/android/qandroidmediaserviceplugin_p.h
new file mode 100644
index 000000000..8c024cad7
--- /dev/null
+++ b/src/multimedia/platform/android/qandroidmediaserviceplugin_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QMediaServiceProviderPlugin>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidMediaServicePlugin
+ : public QMediaServiceProviderPlugin
+ , public QMediaServiceSupportedDevicesInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
+ 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;
+
+ 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 // QANDROIDMEDIASERVICEPLUGIN_H
diff --git a/src/multimedia/platform/android/wrappers/jni/androidcamera.cpp b/src/multimedia/platform/android/wrappers/jni/androidcamera.cpp
new file mode 100644
index 000000000..60fc4ec8a
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidcamera.cpp
@@ -0,0 +1,1712 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "androidsurfacetexture_p.h"
+#include "androidsurfaceview_p.h"
+#include "qandroidmultimediautils_p.h"
+#include "qandroidglobal_p.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/multimedia/platform/android/wrappers/jni/androidcamera_p.h b/src/multimedia/platform/android/wrappers/jni/androidcamera_p.h
new file mode 100644
index 000000000..5536e5919
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidcamera_p.h
@@ -0,0 +1,248 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/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/multimedia/platform/android/wrappers/jni/androidmediametadataretriever.cpp b/src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever.cpp
new file mode 100644
index 000000000..3fa480acb
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever.cpp
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.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/multimedia/platform/android/wrappers/jni/androidmediametadataretriever_p.h b/src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever_p.h
new file mode 100644
index 000000000..8a7b75480
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever_p.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/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/multimedia/platform/android/wrappers/jni/androidmediaplayer.cpp b/src/multimedia/platform/android/wrappers/jni/androidmediaplayer.cpp
new file mode 100644
index 000000000..7fc15e788
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidmediaplayer.cpp
@@ -0,0 +1,435 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.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/multimedia/platform/android/wrappers/jni/androidmediaplayer_p.h b/src/multimedia/platform/android/wrappers/jni/androidmediaplayer_p.h
new file mode 100644
index 000000000..bf7a7002b
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidmediaplayer_p.h
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/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/multimedia/platform/android/wrappers/jni/androidmediarecorder.cpp b/src/multimedia/platform/android/wrappers/jni/androidmediarecorder.cpp
new file mode 100644
index 000000000..97bbd3b6a
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidmediarecorder.cpp
@@ -0,0 +1,405 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "androidcamera_p.h"
+#include "androidsurfacetexture_p.h"
+#include "androidsurfaceview_p.h"
+#include "qandroidglobal_p.h"
+#include "qandroidmultimediautils_p.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/multimedia/platform/android/wrappers/jni/androidmediarecorder_p.h b/src/multimedia/platform/android/wrappers/jni/androidmediarecorder_p.h
new file mode 100644
index 000000000..9cba14f75
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidmediarecorder_p.h
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/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/multimedia/platform/android/wrappers/jni/androidmultimediautils.cpp b/src/multimedia/platform/android/wrappers/jni/androidmultimediautils.cpp
new file mode 100644
index 000000000..387cb1721
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidmultimediautils.cpp
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.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/multimedia/platform/android/wrappers/jni/androidmultimediautils_p.h b/src/multimedia/platform/android/wrappers/jni/androidmultimediautils_p.h
new file mode 100644
index 000000000..cb80c5c1e
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidmultimediautils_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#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>
+#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/multimedia/platform/android/wrappers/jni/androidsurfacetexture.cpp b/src/multimedia/platform/android/wrappers/jni/androidsurfacetexture.cpp
new file mode 100644
index 000000000..8f9be7c3b
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidsurfacetexture.cpp
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.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/multimedia/platform/android/wrappers/jni/androidsurfacetexture_p.h b/src/multimedia/platform/android/wrappers/jni/androidsurfacetexture_p.h
new file mode 100644
index 000000000..5404bb7a7
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidsurfacetexture_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/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/multimedia/platform/android/wrappers/jni/androidsurfaceview.cpp b/src/multimedia/platform/android/wrappers/jni/androidsurfaceview.cpp
new file mode 100644
index 000000000..cbe17a6f9
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidsurfaceview.cpp
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.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/multimedia/platform/android/wrappers/jni/androidsurfaceview_p.h b/src/multimedia/platform/android/wrappers/jni/androidsurfaceview_p.h
new file mode 100644
index 000000000..7d89df09b
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidsurfaceview_p.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/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/multimedia/platform/android/wrappers/jni/jni.pri b/src/multimedia/platform/android/wrappers/jni/jni.pri
new file mode 100644
index 000000000..14bc78573
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/jni.pri
@@ -0,0 +1,21 @@
+QT += core-private
+
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/androidmediaplayer_p.h \
+ $$PWD/androidsurfacetexture_p.h \
+ $$PWD/androidmediametadataretriever_p.h \
+ $$PWD/androidcamera_p.h \
+ $$PWD/androidmultimediautils_p.h \
+ $$PWD/androidmediarecorder_p.h \
+ $$PWD/androidsurfaceview_p.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/multimedia/platform/avfoundation/avfoundation.pri b/src/multimedia/platform/avfoundation/avfoundation.pri
new file mode 100644
index 000000000..29be7ec58
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/avfoundation.pri
@@ -0,0 +1,18 @@
+LIBS += -framework CoreFoundation \
+ -framework Foundation \
+ -framework AudioToolbox \
+ -framework CoreAudio \
+ -framework QuartzCore \
+ -framework CoreMedia \
+ -framework CoreVideo \
+ -framework QuartzCore \
+ -framework Metal
+osx:LIBS += -framework AppKit \
+ -framework AudioUnit
+ios:LIBS += -framework CoreGraphics \
+ -framework CoreVideo
+
+QMAKE_USE += avfoundation
+
+include(mediaplayer/mediaplayer.pri)
+!tvos:include(camera/camera.pri)
diff --git a/src/multimedia/platform/avfoundation/camera/avfaudioencodersettingscontrol.mm b/src/multimedia/platform/avfoundation/camera/avfaudioencodersettingscontrol.mm
new file mode 100644
index 000000000..b613ca32a
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfaudioencodersettingscontrol.mm
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "avfcameraservice_p.h"
+#include "avfcamerasession_p.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/multimedia/platform/avfoundation/camera/avfaudioencodersettingscontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfaudioencodersettingscontrol_p.h
new file mode 100644
index 000000000..b1851a5bf
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfaudioencodersettingscontrol_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/avfoundation/camera/avfaudioinputselectorcontrol.mm b/src/multimedia/platform/avfoundation/camera/avfaudioinputselectorcontrol.mm
new file mode 100644
index 000000000..21ff70917
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfaudioinputselectorcontrol.mm
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "avfaudioinputselectorcontrol_p.h"
+#include "avfcameraservice_p.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_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfaudioinputselectorcontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfaudioinputselectorcontrol_p.h
new file mode 100644
index 000000000..90a9bc3fc
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfaudioinputselectorcontrol_p.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/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/multimedia/platform/avfoundation/camera/avfcameracontrol.mm b/src/multimedia/platform/avfoundation/camera/avfcameracontrol.mm
new file mode 100644
index 000000000..27c28587f
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameracontrol.mm
@@ -0,0 +1,530 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "avfcameracontrol_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcamerautility_p.h"
+#include "avfcamerarenderercontrol_p.h"
+#include "qabstractvideosurface.h"
+
+QT_USE_NAMESPACE
+
+AVFCameraControl::AVFCameraControl(AVFCameraService *service, QObject *parent)
+ : QCameraControl(parent)
+ , m_session(service->session())
+ , m_service(service)
+ , 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;
+
+ m_captureMode = mode;
+ Q_EMIT captureModeChanged(mode);
+}
+
+bool AVFCameraControl::isCaptureModeSupported(QCamera::CaptureModes mode) const
+{
+ return true;
+}
+
+bool AVFCameraControl::canChangeProperty(QCameraControl::PropertyChangeType changeType, QCamera::Status status) const
+{
+ Q_UNUSED(changeType);
+ Q_UNUSED(status);
+
+ return true;
+}
+
+QCamera::LockTypes AVFCameraControl::supportedLocks() const
+{
+ return {};
+}
+
+QCamera::LockStatus AVFCameraControl::lockStatus(QCamera::LockType) const
+{
+ return QCamera::Unlocked;
+}
+
+void AVFCameraControl::searchAndLock(QCamera::LockTypes locks)
+{
+ Q_UNUSED(locks);
+}
+
+void AVFCameraControl::unlock(QCamera::LockTypes locks)
+{
+ Q_UNUSED(locks);
+}
+
+
+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;
+}
+
+}
+
+QList<QCameraViewfinderSettings> AVFCameraControl::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 AVFCameraControl::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 AVFCameraControl::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 AVFCameraControl::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 AVFCameraControl::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 *AVFCameraControl::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> AVFCameraControl::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 AVFCameraControl::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 AVFCameraControl::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 AVFCameraControl::requestedSettings() const
+{
+ return m_settings;
+}
+
+AVCaptureConnection *AVFCameraControl::videoConnection() const
+{
+ if (!m_service->videoOutput() || !m_service->videoOutput()->videoDataOutput())
+ return nil;
+
+ return [m_service->videoOutput()->videoDataOutput() connectionWithMediaType:AVMediaTypeVideo];
+}
+
+#include "moc_avfcameracontrol_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfcameracontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfcameracontrol_p.h
new file mode 100644
index 000000000..2cb464fe5
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameracontrol_p.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/qcameracontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class AVFCameraSession;
+class AVFCameraService;
+@class AVCaptureDeviceFormat;
+@class AVCaptureConnection;
+
+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;
+
+ 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;
+
+ 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;
+
+private Q_SLOTS:
+ void updateStatus();
+
+private:
+ friend class AVFCameraSession;
+ AVFCameraSession *m_session;
+ AVFCameraService *m_service;
+ QCameraViewfinderSettings m_settings;
+
+ QCamera::State m_state;
+ QCamera::Status m_lastStatus;
+ QCamera::CaptureModes m_captureMode;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfcameradebug_p.h b/src/multimedia/platform/avfoundation/camera/avfcameradebug_p.h
new file mode 100644
index 000000000..616e53d99
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameradebug_p.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/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/multimedia/platform/avfoundation/camera/avfcameradevicecontrol.mm b/src/multimedia/platform/avfoundation/camera/avfcameradevicecontrol.mm
new file mode 100644
index 000000000..ac5711fb1
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameradevicecontrol.mm
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "avfcameradevicecontrol_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcamerasession_p.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;
+}
+
+QCamera::Position AVFCameraDeviceControl::cameraPosition(int index) const
+{
+ const QList<AVFCameraInfo> &devices = AVFCameraSession::availableCameraDevices();
+ if (index < 0 || index >= devices.count())
+ return QCamera::UnspecifiedPosition;
+
+ return devices.at(index).position;
+}
+
+int AVFCameraDeviceControl::cameraOrientation(int index) const
+{
+ const QList<AVFCameraInfo> &devices = AVFCameraSession::availableCameraDevices();
+ if (index < 0 || index >= devices.count())
+ return 0;
+
+ return devices.at(index).orientation;
+}
+
+
+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_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfcameradevicecontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfcameradevicecontrol_p.h
new file mode 100644
index 000000000..0fb8628b2
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameradevicecontrol_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/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;
+ QCamera::Position cameraPosition(int index) const override;
+ int cameraOrientation(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/multimedia/platform/avfoundation/camera/avfcameraexposurecontrol.mm b/src/multimedia/platform/avfoundation/camera/avfcameraexposurecontrol.mm
new file mode 100644
index 000000000..a0b2ae06d
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameraexposurecontrol.mm
@@ -0,0 +1,831 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+#include "avfcamerautility_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcameradebug_p.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(QCamera::State)));
+}
+
+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(QCamera::State newState)
+{
+#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
+
+ 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());
+ }
+}
+
+
+
+QCameraExposure::FlashModes AVFCameraExposureControl::flashMode() const
+{
+ return m_flashMode;
+}
+
+void AVFCameraExposureControl::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 AVFCameraExposureControl::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 AVFCameraExposureControl::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;
+}
+
+bool AVFCameraExposureControl::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;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_avfcameraexposurecontrol_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfcameraexposurecontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfcameraexposurecontrol_p.h
new file mode 100644
index 000000000..9cf60a7b8
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameraexposurecontrol_p.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/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;
+
+ 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;
+
+ 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);
+
+ // Set of bits:
+ QCameraExposure::FlashModes m_supportedModes = QCameraExposure::FlashOff;
+ // Only one bit set actually:
+ QCameraExposure::FlashModes m_flashMode = QCameraExposure::FlashOff;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerafocuscontrol.mm b/src/multimedia/platform/avfoundation/camera/avfcamerafocuscontrol.mm
new file mode 100644
index 000000000..62a7d55e2
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerafocuscontrol.mm
@@ -0,0 +1,422 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+#include "avfcamerautility_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameradebug_p.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];
+ }
+ }
+
+#ifdef Q_OS_IOS
+ 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;
+ }
+
+ 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();
+#endif
+}
+
+qreal AVFCameraFocusControl::maximumOpticalZoom() const
+{
+ // Not supported.
+ return 1.;
+}
+
+qreal AVFCameraFocusControl::maximumDigitalZoom() const
+{
+ return m_maxZoomFactor;
+}
+
+qreal AVFCameraFocusControl::requestedOpticalZoom() const
+{
+ // Not supported.
+ return 1;
+}
+
+qreal AVFCameraFocusControl::requestedDigitalZoom() const
+{
+ return m_requestedZoomFactor;
+}
+
+qreal AVFCameraFocusControl::currentOpticalZoom() const
+{
+ // Not supported.
+ return 1.;
+}
+
+qreal AVFCameraFocusControl::currentDigitalZoom() const
+{
+ return m_zoomFactor;
+}
+
+void AVFCameraFocusControl::zoomTo(qreal optical, qreal digital)
+{
+ Q_UNUSED(optical);
+ Q_UNUSED(digital);
+
+#ifdef QOS_IOS
+ if (qFuzzyCompare(CGFloat(digital), m_requestedZoomFactor))
+ return;
+
+ m_requestedZoomFactor = digital;
+ Q_EMIT requestedDigitalZoomChanged(digital);
+
+ zoomToRequestedDigital();
+#endif
+}
+
+#ifdef QOS_IOS
+void AVFCameraFocusControl::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);
+ }
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_avfcamerafocuscontrol_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerafocuscontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfcamerafocuscontrol_p.h
new file mode 100644
index 000000000..3527d48b6
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerafocuscontrol_p.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/qscopedpointer.h>
+#include <QtCore/qglobal.h>
+
+#include <qcamerafocuscontrol.h>
+
+#include <AVFoundation/AVFoundation.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;
+
+ 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:
+#ifdef QOS_IOS
+ void zoomToRequestedDigital();
+#endif
+
+ AVFCameraSession *m_session;
+ QCameraFocus::FocusModes m_focusMode;
+ QCameraFocus::FocusPointMode m_focusPointMode;
+ QPointF m_customFocusPoint;
+ QPointF m_actualFocusPoint;
+
+ CGFloat m_maxZoomFactor;
+ CGFloat m_zoomFactor;
+ CGFloat m_requestedZoomFactor;
+};
+
+
+QT_END_NAMESPACE
+
+#endif // AVFCAMERAFOCUSCONTROL_H
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerametadatacontrol.mm b/src/multimedia/platform/avfoundation/camera/avfcamerametadatacontrol.mm
new file mode 100644
index 000000000..4addfd938
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerametadatacontrol.mm
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameraservice_p.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_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerametadatacontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfcamerametadatacontrol_p.h
new file mode 100644
index 000000000..2f9138986
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerametadatacontrol_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmetadatawritercontrol.h>
+#include <QtCore/qvariant.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/multimedia/platform/avfoundation/camera/avfcamerarenderercontrol.mm b/src/multimedia/platform/avfoundation/camera/avfcamerarenderercontrol.mm
new file mode 100644
index 000000000..13131766c
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerarenderercontrol.mm
@@ -0,0 +1,409 @@
+/****************************************************************************
+**
+** 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 "private/qabstractvideobuffer_p.h"
+#include "avfcamerarenderercontrol_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcameradebug_p.h"
+#include "avfcameracontrol_p.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 QAbstractVideoBuffer
+{
+public:
+ CVImageVideoBuffer(CVImageBufferRef buffer, AVFCameraRendererControl *renderer)
+#ifndef Q_OS_IOS
+ : QAbstractVideoBuffer(NoHandle)
+#else
+ : QAbstractVideoBuffer(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; }
+
+ MapData map(QAbstractVideoBuffer::MapMode mode)
+ {
+ MapData mapData;
+
+ // We only support RGBA or NV12 (or Apple's version of NV12),
+ // they are either 0 planes or 2.
+ mapData.nPlanes = CVPixelBufferGetPlaneCount(m_buffer);
+ Q_ASSERT(mapData.nPlanes <= 2);
+
+ if (!mapData.nPlanes) {
+ mapData.data[0] = map(mode, &mapData.nBytes, &mapData.bytesPerLine[0]);
+ mapData.nPlanes = mapData.data[0] ? 1 : 0;
+ return mapData;
+ }
+
+ // 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);
+
+ mapData.nBytes = CVPixelBufferGetDataSize(m_buffer);
+
+ // At the moment we handle only bi-planar format.
+ mapData.bytesPerLine[0] = CVPixelBufferGetBytesPerRowOfPlane(m_buffer, 0);
+ mapData.bytesPerLine[1] = CVPixelBufferGetBytesPerRowOfPlane(m_buffer, 1);
+
+ mapData.data[0] = static_cast<uchar*>(CVPixelBufferGetBaseAddressOfPlane(m_buffer, 0));
+ mapData.data[1] = static_cast<uchar*>(CVPixelBufferGetBaseAddressOfPlane(m_buffer, 1));
+
+ m_mode = mode;
+ }
+
+ return mapData;
+ }
+
+ uchar *map(MapMode mode, qsizetype *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 =
+ AVFCameraControl::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_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerarenderercontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfcamerarenderercontrol_p.h
new file mode 100644
index 000000000..ade916ed6
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerarenderercontrol_p.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/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/multimedia/platform/avfoundation/camera/avfcameraservice.mm b/src/multimedia/platform/avfoundation/camera/avfcameraservice.mm
new file mode 100644
index 000000000..9fcab0ead
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameraservice.mm
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "avfcameracontrol_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameradevicecontrol_p.h"
+#include "avfaudioinputselectorcontrol_p.h"
+#include "avfcamerametadatacontrol_p.h"
+#include "avfmediarecordercontrol_p.h"
+#include "avfimagecapturecontrol_p.h"
+#include "avfcamerarenderercontrol_p.h"
+#include "avfmediarecordercontrol_p.h"
+#include "avfimagecapturecontrol_p.h"
+#include "avfmediavideoprobecontrol_p.h"
+#include "avfcamerafocuscontrol_p.h"
+#include "avfcameraexposurecontrol_p.h"
+#include "avfimageencodercontrol_p.h"
+#include "avfaudioencodersettingscontrol_p.h"
+#include "avfvideoencodersettingscontrol_p.h"
+#include "avfmediacontainercontrol_p.h"
+#include "avfcamerawindowcontrol_p.h"
+
+#ifdef Q_OS_IOS
+#include "avfmediarecordercontrol_ios_p.h"
+#endif
+
+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_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_imageEncoderControl = new AVFImageEncoderControl(this);
+ m_audioEncoderSettingsControl = new AVFAudioEncoderSettingsControl(this);
+ m_videoEncoderSettingsControl = new AVFVideoEncoderSettingsControl(this);
+ m_mediaContainerControl = new AVFMediaContainerControl(this);
+}
+
+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;
+ delete m_imageEncoderControl;
+ delete m_audioEncoderSettingsControl;
+ delete m_videoEncoderSettingsControl;
+ delete m_mediaContainerControl;
+
+ delete m_session;
+}
+
+QObject *AVFCameraService::requestControl(const char *name)
+{
+ if (qstrcmp(name, QCameraControl_iid) == 0)
+ return m_cameraControl;
+
+ 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, QImageEncoderControl_iid) == 0)
+ return m_imageEncoderControl;
+
+ 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;
+ }
+
+ 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(QObject *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_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfcameraservice_p.h b/src/multimedia/platform/avfoundation/camera/avfcameraservice_p.h
new file mode 100644
index 000000000..9efe4e9cb
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameraservice_p.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <qmediaservice.h>
+
+
+QT_BEGIN_NAMESPACE
+class QCameraControl;
+class QMediaRecorderControl;
+class AVFCameraControl;
+class AVFCameraMetaDataControl;
+class AVFVideoWindowControl;
+class AVFVideoWidgetControl;
+class AVFCameraRendererControl;
+class AVFImageCaptureControl;
+class AVFCameraSession;
+class AVFCameraDeviceControl;
+class AVFAudioInputSelectorControl;
+class AVFCameraFocusControl;
+class AVFCameraExposureControl;
+class AVFImageEncoderControl;
+class AVFMediaRecorderControl;
+class AVFMediaRecorderControlIOS;
+class AVFAudioEncoderSettingsControl;
+class AVFVideoEncoderSettingsControl;
+class AVFMediaContainerControl;
+class AVFCameraWindowControl;
+
+class AVFCameraService : public QMediaService
+{
+Q_OBJECT
+public:
+ AVFCameraService(QObject *parent = nullptr);
+ ~AVFCameraService();
+
+ QObject *requestControl(const char *name);
+ void releaseControl(QObject *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; }
+ AVFCameraRendererControl *videoOutput() const {return m_videoOutput; }
+ AVFImageEncoderControl *imageEncoderControl() const {return m_imageEncoderControl; }
+ AVFAudioEncoderSettingsControl *audioEncoderSettingsControl() const { return m_audioEncoderSettingsControl; }
+ AVFVideoEncoderSettingsControl *videoEncoderSettingsControl() const {return m_videoEncoderSettingsControl; }
+ AVFMediaContainerControl *mediaContainerControl() const { return m_mediaContainerControl; }
+
+private:
+ AVFCameraSession *m_session;
+ AVFCameraControl *m_cameraControl;
+ AVFCameraDeviceControl *m_videoDeviceControl;
+ AVFAudioInputSelectorControl *m_audioInputSelectorControl;
+ AVFCameraRendererControl *m_videoOutput;
+ AVFCameraMetaDataControl *m_metaDataControl;
+ QMediaRecorderControl *m_recorderControl;
+ AVFImageCaptureControl *m_imageCaptureControl;
+ AVFCameraFocusControl *m_cameraFocusControl;
+ AVFCameraExposureControl *m_cameraExposureControl;
+ AVFImageEncoderControl *m_imageEncoderControl;
+ AVFAudioEncoderSettingsControl *m_audioEncoderSettingsControl;
+ AVFVideoEncoderSettingsControl *m_videoEncoderSettingsControl;
+ AVFMediaContainerControl *m_mediaContainerControl;
+ AVFCameraWindowControl *m_captureWindowControl;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfcameraserviceplugin.mm b/src/multimedia/platform/avfoundation/camera/avfcameraserviceplugin.mm
new file mode 100644
index 000000000..2fd8d3fad
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameraserviceplugin.mm
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcamerasession_p.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();
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/avfoundation/camera/avfcameraserviceplugin_p.h b/src/multimedia/platform/avfoundation/camera/avfcameraserviceplugin_p.h
new file mode 100644
index 000000000..e9028542c
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcameraserviceplugin_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmediaserviceproviderplugin.h>
+#include <QtCore/qmap.h>
+
+QT_BEGIN_NAMESPACE
+
+class AVFServicePlugin : public QMediaServiceProviderPlugin,
+ public QMediaServiceSupportedDevicesInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
+
+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;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerasession.mm b/src/multimedia/platform/avfoundation/camera/avfcamerasession.mm
new file mode 100644
index 000000000..914ae6907
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerasession.mm
@@ -0,0 +1,491 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcameracontrol_p.h"
+#include "avfcamerarenderercontrol_p.h"
+#include "avfcameradevicecontrol_p.h"
+#include "avfaudioinputselectorcontrol_p.h"
+#include "avfmediavideoprobecontrol_p.h"
+#include "avfimageencodercontrol_p.h"
+#include "avfcamerautility_p.h"
+#include "avfcamerawindowcontrol_p.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 (auto *camera = m_service->cameraControl()) {
+ m_capturePreviewWindowOutput->setNativeSize(camera->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 (auto *camera = m_service->cameraControl()) {
+ QCamera::CaptureModes currentMode = m_service->cameraControl()->captureMode();
+ QCameraViewfinderSettings vfSettings(camera->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);
+ }
+
+ camera->applySettings(vfSettings);
+
+ if (m_capturePreviewWindowOutput)
+ m_capturePreviewWindowOutput->setNativeSize(camera->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_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerasession_p.h b/src/multimedia/platform/avfoundation/camera/avfcamerasession_p.h
new file mode 100644
index 000000000..19f7945a3
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerasession_p.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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>
+
+#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/multimedia/platform/avfoundation/camera/avfcamerautility.mm b/src/multimedia/platform/avfoundation/camera/avfcamerautility.mm
new file mode 100644
index 000000000..fe56517df
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerautility.mm
@@ -0,0 +1,575 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+#include "avfcameradebug_p.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/multimedia/platform/avfoundation/camera/avfcamerautility_p.h b/src/multimedia/platform/avfoundation/camera/avfcamerautility_p.h
new file mode 100644
index 000000000..ffa9630a7
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerautility_p.h
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <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/multimedia/platform/avfoundation/camera/avfcamerawindowcontrol.mm b/src/multimedia/platform/avfoundation/camera/avfcamerawindowcontrol.mm
new file mode 100644
index 000000000..6c457318d
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerawindowcontrol.mm
@@ -0,0 +1,262 @@
+/****************************************************************************
+**
+** 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_p.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_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfcamerawindowcontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfcamerawindowcontrol_p.h
new file mode 100644
index 000000000..d1a950e38
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfcamerawindowcontrol_p.h
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** 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/multimedia/platform/avfoundation/camera/avfimagecapturecontrol.mm b/src/multimedia/platform/avfoundation/camera/avfimagecapturecontrol.mm
new file mode 100644
index 000000000..b019cf2ef
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfimagecapturecontrol.mm
@@ -0,0 +1,278 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "avfimagecapturecontrol_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcamerautility_p.h"
+#include "avfcameracontrol_p.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->imageCaptureControl()->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
+}
+
+QCameraImageCapture::CaptureDestinations AVFImageCaptureControl::captureDestination() const
+{
+ return m_destination;
+}
+
+void AVFImageCaptureControl::setCaptureDestination(QCameraImageCapture::CaptureDestinations destination)
+{
+ if (m_destination != destination) {
+ m_destination = destination;
+ updateCaptureConnection();
+ }
+}
+
+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_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfimagecapturecontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfimagecapturecontrol_p.h
new file mode 100644
index 000000000..3c781a475
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfimagecapturecontrol_p.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It 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 <QtMultimedia/qcameraimagecapturecontrol.h>
+#include "avfcamerasession_p.h"
+#include "avfstoragelocation_p.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;
+
+ QCameraImageCapture::CaptureDestinations captureDestination() const override;
+ void setCaptureDestination(QCameraImageCapture::CaptureDestinations destination) 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;
+
+ QCameraImageCapture::CaptureDestinations m_destination = QCameraImageCapture::CaptureToFile;
+};
+
+Q_DECLARE_TYPEINFO(AVFImageCaptureControl::CaptureRequest, Q_PRIMITIVE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/avfoundation/camera/avfimageencodercontrol.mm b/src/multimedia/platform/avfoundation/camera/avfimageencodercontrol.mm
new file mode 100644
index 000000000..113fdb62b
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfimageencodercontrol.mm
@@ -0,0 +1,239 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "avfimageencodercontrol_p.h"
+#include "avfimagecapturecontrol_p.h"
+#include "avfcamerautility_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcameradebug_p.h"
+#include "avfcameracontrol_p.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_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfimageencodercontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfimageencodercontrol_p.h
new file mode 100644
index 000000000..8c2742d35
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfimageencodercontrol_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/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/multimedia/platform/avfoundation/camera/avfmediaassetwriter.mm b/src/multimedia/platform/avfoundation/camera/avfmediaassetwriter.mm
new file mode 100644
index 000000000..57c5cb8c5
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediaassetwriter.mm
@@ -0,0 +1,514 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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/multimedia/platform/avfoundation/camera/avfmediaassetwriter_p.h b/src/multimedia/platform/avfoundation/camera/avfmediaassetwriter_p.h
new file mode 100644
index 000000000..61d7d46bc
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediaassetwriter_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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.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/multimedia/platform/avfoundation/camera/avfmediacontainercontrol.mm b/src/multimedia/platform/avfoundation/camera/avfmediacontainercontrol.mm
new file mode 100644
index 000000000..09049de0b
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediacontainercontrol.mm
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include <AVFoundation/AVMediaFormat.h>
+#include <QtCore/qmap.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/multimedia/platform/avfoundation/camera/avfmediacontainercontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfmediacontainercontrol_p.h
new file mode 100644
index 000000000..9450dc16a
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediacontainercontrol_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/avfoundation/camera/avfmediarecordercontrol.mm b/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol.mm
new file mode 100644
index 000000000..27f78fea4
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol.mm
@@ -0,0 +1,430 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "avfmediarecordercontrol_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcameraservice_p.h"
+#include "avfcameracontrol_p.h"
+#include "avfaudioinputselectorcontrol_p.h"
+#include "avfaudioencodersettingscontrol_p.h"
+#include "avfvideoencodersettingscontrol_p.h"
+#include "avfmediacontainercontrol_p.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_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_ios.mm b/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_ios.mm
new file mode 100644
index 000000000..33064827d
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_ios.mm
@@ -0,0 +1,414 @@
+/****************************************************************************
+**
+** 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/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_ios_p.h b/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_ios_p.h
new file mode 100644
index 000000000..178c75cad
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_ios_p.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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.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/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_p.h
new file mode 100644
index 000000000..9d8f6566b
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediarecordercontrol_p.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/qurl.h>
+#include <QtMultimedia/qmediarecordercontrol.h>
+
+#import <AVFoundation/AVFoundation.h>
+#include "avfstoragelocation_p.h"
+#include "avfcamerautility_p.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/multimedia/platform/avfoundation/camera/avfmediavideoprobecontrol.mm b/src/multimedia/platform/avfoundation/camera/avfmediavideoprobecontrol.mm
new file mode 100644
index 000000000..c97ab1919
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediavideoprobecontrol.mm
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** 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_p.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/multimedia/platform/avfoundation/camera/avfmediavideoprobecontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfmediavideoprobecontrol_p.h
new file mode 100644
index 000000000..c18bc4181
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfmediavideoprobecontrol_p.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/avfoundation/camera/avfstoragelocation.mm b/src/multimedia/platform/avfoundation/camera/avfstoragelocation.mm
new file mode 100644
index 000000000..1855f8ec7
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfstoragelocation.mm
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** 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_p.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/multimedia/platform/avfoundation/camera/avfstoragelocation_p.h b/src/multimedia/platform/avfoundation/camera/avfstoragelocation_p.h
new file mode 100644
index 000000000..8794f0fae
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfstoragelocation_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/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/multimedia/platform/avfoundation/camera/avfvideoencodersettingscontrol.mm b/src/multimedia/platform/avfoundation/camera/avfvideoencodersettingscontrol.mm
new file mode 100644
index 000000000..0800e2021
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfvideoencodersettingscontrol.mm
@@ -0,0 +1,385 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "avfcameraservice_p.h"
+#include "avfcamerautility_p.h"
+#include "avfcamerasession_p.h"
+#include "avfcamerarenderercontrol_p.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_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/camera/avfvideoencodersettingscontrol_p.h b/src/multimedia/platform/avfoundation/camera/avfvideoencodersettingscontrol_p.h
new file mode 100644
index 000000000..6d44d4a33
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/avfvideoencodersettingscontrol_p.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qvideoencodersettingscontrol.h>
+
+#include "avfcamerautility_p.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/multimedia/platform/avfoundation/camera/camera.pri b/src/multimedia/platform/avfoundation/camera/camera.pri
new file mode 100644
index 000000000..430094d14
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/camera/camera.pri
@@ -0,0 +1,60 @@
+HEADERS += \
+ $$PWD/avfcameradebug_p.h \
+ $$PWD/avfcameraserviceplugin_p.h \
+ $$PWD/avfcameracontrol_p.h \
+ $$PWD/avfcamerametadatacontrol_p.h \
+ $$PWD/avfimagecapturecontrol_p.h \
+ $$PWD/avfcameraservice_p.h \
+ $$PWD/avfcamerasession_p.h \
+ $$PWD/avfstoragelocation_p.h \
+ $$PWD/avfaudioinputselectorcontrol_p.h \
+ $$PWD/avfmediavideoprobecontrol_p.h \
+ $$PWD/avfcamerarenderercontrol_p.h \
+ $$PWD/avfcameradevicecontrol_p.h \
+ $$PWD/avfcamerafocuscontrol_p.h \
+ $$PWD/avfcameraexposurecontrol_p.h \
+ $$PWD/avfcamerautility_p.h \
+ $$PWD/avfimageencodercontrol_p.h \
+ $$PWD/avfvideoencodersettingscontrol_p.h \
+ $$PWD/avfmediacontainercontrol_p.h \
+ $$PWD/avfaudioencodersettingscontrol_p.h \
+ $$PWD/avfcamerawindowcontrol_p.h \
+
+SOURCES += \
+ $$PWD/avfcameraserviceplugin.mm \
+ $$PWD/avfcameracontrol.mm \
+ $$PWD/avfcamerametadatacontrol.mm \
+ $$PWD/avfimagecapturecontrol.mm \
+ $$PWD/avfcameraservice.mm \
+ $$PWD/avfcamerasession.mm \
+ $$PWD/avfstoragelocation.mm \
+ $$PWD/avfaudioinputselectorcontrol.mm \
+ $$PWD/avfmediavideoprobecontrol.mm \
+ $$PWD/avfcameradevicecontrol.mm \
+ $$PWD/avfcamerarenderercontrol.mm \
+ $$PWD/avfcamerafocuscontrol.mm \
+ $$PWD/avfcameraexposurecontrol.mm \
+ $$PWD/avfcamerautility.mm \
+ $$PWD/avfimageencodercontrol.mm \
+ $$PWD/avfvideoencodersettingscontrol.mm \
+ $$PWD/avfmediacontainercontrol.mm \
+ $$PWD/avfaudioencodersettingscontrol.mm \
+ $$PWD/avfcamerawindowcontrol.mm \
+
+osx {
+
+HEADERS += $$PWD/avfmediarecordercontrol_p.h
+SOURCES += $$PWD/avfmediarecordercontrol.mm
+
+}
+
+ios {
+
+HEADERS += \
+ $$PWD/avfmediaassetwriter_p.h \
+ $$PWD/avfmediarecordercontrol_ios_p.h
+SOURCES += \
+ $$PWD/avfmediaassetwriter.mm \
+ $$PWD/avfmediarecordercontrol_ios.mm
+
+}
diff --git a/src/multimedia/platform/avfoundation/mediaplayer/avfdisplaylink.mm b/src/multimedia/platform/avfoundation/mediaplayer/avfdisplaylink.mm
new file mode 100644
index 000000000..64b625f0e
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfdisplaylink.mm
@@ -0,0 +1,241 @@
+/****************************************************************************
+**
+** 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_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);
+}
diff --git a/src/multimedia/platform/avfoundation/mediaplayer/avfdisplaylink_p.h b/src/multimedia/platform/avfoundation/mediaplayer/avfdisplaylink_p.h
new file mode 100644
index 000000000..6b95e1e07
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfdisplaylink_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/multimedia/platform/avfoundation/mediaplayer/avfmediaplayercontrol.mm b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayercontrol.mm
new file mode 100644
index 000000000..764c1edf8
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayercontrol.mm
@@ -0,0 +1,192 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "avfmediaplayersession_p.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();
+}
+
+QUrl AVFMediaPlayerControl::media() const
+{
+ return m_session->media();
+}
+
+const QIODevice *AVFMediaPlayerControl::mediaStream() const
+{
+ return m_session->mediaStream();
+}
+
+void AVFMediaPlayerControl::setMedia(const QUrl &content, QIODevice *stream)
+{
+ const QUrl 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/multimedia/platform/avfoundation/mediaplayer/avfmediaplayercontrol_p.h b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayercontrol_p.h
new file mode 100644
index 000000000..50a75427c
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayercontrol_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/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;
+
+ QUrl media() const override;
+ const QIODevice *mediaStream() const override;
+ void setMedia(const QUrl &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/multimedia/platform/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.mm b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.mm
new file mode 100644
index 000000000..5a5780e52
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.mm
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "avfmediaplayersession_p.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/multimedia/platform/avfoundation/mediaplayer/avfmediaplayermetadatacontrol_p.h b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayermetadatacontrol_p.h
new file mode 100644
index 000000000..1f8ecf26f
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayermetadatacontrol_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/QMetaDataReaderControl>
+#include <QtCore/qvariant.h>
+
+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/multimedia/platform/avfoundation/mediaplayer/avfmediaplayerservice.mm b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayerservice.mm
new file mode 100644
index 000000000..b8d3d4e82
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayerservice.mm
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "avfmediaplayersession_p.h"
+#include "avfmediaplayercontrol_p.h"
+#include "avfmediaplayermetadatacontrol_p.h"
+#include "avfvideooutput_p.h"
+#if QT_CONFIG(opengl)
+#include "avfvideorenderercontrol_p.h"
+#endif
+#include "avfvideowindowcontrol_p.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;
+}
+
+QObject *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
+ 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(QObject *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/multimedia/platform/avfoundation/mediaplayer/avfmediaplayerservice_p.h b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayerservice_p.h
new file mode 100644
index 000000000..8d5f63da2
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayerservice_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/QMediaService>
+
+QT_BEGIN_NAMESPACE
+
+class AVFMediaPlayerSession;
+class AVFMediaPlayerControl;
+class AVFMediaPlayerMetaDataControl;
+class AVFVideoOutput;
+
+class AVFMediaPlayerService : public QMediaService
+{
+public:
+ explicit AVFMediaPlayerService(QObject *parent = nullptr);
+ ~AVFMediaPlayerService();
+
+ QObject *requestControl(const char *name) override;
+ void releaseControl(QObject *control) override;
+
+private:
+ AVFMediaPlayerSession *m_session;
+ AVFMediaPlayerControl *m_control;
+ QObject *m_videoOutput;
+ AVFMediaPlayerMetaDataControl *m_playerMetaDataControl;
+};
+
+QT_END_NAMESPACE
+
+#endif // AVFMEDIAPLAYERSERVICE_H
diff --git a/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayerserviceplugin.mm b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayerserviceplugin.mm
new file mode 100644
index 000000000..504909598
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayerserviceplugin.mm
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include <QtCore/QDebug>
+
+#include "avfmediaplayerservice_p.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;
+}
+
+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/multimedia/platform/avfoundation/mediaplayer/avfmediaplayerserviceplugin_p.h b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayerserviceplugin_p.h
new file mode 100644
index 000000000..d6e17c446
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayerserviceplugin_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/qmediaserviceproviderplugin.h>
+
+QT_BEGIN_NAMESPACE
+
+class AVFMediaPlayerServicePlugin
+ : public QMediaServiceProviderPlugin
+ , public QMediaServiceSupportedFormatsInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QMediaServiceSupportedFormatsInterface)
+
+public:
+ explicit AVFMediaPlayerServicePlugin();
+
+ QMediaService* create(QString const& key) override;
+ void release(QMediaService *service) 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/multimedia/platform/avfoundation/mediaplayer/avfmediaplayersession.mm b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayersession.mm
new file mode 100644
index 000000000..d6824a3dc
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayersession.mm
@@ -0,0 +1,1067 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "avfmediaplayerservice_p.h"
+#include "avfvideooutput_p.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;
+}
+
+QUrl 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 QUrl &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.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 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.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/multimedia/platform/avfoundation/mediaplayer/avfmediaplayersession_p.h b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayersession_p.h
new file mode 100644
index 000000000..c3728c524
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfmediaplayersession_p.h
@@ -0,0 +1,172 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <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;
+
+ QUrl media() const;
+ QIODevice *mediaStream() const;
+ void setMedia(const QUrl &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;
+ QUrl 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/multimedia/platform/avfoundation/mediaplayer/avfvideoframerenderer.mm b/src/multimedia/platform/avfoundation/mediaplayer/avfvideoframerenderer.mm
new file mode 100644
index 000000000..88c83f1a8
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfvideoframerenderer.mm
@@ -0,0 +1,464 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include <QtMultimedia/qabstractvideosurface.h>
+#include <QtOpenGL/QOpenGLFramebufferObject>
+#include <QtGui/QWindow>
+#include <QOpenGLShaderProgram>
+
+#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;
+
+ if (m_useCoreProfile) {
+ glDeleteVertexArrays(1, &m_quadVao);
+ glDeleteBuffers(2, m_quadVbos);
+ delete m_shader;
+ }
+}
+
+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;
+ }
+
+ if (m_useCoreProfile) {
+ glDeleteVertexArrays(1, &m_quadVao);
+ glDeleteBuffers(2, m_quadVbos);
+ delete m_shader;
+ m_shader = nullptr;
+ }
+ }
+
+ //Need current context
+ if (m_glContext)
+ m_glContext->makeCurrent(m_offscreenSurface);
+
+ if (!m_metalDevice)
+ m_metalDevice = MTLCreateSystemDefaultDevice();
+
+ if (@available(macOS 10.13, *)) {
+ m_useCoreProfile = m_metalDevice && (QOpenGLContext::currentContext()->format().profile() ==
+ QSurfaceFormat::CoreProfile);
+ } else {
+ m_useCoreProfile = false;
+ }
+
+ // Create the CARenderer if needed for no Core OpenGL
+ if (!m_videoLayerRenderer) {
+ if (!m_useCoreProfile) {
+ m_videoLayerRenderer = [CARenderer rendererWithCGLContext: CGLGetCurrentContext()
+ options: nil];
+ [m_videoLayerRenderer retain];
+ } else if (@available(macOS 10.13, *)) {
+ // This is always true when m_useCoreProfile is true, but the compiler wants the check
+ // anyway
+ // Setup Core OpenGL shader, VAO, VBOs and metal renderer
+ m_shader = new QOpenGLShaderProgram();
+ m_shader->create();
+ if (!m_shader->addShaderFromSourceCode(QOpenGLShader::Vertex, R"(#version 150 core
+ in vec2 qt_VertexPosition;
+ in vec2 qt_VertexTexCoord;
+ out vec2 qt_TexCoord;
+ void main()
+ {
+ qt_TexCoord = qt_VertexTexCoord;
+ gl_Position = vec4(qt_VertexPosition, 0.0f, 1.0f);
+ })")) {
+ qCritical() << "Vertex shader compilation failed" << m_shader->log();
+ }
+ if (!m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment, R"(#version 150 core
+ in vec2 qt_TexCoord;
+ out vec4 fragColor;
+ uniform sampler2DRect videoFrame;
+ void main(void)
+ {
+ ivec2 textureDim = textureSize(videoFrame);
+ fragColor = texture(videoFrame, qt_TexCoord * textureDim);
+ })")) {
+ qCritical() << "Fragment shader compilation failed" << m_shader->log();
+ }
+
+ // Setup quad where the video frame will be attached
+ GLfloat vertices[] = {
+ -1.0f, -1.0f,
+ 1.0f, -1.0f,
+ -1.0f, 1.0f,
+ 1.0f, 1.0f,
+ };
+
+ GLfloat uvs[] = {
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ };
+
+ glGenVertexArrays(1, &m_quadVao);
+ glBindVertexArray(m_quadVao);
+
+ // Create vertex buffer objects for vertices
+ glGenBuffers(2, m_quadVbos);
+
+ // Setup vertices
+ glBindBuffer(GL_ARRAY_BUFFER, m_quadVbos[0]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), nullptr);
+ glEnableVertexAttribArray(0);
+
+ // Setup uvs
+ glBindBuffer(GL_ARRAY_BUFFER, m_quadVbos[1]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(uvs), uvs, GL_STATIC_DRAW);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), nullptr);
+ glEnableVertexAttribArray(1);
+
+ glBindVertexArray(0);
+
+ // Setup shared Metal/OpenGL pixel buffer and textures
+ m_NSGLContext = QOpenGLContext::currentContext()->nativeInterface<QNativeInterface::QCocoaGLContext>()->nativeContext();
+ m_CGLPixelFormat = m_NSGLContext.pixelFormat.CGLPixelFormatObj;
+
+ NSDictionary* cvBufferProperties = @{
+ static_cast<NSString*>(kCVPixelBufferOpenGLCompatibilityKey) : @YES,
+ static_cast<NSString*>(kCVPixelBufferMetalCompatibilityKey): @YES,
+ };
+
+ CVPixelBufferCreate(kCFAllocatorDefault, static_cast<size_t>(m_targetSize.width()),
+ static_cast<size_t>(m_targetSize.height()), kCVPixelFormatType_32BGRA,
+ static_cast<CFDictionaryRef>(cvBufferProperties), &m_CVPixelBuffer);
+
+ m_textureName = createGLTexture(reinterpret_cast<CGLContextObj>(m_NSGLContext.CGLContextObj),
+ m_CGLPixelFormat, m_CVGLTextureCache, m_CVPixelBuffer,
+ m_CVGLTexture);
+ m_metalTexture = createMetalTexture(m_metalDevice, m_CVMTLTextureCache, m_CVPixelBuffer,
+ MTLPixelFormatBGRA8Unorm,
+ static_cast<size_t>(m_targetSize.width()),
+ static_cast<size_t>(m_targetSize.height()),
+ m_CVMTLTexture);
+
+ m_videoLayerRenderer = [CARenderer rendererWithMTLTexture:m_metalTexture 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());
+
+ if (m_useCoreProfile) {
+ CGLLockContext(m_NSGLContext.CGLContextObj);
+ m_shader->bind();
+ glBindVertexArray(m_quadVao);
+ } else {
+ 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:nullptr];
+ [m_videoLayerRenderer addUpdateRect:layer.bounds];
+ [m_videoLayerRenderer render];
+ [m_videoLayerRenderer endFrame];
+
+ if (m_useCoreProfile) {
+ glActiveTexture(0);
+ glBindTexture(GL_TEXTURE_RECTANGLE, m_textureName);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ glBindTexture(GL_TEXTURE_RECTANGLE, 0);
+
+ glBindVertexArray(0);
+
+ m_shader->release();
+
+ CGLFlushDrawable(m_NSGLContext.CGLContextObj);
+ CGLUnlockContext(m_NSGLContext.CGLContextObj);
+ } else {
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ }
+
+ glFinish(); //Rendering needs to be done before passing texture to video frame
+
+ fbo->release();
+}
+
+GLuint AVFVideoFrameRenderer::createGLTexture(CGLContextObj cglContextObj, CGLPixelFormatObj cglPixelFormtObj, CVOpenGLTextureCacheRef cvglTextureCache,
+ CVPixelBufferRef cvPixelBufferRef, CVOpenGLTextureRef cvOpenGLTextureRef)
+{
+ CVReturn cvret;
+ // Create an OpenGL CoreVideo texture cache from the pixel buffer.
+ cvret = CVOpenGLTextureCacheCreate(
+ kCFAllocatorDefault,
+ nil,
+ cglContextObj,
+ cglPixelFormtObj,
+ nil,
+ &cvglTextureCache);
+
+ // Create a CVPixelBuffer-backed OpenGL texture image from the texture cache.
+ cvret = CVOpenGLTextureCacheCreateTextureFromImage(
+ kCFAllocatorDefault,
+ cvglTextureCache,
+ cvPixelBufferRef,
+ nil,
+ &cvOpenGLTextureRef);
+
+ // Get an OpenGL texture name from the CVPixelBuffer-backed OpenGL texture image.
+ return CVOpenGLTextureGetName(cvOpenGLTextureRef);
+}
+
+id<MTLTexture> AVFVideoFrameRenderer::createMetalTexture(id<MTLDevice> mtlDevice, CVMetalTextureCacheRef cvMetalTextureCacheRef, CVPixelBufferRef cvPixelBufferRef,
+ MTLPixelFormat pixelFormat, size_t width, size_t height, CVMetalTextureRef cvMetalTextureRef)
+{
+ CVReturn cvret;
+ // Create a Metal Core Video texture cache from the pixel buffer.
+ cvret = CVMetalTextureCacheCreate(
+ kCFAllocatorDefault,
+ nil,
+ mtlDevice,
+ nil,
+ &cvMetalTextureCacheRef);
+
+ // Create a CoreVideo pixel buffer backed Metal texture image from the texture cache.
+ cvret = CVMetalTextureCacheCreateTextureFromImage(
+ kCFAllocatorDefault,
+ cvMetalTextureCacheRef,
+ cvPixelBufferRef, nil,
+ pixelFormat,
+ width, height,
+ 0,
+ &cvMetalTextureRef);
+
+ // Get a Metal texture using the CoreVideo Metal texture reference.
+ return CVMetalTextureGetTexture(cvMetalTextureRef);
+}
diff --git a/src/multimedia/platform/avfoundation/mediaplayer/avfvideoframerenderer_ios.mm b/src/multimedia/platform/avfoundation/mediaplayer/avfvideoframerenderer_ios.mm
new file mode 100644
index 000000000..cbaa1aa11
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfvideoframerenderer_ios.mm
@@ -0,0 +1,307 @@
+/****************************************************************************
+**
+** 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/multimedia/platform/avfoundation/mediaplayer/avfvideoframerenderer_ios_p.h b/src/multimedia/platform/avfoundation/mediaplayer/avfvideoframerenderer_ios_p.h
new file mode 100644
index 000000000..b69e33ceb
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfvideoframerenderer_ios_p.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <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/multimedia/platform/avfoundation/mediaplayer/avfvideoframerenderer_p.h b/src/multimedia/platform/avfoundation/mediaplayer/avfvideoframerenderer_p.h
new file mode 100644
index 000000000..465c1e563
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfvideoframerenderer_p.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <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 QOpenGLShaderProgram;
+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);
+
+ static GLuint createGLTexture(CGLContextObj cglContextObj, CGLPixelFormatObj cglPixelFormtObj,
+ CVOpenGLTextureCacheRef cvglTextureCache,
+ CVPixelBufferRef cvPixelBufferRef,
+ CVOpenGLTextureRef cvOpenGLTextureRef);
+
+ static id<MTLTexture> createMetalTexture(id<MTLDevice> mtlDevice,
+ CVMetalTextureCacheRef cvMetalTextureCacheRef,
+ CVPixelBufferRef cvPixelBufferRef,
+ MTLPixelFormat pixelFormat, size_t width, size_t height,
+ CVMetalTextureRef cvMetalTextureRef);
+
+private:
+ QOpenGLFramebufferObject* initRenderer(AVPlayerLayer *layer);
+ void renderLayerToFBO(AVPlayerLayer *layer, QOpenGLFramebufferObject *fbo);
+ void renderLayerToFBOCoreOpenGL(AVPlayerLayer *layer, QOpenGLFramebufferObject *fbo);
+
+ CARenderer *m_videoLayerRenderer;
+ QAbstractVideoSurface *m_surface;
+ QOpenGLFramebufferObject *m_fbo[2];
+ QOpenGLShaderProgram *m_shader = nullptr;
+ QWindow *m_offscreenSurface;
+ QOpenGLContext *m_glContext;
+ QSize m_targetSize;
+
+ bool m_useCoreProfile = false;
+
+ // Shared pixel buffer
+ CVPixelBufferRef m_CVPixelBuffer;
+
+ // OpenGL Texture
+ CVOpenGLTextureCacheRef m_CVGLTextureCache;
+ CVOpenGLTextureRef m_CVGLTexture;
+ CGLPixelFormatObj m_CGLPixelFormat;
+ GLuint m_textureName = 0;
+
+ // Metal Texture
+ CVMetalTextureRef m_CVMTLTexture;
+ CVMetalTextureCacheRef m_CVMTLTextureCache;
+
+ NSOpenGLContext *m_NSGLContext = nullptr;
+
+ GLuint m_quadVao = 0;
+ GLuint m_quadVbos[2];
+
+ 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/multimedia/platform/avfoundation/mediaplayer/avfvideooutput.mm b/src/multimedia/platform/avfoundation/mediaplayer/avfvideooutput.mm
new file mode 100644
index 000000000..ef12f07c1
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfvideooutput.mm
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+QT_USE_NAMESPACE
diff --git a/src/multimedia/platform/avfoundation/mediaplayer/avfvideooutput_p.h b/src/multimedia/platform/avfoundation/mediaplayer/avfvideooutput_p.h
new file mode 100644
index 000000000..d1dd94460
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfvideooutput_p.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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>
+
+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/multimedia/platform/avfoundation/mediaplayer/avfvideorenderercontrol.mm b/src/multimedia/platform/avfoundation/mediaplayer/avfvideorenderercontrol.mm
new file mode 100644
index 000000000..4cb467296
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfvideorenderercontrol.mm
@@ -0,0 +1,301 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "avfdisplaylink_p.h"
+
+#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
+#include "avfvideoframerenderer_ios_p.h"
+#else
+#include "avfvideoframerenderer_p.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; }
+ MapData map(MapMode mode) override { return {}; }
+ 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/multimedia/platform/avfoundation/mediaplayer/avfvideorenderercontrol_p.h b/src/multimedia/platform/avfoundation/mediaplayer/avfvideorenderercontrol_p.h
new file mode 100644
index 000000000..e46444745
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfvideorenderercontrol_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/QVideoRendererControl>
+#include <QtCore/QMutex>
+#include <QtCore/QSize>
+
+#include "avfvideooutput_p.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/multimedia/platform/avfoundation/mediaplayer/avfvideowindowcontrol.mm b/src/multimedia/platform/avfoundation/mediaplayer/avfvideowindowcontrol.mm
new file mode 100644
index 000000000..85eb82dcb
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfvideowindowcontrol.mm
@@ -0,0 +1,255 @@
+/****************************************************************************
+**
+** 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_p.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_p.cpp"
diff --git a/src/multimedia/platform/avfoundation/mediaplayer/avfvideowindowcontrol_p.h b/src/multimedia/platform/avfoundation/mediaplayer/avfvideowindowcontrol_p.h
new file mode 100644
index 000000000..f4f2fc580
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/avfvideowindowcontrol_p.h
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 AVPlayerLayer;
+#if defined(Q_OS_OSX)
+@class NSView;
+typedef NSView NativeView;
+#else
+@class UIView;
+typedef UIView NativeView;
+#endif
+
+#include "avfvideooutput_p.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/multimedia/platform/avfoundation/mediaplayer/mediaplayer.pri b/src/multimedia/platform/avfoundation/mediaplayer/mediaplayer.pri
new file mode 100644
index 000000000..2929ffe50
--- /dev/null
+++ b/src/multimedia/platform/avfoundation/mediaplayer/mediaplayer.pri
@@ -0,0 +1,48 @@
+QT += opengl network
+
+HEADERS += \
+ $$PWD/avfmediaplayercontrol_p.h \
+ $$PWD/avfmediaplayermetadatacontrol_p.h \
+ $$PWD/avfmediaplayerservice_p.h \
+ $$PWD/avfmediaplayersession_p.h \
+ $$PWD/avfmediaplayerserviceplugin_p.h \
+ $$PWD/avfvideooutput_p.h \
+ $$PWD/avfvideowindowcontrol_p.h
+
+SOURCES += \
+ $$PWD/avfmediaplayercontrol.mm \
+ $$PWD/avfmediaplayermetadatacontrol.mm \
+ $$PWD/avfmediaplayerservice.mm \
+ $$PWD/avfmediaplayerserviceplugin.mm \
+ $$PWD/avfmediaplayersession.mm \
+ $$PWD/avfvideooutput.mm \
+ $$PWD/avfvideowindowcontrol.mm
+
+ios|tvos {
+ qtConfig(opengl) {
+ HEADERS += \
+ $$PWD/avfvideoframerenderer_ios_p.h \
+ $$PWD/avfvideorenderercontrol_p.h \
+ $$PWD/avfdisplaylink_p.h
+
+ SOURCES += \
+ $$PWD/avfvideoframerenderer_ios.mm \
+ $$PWD/avfvideorenderercontrol.mm \
+ $$PWD/avfdisplaylink.mm
+ }
+ LIBS += -framework Foundation
+} else {
+ LIBS += -framework AppKit
+
+ qtConfig(opengl) {
+ HEADERS += \
+ $$PWD/avfvideoframerenderer_p.h \
+ $$PWD/avfvideorenderercontrol_p.h \
+ $$PWD/avfdisplaylink_p.h
+
+ SOURCES += \
+ $$PWD/avfvideoframerenderer.mm \
+ $$PWD/avfvideorenderercontrol.mm \
+ $$PWD/avfdisplaylink.mm
+ }
+}
diff --git a/src/multimedia/platform/coreaudio/coreaudio.pri b/src/multimedia/platform/coreaudio/coreaudio.pri
index 538e9b4a9..1ee6cf7c8 100644
--- a/src/multimedia/platform/coreaudio/coreaudio.pri
+++ b/src/multimedia/platform/coreaudio/coreaudio.pri
@@ -1,20 +1,20 @@
HEADERS += \
- platform/coreaudio/qcoreaudiodeviceinfo_p.h \
- platform/coreaudio/qcoreaudioinput_p.h \
- platform/coreaudio/qcoreaudiooutput_p.h \
- platform/coreaudio/qcoreaudiointerface_p.h \
- platform/coreaudio/qcoreaudioutils_p.h
+ $$PWD/qcoreaudiodeviceinfo_p.h \
+ $$PWD/qcoreaudioinput_p.h \
+ $$PWD/qcoreaudiooutput_p.h \
+ $$PWD/qcoreaudiointerface_p.h \
+ $$PWD/qcoreaudioutils_p.h
SOURCES += \
- platform/coreaudio/qcoreaudiodeviceinfo.mm \
- platform/coreaudio/qcoreaudioinput.mm \
- platform/coreaudio/qcoreaudiooutput.mm \
- platform/coreaudio/qcoreaudiointerface.mm \
- platform/coreaudio/qcoreaudioutils.mm
+ $$PWD/qcoreaudiodeviceinfo.mm \
+ $$PWD/qcoreaudioinput.mm \
+ $$PWD/qcoreaudiooutput.mm \
+ $$PWD/qcoreaudiointerface.mm \
+ $$PWD/qcoreaudioutils.mm
ios|tvos {
- HEADERS += platform/coreaudio/qcoreaudiosessionmanager_p.h
- SOURCES += platform/coreaudio/qcoreaudiosessionmanager.mm
+ HEADERS += $$PWD/qcoreaudiosessionmanager_p.h
+ SOURCES += $$PWD/qcoreaudiosessionmanager.mm
LIBS += -framework Foundation -framework AVFoundation
} else {
LIBS += \
diff --git a/src/multimedia/platform/gstreamer/audio/audio.pri b/src/multimedia/platform/gstreamer/audio/audio.pri
new file mode 100644
index 000000000..9cf3d9e68
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/audio/audio.pri
@@ -0,0 +1,15 @@
+HEADERS += \
+ $$PWD/qaudiointerface_gstreamer_p.h \
+ $$PWD/qaudiodeviceinfo_gstreamer_p.h \
+ $$PWD/qaudiooutput_gstreamer_p.h \
+ $$PWD/qaudioinput_gstreamer_p.h \
+ $$PWD/qaudioengine_gstreamer_p.h \
+ $$PWD/qgstreameraudiodecodercontrol_p.h
+
+SOURCES += \
+ $$PWD/qaudiointerface_gstreamer.cpp \
+ $$PWD/qaudiodeviceinfo_gstreamer.cpp \
+ $$PWD/qaudiooutput_gstreamer.cpp \
+ $$PWD/qaudioinput_gstreamer.cpp \
+ $$PWD/qaudioengine_gstreamer.cpp \
+ $$PWD/qgstreameraudiodecodercontrol.cpp
diff --git a/src/multimedia/platform/gstreamer/qaudiodeviceinfo_gstreamer.cpp b/src/multimedia/platform/gstreamer/audio/qaudiodeviceinfo_gstreamer.cpp
index ace067540..ace067540 100644
--- a/src/multimedia/platform/gstreamer/qaudiodeviceinfo_gstreamer.cpp
+++ b/src/multimedia/platform/gstreamer/audio/qaudiodeviceinfo_gstreamer.cpp
diff --git a/src/multimedia/platform/gstreamer/qaudiodeviceinfo_gstreamer_p.h b/src/multimedia/platform/gstreamer/audio/qaudiodeviceinfo_gstreamer_p.h
index 85c7a2c92..85c7a2c92 100644
--- a/src/multimedia/platform/gstreamer/qaudiodeviceinfo_gstreamer_p.h
+++ b/src/multimedia/platform/gstreamer/audio/qaudiodeviceinfo_gstreamer_p.h
diff --git a/src/multimedia/platform/gstreamer/qaudioengine_gstreamer.cpp b/src/multimedia/platform/gstreamer/audio/qaudioengine_gstreamer.cpp
index 1108fa7b5..1108fa7b5 100644
--- a/src/multimedia/platform/gstreamer/qaudioengine_gstreamer.cpp
+++ b/src/multimedia/platform/gstreamer/audio/qaudioengine_gstreamer.cpp
diff --git a/src/multimedia/platform/gstreamer/qaudioengine_gstreamer_p.h b/src/multimedia/platform/gstreamer/audio/qaudioengine_gstreamer_p.h
index e7043c3f8..e7043c3f8 100644
--- a/src/multimedia/platform/gstreamer/qaudioengine_gstreamer_p.h
+++ b/src/multimedia/platform/gstreamer/audio/qaudioengine_gstreamer_p.h
diff --git a/src/multimedia/platform/gstreamer/qaudioinput_gstreamer.cpp b/src/multimedia/platform/gstreamer/audio/qaudioinput_gstreamer.cpp
index 60b2a6b83..60b2a6b83 100644
--- a/src/multimedia/platform/gstreamer/qaudioinput_gstreamer.cpp
+++ b/src/multimedia/platform/gstreamer/audio/qaudioinput_gstreamer.cpp
diff --git a/src/multimedia/platform/gstreamer/qaudioinput_gstreamer_p.h b/src/multimedia/platform/gstreamer/audio/qaudioinput_gstreamer_p.h
index ceff047c0..ceff047c0 100644
--- a/src/multimedia/platform/gstreamer/qaudioinput_gstreamer_p.h
+++ b/src/multimedia/platform/gstreamer/audio/qaudioinput_gstreamer_p.h
diff --git a/src/multimedia/platform/gstreamer/qaudiointerface_gstreamer.cpp b/src/multimedia/platform/gstreamer/audio/qaudiointerface_gstreamer.cpp
index b449a989a..b449a989a 100644
--- a/src/multimedia/platform/gstreamer/qaudiointerface_gstreamer.cpp
+++ b/src/multimedia/platform/gstreamer/audio/qaudiointerface_gstreamer.cpp
diff --git a/src/multimedia/platform/gstreamer/qaudiointerface_gstreamer_p.h b/src/multimedia/platform/gstreamer/audio/qaudiointerface_gstreamer_p.h
index 7a79bd186..7a79bd186 100644
--- a/src/multimedia/platform/gstreamer/qaudiointerface_gstreamer_p.h
+++ b/src/multimedia/platform/gstreamer/audio/qaudiointerface_gstreamer_p.h
diff --git a/src/multimedia/platform/gstreamer/qaudiooutput_gstreamer.cpp b/src/multimedia/platform/gstreamer/audio/qaudiooutput_gstreamer.cpp
index eaa4c52f2..eaa4c52f2 100644
--- a/src/multimedia/platform/gstreamer/qaudiooutput_gstreamer.cpp
+++ b/src/multimedia/platform/gstreamer/audio/qaudiooutput_gstreamer.cpp
diff --git a/src/multimedia/platform/gstreamer/qaudiooutput_gstreamer_p.h b/src/multimedia/platform/gstreamer/audio/qaudiooutput_gstreamer_p.h
index 685685b61..685685b61 100644
--- a/src/multimedia/platform/gstreamer/qaudiooutput_gstreamer_p.h
+++ b/src/multimedia/platform/gstreamer/audio/qaudiooutput_gstreamer_p.h
diff --git a/src/multimedia/platform/gstreamer/audio/qgstreameraudiodecodercontrol.cpp b/src/multimedia/platform/gstreamer/audio/qgstreameraudiodecodercontrol.cpp
new file mode 100644
index 000000000..73cfe02b5
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/audio/qgstreameraudiodecodercontrol.cpp
@@ -0,0 +1,599 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+//#define DEBUG_DECODER
+
+#include "qgstreameraudiodecodercontrol_p.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;
+
+QGstreamerAudioDecoderControl::QGstreamerAudioDecoderControl(QObject *parent)
+ : QAudioDecoderControl(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("playbin", 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) &QGstreamerAudioDecoderControl::configureAppSrcElement, (gpointer)this);
+#endif
+
+ // Set volume to 100%
+ gdouble volume = 1.0;
+ g_object_set(G_OBJECT(m_playbin), "volume", volume, NULL);
+ }
+}
+
+QGstreamerAudioDecoderControl::~QGstreamerAudioDecoderControl()
+{
+ 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 QGstreamerAudioDecoderControl::configureAppSrcElement(GObject* object, GObject *orig, GParamSpec *pspec, QGstreamerAudioDecoderControl* 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 QGstreamerAudioDecoderControl::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 QGstreamerAudioDecoderControl::sourceFilename() const
+{
+ return mSource;
+}
+
+void QGstreamerAudioDecoderControl::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 *QGstreamerAudioDecoderControl::sourceDevice() const
+{
+ return mDevice;
+}
+
+void QGstreamerAudioDecoderControl::setSourceDevice(QIODevice *device)
+{
+ stop();
+ mSource.clear();
+ bool isSignalRequired = (mDevice != device);
+ mDevice = device;
+ if (isSignalRequired)
+ emit sourceChanged();
+}
+
+void QGstreamerAudioDecoderControl::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 QGstreamerAudioDecoderControl::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 QGstreamerAudioDecoderControl::audioFormat() const
+{
+ return mFormat;
+}
+
+void QGstreamerAudioDecoderControl::setAudioFormat(const QAudioFormat &format)
+{
+ if (mFormat != format) {
+ mFormat = format;
+ emit formatChanged(mFormat);
+ }
+}
+
+QAudioBuffer QGstreamerAudioDecoderControl::read()
+{
+ QAudioBuffer audioBuffer;
+
+ int buffersAvailable;
+ {
+ QMutexLocker locker(&m_buffersMutex);
+ buffersAvailable = m_buffersAvailable;
+
+ // need to decrement before pulling a buffer
+ // to make sure assert in QGstreamerAudioDecoderControl::new_buffer works
+ m_buffersAvailable--;
+ }
+
+
+ if (buffersAvailable) {
+ if (buffersAvailable == 1)
+ emit bufferAvailableChanged(false);
+
+ const char* bufferData = 0;
+ int bufferSize = 0;
+
+ GstSample *sample = gst_app_sink_pull_sample(m_appSink);
+ GstBuffer *buffer = gst_sample_get_buffer(sample);
+ GstMapInfo mapInfo;
+ gst_buffer_map(buffer, &mapInfo, GST_MAP_READ);
+ bufferData = (const char*)mapInfo.data;
+ bufferSize = mapInfo.size;
+ QAudioFormat format = QGstUtils::audioFormatForSample(sample);
+
+ if (format.isValid()) {
+ // XXX At the moment we have to copy data from GstBuffer into QAudioBuffer.
+ // We could improve performance by implementing QAbstractAudioBuffer for GstBuffer.
+ qint64 position = getPositionFromBuffer(buffer);
+ audioBuffer = QAudioBuffer(QByteArray((const char*)bufferData, bufferSize), format, position);
+ position /= 1000; // convert to milliseconds
+ if (position != m_position) {
+ m_position = position;
+ emit positionChanged(m_position);
+ }
+ }
+ gst_buffer_unmap(buffer, &mapInfo);
+ gst_sample_unref(sample);
+ }
+
+ return audioBuffer;
+}
+
+bool QGstreamerAudioDecoderControl::bufferAvailable() const
+{
+ QMutexLocker locker(&m_buffersMutex);
+ return m_buffersAvailable > 0;
+}
+
+qint64 QGstreamerAudioDecoderControl::position() const
+{
+ return m_position;
+}
+
+qint64 QGstreamerAudioDecoderControl::duration() const
+{
+ return m_duration;
+}
+
+void QGstreamerAudioDecoderControl::processInvalidMedia(QAudioDecoder::Error errorCode, const QString& errorString)
+{
+ stop();
+ emit error(int(errorCode), errorString);
+}
+
+GstFlowReturn QGstreamerAudioDecoderControl::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()."
+ QGstreamerAudioDecoderControl *control = reinterpret_cast<QGstreamerAudioDecoderControl*>(user_data);
+
+ int buffersAvailable;
+ {
+ QMutexLocker locker(&control->m_buffersMutex);
+ buffersAvailable = control->m_buffersAvailable;
+ control->m_buffersAvailable++;
+ Q_ASSERT(control->m_buffersAvailable <= MAX_BUFFERS_IN_QUEUE);
+ }
+
+ if (!buffersAvailable)
+ QMetaObject::invokeMethod(control, "bufferAvailableChanged", Qt::QueuedConnection, Q_ARG(bool, true));
+ QMetaObject::invokeMethod(control, "bufferReady", Qt::QueuedConnection);
+ return GST_FLOW_OK;
+}
+
+void QGstreamerAudioDecoderControl::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 QGstreamerAudioDecoderControl::addAppSink()
+{
+ if (m_appSink)
+ return;
+
+ m_appSink = (GstAppSink*)gst_element_factory_make("appsink", NULL);
+
+ GstAppSinkCallbacks callbacks;
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.new_sample = &new_sample;
+ 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 QGstreamerAudioDecoderControl::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 QGstreamerAudioDecoderControl::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 QGstreamerAudioDecoderControl::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/multimedia/platform/gstreamer/audio/qgstreameraudiodecodercontrol_p.h b/src/multimedia/platform/gstreamer/audio/qgstreameraudiodecodercontrol_p.h
new file mode 100644
index 000000000..6f31ad261
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/audio/qgstreameraudiodecodercontrol_p.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#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/qtmultimediaglobal_p.h>
+#include <QObject>
+#include <QtCore/qmutex.h>
+#include "qaudiodecodercontrol.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 QGstreamerAudioDecoderControl
+ : public QAudioDecoderControl,
+ public QGstreamerBusMessageFilter
+{
+Q_OBJECT
+Q_INTERFACES(QGstreamerBusMessageFilter)
+
+public:
+ QGstreamerAudioDecoderControl(QObject *parent);
+ virtual ~QGstreamerAudioDecoderControl();
+
+ // QAudioDecoder interface
+ QAudioDecoder::State state() const override { return m_state; }
+
+ 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;
+
+ // GStreamerBusMessageFilter interface
+ bool processBusMessage(const QGstreamerMessage &message) override;
+
+ QGstreamerBusHelper *bus() const { return m_busHelper; }
+ QAudioDecoder::State pendingState() const { return m_pendingState; }
+
+#if QT_CONFIG(gstreamer_app)
+ QGstAppSrc *appsrc() const { return m_appSrc; }
+ static void configureAppSrcElement(GObject*, GObject*, GParamSpec*, QGstreamerAudioDecoderControl *_this);
+#endif
+
+ static GstFlowReturn new_sample(GstAppSink *sink, gpointer user_data);
+
+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/multimedia/platform/gstreamer/camerabin/camerabin.pri b/src/multimedia/platform/gstreamer/camerabin/camerabin.pri
new file mode 100644
index 000000000..2cacd56b0
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabin.pri
@@ -0,0 +1,51 @@
+
+HEADERS += \
+ $$PWD/camerabinserviceplugin_p.h \
+ $$PWD/camerabinservice_p.h \
+ $$PWD/camerabinsession_p.h \
+ $$PWD/camerabincontrol_p.h \
+ $$PWD/camerabinaudioencoder_p.h \
+ $$PWD/camerabinimageencoder_p.h \
+ $$PWD/camerabinrecorder_p.h \
+ $$PWD/camerabincontainer_p.h \
+ $$PWD/camerabinimagecapture_p.h \
+ $$PWD/camerabinimageprocessing_p.h \
+ $$PWD/camerabinmetadata_p.h \
+ $$PWD/camerabinvideoencoder_p.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/camerabinimageprocessing.cpp \
+ $$PWD/camerabinmetadata.cpp \
+ $$PWD/camerabinrecorder.cpp \
+ $$PWD/camerabinvideoencoder.cpp \
+
+qtConfig(gstreamer__p.hotography) {
+ HEADERS += \
+ $$PWD/camerabinfocus_p.h \
+ $$PWD/camerabinexposure_p.h \
+
+ SOURCES += \
+ $$PWD/camerabinexposure.cpp \
+ $$PWD/camerabinfocus.cpp \
+
+ QMAKE_USE += gstreamer_photography
+ DEFINES += GST_USE_UNSTABLE_API #prevents warnings because of unstable _p.hotography API
+}
+
+qtConfig(gstreamer_gl): QMAKE_USE += gstreamer_gl
+
+qtConfig(linux_v4l) {
+ HEADERS += \
+ $$PWD/camerabinv4limageprocessing_p.h
+
+ SOURCES += \
+ $$PWD/camerabinv4limageprocessing.cpp
+}
diff --git a/src/multimedia/platform/gstreamer/camerabin/camerabinaudioencoder.cpp b/src/multimedia/platform/gstreamer/camerabin/camerabinaudioencoder.cpp
new file mode 100644
index 000000000..9a26016de
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinaudioencoder.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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;
+
+ 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/multimedia/platform/gstreamer/camerabin/camerabinaudioencoder_p.h b/src/multimedia/platform/gstreamer/camerabin/camerabinaudioencoder_p.h
new file mode 100644
index 000000000..0c6708449
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinaudioencoder_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <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/multimedia/platform/gstreamer/camerabin/camerabincontainer.cpp b/src/multimedia/platform/gstreamer/camerabin/camerabincontainer.cpp
new file mode 100644
index 000000000..b19a5524e
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabincontainer.cpp
@@ -0,0 +1,151 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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;
+
+ 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/multimedia/platform/gstreamer/camerabin/camerabincontainer_p.h b/src/multimedia/platform/gstreamer/camerabin/camerabincontainer_p.h
new file mode 100644
index 000000000..c743adf83
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabincontainer_p.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <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/multimedia/platform/gstreamer/camerabin/camerabincontrol.cpp b/src/multimedia/platform/gstreamer/camerabin/camerabincontrol.cpp
new file mode 100644
index 000000000..a02e9ab72
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabincontrol.cpp
@@ -0,0 +1,432 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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 "camerabinfocus.h"
+#include "camerabinimageprocessing.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qcoreevent.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),
+ m_focus(m_session->cameraFocusControl())
+{
+ 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)));
+
+ connect(m_session, SIGNAL(busyChanged(bool)),
+ SLOT(handleBusyChanged(bool)));
+
+ connect(m_focus, SIGNAL(_q_focusStatusChanged(QCamera::LockStatus,QCamera::LockChangeReason)),
+ this, SLOT(updateFocusStatus(QCamera::LockStatus,QCamera::LockChangeReason)));
+}
+
+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);
+
+ 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;
+ }
+
+ //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::handleBusyChanged(bool busy)
+{
+ if (!busy && m_session->status() == QCamera::ActiveStatus) {
+ if (m_state != QCamera::ActiveState) {
+ 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_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);
+}
+
+QCamera::LockTypes CameraBinControl::supportedLocks() const
+{
+ QCamera::LockTypes locks = QCamera::LockFocus;
+
+ 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;
+ }
+ }
+ }
+
+ return locks;
+}
+
+QCamera::LockStatus CameraBinControl::lockStatus(QCamera::LockType lock) const
+{
+ switch (lock) {
+ case QCamera::LockFocus:
+ return m_focus->focusStatus();
+ 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;
+ default:
+ return QCamera::Unlocked;
+ }
+}
+
+void CameraBinControl::searchAndLock(QCamera::LockTypes locks)
+{
+ m_pendingLocks &= ~locks;
+
+ if (locks & QCamera::LockFocus) {
+ m_pendingLocks |= QCamera::LockFocus;
+ m_focus->_q_startFocusing();
+ }
+ 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);
+ }
+ }
+}
+
+void CameraBinControl::unlock(QCamera::LockTypes locks)
+{
+ m_pendingLocks &= ~locks;
+
+ if (locks & QCamera::LockFocus)
+ m_focus->_q_stopFocusing();
+
+ if (!m_pendingLocks)
+ m_lockTimer.stop();
+
+ if (locks & QCamera::LockExposure)
+ unlockExposure(QCamera::Unlocked, QCamera::UserRequest);
+ if (locks & QCamera::LockWhiteBalance)
+ unlockWhiteBalance(QCamera::Unlocked, QCamera::UserRequest);
+}
+
+void CameraBinControl::updateFocusStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason)
+{
+ if (status != QCamera::Searching)
+ m_pendingLocks &= ~QCamera::LockFocus;
+
+ if (status == QCamera::Locked && !m_lockTimer.isActive()) {
+ if (m_pendingLocks & QCamera::LockExposure)
+ lockExposure(QCamera::LockAcquired);
+ if (m_pendingLocks & QCamera::LockWhiteBalance)
+ lockWhiteBalance(QCamera::LockAcquired);
+ }
+ emit lockStatusChanged(QCamera::LockFocus, status, reason);
+}
+
+void CameraBinControl::timerEvent(QTimerEvent *event)
+{
+ if (event->timerId() != m_lockTimer.timerId())
+ return QCameraControl::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 CameraBinControl::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;
+ }
+
+ return false;
+}
+
+void CameraBinControl::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 CameraBinControl::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 CameraBinControl::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;
+ }
+
+ return false;
+}
+
+void CameraBinControl::lockWhiteBalance(QCamera::LockChangeReason reason)
+{
+ m_pendingLocks &= ~QCamera::LockWhiteBalance;
+ m_session->imageProcessingControl()->lockWhiteBalance();
+ emit lockStatusChanged(QCamera::LockWhiteBalance, QCamera::Locked, reason);
+}
+
+void CameraBinControl::unlockWhiteBalance(
+ QCamera::LockStatus status, QCamera::LockChangeReason reason)
+{
+ m_session->imageProcessingControl()->lockWhiteBalance();
+ emit lockStatusChanged(QCamera::LockWhiteBalance, status, reason);
+}
+
+QList<QCameraViewfinderSettings> CameraBinControl::supportedViewfinderSettings() const
+{
+ return m_session->supportedViewfinderSettings();
+}
+
+QCameraViewfinderSettings CameraBinControl::viewfinderSettings() const
+{
+ return m_session->viewfinderSettings();
+}
+
+void CameraBinControl::setViewfinderSettings(const QCameraViewfinderSettings &settings)
+{
+ m_session->setViewfinderSettings(settings);
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/gstreamer/camerabin/camerabincontrol_p.h b/src/multimedia/platform/gstreamer/camerabin/camerabincontrol_p.h
new file mode 100644
index 000000000..3c1396c82
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabincontrol_p.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QHash>
+#include <qbasictimer.h>
+#include <qcameracontrol.h>
+#include "camerabinsession.h"
+
+QT_BEGIN_NAMESPACE
+
+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;
+
+ 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;
+
+ QList<QCameraViewfinderSettings> supportedViewfinderSettings() const override;
+
+ QCameraViewfinderSettings viewfinderSettings() const override;
+ void setViewfinderSettings(const QCameraViewfinderSettings &settings) override;
+
+public slots:
+ void reloadLater();
+ void setViewfinderColorSpaceConversion(bool enabled);
+
+private slots:
+ void delayedReload();
+
+ void handleBusyChanged(bool);
+ void handleCameraError(int error, const QString &errorString);
+
+private:
+ void updateSupportedResolutions(const QString &device);
+
+protected:
+ void timerEvent(QTimerEvent *event) override;
+
+private slots:
+ void updateFocusStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason);
+
+private:
+ 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);
+
+ CameraBinSession *m_session;
+ QCamera::State m_state;
+
+ bool m_reloadPending;
+
+ CameraBinFocus *m_focus;
+ QBasicTimer m_lockTimer;
+ QCamera::LockTypes m_pendingLocks;
+};
+
+QT_END_NAMESPACE
+
+#endif // CAMERABINCONTROL_H
diff --git a/src/multimedia/platform/gstreamer/camerabin/camerabinexposure.cpp b/src/multimedia/platform/gstreamer/camerabin/camerabinexposure.cpp
new file mode 100644
index 000000000..740532d22
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinexposure.cpp
@@ -0,0 +1,322 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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>
+
+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);
+ 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);
+ //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);
+ }
+ }
+ 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;
+ 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;
+ 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;
+}
+
+QCameraExposure::FlashModes CameraBinExposure::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 CameraBinExposure::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 CameraBinExposure::isFlashModeSupported(QCameraExposure::FlashModes mode) const
+{
+ return mode == QCameraExposure::FlashOff ||
+ mode == QCameraExposure::FlashOn ||
+ mode == QCameraExposure::FlashAuto ||
+ mode == QCameraExposure::FlashRedEyeReduction ||
+ mode == QCameraExposure::FlashFill;
+}
+
+bool CameraBinExposure::isFlashReady() const
+{
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/gstreamer/camerabin/camerabinexposure_p.h b/src/multimedia/platform/gstreamer/camerabin/camerabinexposure_p.h
new file mode 100644
index 000000000..113187bc8
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinexposure_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <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;
+
+ 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;
+ QHash<ExposureParameter, QVariant> m_requestedValues;
+};
+
+QT_END_NAMESPACE
+
+#endif // CAMERABINEXPOSURECONTROL_H
diff --git a/src/multimedia/platform/gstreamer/camerabin/camerabinfocus.cpp b/src/multimedia/platform/gstreamer/camerabin/camerabinfocus.cpp
new file mode 100644
index 000000000..f795b0f2f
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinfocus.cpp
@@ -0,0 +1,607 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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>
+
+#define ZOOM_PROPERTY "zoom"
+#define MAX_ZOOM_PROPERTY "max-zoom"
+
+//#define CAMERABIN_DEBUG 1
+
+QT_BEGIN_NAMESPACE
+
+CameraBinFocus::CameraBinFocus(CameraBinSession *session)
+ :QCameraFocusControl(session),
+ QGstreamerBufferProbe(ProbeBuffers),
+ 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)));
+
+ 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);
+}
+
+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 (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();
+ }
+
+ if (m_focusPointMode != QCameraFocus::FocusPointAuto)
+ resetFocusPoint();
+
+ switch (mode) {
+ case QCameraFocus::FocusPointAuto:
+ case QCameraFocus::FocusPointCustom:
+ break;
+ 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;
+ 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;
+ case QCameraFocus::FocusPointFaceDetection:
+ if (GstElement *source = m_session->cameraSource())
+ return g_object_class_find_property(G_OBJECT_GET_CLASS(source), "detect-faces");
+ return false;
+ 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));
+ } 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));
+ }
+ 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 (m_focusPointMode == QCameraFocus::FocusPointFaceDetection
+ && m_focusStatus == QCamera::Unlocked) {
+ _q_updateFaces();
+ }
+
+ 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);
+}
+
+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;
+
+ 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));
+ }
+
+ 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;
+}
+
+qreal CameraBinFocus::maximumOpticalZoom() const
+{
+ return 1.0;
+}
+
+qreal CameraBinFocus::maximumDigitalZoom() const
+{
+ gfloat zoomFactor = 1.0;
+ g_object_get(GST_BIN(m_session->cameraBin()), MAX_ZOOM_PROPERTY, &zoomFactor, NULL);
+ return zoomFactor;
+}
+
+qreal CameraBinFocus::requestedDigitalZoom() const
+{
+ return m_requestedDigitalZoom;
+}
+
+qreal CameraBinFocus::requestedOpticalZoom() const
+{
+ return m_requestedOpticalZoom;
+}
+
+qreal CameraBinFocus::currentOpticalZoom() const
+{
+ return 1.0;
+}
+
+qreal CameraBinFocus::currentDigitalZoom() const
+{
+ gfloat zoomFactor = 1.0;
+ g_object_get(GST_BIN(m_session->cameraBin()), ZOOM_PROPERTY, &zoomFactor, NULL);
+ return zoomFactor;
+}
+
+void CameraBinFocus::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 CameraBinFocus::updateZoom(GObject *o, GParamSpec *p, gpointer d)
+{
+ Q_UNUSED(p);
+
+ gfloat zoomFactor = 1.0;
+ g_object_get(o, ZOOM_PROPERTY, &zoomFactor, NULL);
+
+ CameraBinFocus *zoom = reinterpret_cast<CameraBinFocus *>(d);
+
+ QMetaObject::invokeMethod(zoom, "currentDigitalZoomChanged",
+ Qt::QueuedConnection,
+ Q_ARG(qreal, zoomFactor));
+}
+
+void CameraBinFocus::updateMaxZoom(GObject *o, GParamSpec *p, gpointer d)
+{
+ Q_UNUSED(p);
+
+ gfloat zoomFactor = 1.0;
+ g_object_get(o, MAX_ZOOM_PROPERTY, &zoomFactor, NULL);
+
+ CameraBinFocus *zoom = reinterpret_cast<CameraBinFocus *>(d);
+
+ QMetaObject::invokeMethod(zoom, "maximumDigitalZoomChanged",
+ Qt::QueuedConnection,
+ Q_ARG(qreal, zoomFactor));
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/gstreamer/camerabin/camerabinfocus_p.h b/src/multimedia/platform/gstreamer/camerabin/camerabinfocus_p.h
new file mode 100644
index 000000000..4ffef7a3a
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinfocus_p.h
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <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
+ , QGstreamerBufferProbe
+{
+ 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;
+
+
+ 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;
+
+ 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);
+
+protected:
+ void timerEvent(QTimerEvent *event) override;
+
+private Q_SLOTS:
+ void _q_setFocusStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason);
+ void _q_handleCameraStatusChange(QCamera::Status status);
+ void _q_updateFaces();
+
+private:
+ void resetFocusPoint();
+ void updateRegionOfInterest(const QRectF &rectangle);
+ void updateRegionOfInterest(const QList<QRect> &rectangles);
+ bool probeBuffer(GstBuffer *buffer) override;
+
+ 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;
+
+ static void updateZoom(GObject *o, GParamSpec *p, gpointer d);
+ static void updateMaxZoom(GObject *o, GParamSpec *p, gpointer d);
+
+ qreal m_requestedOpticalZoom = 1.;
+ qreal m_requestedDigitalZoom = 1.;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // CAMERABINFOCUSCONTROL_H
diff --git a/src/multimedia/platform/gstreamer/camerabin/camerabinimagecapture.cpp b/src/multimedia/platform/gstreamer/camerabin/camerabinimagecapture.cpp
new file mode 100644
index 000000000..36fb4c205
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinimagecapture.cpp
@@ -0,0 +1,303 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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 "camerabinsession.h"
+#include <private/qgstvideobuffer_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)));
+
+ 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()
+{
+}
+
+QCameraImageCapture::CaptureDestinations CameraBinImageCapture::captureDestination() const
+{
+ return m_destination;
+}
+
+void CameraBinImageCapture::setCaptureDestination(QCameraImageCapture::CaptureDestinations destination)
+{
+ m_destination = destination;
+}
+
+
+void CameraBinImageCapture::updateState()
+{
+ bool ready = m_session->status() == QCamera::ActiveStatus;
+ if (m_ready != ready) {
+#ifdef DEBUG_CAPTURE
+ qDebug() << "readyForCaptureChanged" << ready;
+#endif
+ emit readyForCaptureChanged(m_ready = ready);
+ }
+}
+
+GstPadProbeReturn CameraBinImageCapture::encoderEventProbe(
+ GstPad *, GstPadProbeInfo *info, gpointer user_data)
+{
+ GstEvent * const event = gst_pad_probe_info_get_event(info);
+ 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::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()));
+ }
+ }
+ }
+ return GST_PAD_PROBE_OK;
+}
+
+void CameraBinImageCapture::EncoderProbe::probeCaps(GstCaps *caps)
+{
+ capture->m_bufferFormat = QGstUtils::formatForCaps(caps, &capture->m_videoInfo);
+}
+
+bool CameraBinImageCapture::EncoderProbe::probeBuffer(GstBuffer *)
+{
+#ifdef DEBUG_CAPTURE
+ qDebug() << "Uncompressed buffer probe";
+#endif
+ // keep the buffer so we can capture to file or encode the image
+ return true;
+}
+
+void CameraBinImageCapture::MuxerProbe::probeCaps(GstCaps *caps)
+{
+ capture->m_jpegResolution = QGstUtils::capsCorrectedResolution(caps);
+}
+
+bool CameraBinImageCapture::MuxerProbe::probeBuffer(GstBuffer *buffer)
+{
+ QCameraImageCapture::CaptureDestinations destination = capture->m_destination;
+
+ if (destination & QCameraImageCapture::CaptureToBuffer) {
+ QSize resolution = capture->m_jpegResolution;
+ //if resolution is not presented in caps, try to find it from encoded jpeg data:
+ 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);
+
+ 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 (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
+ gst_pad_add_probe(
+ sinkpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, encoderEventProbe, this, NULL);
+#ifdef DEBUG_CAPTURE
+ qDebug() << "install uncompressed buffer probe";
+#endif
+ m_encoderProbe.addProbeToPad(sinkpad, true);
+
+ gst_object_unref(sinkpad);
+ } else if ((elementName.contains("jifmux") || 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_destination & 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/multimedia/platform/gstreamer/camerabin/camerabinimagecapture_p.h b/src/multimedia/platform/gstreamer/camerabin/camerabinimagecapture_p.h
new file mode 100644
index 000000000..b91987bcd
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinimagecapture_p.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qcameraimagecapturecontrol.h>
+#include "camerabinsession.h"
+
+#include <qvideosurfaceformat.h>
+
+#include <private/qgstreamerbufferprobe_p.h>
+
+#include <gst/video/video.h>
+
+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;
+
+ QCameraImageCapture::CaptureDestinations captureDestination() const override;
+ void setCaptureDestination(QCameraImageCapture::CaptureDestinations destination) override;
+
+ bool processBusMessage(const QGstreamerMessage &message) override;
+
+private slots:
+ void updateState();
+
+private:
+ static GstPadProbeReturn encoderEventProbe(GstPad *, GstPadProbeInfo *info, gpointer user_data);
+
+ 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;
+ GstVideoInfo m_videoInfo;
+ int m_requestId;
+ bool m_ready;
+ QCameraImageCapture::CaptureDestinations m_destination;
+};
+
+QT_END_NAMESPACE
+
+#endif // CAMERABINCAPTURECORNTROL_H
diff --git a/src/multimedia/platform/gstreamer/camerabin/camerabinimageencoder.cpp b/src/multimedia/platform/gstreamer/camerabin/camerabinimageencoder.cpp
new file mode 100644
index 000000000..8c4eaec70
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinimageencoder.cpp
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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/multimedia/platform/gstreamer/camerabin/camerabinimageencoder_p.h b/src/multimedia/platform/gstreamer/camerabin/camerabinimageencoder_p.h
new file mode 100644
index 000000000..217eb1f22
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinimageencoder_p.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/gstreamer/camerabin/camerabinimageprocessing.cpp b/src/multimedia/platform/gstreamer/camerabin/camerabinimageprocessing.cpp
new file mode 100644
index 000000000..3629a1336
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinimageprocessing.cpp
@@ -0,0 +1,409 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+# include <gst/video/colorbalance.h>
+
+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();
+ }
+
+ 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);
+ 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
+
+#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;
+ GstPhotographyWhiteBalanceMode currentMode;
+ if (gst_photography_get_white_balance_mode(m_session->photography(), &currentMode)
+ && currentMode != GST_PHOTOGRAPHY_WB_MODE_MANUAL)
+ {
+ 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()) {
+ GstPhotographyColorToneMode mode = GST_PHOTOGRAPHY_COLOR_TONE_MODE_NORMAL;
+ gst_photography_get_color_tone_mode(photography, &mode);
+ 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()) {
+ gst_photography_set_color_tone_mode(photography, m_filterMap.value(
+ value.value<QCameraImageProcessing::ColorFilter>(),
+ GST_PHOTOGRAPHY_COLOR_TONE_MODE_NORMAL));
+ }
+#endif
+ break;
+ default:
+ break;
+ }
+
+ updateColorBalanceValues();
+}
+
+#if QT_CONFIG(gstreamer_photography)
+void CameraBinImageProcessing::lockWhiteBalance()
+{
+ if (GstPhotography *photography = m_session->photography())
+ gst_photography_set_white_balance_mode(photography, GST_PHOTOGRAPHY_WB_MODE_MANUAL);
+}
+
+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/multimedia/platform/gstreamer/camerabin/camerabinimageprocessing_p.h b/src/multimedia/platform/gstreamer/camerabin/camerabinimageprocessing_p.h
new file mode 100644
index 000000000..2c5c41415
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinimageprocessing_p.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <qcamera.h>
+#include <qcameraimageprocessingcontrol.h>
+
+#include <gst/gst.h>
+#include <glib.h>
+
+#if QT_CONFIG(gstreamer_photography)
+# include <gst/interfaces/photography.h>
+#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/multimedia/platform/gstreamer/camerabin/camerabinmetadata.cpp b/src/multimedia/platform/gstreamer/camerabin/camerabinmetadata.cpp
new file mode 100644
index 000000000..e2b12aab1
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinmetadata.cpp
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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;
+ QMetaType::Type type;
+
+ QGStreamerMetaDataKey(const QString &qtn, const char *gstn, QMetaType::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, QMetaType::QString));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::SubTitle, 0, QMetaType::QString));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Author, 0, QMetaType::QString));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Comment, GST_TAG_COMMENT, QMetaType::QString));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Date, GST_TAG_DATE_TIME, QMetaType::QDateTime));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Description, GST_TAG_DESCRIPTION, QMetaType::QString));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Category, 0, QMetaType::QString));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Genre, GST_TAG_GENRE, QMetaType::QString));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Year, 0, QMetaType::Int));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::UserRating, , QMetaType::Int));
+
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Language, GST_TAG_LANGUAGE_CODE, QMetaType::QString));
+
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Publisher, GST_TAG_ORGANIZATION, QMetaType::QString));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Copyright, GST_TAG_COPYRIGHT, QMetaType::QString));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::ParentalRating, 0, QMetaType::QString));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::RatingOrganisation, 0, QMetaType::QString));
+
+ // Media
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Size, 0, QMetaType::Int));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::MediaType, 0, QMetaType::QString));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Duration, GST_TAG_DURATION, QMetaType::Int));
+
+ // Audio
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::AudioBitRate, GST_TAG_BITRATE, QMetaType::Int));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::AudioCodec, GST_TAG_AUDIO_CODEC, QMetaType::QString));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::ChannelCount, 0, QMetaType::Int));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::SampleRate, 0, QMetaType::Int));
+
+ // Music
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::AlbumTitle, GST_TAG_ALBUM, QMetaType::QString));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::AlbumArtist, GST_TAG_ARTIST, QMetaType::QString));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::ContributingArtist, GST_TAG_PERFORMER, QMetaType::QString));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Composer, GST_TAG_COMPOSER, QMetaType::QString));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Conductor, 0, QMetaType::QString));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Lyrics, 0, QMetaType::QString));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Mood, 0, QMetaType::QString));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::TrackNumber, GST_TAG_TRACK_NUMBER, QMetaType::Int));
+
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::CoverArtUrlSmall, 0, QMetaType::QString));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::CoverArtUrlLarge, 0, QMetaType::QString));
+
+ // Image/Video
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Resolution, 0, QMetaType::QSize));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::PixelAspectRatio, 0, QMetaType::QSize));
+
+ // Video
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::VideoFrameRate, 0, QMetaType::QString));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::VideoBitRate, 0, QMetaType::Double));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::VideoCodec, GST_TAG_VIDEO_CODEC, QMetaType::QString));
+
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::PosterUrl, 0, QMetaType::QString));
+
+ // Movie
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::ChapterNumber, 0, QMetaType::Int));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Director, 0, QMetaType::QString));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::LeadPerformer, GST_TAG_PERFORMER, QMetaType::QString));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Writer, 0, QMetaType::QString));
+
+ // Photos
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::CameraManufacturer, GST_TAG_DEVICE_MANUFACTURER, QMetaType::QString));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::CameraModel, GST_TAG_DEVICE_MODEL, QMetaType::QString));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Event, 0, QMetaType::QString));
+ //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Subject, 0, QMetaType::QString));
+
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Orientation, GST_TAG_IMAGE_ORIENTATION, QMetaType::QString));
+
+ // GPS
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::GPSLatitude, GST_TAG_GEO_LOCATION_LATITUDE, QMetaType::Double));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::GPSLongitude, GST_TAG_GEO_LOCATION_LONGITUDE, QMetaType::Double));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::GPSAltitude, GST_TAG_GEO_LOCATION_ELEVATION, QMetaType::Double));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::GPSTrack, GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION, QMetaType::Double));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::GPSSpeed, GST_TAG_GEO_LOCATION_MOVEMENT_SPEED, QMetaType::Double));
+ metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::GPSImgDirection, GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION, QMetaType::Double));
+ }
+
+ return metadataKeys;
+}
+
+CameraBinMetaData::CameraBinMetaData(QObject *parent)
+ :QMetaDataWriterControl(parent)
+{
+}
+
+QVariant CameraBinMetaData::metaData(const QString &key) const
+{
+ if (key == QMediaMetaData::Orientation)
+ return QGstUtils::fromGStreamerOrientation(m_values.value(QByteArray(GST_TAG_IMAGE_ORIENTATION)));
+
+ if (key == QMediaMetaData::GPSSpeed) {
+ const double metersPerSec = m_values.value(QByteArray(GST_TAG_GEO_LOCATION_MOVEMENT_SPEED)).toDouble();
+ return (metersPerSec * 3600) / 1000;
+ }
+
+ 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 (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;
+ }
+ }
+
+ const auto keys = *qt_gstreamerMetaDataKeys();
+ for (const QGStreamerMetaDataKey &metadataKey : keys) {
+ if (metadataKey.qtName == key) {
+ const char *name = metadataKey.gstName;
+
+ if (correctedValue.isValid()) {
+ correctedValue.convert(QMetaType(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/multimedia/platform/gstreamer/camerabin/camerabinmetadata_p.h b/src/multimedia/platform/gstreamer/camerabin/camerabinmetadata_p.h
new file mode 100644
index 000000000..b4811b9ba
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinmetadata_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmetadatawritercontrol.h>
+#include <qmap.h>
+#include <qvariant.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/multimedia/platform/gstreamer/camerabin/camerabinrecorder.cpp b/src/multimedia/platform/gstreamer/camerabin/camerabinrecorder.cpp
new file mode 100644
index 000000000..a83971226
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinrecorder.cpp
@@ -0,0 +1,289 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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 "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)));
+}
+
+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_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 {
+ 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/multimedia/platform/gstreamer/camerabin/camerabinrecorder_p.h b/src/multimedia/platform/gstreamer/camerabin/camerabinrecorder_p.h
new file mode 100644
index 000000000..58a5ee725
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinrecorder_p.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <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/multimedia/platform/gstreamer/camerabin/camerabinservice.cpp b/src/multimedia/platform/gstreamer/camerabin/camerabinservice.cpp
new file mode 100644
index 000000000..9eee847e0
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinservice.cpp
@@ -0,0 +1,206 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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"
+
+#if QT_CONFIG(gstreamer_photography)
+#include "camerabinexposure.h"
+#include "camerabinfocus.h"
+#endif
+
+#include "camerabinimagecapture.h"
+#include "camerabinimageprocessing.h"
+#include <private/qgstreamerbushelper_p.h>
+#include <private/qgstutils_p.h>
+
+#include <private/qgstreameraudioinputselector_p.h>
+#include <private/qgstreamervideoinputdevicecontrol_p.h>
+
+#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_captureSession = 0;
+ m_metaDataControl = 0;
+
+ m_audioInputSelector = 0;
+ m_videoInputDevice = 0;
+
+ m_videoOutput = 0;
+ m_videoRenderer = 0;
+ m_videoWindow = 0;
+ 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;
+ }
+
+ 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()
+{
+}
+
+QObject *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 (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, QCameraFocusControl_iid) == 0)
+ return m_captureSession->cameraFocusControl();
+#endif
+
+ if (qstrcmp(name, QCameraImageProcessingControl_iid) == 0)
+ return m_captureSession->imageProcessingControl();
+
+ return nullptr;
+}
+
+void CameraBinService::releaseControl(QObject *control)
+{
+ if (control && control == m_videoOutput) {
+ m_videoOutput = 0;
+ m_captureSession->setViewfinder(0);
+ }
+}
+
+bool CameraBinService::isCameraBinAvailable()
+{
+ GstElementFactory *factory = gst_element_factory_find("camerabin");
+ if (factory) {
+ gst_object_unref(GST_OBJECT(factory));
+ return true;
+ }
+
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/gstreamer/camerabin/camerabinservice_p.h b/src/multimedia/platform/gstreamer/camerabin/camerabinservice_p.h
new file mode 100644
index 000000000..24d388759
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinservice_p.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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 QGstreamerElementFactory;
+class CameraBinMetaData;
+class CameraBinImageCapture;
+class CameraBinMetaData;
+
+class CameraBinService : public QMediaService
+{
+ Q_OBJECT
+
+public:
+ CameraBinService(GstElementFactory *sourceFactory, QObject *parent = 0);
+ virtual ~CameraBinService();
+
+ QObject *requestControl(const char *name) override;
+ void releaseControl(QObject *) override;
+
+ static bool isCameraBinAvailable();
+
+private:
+ void setAudioPreview(GstElement*);
+
+ CameraBinSession *m_captureSession;
+ CameraBinMetaData *m_metaDataControl;
+
+ QAudioInputSelectorControl *m_audioInputSelector;
+ QVideoDeviceSelectorControl *m_videoInputDevice;
+
+ QObject *m_videoOutput;
+
+ QObject *m_videoRenderer;
+ QGstreamerVideoWindow *m_videoWindow;
+ CameraBinImageCapture *m_imageCaptureControl;
+};
+
+QT_END_NAMESPACE
+
+#endif // CAMERABINCAPTURESERVICE_H
diff --git a/src/multimedia/platform/gstreamer/camerabin/camerabinserviceplugin.cpp b/src/multimedia/platform/gstreamer/camerabin/camerabinserviceplugin.cpp
new file mode 100644
index 000000000..f8d9b5a37
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinserviceplugin.cpp
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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;
+}
+
+QByteArray CameraBinServicePlugin::defaultDevice(const QByteArray &service) const
+{
+ return service == Q_MEDIASERVICE_CAMERA
+ ? QGstUtils::enumerateCameras().value(0).name.toUtf8()
+ : QByteArray();
+}
+
+QList<QByteArray> CameraBinServicePlugin::devices(const QByteArray &service) const
+{
+
+ return service == Q_MEDIASERVICE_CAMERA
+ ? QGstUtils::cameraDevices()
+ : QList<QByteArray>();
+}
+
+QString CameraBinServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &deviceName)
+{
+ return service == Q_MEDIASERVICE_CAMERA
+ ? QGstUtils::cameraDescription(deviceName)
+ : QString();
+}
+
+QVariant CameraBinServicePlugin::deviceProperty(const QByteArray &service, const QByteArray &device, const QByteArray &property)
+{
+ Q_UNUSED(service);
+ Q_UNUSED(device);
+ Q_UNUSED(property);
+ return QVariant();
+}
+
+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/multimedia/platform/gstreamer/camerabin/camerabinserviceplugin_p.h b/src/multimedia/platform/gstreamer/camerabin/camerabinserviceplugin_p.h
new file mode 100644
index 000000000..00de757bf
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinserviceplugin_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmediaserviceproviderplugin.h>
+#include <private/qgstreamervideoinputdevicecontrol_p.h>
+
+#include <gst/gst.h>
+
+QT_BEGIN_NAMESPACE
+
+class CameraBinServicePlugin
+ : public QMediaServiceProviderPlugin
+ , public QMediaServiceSupportedDevicesInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
+ 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;
+
+ 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);
+
+private:
+ GstElementFactory *sourceFactory() const;
+
+ mutable GstElementFactory *m_sourceFactory;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGSTREAMERCAPTURESERVICEPLUGIN_H
diff --git a/src/multimedia/platform/gstreamer/camerabin/camerabinsession.cpp b/src/multimedia/platform/gstreamer/camerabin/camerabinsession.cpp
new file mode 100644
index 000000000..3c05156cc
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinsession.cpp
@@ -0,0 +1,1510 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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 "camerabinfocus.h"
+#endif
+
+#include "camerabinimageprocessing.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_cameraFocusControl(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("camerabin", "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_imageProcessingControl = new CameraBinImageProcessing(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);
+
+ GstCaps *previewCaps = gst_caps_new_simple(
+ "video/x-raw",
+ "format", G_TYPE_STRING, "RGBx",
+ NULL);
+
+ 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;
+}
+
+CameraBinFocus *CameraBinSession::cameraFocusControl()
+{
+ if (!m_cameraFocusControl && photography())
+ m_cameraFocusControl = new CameraBinFocus(this);
+ return m_cameraFocusControl;
+}
+#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;
+
+ GstStructure *structure = gst_structure_new_empty("audio/x-raw");
+ 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))
+ 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")
+ && 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);
+
+ QImage image;
+ GstVideoInfo previewInfo;
+ if (gst_video_info_from_caps(&previewInfo, previewCaps))
+ image = QGstUtils::bufferToImage(buffer, previewInfo);
+ 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");
+ gst_caps_set_features(caps, i, NULL);
+ 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);
+ }
+ caps = gst_caps_simplify(caps);
+
+ 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");
+ gst_caps_set_features(caps, i, NULL);
+ 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);
+ }
+
+ caps = gst_caps_simplify(caps);
+
+ 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
+ } else if (gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_AUDIO_ENCODER)) {
+ session->m_audioEncoder = element;
+ session->m_audioEncodeControl->applySettings(element);
+ } else if (gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_VIDEO_ENCODER)) {
+ session->m_videoEncoder = element;
+ session->m_videoEncodeControl->applySettings(element);
+ } else if (gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_MUXER)) {
+ 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/multimedia/platform/gstreamer/camerabin/camerabinsession_p.h b/src/multimedia/platform/gstreamer/camerabin/camerabinsession_p.h
new file mode 100644
index 000000000..faa440dde
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinsession_p.h
@@ -0,0 +1,291 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <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 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();
+ CameraBinFocus *cameraFocusControl();
+#endif
+
+ CameraBinImageProcessing *imageProcessingControl() const { return m_imageProcessingControl; }
+
+ 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;
+ CameraBinFocus *m_cameraFocusControl;
+#endif
+ CameraBinImageProcessing *m_imageProcessingControl;
+
+ 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/multimedia/platform/gstreamer/camerabin/camerabinv4limageprocessing.cpp b/src/multimedia/platform/gstreamer/camerabin/camerabinv4limageprocessing.cpp
new file mode 100644
index 000000000..963eb8580
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinv4limageprocessing.cpp
@@ -0,0 +1,316 @@
+/****************************************************************************
+**
+** 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);
+ }
+ 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;
+ }
+
+ constexpr 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 (auto supportedParametersEntrie : supportedParametersEntries) {
+ struct v4l2_queryctrl queryControl;
+ ::memset(&queryControl, 0, sizeof(queryControl));
+ queryControl.id = supportedParametersEntrie.cid;
+
+ if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) != 0) {
+ qWarning() << "Unable to query the parameter info:" << supportedParametersEntrie.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(supportedParametersEntrie.parameter, sourceValueInfo);
+ }
+
+ qt_safe_close(fd);
+ }
+}
+
+qreal CameraBinV4LImageProcessing::scaledImageProcessingParameterValue(
+ qint32 sourceValue, const SourceParameterValueInfo &sourceValueInfo)
+{
+ if (sourceValue == sourceValueInfo.defaultValue)
+ return 0.0f;
+
+ if (sourceValue < sourceValueInfo.defaultValue)
+ return ((sourceValue - sourceValueInfo.minimumValue)
+ / qreal(sourceValueInfo.defaultValue - sourceValueInfo.minimumValue))
+ + (-1.0f);
+
+ return ((sourceValue - sourceValueInfo.defaultValue)
+ / qreal(sourceValueInfo.maximumValue - sourceValueInfo.defaultValue));
+}
+
+qint32 CameraBinV4LImageProcessing::sourceImageProcessingParameterValue(
+ qreal scaledValue, const SourceParameterValueInfo &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;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/gstreamer/camerabin/camerabinv4limageprocessing_p.h b/src/multimedia/platform/gstreamer/camerabin/camerabinv4limageprocessing_p.h
new file mode 100644
index 000000000..d1ca6f0c0
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinv4limageprocessing_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <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/multimedia/platform/gstreamer/camerabin/camerabinvideoencoder.cpp b/src/multimedia/platform/gstreamer/camerabin/camerabinvideoencoder.cpp
new file mode 100644
index 000000000..5bba2ddb5
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinvideoencoder.cpp
@@ -0,0 +1,239 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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/multimedia/platform/gstreamer/camerabin/camerabinvideoencoder_p.h b/src/multimedia/platform/gstreamer/camerabin/camerabinvideoencoder_p.h
new file mode 100644
index 000000000..15ab1a08d
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/camerabin/camerabinvideoencoder_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <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/multimedia/platform/gstreamer/common/common.pri b/src/multimedia/platform/gstreamer/common/common.pri
new file mode 100644
index 000000000..ee1aeeb72
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/common/common.pri
@@ -0,0 +1,41 @@
+HEADERS += \
+ $$PWD/qgstappsrc_p.h \
+ $$PWD/qgstreamerbushelper_p.h \
+ $$PWD/qgstreamermessage_p.h \
+ $$PWD/qgstutils_p.h \
+ $$PWD/qgstvideobuffer_p.h \
+ $$PWD/qgstreamerbufferprobe_p.h \
+ $$PWD/qgstreamervideorendererinterface_p.h \
+ $$PWD/qgstreameraudioinputselector_p.h \
+ $$PWD/qgstreamervideorenderer_p.h \
+ $$PWD/qgstreamervideoinputdevicecontrol_p.h \
+ $$PWD/qgstcodecsinfo_p.h \
+ $$PWD/qgstreamervideoprobecontrol_p.h \
+ $$PWD/qgstreameraudioprobecontrol_p.h \
+ $$PWD/qgstreamervideowindow_p.h \
+ $$PWD/qgstreamervideooverlay_p.h \
+ $$PWD/qgstreamerplayersession_p.h \
+ $$PWD/qgstreamerplayercontrol_p.h \
+ $$PWD/qgstvideorendererplugin_p.h \
+ $$PWD/qgstvideorenderersink_p.h
+
+SOURCES += \
+ $$PWD/qgstappsrc.cpp \
+ $$PWD/qgstreamerbushelper.cpp \
+ $$PWD/qgstreamermessage.cpp \
+ $$PWD/qgstutils.cpp \
+ $$PWD/qgstvideobuffer.cpp \
+ $$PWD/qgstreamerbufferprobe.cpp \
+ $$PWD/qgstreamervideorendererinterface.cpp \
+ $$PWD/qgstreameraudioinputselector.cpp \
+ $$PWD/qgstreamervideorenderer.cpp \
+ $$PWD/qgstreamervideoinputdevicecontrol.cpp \
+ $$PWD/qgstcodecsinfo.cpp \
+ $$PWD/qgstreamervideoprobecontrol.cpp \
+ $$PWD/qgstreameraudioprobecontrol.cpp \
+ $$PWD/qgstreamervideowindow.cpp \
+ $$PWD/qgstreamervideooverlay.cpp \
+ $$PWD/qgstreamerplayersession.cpp \
+ $$PWD/qgstreamerplayercontrol.cpp \
+ $$PWD/qgstvideorendererplugin.cpp \
+ $$PWD/qgstvideorenderersink.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstappsrc.cpp b/src/multimedia/platform/gstreamer/common/qgstappsrc.cpp
index 3c2df339c..3c2df339c 100644
--- a/src/multimedia/platform/gstreamer/qgstappsrc.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstappsrc.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstappsrc_p.h b/src/multimedia/platform/gstreamer/common/qgstappsrc_p.h
index 29603eb98..29603eb98 100644
--- a/src/multimedia/platform/gstreamer/qgstappsrc_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstappsrc_p.h
diff --git a/src/multimedia/platform/gstreamer/qgstcodecsinfo.cpp b/src/multimedia/platform/gstreamer/common/qgstcodecsinfo.cpp
index bbf78124d..bbf78124d 100644
--- a/src/multimedia/platform/gstreamer/qgstcodecsinfo.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstcodecsinfo.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstcodecsinfo_p.h b/src/multimedia/platform/gstreamer/common/qgstcodecsinfo_p.h
index 7f4415e69..7f4415e69 100644
--- a/src/multimedia/platform/gstreamer/qgstcodecsinfo_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstcodecsinfo_p.h
diff --git a/src/multimedia/platform/gstreamer/qgstreameraudioinputselector.cpp b/src/multimedia/platform/gstreamer/common/qgstreameraudioinputselector.cpp
index e91a2af73..e91a2af73 100644
--- a/src/multimedia/platform/gstreamer/qgstreameraudioinputselector.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstreameraudioinputselector.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstreameraudioinputselector_p.h b/src/multimedia/platform/gstreamer/common/qgstreameraudioinputselector_p.h
index 3b258ba3a..3b258ba3a 100644
--- a/src/multimedia/platform/gstreamer/qgstreameraudioinputselector_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstreameraudioinputselector_p.h
diff --git a/src/multimedia/platform/gstreamer/qgstreameraudioprobecontrol.cpp b/src/multimedia/platform/gstreamer/common/qgstreameraudioprobecontrol.cpp
index 8b0415bde..8b0415bde 100644
--- a/src/multimedia/platform/gstreamer/qgstreameraudioprobecontrol.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstreameraudioprobecontrol.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstreameraudioprobecontrol_p.h b/src/multimedia/platform/gstreamer/common/qgstreameraudioprobecontrol_p.h
index b641929cd..b641929cd 100644
--- a/src/multimedia/platform/gstreamer/qgstreameraudioprobecontrol_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstreameraudioprobecontrol_p.h
diff --git a/src/multimedia/platform/gstreamer/qgstreamerbufferprobe.cpp b/src/multimedia/platform/gstreamer/common/qgstreamerbufferprobe.cpp
index 230807466..230807466 100644
--- a/src/multimedia/platform/gstreamer/qgstreamerbufferprobe.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstreamerbufferprobe.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstreamerbufferprobe_p.h b/src/multimedia/platform/gstreamer/common/qgstreamerbufferprobe_p.h
index 5d66f7320..5d66f7320 100644
--- a/src/multimedia/platform/gstreamer/qgstreamerbufferprobe_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstreamerbufferprobe_p.h
diff --git a/src/multimedia/platform/gstreamer/qgstreamerbushelper.cpp b/src/multimedia/platform/gstreamer/common/qgstreamerbushelper.cpp
index 2eb038dfa..2eb038dfa 100644
--- a/src/multimedia/platform/gstreamer/qgstreamerbushelper.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstreamerbushelper.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstreamerbushelper_p.h b/src/multimedia/platform/gstreamer/common/qgstreamerbushelper_p.h
index 01d3ed826..01d3ed826 100644
--- a/src/multimedia/platform/gstreamer/qgstreamerbushelper_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstreamerbushelper_p.h
diff --git a/src/multimedia/platform/gstreamer/qgstreamermessage.cpp b/src/multimedia/platform/gstreamer/common/qgstreamermessage.cpp
index 7191565e1..7191565e1 100644
--- a/src/multimedia/platform/gstreamer/qgstreamermessage.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstreamermessage.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstreamermessage_p.h b/src/multimedia/platform/gstreamer/common/qgstreamermessage_p.h
index d552731d2..d552731d2 100644
--- a/src/multimedia/platform/gstreamer/qgstreamermessage_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstreamermessage_p.h
diff --git a/src/multimedia/platform/gstreamer/qgstreamerplayercontrol.cpp b/src/multimedia/platform/gstreamer/common/qgstreamerplayercontrol.cpp
index be71b81c2..be71b81c2 100644
--- a/src/multimedia/platform/gstreamer/qgstreamerplayercontrol.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstreamerplayercontrol.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstreamerplayercontrol_p.h b/src/multimedia/platform/gstreamer/common/qgstreamerplayercontrol_p.h
index dbc6e3151..dbc6e3151 100644
--- a/src/multimedia/platform/gstreamer/qgstreamerplayercontrol_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstreamerplayercontrol_p.h
diff --git a/src/multimedia/platform/gstreamer/qgstreamerplayersession.cpp b/src/multimedia/platform/gstreamer/common/qgstreamerplayersession.cpp
index 6cccb721e..6cccb721e 100644
--- a/src/multimedia/platform/gstreamer/qgstreamerplayersession.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstreamerplayersession.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstreamerplayersession_p.h b/src/multimedia/platform/gstreamer/common/qgstreamerplayersession_p.h
index f7d09ed3d..f7d09ed3d 100644
--- a/src/multimedia/platform/gstreamer/qgstreamerplayersession_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstreamerplayersession_p.h
diff --git a/src/multimedia/platform/gstreamer/qgstreamervideoinputdevicecontrol.cpp b/src/multimedia/platform/gstreamer/common/qgstreamervideoinputdevicecontrol.cpp
index 088b97101..088b97101 100644
--- a/src/multimedia/platform/gstreamer/qgstreamervideoinputdevicecontrol.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstreamervideoinputdevicecontrol.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstreamervideoinputdevicecontrol_p.h b/src/multimedia/platform/gstreamer/common/qgstreamervideoinputdevicecontrol_p.h
index 632b6dbb4..632b6dbb4 100644
--- a/src/multimedia/platform/gstreamer/qgstreamervideoinputdevicecontrol_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstreamervideoinputdevicecontrol_p.h
diff --git a/src/multimedia/platform/gstreamer/qgstreamervideooverlay.cpp b/src/multimedia/platform/gstreamer/common/qgstreamervideooverlay.cpp
index 06e410821..06e410821 100644
--- a/src/multimedia/platform/gstreamer/qgstreamervideooverlay.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstreamervideooverlay.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstreamervideooverlay_p.h b/src/multimedia/platform/gstreamer/common/qgstreamervideooverlay_p.h
index 883da8a4d..883da8a4d 100644
--- a/src/multimedia/platform/gstreamer/qgstreamervideooverlay_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstreamervideooverlay_p.h
diff --git a/src/multimedia/platform/gstreamer/qgstreamervideoprobecontrol.cpp b/src/multimedia/platform/gstreamer/common/qgstreamervideoprobecontrol.cpp
index 3d587eb2c..3d587eb2c 100644
--- a/src/multimedia/platform/gstreamer/qgstreamervideoprobecontrol.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstreamervideoprobecontrol.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstreamervideoprobecontrol_p.h b/src/multimedia/platform/gstreamer/common/qgstreamervideoprobecontrol_p.h
index 0c4b734b2..0c4b734b2 100644
--- a/src/multimedia/platform/gstreamer/qgstreamervideoprobecontrol_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstreamervideoprobecontrol_p.h
diff --git a/src/multimedia/platform/gstreamer/qgstreamervideorenderer.cpp b/src/multimedia/platform/gstreamer/common/qgstreamervideorenderer.cpp
index c6ca935a4..c6ca935a4 100644
--- a/src/multimedia/platform/gstreamer/qgstreamervideorenderer.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstreamervideorenderer.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstreamervideorenderer_p.h b/src/multimedia/platform/gstreamer/common/qgstreamervideorenderer_p.h
index 10d2c8e2e..10d2c8e2e 100644
--- a/src/multimedia/platform/gstreamer/qgstreamervideorenderer_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstreamervideorenderer_p.h
diff --git a/src/multimedia/platform/gstreamer/qgstreamervideorendererinterface.cpp b/src/multimedia/platform/gstreamer/common/qgstreamervideorendererinterface.cpp
index ae7de06f1..ae7de06f1 100644
--- a/src/multimedia/platform/gstreamer/qgstreamervideorendererinterface.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstreamervideorendererinterface.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstreamervideorendererinterface_p.h b/src/multimedia/platform/gstreamer/common/qgstreamervideorendererinterface_p.h
index af163c1b5..af163c1b5 100644
--- a/src/multimedia/platform/gstreamer/qgstreamervideorendererinterface_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstreamervideorendererinterface_p.h
diff --git a/src/multimedia/platform/gstreamer/qgstreamervideowindow.cpp b/src/multimedia/platform/gstreamer/common/qgstreamervideowindow.cpp
index e7e3c5044..e7e3c5044 100644
--- a/src/multimedia/platform/gstreamer/qgstreamervideowindow.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstreamervideowindow.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstreamervideowindow_p.h b/src/multimedia/platform/gstreamer/common/qgstreamervideowindow_p.h
index cae656347..cae656347 100644
--- a/src/multimedia/platform/gstreamer/qgstreamervideowindow_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstreamervideowindow_p.h
diff --git a/src/multimedia/platform/gstreamer/qgstutils.cpp b/src/multimedia/platform/gstreamer/common/qgstutils.cpp
index 929fbb2d3..929fbb2d3 100644
--- a/src/multimedia/platform/gstreamer/qgstutils.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstutils.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstutils_p.h b/src/multimedia/platform/gstreamer/common/qgstutils_p.h
index 607e543be..607e543be 100644
--- a/src/multimedia/platform/gstreamer/qgstutils_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstutils_p.h
diff --git a/src/multimedia/platform/gstreamer/qgstvideobuffer.cpp b/src/multimedia/platform/gstreamer/common/qgstvideobuffer.cpp
index 58738ffa0..58738ffa0 100644
--- a/src/multimedia/platform/gstreamer/qgstvideobuffer.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstvideobuffer.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstvideobuffer_p.h b/src/multimedia/platform/gstreamer/common/qgstvideobuffer_p.h
index 11a217f51..11a217f51 100644
--- a/src/multimedia/platform/gstreamer/qgstvideobuffer_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstvideobuffer_p.h
diff --git a/src/multimedia/platform/gstreamer/qgstvideorendererplugin.cpp b/src/multimedia/platform/gstreamer/common/qgstvideorendererplugin.cpp
index 1b63cbfa8..1b63cbfa8 100644
--- a/src/multimedia/platform/gstreamer/qgstvideorendererplugin.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstvideorendererplugin.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstvideorendererplugin_p.h b/src/multimedia/platform/gstreamer/common/qgstvideorendererplugin_p.h
index d6f13062e..d6f13062e 100644
--- a/src/multimedia/platform/gstreamer/qgstvideorendererplugin_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstvideorendererplugin_p.h
diff --git a/src/multimedia/platform/gstreamer/qgstvideorenderersink.cpp b/src/multimedia/platform/gstreamer/common/qgstvideorenderersink.cpp
index 0f930cf02..0f930cf02 100644
--- a/src/multimedia/platform/gstreamer/qgstvideorenderersink.cpp
+++ b/src/multimedia/platform/gstreamer/common/qgstvideorenderersink.cpp
diff --git a/src/multimedia/platform/gstreamer/qgstvideorenderersink_p.h b/src/multimedia/platform/gstreamer/common/qgstvideorenderersink_p.h
index 0b05bbe83..0b05bbe83 100644
--- a/src/multimedia/platform/gstreamer/qgstvideorenderersink_p.h
+++ b/src/multimedia/platform/gstreamer/common/qgstvideorenderersink_p.h
diff --git a/src/multimedia/platform/gstreamer/gstreamer.pri b/src/multimedia/platform/gstreamer/gstreamer.pri
index 324e430f5..c393c756d 100644
--- a/src/multimedia/platform/gstreamer/gstreamer.pri
+++ b/src/multimedia/platform/gstreamer/gstreamer.pri
@@ -2,66 +2,24 @@ DEFINES += GLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_26
QMAKE_USE_PRIVATE += gstreamer gstreamer_app
-HEADERS += \
- platform/gstreamer/qaudiointerface_gstreamer_p.h \
- platform/gstreamer/qaudiodeviceinfo_gstreamer_p.h \
- platform/gstreamer/qaudiooutput_gstreamer_p.h \
- platform/gstreamer/qaudioinput_gstreamer_p.h \
- platform/gstreamer/qaudioengine_gstreamer_p.h \
- platform/gstreamer/qgstappsrc_p.h \
- platform/gstreamer/qgstreamerbushelper_p.h \
- platform/gstreamer/qgstreamermessage_p.h \
- platform/gstreamer/qgstutils_p.h \
- platform/gstreamer/qgstvideobuffer_p.h \
- platform/gstreamer/qgstreamerbufferprobe_p.h \
- platform/gstreamer/qgstreamervideorendererinterface_p.h \
- platform/gstreamer/qgstreameraudioinputselector_p.h \
- platform/gstreamer/qgstreamervideorenderer_p.h \
- platform/gstreamer/qgstreamervideoinputdevicecontrol_p.h \
- platform/gstreamer/qgstcodecsinfo_p.h \
- platform/gstreamer/qgstreamervideoprobecontrol_p.h \
- platform/gstreamer/qgstreameraudioprobecontrol_p.h \
- platform/gstreamer/qgstreamervideowindow_p.h \
- platform/gstreamer/qgstreamervideooverlay_p.h \
- platform/gstreamer/qgstreamerplayersession_p.h \
- platform/gstreamer/qgstreamerplayercontrol_p.h \
- platform/gstreamer/qgstvideorendererplugin_p.h \
- platform/gstreamer/qgstvideorenderersink_p.h
-
-SOURCES += \
- platform/gstreamer/qaudiointerface_gstreamer.cpp \
- platform/gstreamer/qaudiodeviceinfo_gstreamer.cpp \
- platform/gstreamer/qaudiooutput_gstreamer.cpp \
- platform/gstreamer/qaudioinput_gstreamer.cpp \
- platform/gstreamer/qaudioengine_gstreamer.cpp \
- platform/gstreamer/qgstappsrc.cpp \
- platform/gstreamer/qgstreamerbushelper.cpp \
- platform/gstreamer/qgstreamermessage.cpp \
- platform/gstreamer/qgstutils.cpp \
- platform/gstreamer/qgstvideobuffer.cpp \
- platform/gstreamer/qgstreamerbufferprobe.cpp \
- platform/gstreamer/qgstreamervideorendererinterface.cpp \
- platform/gstreamer/qgstreameraudioinputselector.cpp \
- platform/gstreamer/qgstreamervideorenderer.cpp \
- platform/gstreamer/qgstreamervideoinputdevicecontrol.cpp \
- platform/gstreamer/qgstcodecsinfo.cpp \
- platform/gstreamer/qgstreamervideoprobecontrol.cpp \
- platform/gstreamer/qgstreameraudioprobecontrol.cpp \
- platform/gstreamer/qgstreamervideowindow.cpp \
- platform/gstreamer/qgstreamervideooverlay.cpp \
- platform/gstreamer/qgstreamerplayersession.cpp \
- platform/gstreamer/qgstreamerplayercontrol.cpp \
- platform/gstreamer/qgstvideorendererplugin.cpp \
- platform/gstreamer/qgstvideorenderersink.cpp
+include(audio/audio.pri)
+include(common/common.pri)
+use_camerabin {
+ include(camerabin/camerabin.pri)
+ DEFINES += GST_USE_CAMERABIN
+} else {
+ include(mediacapture/mediacapture.pri)
+}
+include(mediaplayer/mediaplayer.pri)
qtConfig(gstreamer_gl): QMAKE_USE += gstreamer_gl
android {
LIBS_PRIVATE += \
-L$$(GSTREAMER_ROOT_ANDROID)/armv7/lib \
- -Wl,--whole-archive \
+ -Wl,--_p.hole-archive \
-lgstapp-1.0 -lgstreamer-1.0 -lgstaudio-1.0 -lgsttag-1.0 -lgstvideo-1.0 -lgstbase-1.0 -lgstpbutils-1.0 \
-lgobject-2.0 -lgmodule-2.0 -lglib-2.0 -lffi -lintl -liconv -lorc-0.4 \
- -Wl,--no-whole-archive
+ -Wl,--no-_p.hole-archive
}
diff --git a/src/multimedia/platform/gstreamer/mediacapture/mediacapture.pri b/src/multimedia/platform/gstreamer/mediacapture/mediacapture.pri
new file mode 100644
index 000000000..f87e95200
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/mediacapture.pri
@@ -0,0 +1,37 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += $$PWD/qgstreamercaptureservice_p.h \
+ $$PWD/qgstreamercapturesession_p.h \
+ $$PWD/qgstreameraudioencode_p.h \
+ $$PWD/qgstreamervideoencode_p.h \
+ $$PWD/qgstreamerrecordercontrol_p.h \
+ $$PWD/qgstreamermediacontainercontrol_p.h \
+ $$PWD/qgstreamercameracontrol_p.h \
+ $$PWD/qgstreamercapturemetadatacontrol_p.h \
+ $$PWD/qgstreamerimagecapturecontrol_p.h \
+ $$PWD/qgstreamerimageencode_p.h \
+ $$PWD/qgstreamercaptureserviceplugin_p.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
+
+ HEADERS += \
+ $$PWD/qgstreamerv4l2input_p.h
+ SOURCES += \
+ $$PWD/qgstreamerv4l2input.cpp
+}
diff --git a/src/multimedia/platform/gstreamer/mediacapture/qgstreameraudioencode.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreameraudioencode.cpp
new file mode 100644
index 000000000..08a3a41d0
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreameraudioencode.cpp
@@ -0,0 +1,239 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+#include "qgstreamercapturesession_p.h"
+#include "qgstreamermediacontainercontrol_p.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("audio/x-raw");
+
+ 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.typeId()) {
+ case QMetaType::Int:
+ g_object_set(G_OBJECT(encoderElement), option.toLatin1(), value.toInt(), NULL);
+ break;
+ case QMetaType::Bool:
+ g_object_set(G_OBJECT(encoderElement), option.toLatin1(), value.toBool(), NULL);
+ break;
+ case QMetaType::Double:
+ g_object_set(G_OBJECT(encoderElement), option.toLatin1(), value.toDouble(), NULL);
+ break;
+ case QMetaType::QString:
+ 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/multimedia/platform/gstreamer/mediacapture/qgstreameraudioencode_p.h b/src/multimedia/platform/gstreamer/mediacapture/qgstreameraudioencode_p.h
new file mode 100644
index 000000000..f77a6fab9
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreameraudioencode_p.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/gstreamer/mediacapture/qgstreamercameracontrol.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercameracontrol.cpp
new file mode 100644
index 000000000..277880c35
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercameracontrol.cpp
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+#include "qgstreamerimageencode_p.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;
+ }
+
+ 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/multimedia/platform/gstreamer/mediacapture/qgstreamercameracontrol_p.h b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercameracontrol_p.h
new file mode 100644
index 000000000..fb3b3312d
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercameracontrol_p.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QHash>
+#include <qcameracontrol.h>
+#include "qgstreamercapturesession_p.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;
+
+ bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const override;
+
+ QCamera::LockTypes supportedLocks() const override
+ {
+ return QCamera::NoLock;
+ }
+
+ QCamera::LockStatus lockStatus(QCamera::LockType /*lock*/) const override { return QCamera::Unlocked; }
+
+ void searchAndLock(QCamera::LockTypes /*locks*/) override {}
+ void unlock(QCamera::LockTypes /*locks*/) override {}
+
+
+ QList<QCameraViewfinderSettings> supportedViewfinderSettings() const override { return {}; }
+
+ QCameraViewfinderSettings viewfinderSettings() const override { return {}; }
+ void setViewfinderSettings(const QCameraViewfinderSettings &/*settings*/) 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/multimedia/platform/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.cpp
new file mode 100644
index 000000000..cbabca6ee
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.cpp
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.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/multimedia/platform/gstreamer/mediacapture/qgstreamercapturemetadatacontrol_p.h b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturemetadatacontrol_p.h
new file mode 100644
index 000000000..6e2106019
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturemetadatacontrol_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmetadatawritercontrol.h>
+#include <qvariant.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/multimedia/platform/gstreamer/mediacapture/qgstreamercaptureservice.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercaptureservice.cpp
new file mode 100644
index 000000000..16e5badbc
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercaptureservice.cpp
@@ -0,0 +1,205 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+#include "qgstreamercapturesession_p.h"
+#include "qgstreamerrecordercontrol_p.h"
+#include "qgstreamermediacontainercontrol_p.h"
+#include "qgstreameraudioencode_p.h"
+#include "qgstreamervideoencode_p.h"
+#include "qgstreamerimageencode_p.h"
+#include "qgstreamercameracontrol_p.h"
+#include <private/qgstreamerbushelper_p.h>
+#include "qgstreamercapturemetadatacontrol_p.h"
+
+#if defined(USE_GSTREAMER_CAMERA)
+#include "qgstreamerv4l2input_p.h"
+#endif
+
+#include "qgstreamerimagecapturecontrol_p.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>
+
+#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)
+ , 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;
+ }
+
+ 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()
+{
+}
+
+QObject *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 (m_videoOutput) {
+ m_captureSession->setVideoPreview(m_videoOutput);
+ return m_videoOutput;
+ }
+ }
+
+ return 0;
+}
+
+void QGstreamerCaptureService::releaseControl(QObject *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/multimedia/platform/gstreamer/mediacapture/qgstreamercaptureservice_p.h b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercaptureservice_p.h
new file mode 100644
index 000000000..fbb8992b6
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercaptureservice_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmediaservice.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 QGstreamerElementFactory;
+class QGstreamerCaptureMetaDataControl;
+class QGstreamerImageCaptureControl;
+class QGstreamerV4L2Input;
+
+class QGstreamerCaptureService : public QMediaService
+{
+ Q_OBJECT
+
+public:
+ QGstreamerCaptureService(const QString &service, QObject *parent = 0);
+ virtual ~QGstreamerCaptureService();
+
+ QObject *requestControl(const char *name) override;
+ void releaseControl(QObject *) 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;
+
+ QObject *m_videoOutput;
+
+ QGstreamerVideoRenderer *m_videoRenderer;
+ QGstreamerVideoWindow *m_videoWindow;
+ QGstreamerImageCaptureControl *m_imageCaptureControl;
+
+ QGstreamerAudioProbeControl *m_audioProbeControl;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGSTREAMERCAPTURESERVICE_H
diff --git a/src/multimedia/platform/gstreamer/mediacapture/qgstreamercaptureserviceplugin.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercaptureserviceplugin.cpp
new file mode 100644
index 000000000..5adf9c701
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercaptureserviceplugin.cpp
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+//#define QT_SUPPORTEDMIMETYPES_DEBUG
+
+#include "qgstreamercaptureservice_p.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)
+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)
+{
+ 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);
+}
+
+void QGstreamerCaptureServicePlugin::updateSupportedMimeTypes() const
+{
+ m_supportedMimeTypeSet = QGstUtils::supportedMimeTypes(isEncoderOrMuxer);
+}
+
+QStringList QGstreamerCaptureServicePlugin::supportedMimeTypes() const
+{
+ return QStringList();
+}
+
diff --git a/src/multimedia/platform/gstreamer/mediacapture/qgstreamercaptureserviceplugin_p.h b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercaptureserviceplugin_p.h
new file mode 100644
index 000000000..e0290195a
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercaptureserviceplugin_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmediaserviceproviderplugin.h>
+#include <QtCore/qset.h>
+#include <QtCore/QObject>
+
+QT_BEGIN_NAMESPACE
+
+class QGstreamerCaptureServicePlugin
+ : public QMediaServiceProviderPlugin
+#if defined(USE_GSTREAMER_CAMERA)
+ , public QMediaServiceSupportedDevicesInterface
+#endif
+ , public QMediaServiceSupportedFormatsInterface
+{
+ Q_OBJECT
+#if defined(USE_GSTREAMER_CAMERA)
+ Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
+#endif
+ Q_INTERFACES(QMediaServiceSupportedFormatsInterface)
+public:
+ QMediaService* create(const QString &key) override;
+ void release(QMediaService *service) override;
+
+#if defined(USE_GSTREAMER_CAMERA)
+ 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/multimedia/platform/gstreamer/mediacapture/qgstreamercapturesession.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturesession.cpp
new file mode 100644
index 000000000..c19120a73
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturesession.cpp
@@ -0,0 +1,1020 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+#include "qgstreamerrecordercontrol_p.h"
+#include "qgstreamermediacontainercontrol_p.h"
+#include "qgstreameraudioencode_p.h"
+#include "qgstreamervideoencode_p.h"
+#include "qgstreamerimageencode_p.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("videoconvert", "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("videoconvert", "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("videoconvert", "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)
+{
+ gst_video_info_from_caps(&m_previewInfo, caps);
+}
+
+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;
+
+ QImage img = QGstUtils::bufferToImage(buffer, m_previewInfo);
+
+ 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)) {
+ GstMapInfo info;
+ if (gst_buffer_map(buffer, &info, GST_MAP_READ)) {
+ f.write(reinterpret_cast<const char *>(info.data), info.size);
+ gst_buffer_unmap(buffer, &info);
+ }
+ f.close();
+
+ static QMetaMethod savedSignal = QMetaMethod::fromSignal(&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("videoconvert", "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/multimedia/platform/gstreamer/mediacapture/qgstreamercapturesession_p.h b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturesession_p.h
new file mode 100644
index 000000000..3c3e0c82f
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturesession_p.h
@@ -0,0 +1,253 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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;
+
+ GstVideoInfo m_previewInfo;
+
+public:
+ bool m_passImage;
+ bool m_passPrerollImage;
+ QString m_imageFileName;
+ int m_imageRequestId;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGSTREAMERCAPTURESESSION_H
diff --git a/src/multimedia/platform/gstreamer/mediacapture/qgstreamerimagecapturecontrol.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerimagecapturecontrol.cpp
new file mode 100644
index 000000000..f89c30110
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerimagecapturecontrol.cpp
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.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/multimedia/platform/gstreamer/mediacapture/qgstreamerimagecapturecontrol_p.h b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerimagecapturecontrol_p.h
new file mode 100644
index 000000000..3bdf96f4a
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerimagecapturecontrol_p.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qcameraimagecapturecontrol.h>
+#include "qgstreamercapturesession_p.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;
+
+ QCameraImageCapture::CaptureDestinations captureDestination() const override { return QCameraImageCapture::CaptureToBuffer; }
+ virtual void setCaptureDestination(QCameraImageCapture::CaptureDestinations /*destination*/) 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/multimedia/platform/gstreamer/mediacapture/qgstreamerimageencode.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerimageencode.cpp
new file mode 100644
index 000000000..f64fd235a
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerimageencode.cpp
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+#include "qgstreamercapturesession_p.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/multimedia/platform/gstreamer/mediacapture/qgstreamerimageencode_p.h b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerimageencode_p.h
new file mode 100644
index 000000000..eef51eee0
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerimageencode_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/gstreamer/mediacapture/qgstreamermediacontainercontrol.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamermediacontainercontrol.cpp
new file mode 100644
index 000000000..6375a2207
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamermediacontainercontrol.cpp
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgstreamermediacontainercontrol_p.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/multimedia/platform/gstreamer/mediacapture/qgstreamermediacontainercontrol_p.h b/src/multimedia/platform/gstreamer/mediacapture/qgstreamermediacontainercontrol_p.h
new file mode 100644
index 000000000..279dbe931
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamermediacontainercontrol_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp
new file mode 100644
index 000000000..4477973f8
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp
@@ -0,0 +1,372 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+#include "qgstreameraudioencode_p.h"
+#include "qgstreamervideoencode_p.h"
+#include "qgstreamermediacontainercontrol_p.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/multimedia/platform/gstreamer/mediacapture/qgstreamerrecordercontrol_p.h b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerrecordercontrol_p.h
new file mode 100644
index 000000000..dfe0299a4
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerrecordercontrol_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/QDir>
+
+#include <qmediarecordercontrol.h>
+#include "qgstreamercapturesession_p.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/multimedia/platform/gstreamer/mediacapture/qgstreamerv4l2input.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerv4l2input.cpp
new file mode 100644
index 000000000..473700e64
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerv4l2input.cpp
@@ -0,0 +1,286 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.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(QSet<int>{frameRates.constBegin(), frameRates.constEnd()});
+ m_ratesByResolution[s].unite(QSet<int>{frameRates.constBegin(), frameRates.constEnd()});
+ }
+ }
+
+ f.close();
+
+ for (int rate : qAsConst(allFrameRates)) {
+ m_frameRates.append(rate/1000.0);
+ }
+
+ std::sort(m_frameRates.begin(), m_frameRates.end());
+
+ m_resolutions = QList<QSize>{allResolutions.constBegin(), allResolutions.constEnd()};
+ 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/multimedia/platform/gstreamer/mediacapture/qgstreamerv4l2input_p.h b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerv4l2input_p.h
new file mode 100644
index 000000000..f2a3f075a
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerv4l2input_p.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/qhash.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qsize.h>
+#include "qgstreamercapturesession_p.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/multimedia/platform/gstreamer/mediacapture/qgstreamervideoencode.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamervideoencode.cpp
new file mode 100644
index 000000000..ae46f2422
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamervideoencode.cpp
@@ -0,0 +1,296 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+#include "qgstreamercapturesession_p.h"
+#include "qgstreamermediacontainercontrol_p.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("videoconvert", 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.typeId()) {
+ case QMetaType::Int:
+ g_object_set(G_OBJECT(encoderElement), option.toLatin1(), value.toInt(), NULL);
+ break;
+ case QMetaType::Bool:
+ g_object_set(G_OBJECT(encoderElement), option.toLatin1(), value.toBool(), NULL);
+ break;
+ case QMetaType::Double:
+ g_object_set(G_OBJECT(encoderElement), option.toLatin1(), value.toDouble(), NULL);
+ break;
+ case QMetaType::QString:
+ 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/multimedia/platform/gstreamer/mediacapture/qgstreamervideoencode_p.h b/src/multimedia/platform/gstreamer/mediacapture/qgstreamervideoencode_p.h
new file mode 100644
index 000000000..5cc38e25d
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamervideoencode_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/gstreamer/mediaplayer/mediaplayer.pri b/src/multimedia/platform/gstreamer/mediaplayer/mediaplayer.pri
new file mode 100644
index 000000000..0e7502105
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediaplayer/mediaplayer.pri
@@ -0,0 +1,13 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/qgstreamerplayerservice_p.h \
+ $$PWD/qgstreamerstreamscontrol_p.h \
+ $$PWD/qgstreamermetadataprovider_p.h \
+ $$PWD/qgstreamerplayerserviceplugin_p.h
+
+SOURCES += \
+ $$PWD/qgstreamerplayerservice.cpp \
+ $$PWD/qgstreamerstreamscontrol.cpp \
+ $$PWD/qgstreamermetadataprovider.cpp \
+ $$PWD/qgstreamerplayerserviceplugin.cpp
diff --git a/src/multimedia/platform/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp b/src/multimedia/platform/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp
new file mode 100644
index 000000000..578bbb8db
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.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);
+ metadataKeys->insert(GST_TAG_ALBUM_ARTIST, QMediaMetaData::AlbumArtist);
+ 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);
+ metadataKeys->insert(GST_TAG_IMAGE_ORIENTATION, QMediaMetaData::Orientation);
+
+ // 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 (key == QMediaMetaData::Orientation)
+ return QGstUtils::fromGStreamerOrientation(m_tags.value(key));
+ 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/multimedia/platform/gstreamer/mediaplayer/qgstreamermetadataprovider_p.h b/src/multimedia/platform/gstreamer/mediaplayer/qgstreamermetadataprovider_p.h
new file mode 100644
index 000000000..468ab060b
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediaplayer/qgstreamermetadataprovider_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmetadatareadercontrol.h>
+#include <qvariant.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/multimedia/platform/gstreamer/mediaplayer/qgstreamerplayerservice.cpp b/src/multimedia/platform/gstreamer/mediaplayer/qgstreamerplayerservice.cpp
new file mode 100644
index 000000000..0c548c730
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediaplayer/qgstreamerplayerservice.cpp
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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>
+
+
+#include "qgstreamerplayerservice_p.h"
+#include "qgstreamermetadataprovider_p.h"
+
+#include <private/qgstreamervideowindow_p.h>
+#include <private/qgstreamervideorenderer_p.h>
+
+#include "qgstreamerstreamscontrol_p.h"
+#include <private/qgstreameraudioprobecontrol_p.h>
+#include <private/qgstreamervideoprobecontrol_p.h>
+#include <private/qgstreamerplayersession_p.h>
+#include <private/qgstreamerplayercontrol_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_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;
+ }
+}
+
+QGstreamerPlayerService::~QGstreamerPlayerService()
+{
+}
+
+QObject *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, 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 (m_videoOutput) {
+ increaseVideoRef();
+ m_control->setVideoOutput(m_videoOutput);
+ return m_videoOutput;
+ }
+ }
+
+ return 0;
+}
+
+void QGstreamerPlayerService::releaseControl(QObject *control)
+{
+ if (!control)
+ return;
+
+ 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++;
+}
+
+void QGstreamerPlayerService::decreaseVideoRef()
+{
+ m_videoReferenceCount--;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/gstreamer/mediaplayer/qgstreamerplayerservice_p.h b/src/multimedia/platform/gstreamer/mediaplayer/qgstreamerplayerservice_p.h
new file mode 100644
index 000000000..12550be16
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediaplayer/qgstreamerplayerservice_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/qiodevice.h>
+
+#include <qmediaservice.h>
+
+QT_BEGIN_NAMESPACE
+class QMediaPlayerControl;
+
+class QGstreamerMetaData;
+class QGstreamerPlayerControl;
+class QGstreamerPlayerSession;
+class QGstreamerMetaDataProvider;
+class QGstreamerStreamsControl;
+class QGstreamerVideoRenderer;
+class QGstreamerVideoWindow;
+class QGStreamerAvailabilityControl;
+class QGstreamerAudioProbeControl;
+class QGstreamerVideoProbeControl;
+
+class QGstreamerPlayerService : public QMediaService
+{
+ Q_OBJECT
+public:
+ QGstreamerPlayerService(QObject *parent = 0);
+ ~QGstreamerPlayerService();
+
+ QObject *requestControl(const char *name) override;
+ void releaseControl(QObject *control) override;
+
+private:
+ QGstreamerPlayerControl *m_control = nullptr;
+ QGstreamerPlayerSession *m_session = nullptr;
+ QGstreamerMetaDataProvider *m_metaData = nullptr;
+ QGstreamerStreamsControl *m_streamsControl = nullptr;
+
+ QGstreamerAudioProbeControl *m_audioProbeControl = nullptr;
+ QGstreamerVideoProbeControl *m_videoProbeControl = nullptr;
+
+ QObject *m_videoOutput = nullptr;
+ QObject *m_videoRenderer = nullptr;
+ QGstreamerVideoWindow *m_videoWindow = nullptr;
+
+ void increaseVideoRef();
+ void decreaseVideoRef();
+ int m_videoReferenceCount = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.cpp b/src/multimedia/platform/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.cpp
new file mode 100644
index 000000000..4da251014
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.cpp
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+//#define QT_SUPPORTEDMIMETYPES_DEBUG
+
+#include "qgstreamerplayerservice_p.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;
+}
+
+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)
+{
+ 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);
+}
+
+void QGstreamerPlayerServicePlugin::updateSupportedMimeTypes() const
+{
+ m_supportedMimeTypeSet = QGstUtils::supportedMimeTypes(isDecoderOrDemuxer);
+}
+
+QStringList QGstreamerPlayerServicePlugin::supportedMimeTypes() const
+{
+ return QStringList();
+}
+
diff --git a/src/multimedia/platform/gstreamer/mediaplayer/qgstreamerplayerserviceplugin_p.h b/src/multimedia/platform/gstreamer/mediaplayer/qgstreamerplayerserviceplugin_p.h
new file mode 100644
index 000000000..8c4e6630c
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediaplayer/qgstreamerplayerserviceplugin_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmediaserviceproviderplugin.h>
+#include <QtCore/qset.h>
+#include <QtCore/QObject>
+
+QT_BEGIN_NAMESPACE
+
+
+class QGstreamerPlayerServicePlugin
+ : public QMediaServiceProviderPlugin
+ , public QMediaServiceSupportedFormatsInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QMediaServiceSupportedFormatsInterface)
+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; //for fast access
+};
+
+QT_END_NAMESPACE
+
+#endif // QGSTREAMERPLAYERSERVICEPLUGIN_H
+
diff --git a/src/multimedia/platform/gstreamer/mediaplayer/qgstreamerstreamscontrol.cpp b/src/multimedia/platform/gstreamer/mediaplayer/qgstreamerstreamscontrol.cpp
new file mode 100644
index 000000000..a4a2f46ec
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediaplayer/qgstreamerstreamscontrol.cpp
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.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/multimedia/platform/gstreamer/mediaplayer/qgstreamerstreamscontrol_p.h b/src/multimedia/platform/gstreamer/mediaplayer/qgstreamerstreamscontrol_p.h
new file mode 100644
index 000000000..730dcba3a
--- /dev/null
+++ b/src/multimedia/platform/gstreamer/mediaplayer/qgstreamerstreamscontrol_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/opensles/opensles.pri b/src/multimedia/platform/opensles/opensles.pri
index 845c2f680..c8f0eda4f 100644
--- a/src/multimedia/platform/opensles/opensles.pri
+++ b/src/multimedia/platform/opensles/opensles.pri
@@ -1,15 +1,15 @@
LIBS += -lOpenSLES
HEADERS += \
- platform/opensles/qopenslesinterface_p.h \
- platform/opensles/qopenslesengine_p.h \
- platform/opensles/qopenslesdeviceinfo_p.h \
- platform/opensles/qopenslesaudioinput_p.h \
- platform/opensles/qopenslesaudiooutput_p.h
+ $$PWD/qopenslesinterface_p.h \
+ $$PWD/qopenslesengine_p.h \
+ $$PWD/qopenslesdeviceinfo_p.h \
+ $$PWD/qopenslesaudioinput_p.h \
+ $$PWD/qopenslesaudiooutput_p.h
SOURCES += \
- platform/opensles/qopenslesinterface.cpp \
- platform/opensles/qopenslesengine.cpp \
- platform/opensles/qopenslesdeviceinfo.cpp \
- platform/opensles/qopenslesaudioinput.cpp \
- platform/opensles/qopenslesaudiooutput.cpp
+ $$PWD/qopenslesinterface.cpp \
+ $$PWD/qopenslesengine.cpp \
+ $$PWD/qopenslesdeviceinfo.cpp \
+ $$PWD/qopenslesaudioinput.cpp \
+ $$PWD/qopenslesaudiooutput.cpp
diff --git a/src/multimedia/platform/platform.pri b/src/multimedia/platform/platform.pri
index 5d2ab45f5..b1c0d5db5 100644
--- a/src/multimedia/platform/platform.pri
+++ b/src/multimedia/platform/platform.pri
@@ -2,8 +2,17 @@ qtConfig(gstreamer):include(gstreamer/gstreamer.pri)
qtConfig(pulseaudio): include(pulseaudio/pulseaudio.pri)
qtConfig(alsa): include(alsa/alsa.pri)
-android: include(opensles/opensles.pri)
-win32: include(wasapi/wasapi.pri)
-darwin:!watchos: include(coreaudio/coreaudio.pri)
+android {
+ include(android/android.pri)
+ include(opensles/opensles.pri)
+}
+win32 {
+ include(wasapi/wasapi.pri)
+ include(wmf/wmf.pri)
+}
+darwin:!watchos {
+ include(coreaudio/coreaudio.pri)
+ include(avfoundation/avfoundation.pri)
+}
qnx: include(qnx/qnx.pri)
diff --git a/src/multimedia/platform/pulseaudio/pulseaudio.pri b/src/multimedia/platform/pulseaudio/pulseaudio.pri
index c46054579..abf48caaa 100644
--- a/src/multimedia/platform/pulseaudio/pulseaudio.pri
+++ b/src/multimedia/platform/pulseaudio/pulseaudio.pri
@@ -1,15 +1,15 @@
QMAKE_USE_PRIVATE += pulseaudio
-HEADERS += platform/pulseaudio/qaudiointerface_pulse_p.h \
- platform/pulseaudio/qaudiodeviceinfo_pulse_p.h \
- platform/pulseaudio/qaudiooutput_pulse_p.h \
- platform/pulseaudio/qaudioinput_pulse_p.h \
- platform/pulseaudio/qaudioengine_pulse_p.h \
- platform/pulseaudio/qpulsehelpers_p.h
+HEADERS += $$PWD/qaudiointerface_pulse_p.h \
+ $$PWD/qaudiodeviceinfo_pulse_p.h \
+ $$PWD/qaudiooutput_pulse_p.h \
+ $$PWD/qaudioinput_pulse_p.h \
+ $$PWD/qaudioengine_pulse_p.h \
+ $$PWD/qpuls_p.helpers_p.h
-SOURCES += platform/pulseaudio/qaudiointerface_pulse.cpp \
- platform/pulseaudio/qaudiodeviceinfo_pulse.cpp \
- platform/pulseaudio/qaudiooutput_pulse.cpp \
- platform/pulseaudio/qaudioinput_pulse.cpp \
- platform/pulseaudio/qaudioengine_pulse.cpp \
- platform/pulseaudio/qpulsehelpers.cpp
+SOURCES += $$PWD/qaudiointerface_pulse.cpp \
+ $$PWD/qaudiodeviceinfo_pulse.cpp \
+ $$PWD/qaudiooutput_pulse.cpp \
+ $$PWD/qaudioinput_pulse.cpp \
+ $$PWD/qaudioengine_pulse.cpp \
+ $$PWD/qpuls_p.helpers.cpp
diff --git a/src/multimedia/platform/qnx/camera/bbcameraaudioencodersettingscontrol.cpp b/src/multimedia/platform/qnx/camera/bbcameraaudioencodersettingscontrol.cpp
new file mode 100644
index 000000000..2c0529bc4
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameraaudioencodersettingscontrol.cpp
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include "bbcamerasession_p.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/multimedia/platform/qnx/camera/bbcameraaudioencodersettingscontrol_p.h b/src/multimedia/platform/qnx/camera/bbcameraaudioencodersettingscontrol_p.h
new file mode 100644
index 000000000..cdc384537
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameraaudioencodersettingscontrol_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/qnx/camera/bbcameracontrol.cpp b/src/multimedia/platform/qnx/camera/bbcameracontrol.cpp
new file mode 100644
index 000000000..ba298301e
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameracontrol.cpp
@@ -0,0 +1,290 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include "bbcamerasession_p.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)));
+
+ connect(m_session, SIGNAL(cameraOpened()), SLOT(cameraOpened()));
+ connect(m_session, SIGNAL(focusStatusChanged(int)), SLOT(focusStatusChanged(int)));
+}
+
+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;
+}
+
+QCamera::LockTypes BbCameraControl::supportedLocks() const
+{
+ return (QCamera::LockFocus | QCamera::LockExposure | QCamera::LockWhiteBalance);
+}
+
+QCamera::LockStatus BbCameraControl::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 BbCameraControl::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 BbCameraControl::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 BbCameraControl::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 BbCameraControl::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/multimedia/platform/qnx/camera/bbcameracontrol_p.h b/src/multimedia/platform/qnx/camera/bbcameracontrol_p.h
new file mode 100644
index 000000000..7557300cd
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameracontrol_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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;
+
+ enum LocksApplyMode
+ {
+ IndependentMode,
+ FocusExposureBoundMode,
+ AllBoundMode,
+ FocusOnlyMode
+ };
+
+ 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 = IndependentMode;
+ QCamera::LockStatus m_focusLockStatus = QCamera::Unlocked;
+ QCamera::LockStatus m_exposureLockStatus = QCamera::Unlocked;
+ QCamera::LockStatus m_whiteBalanceLockStatus = QCamera::Unlocked;
+ QCamera::LockTypes m_currentLockTypes = QCamera::NoLock;
+ QCamera::LockTypes m_supportedLockTypes = QCamera::NoLock;
+
+private:
+ BbCameraSession *m_session;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/qnx/camera/bbcameraexposurecontrol.cpp b/src/multimedia/platform/qnx/camera/bbcameraexposurecontrol.cpp
new file mode 100644
index 000000000..6dbde9556
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameraexposurecontrol.cpp
@@ -0,0 +1,287 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include "bbcamerasession_p.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::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);
+}
+
+QCameraExposure::FlashModes BbCameraExposureControl::flashMode() const
+{
+ return m_flashMode;
+}
+
+void BbCameraExposureControl::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 BbCameraExposureControl::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 BbCameraExposureControl::isFlashReady() const
+{
+ //TODO: check for flash charge-level here?!?
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/qnx/camera/bbcameraexposurecontrol_p.h b/src/multimedia/platform/qnx/camera/bbcameraexposurecontrol_p.h
new file mode 100644
index 000000000..56ea61b7f
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameraexposurecontrol_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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;
+
+ 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 statusChanged(QCamera::Status status);
+
+private:
+ BbCameraSession *m_session;
+ QCameraExposure::ExposureMode m_requestedExposureMode;
+
+ QCameraExposure::FlashModes m_flashMode = QCameraExposure::FlashAuto;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/qnx/camera/bbcamerafocuscontrol.cpp b/src/multimedia/platform/qnx/camera/bbcamerafocuscontrol.cpp
new file mode 100644
index 000000000..25f61190c
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcamerafocuscontrol.cpp
@@ -0,0 +1,428 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include "bbcamerasession_p.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))
+{
+ connect(m_session, SIGNAL(statusChanged(QCamera::Status)), this, SLOT(statusChanged(QCamera::Status)));
+}
+
+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;
+}
+
+
+qreal BbCameraFocusControl::maximumOpticalZoom() const
+{
+ //TODO: optical zoom support not available in BB10 API yet
+ return 1.0;
+}
+
+qreal BbCameraFocusControl::maximumDigitalZoom() const
+{
+ return m_maximumZoomFactor;
+}
+
+qreal BbCameraFocusControl::requestedOpticalZoom() const
+{
+ //TODO: optical zoom support not available in BB10 API yet
+ return 1.0;
+}
+
+qreal BbCameraFocusControl::requestedDigitalZoom() const
+{
+ return currentDigitalZoom();
+}
+
+qreal BbCameraFocusControl::currentOpticalZoom() const
+{
+ //TODO: optical zoom support not available in BB10 API yet
+ return 1.0;
+}
+
+qreal BbCameraFocusControl::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 BbCameraFocusControl::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 BbCameraFocusControl::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/multimedia/platform/qnx/camera/bbcamerafocuscontrol_p.h b/src/multimedia/platform/qnx/camera/bbcamerafocuscontrol_p.h
new file mode 100644
index 000000000..e91e032c5
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcamerafocuscontrol_p.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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;
+
+ 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:
+ void updateCustomFocusRegion();
+ bool retrieveViewfinderSize(int *width, int *height);
+
+ BbCameraSession *m_session;
+
+ QCameraFocus::FocusModes m_focusMode;
+ QCameraFocus::FocusPointMode m_focusPointMode;
+ QPointF m_customFocusPoint;
+
+ qreal m_minimumZoomFactor;
+ qreal m_maximumZoomFactor;
+ bool m_supportsSmoothZoom;
+ qreal m_requestedZoomFactor;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/qnx/camera/bbcameraimagecapturecontrol.cpp b/src/multimedia/platform/qnx/camera/bbcameraimagecapturecontrol.cpp
new file mode 100644
index 000000000..25f0468ef
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameraimagecapturecontrol.cpp
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include "bbcamerasession_p.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();
+}
+
+QCameraImageCapture::CaptureDestinations BbCameraImageCaptureControl::captureDestination() const
+{
+ return m_session->captureDestination();
+}
+
+void BbCameraImageCaptureControl::setCaptureDestination(QCameraImageCapture::CaptureDestinations destination)
+{
+ m_session->setCaptureDestination(destination);
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/qnx/camera/bbcameraimagecapturecontrol_p.h b/src/multimedia/platform/qnx/camera/bbcameraimagecapturecontrol_p.h
new file mode 100644
index 000000000..f36145cf6
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameraimagecapturecontrol_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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;
+
+ QCameraImageCapture::CaptureDestinations captureDestination() const override;
+ void setCaptureDestination(QCameraImageCapture::CaptureDestinations destination) override;
+
+private:
+ BbCameraSession *m_session;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/qnx/camera/bbcameraimageprocessingcontrol.cpp b/src/multimedia/platform/qnx/camera/bbcameraimageprocessingcontrol.cpp
new file mode 100644
index 000000000..a5787bd18
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameraimageprocessingcontrol.cpp
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include "bbcamerasession_p.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/multimedia/platform/qnx/camera/bbcameraimageprocessingcontrol_p.h b/src/multimedia/platform/qnx/camera/bbcameraimageprocessingcontrol_p.h
new file mode 100644
index 000000000..6a174ebad
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameraimageprocessingcontrol_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/qnx/camera/bbcameramediarecordercontrol.cpp b/src/multimedia/platform/qnx/camera/bbcameramediarecordercontrol.cpp
new file mode 100644
index 000000000..e127f5d70
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameramediarecordercontrol.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include "bbcamerasession_p.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/multimedia/platform/qnx/camera/bbcameramediarecordercontrol_p.h b/src/multimedia/platform/qnx/camera/bbcameramediarecordercontrol_p.h
new file mode 100644
index 000000000..f1c6a0873
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameramediarecordercontrol_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/qnx/camera/bbcameraorientationhandler.cpp b/src/multimedia/platform/qnx/camera/bbcameraorientationhandler.cpp
new file mode 100644
index 000000000..d600f3db0
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameraorientationhandler.cpp
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** 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/multimedia/platform/qnx/camera/bbcameraorientationhandler_p.h b/src/multimedia/platform/qnx/camera/bbcameraorientationhandler_p.h
new file mode 100644
index 000000000..ace5118ef
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameraorientationhandler_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** 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>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It 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 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/multimedia/platform/qnx/camera/bbcameraservice.cpp b/src/multimedia/platform/qnx/camera/bbcameraservice.cpp
new file mode 100644
index 000000000..860a7f5f4
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameraservice.cpp
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include "bbcameraaudioencodersettingscontrol_p.h"
+#include "bbcameracontrol_p.h"
+#include "bbcameraexposurecontrol_p.h"
+#include "bbcamerafocuscontrol_p.h"
+#include "bbcameraimagecapturecontrol_p.h"
+#include "bbcameraimageprocessingcontrol_p.h"
+#include "bbcameramediarecordercontrol_p.h"
+#include "bbcamerasession_p.h"
+#include "bbcameravideoencodersettingscontrol_p.h"
+#include "bbcameraviewfindersettingscontrol_p.h"
+#include "bbimageencodercontrol_p.h"
+#include "bbvideodeviceselectorcontrol_p.h"
+#include "bbvideorenderercontrol_p.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_cameraControl(new BbCameraControl(m_cameraSession, this))
+ , m_cameraExposureControl(new BbCameraExposureControl(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_cameraMediaRecorderControl(new BbCameraMediaRecorderControl(m_cameraSession, this))
+ , m_cameraVideoEncoderSettingsControl(new BbCameraVideoEncoderSettingsControl(m_cameraSession, this))
+ , m_cameraViewfinderSettingsControl(new BbCameraViewfinderSettingsControl(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, QCameraControl_iid) == 0)
+ return m_cameraControl;
+ else if (qstrcmp(name, QCameraExposureControl_iid) == 0)
+ return m_cameraExposureControl;
+ 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, 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, 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/multimedia/platform/qnx/camera/bbcameraservice_p.h b/src/multimedia/platform/qnx/camera/bbcameraservice_p.h
new file mode 100644
index 000000000..6f172d8f5
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameraservice_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <qmediaservice.h>
+
+QT_BEGIN_NAMESPACE
+
+class BbCameraAudioEncoderSettingsControl;
+class BbCameraControl;
+class BbCameraExposureControl;
+class BbCameraFocusControl;
+class BbCameraImageCaptureControl;
+class BbCameraImageProcessingControl;
+class BbCameraMediaRecorderControl;
+class BbCameraSession;
+class BbCameraVideoEncoderSettingsControl;
+class BbCameraViewfinderSettingsControl;
+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;
+ BbCameraControl* m_cameraControl;
+ BbCameraExposureControl* m_cameraExposureControl;
+ BbCameraFocusControl* m_cameraFocusControl;
+ BbCameraImageCaptureControl* m_cameraImageCaptureControl;
+ BbCameraImageProcessingControl* m_cameraImageProcessingControl;
+ BbCameraMediaRecorderControl* m_cameraMediaRecorderControl;
+ BbCameraVideoEncoderSettingsControl* m_cameraVideoEncoderSettingsControl;
+ BbCameraViewfinderSettingsControl* m_cameraViewfinderSettingsControl;
+ BbImageEncoderControl* m_imageEncoderControl;
+ BbVideoDeviceSelectorControl* m_videoDeviceSelectorControl;
+ BbVideoRendererControl* m_videoRendererControl;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/qnx/camera/bbcamerasession.cpp b/src/multimedia/platform/qnx/camera/bbcamerasession.cpp
new file mode 100644
index 000000000..866a9bb09
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcamerasession.cpp
@@ -0,0 +1,1154 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include "bbcameraorientationhandler_p.h"
+#include "bbcameraviewfindersettingscontrol_p.h"
+#include "windowgrabber_p.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/multimedia/platform/qnx/camera/bbcamerasession_p.h b/src/multimedia/platform/qnx/camera/bbcamerasession_p.h
new file mode 100644
index 000000000..e49951141
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcamerasession_p.h
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "bbmediastoragelocation_p.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/multimedia/platform/qnx/camera/bbcameravideoencodersettingscontrol.cpp b/src/multimedia/platform/qnx/camera/bbcameravideoencodersettingscontrol.cpp
new file mode 100644
index 000000000..d16d7a307
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameravideoencodersettingscontrol.cpp
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include "bbcamerasession_p.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/multimedia/platform/qnx/camera/bbcameravideoencodersettingscontrol_p.h b/src/multimedia/platform/qnx/camera/bbcameravideoencodersettingscontrol_p.h
new file mode 100644
index 000000000..893b26d5d
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameravideoencodersettingscontrol_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/qnx/camera/bbcameraviewfindersettingscontrol.cpp b/src/multimedia/platform/qnx/camera/bbcameraviewfindersettingscontrol.cpp
new file mode 100644
index 000000000..ada52ab6b
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameraviewfindersettingscontrol.cpp
@@ -0,0 +1,245 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include "bbcamerasession_p.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/multimedia/platform/qnx/camera/bbcameraviewfindersettingscontrol_p.h b/src/multimedia/platform/qnx/camera/bbcameraviewfindersettingscontrol_p.h
new file mode 100644
index 000000000..a41ff3001
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbcameraviewfindersettingscontrol_p.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/qnx/camera/bbimageencodercontrol.cpp b/src/multimedia/platform/qnx/camera/bbimageencodercontrol.cpp
new file mode 100644
index 000000000..960accb02
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbimageencodercontrol.cpp
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include "bbcamerasession_p.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/multimedia/platform/qnx/camera/bbimageencodercontrol_p.h b/src/multimedia/platform/qnx/camera/bbimageencodercontrol_p.h
new file mode 100644
index 000000000..2ace35b4f
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbimageencodercontrol_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/qnx/camera/bbmediastoragelocation.cpp b/src/multimedia/platform/qnx/camera/bbmediastoragelocation.cpp
new file mode 100644
index 000000000..3b8eac160
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbmediastoragelocation.cpp
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** 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_p.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/multimedia/platform/qnx/camera/bbmediastoragelocation_p.h b/src/multimedia/platform/qnx/camera/bbmediastoragelocation_p.h
new file mode 100644
index 000000000..3ea1bfbf0
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbmediastoragelocation_p.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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>
+#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/multimedia/platform/qnx/camera/bbvideodeviceselectorcontrol.cpp b/src/multimedia/platform/qnx/camera/bbvideodeviceselectorcontrol.cpp
new file mode 100644
index 000000000..61c6d87f8
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbvideodeviceselectorcontrol.cpp
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include "bbcamerasession_p.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);
+}
+
+QCamera::Position BbVideoDeviceSelectorControl::cameraPosition(int index) const
+{
+ if (deviceName(index) == QString::fromUtf8(BbCameraSession::cameraIdentifierFront()))
+ return QCamera::FrontFace;
+ else if (deviceName(index) == QString::fromUtf8(BbCameraSession::cameraIdentifierRear()))
+ return QCamera::BackFace;
+ else
+ return QCamera::UnspecifiedPosition;
+}
+
+int BbVideoDeviceSelectorControl::cameraOrientation(int index) const
+{
+ return 0;
+}
+
+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/multimedia/platform/qnx/camera/bbvideodeviceselectorcontrol_p.h b/src/multimedia/platform/qnx/camera/bbvideodeviceselectorcontrol_p.h
new file mode 100644
index 000000000..36e6b2515
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbvideodeviceselectorcontrol_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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;
+ QCamera::Position cameraPosition(int index) const;
+ int cameraOrientation(int index) const;
+
+ 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/multimedia/platform/qnx/camera/bbvideorenderercontrol.cpp b/src/multimedia/platform/qnx/camera/bbvideorenderercontrol.cpp
new file mode 100644
index 000000000..58739a1ed
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbvideorenderercontrol.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include "bbcamerasession_p.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/multimedia/platform/qnx/camera/bbvideorenderercontrol_p.h b/src/multimedia/platform/qnx/camera/bbvideorenderercontrol_p.h
new file mode 100644
index 000000000..95448077b
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/bbvideorenderercontrol_p.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/qnx/camera/camera.pri b/src/multimedia/platform/qnx/camera/camera.pri
new file mode 100644
index 000000000..02cee13d3
--- /dev/null
+++ b/src/multimedia/platform/qnx/camera/camera.pri
@@ -0,0 +1,40 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/bbcameraaudioencodersettingscontrol_p.h \
+ $$PWD/bbcameracontrol_p.h \
+ $$PWD/bbcameraexposurecontrol_p.h \
+ $$PWD/bbcamerafocuscontrol_p.h \
+ $$PWD/bbcameraimagecapturecontrol_p.h \
+ $$PWD/bbcameraimageprocessingcontrol_p.h \
+ $$PWD/bbcameramediarecordercontrol_p.h \
+ $$PWD/bbcameraorientatio_p.handler.h \
+ $$PWD/bbcameraservice_p.h \
+ $$PWD/bbcamerasession_p.h \
+ $$PWD/bbcameravideoencodersettingscontrol_p.h \
+ $$PWD/bbcameraviewfindersettingscontrol_p.h \
+ $$PWD/bbimageencodercontrol_p.h \
+ $$PWD/bbmediastoragelocation_p.h \
+ $$PWD/bbvideodeviceselectorcontrol_p.h \
+ $$PWD/bbvideorenderercontrol_p.h
+
+SOURCES += \
+ $$PWD/bbcameraaudioencodersettingscontrol.cpp \
+ $$PWD/bbcameracontrol.cpp \
+ $$PWD/bbcameraexposurecontrol.cpp \
+ $$PWD/bbcamerafocuscontrol.cpp \
+ $$PWD/bbcameraimagecapturecontrol.cpp \
+ $$PWD/bbcameraimageprocessingcontrol.cpp \
+ $$PWD/bbcameramediarecordercontrol.cpp \
+ $$PWD/bbcameraorientatio_p.handler.cpp \
+ $$PWD/bbcameraservice.cpp \
+ $$PWD/bbcamerasession.cpp \
+ $$PWD/bbcameravideoencodersettingscontrol.cpp \
+ $$PWD/bbcameraviewfindersettingscontrol.cpp \
+ $$PWD/bbimageencodercontrol.cpp \
+ $$PWD/bbmediastoragelocation.cpp \
+ $$PWD/bbvideodeviceselectorcontrol.cpp \
+ $$PWD/bbvideorenderercontrol.cpp
+
+LIBS += -lcamapi -laudio_manager
+
diff --git a/src/multimedia/platform/qnx/common/common.pri b/src/multimedia/platform/qnx/common/common.pri
new file mode 100644
index 000000000..dc37a291d
--- /dev/null
+++ b/src/multimedia/platform/qnx/common/common.pri
@@ -0,0 +1,7 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/windowgrabber_p.h
+
+SOURCES += \
+ $$PWD/windowgrabber.cpp
diff --git a/src/multimedia/platform/qnx/common/windowgrabber.cpp b/src/multimedia/platform/qnx/common/windowgrabber.cpp
new file mode 100644
index 000000000..e4c8c926d
--- /dev/null
+++ b/src/multimedia/platform/qnx/common/windowgrabber.cpp
@@ -0,0 +1,419 @@
+/****************************************************************************
+**
+** 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_p.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/multimedia/platform/qnx/common/windowgrabber_p.h b/src/multimedia/platform/qnx/common/windowgrabber_p.h
new file mode 100644
index 000000000..79a234b2d
--- /dev/null
+++ b/src/multimedia/platform/qnx/common/windowgrabber_p.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It 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 <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/multimedia/platform/qnx/mediaplayer/mediaplayer.pri b/src/multimedia/platform/qnx/mediaplayer/mediaplayer.pri
new file mode 100644
index 000000000..a44790b7d
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mediaplayer.pri
@@ -0,0 +1,24 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/mmrenderermediaplayercontrol_p.h \
+ $$PWD/mmrenderermediaplayerservice_p.h \
+ $$PWD/mmrenderermetadata_p.h \
+ $$PWD/mmrenderermetadatareadercontrol_p.h \
+ $$PWD/mmrendererplayervideorenderercontrol_p.h \
+ $$PWD/mmrendererutil_p.h \
+ $$PWD/mmrenderervideowindowcontrol_p.h \
+ $$PWD/mmreventmediaplayercontrol_p.h \
+ $$PWD/mmrevent_p.hread.h
+SOURCES += \
+ $$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/mmrevent_p.hread.cpp
+
+QMAKE_USE += mmrenderer
diff --git a/src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp b/src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp
new file mode 100644
index 000000000..c374d97b2
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp
@@ -0,0 +1,664 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "mmrenderercustomaudiorolecontrol_p.h"
+#include "mmrenderermediaplayercontrol_p.h"
+#include "mmrenderermetadatareadercontrol_p.h"
+#include "mmrendererplayervideorenderercontrol_p.h"
+#include "mmrendererutil_p.h"
+#include "mmrenderervideowindowcontrol_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 <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) {
+ QString audioType = m_role == QAudio::CustomRole
+ ? m_customRole
+ : qnxAudioType(m_role);
+ 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);
+ }
+}
+
+QUrl 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 QUrl &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);
+}
+
+void MmRendererMediaPlayerControl::setAudioRole(QAudio::Role role)
+{
+ m_role = role;
+ m_customRole.clear();
+}
+
+QList<QAudio::Role> MmRendererMediaPlayerControl::supportedAudioRoles() const
+{
+ return qnxSupportedAudioRoles();
+}
+
+void MmRendererMediaPlayerControl::setCustomAudioRole(const QString &role)
+{
+ m_role = QAudio::CustomRole;
+ m_customRole = role;
+}
+
+QStringList MmRendererMediaPlayerControl::supportedCustomAudioRoles() const
+{
+ return QStringList();
+}
+
+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::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/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayercontrol_p.h b/src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayercontrol_p.h
new file mode 100644
index 000000000..d6a75c597
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayercontrol_p.h
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "mmrenderermetadata_p.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;
+
+ 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 setAudioRole(QAudio::Role role) override;
+ QList<QAudio::Role> supportedAudioRoles() const override;
+
+ void setCustomAudioRole(const QString &role) override;
+ QStringList supportedCustomAudioRoles() const override;
+
+ MmRendererPlayerVideoRendererControl *videoRendererControl() const;
+ void setVideoRendererControl(MmRendererPlayerVideoRendererControl *videoControl);
+
+ MmRendererVideoWindowControl *videoWindowControl() const;
+ void setVideoWindowControl(MmRendererVideoWindowControl *videoControl);
+ void setMetaDataReaderControl(MmRendererMetaDataReaderControl *metaDataReaderControl);
+
+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);
+
+ QUrl 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;
+ 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/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayerservice.cpp b/src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayerservice.cpp
new file mode 100644
index 000000000..7ff9d9ea9
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayerservice.cpp
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include "mmrenderermediaplayercontrol_p.h"
+#include "mmrenderermetadatareadercontrol_p.h"
+#include "mmrendererplayervideorenderercontrol_p.h"
+#include "mmrendererutil_p.h"
+#include "mmrenderervideowindowcontrol_p.h"
+
+#include "mmreventmediaplayercontrol_p.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;
+}
+
+QObject *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, 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(QObject *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;
+ 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);
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayerservice_p.h b/src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayerservice_p.h
new file mode 100644
index 000000000..07c7c000d
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayerservice_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmediaservice.h>
+#include <QtCore/qpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class MmRendererMediaPlayerControl;
+class MmRendererMetaDataReaderControl;
+class MmRendererPlayerVideoRendererControl;
+class MmRendererVideoWindowControl;
+
+class MmRendererMediaPlayerService : public QMediaService
+{
+ Q_OBJECT
+public:
+ explicit MmRendererMediaPlayerService(QObject *parent = 0);
+ ~MmRendererMediaPlayerService();
+
+ QObject *requestControl(const char *name) override;
+ void releaseControl(QObject *control) override;
+
+private:
+ void updateControls();
+
+ QPointer<MmRendererPlayerVideoRendererControl> m_videoRendererControl;
+ QPointer<MmRendererVideoWindowControl> m_videoWindowControl;
+ QPointer<MmRendererMediaPlayerControl> m_mediaPlayerControl;
+ QPointer<MmRendererMetaDataReaderControl> m_metaDataReaderControl;
+
+ bool m_appHasDrmPermission : 1;
+ bool m_appHasDrmPermissionChecked : 1;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/qnx/mediaplayer/mmrenderermetadata.cpp b/src/multimedia/platform/qnx/mediaplayer/mmrenderermetadata.cpp
new file mode 100644
index 000000000..d369ea560
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mmrenderermetadata.cpp
@@ -0,0 +1,298 @@
+/****************************************************************************
+**
+** 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_p.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/multimedia/platform/qnx/mediaplayer/mmrenderermetadata_p.h b/src/multimedia/platform/qnx/mediaplayer/mmrenderermetadata_p.h
new file mode 100644
index 000000000..72b10a110
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mmrenderermetadata_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 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/multimedia/platform/qnx/mediaplayer/mmrenderermetadatareadercontrol.cpp b/src/multimedia/platform/qnx/mediaplayer/mmrenderermetadatareadercontrol.cpp
new file mode 100644
index 000000000..76ad98cb3
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mmrenderermetadatareadercontrol.cpp
@@ -0,0 +1,170 @@
+/****************************************************************************
+**
+** 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_p.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/multimedia/platform/qnx/mediaplayer/mmrenderermetadatareadercontrol_p.h b/src/multimedia/platform/qnx/mediaplayer/mmrenderermetadatareadercontrol_p.h
new file mode 100644
index 000000000..f540f3f93
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mmrenderermetadatareadercontrol_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "mmrenderermetadata_p.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/multimedia/platform/qnx/mediaplayer/mmrendererplayervideorenderercontrol.cpp b/src/multimedia/platform/qnx/mediaplayer/mmrendererplayervideorenderercontrol.cpp
new file mode 100644
index 000000000..d682c40a3
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mmrendererplayervideorenderercontrol.cpp
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** 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_p.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() {}
+ MapData map(MapMode mode) override { return {}; }
+ 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/multimedia/platform/qnx/mediaplayer/mmrendererplayervideorenderercontrol_p.h b/src/multimedia/platform/qnx/mediaplayer/mmrendererplayervideorenderercontrol_p.h
new file mode 100644
index 000000000..98a5304f3
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mmrendererplayervideorenderercontrol_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/qnx/mediaplayer/mmrendererutil.cpp b/src/multimedia/platform/qnx/mediaplayer/mmrendererutil.cpp
new file mode 100644
index 000000000..af19f7368
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mmrendererutil.cpp
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** 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_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>
+
+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/multimedia/platform/qnx/mediaplayer/mmrendererutil_p.h b/src/multimedia/platform/qnx/mediaplayer/mmrendererutil_p.h
new file mode 100644
index 000000000..9bba99487
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mmrendererutil_p.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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();
+
+QString qnxAudioType(QAudio::Role role);
+QList<QAudio::Role> qnxSupportedAudioRoles();
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/qnx/mediaplayer/mmrenderervideowindowcontrol.cpp b/src/multimedia/platform/qnx/mediaplayer/mmrenderervideowindowcontrol.cpp
new file mode 100644
index 000000000..0bc73b621
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mmrenderervideowindowcontrol.cpp
@@ -0,0 +1,413 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "mmrendererutil_p.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/multimedia/platform/qnx/mediaplayer/mmrenderervideowindowcontrol_p.h b/src/multimedia/platform/qnx/mediaplayer/mmrenderervideowindowcontrol_p.h
new file mode 100644
index 000000000..d318d99a1
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mmrenderervideowindowcontrol_p.h
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "mmrenderermetadata_p.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/multimedia/platform/qnx/mediaplayer/mmreventmediaplayercontrol.cpp b/src/multimedia/platform/qnx/mediaplayer/mmreventmediaplayercontrol.cpp
new file mode 100644
index 000000000..f2e2b999b
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mmreventmediaplayercontrol.cpp
@@ -0,0 +1,229 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "mmreventthread_p.h"
+#include "mmrenderervideowindowcontrol_p.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/multimedia/platform/qnx/mediaplayer/mmreventmediaplayercontrol_p.h b/src/multimedia/platform/qnx/mediaplayer/mmreventmediaplayercontrol_p.h
new file mode 100644
index 000000000..f2cb41561
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mmreventmediaplayercontrol_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "mmrenderermediaplayercontrol_p.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/multimedia/platform/qnx/mediaplayer/mmreventthread.cpp b/src/multimedia/platform/qnx/mediaplayer/mmreventthread.cpp
new file mode 100644
index 000000000..25f26e216
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mmreventthread.cpp
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** 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/multimedia/platform/qnx/mediaplayer/mmreventthread_p.h b/src/multimedia/platform/qnx/mediaplayer/mmreventthread_p.h
new file mode 100644
index 000000000..946548686
--- /dev/null
+++ b/src/multimedia/platform/qnx/mediaplayer/mmreventthread_p.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 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/multimedia/platform/qnx/neutrinoserviceplugin.cpp b/src/multimedia/platform/qnx/neutrinoserviceplugin.cpp
new file mode 100644
index 000000000..25a26d28f
--- /dev/null
+++ b/src/multimedia/platform/qnx/neutrinoserviceplugin.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** 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;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/qnx/neutrinoserviceplugin_p.h b/src/multimedia/platform/qnx/neutrinoserviceplugin_p.h
new file mode 100644
index 000000000..7fa4ac5fb
--- /dev/null
+++ b/src/multimedia/platform/qnx/neutrinoserviceplugin_p.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmediaserviceproviderplugin.h>
+
+QT_BEGIN_NAMESPACE
+
+class NeutrinoServicePlugin
+ : public QMediaServiceProviderPlugin
+{
+ Q_OBJECT
+ 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;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/platform/qnx/qnx.pri b/src/multimedia/platform/qnx/qnx.pri
index fa7146940..4fe13620c 100644
--- a/src/multimedia/platform/qnx/qnx.pri
+++ b/src/multimedia/platform/qnx/qnx.pri
@@ -1,13 +1,19 @@
LIBS += -lasound
-HEADERS += platform/qnx/qnxaudiointerface_p.h \
- platform/qnx/qnxaudiodeviceinfo_p.h \
- platform/qnx/qnxaudioinput_p.h \
- platform/qnx/qnxaudiooutput_p.h \
- platform/qnx/qnxaudioutils_p.h
+HEADERS += $$PWD/qnxaudiointerface_p.h \
+ $$PWD/qnxaudiodeviceinfo_p.h \
+ $$PWD/qnxaudioinput_p.h \
+ $$PWD/qnxaudiooutput_p.h \
+ $$PWD/qnxaudioutils_p.h \
+ $$PWD/neutrinoserviceplugin_p.h
-SOURCES += platform/qnx/qnxaudiointerface.cpp \
- platform/qnx/qnxaudiodeviceinfo.cpp \
- platform/qnx/qnxaudioinput.cpp \
- platform/qnx/qnxaudiooutput.cpp \
- platform/qnx/qnxaudioutils.cpp
+SOURCES += $$PWD/qnxaudiointerface.cpp \
+ $$PWD/qnxaudiodeviceinfo.cpp \
+ $$PWD/qnxaudioinput.cpp \
+ $$PWD/qnxaudiooutput.cpp \
+ $$PWD/qnxaudioutils.cpp \
+ $$PWD/neutrinoserviceplugin.cpp
+
+include(common/common.pri)
+include(mediaplayer/mediaplayer.pri)
+include(camera/camera.pri)
diff --git a/src/multimedia/platform/qnx/qnxaudiodeviceinfo_p.h b/src/multimedia/platform/qnx/qnxaudiodeviceinfo_p.h
index 6f09c9d10..b072af1a2 100644
--- a/src/multimedia/platform/qnx/qnxaudiodeviceinfo_p.h
+++ b/src/multimedia/platform/qnx/qnxaudiodeviceinfo_p.h
@@ -40,6 +40,17 @@
#ifndef QNXAUDIODEVICEINFO_H
#define QNXAUDIODEVICEINFO_H
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
#include "qaudiosystem_p.h"
QT_BEGIN_NAMESPACE
diff --git a/src/multimedia/platform/qnx/qnxaudioinput_p.h b/src/multimedia/platform/qnx/qnxaudioinput_p.h
index d0e3ca30b..59087e37b 100644
--- a/src/multimedia/platform/qnx/qnxaudioinput_p.h
+++ b/src/multimedia/platform/qnx/qnxaudioinput_p.h
@@ -40,6 +40,17 @@
#ifndef QNXAUDIOINPUT_H
#define QNXAUDIOINPUT_H
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
#include "qaudiosystem_p.h"
#include <QSocketNotifier>
diff --git a/src/multimedia/platform/qnx/qnxaudiointerface_p.h b/src/multimedia/platform/qnx/qnxaudiointerface_p.h
index 8fc50af5f..259bf94a9 100644
--- a/src/multimedia/platform/qnx/qnxaudiointerface_p.h
+++ b/src/multimedia/platform/qnx/qnxaudiointerface_p.h
@@ -40,6 +40,17 @@
#ifndef QNXAUDIOPLUGIN_H
#define QNXAUDIOPLUGIN_H
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/qaudiosystem_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/multimedia/platform/qnx/qnxaudiooutput_p.h b/src/multimedia/platform/qnx/qnxaudiooutput_p.h
index 4fff7ad38..55b5e7be5 100644
--- a/src/multimedia/platform/qnx/qnxaudiooutput_p.h
+++ b/src/multimedia/platform/qnx/qnxaudiooutput_p.h
@@ -40,6 +40,17 @@
#ifndef QNXAUDIOOUTPUT_H
#define QNXAUDIOOUTPUT_H
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
#include "qaudiosystem_p.h"
#include <QElapsedTimer>
diff --git a/src/multimedia/platform/qnx/qnxaudioutils_p.h b/src/multimedia/platform/qnx/qnxaudioutils_p.h
index b997e0c45..0696d986e 100644
--- a/src/multimedia/platform/qnx/qnxaudioutils_p.h
+++ b/src/multimedia/platform/qnx/qnxaudioutils_p.h
@@ -40,6 +40,17 @@
#ifndef QNXAUDIOUTILS_H
#define QNXAUDIOUTILS_H
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
#include "qaudiosystem_p.h"
#include <sys/asoundlib.h>
diff --git a/src/multimedia/platform/wasapi/wasapi.pri b/src/multimedia/platform/wasapi/wasapi.pri
index 5b7336b1b..eb60edcbb 100644
--- a/src/multimedia/platform/wasapi/wasapi.pri
+++ b/src/multimedia/platform/wasapi/wasapi.pri
@@ -2,16 +2,16 @@
LIBS += -lstrmiids -lole32 -loleaut32 -lwinmm
HEADERS += \
- platform/wasapi/qwindowsaudiointerface_p.h \
- platform/wasapi/qwindowsaudiodeviceinfo_p.h \
- platform/wasapi/qwindowsaudioinput_p.h \
- platform/wasapi/qwindowsaudiooutput_p.h \
- platform/wasapi/qwindowsaudioutils_p.h
+ $$PWD/qwindowsaudiointerface_p.h \
+ $$PWD/qwindowsaudiodeviceinfo_p.h \
+ $$PWD/qwindowsaudioinput_p.h \
+ $$PWD/qwindowsaudiooutput_p.h \
+ $$PWD/qwindowsaudioutils_p.h
SOURCES += \
- platform/wasapi/qwindowsaudiointerface.cpp \
- platform/wasapi/qwindowsaudiodeviceinfo.cpp \
- platform/wasapi/qwindowsaudioinput.cpp \
- platform/wasapi/qwindowsaudiooutput.cpp \
- platform/wasapi/qwindowsaudioutils.cpp
+ $$PWD/qwindowsaudiointerface.cpp \
+ $$PWD/qwindowsaudiodeviceinfo.cpp \
+ $$PWD/qwindowsaudioinput.cpp \
+ $$PWD/qwindowsaudiooutput.cpp \
+ $$PWD/qwindowsaudioutils.cpp
diff --git a/src/multimedia/platform/wmf/decoder/decoder.pri b/src/multimedia/platform/wmf/decoder/decoder.pri
new file mode 100644
index 000000000..93cc683d4
--- /dev/null
+++ b/src/multimedia/platform/wmf/decoder/decoder.pri
@@ -0,0 +1,12 @@
+INCLUDEPATH += $$PWD
+
+LIBS += -lmfreadwrite -lwmcodecdspuuid
+QMAKE_USE += wmf
+
+HEADERS += \
+ $$PWD/mfdecodersourcereader_p.h \
+ $$PWD/mfaudiodecodercontrol_p.h
+
+SOURCES += \
+ $$PWD/mfdecodersourcereader.cpp \
+ $$PWD/mfaudiodecodercontrol.cpp
diff --git a/src/multimedia/platform/wmf/decoder/mfaudiodecodercontrol.cpp b/src/multimedia/platform/wmf/decoder/mfaudiodecodercontrol.cpp
new file mode 100644
index 000000000..f41e05ed9
--- /dev/null
+++ b/src/multimedia/platform/wmf/decoder/mfaudiodecodercontrol.cpp
@@ -0,0 +1,486 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.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/x-raw");
+ 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/x-raw")) {
+ 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/multimedia/platform/wmf/decoder/mfaudiodecodercontrol_p.h b/src/multimedia/platform/wmf/decoder/mfaudiodecodercontrol_p.h
new file mode 100644
index 000000000..98a3b9d02
--- /dev/null
+++ b/src/multimedia/platform/wmf/decoder/mfaudiodecodercontrol_p.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qaudiodecodercontrol.h"
+#include "mfdecodersourcereader_p.h"
+#include "private/sourceresolver_p.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/multimedia/platform/wmf/decoder/mfdecodersourcereader.cpp b/src/multimedia/platform/wmf/decoder/mfdecodersourcereader.cpp
new file mode 100644
index 000000000..b2b9cf60d
--- /dev/null
+++ b/src/multimedia/platform/wmf/decoder/mfdecodersourcereader.cpp
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.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/multimedia/platform/wmf/decoder/mfdecodersourcereader_p.h b/src/multimedia/platform/wmf/decoder/mfdecodersourcereader_p.h
new file mode 100644
index 000000000..7d63f5368
--- /dev/null
+++ b/src/multimedia/platform/wmf/decoder/mfdecodersourcereader_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <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/multimedia/platform/wmf/evr/evr.pri b/src/multimedia/platform/wmf/evr/evr.pri
new file mode 100644
index 000000000..6168063e9
--- /dev/null
+++ b/src/multimedia/platform/wmf/evr/evr.pri
@@ -0,0 +1,20 @@
+INCLUDEPATH += $$PWD
+
+qtHaveModule(widgets): QT += widgets
+QT += gui-private
+
+LIBS += -lmf -lmfplat -lmfuuid -ld3d9 -ldxva2 -lwinmm -levr
+
+HEADERS += \
+ $$PWD/evrvideowindowcontrol_p.h \
+ $$PWD/evrcustompresenter_p.h \
+ $$PWD/evrd3dpresentengine_p.h \
+ $$PWD/evrhelpers_p.h \
+ $$PWD/evrdefs_p.h
+
+SOURCES += \
+ $$PWD/evrvideowindowcontrol.cpp \
+ $$PWD/evrcustompresenter.cpp \
+ $$PWD/evrd3dpresentengine.cpp \
+ $$PWD/evrhelpers.cpp \
+ $$PWD/evrdefs.cpp
diff --git a/src/multimedia/platform/wmf/evr/evrcustompresenter.cpp b/src/multimedia/platform/wmf/evr/evrcustompresenter.cpp
new file mode 100644
index 000000000..dd6c0021b
--- /dev/null
+++ b/src/multimedia/platform/wmf/evr/evrcustompresenter.cpp
@@ -0,0 +1,2062 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "evrd3dpresentengine_p.h"
+#include "evrhelpers_p.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/multimedia/platform/wmf/evr/evrcustompresenter_p.h b/src/multimedia/platform/wmf/evr/evrcustompresenter_p.h
new file mode 100644
index 000000000..d60bc4d4c
--- /dev/null
+++ b/src/multimedia/platform/wmf/evr/evrcustompresenter_p.h
@@ -0,0 +1,388 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <qvideosurfaceformat.h>
+
+#include "evrdefs_p.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/multimedia/platform/wmf/evr/evrd3dpresentengine.cpp b/src/multimedia/platform/wmf/evr/evrd3dpresentengine.cpp
new file mode 100644
index 000000000..8cb6d2593
--- /dev/null
+++ b/src/multimedia/platform/wmf/evr/evrd3dpresentengine.cpp
@@ -0,0 +1,412 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+#include "evrhelpers_p.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; }
+ MapData map(MapMode mode) override;
+ void unmap() override;
+
+private:
+ mutable D3DPresentEngine *m_engine;
+ IMFSample *m_sample;
+ IDirect3DSurface9 *m_surface;
+ MapMode m_mapMode;
+ mutable unsigned int m_textureId = 0;
+};
+
+IMFSampleVideoBuffer::MapData IMFSampleVideoBuffer::map(MapMode mode)
+{
+ if (!m_surface || m_mapMode != NotMapped)
+ return {};
+
+ D3DSURFACE_DESC desc;
+ if (FAILED(m_surface->GetDesc(&desc)))
+ return {};
+
+ D3DLOCKED_RECT rect;
+ if (FAILED(m_surface->LockRect(&rect, NULL, mode == ReadOnly ? D3DLOCK_READONLY : 0)))
+ return {};
+
+ m_mapMode = mode;
+
+ MapData mapData;
+ mapData.nBytes = (int)(rect.Pitch * desc.Height);
+ mapData.nPlanes = 1;
+ mapData.bytesPerLine[0] = (int)rect.Pitch;
+ mapData.data[0] = reinterpret_cast<uchar *>(rect.pBits);
+ return mapData;
+}
+
+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/multimedia/platform/wmf/evr/evrd3dpresentengine_p.h b/src/multimedia/platform/wmf/evr/evrd3dpresentengine_p.h
new file mode 100644
index 000000000..1e70feadb
--- /dev/null
+++ b/src/multimedia/platform/wmf/evr/evrd3dpresentengine_p.h
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <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/multimedia/platform/wmf/evr/evrdefs.cpp b/src/multimedia/platform/wmf/evr/evrdefs.cpp
new file mode 100644
index 000000000..94370a14a
--- /dev/null
+++ b/src/multimedia/platform/wmf/evr/evrdefs.cpp
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.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/multimedia/platform/wmf/evr/evrdefs_p.h b/src/multimedia/platform/wmf/evr/evrdefs_p.h
new file mode 100644
index 000000000..f9df48387
--- /dev/null
+++ b/src/multimedia/platform/wmf/evr/evrdefs_p.h
@@ -0,0 +1,364 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 <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/multimedia/platform/wmf/evr/evrhelpers.cpp b/src/multimedia/platform/wmf/evr/evrhelpers.cpp
new file mode 100644
index 000000000..aa2311f46
--- /dev/null
+++ b/src/multimedia/platform/wmf/evr/evrhelpers.cpp
@@ -0,0 +1,186 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_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;
+}
+
+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/multimedia/platform/wmf/evr/evrhelpers_p.h b/src/multimedia/platform/wmf/evr/evrhelpers_p.h
new file mode 100644
index 000000000..89bff6288
--- /dev/null
+++ b/src/multimedia/platform/wmf/evr/evrhelpers_p.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "evrdefs_p.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/multimedia/platform/wmf/evr/evrvideowindowcontrol.cpp b/src/multimedia/platform/wmf/evr/evrvideowindowcontrol.cpp
new file mode 100644
index 000000000..523dddca8
--- /dev/null
+++ b/src/multimedia/platform/wmf/evr/evrvideowindowcontrol.cpp
@@ -0,0 +1,350 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+
+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;
+
+ 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/multimedia/platform/wmf/evr/evrvideowindowcontrol_p.h b/src/multimedia/platform/wmf/evr/evrvideowindowcontrol_p.h
new file mode 100644
index 000000000..059376f7e
--- /dev/null
+++ b/src/multimedia/platform/wmf/evr/evrvideowindowcontrol_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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.h"
+
+#include "evrdefs_p.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/multimedia/platform/wmf/mfstream.cpp b/src/multimedia/platform/wmf/mfstream.cpp
new file mode 100644
index 000000000..b01dbb7b1
--- /dev/null
+++ b/src/multimedia/platform/wmf/mfstream.cpp
@@ -0,0 +1,361 @@
+/****************************************************************************
+**
+** 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_p.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/multimedia/platform/wmf/mfstream_p.h b/src/multimedia/platform/wmf/mfstream_p.h
new file mode 100644
index 000000000..975d02c9d
--- /dev/null
+++ b/src/multimedia/platform/wmf/mfstream_p.h
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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>
+
+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/multimedia/platform/wmf/player/mfactivate.cpp b/src/multimedia/platform/wmf/player/mfactivate.cpp
new file mode 100644
index 000000000..05d9321be
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfactivate.cpp
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.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/multimedia/platform/wmf/player/mfactivate_p.h b/src/multimedia/platform/wmf/player/mfactivate_p.h
new file mode 100644
index 000000000..86ef1c438
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfactivate_p.h
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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>
+
+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/multimedia/platform/wmf/player/mfaudioendpointcontrol.cpp b/src/multimedia/platform/wmf/player/mfaudioendpointcontrol.cpp
new file mode 100644
index 000000000..5e1f130cf
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfaudioendpointcontrol.cpp
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** 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_p.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/multimedia/platform/wmf/player/mfaudioendpointcontrol_p.h b/src/multimedia/platform/wmf/player/mfaudioendpointcontrol_p.h
new file mode 100644
index 000000000..21d404104
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfaudioendpointcontrol_p.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 "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/multimedia/platform/wmf/player/mfaudioprobecontrol.cpp b/src/multimedia/platform/wmf/player/mfaudioprobecontrol.cpp
new file mode 100644
index 000000000..a371629da
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfaudioprobecontrol.cpp
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.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/multimedia/platform/wmf/player/mfaudioprobecontrol_p.h b/src/multimedia/platform/wmf/player/mfaudioprobecontrol_p.h
new file mode 100644
index 000000000..0ccf151ad
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfaudioprobecontrol_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MFAUDIOPROBECONTROL_H
+#define MFAUDIOPROBECONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/wmf/player/mfevrvideowindowcontrol.cpp b/src/multimedia/platform/wmf/player/mfevrvideowindowcontrol.cpp
new file mode 100644
index 000000000..24c176c24
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfevrvideowindowcontrol.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.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/multimedia/platform/wmf/player/mfevrvideowindowcontrol_p.h b/src/multimedia/platform/wmf/player/mfevrvideowindowcontrol_p.h
new file mode 100644
index 000000000..c74148431
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfevrvideowindowcontrol_p.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/evrvideowindowcontrol_p.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/multimedia/platform/wmf/player/mfmetadatacontrol.cpp b/src/multimedia/platform/wmf/player/mfmetadatacontrol.cpp
new file mode 100644
index 000000000..b7cf771e8
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfmetadatacontrol.cpp
@@ -0,0 +1,431 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "mfplayerservice_p.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/multimedia/platform/wmf/player/mfmetadatacontrol_p.h b/src/multimedia/platform/wmf/player/mfmetadatacontrol_p.h
new file mode 100644
index 000000000..dcce5bb1d
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfmetadatacontrol_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/wmf/player/mfplayercontrol.cpp b/src/multimedia/platform/wmf/player/mfplayercontrol.cpp
new file mode 100644
index 000000000..3bb963417
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfplayercontrol.cpp
@@ -0,0 +1,316 @@
+/****************************************************************************
+**
+** 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_p.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 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(-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);
+}
+
+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();
+ emit error(int(errorCode), errorString);
+}
diff --git a/src/multimedia/platform/wmf/player/mfplayercontrol_p.h b/src/multimedia/platform/wmf/player/mfplayercontrol_p.h
new file mode 100644
index 000000000..35695e0db
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfplayercontrol_p.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 "qmediaplayercontrol.h"
+
+#include <QtCore/qcoreevent.h>
+
+#include "mfplayersession_p.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);
+
+ QUrl media() const;
+ const QIODevice *mediaStream() const;
+ void setMedia(const QUrl &media, QIODevice *stream);
+
+ void play();
+ void pause();
+ void stop();
+
+ bool streamPlaybackSupported() const { return true; }
+
+
+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;
+ QUrl m_media;
+ MFPlayerSession *m_session;
+};
+
+#endif
diff --git a/src/multimedia/platform/wmf/player/mfplayerservice.cpp b/src/multimedia/platform/wmf/player/mfplayerservice.cpp
new file mode 100644
index 000000000..1fce4f6c3
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfplayerservice.cpp
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** 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 "QUrl.h"
+
+#include <QtCore/qdebug.h>
+
+#include "mfplayercontrol_p.h"
+#include "mfevrvideowindowcontrol_p.h"
+#include "mfvideorenderercontrol_p.h"
+#include "mfaudioendpointcontrol_p.h"
+#include "mfaudioprobecontrol_p.h"
+#include "mfvideoprobecontrol_p.h"
+#include "mfplayerservice_p.h"
+#include "mfplayersession_p.h"
+#include "mfmetadatacontrol_p.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();
+}
+
+QObject *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(QObject *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/multimedia/platform/wmf/player/mfplayerservice_p.h b/src/multimedia/platform/wmf/player/mfplayerservice_p.h
new file mode 100644
index 000000000..50362c381
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfplayerservice_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 "qmediaservice.h"
+#include "qmediatimerange.h"
+
+QT_BEGIN_NAMESPACE
+class QUrl;
+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();
+
+ QObject *requestControl(const char *name);
+ void releaseControl(QObject *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/multimedia/platform/wmf/player/mfplayersession.cpp b/src/multimedia/platform/wmf/player/mfplayersession.cpp
new file mode 100644
index 000000000..0d1213c84
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfplayersession.cpp
@@ -0,0 +1,1816 @@
+/****************************************************************************
+**
+** 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 "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_p.h"
+#include "mfevrvideowindowcontrol_p.h"
+#include "mfvideorenderercontrol_p.h"
+#include "mfaudioendpointcontrol_p.h"
+
+#include "mfplayersession_p.h"
+#include "mfplayerservice_p.h"
+#include "mfmetadatacontrol_p.h"
+#include <mferror.h>
+#include <nserror.h>
+#include "private/sourceresolver_p.h"
+#include "samplegrabber_p.h"
+#include "mftvideo_p.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 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) {
+ 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/x-raw");
+ 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/multimedia/platform/wmf/player/mfplayersession_p.h b/src/multimedia/platform/wmf/player/mfplayersession_p.h
new file mode 100644
index 000000000..92f645017
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfplayersession_p.h
@@ -0,0 +1,250 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 "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 QUrl;
+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 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);
+ 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/multimedia/platform/wmf/player/mftvideo.cpp b/src/multimedia/platform/wmf/player/mftvideo.cpp
new file mode 100644
index 000000000..b2ff27f80
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mftvideo.cpp
@@ -0,0 +1,753 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mftvideo_p.h"
+#include "mfvideoprobecontrol_p.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/multimedia/platform/wmf/player/mftvideo_p.h b/src/multimedia/platform/wmf/player/mftvideo_p.h
new file mode 100644
index 000000000..e08f0977f
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mftvideo_p.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MFTRANSFORM_H
+#define MFTRANSFORM_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <mfapi.h>
+#include <mfidl.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmutex.h>
+#include <QtMultimedia/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/multimedia/platform/wmf/player/mfvideoprobecontrol.cpp b/src/multimedia/platform/wmf/player/mfvideoprobecontrol.cpp
new file mode 100644
index 000000000..e4d1a8b23
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfvideoprobecontrol.cpp
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mfvideoprobecontrol_p.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/multimedia/platform/wmf/player/mfvideoprobecontrol_p.h b/src/multimedia/platform/wmf/player/mfvideoprobecontrol_p.h
new file mode 100644
index 000000000..a7f79ef92
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfvideoprobecontrol_p.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/wmf/player/mfvideorenderercontrol.cpp b/src/multimedia/platform/wmf/player/mfvideorenderercontrol.cpp
new file mode 100644
index 000000000..2acde7e4c
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfvideorenderercontrol.cpp
@@ -0,0 +1,2424 @@
+/****************************************************************************
+**
+** 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_p.h"
+#include "mfactivate_p.h"
+
+#include "evrcustompresenter_p.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();
+ }
+
+ MapData map(MapMode mode) override
+ {
+ MapData mapData;
+ if (m_mapMode == NotMapped && mode != NotMapped) {
+ BYTE *bytes;
+ DWORD length;
+ HRESULT hr = m_buffer->Lock(&bytes, NULL, &length);
+ if (SUCCEEDED(hr)) {
+ mapData.nBytes = qsizetype(length);
+ mapData.nPlanes = 1;
+ mapData.bytesPerLine[0] = m_bytesPerLine;
+ mapData.data[0] = reinterpret_cast<uchar *>(bytes);
+ m_mapMode = mode;
+ } else {
+ qWarning("Faild to lock mf buffer!");
+ }
+ }
+ return mapData;
+ }
+
+ void unmap() override
+ {
+ if (m_mapMode == NotMapped)
+ return;
+ m_mapMode = NotMapped;
+ m_buffer->Unlock();
+ }
+
+ MapMode mapMode() const override
+ {
+ 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_p.cpp"
+#include "mfvideorenderercontrol.moc"
diff --git a/src/multimedia/platform/wmf/player/mfvideorenderercontrol_p.h b/src/multimedia/platform/wmf/player/mfvideorenderercontrol_p.h
new file mode 100644
index 000000000..dd8a6a278
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/mfvideorenderercontrol_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#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/multimedia/platform/wmf/player/player.pri b/src/multimedia/platform/wmf/player/player.pri
new file mode 100644
index 000000000..3263b84ba
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/player.pri
@@ -0,0 +1,32 @@
+INCLUDEPATH += $$PWD
+
+LIBS += -lgdi32 -luser32
+QMAKE_USE += wmf
+
+HEADERS += \
+ $$PWD/mfplayerservice_p.h \
+ $$PWD/mfplayersession_p.h \
+ $$PWD/mfplayercontrol_p.h \
+ $$PWD/mfvideorenderercontrol_p.h \
+ $$PWD/mfaudioendpointcontrol_p.h \
+ $$PWD/mfmetadatacontrol_p.h \
+ $$PWD/mfaudioprobecontrol_p.h \
+ $$PWD/mfvideoprobecontrol_p.h \
+ $$PWD/mfevrvideowindowcontrol_p.h \
+ $$PWD/samplegrabber_p.h \
+ $$PWD/mftvideo_p.h \
+ $$PWD/mfactivate_p.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
diff --git a/src/multimedia/platform/wmf/player/samplegrabber.cpp b/src/multimedia/platform/wmf/player/samplegrabber.cpp
new file mode 100644
index 000000000..6c14dc152
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/samplegrabber.cpp
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "samplegrabber_p.h"
+#include "mfaudioprobecontrol_p.h"
+
+STDMETHODIMP SampleGrabberCallback::QueryInterface(REFIID riid, void** ppv)
+{
+ if (!ppv)
+ return E_POINTER;
+ if (riid == IID_IMFSampleGrabberSinkCallback) {
+ *ppv = static_cast<IMFSampleGrabberSinkCallback*>(this);
+ } else if (riid == IID_IMFClockStateSink) {
+ *ppv = static_cast<IMFClockStateSink*>(this);
+ } else if (riid == IID_IUnknown) {
+ *ppv = static_cast<IUnknown*>(this);
+ } else {
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+STDMETHODIMP_(ULONG) SampleGrabberCallback::AddRef()
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+STDMETHODIMP_(ULONG) SampleGrabberCallback::Release()
+{
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0) {
+ delete this;
+ }
+ return cRef;
+
+}
+
+// IMFClockStateSink methods.
+
+STDMETHODIMP SampleGrabberCallback::OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset)
+{
+ Q_UNUSED(hnsSystemTime);
+ Q_UNUSED(llClockStartOffset);
+ return S_OK;
+}
+
+STDMETHODIMP SampleGrabberCallback::OnClockStop(MFTIME hnsSystemTime)
+{
+ Q_UNUSED(hnsSystemTime);
+ return S_OK;
+}
+
+STDMETHODIMP SampleGrabberCallback::OnClockPause(MFTIME hnsSystemTime)
+{
+ Q_UNUSED(hnsSystemTime);
+ return S_OK;
+}
+
+STDMETHODIMP SampleGrabberCallback::OnClockRestart(MFTIME hnsSystemTime)
+{
+ Q_UNUSED(hnsSystemTime);
+ return S_OK;
+}
+
+STDMETHODIMP SampleGrabberCallback::OnClockSetRate(MFTIME hnsSystemTime, float flRate)
+{
+ Q_UNUSED(hnsSystemTime);
+ Q_UNUSED(flRate);
+ return S_OK;
+}
+
+// IMFSampleGrabberSink methods.
+
+STDMETHODIMP SampleGrabberCallback::OnSetPresentationClock(IMFPresentationClock* pClock)
+{
+ Q_UNUSED(pClock);
+ return S_OK;
+}
+
+STDMETHODIMP SampleGrabberCallback::OnShutdown()
+{
+ return S_OK;
+}
+
+void AudioSampleGrabberCallback::addProbe(MFAudioProbeControl* probe)
+{
+ QMutexLocker locker(&m_audioProbeMutex);
+
+ if (m_audioProbes.contains(probe))
+ return;
+
+ m_audioProbes.append(probe);
+}
+
+void AudioSampleGrabberCallback::removeProbe(MFAudioProbeControl* probe)
+{
+ QMutexLocker locker(&m_audioProbeMutex);
+ m_audioProbes.removeOne(probe);
+}
+
+void AudioSampleGrabberCallback::setFormat(const QAudioFormat& format)
+{
+ m_format = format;
+}
+
+STDMETHODIMP AudioSampleGrabberCallback::OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags,
+ LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer,
+ DWORD dwSampleSize)
+{
+ Q_UNUSED(dwSampleFlags);
+ Q_UNUSED(llSampleTime);
+ Q_UNUSED(llSampleDuration);
+
+ 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/multimedia/platform/wmf/player/samplegrabber_p.h b/src/multimedia/platform/wmf/player/samplegrabber_p.h
new file mode 100644
index 000000000..74eb62065
--- /dev/null
+++ b/src/multimedia/platform/wmf/player/samplegrabber_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SAMPLEGRABBER_H
+#define SAMPLEGRABBER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qmutex.h>
+#include <QtCore/qlist.h>
+#include <QtMultimedia/qaudioformat.h>
+#include <mfapi.h>
+#include <mfidl.h>
+
+class 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/multimedia/platform/wmf/sourceresolver.cpp b/src/multimedia/platform/wmf/sourceresolver.cpp
new file mode 100644
index 000000000..93af15a74
--- /dev/null
+++ b/src/multimedia/platform/wmf/sourceresolver.cpp
@@ -0,0 +1,325 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+#include "sourceresolver_p.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/multimedia/platform/wmf/sourceresolver_p.h b/src/multimedia/platform/wmf/sourceresolver_p.h
new file mode 100644
index 000000000..07da1d8bd
--- /dev/null
+++ b/src/multimedia/platform/wmf/sourceresolver_p.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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>
+
+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/multimedia/platform/wmf/wmf.pri b/src/multimedia/platform/wmf/wmf.pri
new file mode 100644
index 000000000..81d63c3bf
--- /dev/null
+++ b/src/multimedia/platform/wmf/wmf.pri
@@ -0,0 +1,21 @@
+QT += network
+
+win32:!qtHaveModule(opengl) {
+ LIBS_PRIVATE += -lgdi32 -luser32
+}
+
+INCLUDEPATH += .
+
+HEADERS += \
+ $$PWD/wmfserviceplugin_p.h \
+ $$PWD/mfstream_p.h \
+ $$PWD/sourceresolver_p.h
+
+SOURCES += \
+ $$PWD/wmfserviceplugin.cpp \
+ $$PWD/mfstream.cpp \
+ $$PWD/sourceresolver.cpp
+
+include (evr/evr.pri)
+include (player/player.pri)
+include (decoder/decoder.pri)
diff --git a/src/multimedia/platform/wmf/wmfserviceplugin.cpp b/src/multimedia/platform/wmf/wmfserviceplugin.cpp
new file mode 100644
index 000000000..0eb20e482
--- /dev/null
+++ b/src/multimedia/platform/wmf/wmfserviceplugin.cpp
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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_p.h"
+#include "mfplayerservice_p.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;
+ }
+
+ //qDebug() << "unsupported key:" << key;
+ return 0;
+}
+
+void WMFServicePlugin::release(QMediaService *service)
+{
+ delete service;
+ releaseRefCount();
+}
+
+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/multimedia/platform/wmf/wmfserviceplugin_p.h b/src/multimedia/platform/wmf/wmfserviceplugin_p.h
new file mode 100644
index 000000000..ef769b22a
--- /dev/null
+++ b/src/multimedia/platform/wmf/wmfserviceplugin_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be 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
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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 "qmediaserviceproviderplugin.h"
+
+QT_USE_NAMESPACE
+
+class WMFServicePlugin
+ : public QMediaServiceProviderPlugin
+ , public QMediaServiceSupportedDevicesInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
+ Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "wmf.json")
+
+public:
+ QMediaService* create(QString const& key);
+ void release(QMediaService *service);
+
+ QByteArray defaultDevice(const QByteArray &service) const;
+ QList<QByteArray> devices(const QByteArray &service) const;
+ QString deviceDescription(const QByteArray &service, const QByteArray &device);
+};
+
+#endif // WMFSERVICEPLUGIN_H