summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Strømme <christian.stromme@theqtcompany.com>2016-01-15 17:27:32 +0100
committerChristian Strømme <christian.stromme@theqtcompany.com>2016-01-15 17:28:17 +0100
commit84e426c3af2a3bb1b7f916e54263aea758db38d0 (patch)
tree4fe09a8da5b15ba466e5771239d06f29a6c123da
parent84aaa48fdfc1f35c9870518a3d4b6f08a1f99449 (diff)
parent924dc7f48c7003b46079623738ae531f34aed903 (diff)
Merge remote-tracking branch 'origin/5.6' into dev
Conflicts: src/plugins/android/src/mediacapture/qandroidcamerasession.cpp src/plugins/wmf/mftvideo.cpp Change-Id: I78868b416ea4baec89ca3e2dc9eb4712db16d5fc
-rw-r--r--config.tests/alsa/alsatest.cpp2
-rw-r--r--examples/multimedia/audiodevices/doc/src/audiodevices.qdoc2
-rw-r--r--examples/multimedia/audioengine/doc/src/audioengine.qdoc2
-rw-r--r--examples/multimedia/audioinput/doc/src/audioinput.qdoc2
-rw-r--r--examples/multimedia/audiooutput/doc/src/audiooutput.qdoc2
-rw-r--r--examples/multimedia/audiorecorder/doc/src/audiorecorder.qdoc2
-rw-r--r--examples/multimedia/declarative-camera/declarative-camera.pro3
-rw-r--r--examples/multimedia/declarative-camera/doc/src/declarative-camera.qdoc2
-rw-r--r--examples/multimedia/declarative-radio/doc/src/declarative-radio.qdoc2
-rw-r--r--examples/multimedia/spectrum/doc/src/spectrum.qdoc2
-rw-r--r--examples/multimedia/video/doc/src/qmlvideo.qdoc44
-rw-r--r--examples/multimedia/video/doc/src/qmlvideofx.qdoc22
-rw-r--r--examples/multimediawidgets/camera/doc/src/camera.qdoc2
-rw-r--r--examples/multimediawidgets/player/doc/src/player.qdoc2
-rw-r--r--examples/multimediawidgets/videographicsitem/doc/src/videographicsitem.qdoc2
-rw-r--r--examples/multimediawidgets/videowidget/doc/src/videowidget.qdoc2
-rw-r--r--src/multimedia/audio/qaudio.cpp1
-rw-r--r--src/multimedia/audio/qsoundeffect_qaudio_p.cpp2
-rw-r--r--src/multimedia/doc/qtmultimedia-dita.qdocconf32
-rw-r--r--src/multimedia/doc/qtmultimedia.qdocconf26
-rw-r--r--src/multimedia/doc/src/examples/video-qml-paint-rate.qdocinc6
-rw-r--r--src/multimedia/doc/src/multimedia.qdoc30
-rw-r--r--src/multimedia/doc/src/multimediabackend.qdoc5
-rw-r--r--src/multimedia/doc/src/plugins/qml-multimedia.qdoc6
-rw-r--r--src/multimedia/doc/src/qtmultimedia-cpp.qdoc19
-rw-r--r--src/multimedia/doc/src/qtmultimedia-index.qdoc17
-rw-r--r--src/multimedia/playback/qmediaplaylist.cpp72
-rw-r--r--src/multimedia/playback/qmediaplaylist_p.h4
-rw-r--r--src/multimedia/qmediametadata.cpp1
-rw-r--r--src/multimedia/qmultimedia.cpp1
-rw-r--r--src/multimedia/qtmultimediaquicktools_headers/qsgvideonode_p.h2
-rw-r--r--src/multimedia/recording/qaudiorecorder.cpp3
-rw-r--r--src/multimedia/video/qvideoframeconversionhelper_p.h11
-rw-r--r--src/multimediawidgets/doc/qtmultimediawidgets.qdocconf44
-rw-r--r--src/multimediawidgets/multimediawidgets.pro2
-rw-r--r--src/plugins/alsa/qalsaaudiodeviceinfo.cpp8
-rw-r--r--src/plugins/alsa/qalsaaudioinput.cpp4
-rw-r--r--src/plugins/alsa/qalsaaudiooutput.cpp4
-rw-r--r--src/plugins/android/jar/jar.pri3
-rw-r--r--src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtCameraListener.java56
-rw-r--r--src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceHolderCallback.java67
-rw-r--r--src/plugins/android/src/common/common.pri3
-rw-r--r--src/plugins/android/src/common/qandroidmultimediautils.cpp35
-rw-r--r--src/plugins/android/src/common/qandroidmultimediautils.h3
-rw-r--r--src/plugins/android/src/common/qandroidvideooutput.cpp (renamed from src/plugins/android/src/common/qandroidvideorendercontrol.cpp)83
-rw-r--r--src/plugins/android/src/common/qandroidvideooutput.h64
-rw-r--r--src/plugins/android/src/mediacapture/mediacapture.pri6
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcamerasession.cpp169
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcamerasession.h22
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.cpp275
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.h (renamed from src/plugins/android/src/common/qandroidvideorendercontrol.h)78
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcaptureservice.cpp6
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcaptureservice.h4
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcapturesession.cpp36
-rw-r--r--src/plugins/android/src/mediaplayer/mediaplayer.pri6
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp5
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h2
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp70
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.h62
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp10
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaservice.h3
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp209
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h7
-rw-r--r--src/plugins/android/src/qandroidmediaserviceplugin.cpp4
-rw-r--r--src/plugins/android/src/wrappers/jni/androidcamera.cpp198
-rw-r--r--src/plugins/android/src/wrappers/jni/androidcamera.h14
-rw-r--r--src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp1
-rw-r--r--src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h2
-rw-r--r--src/plugins/android/src/wrappers/jni/androidmediaplayer.cpp66
-rw-r--r--src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp37
-rw-r--r--src/plugins/android/src/wrappers/jni/androidmediarecorder.h5
-rw-r--r--src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp20
-rw-r--r--src/plugins/android/src/wrappers/jni/androidsurfacetexture.h4
-rw-r--r--src/plugins/android/src/wrappers/jni/androidsurfaceview.cpp204
-rw-r--r--src/plugins/android/src/wrappers/jni/androidsurfaceview.h101
-rw-r--r--src/plugins/android/src/wrappers/jni/jni.pri6
-rw-r--r--src/plugins/avfoundation/camera/avfcamerasession.h4
-rw-r--r--src/plugins/avfoundation/camera/avfcamerasession.mm34
-rw-r--r--src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h10
-rw-r--r--src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm228
-rw-r--r--src/plugins/avfoundation/camera/avfimageencodercontrol.h4
-rw-r--r--src/plugins/avfoundation/camera/avfimageencodercontrol.mm30
-rw-r--r--src/plugins/avfoundation/camera/avfmediarecordercontrol.mm9
-rw-r--r--src/plugins/common/evr.pri18
-rw-r--r--src/plugins/common/evr/evrcustompresenter.cpp (renamed from src/plugins/wmf/evrcustompresenter.cpp)525
-rw-r--r--src/plugins/common/evr/evrcustompresenter.h (renamed from src/plugins/wmf/evrcustompresenter.h)121
-rw-r--r--src/plugins/common/evr/evrd3dpresentengine.cpp (renamed from src/plugins/wmf/evrd3dpresentengine.cpp)761
-rw-r--r--src/plugins/common/evr/evrd3dpresentengine.h (renamed from src/plugins/wmf/evrd3dpresentengine.h)71
-rw-r--r--src/plugins/common/evr/evrdefs.cpp42
-rw-r--r--src/plugins/common/evr/evrdefs.h219
-rw-r--r--src/plugins/common/evr/evrhelpers.cpp (renamed from src/plugins/wmf/mfglobal.cpp)100
-rw-r--r--src/plugins/common/evr/evrhelpers.h91
-rw-r--r--src/plugins/common/evr/evrvideowindowcontrol.cpp2
-rw-r--r--src/plugins/directshow/camera/camera.pri6
-rw-r--r--src/plugins/directshow/camera/dscameraimageprocessingcontrol.cpp74
-rw-r--r--src/plugins/directshow/camera/dscameraimageprocessingcontrol.h63
-rw-r--r--src/plugins/directshow/camera/dscameraservice.cpp6
-rw-r--r--src/plugins/directshow/camera/dscameraservice.h2
-rw-r--r--src/plugins/directshow/camera/dscamerasession.cpp312
-rw-r--r--src/plugins/directshow/camera/dscamerasession.h45
-rw-r--r--src/plugins/directshow/player/directshowevrvideowindowcontrol.cpp4
-rw-r--r--src/plugins/directshow/player/directshowmetadatacontrol.cpp27
-rw-r--r--src/plugins/directshow/player/directshowmetadatacontrol.h7
-rw-r--r--src/plugins/directshow/player/directshowplayercontrol.cpp26
-rw-r--r--src/plugins/directshow/player/directshowplayercontrol.h2
-rw-r--r--src/plugins/directshow/player/directshowplayerservice.cpp28
-rw-r--r--src/plugins/directshow/player/directshowplayerservice.h1
-rw-r--r--src/plugins/directshow/player/directshowvideorenderercontrol.cpp38
-rw-r--r--src/plugins/directshow/player/directshowvideorenderercontrol.h3
-rw-r--r--src/plugins/directshow/player/player.pri7
-rw-r--r--src/plugins/gstreamer/camerabin/camerabin.pro11
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp166
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinimageprocessing.h10
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinsession.h2
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.cpp304
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.h84
-rw-r--r--src/plugins/opensles/opensles.pro2
-rw-r--r--src/plugins/opensles/qopenslesaudiooutput.cpp31
-rw-r--r--src/plugins/opensles/qopenslesdeviceinfo.cpp2
-rw-r--r--src/plugins/opensles/qopenslesengine.cpp150
-rw-r--r--src/plugins/opensles/qopenslesengine.h8
-rw-r--r--src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp3
-rw-r--r--src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp5
-rw-r--r--src/plugins/wmf/mfactivate.h2
-rw-r--r--src/plugins/wmf/mfglobal.h149
-rw-r--r--src/plugins/wmf/mftvideo.cpp216
-rw-r--r--src/plugins/wmf/mftvideo.h4
-rw-r--r--src/plugins/wmf/player/mfplayersession.cpp181
-rw-r--r--src/plugins/wmf/player/mfplayersession.h3
-rw-r--r--src/plugins/wmf/player/mfvideorenderercontrol.cpp111
-rw-r--r--src/plugins/wmf/player/mfvideorenderercontrol.h6
-rw-r--r--src/plugins/wmf/wmf.pro17
-rw-r--r--src/qtmultimediaquicktools/qdeclarativevideooutput_render_p.h8
-rw-r--r--src/qtmultimediaquicktools/qdeclarativevideooutput_window_p.h2
-rw-r--r--src/qtmultimediaquicktools/qsgvideonode_p.cpp4
-rw-r--r--src/qtmultimediaquicktools/qsgvideonode_rgb.cpp2
-rw-r--r--src/qtmultimediaquicktools/qsgvideonode_rgb_p.h (renamed from src/qtmultimediaquicktools/qsgvideonode_rgb.h)11
-rw-r--r--src/qtmultimediaquicktools/qsgvideonode_texture.cpp2
-rw-r--r--src/qtmultimediaquicktools/qsgvideonode_texture_p.h (renamed from src/qtmultimediaquicktools/qsgvideonode_texture.h)11
-rw-r--r--src/qtmultimediaquicktools/qsgvideonode_yuv.cpp2
-rw-r--r--src/qtmultimediaquicktools/qsgvideonode_yuv_p.h (renamed from src/qtmultimediaquicktools/qsgvideonode_yuv.h)11
-rw-r--r--src/qtmultimediaquicktools/qtmultimediaquicktools.pro25
-rw-r--r--tests/auto/integration/qaudiodecoderbackend/BLACKLIST4
-rw-r--r--tests/auto/integration/qmediaplayerbackend/BLACKLIST4
-rw-r--r--tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp11
-rw-r--r--tests/auto/unit/qmediaplaylist/tst_qmediaplaylist.cpp304
-rw-r--r--tests/auto/unit/qmultimedia_common/mockmediaplaylistcontrol.h46
-rw-r--r--tests/auto/unit/qmultimedia_common/mockplaylistservice.h2
-rw-r--r--tests/auto/unit/qmultimedia_common/mockreadonlyplaylistprovider.h1
149 files changed, 5156 insertions, 2022 deletions
diff --git a/config.tests/alsa/alsatest.cpp b/config.tests/alsa/alsatest.cpp
index a9f9c2494..1b59cb17b 100644
--- a/config.tests/alsa/alsatest.cpp
+++ b/config.tests/alsa/alsatest.cpp
@@ -32,7 +32,7 @@
****************************************************************************/
#include <alsa/asoundlib.h>
-#if (!(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 10))
+#if (!(SND_LIB_MAJOR == 1 && (SND_LIB_MINOR > 0 || SND_LIB_SUBMINOR >= 10)))
#error "Alsa version found too old, require >= 1.0.10"
#endif
diff --git a/examples/multimedia/audiodevices/doc/src/audiodevices.qdoc b/examples/multimedia/audiodevices/doc/src/audiodevices.qdoc
index 6a4f176a4..a889a208f 100644
--- a/examples/multimedia/audiodevices/doc/src/audiodevices.qdoc
+++ b/examples/multimedia/audiodevices/doc/src/audiodevices.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
- \example audiodevices
+ \example multimedia/audiodevices
\title Audio Devices Example
\ingroup multimedia_examples
\brief Testing the available audio devices and their configuration.
diff --git a/examples/multimedia/audioengine/doc/src/audioengine.qdoc b/examples/multimedia/audioengine/doc/src/audioengine.qdoc
index d713dbe49..5b66b7da4 100644
--- a/examples/multimedia/audioengine/doc/src/audioengine.qdoc
+++ b/examples/multimedia/audioengine/doc/src/audioengine.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
- \example audioengine
+ \example multimedia/audioengine
\title AudioEngine Example
\ingroup multimedia_examples
\brief Enabling 3D sound control using the Qt Audio Engine API.
diff --git a/examples/multimedia/audioinput/doc/src/audioinput.qdoc b/examples/multimedia/audioinput/doc/src/audioinput.qdoc
index 7f30c5a4d..b4926ac5d 100644
--- a/examples/multimedia/audioinput/doc/src/audioinput.qdoc
+++ b/examples/multimedia/audioinput/doc/src/audioinput.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
- \example audioinput
+ \example multimedia/audioinput
\title Audio Input Example
\ingroup multimedia_examples
\brief Recording audio using the QAudioInput class
diff --git a/examples/multimedia/audiooutput/doc/src/audiooutput.qdoc b/examples/multimedia/audiooutput/doc/src/audiooutput.qdoc
index 374277aaa..e3a2c4167 100644
--- a/examples/multimedia/audiooutput/doc/src/audiooutput.qdoc
+++ b/examples/multimedia/audiooutput/doc/src/audiooutput.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
- \example audiooutput
+ \example multimedia/audiooutput
\title Audio Output Example
\ingroup multimedia_examples
\brief Enabling audio playback using the QAudioOutput class.
diff --git a/examples/multimedia/audiorecorder/doc/src/audiorecorder.qdoc b/examples/multimedia/audiorecorder/doc/src/audiorecorder.qdoc
index 3a20199ac..a28c3478c 100644
--- a/examples/multimedia/audiorecorder/doc/src/audiorecorder.qdoc
+++ b/examples/multimedia/audiorecorder/doc/src/audiorecorder.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
- \example audiorecorder
+ \example multimedia/audiorecorder
\title Audio Recorder Example
\ingroup multimedia_examples
\brief Discovering the available devices and supported codecs.
diff --git a/examples/multimedia/declarative-camera/declarative-camera.pro b/examples/multimedia/declarative-camera/declarative-camera.pro
index 71d4f68b0..f977e1cb3 100644
--- a/examples/multimedia/declarative-camera/declarative-camera.pro
+++ b/examples/multimedia/declarative-camera/declarative-camera.pro
@@ -9,3 +9,6 @@ RESOURCES += declarative-camera.qrc
target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/declarative-camera
INSTALLS += target
+winrt {
+ WINRT_MANIFEST.capabilities_device += webcam microphone
+}
diff --git a/examples/multimedia/declarative-camera/doc/src/declarative-camera.qdoc b/examples/multimedia/declarative-camera/doc/src/declarative-camera.qdoc
index 22215474e..1fe343845 100644
--- a/examples/multimedia/declarative-camera/doc/src/declarative-camera.qdoc
+++ b/examples/multimedia/declarative-camera/doc/src/declarative-camera.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
-\example declarative-camera
+\example multimedia/declarative-camera
\title QML Camera Example
\ingroup multimedia_examples
\brief The Camera Example shows how to use the API to capture a still image
diff --git a/examples/multimedia/declarative-radio/doc/src/declarative-radio.qdoc b/examples/multimedia/declarative-radio/doc/src/declarative-radio.qdoc
index 62687f03c..0d6a3866e 100644
--- a/examples/multimedia/declarative-radio/doc/src/declarative-radio.qdoc
+++ b/examples/multimedia/declarative-radio/doc/src/declarative-radio.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
- \example declarative-radio
+ \example multimedia/declarative-radio
\title Declarative Radio Example
\brief Demonstrates using the Radio QML type from Qt Multimedia.
\ingroup multimedia_examples
diff --git a/examples/multimedia/spectrum/doc/src/spectrum.qdoc b/examples/multimedia/spectrum/doc/src/spectrum.qdoc
index 80bed1c00..eb2c8e3bd 100644
--- a/examples/multimedia/spectrum/doc/src/spectrum.qdoc
+++ b/examples/multimedia/spectrum/doc/src/spectrum.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
- \example spectrum
+ \example multimedia/spectrum
\title Spectrum Example
\ingroup multimedia_examples
\brief Analyzing a raw audio stream using the FFTReal library.
diff --git a/examples/multimedia/video/doc/src/qmlvideo.qdoc b/examples/multimedia/video/doc/src/qmlvideo.qdoc
index 2fd4c3fae..212a1b6bc 100644
--- a/examples/multimedia/video/doc/src/qmlvideo.qdoc
+++ b/examples/multimedia/video/doc/src/qmlvideo.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
-\example video/qmlvideo
+\example multimedia/video/qmlvideo
\title QML Video Example
\ingroup multimedia_examples
\brief Transforming video and camera viewfinder content.
@@ -50,15 +50,15 @@ which moves across the \l{VideoOutput} item.
\section1 Application Structure
-The \l{video/qmlvideo/qml/qmlvideo/main.qml} file creates a UI which includes
+The \l{multimedia/video/qmlvideo/qml/qmlvideo/main.qml} file creates a UI which includes
the following items:
\list
- \li Two \l{video/qmlvideo/qml/qmlvideo/Button.qml}{Button} instances, each
+ \li Two \l{multimedia/video/qmlvideo/qml/qmlvideo/Button.qml}{Button} instances, each
of which displays a filename, and can be used to launch a
- \l{video/qmlvideo/qml/qmlvideo/FileBrowser.qml}{FileBrowser}
- \li An exit \l{video/qmlvideo/qml/qmlvideo/Button.qml}{Button}
- \li A \l{video/qmlvideo/qml/qmlvideo/SceneSelectionPanel.qml}{SceneSelectionPanel},
+ \l{multimedia/video/qmlvideo/qml/qmlvideo/FileBrowser.qml}{FileBrowser}
+ \li An exit \l{multimedia/video/qmlvideo/qml/qmlvideo/Button.qml}{Button}
+ \li A \l{multimedia/video/qmlvideo/qml/qmlvideo/SceneSelectionPanel.qml}{SceneSelectionPanel},
which is a flickable list displaying the available scenes
\li At the lower left, an item which displays the QML repainting rate - the
upper number is the instantaneous frame rate and the lower number is the
@@ -70,18 +70,18 @@ the following items:
Each scene in the flickable list is implemented in its own QML file - for
example the video-basic scene (which just displays a static \l{VideoOutput}
in the center of the screen) is implemented in the
-\l{video/qmlvideo/qml/qmlvideo/VideoBasic.qml}{VideoBasic.qml} file. As you
+\l{multimedia/video/qmlvideo/qml/qmlvideo/VideoBasic.qml}{VideoBasic.qml} file. As you
can see from the code, this makes use of a type of inheritance: a
-\l{video/qmlvideo/qml/qmlvideo/VideoBasic.qml}{VideoBasic} item ...
+\l{multimedia/video/qmlvideo/qml/qmlvideo/VideoBasic.qml}{VideoBasic} item ...
-\quotefromfile video/qmlvideo/qml/qmlvideo/VideoBasic.qml
+\quotefromfile multimedia/video/qmlvideo/qml/qmlvideo/VideoBasic.qml
\skipto import
\printuntil /^\}/
... is-a
-\l{video/qmlvideo/qml/qmlvideo/SceneBasic.qml}{SceneBasic} ...
+\l{multimedia/video/qmlvideo/qml/qmlvideo/SceneBasic.qml}{SceneBasic} ...
-\quotefromfile video/qmlvideo/qml/qmlvideo/SceneBasic.qml
+\quotefromfile multimedia/video/qmlvideo/qml/qmlvideo/SceneBasic.qml
\skipto import
\printuntil contentType
\dots
@@ -92,9 +92,9 @@ can see from the code, this makes use of a type of inheritance: a
\printuntil /^\}/
... which is-a
-\l{video/qmlvideo/qml/qmlvideo/Scene.qml}{Scene}:
+\l{multimedia/video/qmlvideo/qml/qmlvideo/Scene.qml}{Scene}:
-\quotefromfile video/qmlvideo/qml/qmlvideo/Scene.qml
+\quotefromfile multimedia/video/qmlvideo/qml/qmlvideo/Scene.qml
\skipto import
\printuntil root
\dots
@@ -104,25 +104,25 @@ can see from the code, this makes use of a type of inheritance: a
\skipto Button
\printuntil /^\}/
-\l{video/qmlvideo/qml/qmlvideo/SceneBasic.qml}{SceneBasic} describes the
+\l{multimedia/video/qmlvideo/qml/qmlvideo/SceneBasic.qml}{SceneBasic} describes the
structure and behaviour of the scene, but is agnostic of the type of content
which will be displayed - this is abstracted by
-\l{video/qmlvideo/qml/qmlvideo/Content.qml}{Content}.
+\l{multimedia/video/qmlvideo/qml/qmlvideo/Content.qml}{Content}.
This pattern allows us to define a particular use case (in this case, simply
display a static piece of content), and then instantiate that use case for
both video content
-(\l{video/qmlvideo/qml/qmlvideo/VideoBasic.qml}{VideoBasic}) and camera content
-(\l{video/qmlvideo/qml/qmlvideo/CameraBasic.qml}{CameraBasic}). This approach
+(\l{multimedia/video/qmlvideo/qml/qmlvideo/VideoBasic.qml}{VideoBasic}) and camera content
+(\l{multimedia/video/qmlvideo/qml/qmlvideo/CameraBasic.qml}{CameraBasic}). This approach
is used to implement many of the other scenes - for example, "repeatedly slide
the content from left to right and back again" is implemented by
-\l{video/qmlvideo/qml/qmlvideo/SceneMove.qml}{SceneMove}, on which
-\l{video/qmlvideo/qml/qmlvideo/VideoMove.qml}{VideoMove} and
-\l{video/qmlvideo/qml/qmlvideo/CameraMove.qml}{CameraMove} are based.
+\l{multimedia/video/qmlvideo/qml/qmlvideo/SceneMove.qml}{SceneMove}, on which
+\l{multimedia/video/qmlvideo/qml/qmlvideo/VideoMove.qml}{VideoMove} and
+\l{multimedia/video/qmlvideo/qml/qmlvideo/CameraMove.qml}{CameraMove} are based.
Depending on the value of the contentType property in the top-level scene
instance, the embedded
-\l{video/qmlvideo/qml/qmlvideo/Content.qml}{Content} item creates either a
+\l{multimedia/video/qmlvideo/qml/qmlvideo/Content.qml}{Content} item creates either a
\l{MediaPlayer} or a \l{Camera} item.
\section1 Calculating and Displaying QML Painting Rate
@@ -132,7 +132,7 @@ instance, the embedded
All that remains is to connect the afterRendering() signal of the QQuickView
object to a JavaScript function, which will eventually call frequencyItem.notify():
-\quotefromfile video/qmlvideo/main.cpp
+\quotefromfile multimedia/video/qmlvideo/main.cpp
\skipto QGuiApplication
\printuntil ;
\dots
diff --git a/examples/multimedia/video/doc/src/qmlvideofx.qdoc b/examples/multimedia/video/doc/src/qmlvideofx.qdoc
index eaad45fd8..5aed593af 100644
--- a/examples/multimedia/video/doc/src/qmlvideofx.qdoc
+++ b/examples/multimedia/video/doc/src/qmlvideofx.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
-\example video/qmlvideofx
+\example multimedia/video/qmlvideofx
\title QML Video Shader Effects Example
\ingroup multimedia_examples
@@ -128,10 +128,10 @@ types is a bit more complicated, for the following reasons:
\endlist
The abstraction of source item type is achieved by the
-\l{video/qmlvideofx/qml/qmlvideofx/Content.qml}{Content}, which uses a
+\l{multimedia/video/qmlvideofx/qml/qmlvideofx/Content.qml}{Content}, which uses a
\l{Loader} to create either a \l{MediaPlayer}, \l{Camera} or \l{Image}:
-\quotefromfile video/qmlvideofx/qml/qmlvideofx/Content.qml
+\quotefromfile multimedia/video/qmlvideofx/qml/qmlvideofx/Content.qml
\skipto import
\printuntil {
\dots
@@ -158,10 +158,10 @@ The abstraction of source item type is achieved by the
\printuntil }
Each effect is implemented as a QML item which is based on the
-\l{video/qmlvideofx/qml/qmlvideofx/Effect.qml}{Effect}, which in turn
+\l{multimedia/video/qmlvideofx/qml/qmlvideofx/Effect.qml}{Effect}, which in turn
is based on the \l{ShaderEffect}:
-\quotefromfile video/qmlvideofx/qml/qmlvideofx/Effect.qml
+\quotefromfile multimedia/video/qmlvideofx/qml/qmlvideofx/Effect.qml
\skipto import
\printuntil /^\}/
@@ -173,19 +173,19 @@ implementation of the pixelation effect. As you can see, the pixelation effect
supports one parameter (which controls the pixelation granularity), and states
that the divider should be displayed.
-\quotefromfile video/qmlvideofx/qml/qmlvideofx/EffectPixelate.qml
+\quotefromfile multimedia/video/qmlvideofx/qml/qmlvideofx/EffectPixelate.qml
\skipto import
\printuntil /^\}/
The main.qml file shows a
-\l{video/qmlvideofx/qml/qmlvideofx/FileOpen.qml}{FileOpen}, which allows
+\l{multimedia/video/qmlvideofx/qml/qmlvideofx/FileOpen.qml}{FileOpen}, which allows
the user to select the input source and an EffectSelectionPanel
item, which lists each of the available shader effects. As described above, a
-\l{video/qmlvideofx/qml/qmlvideofx/Content.qml}{Content} item is used to load the
+\l{multimedia/video/qmlvideofx/qml/qmlvideofx/Content.qml}{Content} item is used to load the
appropriate input and effect type. A
-\l{video/qmlvideofx/qml/qmlvideofx/Divider.qml}{Divider} item draws the
+\l{multimedia/video/qmlvideofx/qml/qmlvideofx/Divider.qml}{Divider} item draws the
vertical dividing line, which can be dragged left / right by the user. Finally,
-a \l{video/qmlvideofx/qml/qmlvideofx/ParameterPanel.qml}{ParameterPanel} item
+a \l{multimedia/video/qmlvideofx/qml/qmlvideofx/ParameterPanel.qml}{ParameterPanel} item
renders the sliders corresponding to each effect parameter.
Here is the effect selection menu:
@@ -198,7 +198,7 @@ Here is the effect selection menu:
All that remains is to connect the afterRendering() signal of the QQuickView
object to a JavaScript function, which will eventually call frequencyItem.notify():
-\quotefromfile video/qmlvideofx/main.cpp
+\quotefromfile multimedia/video/qmlvideofx/main.cpp
\skipto QGuiApplication
\printuntil ;
\dots
diff --git a/examples/multimediawidgets/camera/doc/src/camera.qdoc b/examples/multimediawidgets/camera/doc/src/camera.qdoc
index b00d5f1ea..0f54907de 100644
--- a/examples/multimediawidgets/camera/doc/src/camera.qdoc
+++ b/examples/multimediawidgets/camera/doc/src/camera.qdoc
@@ -29,7 +29,7 @@
/*!
-\example camera
+\example multimediawidgets/camera
\title Camera Example
\ingroup multimedia_examples
\brief The Camera Example shows how to use the API to capture a still image
diff --git a/examples/multimediawidgets/player/doc/src/player.qdoc b/examples/multimediawidgets/player/doc/src/player.qdoc
index 3bd724583..37ffca053 100644
--- a/examples/multimediawidgets/player/doc/src/player.qdoc
+++ b/examples/multimediawidgets/player/doc/src/player.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
- \example player
+ \example multimediawidgets/player
\title Media Player Example
\ingroup multimedia_examples
\brief Playing audio and video.
diff --git a/examples/multimediawidgets/videographicsitem/doc/src/videographicsitem.qdoc b/examples/multimediawidgets/videographicsitem/doc/src/videographicsitem.qdoc
index 306a26bde..5dd48e8d7 100644
--- a/examples/multimediawidgets/videographicsitem/doc/src/videographicsitem.qdoc
+++ b/examples/multimediawidgets/videographicsitem/doc/src/videographicsitem.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
-\example videographicsitem
+\example multimediawidgets/videographicsitem
\title Video Graphics Item Example
\ingroup multimedia_examples
\brief Streaming video on a graphics scene.
diff --git a/examples/multimediawidgets/videowidget/doc/src/videowidget.qdoc b/examples/multimediawidgets/videowidget/doc/src/videowidget.qdoc
index 5cc748a10..85c627720 100644
--- a/examples/multimediawidgets/videowidget/doc/src/videowidget.qdoc
+++ b/examples/multimediawidgets/videowidget/doc/src/videowidget.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
- \example videowidget
+ \example multimediawidgets/videowidget
\title Video Widget Example
\ingroup multimedia_examples
\brief Implementing a video player widget.
diff --git a/src/multimedia/audio/qaudio.cpp b/src/multimedia/audio/qaudio.cpp
index 72c24ad9f..45540a2b9 100644
--- a/src/multimedia/audio/qaudio.cpp
+++ b/src/multimedia/audio/qaudio.cpp
@@ -49,6 +49,7 @@ Q_CONSTRUCTOR_FUNCTION(qRegisterAudioMetaTypes)
/*!
\namespace QAudio
+ \ingroup multimedia-namespaces
\brief The QAudio namespace contains enums used by the audio classes.
\inmodule QtMultimedia
\ingroup multimedia
diff --git a/src/multimedia/audio/qsoundeffect_qaudio_p.cpp b/src/multimedia/audio/qsoundeffect_qaudio_p.cpp
index e95e4e521..77ed48b4a 100644
--- a/src/multimedia/audio/qsoundeffect_qaudio_p.cpp
+++ b/src/multimedia/audio/qsoundeffect_qaudio_p.cpp
@@ -395,6 +395,8 @@ qint64 PrivateSoundSource::readData( char* data, qint64 len)
memcpy(data + dataOffset, sampleData + m_offset, sampleSize - m_offset);
bytesWritten += sampleSize - m_offset;
int wrapLen = periodSize - (sampleSize - m_offset);
+ if (wrapLen > sampleSize)
+ wrapLen = sampleSize;
#ifdef QT_QAUDIO_DEBUG
qDebug() << "END OF SOUND: bytesWritten=" << bytesWritten << ", offset=" << m_offset
<< ", part1=" << (sampleSize-m_offset);
diff --git a/src/multimedia/doc/qtmultimedia-dita.qdocconf b/src/multimedia/doc/qtmultimedia-dita.qdocconf
deleted file mode 100644
index cb8a73c4f..000000000
--- a/src/multimedia/doc/qtmultimedia-dita.qdocconf
+++ /dev/null
@@ -1,32 +0,0 @@
-# Name of the project.
-project = Qt Multimedia
-
-# Directories in which to search for files to document and images.
-# By default set to the root directory of the project for sources
-# and headers and qdoc will therefore generate output for each file.
-# Images should be placed in <rootdir>/dic/images and examples in
-# <rootdir>/examples.
-# Paths are relative to the location of this file.
-exampledirs += ../src/examples \
- ../.. \
- ../../examples
-
-headerdirs += ../src \
- ../../src
-
-imagedirs += ../src/images \
-
-sourcedirs += ../src \
- ../../src
-
-excludedirs +=
-
-#Do not change the variables after this line unless you know what you are doing.
-
-outputdir = ../ditaxml
-outputformats = DITAXML
-
-examples.fileextensions = "*.cpp *.h *.js *.svg *.xml *.ui *.qml"
-examples.imageextensions = "*.png *.jpeg *.jpg *.gif *.mng"
-headers.fileextensions = "*.h *.ch *.h++ *.hh *.hpp *.hxx"
-sources.fileextensions = "*.cpp *.qdoc *.mm *.qml"
diff --git a/src/multimedia/doc/qtmultimedia.qdocconf b/src/multimedia/doc/qtmultimedia.qdocconf
index a32043b39..2ea23e2ee 100644
--- a/src/multimedia/doc/qtmultimedia.qdocconf
+++ b/src/multimedia/doc/qtmultimedia.qdocconf
@@ -18,21 +18,33 @@ qhp.QtMultimedia.indexTitle = Qt Multimedia
qhp.QtMultimedia.virtualFolder = qtmultimedia
# For listing child nodes in Qt Creator or Assistant.
-qhp.QtMultimedia.subprojects = classes qmltypes
-qhp.QtMultimedia.subprojects.classes.title = C++ Classes
+qhp.QtMultimedia.subprojects = classes widgetclasses qmltypes examples
+
+qhp.QtMultimedia.subprojects.classes.title = Qt Multimedia Classes and Namespaces
qhp.QtMultimedia.subprojects.classes.indexTitle = Qt Multimedia C++ Classes
-qhp.QtMultimedia.subprojects.classes.selectors = class fake:headerfile
+qhp.QtMultimedia.subprojects.classes.selectors = module:QtMultimedia
qhp.QtMultimedia.subprojects.classes.sortPages = true
+
+qhp.QtMultimedia.subprojects.widgetclasses.title = Qt Multimedia Widgets C++ Classes
+qhp.QtMultimedia.subprojects.widgetclasses.indexTitle = Qt Multimedia Widgets C++ Classes
+qhp.QtMultimedia.subprojects.widgetclasses.selectors = module:QtMultimediaWidgets
+qhp.QtMultimedia.subprojects.widgetclasses.sortPages = true
+
qhp.QtMultimedia.subprojects.qmltypes.title = QML Types
qhp.QtMultimedia.subprojects.qmltypes.indexTitle = Qt Multimedia QML Types
qhp.QtMultimedia.subprojects.qmltypes.selectors = qmlclass
qhp.QtMultimedia.subprojects.qmltypes.sortPages = true
-exampledirs += ../../../examples/multimedia \
+qhp.QtMultimedia.subprojects.examples.title = Examples
+qhp.QtMultimedia.subprojects.examples.indexTitle = Qt Multimedia Examples
+qhp.QtMultimedia.subprojects.examples.selectors = doc:example
+qhp.QtMultimedia.subprojects.examples.sortPages = true
+
+exampledirs += ../../../examples \
snippets
# Specify example install dir under QT_INSTALL_EXAMPLES
-examplesinstallpath = qtmultimedia/multimedia
+examplesinstallpath = qtmultimedia
headerdirs += ../..
@@ -40,9 +52,9 @@ imagedirs += src/images \
sourcedirs += ../..
-excludedirs += ../../multimediawidgets
+#excludedirs += ../../multimediawidgets
-depends += qtcore qtdoc qtgui qtquick qtqml qtmultimediawidgets qtwidgets
+depends += qtcore qtdoc qtgui qtquick qtqml qtwidgets
navigation.landingpage = "Qt Multimedia"
navigation.cppclassespage = "Qt Multimedia C++ Classes"
diff --git a/src/multimedia/doc/src/examples/video-qml-paint-rate.qdocinc b/src/multimedia/doc/src/examples/video-qml-paint-rate.qdocinc
index 84098e241..860f14270 100644
--- a/src/multimedia/doc/src/examples/video-qml-paint-rate.qdocinc
+++ b/src/multimedia/doc/src/examples/video-qml-paint-rate.qdocinc
@@ -2,7 +2,7 @@ The QML painting rate is calculated by the FrequencyMonitor class, which
turns a stream of events (received via the notify() slot), into an
instantaneous and an averaged frequency:
-\quotefromfile video/snippets/frequencymonitor/frequencymonitor.h
+\quotefromfile multimedia/video/snippets/frequencymonitor/frequencymonitor.h
\skipto class FrequencyMonitor : public QObject
\printuntil Q_OBJECT
\skipto Q_PROPERTY(qreal instantaneousFrequency
@@ -19,13 +19,13 @@ instantaneous and an averaged frequency:
The FrequencyMonitor class is exposed to QML like this
-\quotefromfile video/snippets/frequencymonitor/frequencymonitordeclarative.cpp
+\quotefromfile multimedia/video/snippets/frequencymonitor/frequencymonitordeclarative.cpp
\skipto FrequencyMonitor::qmlRegisterType
\printuntil }
and its data is displayed by defining a QML item called FrequencyItem, like this:
-\quotefromfile video/snippets/frequencymonitor/qml/frequencymonitor/FrequencyItem.qml
+\quotefromfile multimedia/video/snippets/frequencymonitor/qml/frequencymonitor/FrequencyItem.qml
\skipto import FrequencyMonitor
\printuntil id: root
\dots
diff --git a/src/multimedia/doc/src/multimedia.qdoc b/src/multimedia/doc/src/multimedia.qdoc
index 23c2ee431..5849af59d 100644
--- a/src/multimedia/doc/src/multimedia.qdoc
+++ b/src/multimedia/doc/src/multimedia.qdoc
@@ -81,52 +81,52 @@ For some quick recipes, look at the overviews above and consult this table:
\li QSoundEffect
\row
\li Playing low latency audio
- \li \l{audioinput},
- \l{spectrum}
+ \li \l{Audio Input Example}{audioinput},
+ \l{Spectrum Example}{spectrum}
\li
\li QAudioOutput
\row
\li Playing encoded audio (MP3, AAC etc)
- \li \l{player}
+ \li \l{Media Player Example}{player}
\li \l Audio, \l {MediaPlayer}
\li QMediaPlayer
\row
\li Accessing raw audio input data
- \li \l{spectrum},
- \l{audioinput}
+ \li \l{Spectrum Example}{spectrum},
+ \l{Audio Input Example}{audioinput}
\li
\li QAudioInput
\row
\li Recording encoded audio data
- \li \l{audiorecorder}
+ \li \l{Audio Recorder Example}{audiorecorder}
\li
\li QAudioRecorder
\row
\li Discovering raw audio devices
- \li \l{audiodevices}
+ \li \l{Audio Devices Example}{audiodevices}
\li
\li QAudioDeviceInfo
\row
\li Video Playback
- \li \l{player},
- \l{video/qmlvideo}{qmlvideo},
- \l{video/qmlvideofx}{qmlvideofx}
+ \li \l{Media Player Example}{player},
+ \l{QML Video Example}{qmlvideo},
+ \l{QML Video Shader Effects Example}{qmlvideofx}
\li \l MediaPlayer, \l VideoOutput, \l Video
\li QMediaPlayer, QVideoWidget, QGraphicsVideoItem
\row
\li Video Processing
- \li \l {video/qmlvideofx}{qmlvideofx}
+ \li \l {QML Video Example}{qmlvideofx}
\li \l {MediaPlayer}, \l VideoOutput
\li QMediaPlayer, QAbstractVideoSurface, QVideoFrame
\row
\li Listening to the radio
- \li \l {declarative-radio}
+ \li \l {Declarative Radio Example}{declarative-radio}
\li \l Radio, \l RadioData
\li QRadioTuner, QRadioData
\row
\li Accessing camera viewfinder
\li \l {Camera Example}{camera},
- \l {declarative-camera}
+ \l {QML Camera Example}{declarative-camera}
\li \l Camera, \l VideoOutput
\li QCamera, QVideoWidget, QGraphicsVideoItem
\row
@@ -137,13 +137,13 @@ For some quick recipes, look at the overviews above and consult this table:
\row
\li Capturing photos
\li \l {Camera Example}{camera},
- \l {declarative-camera}
+ \l {QML Camera Example}{declarative-camera}
\li \l Camera
\li QCamera, QCameraImageCapture
\row
\li Capturing movies
\li \l {Camera Example}{camera},
- \l {declarative-camera}
+ \l {QML Camera Example}{declarative-camera}
\li \l Camera
\li QCamera, QMediaRecorder
\row
diff --git a/src/multimedia/doc/src/multimediabackend.qdoc b/src/multimedia/doc/src/multimediabackend.qdoc
index b56f1040d..0622f5f09 100644
--- a/src/multimedia/doc/src/multimediabackend.qdoc
+++ b/src/multimedia/doc/src/multimediabackend.qdoc
@@ -37,14 +37,14 @@
\section1 Overview
-A multimedia backend provides the glue between platform specific libraries, and
+A multimedia backend provides the glue between platform-specific libraries, and
Qt Multimedia. In some cases the available cross-platform Multimedia APIs or
implementations are not sufficient, or not immediately available on a certain
platform. Alternatively, the multimedia implementation on a platform might expose
certain extra properties or functionality that other platforms do not, or a finer
degree of control might be possible. For these cases, it is possible to use
extended controls directly.
-
+\omit
In addition, if you plan to port the Qt Multimedia APIs to a new platform, you do
this by implementing certain control and service classes, as detailed below.
@@ -94,7 +94,6 @@ In general, adding a new media service provider is outside the scope of this doc
For best results, consult the existing provider source code, and seek assistance on the relevant
mailing lists and IRC channels.
-\omit
The base class for creating new service providers is \l{QMediaServiceProvider}.
The user must implement the \l{QMediaServiceProvider::requestService()}{requestService()}
function
diff --git a/src/multimedia/doc/src/plugins/qml-multimedia.qdoc b/src/multimedia/doc/src/plugins/qml-multimedia.qdoc
index 15c6d3c1d..4cb30a8b8 100644
--- a/src/multimedia/doc/src/plugins/qml-multimedia.qdoc
+++ b/src/multimedia/doc/src/plugins/qml-multimedia.qdoc
@@ -26,12 +26,6 @@
****************************************************************************/
/*!
- \group qml-multimedia
- \title QML Multimedia Plugin
- QML Support for the Qt Multimedia API.
-*/
-
-/*!
\page qml-multimedia.html
\title Qt Multimedia QML API
diff --git a/src/multimedia/doc/src/qtmultimedia-cpp.qdoc b/src/multimedia/doc/src/qtmultimedia-cpp.qdoc
index 8460238e7..44623beca 100644
--- a/src/multimedia/doc/src/qtmultimedia-cpp.qdoc
+++ b/src/multimedia/doc/src/qtmultimedia-cpp.qdoc
@@ -27,16 +27,33 @@
/*!
\module QtMultimedia
- \title Qt Multimedia C++ Classes
+ \title Qt Multimedia Module C++ Classes
\ingroup modules
\qtvariable multimedia
\brief The \l {Qt Multimedia} module provides audio, video, radio and camera
functionality.
+*/
+
+/*!
+ \page qtmultimedia-modules.html
+ \title Qt Multimedia C++ Classes
+ \brief Provides C++ class for multimedia use cases.
The C++ classes provide more control over the multimedia content than the
QML alternatives. If your application is serving complex use cases such as
decoding media files, accessing video or audio buffers, use the C++
alternative. For more details about the complex audio, video, and camera use
cases supported by the C++ classes, refer to \l {Multimedia}{Multimedia Overview}.
+
+ \section1 Namespaces
+ \annotatedlist multimedia-namespaces
+
+ \section1 Classes
+
+ \section2 Qt Multimedia Module
+ \generatelist {classesbymodule QtMultimedia}
+
+ \section2 Qt Multimedia Widgets Module
+ \generatelist {classesbymodule QtMultimediaWidgets}
*/
diff --git a/src/multimedia/doc/src/qtmultimedia-index.qdoc b/src/multimedia/doc/src/qtmultimedia-index.qdoc
index 0e2750872..e51ec97e0 100644
--- a/src/multimedia/doc/src/qtmultimedia-index.qdoc
+++ b/src/multimedia/doc/src/qtmultimedia-index.qdoc
@@ -35,10 +35,18 @@
and C++ classes to handle multimedia content. It also provides necessary
APIs to access the camera and radio functionality. The included
\l{Qt Audio Engine QML Types}{Qt Audio Engine} provides types for
- 3D positional audio playback and management.
+ 3D positional audio playback and content management.
- The \l{Qt Multimedia Widgets} module provides widget based multimedia
- classes.
+ The functionality of this module is divided into the following submodules:
+
+ \table
+ \row
+ \li \l{QtMultimedia}{Qt Multimedia}
+ \li Provides API for multimedia-specific use cases.
+ \row
+ \li \l{QtMultimediaWidgets}{Qt Multimedia Widgets}
+ \li Provides the widget-based multimedia API.
+ \endtable
\section1 Getting Started
@@ -54,6 +62,7 @@
\code
#include <QtMultimedia>
+ #include <QtMultimediaWidgets>
\endcode
\note If you are using a few classes from this module, we recommend
@@ -63,7 +72,7 @@
\c {qmake} project file:
\code
- QT += multimedia
+ QT += multimedia multimediawidgets
\endcode
\section1 QML Types and C++ Classes
diff --git a/src/multimedia/playback/qmediaplaylist.cpp b/src/multimedia/playback/qmediaplaylist.cpp
index 219dbc1fa..f863c78d9 100644
--- a/src/multimedia/playback/qmediaplaylist.cpp
+++ b/src/multimedia/playback/qmediaplaylist.cpp
@@ -168,10 +168,13 @@ bool QMediaPlaylist::setMediaObject(QMediaObject *mediaObject)
newControl = d->networkPlaylistControl;
if (d->control != newControl) {
- int oldSize = 0;
+ int removedStart = -1;
+ int removedEnd = -1;
+ int insertedStart = -1;
+ int insertedEnd = -1;
+
if (d->control) {
QMediaPlaylistProvider *playlist = d->control->playlistProvider();
- oldSize = playlist->mediaCount();
disconnect(playlist, SIGNAL(loadFailed(QMediaPlaylist::Error,QString)),
this, SLOT(_q_loadFailed(QMediaPlaylist::Error,QString)));
@@ -190,6 +193,12 @@ bool QMediaPlaylist::setMediaObject(QMediaObject *mediaObject)
disconnect(d->control, SIGNAL(currentMediaChanged(QMediaContent)),
this, SIGNAL(currentMediaChanged(QMediaContent)));
+ // Copy playlist items, sync playback mode and sync current index between
+ // old control and new control
+ d->syncControls(d->control, newControl,
+ &removedStart, &removedEnd,
+ &insertedStart, &insertedEnd);
+
if (d->mediaObject)
d->mediaObject->service()->releaseControl(d->control);
}
@@ -214,14 +223,14 @@ bool QMediaPlaylist::setMediaObject(QMediaObject *mediaObject)
connect(d->control, SIGNAL(currentMediaChanged(QMediaContent)),
this, SIGNAL(currentMediaChanged(QMediaContent)));
- if (oldSize) {
- emit mediaAboutToBeRemoved(0, oldSize-1);
- emit mediaRemoved(0, oldSize-1);
+ if (removedStart != -1 && removedEnd != -1) {
+ emit mediaAboutToBeRemoved(removedStart, removedEnd);
+ emit mediaRemoved(removedStart, removedEnd);
}
- if (playlist->mediaCount()) {
- emit mediaAboutToBeInserted(0,playlist->mediaCount()-1);
- emit mediaInserted(0,playlist->mediaCount()-1);
+ if (insertedStart != -1 && insertedEnd != -1) {
+ emit mediaAboutToBeInserted(insertedStart, insertedEnd);
+ emit mediaInserted(insertedStart, insertedEnd);
}
}
@@ -450,6 +459,53 @@ bool QMediaPlaylistPrivate::writeItems(QMediaPlaylistWriter *writer)
}
/*!
+ * \internal
+ * Copy playlist items, sync playback mode and sync current index between old control and new control
+*/
+void QMediaPlaylistPrivate::syncControls(QMediaPlaylistControl *oldControl, QMediaPlaylistControl *newControl,
+ int *removedStart, int *removedEnd,
+ int *insertedStart, int *insertedEnd)
+{
+ Q_ASSERT(oldControl != NULL && newControl != NULL);
+ Q_ASSERT(removedStart != NULL && removedEnd != NULL
+ && insertedStart != NULL && insertedEnd != NULL);
+
+ QMediaPlaylistProvider *oldPlaylist = oldControl->playlistProvider();
+ QMediaPlaylistProvider *newPlaylist = newControl->playlistProvider();
+
+ Q_ASSERT(oldPlaylist != NULL && newPlaylist != NULL);
+
+ *removedStart = -1;
+ *removedEnd = -1;
+ *insertedStart = -1;
+ *insertedEnd = -1;
+
+ if (newPlaylist->isReadOnly()) {
+ // we can't transfer the items from the old control.
+ // Report these items as removed.
+ if (oldPlaylist->mediaCount() > 0) {
+ *removedStart = 0;
+ *removedEnd = oldPlaylist->mediaCount() - 1;
+ }
+ // The new control might have some items that can't be cleared.
+ // Report these as inserted.
+ if (newPlaylist->mediaCount() > 0) {
+ *insertedStart = 0;
+ *insertedEnd = newPlaylist->mediaCount() - 1;
+ }
+ } else {
+ const int oldPlaylistSize = oldPlaylist->mediaCount();
+
+ newPlaylist->clear();
+ for (int i = 0; i < oldPlaylistSize; ++i)
+ newPlaylist->addMedia(oldPlaylist->media(i));
+ }
+
+ newControl->setPlaybackMode(oldControl->playbackMode());
+ newControl->setCurrentIndex(oldControl->currentIndex());
+}
+
+/*!
Load playlist using network \a request. If \a format is specified, it is used,
otherwise format is guessed from playlist name and data.
diff --git a/src/multimedia/playback/qmediaplaylist_p.h b/src/multimedia/playback/qmediaplaylist_p.h
index 25571378f..1440362df 100644
--- a/src/multimedia/playback/qmediaplaylist_p.h
+++ b/src/multimedia/playback/qmediaplaylist_p.h
@@ -108,6 +108,10 @@ public:
bool readItems(QMediaPlaylistReader *reader);
bool writeItems(QMediaPlaylistWriter *writer);
+ void syncControls(QMediaPlaylistControl *oldControl, QMediaPlaylistControl *newControl,
+ int *removedStart, int *removedEnd,
+ int *insertedStart, int *insertedEnd);
+
QMediaPlaylist::Error error;
QString errorString;
diff --git a/src/multimedia/qmediametadata.cpp b/src/multimedia/qmediametadata.cpp
index f07c0936b..9ffd2a727 100644
--- a/src/multimedia/qmediametadata.cpp
+++ b/src/multimedia/qmediametadata.cpp
@@ -160,6 +160,7 @@ Q_DEFINE_METADATA(ThumbnailImage);
/*!
\namespace QMediaMetaData
+ \ingroup multimedia-namespaces
\ingroup multimedia
\inmodule QtMultimedia
diff --git a/src/multimedia/qmultimedia.cpp b/src/multimedia/qmultimedia.cpp
index 2641cae44..f1673871f 100644
--- a/src/multimedia/qmultimedia.cpp
+++ b/src/multimedia/qmultimedia.cpp
@@ -31,6 +31,7 @@ QT_BEGIN_NAMESPACE
/*!
\namespace QMultimedia
+ \ingroup multimedia-namespaces
\ingroup multimedia
\inmodule QtMultimedia
diff --git a/src/multimedia/qtmultimediaquicktools_headers/qsgvideonode_p.h b/src/multimedia/qtmultimediaquicktools_headers/qsgvideonode_p.h
index 80fdab3b3..3024d1316 100644
--- a/src/multimedia/qtmultimediaquicktools_headers/qsgvideonode_p.h
+++ b/src/multimedia/qtmultimediaquicktools_headers/qsgvideonode_p.h
@@ -81,6 +81,8 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QSGVideoNode::FrameFlags)
class Q_MULTIMEDIAQUICK_EXPORT QSGVideoNodeFactoryInterface
{
public:
+ virtual ~QSGVideoNodeFactoryInterface();
+
virtual QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const = 0;
virtual QSGVideoNode *createNode(const QVideoSurfaceFormat &format) = 0;
};
diff --git a/src/multimedia/recording/qaudiorecorder.cpp b/src/multimedia/recording/qaudiorecorder.cpp
index 3c0d1c22d..9750c0ffe 100644
--- a/src/multimedia/recording/qaudiorecorder.cpp
+++ b/src/multimedia/recording/qaudiorecorder.cpp
@@ -64,8 +64,7 @@ QT_BEGIN_NAMESPACE
\snippet multimedia-snippets/media.cpp Audio recorder inputs
- The \l {audiorecorder}{Audio Recorder} example shows how to use this class
- in more detail.
+ The \l {Audio Recorder Example} shows how to use this class in more detail.
\sa QMediaRecorder, QAudioInputSelectorControl
*/
diff --git a/src/multimedia/video/qvideoframeconversionhelper_p.h b/src/multimedia/video/qvideoframeconversionhelper_p.h
index eead17a91..10e9d3ab3 100644
--- a/src/multimedia/video/qvideoframeconversionhelper_p.h
+++ b/src/multimedia/video/qvideoframeconversionhelper_p.h
@@ -34,6 +34,17 @@
#ifndef QVIDEOFRAMECONVERSIONHELPER_P_H
#define QVIDEOFRAMECONVERSIONHELPER_P_H
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
#include <qvideoframe.h>
#include <private/qsimd_p.h>
diff --git a/src/multimediawidgets/doc/qtmultimediawidgets.qdocconf b/src/multimediawidgets/doc/qtmultimediawidgets.qdocconf
deleted file mode 100644
index 4d36dae89..000000000
--- a/src/multimediawidgets/doc/qtmultimediawidgets.qdocconf
+++ /dev/null
@@ -1,44 +0,0 @@
-include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
-
-project = QtMultimediaWidgets
-description = Qt Multimedia Widgets Documentation
-version = $QT_VERSION
-
-# The following parameters are for creating a qhp file, the qhelpgenerator
-# program can convert the qhp file into a qch file which can be opened in
-# Qt Assistant and/or Qt Creator.
-
-# Defines the name of the project. You cannot use operators (+, =, -) in
-# the name. Properties for this project are set using a qhp.<projectname>.property
-# format.
-qhp.projects = QtMultimediaWidgets
-qhp.QtMultimediaWidgets.file = qtmultimediawidgets.qhp
-qhp.QtMultimediaWidgets.namespace = org.qt-project.qtmultimediawidgets.$QT_VERSION_TAG
-qhp.QtMultimediaWidgets.indexTitle = Qt Multimedia Widgets
-qhp.QtMultimediaWidgets.virtualFolder = qtmultimediawidgets
-
-# For listing child nodes in Qt Creator or Assistant.
-qhp.QtMultimediaWidgets.subprojects = classes
-qhp.QtMultimediaWidgets.subprojects.classes.title = C++ Classes
-qhp.QtMultimediaWidgets.subprojects.classes.indexTitle = Qt Multimedia Widgets C++ Classes
-qhp.QtMultimediaWidgets.subprojects.classes.selectors = class fake:headerfile
-qhp.QtMultimediaWidgets.subprojects.classes.sortPages = true
-
-exampledirs += ../../../examples/multimediawidgets \
- snippets
-
-# Specify example install dir under QT_INSTALL_EXAMPLES
-examplesinstallpath = qtmultimedia/multimediawidgets
-
-headerdirs += ../
-
-imagedirs += \
-
-sourcedirs += ../
-
-excludedirs +=
-
-depends += qtcore qtdoc qtquick qtqml qtmultimedia qtwidgets qtgui
-
-navigation.landingpage = "Qt Multimedia Widgets"
-navigation.cppclassespage = "Qt Multimedia Widgets C++ Classes"
diff --git a/src/multimediawidgets/multimediawidgets.pro b/src/multimediawidgets/multimediawidgets.pro
index 1cf35986f..ec96be57c 100644
--- a/src/multimediawidgets/multimediawidgets.pro
+++ b/src/multimediawidgets/multimediawidgets.pro
@@ -7,8 +7,6 @@ qtHaveModule(opengl):!contains(QT_CONFIG, opengles1) {
DEFINES += QT_NO_OPENGL
}
-QMAKE_DOCS = $$PWD/doc/qtmultimediawidgets.qdocconf
-
load(qt_module)
PRIVATE_HEADERS += \
diff --git a/src/plugins/alsa/qalsaaudiodeviceinfo.cpp b/src/plugins/alsa/qalsaaudiodeviceinfo.cpp
index be90ca666..3d310871f 100644
--- a/src/plugins/alsa/qalsaaudiodeviceinfo.cpp
+++ b/src/plugins/alsa/qalsaaudiodeviceinfo.cpp
@@ -141,7 +141,7 @@ bool QAlsaAudioDeviceInfo::open()
QList<QByteArray> devices = availableDevices(mode);
if(dev.compare(QLatin1String("default")) == 0) {
-#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
+#if (SND_LIB_MAJOR == 1 && (SND_LIB_MINOR > 0 || SND_LIB_SUBMINOR >= 14))
if (devices.size() > 0)
dev = QLatin1String(devices.first().constData());
else
@@ -150,7 +150,7 @@ bool QAlsaAudioDeviceInfo::open()
dev = QLatin1String("hw:0,0");
#endif
} else {
-#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
+#if (SND_LIB_MAJOR == 1 && (SND_LIB_MINOR > 0 || SND_LIB_SUBMINOR >= 14))
dev = device;
#else
int idx = 0;
@@ -194,7 +194,7 @@ bool QAlsaAudioDeviceInfo::testSettings(const QAudioFormat& format) const
snd_pcm_hw_params_t *params;
QString dev;
-#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
+#if (SND_LIB_MAJOR == 1 && (SND_LIB_MINOR > 0 || SND_LIB_SUBMINOR >= 14))
dev = device;
if (dev.compare(QLatin1String("default")) == 0) {
QList<QByteArray> devices = availableDevices(QAudio::AudioOutput);
@@ -335,7 +335,7 @@ QList<QByteArray> QAlsaAudioDeviceInfo::availableDevices(QAudio::Mode mode)
QList<QByteArray> devices;
QByteArray filter;
-#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
+#if (SND_LIB_MAJOR == 1 && (SND_LIB_MINOR > 0 || SND_LIB_SUBMINOR >= 14))
// Create a list of all current audio devices that support mode
void **hints, **n;
char *name, *descr, *io;
diff --git a/src/plugins/alsa/qalsaaudioinput.cpp b/src/plugins/alsa/qalsaaudioinput.cpp
index 4a8dd80da..d6d8adcff 100644
--- a/src/plugins/alsa/qalsaaudioinput.cpp
+++ b/src/plugins/alsa/qalsaaudioinput.cpp
@@ -303,7 +303,7 @@ bool QAlsaAudioInput::open()
QString dev = QString(QLatin1String(m_device.constData()));
QList<QByteArray> devices = QAlsaAudioDeviceInfo::availableDevices(QAudio::AudioInput);
if(dev.compare(QLatin1String("default")) == 0) {
-#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
+#if (SND_LIB_MAJOR == 1 && (SND_LIB_MINOR > 0 || SND_LIB_SUBMINOR >= 14))
if (devices.size() > 0)
dev = QLatin1String(devices.first());
else
@@ -312,7 +312,7 @@ bool QAlsaAudioInput::open()
dev = QLatin1String("hw:0,0");
#endif
} else {
-#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
+#if (SND_LIB_MAJOR == 1 && (SND_LIB_MINOR > 0 || SND_LIB_SUBMINOR >= 14))
dev = QLatin1String(m_device);
#else
int idx = 0;
diff --git a/src/plugins/alsa/qalsaaudiooutput.cpp b/src/plugins/alsa/qalsaaudiooutput.cpp
index 7b7da50a4..f8f0f58e8 100644
--- a/src/plugins/alsa/qalsaaudiooutput.cpp
+++ b/src/plugins/alsa/qalsaaudiooutput.cpp
@@ -306,7 +306,7 @@ bool QAlsaAudioOutput::open()
QString dev = QString(QLatin1String(m_device.constData()));
QList<QByteArray> devices = QAlsaAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
if(dev.compare(QLatin1String("default")) == 0) {
-#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
+#if (SND_LIB_MAJOR == 1 && (SND_LIB_MINOR > 0 || SND_LIB_SUBMINOR >= 14))
if (devices.size() > 0)
dev = QLatin1String(devices.first());
else
@@ -315,7 +315,7 @@ bool QAlsaAudioOutput::open()
dev = QLatin1String("hw:0,0");
#endif
} else {
-#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
+#if (SND_LIB_MAJOR == 1 && (SND_LIB_MINOR > 0 || SND_LIB_SUBMINOR >= 14))
dev = QLatin1String(m_device);
#else
int idx = 0;
diff --git a/src/plugins/android/jar/jar.pri b/src/plugins/android/jar/jar.pri
index d31839c61..713123baf 100644
--- a/src/plugins/android/jar/jar.pri
+++ b/src/plugins/android/jar/jar.pri
@@ -10,7 +10,8 @@ JAVASOURCES += $$PWD/src/org/qtproject/qt5/android/multimedia/QtAndroidMediaPlay
$$PWD/src/org/qtproject/qt5/android/multimedia/QtSurfaceTextureListener.java \
$$PWD/src/org/qtproject/qt5/android/multimedia/QtSurfaceTextureHolder.java \
$$PWD/src/org/qtproject/qt5/android/multimedia/QtMultimediaUtils.java \
- $$PWD/src/org/qtproject/qt5/android/multimedia/QtMediaRecorderListener.java
+ $$PWD/src/org/qtproject/qt5/android/multimedia/QtMediaRecorderListener.java \
+ $$PWD/src/org/qtproject/qt5/android/multimedia/QtSurfaceHolderCallback.java
# install
target.path = $$[QT_INSTALL_PREFIX]/jar
diff --git a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtCameraListener.java b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtCameraListener.java
index 974489c19..8724eeba4 100644
--- a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtCameraListener.java
+++ b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtCameraListener.java
@@ -54,6 +54,8 @@ public class QtCameraListener implements Camera.ShutterCallback,
private byte[][] m_previewBuffers = null;
private byte[] m_lastPreviewBuffer = null;
private Camera.Size m_previewSize = null;
+ private int m_previewFormat = ImageFormat.NV21; // Default preview format on all devices
+ private int m_previewBytesPerLine = -1;
private QtCameraListener(int id)
{
@@ -86,6 +88,16 @@ public class QtCameraListener implements Camera.ShutterCallback,
return m_previewSize.height;
}
+ public int previewFormat()
+ {
+ return m_previewFormat;
+ }
+
+ public int previewBytesPerLine()
+ {
+ return m_previewBytesPerLine;
+ }
+
public void setupPreviewCallback(Camera camera)
{
// Clear previous callback (also clears added buffers)
@@ -94,8 +106,37 @@ public class QtCameraListener implements Camera.ShutterCallback,
final Camera.Parameters params = camera.getParameters();
m_previewSize = params.getPreviewSize();
- double bytesPerPixel = ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8.0;
- int bufferSizeNeeded = (int) Math.ceil(bytesPerPixel * m_previewSize.width * m_previewSize.height);
+ m_previewFormat = params.getPreviewFormat();
+
+ int bufferSizeNeeded = 0;
+ if (m_previewFormat == ImageFormat.YV12) {
+ // For YV12, bytes per line must be a multiple of 16
+ final int yStride = (int) Math.ceil(m_previewSize.width / 16.0) * 16;
+ final int uvStride = (int) Math.ceil((yStride / 2) / 16.0) * 16;
+ final int ySize = yStride * m_previewSize.height;
+ final int uvSize = uvStride * m_previewSize.height / 2;
+ bufferSizeNeeded = ySize + uvSize * 2;
+
+ m_previewBytesPerLine = yStride;
+
+ } else {
+ double bytesPerPixel = ImageFormat.getBitsPerPixel(m_previewFormat) / 8.0;
+ bufferSizeNeeded = (int) Math.ceil(bytesPerPixel * m_previewSize.width * m_previewSize.height);
+
+ // bytes per line are calculated only for the first plane
+ switch (m_previewFormat) {
+ case ImageFormat.NV21:
+ m_previewBytesPerLine = m_previewSize.width; // 1 byte per sample and tightly packed
+ break;
+ case ImageFormat.RGB_565:
+ case ImageFormat.YUY2:
+ m_previewBytesPerLine = m_previewSize.width * 2; // 2 bytes per pixel
+ break;
+ default:
+ m_previewBytesPerLine = -1;
+ break;
+ }
+ }
// We could keep the same buffers when they are already bigger than the required size
// but the Android doc says the size must match, so in doubt just replace them.
@@ -117,8 +158,12 @@ public class QtCameraListener implements Camera.ShutterCallback,
m_lastPreviewBuffer = data;
- if (data != null && m_notifyNewFrames)
- notifyNewPreviewFrame(m_cameraId, data, m_previewSize.width, m_previewSize.height);
+ if (data != null && m_notifyNewFrames) {
+ notifyNewPreviewFrame(m_cameraId, data,
+ m_previewSize.width, m_previewSize.height,
+ m_previewFormat,
+ m_previewBytesPerLine);
+ }
}
@Override
@@ -142,5 +187,6 @@ public class QtCameraListener implements Camera.ShutterCallback,
private static native void notifyAutoFocusComplete(int id, boolean success);
private static native void notifyPictureExposed(int id);
private static native void notifyPictureCaptured(int id, byte[] data);
- private static native void notifyNewPreviewFrame(int id, byte[] data, int width, int height);
+ private static native void notifyNewPreviewFrame(int id, byte[] data, int width, int height,
+ int pixelFormat, int bytesPerLine);
}
diff --git a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceHolderCallback.java b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceHolderCallback.java
new file mode 100644
index 000000000..266d8a150
--- /dev/null
+++ b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceHolderCallback.java
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtMultimedia of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+package org.qtproject.qt5.android.multimedia;
+
+import android.view.SurfaceHolder;
+
+public class QtSurfaceHolderCallback implements SurfaceHolder.Callback
+{
+ private long m_id = -1;
+
+ public QtSurfaceHolderCallback(long id)
+ {
+ m_id = id;
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
+ {
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder)
+ {
+ notifySurfaceCreated(m_id);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder)
+ {
+ notifySurfaceDestroyed(m_id);
+ }
+
+
+ private static native void notifySurfaceCreated(long id);
+ private static native void notifySurfaceDestroyed(long id);
+}
diff --git a/src/plugins/android/src/common/common.pri b/src/plugins/android/src/common/common.pri
index f99dad507..9c741bd94 100644
--- a/src/plugins/android/src/common/common.pri
+++ b/src/plugins/android/src/common/common.pri
@@ -2,9 +2,8 @@ INCLUDEPATH += $$PWD
HEADERS += \
$$PWD/qandroidvideooutput.h \
- $$PWD/qandroidvideorendercontrol.h \
$$PWD/qandroidmultimediautils.h
SOURCES += \
- $$PWD/qandroidvideorendercontrol.cpp \
+ $$PWD/qandroidvideooutput.cpp \
$$PWD/qandroidmultimediautils.cpp
diff --git a/src/plugins/android/src/common/qandroidmultimediautils.cpp b/src/plugins/android/src/common/qandroidmultimediautils.cpp
index 9255db549..6b6ca3255 100644
--- a/src/plugins/android/src/common/qandroidmultimediautils.cpp
+++ b/src/plugins/android/src/common/qandroidmultimediautils.cpp
@@ -68,5 +68,40 @@ 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;
+ }
+}
QT_END_NAMESPACE
diff --git a/src/plugins/android/src/common/qandroidmultimediautils.h b/src/plugins/android/src/common/qandroidmultimediautils.h
index 6955c49e9..622f343f5 100644
--- a/src/plugins/android/src/common/qandroidmultimediautils.h
+++ b/src/plugins/android/src/common/qandroidmultimediautils.h
@@ -36,6 +36,7 @@
#include <qglobal.h>
#include <qsize.h>
+#include "androidcamera.h"
QT_BEGIN_NAMESPACE
@@ -45,6 +46,8 @@ 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);
QT_END_NAMESPACE
diff --git a/src/plugins/android/src/common/qandroidvideorendercontrol.cpp b/src/plugins/android/src/common/qandroidvideooutput.cpp
index cd9c9d1d6..82c27035d 100644
--- a/src/plugins/android/src/common/qandroidvideorendercontrol.cpp
+++ b/src/plugins/android/src/common/qandroidvideooutput.cpp
@@ -31,9 +31,9 @@
**
****************************************************************************/
-#include "qandroidvideorendercontrol.h"
-#include "androidsurfacetexture.h"
+#include "qandroidvideooutput.h"
+#include "androidsurfacetexture.h"
#include <QAbstractVideoSurface>
#include <QVideoSurfaceFormat>
#include <qevent.h>
@@ -59,19 +59,13 @@ static const GLfloat g_texture_data[] = {
0.f, 1.f
};
-OpenGLResourcesDeleter::~OpenGLResourcesDeleter()
-{
- glDeleteTextures(1, &m_textureID);
- delete m_fbo;
- delete m_program;
-}
class AndroidTextureVideoBuffer : public QAbstractVideoBuffer
{
public:
- AndroidTextureVideoBuffer(QAndroidVideoRendererControl *control)
+ AndroidTextureVideoBuffer(QAndroidTextureVideoOutput *output)
: QAbstractVideoBuffer(GLTextureHandle)
- , m_control(control)
+ , m_output(output)
, m_textureUpdated(false)
, m_mapMode(NotMapped)
{
@@ -86,7 +80,7 @@ public:
if (m_mapMode == NotMapped && mode == ReadOnly) {
updateFrame();
m_mapMode = mode;
- m_image = m_control->m_fbo->toImage();
+ m_image = m_output->m_fbo->toImage();
if (numBytes)
*numBytes = m_image.byteCount();
@@ -110,7 +104,7 @@ public:
{
AndroidTextureVideoBuffer *that = const_cast<AndroidTextureVideoBuffer*>(this);
that->updateFrame();
- return m_control->m_fbo->texture();
+ return m_output->m_fbo->texture();
}
private:
@@ -118,19 +112,47 @@ private:
{
if (!m_textureUpdated) {
// update the video texture (called from the render thread)
- m_control->renderFrameToFbo();
+ m_output->renderFrameToFbo();
m_textureUpdated = true;
}
}
- QAndroidVideoRendererControl *m_control;
+ QAndroidTextureVideoOutput *m_output;
bool m_textureUpdated;
MapMode m_mapMode;
QImage m_image;
};
-QAndroidVideoRendererControl::QAndroidVideoRendererControl(QObject *parent)
- : QVideoRendererControl(parent)
+
+class OpenGLResourcesDeleter : public QObject
+{
+public:
+ OpenGLResourcesDeleter()
+ : m_textureID(0)
+ , m_fbo(0)
+ , m_program(0)
+ { }
+
+ ~OpenGLResourcesDeleter()
+ {
+ glDeleteTextures(1, &m_textureID);
+ delete m_fbo;
+ delete m_program;
+ }
+
+ void setTexture(quint32 id) { m_textureID = id; }
+ void setFbo(QOpenGLFramebufferObject *fbo) { m_fbo = fbo; }
+ void setShaderProgram(QOpenGLShaderProgram *prog) { m_program = prog; }
+
+private:
+ quint32 m_textureID;
+ QOpenGLFramebufferObject *m_fbo;
+ QOpenGLShaderProgram *m_program;
+};
+
+
+QAndroidTextureVideoOutput::QAndroidTextureVideoOutput(QObject *parent)
+ : QAndroidVideoOutput(parent)
, m_surface(0)
, m_surfaceTexture(0)
, m_externalTex(0)
@@ -138,9 +160,10 @@ QAndroidVideoRendererControl::QAndroidVideoRendererControl(QObject *parent)
, m_program(0)
, m_glDeleter(0)
{
+
}
-QAndroidVideoRendererControl::~QAndroidVideoRendererControl()
+QAndroidTextureVideoOutput::~QAndroidTextureVideoOutput()
{
clearSurfaceTexture();
@@ -148,12 +171,12 @@ QAndroidVideoRendererControl::~QAndroidVideoRendererControl()
m_glDeleter->deleteLater();
}
-QAbstractVideoSurface *QAndroidVideoRendererControl::surface() const
+QAbstractVideoSurface *QAndroidTextureVideoOutput::surface() const
{
return m_surface;
}
-void QAndroidVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
+void QAndroidTextureVideoOutput::setSurface(QAbstractVideoSurface *surface)
{
if (surface == m_surface)
return;
@@ -172,12 +195,12 @@ void QAndroidVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
}
}
-bool QAndroidVideoRendererControl::isReady()
+bool QAndroidTextureVideoOutput::isReady()
{
return QOpenGLContext::currentContext() || m_externalTex;
}
-bool QAndroidVideoRendererControl::initSurfaceTexture()
+bool QAndroidTextureVideoOutput::initSurfaceTexture()
{
if (m_surfaceTexture)
return true;
@@ -210,7 +233,7 @@ bool QAndroidVideoRendererControl::initSurfaceTexture()
return m_surfaceTexture != 0;
}
-void QAndroidVideoRendererControl::clearSurfaceTexture()
+void QAndroidTextureVideoOutput::clearSurfaceTexture()
{
if (m_surfaceTexture) {
delete m_surfaceTexture;
@@ -218,7 +241,7 @@ void QAndroidVideoRendererControl::clearSurfaceTexture()
}
}
-AndroidSurfaceTexture *QAndroidVideoRendererControl::surfaceTexture()
+AndroidSurfaceTexture *QAndroidTextureVideoOutput::surfaceTexture()
{
if (!initSurfaceTexture())
return 0;
@@ -226,7 +249,7 @@ AndroidSurfaceTexture *QAndroidVideoRendererControl::surfaceTexture()
return m_surfaceTexture;
}
-void QAndroidVideoRendererControl::setVideoSize(const QSize &size)
+void QAndroidTextureVideoOutput::setVideoSize(const QSize &size)
{
QMutexLocker locker(&m_mutex);
@@ -238,19 +261,19 @@ void QAndroidVideoRendererControl::setVideoSize(const QSize &size)
m_nativeSize = size;
}
-void QAndroidVideoRendererControl::stop()
+void QAndroidTextureVideoOutput::stop()
{
if (m_surface && m_surface->isActive())
m_surface->stop();
m_nativeSize = QSize();
}
-void QAndroidVideoRendererControl::reset()
+void QAndroidTextureVideoOutput::reset()
{
clearSurfaceTexture();
}
-void QAndroidVideoRendererControl::onFrameAvailable()
+void QAndroidTextureVideoOutput::onFrameAvailable()
{
if (!m_nativeSize.isValid() || !m_surface)
return;
@@ -274,7 +297,7 @@ void QAndroidVideoRendererControl::onFrameAvailable()
m_surface->present(frame);
}
-void QAndroidVideoRendererControl::renderFrameToFbo()
+void QAndroidTextureVideoOutput::renderFrameToFbo()
{
QMutexLocker locker(&m_mutex);
@@ -333,7 +356,7 @@ void QAndroidVideoRendererControl::renderFrameToFbo()
glEnable(GL_BLEND);
}
-void QAndroidVideoRendererControl::createGLResources()
+void QAndroidTextureVideoOutput::createGLResources()
{
if (!m_fbo || m_fbo->size() != m_nativeSize) {
delete m_fbo;
@@ -374,7 +397,7 @@ void QAndroidVideoRendererControl::createGLResources()
}
}
-void QAndroidVideoRendererControl::customEvent(QEvent *e)
+void QAndroidTextureVideoOutput::customEvent(QEvent *e)
{
if (e->type() == QEvent::User) {
// This is running in the render thread (OpenGL enabled)
diff --git a/src/plugins/android/src/common/qandroidvideooutput.h b/src/plugins/android/src/common/qandroidvideooutput.h
index d45779d12..f4401fa1d 100644
--- a/src/plugins/android/src/common/qandroidvideooutput.h
+++ b/src/plugins/android/src/common/qandroidvideooutput.h
@@ -34,19 +34,27 @@
#ifndef QANDROIDVIDEOOUTPUT_H
#define QANDROIDVIDEOOUTPUT_H
-#include <qglobal.h>
+#include <qobject.h>
#include <qsize.h>
+#include <qmutex.h>
QT_BEGIN_NAMESPACE
class AndroidSurfaceTexture;
+class AndroidSurfaceHolder;
+class QOpenGLFramebufferObject;
+class QOpenGLShaderProgram;
+class OpenGLResourcesDeleter;
+class QAbstractVideoSurface;
-class QAndroidVideoOutput
+class QAndroidVideoOutput : public QObject
{
+ Q_OBJECT
public:
virtual ~QAndroidVideoOutput() { }
virtual AndroidSurfaceTexture *surfaceTexture() { return 0; }
+ virtual AndroidSurfaceHolder *surfaceHolder() { return 0; }
virtual bool isReady() { return true; }
@@ -54,12 +62,56 @@ public:
virtual void stop() { }
virtual void reset() { }
- // signals:
- // void readyChanged(bool);
+Q_SIGNALS:
+ void readyChanged(bool);
+
+protected:
+ QAndroidVideoOutput(QObject *parent) : QObject(parent) { }
};
-#define QAndroidVideoOutput_iid "org.qt-project.qt.qandroidvideooutput/5.0"
-Q_DECLARE_INTERFACE(QAndroidVideoOutput, QAndroidVideoOutput_iid)
+
+class QAndroidTextureVideoOutput : public QAndroidVideoOutput
+{
+ Q_OBJECT
+public:
+ explicit QAndroidTextureVideoOutput(QObject *parent = 0);
+ ~QAndroidTextureVideoOutput() Q_DECL_OVERRIDE;
+
+ QAbstractVideoSurface *surface() const;
+ void setSurface(QAbstractVideoSurface *surface);
+
+ AndroidSurfaceTexture *surfaceTexture() Q_DECL_OVERRIDE;
+
+ bool isReady() Q_DECL_OVERRIDE;
+ void setVideoSize(const QSize &) Q_DECL_OVERRIDE;
+ void stop() Q_DECL_OVERRIDE;
+ void reset() Q_DECL_OVERRIDE;
+
+ void customEvent(QEvent *) Q_DECL_OVERRIDE;
+
+private Q_SLOTS:
+ void onFrameAvailable();
+
+private:
+ bool initSurfaceTexture();
+ void 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;
+
+ friend class AndroidTextureVideoBuffer;
+};
QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/mediacapture.pri b/src/plugins/android/src/mediacapture/mediacapture.pri
index fde0e3d6f..2811f0371 100644
--- a/src/plugins/android/src/mediacapture/mediacapture.pri
+++ b/src/plugins/android/src/mediacapture/mediacapture.pri
@@ -22,7 +22,8 @@ SOURCES += \
$$PWD/qandroidvideoencodersettingscontrol.cpp \
$$PWD/qandroidaudioinputselectorcontrol.cpp \
$$PWD/qandroidmediavideoprobecontrol.cpp \
- $$PWD/qandroidcamerainfocontrol.cpp
+ $$PWD/qandroidcamerainfocontrol.cpp \
+ $$PWD/qandroidcameravideorenderercontrol.cpp
HEADERS += \
$$PWD/qandroidcaptureservice.h \
@@ -46,4 +47,5 @@ HEADERS += \
$$PWD/qandroidvideoencodersettingscontrol.h \
$$PWD/qandroidaudioinputselectorcontrol.h \
$$PWD/qandroidmediavideoprobecontrol.h \
- $$PWD/qandroidcamerainfocontrol.h
+ $$PWD/qandroidcamerainfocontrol.h \
+ $$PWD/qandroidcameravideorenderercontrol.h
diff --git a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp
index 3623ce05a..eec31e65e 100644
--- a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp
+++ b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp
@@ -48,42 +48,6 @@
QT_BEGIN_NAMESPACE
-class DataVideoBuffer : public QAbstractVideoBuffer
-{
-public:
- DataVideoBuffer(const QByteArray &d, int bpl = -1)
- : QAbstractVideoBuffer(NoHandle)
- , data(d)
- , mode(NotMapped)
- , bytesPerLine(bpl)
- { }
-
- MapMode mapMode() const { return mode; }
-
- uchar *map(MapMode m, int *numBytes, int *bpl)
- {
- if (mode != NotMapped || m == NotMapped)
- return 0;
-
- mode = m;
-
- if (numBytes)
- *numBytes = data.size();
-
- if (bpl)
- *bpl = bytesPerLine;
-
- return reinterpret_cast<uchar *>(data.data());
- }
-
- void unmap() { mode = NotMapped; }
-
-private:
- QByteArray data;
- MapMode mode;
- int bytesPerLine;
-};
-
Q_GLOBAL_STATIC(QList<AndroidCameraInfo>, g_availableCameras)
QAndroidCameraSession::QAndroidCameraSession(QObject *parent)
@@ -104,6 +68,7 @@ QAndroidCameraSession::QAndroidCameraSession(QObject *parent)
, m_readyForCapture(false)
, m_captureCanceled(false)
, m_currentImageCaptureId(-1)
+ , m_previewCallback(0)
{
m_mediaStorageLocation.addStorageLocation(
QMediaStorageLocation::Pictures,
@@ -208,14 +173,17 @@ bool QAndroidCameraSession::open()
if (m_camera) {
connect(m_camera, SIGNAL(pictureExposed()), this, SLOT(onCameraPictureExposed()));
- connect(m_camera, SIGNAL(lastPreviewFrameFetched(QByteArray,int,int)),
- this, SLOT(onLastPreviewFrameFetched(QByteArray,int,int)));
- connect(m_camera, SIGNAL(newPreviewFrame(QByteArray,int,int)),
- this, SLOT(onNewPreviewFrame(QByteArray,int,int)),
+ 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();
@@ -224,7 +192,7 @@ bool QAndroidCameraSession::open()
if (m_camera->getPreviewFormat() != AndroidCamera::NV21)
m_camera->setPreviewFormat(AndroidCamera::NV21);
- m_camera->notifyNewFrames(m_videoProbes.count());
+ m_camera->notifyNewFrames(m_videoProbes.count() || m_previewCallback);
emit opened();
} else {
@@ -259,16 +227,19 @@ void QAndroidCameraSession::close()
emit statusChanged(m_status);
}
-void QAndroidCameraSession::setVideoPreview(QObject *videoOutput)
+void QAndroidCameraSession::setVideoOutput(QAndroidVideoOutput *output)
{
if (m_videoOutput) {
m_videoOutput->stop();
m_videoOutput->reset();
}
- if (videoOutput) {
- connect(videoOutput, SIGNAL(readyChanged(bool)), this, SLOT(onVideoOutputReady(bool)));
- m_videoOutput = qobject_cast<QAndroidVideoOutput *>(videoOutput);
+ 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;
}
@@ -336,7 +307,10 @@ bool QAndroidCameraSession::startPreview()
if (!m_videoOutput->isReady())
return true; // delay starting until the video output is ready
- if (!m_camera->setPreviewTexture(m_videoOutput->surfaceTexture()))
+ 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;
m_status = QCamera::StartingStatus;
@@ -366,6 +340,7 @@ void QAndroidCameraSession::stopPreview()
m_camera->stopPreview();
m_camera->setPreviewSize(QSize());
m_camera->setPreviewTexture(0);
+ m_camera->setPreviewDisplay(0);
if (m_videoOutput) {
m_videoOutput->stop();
@@ -413,7 +388,7 @@ void QAndroidCameraSession::addProbe(QAndroidMediaVideoProbeControl *probe)
if (probe)
m_videoProbes << probe;
if (m_camera)
- m_camera->notifyNewFrames(m_videoProbes.count());
+ m_camera->notifyNewFrames(m_videoProbes.count() || m_previewCallback);
m_videoProbesMutex.unlock();
}
@@ -422,7 +397,24 @@ void QAndroidCameraSession::removeProbe(QAndroidMediaVideoProbeControl *probe)
m_videoProbesMutex.lock();
m_videoProbes.remove(probe);
if (m_camera)
- m_camera->notifyNewFrames(m_videoProbes.count());
+ 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();
}
@@ -556,6 +548,12 @@ void QAndroidCameraSession::cancelCapture()
m_captureCanceled = true;
}
+void QAndroidCameraSession::onCameraTakePictureFailed()
+{
+ emit imageCaptureError(m_currentImageCaptureId, QCameraImageCapture::ResourceError,
+ tr("Failed to capture image"));
+}
+
void QAndroidCameraSession::onCameraPictureExposed()
{
if (m_captureCanceled)
@@ -565,57 +563,37 @@ void QAndroidCameraSession::onCameraPictureExposed()
m_camera->fetchLastPreviewFrame();
}
-void QAndroidCameraSession::onLastPreviewFrameFetched(const QByteArray &preview, int width, int height)
+void QAndroidCameraSession::onLastPreviewFrameFetched(const QVideoFrame &frame)
{
- if (preview.size()) {
- QtConcurrent::run(this, &QAndroidCameraSession::processPreviewImage,
- m_currentImageCaptureId,
- preview,
- width,
- height,
- m_camera->getRotation());
- }
+ QtConcurrent::run(this, &QAndroidCameraSession::processPreviewImage,
+ m_currentImageCaptureId,
+ frame,
+ m_camera->getRotation());
}
-void QAndroidCameraSession::processPreviewImage(int id, const QByteArray &data, int width, int height, int rotation)
+void QAndroidCameraSession::processPreviewImage(int id, const QVideoFrame &frame, int rotation)
{
- emit imageCaptured(id, prepareImageFromPreviewData(data, width, height, rotation));
-}
-
-QImage QAndroidCameraSession::prepareImageFromPreviewData(const QByteArray &data, int width, int height, int rotation)
-{
- QVideoFrame frame(new QMemoryVideoBuffer(data, width),
- QSize(width, height), QVideoFrame::Format_NV21);
-
- QImage result = qt_imageFromVideoFrame(frame);
-
- QTransform transform;
-
// 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);
- result = result.transformed(transform);
-
- return result;
+ emit imageCaptured(id, qt_imageFromVideoFrame(frame).transformed(transform));
}
-void QAndroidCameraSession::onNewPreviewFrame(const QByteArray &frame, int width, int height)
+void QAndroidCameraSession::onNewPreviewFrame(const QVideoFrame &frame)
{
m_videoProbesMutex.lock();
- if (frame.size() && m_videoProbes.count()) {
- // Bytes per line should be only for the first plane. For NV21, the Y plane has 8 bits
- // per sample, so bpl == width
- QVideoFrame videoFrame(new DataVideoBuffer(frame, width),
- QSize(width, height),
- QVideoFrame::Format_NV21);
- for (QAndroidMediaVideoProbeControl *probe : qAsConst(m_videoProbes))
- probe->newFrameProbed(videoFrame);
- }
+
+ for (QAndroidMediaVideoProbeControl *probe : qAsConst(m_videoProbes))
+ probe->newFrameProbed(frame);
+
+ if (m_previewCallback)
+ m_previewCallback->onFrameAvailable(frame);
+
m_videoProbesMutex.unlock();
}
@@ -647,6 +625,27 @@ void QAndroidCameraSession::onCameraPreviewStarted()
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) {
@@ -692,7 +691,7 @@ void QAndroidCameraSession::processCapturedImage(int id,
}
if (dest & QCameraImageCapture::CaptureToBuffer) {
- QVideoFrame frame(new DataVideoBuffer(data), resolution, QVideoFrame::Format_Jpeg);
+ QVideoFrame frame(new QMemoryVideoBuffer(data, -1), resolution, QVideoFrame::Format_Jpeg);
emit imageAvailable(id, frame);
}
}
diff --git a/src/plugins/android/src/mediacapture/qandroidcamerasession.h b/src/plugins/android/src/mediacapture/qandroidcamerasession.h
index a56721bcd..d15509fe8 100644
--- a/src/plugins/android/src/mediacapture/qandroidcamerasession.h
+++ b/src/plugins/android/src/mediacapture/qandroidcamerasession.h
@@ -68,7 +68,8 @@ public:
void setCaptureMode(QCamera::CaptureModes mode);
bool isCaptureModeSupported(QCamera::CaptureModes mode) const;
- void setVideoPreview(QObject *videoOutput);
+ QAndroidVideoOutput *videoOutput() const { return m_videoOutput; }
+ void setVideoOutput(QAndroidVideoOutput *output);
void adjustViewfinderSize(const QSize &captureSize, bool restartPreview = true);
QImageEncoderSettings imageSettings() const { return m_imageSettings; }
@@ -90,6 +91,14 @@ public:
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);
+
Q_SIGNALS:
void statusChanged(QCamera::Status status);
void stateChanged(QCamera::State);
@@ -112,11 +121,13 @@ private Q_SLOTS:
void onApplicationStateChanged(Qt::ApplicationState state);
+ void onCameraTakePictureFailed();
void onCameraPictureExposed();
void onCameraPictureCaptured(const QByteArray &data);
- void onLastPreviewFrameFetched(const QByteArray &preview, int width, int height);
- void onNewPreviewFrame(const QByteArray &frame, int width, int height);
+ void onLastPreviewFrameFetched(const QVideoFrame &frame);
+ void onNewPreviewFrame(const QVideoFrame &frame);
void onCameraPreviewStarted();
+ void onCameraPreviewFailedToStart();
void onCameraPreviewStopped();
private:
@@ -129,8 +140,8 @@ private:
void stopPreview();
void applyImageSettings();
- void processPreviewImage(int id, const QByteArray &data, int width, int height, int rotation);
- QImage prepareImageFromPreviewData(const QByteArray &data, int width, int height, int rotation);
+
+ void processPreviewImage(int id, const QVideoFrame &frame, int rotation);
void processCapturedImage(int id,
const QByteArray &data,
const QSize &resolution,
@@ -162,6 +173,7 @@ private:
QSet<QAndroidMediaVideoProbeControl *> m_videoProbes;
QMutex m_videoProbesMutex;
+ PreviewCallback *m_previewCallback;
};
QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.cpp
new file mode 100644
index 000000000..1d5b521b8
--- /dev/null
+++ b/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.cpp
@@ -0,0 +1,275 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidcameravideorenderercontrol.h"
+
+#include "qandroidcamerasession.h"
+#include "qandroidvideooutput.h"
+#include "androidsurfaceview.h"
+#include "qandroidmultimediautils.h"
+#include <qabstractvideosurface.h>
+#include <qvideosurfaceformat.h>
+#include <qcoreapplication.h>
+#include <qthread.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCameraDataVideoOutput : public QAndroidVideoOutput
+ , public QAndroidCameraSession::PreviewCallback
+{
+ Q_OBJECT
+public:
+ explicit QAndroidCameraDataVideoOutput(QAndroidCameraVideoRendererControl *control);
+ ~QAndroidCameraDataVideoOutput() Q_DECL_OVERRIDE;
+
+ AndroidSurfaceHolder *surfaceHolder() Q_DECL_OVERRIDE;
+
+ bool isReady() Q_DECL_OVERRIDE;
+
+ void stop() Q_DECL_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(Q_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(Q_NULLPTR);
+ qWarning("The video surface is not compatible with any format supported by the camera");
+ } else {
+ m_control->cameraSession()->setPreviewCallback(this);
+
+ if (m_control->cameraSession()->status() > QCamera::LoadedStatus)
+ m_control->cameraSession()->camera()->stopPreview();
+
+ m_control->cameraSession()->setPreviewFormat(qt_androidImageFormatFromPixelFormat(m_pixelFormat));
+
+ if (m_control->cameraSession()->status() > QCamera::LoadedStatus)
+ m_control->cameraSession()->camera()->startPreview();
+ }
+}
+
+void QAndroidCameraDataVideoOutput::stop()
+{
+ m_mutex.lock();
+ m_lastFrame = QVideoFrame();
+ m_mutex.unlock();
+
+ if (m_control->surface() && m_control->surface()->isActive())
+ m_control->surface()->stop();
+}
+
+void QAndroidCameraDataVideoOutput::onFrameAvailable(const QVideoFrame &frame)
+{
+ m_mutex.lock();
+ m_lastFrame = frame;
+ m_mutex.unlock();
+
+ if (thread() == QThread::currentThread())
+ presentFrame();
+ else
+ QCoreApplication::postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority);
+}
+
+bool QAndroidCameraDataVideoOutput::event(QEvent *e)
+{
+ if (e->type() == QEvent::User) {
+ presentFrame();
+ return true;
+ }
+
+ return QObject::event(e);
+}
+
+void QAndroidCameraDataVideoOutput::presentFrame()
+{
+ Q_ASSERT(thread() == QThread::currentThread());
+
+ QMutexLocker locker(&m_mutex);
+
+ if (m_control->surface() && m_lastFrame.isValid() && m_lastFrame.pixelFormat() == m_pixelFormat) {
+
+ if (m_control->surface()->isActive() && (m_control->surface()->surfaceFormat().pixelFormat() != m_lastFrame.pixelFormat()
+ || m_control->surface()->surfaceFormat().frameSize() != m_lastFrame.size())) {
+ m_control->surface()->stop();
+ }
+
+ if (!m_control->surface()->isActive()) {
+ QVideoSurfaceFormat format(m_lastFrame.size(), m_lastFrame.pixelFormat(), m_lastFrame.handleType());
+ // Front camera frames are automatically mirrored when using SurfaceTexture or SurfaceView,
+ // but the buffers we get from the data callback are not. Tell the QAbstractVideoSurface
+ // that it needs to mirror the frames.
+ if (m_control->cameraSession()->camera()->getFacing() == AndroidCamera::CameraFacingFront)
+ format.setProperty("mirrored", true);
+
+ m_control->surface()->start(format);
+ }
+
+ if (m_control->surface()->isActive())
+ m_control->surface()->present(m_lastFrame);
+ }
+
+ m_lastFrame = QVideoFrame();
+}
+
+
+QAndroidCameraVideoRendererControl::QAndroidCameraVideoRendererControl(QAndroidCameraSession *session, QObject *parent)
+ : QVideoRendererControl(parent)
+ , m_cameraSession(session)
+ , m_surface(0)
+ , m_textureOutput(0)
+ , m_dataOutput(0)
+{
+}
+
+QAndroidCameraVideoRendererControl::~QAndroidCameraVideoRendererControl()
+{
+ m_cameraSession->setVideoOutput(0);
+}
+
+QAbstractVideoSurface *QAndroidCameraVideoRendererControl::surface() const
+{
+ return m_surface;
+}
+
+void QAndroidCameraVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
+{
+ if (m_surface == surface)
+ return;
+
+ m_surface = surface;
+ QAndroidVideoOutput *oldOutput = m_textureOutput ? static_cast<QAndroidVideoOutput*>(m_textureOutput)
+ : static_cast<QAndroidVideoOutput*>(m_dataOutput);
+ QAndroidVideoOutput *newOutput = 0;
+
+ if (m_surface) {
+ if (!m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty()) {
+ if (!m_textureOutput) {
+ m_dataOutput = 0;
+ newOutput = m_textureOutput = new QAndroidTextureVideoOutput(this);
+ }
+ } else if (!m_dataOutput) {
+ m_textureOutput = 0;
+ newOutput = m_dataOutput = new QAndroidCameraDataVideoOutput(this);
+ }
+
+ if (m_textureOutput)
+ m_textureOutput->setSurface(m_surface);
+ }
+
+ if (newOutput != oldOutput) {
+ m_cameraSession->setVideoOutput(newOutput);
+ delete oldOutput;
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "qandroidcameravideorenderercontrol.moc"
+
diff --git a/src/plugins/android/src/common/qandroidvideorendercontrol.h b/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.h
index c660758fb..4b6428ba0 100644
--- a/src/plugins/android/src/common/qandroidvideorendercontrol.h
+++ b/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.h
@@ -31,88 +31,36 @@
**
****************************************************************************/
-#ifndef QANDROIDVIDEORENDERCONTROL_H
-#define QANDROIDVIDEORENDERCONTROL_H
+#ifndef QANDROIDCAMERAVIDEORENDERERCONTROL_H
+#define QANDROIDCAMERAVIDEORENDERERCONTROL_H
#include <qvideorenderercontrol.h>
-#include <qmutex.h>
-#include "qandroidvideooutput.h"
QT_BEGIN_NAMESPACE
-class QOpenGLTexture;
-class QOpenGLFramebufferObject;
-class QOpenGLShaderProgram;
-class AndroidSurfaceTexture;
+class QAndroidCameraSession;
+class QAndroidTextureVideoOutput;
+class QAndroidCameraDataVideoOutput;
-class OpenGLResourcesDeleter : public QObject
+class QAndroidCameraVideoRendererControl : public QVideoRendererControl
{
Q_OBJECT
public:
- OpenGLResourcesDeleter()
- : m_textureID(0)
- , m_fbo(0)
- , m_program(0)
- { }
-
- ~OpenGLResourcesDeleter();
-
- void setTexture(quint32 id) { m_textureID = id; }
- void setFbo(QOpenGLFramebufferObject *fbo) { m_fbo = fbo; }
- void setShaderProgram(QOpenGLShaderProgram *prog) { m_program = prog; }
-
-private:
- quint32 m_textureID;
- QOpenGLFramebufferObject *m_fbo;
- QOpenGLShaderProgram *m_program;
-};
-
-class QAndroidVideoRendererControl : public QVideoRendererControl, public QAndroidVideoOutput
-{
- Q_OBJECT
- Q_INTERFACES(QAndroidVideoOutput)
-public:
- explicit QAndroidVideoRendererControl(QObject *parent = 0);
- ~QAndroidVideoRendererControl() Q_DECL_OVERRIDE;
+ QAndroidCameraVideoRendererControl(QAndroidCameraSession *session, QObject *parent = 0);
+ ~QAndroidCameraVideoRendererControl() Q_DECL_OVERRIDE;
QAbstractVideoSurface *surface() const Q_DECL_OVERRIDE;
void setSurface(QAbstractVideoSurface *surface) Q_DECL_OVERRIDE;
- AndroidSurfaceTexture *surfaceTexture() Q_DECL_OVERRIDE;
- bool isReady() Q_DECL_OVERRIDE;
- void setVideoSize(const QSize &size) Q_DECL_OVERRIDE;
- void stop() Q_DECL_OVERRIDE;
- void reset() Q_DECL_OVERRIDE;
-
- void customEvent(QEvent *) Q_DECL_OVERRIDE;
-
-Q_SIGNALS:
- void readyChanged(bool);
-
-private Q_SLOTS:
- void onFrameAvailable();
+ QAndroidCameraSession *cameraSession() const { return m_cameraSession; }
private:
- bool initSurfaceTexture();
- void renderFrameToFbo();
- void createGLResources();
-
- QMutex m_mutex;
- void clearSurfaceTexture();
-
+ QAndroidCameraSession *m_cameraSession;
QAbstractVideoSurface *m_surface;
- QSize m_nativeSize;
-
- AndroidSurfaceTexture *m_surfaceTexture;
-
- quint32 m_externalTex;
- QOpenGLFramebufferObject *m_fbo;
- QOpenGLShaderProgram *m_program;
- OpenGLResourcesDeleter *m_glDeleter;
-
- friend class AndroidTextureVideoBuffer;
+ QAndroidTextureVideoOutput *m_textureOutput;
+ QAndroidCameraDataVideoOutput *m_dataOutput;
};
QT_END_NAMESPACE
-#endif // QANDROIDVIDEORENDERCONTROL_H
+#endif // QANDROIDCAMERAVIDEORENDERERCONTROL_H
diff --git a/src/plugins/android/src/mediacapture/qandroidcaptureservice.cpp b/src/plugins/android/src/mediacapture/qandroidcaptureservice.cpp
index e9cdb1e78..d2107e8a5 100644
--- a/src/plugins/android/src/mediacapture/qandroidcaptureservice.cpp
+++ b/src/plugins/android/src/mediacapture/qandroidcaptureservice.cpp
@@ -40,7 +40,7 @@
#include "qandroidvideodeviceselectorcontrol.h"
#include "qandroidaudioinputselectorcontrol.h"
#include "qandroidcamerasession.h"
-#include "qandroidvideorendercontrol.h"
+#include "qandroidcameravideorenderercontrol.h"
#include "qandroidcamerazoomcontrol.h"
#include "qandroidcameraexposurecontrol.h"
#include "qandroidcameraflashcontrol.h"
@@ -196,8 +196,7 @@ QMediaControl *QAndroidCaptureService::requestControl(const char *name)
if (qstrcmp(name, QVideoRendererControl_iid) == 0
&& m_service == QLatin1String(Q_MEDIASERVICE_CAMERA)
&& !m_videoRendererControl) {
- m_videoRendererControl = new QAndroidVideoRendererControl;
- m_cameraSession->setVideoPreview(m_videoRendererControl);
+ m_videoRendererControl = new QAndroidCameraVideoRendererControl(m_cameraSession);
return m_videoRendererControl;
}
@@ -217,7 +216,6 @@ void QAndroidCaptureService::releaseControl(QMediaControl *control)
{
if (control) {
if (control == m_videoRendererControl) {
- m_cameraSession->setVideoPreview(0);
delete m_videoRendererControl;
m_videoRendererControl = 0;
return;
diff --git a/src/plugins/android/src/mediacapture/qandroidcaptureservice.h b/src/plugins/android/src/mediacapture/qandroidcaptureservice.h
index fc84ac124..02f063444 100644
--- a/src/plugins/android/src/mediacapture/qandroidcaptureservice.h
+++ b/src/plugins/android/src/mediacapture/qandroidcaptureservice.h
@@ -46,7 +46,7 @@ class QAndroidCameraInfoControl;
class QAndroidVideoDeviceSelectorControl;
class QAndroidAudioInputSelectorControl;
class QAndroidCameraSession;
-class QAndroidVideoRendererControl;
+class QAndroidCameraVideoRendererControl;
class QAndroidCameraZoomControl;
class QAndroidCameraExposureControl;
class QAndroidCameraFlashControl;
@@ -82,7 +82,7 @@ private:
QAndroidVideoDeviceSelectorControl *m_videoInputControl;
QAndroidAudioInputSelectorControl *m_audioInputControl;
QAndroidCameraSession *m_cameraSession;
- QMediaControl *m_videoRendererControl;
+ QAndroidCameraVideoRendererControl *m_videoRendererControl;
QAndroidCameraZoomControl *m_cameraZoomControl;
QAndroidCameraExposureControl *m_cameraExposureControl;
QAndroidCameraFlashControl *m_cameraFlashControl;
diff --git a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp
index f2ea1b9d7..f02016654 100644
--- a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp
+++ b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp
@@ -37,6 +37,7 @@
#include "qandroidcamerasession.h"
#include "androidmultimediautils.h"
#include "qandroidmultimediautils.h"
+#include "qandroidvideooutput.h"
QT_BEGIN_NAMESPACE
@@ -217,15 +218,31 @@ void QAndroidCaptureSession::start()
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."));
- restartViewfinder();
+ if (m_cameraSession)
+ restartViewfinder();
return;
}
if (!m_mediaRecorder->start()) {
emit error(QMediaRecorder::FormatError, QLatin1String("Unable to start the media recorder."));
- restartViewfinder();
+ if (m_cameraSession)
+ restartViewfinder();
return;
}
@@ -412,13 +429,26 @@ void QAndroidCaptureSession::applySettings()
void QAndroidCaptureSession::updateViewfinder()
{
- m_cameraSession->camera()->stopPreview();
+ m_cameraSession->camera()->stopPreviewSynchronous();
m_cameraSession->adjustViewfinderSize(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);
}
diff --git a/src/plugins/android/src/mediaplayer/mediaplayer.pri b/src/plugins/android/src/mediaplayer/mediaplayer.pri
index c386d996b..9f758a993 100644
--- a/src/plugins/android/src/mediaplayer/mediaplayer.pri
+++ b/src/plugins/android/src/mediaplayer/mediaplayer.pri
@@ -3,9 +3,11 @@ INCLUDEPATH += $$PWD
HEADERS += \
$$PWD/qandroidmediaplayercontrol.h \
$$PWD/qandroidmediaservice.h \
- $$PWD/qandroidmetadatareadercontrol.h
+ $$PWD/qandroidmetadatareadercontrol.h \
+ $$PWD/qandroidmediaplayervideorenderercontrol.h
SOURCES += \
$$PWD/qandroidmediaplayercontrol.cpp \
$$PWD/qandroidmediaservice.cpp \
- $$PWD/qandroidmetadatareadercontrol.cpp
+ $$PWD/qandroidmetadatareadercontrol.cpp \
+ $$PWD/qandroidmediaplayervideorenderercontrol.cpp
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp
index 9a050e7ad..a6258a74d 100644
--- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp
+++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp
@@ -345,7 +345,7 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent,
mReloadingMedia = false;
}
-void QAndroidMediaPlayerControl::setVideoOutput(QObject *videoOutput)
+void QAndroidMediaPlayerControl::setVideoOutput(QAndroidVideoOutput *videoOutput)
{
if (mVideoOutput) {
mMediaPlayer->setDisplay(0);
@@ -353,7 +353,7 @@ void QAndroidMediaPlayerControl::setVideoOutput(QObject *videoOutput)
mVideoOutput->reset();
}
- mVideoOutput = qobject_cast<QAndroidVideoOutput *>(videoOutput);
+ mVideoOutput = videoOutput;
if (!mVideoOutput)
return;
@@ -563,6 +563,7 @@ void QAndroidMediaPlayerControl::onStateChanged(qint32 state)
} else {
onBufferingChanged(100);
}
+ Q_EMIT metaDataUpdated();
setAudioAvailable(true);
flushPendingStates();
break;
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h
index 3f92d809c..a015a6809 100644
--- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h
+++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h
@@ -67,7 +67,7 @@ public:
const QIODevice *mediaStream() const Q_DECL_OVERRIDE;
void setMedia(const QMediaContent &mediaContent, QIODevice *stream) Q_DECL_OVERRIDE;
- void setVideoOutput(QObject *videoOutput);
+ void setVideoOutput(QAndroidVideoOutput *videoOutput);
Q_SIGNALS:
void metaDataUpdated();
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp
new file mode 100644
index 000000000..5dd51c395
--- /dev/null
+++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidmediaplayervideorenderercontrol.h"
+
+#include "qandroidmediaplayercontrol.h"
+#include "qandroidvideooutput.h"
+#include <qabstractvideosurface.h>
+
+QT_BEGIN_NAMESPACE
+
+QAndroidMediaPlayerVideoRendererControl::QAndroidMediaPlayerVideoRendererControl(QAndroidMediaPlayerControl *mediaPlayer, QObject *parent)
+ : QVideoRendererControl(parent)
+ , m_mediaPlayerControl(mediaPlayer)
+ , m_surface(0)
+ , m_textureOutput(new QAndroidTextureVideoOutput(this))
+{
+ m_mediaPlayerControl->setVideoOutput(m_textureOutput);
+}
+
+QAndroidMediaPlayerVideoRendererControl::~QAndroidMediaPlayerVideoRendererControl()
+{
+ m_mediaPlayerControl->setVideoOutput(0);
+}
+
+QAbstractVideoSurface *QAndroidMediaPlayerVideoRendererControl::surface() const
+{
+ return m_surface;
+}
+
+void QAndroidMediaPlayerVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
+{
+ if (m_surface == surface)
+ return;
+
+ m_surface = surface;
+ m_textureOutput->setSurface(m_surface);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.h
new file mode 100644
index 000000000..cfa41980d
--- /dev/null
+++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDMEDIAPLAYERVIDEORENDERERCONTROL_H
+#define QANDROIDMEDIAPLAYERVIDEORENDERERCONTROL_H
+
+#include <qvideorenderercontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidMediaPlayerControl;
+class QAndroidTextureVideoOutput;
+
+class QAndroidMediaPlayerVideoRendererControl : public QVideoRendererControl
+{
+ Q_OBJECT
+public:
+ QAndroidMediaPlayerVideoRendererControl(QAndroidMediaPlayerControl *mediaPlayer, QObject *parent = 0);
+ ~QAndroidMediaPlayerVideoRendererControl() Q_DECL_OVERRIDE;
+
+ QAbstractVideoSurface *surface() const Q_DECL_OVERRIDE;
+ void setSurface(QAbstractVideoSurface *surface) Q_DECL_OVERRIDE;
+
+private:
+ QAndroidMediaPlayerControl *m_mediaPlayerControl;
+ QAbstractVideoSurface *m_surface;
+ QAndroidTextureVideoOutput *m_textureOutput;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMEDIAPLAYERVIDEORENDERERCONTROL_H
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp
index 74943ca64..992bcead2 100644
--- a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp
+++ b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp
@@ -35,7 +35,7 @@
#include "qandroidmediaplayercontrol.h"
#include "qandroidmetadatareadercontrol.h"
-#include "qandroidvideorendercontrol.h"
+#include "qandroidmediaplayervideorenderercontrol.h"
QT_BEGIN_NAMESPACE
@@ -53,9 +53,9 @@ QAndroidMediaService::QAndroidMediaService(QObject *parent)
QAndroidMediaService::~QAndroidMediaService()
{
- delete mMediaControl;
- delete mMetadataControl;
delete mVideoRendererControl;
+ delete mMetadataControl;
+ delete mMediaControl;
}
QMediaControl *QAndroidMediaService::requestControl(const char *name)
@@ -68,8 +68,7 @@ QMediaControl *QAndroidMediaService::requestControl(const char *name)
if (qstrcmp(name, QVideoRendererControl_iid) == 0) {
if (!mVideoRendererControl) {
- mVideoRendererControl = new QAndroidVideoRendererControl;
- mMediaControl->setVideoOutput(mVideoRendererControl);
+ mVideoRendererControl = new QAndroidMediaPlayerVideoRendererControl(mMediaControl);
return mVideoRendererControl;
}
}
@@ -80,7 +79,6 @@ QMediaControl *QAndroidMediaService::requestControl(const char *name)
void QAndroidMediaService::releaseControl(QMediaControl *control)
{
if (control == mVideoRendererControl) {
- mMediaControl->setVideoOutput(0);
delete mVideoRendererControl;
mVideoRendererControl = 0;
}
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.h b/src/plugins/android/src/mediaplayer/qandroidmediaservice.h
index 6babbb15f..798d6ef39 100644
--- a/src/plugins/android/src/mediaplayer/qandroidmediaservice.h
+++ b/src/plugins/android/src/mediaplayer/qandroidmediaservice.h
@@ -40,6 +40,7 @@ QT_BEGIN_NAMESPACE
class QAndroidMediaPlayerControl;
class QAndroidMetaDataReaderControl;
+class QAndroidMediaPlayerVideoRendererControl;
class QAndroidMediaService : public QMediaService
{
@@ -54,7 +55,7 @@ public:
private:
QAndroidMediaPlayerControl *mMediaControl;
QAndroidMetaDataReaderControl *mMetadataControl;
- QMediaControl *mVideoRendererControl;
+ QAndroidMediaPlayerVideoRendererControl *mVideoRendererControl;
};
QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp
index d09a7734f..b0f027ac3 100644
--- a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp
+++ b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp
@@ -37,6 +37,8 @@
#include <QtMultimedia/qmediametadata.h>
#include <qsize.h>
#include <QDate>
+#include <QtConcurrent/qtconcurrentrun.h>
+#include <QtCore/qvector.h>
QT_BEGIN_NAMESPACE
@@ -63,147 +65,178 @@ static const char* qt_ID3GenreNames[] =
"Euro-House", "Dance Hall"
};
+typedef QVector<QAndroidMetaDataReaderControl *> AndroidMetaDataReaders;
+Q_GLOBAL_STATIC(AndroidMetaDataReaders, g_metaDataReaders)
+Q_GLOBAL_STATIC(QMutex, g_metaDataReadersMtx)
+
QAndroidMetaDataReaderControl::QAndroidMetaDataReaderControl(QObject *parent)
: QMetaDataReaderControl(parent)
, m_available(false)
- , m_retriever(new AndroidMediaMetadataRetriever)
{
}
QAndroidMetaDataReaderControl::~QAndroidMetaDataReaderControl()
{
- if (m_retriever) {
- m_retriever->release();
- delete m_retriever;
- }
+ QMutexLocker l(g_metaDataReadersMtx);
+ const int idx = g_metaDataReaders->indexOf(this);
+ if (idx != -1)
+ g_metaDataReaders->remove(idx);
}
bool QAndroidMetaDataReaderControl::isMetaDataAvailable() const
{
- return m_available;
+ const QMutexLocker l(&m_mtx);
+ return m_available && !m_metadata.isEmpty();
}
QVariant QAndroidMetaDataReaderControl::metaData(const QString &key) const
{
+ const QMutexLocker l(&m_mtx);
return m_metadata.value(key);
}
QStringList QAndroidMetaDataReaderControl::availableMetaData() const
{
+ const QMutexLocker l(&m_mtx);
return m_metadata.keys();
}
void QAndroidMetaDataReaderControl::onMediaChanged(const QMediaContent &media)
{
- if (!m_retriever)
- return;
-
+ const QMutexLocker l(&m_mtx);
+ m_metadata.clear();
m_mediaContent = media;
- updateData();
}
void QAndroidMetaDataReaderControl::onUpdateMetaData()
{
- if (!m_retriever || m_mediaContent.isNull())
+ {
+ const QMutexLocker l(g_metaDataReadersMtx);
+ if (!g_metaDataReaders->contains(this))
+ g_metaDataReaders->append(this);
+ }
+
+ const QMutexLocker ml(&m_mtx);
+ if (m_mediaContent.isNull())
return;
- updateData();
+ const QUrl &url = m_mediaContent.canonicalUrl();
+ QtConcurrent::run(&extractMetadata, this, url);
}
-void QAndroidMetaDataReaderControl::updateData()
+void QAndroidMetaDataReaderControl::updateData(const QVariantMap &metadata, const QUrl &url)
{
- m_metadata.clear();
+ const QMutexLocker l(&m_mtx);
- if (!m_mediaContent.isNull()) {
- if (m_retriever->setDataSource(m_mediaContent.canonicalUrl())) {
- QString mimeType = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::MimeType);
- if (!mimeType.isNull())
- m_metadata.insert(QMediaMetaData::MediaType, mimeType);
+ if (m_mediaContent.canonicalUrl() != url)
+ return;
- bool isVideo = !m_retriever->extractMetadata(AndroidMediaMetadataRetriever::HasVideo).isNull()
- || mimeType.startsWith(QStringLiteral("video"));
+ const bool oldAvailable = m_available;
+ m_metadata = metadata;
+ m_available = !m_metadata.isEmpty();
- QString string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Album);
- if (!string.isNull())
- m_metadata.insert(QMediaMetaData::AlbumTitle, string);
+ if (m_available != oldAvailable)
+ Q_EMIT metaDataAvailableChanged(m_available);
- string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::AlbumArtist);
- if (!string.isNull())
- m_metadata.insert(QMediaMetaData::AlbumArtist, string);
+ Q_EMIT metaDataChanged();
+}
- string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Artist);
- if (!string.isNull()) {
- m_metadata.insert(isVideo ? QMediaMetaData::LeadPerformer
- : QMediaMetaData::ContributingArtist,
- string.split('/', QString::SkipEmptyParts));
- }
+void QAndroidMetaDataReaderControl::extractMetadata(QAndroidMetaDataReaderControl *caller,
+ const QUrl &url)
+{
+ QVariantMap metadata;
- string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Author);
- if (!string.isNull())
- m_metadata.insert(QMediaMetaData::Author, string.split('/', QString::SkipEmptyParts));
+ if (!url.isEmpty()) {
+ AndroidMediaMetadataRetriever retriever;
+ if (!retriever.setDataSource(url))
+ return;
- string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Bitrate);
- if (!string.isNull()) {
- m_metadata.insert(isVideo ? QMediaMetaData::VideoBitRate
- : QMediaMetaData::AudioBitRate,
- string.toInt());
- }
+ QString mimeType = retriever.extractMetadata(AndroidMediaMetadataRetriever::MimeType);
+ if (!mimeType.isNull())
+ metadata.insert(QMediaMetaData::MediaType, mimeType);
- string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::CDTrackNumber);
- if (!string.isNull())
- m_metadata.insert(QMediaMetaData::TrackNumber, string.toInt());
-
- string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Composer);
- if (!string.isNull())
- m_metadata.insert(QMediaMetaData::Composer, string.split('/', QString::SkipEmptyParts));
-
- string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Date);
- if (!string.isNull())
- m_metadata.insert(QMediaMetaData::Date, QDateTime::fromString(string, QStringLiteral("yyyyMMddTHHmmss.zzzZ")).date());
-
- string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Duration);
- if (!string.isNull())
- m_metadata.insert(QMediaMetaData::Duration, string.toLongLong());
-
- string = m_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;
- int genreId = string.midRef(1, string.length() - 2).toInt(&ok);
- if (ok && genreId >= 0 && genreId <= 125)
- string = QLatin1String(qt_ID3GenreNames[genreId]);
- }
- m_metadata.insert(QMediaMetaData::Genre, string);
- }
+ 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 = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Title);
- if (!string.isNull())
- m_metadata.insert(QMediaMetaData::Title, string);
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Artist);
+ if (!string.isNull()) {
+ metadata.insert(isVideo ? QMediaMetaData::LeadPerformer
+ : QMediaMetaData::ContributingArtist,
+ string.split('/', QString::SkipEmptyParts));
+ }
+
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Author);
+ if (!string.isNull())
+ metadata.insert(QMediaMetaData::Author, string.split('/', QString::SkipEmptyParts));
+
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Bitrate);
+ if (!string.isNull()) {
+ metadata.insert(isVideo ? QMediaMetaData::VideoBitRate
+ : QMediaMetaData::AudioBitRate,
+ string.toInt());
+ }
- string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::VideoHeight);
- if (!string.isNull()) {
- int height = string.toInt();
- int width = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::VideoWidth).toInt();
- m_metadata.insert(QMediaMetaData::Resolution, QSize(width, height));
+ 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('/', QString::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 = string.midRef(1, string.length() - 2).toInt(&ok);
+ if (ok && genreId >= 0 && genreId <= 125)
+ string = QLatin1String(qt_ID3GenreNames[genreId]);
}
+ metadata.insert(QMediaMetaData::Genre, string);
+ }
- string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Writer);
- if (!string.isNull())
- m_metadata.insert(QMediaMetaData::Writer, string.split('/', QString::SkipEmptyParts));
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Title);
+ if (!string.isNull())
+ metadata.insert(QMediaMetaData::Title, string);
- string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Year);
- if (!string.isNull())
- m_metadata.insert(QMediaMetaData::Year, string.toInt());
+ 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('/', QString::SkipEmptyParts));
+
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Year);
+ if (!string.isNull())
+ metadata.insert(QMediaMetaData::Year, string.toInt());
}
- bool oldAvailable = m_available;
- m_available = !m_metadata.isEmpty();
- if (m_available != oldAvailable)
- Q_EMIT metaDataAvailableChanged(m_available);
+ const QMutexLocker lock(g_metaDataReadersMtx);
+ if (!g_metaDataReaders->contains(caller))
+ return;
- Q_EMIT metaDataChanged();
+ caller->updateData(metadata, url);
}
QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h
index 14fb01ea0..e2e668d5c 100644
--- a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h
+++ b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h
@@ -36,6 +36,7 @@
#include <QMetaDataReaderControl>
#include <qmediacontent.h>
+#include <QMutex>
QT_BEGIN_NAMESPACE
@@ -58,13 +59,13 @@ public Q_SLOTS:
void onUpdateMetaData();
private:
- void updateData();
+ void updateData(const QVariantMap &metadata, const QUrl &url);
+ static void extractMetadata(QAndroidMetaDataReaderControl *caller, const QUrl &url);
+ mutable QMutex m_mtx;
QMediaContent m_mediaContent;
bool m_available;
QVariantMap m_metadata;
-
- AndroidMediaMetadataRetriever *m_retriever;
};
QT_END_NAMESPACE
diff --git a/src/plugins/android/src/qandroidmediaserviceplugin.cpp b/src/plugins/android/src/qandroidmediaserviceplugin.cpp
index 5d35ddf51..bf89badb3 100644
--- a/src/plugins/android/src/qandroidmediaserviceplugin.cpp
+++ b/src/plugins/android/src/qandroidmediaserviceplugin.cpp
@@ -43,6 +43,7 @@
#include "androidcamera.h"
#include "androidmultimediautils.h"
#include "androidmediarecorder.h"
+#include "androidsurfaceview.h"
#include <qdebug.h>
QT_BEGIN_NAMESPACE
@@ -160,7 +161,8 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/)
if (!AndroidMediaPlayer::initJNI(jniEnv) ||
!AndroidCamera::initJNI(jniEnv) ||
- !AndroidMediaRecorder::initJNI(jniEnv)) {
+ !AndroidMediaRecorder::initJNI(jniEnv) ||
+ !AndroidSurfaceHolder::initJNI(jniEnv)) {
return JNI_ERR;
}
diff --git a/src/plugins/android/src/wrappers/jni/androidcamera.cpp b/src/plugins/android/src/wrappers/jni/androidcamera.cpp
index a4acbd8f9..23200462e 100644
--- a/src/plugins/android/src/wrappers/jni/androidcamera.cpp
+++ b/src/plugins/android/src/wrappers/jni/androidcamera.cpp
@@ -33,20 +33,24 @@
#include "androidcamera.h"
#include "androidsurfacetexture.h"
+#include "androidsurfaceview.h"
#include "qandroidmultimediautils.h"
#include <qstringlist.h>
#include <qdebug.h>
-#include <qmutex.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>
QT_BEGIN_NAMESPACE
static const char QtCameraListenerClassName[] = "org/qtproject/qt5/android/multimedia/QtCameraListener";
-static QMutex g_cameraMapMutex;
-typedef QMap<int, AndroidCamera *> CameraMap;
-Q_GLOBAL_STATIC(CameraMap, g_cameraMap)
+
+typedef QHash<int, AndroidCamera *> CameraMap;
+Q_GLOBAL_STATIC(CameraMap, cameras)
+Q_GLOBAL_STATIC(QReadWriteLock, rwLock)
static inline bool exceptionCheckAndClear(JNIEnv *env)
{
@@ -88,43 +92,57 @@ static QJNIObjectPrivate rectToArea(const QRect &rect)
// native method for QtCameraLisener.java
static void notifyAutoFocusComplete(JNIEnv* , jobject, int id, jboolean success)
{
- QMutexLocker locker(&g_cameraMapMutex);
- AndroidCamera *obj = g_cameraMap->value(id, 0);
- if (obj)
- Q_EMIT obj->autoFocusComplete(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)
{
- QMutexLocker locker(&g_cameraMapMutex);
- AndroidCamera *obj = g_cameraMap->value(id, 0);
- if (obj)
- Q_EMIT obj->pictureExposed();
+ 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)
{
- QMutexLocker locker(&g_cameraMapMutex);
- AndroidCamera *obj = g_cameraMap->value(id, 0);
- if (obj) {
- const int arrayLength = env->GetArrayLength(data);
- QByteArray bytes(arrayLength, Qt::Uninitialized);
- env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data());
- Q_EMIT obj->pictureCaptured(bytes);
- }
+ 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)
+static void notifyNewPreviewFrame(JNIEnv *env, jobject, int id, jbyteArray data,
+ int width, int height, int format, int bpl)
{
- QMutexLocker locker(&g_cameraMapMutex);
- AndroidCamera *obj = g_cameraMap->value(id, 0);
- if (obj) {
- const int arrayLength = env->GetArrayLength(data);
- QByteArray bytes(arrayLength, Qt::Uninitialized);
- env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data());
+ QReadLocker locker(rwLock);
+ const auto it = cameras->constFind(id);
+ if (Q_UNLIKELY(it == cameras->cend()))
+ return;
- Q_EMIT obj->newPreviewFrame(bytes, width, height);
- }
+ 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);
}
class AndroidCameraPrivate : public QObject
@@ -149,10 +167,12 @@ public:
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 void updatePreviewSize();
Q_INVOKABLE bool setPreviewTexture(void *surfaceTexture);
+ Q_INVOKABLE bool setPreviewDisplay(void *surfaceHolder);
Q_INVOKABLE bool isZoomSupported();
Q_INVOKABLE int getMaxZoom();
@@ -224,13 +244,16 @@ public:
Q_SIGNALS:
void previewSizeChanged();
void previewStarted();
+ void previewFailedToStart();
void previewStopped();
void autoFocusStarted();
void whiteBalanceChanged();
- void lastPreviewFrameFetched(const QByteArray &preview, int width, int height);
+ void takePictureFailed();
+
+ void lastPreviewFrameFetched(const QVideoFrame &frame);
};
AndroidCamera::AndroidCamera(AndroidCameraPrivate *d, QThread *worker)
@@ -242,12 +265,15 @@ AndroidCamera::AndroidCamera(AndroidCameraPrivate *d, QThread *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);
}
@@ -255,12 +281,11 @@ AndroidCamera::~AndroidCamera()
{
Q_D(AndroidCamera);
if (d->m_camera.isValid()) {
- g_cameraMapMutex.lock();
- g_cameraMap->remove(d->m_cameraId);
- g_cameraMapMutex.unlock();
+ release();
+ QWriteLocker locker(rwLock);
+ cameras->remove(cameraId());
}
- release();
m_worker->exit();
m_worker->wait(5000);
}
@@ -283,9 +308,9 @@ AndroidCamera *AndroidCamera::open(int cameraId)
}
AndroidCamera *q = new AndroidCamera(d, worker);
- g_cameraMapMutex.lock();
- g_cameraMap->insert(cameraId, q);
- g_cameraMapMutex.unlock();
+ QWriteLocker locker(rwLock);
+ cameras->insert(cameraId, q);
+
return q;
}
@@ -361,6 +386,12 @@ void AndroidCamera::setPreviewFormat(ImageFormat fmt)
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);
@@ -392,6 +423,18 @@ bool AndroidCamera::setPreviewTexture(AndroidSurfaceTexture *surfaceTexture)
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;
+}
+
bool AndroidCamera::isZoomSupported()
{
Q_D(AndroidCamera);
@@ -706,6 +749,12 @@ void AndroidCamera::stopPreview()
QMetaObject::invokeMethod(d, "stopPreview");
}
+void AndroidCamera::stopPreviewSynchronous()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "stopPreview", Qt::BlockingQueuedConnection);
+}
+
AndroidCameraPrivate::AndroidCameraPrivate()
: QObject(),
m_parametersMutex(QMutex::Recursive)
@@ -827,7 +876,7 @@ AndroidCamera::ImageFormat AndroidCameraPrivate::getPreviewFormat()
QMutexLocker parametersLocker(&m_parametersMutex);
if (!m_parameters.isValid())
- return AndroidCamera::Unknown;
+ return AndroidCamera::UnknownImageFormat;
return AndroidCamera::ImageFormat(m_parameters.callMethod<jint>("getPreviewFormat"));
}
@@ -843,6 +892,27 @@ void AndroidCameraPrivate::setPreviewFormat(AndroidCamera::ImageFormat fmt)
applyParameters();
}
+QList<AndroidCamera::ImageFormat> AndroidCameraPrivate::getSupportedPreviewFormats()
+{
+ QList<AndroidCamera::ImageFormat> list;
+
+ QMutexLocker parametersLocker(&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;
+}
+
void AndroidCameraPrivate::updatePreviewSize()
{
QMutexLocker parametersLocker(&m_parametersMutex);
@@ -864,6 +934,15 @@ bool AndroidCameraPrivate::setPreviewTexture(void *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);
+}
+
bool AndroidCameraPrivate::isZoomSupported()
{
QMutexLocker parametersLocker(&m_parametersMutex);
@@ -1057,15 +1136,21 @@ void AndroidCameraPrivate::setFocusAreas(const QList<QRect> &areas)
void AndroidCameraPrivate::autoFocus()
{
+ QJNIEnvironmentPrivate env;
+
m_camera.callMethod<void>("autoFocus",
"(Landroid/hardware/Camera$AutoFocusCallback;)V",
m_cameraListener.object());
- emit autoFocusStarted();
+
+ if (!exceptionCheckAndClear(env))
+ emit autoFocusStarted();
}
void AndroidCameraPrivate::cancelAutoFocus()
{
+ QJNIEnvironmentPrivate env;
m_camera.callMethod<void>("cancelAutoFocus");
+ exceptionCheckAndClear(env);
}
bool AndroidCameraPrivate::isAutoExposureLockSupported()
@@ -1314,25 +1399,40 @@ void AndroidCameraPrivate::setJpegQuality(int quality)
void AndroidCameraPrivate::startPreview()
{
+ QJNIEnvironmentPrivate env;
+
setupPreviewFrameCallback();
m_camera.callMethod<void>("startPreview");
- emit previewStarted();
+
+ if (exceptionCheckAndClear(env))
+ emit previewFailedToStart();
+ else
+ emit previewStarted();
}
void AndroidCameraPrivate::stopPreview()
{
+ QJNIEnvironmentPrivate env;
+
m_camera.callMethod<void>("stopPreview");
+
+ exceptionCheckAndClear(env);
emit previewStopped();
}
void AndroidCameraPrivate::takePicture()
{
+ QJNIEnvironmentPrivate env;
+
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()
@@ -1354,15 +1454,25 @@ void AndroidCameraPrivate::fetchLastPreviewFrame()
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()));
- emit lastPreviewFrameFetched(bytes,
- m_cameraListener.callMethod<jint>("previewWidth"),
- m_cameraListener.callMethod<jint>("previewHeight"));
+ 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()
@@ -1407,7 +1517,7 @@ bool AndroidCamera::initJNI(JNIEnv *env)
{"notifyAutoFocusComplete", "(IZ)V", (void *)notifyAutoFocusComplete},
{"notifyPictureExposed", "(I)V", (void *)notifyPictureExposed},
{"notifyPictureCaptured", "(I[B)V", (void *)notifyPictureCaptured},
- {"notifyNewPreviewFrame", "(I[BII)V", (void *)notifyNewPreviewFrame}
+ {"notifyNewPreviewFrame", "(I[BIIII)V", (void *)notifyNewPreviewFrame}
};
if (clazz && env->RegisterNatives(clazz,
diff --git a/src/plugins/android/src/wrappers/jni/androidcamera.h b/src/plugins/android/src/wrappers/jni/androidcamera.h
index 7a8ae8b23..a5e0294c0 100644
--- a/src/plugins/android/src/wrappers/jni/androidcamera.h
+++ b/src/plugins/android/src/wrappers/jni/androidcamera.h
@@ -46,6 +46,7 @@ class QThread;
class AndroidCameraPrivate;
class AndroidSurfaceTexture;
+class AndroidSurfaceHolder;
struct AndroidCameraInfo
{
@@ -67,7 +68,7 @@ public:
};
enum ImageFormat { // same values as in android.graphics.ImageFormat Java class
- Unknown = 0,
+ UnknownImageFormat = 0,
RGB565 = 4,
NV16 = 16,
NV21 = 17,
@@ -95,10 +96,12 @@ public:
ImageFormat getPreviewFormat();
void setPreviewFormat(ImageFormat fmt);
+ QList<ImageFormat> getSupportedPreviewFormats();
QSize previewSize() const;
void setPreviewSize(const QSize &size);
bool setPreviewTexture(AndroidSurfaceTexture *surfaceTexture);
+ bool setPreviewDisplay(AndroidSurfaceHolder *surfaceHolder);
bool isZoomSupported();
int getMaxZoom();
@@ -152,6 +155,7 @@ public:
void startPreview();
void stopPreview();
+ void stopPreviewSynchronous();
void takePicture();
@@ -168,6 +172,7 @@ public:
Q_SIGNALS:
void previewSizeChanged();
void previewStarted();
+ void previewFailedToStart();
void previewStopped();
void autoFocusStarted();
@@ -175,10 +180,11 @@ Q_SIGNALS:
void whiteBalanceChanged();
+ void takePictureFailed();
void pictureExposed();
void pictureCaptured(const QByteArray &data);
- void lastPreviewFrameFetched(const QByteArray &preview, int width, int height);
- void newPreviewFrame(const QByteArray &frame, int width, int height);
+ void lastPreviewFrameFetched(const QVideoFrame &frame);
+ void newPreviewFrame(const QVideoFrame &frame);
private:
AndroidCamera(AndroidCameraPrivate *d, QThread *worker);
@@ -188,6 +194,8 @@ private:
QScopedPointer<QThread> m_worker;
};
+Q_DECLARE_METATYPE(AndroidCamera::ImageFormat)
+
QT_END_NAMESPACE
#endif // ANDROIDCAMERA_H
diff --git a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp
index 56ac0e0ac..f67428b6e 100644
--- a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp
+++ b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp
@@ -60,6 +60,7 @@ AndroidMediaMetadataRetriever::AndroidMediaMetadataRetriever()
AndroidMediaMetadataRetriever::~AndroidMediaMetadataRetriever()
{
+ release();
}
QString AndroidMediaMetadataRetriever::extractMetadata(MetadataKey key)
diff --git a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h
index 01a98490b..1b4a09bb7 100644
--- a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h
+++ b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h
@@ -71,10 +71,10 @@ public:
~AndroidMediaMetadataRetriever();
QString extractMetadata(MetadataKey key);
- void release();
bool setDataSource(const QUrl &url);
private:
+ void release();
QJNIObjectPrivate m_metadataRetriever;
};
diff --git a/src/plugins/android/src/wrappers/jni/androidmediaplayer.cpp b/src/plugins/android/src/wrappers/jni/androidmediaplayer.cpp
index 8fbecbc73..3267d838b 100644
--- a/src/plugins/android/src/wrappers/jni/androidmediaplayer.cpp
+++ b/src/plugins/android/src/wrappers/jni/androidmediaplayer.cpp
@@ -37,29 +37,34 @@
#include <QtCore/private/qjni_p.h>
#include <QtCore/private/qjnihelpers_p.h>
#include "androidsurfacetexture.h"
-#include <QMap>
+#include <QVector>
+#include <QReadWriteLock>
static const char QtAndroidMediaPlayerClassName[] = "org/qtproject/qt5/android/multimedia/QtAndroidMediaPlayer";
-typedef QMap<jlong, AndroidMediaPlayer *> MediaPlayerMap;
-Q_GLOBAL_STATIC(MediaPlayerMap, mediaPlayers)
+typedef QVector<AndroidMediaPlayer *> MediaPlayerList;
+Q_GLOBAL_STATIC(MediaPlayerList, mediaPlayers)
+Q_GLOBAL_STATIC(QReadWriteLock, rwLock)
QT_BEGIN_NAMESPACE
AndroidMediaPlayer::AndroidMediaPlayer()
: QObject()
{
-
+ QWriteLocker locker(rwLock);
const jlong id = reinterpret_cast<jlong>(this);
mMediaPlayer = QJNIObjectPrivate(QtAndroidMediaPlayerClassName,
"(Landroid/app/Activity;J)V",
QtAndroidPrivate::activity(),
id);
- (*mediaPlayers)[id] = this;
+ mediaPlayers->append(this);
}
AndroidMediaPlayer::~AndroidMediaPlayer()
{
- mediaPlayers->remove(reinterpret_cast<jlong>(this));
+ QWriteLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(this);
+ Q_ASSERT(i != -1);
+ mediaPlayers->remove(i);
}
void AndroidMediaPlayer::release()
@@ -154,66 +159,72 @@ static void onErrorNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlon
{
Q_UNUSED(env);
Q_UNUSED(thiz);
- AndroidMediaPlayer *const mp = (*mediaPlayers)[id];
- if (!mp)
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
return;
- Q_EMIT mp->error(what, extra);
+ Q_EMIT (*mediaPlayers)[i]->error(what, extra);
}
static void onBufferingUpdateNative(JNIEnv *env, jobject thiz, jint percent, jlong id)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
- AndroidMediaPlayer *const mp = (*mediaPlayers)[id];
- if (!mp)
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
return;
- Q_EMIT mp->bufferingChanged(percent);
+ Q_EMIT (*mediaPlayers)[i]->bufferingChanged(percent);
}
static void onProgressUpdateNative(JNIEnv *env, jobject thiz, jint progress, jlong id)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
- AndroidMediaPlayer *const mp = (*mediaPlayers)[id];
- if (!mp)
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
return;
- Q_EMIT mp->progressChanged(progress);
+ Q_EMIT (*mediaPlayers)[i]->progressChanged(progress);
}
static void onDurationChangedNative(JNIEnv *env, jobject thiz, jint duration, jlong id)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
- AndroidMediaPlayer *const mp = (*mediaPlayers)[id];
- if (!mp)
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
return;
- Q_EMIT mp->durationChanged(duration);
+ 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);
- AndroidMediaPlayer *const mp = (*mediaPlayers)[id];
- if (!mp)
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
return;
- Q_EMIT mp->info(what, extra);
+ Q_EMIT (*mediaPlayers)[i]->info(what, extra);
}
static void onStateChangedNative(JNIEnv *env, jobject thiz, jint state, jlong id)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
- AndroidMediaPlayer *const mp = (*mediaPlayers)[id];
- if (!mp)
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
return;
- Q_EMIT mp->stateChanged(state);
+ Q_EMIT (*mediaPlayers)[i]->stateChanged(state);
}
static void onVideoSizeChangedNative(JNIEnv *env,
@@ -224,11 +235,12 @@ static void onVideoSizeChangedNative(JNIEnv *env,
{
Q_UNUSED(env);
Q_UNUSED(thiz);
- AndroidMediaPlayer *const mp = (*mediaPlayers)[id];
- if (!mp)
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
return;
- Q_EMIT mp->videoSizeChanged(width, height);
+ Q_EMIT (*mediaPlayers)[i]->videoSizeChanged(width, height);
}
bool AndroidMediaPlayer::initJNI(JNIEnv *env)
diff --git a/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp b/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp
index fa32f31ef..34063056f 100644
--- a/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp
+++ b/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp
@@ -34,6 +34,8 @@
#include "androidmediarecorder.h"
#include "androidcamera.h"
+#include "androidsurfacetexture.h"
+#include "androidsurfaceview.h"
#include <QtCore/private/qjni_p.h>
#include <qmap.h>
@@ -339,6 +341,41 @@ void AndroidMediaRecorder::setOutputFile(const QString &path)
}
}
+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,
diff --git a/src/plugins/android/src/wrappers/jni/androidmediarecorder.h b/src/plugins/android/src/wrappers/jni/androidmediarecorder.h
index 1aa83a201..95b48ed47 100644
--- a/src/plugins/android/src/wrappers/jni/androidmediarecorder.h
+++ b/src/plugins/android/src/wrappers/jni/androidmediarecorder.h
@@ -41,6 +41,8 @@
QT_BEGIN_NAMESPACE
class AndroidCamera;
+class AndroidSurfaceTexture;
+class AndroidSurfaceHolder;
class AndroidCamcorderProfile
{
@@ -149,6 +151,9 @@ public:
void setOutputFormat(OutputFormat format);
void setOutputFile(const QString &path);
+ void setSurfaceTexture(AndroidSurfaceTexture *texture);
+ void setSurfaceHolder(AndroidSurfaceHolder *holder);
+
static bool initJNI(JNIEnv *env);
Q_SIGNALS:
diff --git a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp b/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp
index ffa37d7d4..9a25b7e28 100644
--- a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp
+++ b/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp
@@ -78,8 +78,8 @@ AndroidSurfaceTexture::AndroidSurfaceTexture(unsigned int texName)
AndroidSurfaceTexture::~AndroidSurfaceTexture()
{
- if (QtAndroidPrivate::androidSdkVersion() > 13 && m_surfaceView.isValid())
- m_surfaceView.callMethod<void>("release");
+ if (QtAndroidPrivate::androidSdkVersion() > 13 && m_surface.isValid())
+ m_surface.callMethod<void>("release");
if (m_surfaceTexture.isValid()) {
release();
@@ -124,21 +124,23 @@ jobject AndroidSurfaceTexture::surfaceTexture()
return m_surfaceTexture.object();
}
-jobject AndroidSurfaceTexture::surfaceView()
+jobject AndroidSurfaceTexture::surface()
{
- return m_surfaceView.object();
+ 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_surfaceView = QJNIObjectPrivate("android/view/Surface",
- "(Landroid/graphics/SurfaceTexture;)V",
- m_surfaceTexture.object());
-
m_surfaceHolder = QJNIObjectPrivate("org/qtproject/qt5/android/multimedia/QtSurfaceTextureHolder",
"(Landroid/view/Surface;)V",
- m_surfaceView.object());
+ surface());
}
return m_surfaceHolder.object();
diff --git a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h b/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h
index 2618ed6c9..ac2af694e 100644
--- a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h
+++ b/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h
@@ -50,7 +50,7 @@ public:
int textureID() const { return m_texID; }
jobject surfaceTexture();
- jobject surfaceView();
+ jobject surface();
jobject surfaceHolder();
inline bool isValid() const { return m_surfaceTexture.isValid(); }
@@ -66,7 +66,7 @@ Q_SIGNALS:
private:
int m_texID;
QJNIObjectPrivate m_surfaceTexture;
- QJNIObjectPrivate m_surfaceView;
+ QJNIObjectPrivate m_surface;
QJNIObjectPrivate m_surfaceHolder;
};
diff --git a/src/plugins/android/src/wrappers/jni/androidsurfaceview.cpp b/src/plugins/android/src/wrappers/jni/androidsurfaceview.cpp
new file mode 100644
index 000000000..67560baf4
--- /dev/null
+++ b/src/plugins/android/src/wrappers/jni/androidsurfaceview.cpp
@@ -0,0 +1,204 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "androidsurfaceview.h"
+
+#include <QtCore/private/qjnihelpers_p.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qmutex.h>
+#include <QtGui/qwindow.h>
+
+QT_BEGIN_NAMESPACE
+
+static const char QtSurfaceHolderCallbackClassName[] = "org/qtproject/qt5/android/multimedia/QtSurfaceHolderCallback";
+typedef QVector<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)
+{
+ setAutoDelete(false);
+ QtAndroidPrivate::runOnUiThread(this, QJNIEnvironmentPrivate());
+}
+
+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);
+}
+
+bool AndroidSurfaceView::event(QEvent *e)
+{
+ if (e->type() == QEvent::User) {
+ 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);
+ }
+ }
+
+ return true;
+ }
+
+ return QObject::event(e);
+}
+
+// Called on the Android UI thread.
+void AndroidSurfaceView::run()
+{
+ m_surfaceView = QJNIObjectPrivate("android/view/SurfaceView",
+ "(Landroid/content/Context;)V",
+ QtAndroidPrivate::activity());
+ QCoreApplication::postEvent(this, new QEvent(QEvent::User));
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/android/src/wrappers/jni/androidsurfaceview.h b/src/plugins/android/src/wrappers/jni/androidsurfaceview.h
new file mode 100644
index 000000000..661a5959f
--- /dev/null
+++ b/src/plugins/android/src/wrappers/jni/androidsurfaceview.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ANDROIDSURFACEVIEW_H
+#define ANDROIDSURFACEVIEW_H
+
+#include <QtCore/private/qjni_p.h>
+#include <qrect.h>
+#include <QtCore/qrunnable.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindow;
+
+class AndroidSurfaceHolder : public QObject
+{
+ Q_OBJECT
+public:
+ ~AndroidSurfaceHolder();
+
+ jobject surfaceHolder() const;
+ bool isSurfaceCreated() const;
+
+ static bool initJNI(JNIEnv *env);
+
+Q_SIGNALS:
+ void surfaceCreated();
+
+private:
+ AndroidSurfaceHolder(QJNIObjectPrivate object);
+
+ static void handleSurfaceCreated(JNIEnv*, jobject, jlong id);
+ static void handleSurfaceDestroyed(JNIEnv*, jobject, jlong id);
+
+ QJNIObjectPrivate m_surfaceHolder;
+ bool m_surfaceCreated;
+
+ friend class AndroidSurfaceView;
+};
+
+class AndroidSurfaceView : public QObject, public QRunnable
+{
+ Q_OBJECT
+public:
+ AndroidSurfaceView();
+ ~AndroidSurfaceView();
+
+ AndroidSurfaceHolder *holder() const;
+
+ void setVisible(bool v);
+ void setGeometry(int x, int y, int width, int height);
+
+ bool event(QEvent *);
+
+Q_SIGNALS:
+ void surfaceCreated();
+
+protected:
+ void run() override;
+
+private:
+ QJNIObjectPrivate m_surfaceView;
+ QWindow *m_window;
+ AndroidSurfaceHolder *m_surfaceHolder;
+ int m_pendingVisible;
+ QRect m_pendingGeometry;
+};
+
+QT_END_NAMESPACE
+
+#endif // ANDROIDSURFACEVIEW_H
diff --git a/src/plugins/android/src/wrappers/jni/jni.pri b/src/plugins/android/src/wrappers/jni/jni.pri
index e96baff1c..930d7e922 100644
--- a/src/plugins/android/src/wrappers/jni/jni.pri
+++ b/src/plugins/android/src/wrappers/jni/jni.pri
@@ -8,7 +8,8 @@ HEADERS += \
$$PWD/androidmediametadataretriever.h \
$$PWD/androidcamera.h \
$$PWD/androidmultimediautils.h \
- $$PWD/androidmediarecorder.h
+ $$PWD/androidmediarecorder.h \
+ $$PWD/androidsurfaceview.h
SOURCES += \
$$PWD/androidmediaplayer.cpp \
@@ -16,4 +17,5 @@ SOURCES += \
$$PWD/androidmediametadataretriever.cpp \
$$PWD/androidcamera.cpp \
$$PWD/androidmultimediautils.cpp \
- $$PWD/androidmediarecorder.cpp
+ $$PWD/androidmediarecorder.cpp \
+ $$PWD/androidsurfaceview.cpp
diff --git a/src/plugins/avfoundation/camera/avfcamerasession.h b/src/plugins/avfoundation/camera/avfcamerasession.h
index 838234522..13a8a35c5 100644
--- a/src/plugins/avfoundation/camera/avfcamerasession.h
+++ b/src/plugins/avfoundation/camera/avfcamerasession.h
@@ -105,8 +105,8 @@ Q_SIGNALS:
private:
static void updateCameraDevices();
void attachVideoInputDevice();
- void applyImageEncoderSettings();
- void applyViewfinderSettings();
+ bool applyImageEncoderSettings();
+ bool applyViewfinderSettings();
static int m_defaultCameraIndex;
static QList<AVFCameraInfo> m_cameraDevices;
diff --git a/src/plugins/avfoundation/camera/avfcamerasession.mm b/src/plugins/avfoundation/camera/avfcamerasession.mm
index 43ab1d149..e0f18c114 100644
--- a/src/plugins/avfoundation/camera/avfcamerasession.mm
+++ b/src/plugins/avfoundation/camera/avfcamerasession.mm
@@ -285,10 +285,23 @@ void AVFCameraSession::setState(QCamera::State newState)
Q_EMIT readyToConfigureConnections();
m_defaultCodec = 0;
defaultCodec();
- applyImageEncoderSettings();
- applyViewfinderSettings();
+
+ bool activeFormatSet = applyImageEncoderSettings();
+ activeFormatSet |= 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) {
@@ -357,27 +370,32 @@ void AVFCameraSession::attachVideoInputDevice()
}
}
-void AVFCameraSession::applyImageEncoderSettings()
+bool AVFCameraSession::applyImageEncoderSettings()
{
if (AVFImageEncoderControl *control = m_service->imageEncoderControl())
- control->applySettings();
+ return control->applySettings();
+
+ return false;
}
-void AVFCameraSession::applyViewfinderSettings()
+bool AVFCameraSession::applyViewfinderSettings()
{
if (AVFCameraViewfinderSettingsControl2 *vfControl = m_service->viewfinderSettingsControl2()) {
QCameraViewfinderSettings vfSettings(vfControl->requestedSettings());
+ // Viewfinder and image capture solutions must be the same, if an image capture
+ // resolution is set, it takes precedence over the viewfinder resolution.
if (AVFImageEncoderControl *imControl = m_service->imageEncoderControl()) {
- const QSize imageResolution(imControl->imageSettings().resolution());
+ const QSize imageResolution(imControl->requestedSettings().resolution());
if (!imageResolution.isNull() && imageResolution.isValid()) {
vfSettings.setResolution(imageResolution);
vfControl->setViewfinderSettings(vfSettings);
- return;
}
}
- vfControl->applySettings();
+ return vfControl->applySettings();
}
+
+ return false;
}
void AVFCameraSession::addProbe(AVFMediaVideoProbeControl *probe)
diff --git a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h
index cf2f512a7..9a5bbd5de 100644
--- a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h
+++ b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h
@@ -76,17 +76,13 @@ private:
AVCaptureDeviceFormat *findBestFormatMatch(const QCameraViewfinderSettings &settings) const;
QVector<QVideoFrame::PixelFormat> viewfinderPixelFormats() const;
bool convertPixelFormatIfSupported(QVideoFrame::PixelFormat format, unsigned &avfFormat) const;
- void applySettings();
+ bool applySettings();
QCameraViewfinderSettings requestedSettings() const;
- // Aux. function to extract things like captureDevice, videoOutput, etc.
- bool updateAVFoundationObjects() const;
+
+ AVCaptureConnection *videoConnection() const;
AVFCameraService *m_service;
- mutable AVFCameraSession *m_session;
QCameraViewfinderSettings m_settings;
- mutable AVCaptureDevice *m_captureDevice;
- mutable AVCaptureVideoDataOutput *m_videoOutput;
- mutable AVCaptureConnection *m_videoConnection;
};
class AVFCameraViewfinderSettingsControl : public QCameraViewfinderSettingsControl
diff --git a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm
index c5da1c343..3c20801e5 100644
--- a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm
+++ b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm
@@ -206,7 +206,6 @@ void qt_set_framerate_limits(AVCaptureDevice *captureDevice,
AVFPSRange qt_current_framerates(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection)
{
Q_ASSERT(captureDevice);
- Q_ASSERT(videoConnection);
AVFPSRange fps;
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
@@ -234,7 +233,8 @@ AVFPSRange qt_current_framerates(AVCaptureDevice *captureDevice, AVCaptureConnec
#else // OSX < 10.7 or iOS < 7.0
{
#endif // QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
- fps = qt_connection_framerates(videoConnection);
+ if (videoConnection)
+ fps = qt_connection_framerates(videoConnection);
}
return fps;
@@ -244,24 +244,20 @@ void qt_set_framerate_limits(AVCaptureDevice *captureDevice, AVCaptureConnection
const QCameraViewfinderSettings &settings)
{
Q_ASSERT(captureDevice);
- Q_ASSERT(videoConnection);
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_9, QSysInfo::MV_IOS_7_0))
qt_set_framerate_limits(captureDevice, settings);
else
- qt_set_framerate_limits(videoConnection, settings);
-#else
- qt_set_framerate_limits(videoConnection, settings);
#endif
+ if (videoConnection)
+ qt_set_framerate_limits(videoConnection, settings);
+
}
} // Unnamed namespace.
AVFCameraViewfinderSettingsControl2::AVFCameraViewfinderSettingsControl2(AVFCameraService *service)
- : m_service(service),
- m_captureDevice(0),
- m_videoOutput(0),
- m_videoConnection(0)
+ : m_service(service)
{
Q_ASSERT(service);
}
@@ -270,8 +266,9 @@ QList<QCameraViewfinderSettings> AVFCameraViewfinderSettingsControl2::supportedV
{
QList<QCameraViewfinderSettings> supportedSettings;
- if (!updateAVFoundationObjects()) {
- qDebugCamera() << Q_FUNC_INFO << "no capture device or video output found";
+ AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
+ if (!captureDevice) {
+ qDebugCamera() << Q_FUNC_INFO << "no capture device found";
return supportedSettings;
}
@@ -281,15 +278,16 @@ QList<QCameraViewfinderSettings> AVFCameraViewfinderSettingsControl2::supportedV
if (!pixelFormats.size())
pixelFormats << QVideoFrame::Format_Invalid; // The default value.
+
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_7, QSysInfo::MV_IOS_7_0)) {
- if (!m_captureDevice.formats || !m_captureDevice.formats.count) {
+ 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(m_captureDevice,
- m_session->defaultCodec()));
+ 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];
@@ -320,15 +318,18 @@ QList<QCameraViewfinderSettings> AVFCameraViewfinderSettingsControl2::supportedV
#else
{
#endif
- // TODO: resolution and PAR.
- framerates << qt_connection_framerates(m_videoConnection);
- for (int i = 0; i < pixelFormats.size(); ++i) {
- for (int j = 0; j < framerates.size(); ++j) {
- QCameraViewfinderSettings newSet;
- newSet.setPixelFormat(pixelFormats[i]);
- newSet.setMinimumFrameRate(framerates[j].first);
- newSet.setMaximumFrameRate(framerates[j].second);
- supportedSettings << newSet;
+ AVCaptureConnection *connection = videoConnection();
+ if (connection) {
+ // TODO: resolution and PAR.
+ framerates << qt_connection_framerates(connection);
+ for (int i = 0; i < pixelFormats.size(); ++i) {
+ for (int j = 0; j < framerates.size(); ++j) {
+ QCameraViewfinderSettings newSet;
+ newSet.setPixelFormat(pixelFormats[i]);
+ newSet.setMinimumFrameRate(framerates[j].first);
+ newSet.setMaximumFrameRate(framerates[j].second);
+ supportedSettings << newSet;
+ }
}
}
}
@@ -340,20 +341,21 @@ QCameraViewfinderSettings AVFCameraViewfinderSettingsControl2::viewfinderSetting
{
QCameraViewfinderSettings settings;
- if (!updateAVFoundationObjects()) {
- qDebugCamera() << Q_FUNC_INFO << "no capture device or video output found";
+ AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
+ if (!captureDevice) {
+ qDebugCamera() << Q_FUNC_INFO << "no capture device found";
return settings;
}
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_7, QSysInfo::MV_IOS_7_0)) {
- if (!m_captureDevice.activeFormat) {
+ if (!captureDevice.activeFormat) {
qDebugCamera() << Q_FUNC_INFO << "no active capture device format";
return settings;
}
- const QSize res(qt_device_format_resolution(m_captureDevice.activeFormat));
- const QSize par(qt_device_format_pixel_aspect_ratio(m_captureDevice.activeFormat));
+ 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;
@@ -364,12 +366,14 @@ QCameraViewfinderSettings AVFCameraViewfinderSettingsControl2::viewfinderSetting
}
#endif
// TODO: resolution and PAR before 7.0.
- const AVFPSRange fps = qt_current_framerates(m_captureDevice, m_videoConnection);
+ const AVFPSRange fps = qt_current_framerates(captureDevice, videoConnection());
settings.setMinimumFrameRate(fps.first);
settings.setMaximumFrameRate(fps.second);
- if (NSObject *obj = [m_videoOutput.videoSettings objectForKey:(id)kCVPixelBufferPixelFormatTypeKey]) {
- if ([obj isKindOfClass:[NSNumber class]]) {
+ AVCaptureVideoDataOutput *videoOutput = m_service->videoOutput() ? m_service->videoOutput()->videoDataOutput() : 0;
+ 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]));
}
@@ -380,11 +384,6 @@ QCameraViewfinderSettings AVFCameraViewfinderSettingsControl2::viewfinderSetting
void AVFCameraViewfinderSettingsControl2::setViewfinderSettings(const QCameraViewfinderSettings &settings)
{
- if (settings.isNull()) {
- qDebugCamera() << Q_FUNC_INFO << "empty viewfinder settings";
- return;
- }
-
if (m_settings == settings)
return;
@@ -449,17 +448,19 @@ bool AVFCameraViewfinderSettingsControl2::CVPixelFormatFromQtFormat(QVideoFrame:
AVCaptureDeviceFormat *AVFCameraViewfinderSettingsControl2::findBestFormatMatch(const QCameraViewfinderSettings &settings) const
{
+ AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
+ if (!captureDevice || settings.isNull())
+ return nil;
+
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_7, QSysInfo::MV_IOS_7_0)) {
- Q_ASSERT(m_captureDevice);
- Q_ASSERT(m_session);
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(m_captureDevice, resolution,
- m_session->defaultCodec());
+ return qt_find_best_resolution_match(captureDevice, resolution,
+ m_service->session()->defaultCodec());
}
// No resolution requested, what about framerates?
@@ -472,22 +473,28 @@ AVCaptureDeviceFormat *AVFCameraViewfinderSettingsControl2::findBestFormatMatch(
const qreal minFPS(settings.minimumFrameRate());
const qreal maxFPS(settings.maximumFrameRate());
if (minFPS || maxFPS)
- return qt_find_best_framerate_match(m_captureDevice, maxFPS ? maxFPS : minFPS,
- m_session->defaultCodec());
+ return qt_find_best_framerate_match(captureDevice, maxFPS ? maxFPS : minFPS,
+ m_service->session()->defaultCodec());
// 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.
}
#endif
+
return nil;
}
QVector<QVideoFrame::PixelFormat> AVFCameraViewfinderSettingsControl2::viewfinderPixelFormats() const
{
- Q_ASSERT(m_videoOutput);
-
QVector<QVideoFrame::PixelFormat> qtFormats;
- NSArray *pixelFormats = [m_videoOutput availableVideoCVPixelFormatTypes];
+
+ AVCaptureVideoDataOutput *videoOutput = m_service->videoOutput() ? m_service->videoOutput()->videoDataOutput() : 0;
+ 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]])
@@ -508,17 +515,19 @@ QVector<QVideoFrame::PixelFormat> AVFCameraViewfinderSettingsControl2::viewfinde
bool AVFCameraViewfinderSettingsControl2::convertPixelFormatIfSupported(QVideoFrame::PixelFormat qtFormat,
unsigned &avfFormat)const
{
- Q_ASSERT(m_videoOutput);
+ AVCaptureVideoDataOutput *videoOutput = m_service->videoOutput() ? m_service->videoOutput()->videoDataOutput() : 0;
+ if (!videoOutput)
+ return false;
unsigned conv = 0;
if (!CVPixelFormatFromQtFormat(qtFormat, conv))
return false;
- NSArray *formats = [m_videoOutput availableVideoCVPixelFormatTypes];
+ NSArray *formats = [videoOutput availableVideoCVPixelFormatTypes];
if (!formats || !formats.count)
return false;
- if (m_service->videoOutput() && m_service->videoOutput()->surface()) {
+ if (m_service->videoOutput()->surface()) {
const QAbstractVideoSurface *surface = m_service->videoOutput()->surface();
if (!surface->supportedPixelFormats().contains(qtFormat))
return false;
@@ -539,31 +548,30 @@ bool AVFCameraViewfinderSettingsControl2::convertPixelFormatIfSupported(QVideoFr
return found;
}
-void AVFCameraViewfinderSettingsControl2::applySettings()
+bool AVFCameraViewfinderSettingsControl2::applySettings()
{
- if (m_settings.isNull())
- return;
+ if (m_service->session()->state() != QCamera::LoadedState &&
+ m_service->session()->state() != QCamera::ActiveState) {
+ return false;
+ }
- if (!updateAVFoundationObjects())
- return;
+ AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
+ if (!captureDevice)
+ return false;
- if (m_session->state() != QCamera::LoadedState &&
- m_session->state() != QCamera::ActiveState) {
- return;
- }
+ bool activeFormatChanged = false;
- NSMutableDictionary *videoSettings = [NSMutableDictionary dictionaryWithCapacity:1];
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
AVCaptureDeviceFormat *match = findBestFormatMatch(m_settings);
if (match) {
- if (match != m_captureDevice.activeFormat) {
- const AVFConfigurationLock lock(m_captureDevice);
- if (!lock) {
+ if (match != captureDevice.activeFormat) {
+ const AVFConfigurationLock lock(captureDevice);
+ if (lock) {
+ captureDevice.activeFormat = match;
+ activeFormatChanged = true;
+ } else {
qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
- return;
}
-
- m_captureDevice.activeFormat = match;
}
} else {
qDebugCamera() << Q_FUNC_INFO << "matching device format not found";
@@ -571,43 +579,48 @@ void AVFCameraViewfinderSettingsControl2::applySettings()
}
#endif
- unsigned avfPixelFormat = 0;
- if (!convertPixelFormatIfSupported(m_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();
- QVideoFrame::PixelFormat pickedFormat = deviceFormats.first();
-
- QAbstractVideoSurface *surface = m_service->videoOutput() ? m_service->videoOutput()->surface()
- : 0;
- if (surface) {
- if (m_service->videoOutput()->supportsTextures()) {
- pickedFormat = QVideoFrame::Format_ARGB32;
- } else {
- QList<QVideoFrame::PixelFormat> surfaceFormats = m_service->videoOutput()->surface()->supportedPixelFormats();
-
- for (int i = 0; i < surfaceFormats.count(); ++i) {
- const QVideoFrame::PixelFormat surfaceFormat = surfaceFormats.at(i);
- if (deviceFormats.contains(surfaceFormat)) {
- pickedFormat = surfaceFormat;
- break;
+ AVCaptureVideoDataOutput *videoOutput = m_service->videoOutput() ? m_service->videoOutput()->videoDataOutput() : 0;
+ if (videoOutput) {
+ unsigned avfPixelFormat = 0;
+ if (!convertPixelFormatIfSupported(m_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();
+ QVideoFrame::PixelFormat pickedFormat = deviceFormats.first();
+
+ QAbstractVideoSurface *surface = m_service->videoOutput()->surface();
+ if (surface) {
+ if (m_service->videoOutput()->supportsTextures()) {
+ pickedFormat = QVideoFrame::Format_ARGB32;
+ } else {
+ QList<QVideoFrame::PixelFormat> surfaceFormats = surface->supportedPixelFormats();
+
+ 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);
- }
+ CVPixelFormatFromQtFormat(pickedFormat, avfPixelFormat);
+ }
- if (avfPixelFormat != 0) {
- [videoSettings setObject:[NSNumber numberWithUnsignedInt:avfPixelFormat]
- forKey:(id)kCVPixelBufferPixelFormatTypeKey];
+ if (avfPixelFormat != 0) {
+ NSMutableDictionary *videoSettings = [NSMutableDictionary dictionaryWithCapacity:1];
+ [videoSettings setObject:[NSNumber numberWithUnsignedInt:avfPixelFormat]
+ forKey:(id)kCVPixelBufferPixelFormatTypeKey];
- m_videoOutput.videoSettings = videoSettings;
+ videoOutput.videoSettings = videoSettings;
+ }
}
- qt_set_framerate_limits(m_captureDevice, m_videoConnection, m_settings);
+ qt_set_framerate_limits(captureDevice, videoConnection(), m_settings);
+
+ return activeFormatChanged;
}
QCameraViewfinderSettings AVFCameraViewfinderSettingsControl2::requestedSettings() const
@@ -615,33 +628,12 @@ QCameraViewfinderSettings AVFCameraViewfinderSettingsControl2::requestedSettings
return m_settings;
}
-bool AVFCameraViewfinderSettingsControl2::updateAVFoundationObjects() const
+AVCaptureConnection *AVFCameraViewfinderSettingsControl2::videoConnection() const
{
- m_session = 0;
- m_captureDevice = 0;
- m_videoOutput = 0;
- m_videoConnection = 0;
-
- if (!m_service->session())
- return false;
-
- if (!m_service->session()->videoCaptureDevice())
- return false;
-
if (!m_service->videoOutput() || !m_service->videoOutput()->videoDataOutput())
- return false;
-
- AVCaptureVideoDataOutput *output = m_service->videoOutput()->videoDataOutput();
- AVCaptureConnection *connection = [output connectionWithMediaType:AVMediaTypeVideo];
- if (!connection)
- return false;
-
- m_session = m_service->session();
- m_captureDevice = m_session->videoCaptureDevice();
- m_videoOutput = output;
- m_videoConnection = connection;
+ return nil;
- return true;
+ return [m_service->videoOutput()->videoDataOutput() connectionWithMediaType:AVMediaTypeVideo];
}
AVFCameraViewfinderSettingsControl::AVFCameraViewfinderSettingsControl(AVFCameraService *service)
diff --git a/src/plugins/avfoundation/camera/avfimageencodercontrol.h b/src/plugins/avfoundation/camera/avfimageencodercontrol.h
index fcb665a03..d3af77ffd 100644
--- a/src/plugins/avfoundation/camera/avfimageencodercontrol.h
+++ b/src/plugins/avfoundation/camera/avfimageencodercontrol.h
@@ -62,11 +62,13 @@ public:
QImageEncoderSettings imageSettings() const Q_DECL_OVERRIDE;
void setImageSettings(const QImageEncoderSettings &settings) Q_DECL_OVERRIDE;
+ QImageEncoderSettings requestedSettings() const;
+
private:
AVFCameraService *m_service;
QImageEncoderSettings m_settings;
- void applySettings();
+ bool applySettings();
bool videoCaptureDeviceIsValid() const;
};
diff --git a/src/plugins/avfoundation/camera/avfimageencodercontrol.mm b/src/plugins/avfoundation/camera/avfimageencodercontrol.mm
index 36050c3a2..e2eb0bd01 100644
--- a/src/plugins/avfoundation/camera/avfimageencodercontrol.mm
+++ b/src/plugins/avfoundation/camera/avfimageencodercontrol.mm
@@ -115,6 +115,11 @@ QList<QSize> AVFImageEncoderControl::supportedResolutions(const QImageEncoderSet
return resolutions;
}
+QImageEncoderSettings AVFImageEncoderControl::requestedSettings() const
+{
+ return m_settings;
+}
+
QImageEncoderSettings AVFImageEncoderControl::imageSettings() const
{
QImageEncoderSettings settings;
@@ -163,40 +168,40 @@ QImageEncoderSettings AVFImageEncoderControl::imageSettings() const
void AVFImageEncoderControl::setImageSettings(const QImageEncoderSettings &settings)
{
- if (m_settings == settings || settings.isNull())
+ if (m_settings == settings)
return;
m_settings = settings;
applySettings();
}
-void AVFImageEncoderControl::applySettings()
+bool AVFImageEncoderControl::applySettings()
{
if (!videoCaptureDeviceIsValid())
- return;
+ return false;
AVFCameraSession *session = m_service->session();
if (!session || (session->state() != QCamera::ActiveState
&& session->state() != QCamera::LoadedState)) {
- return;
+ return false;
}
if (!m_service->imageCaptureControl()
|| !m_service->imageCaptureControl()->stillImageOutput()) {
qDebugCamera() << Q_FUNC_INFO << "no still image output";
- return;
+ return false;
}
if (m_settings.codec().size()
&& m_settings.codec() != QLatin1String("jpeg")) {
qDebugCamera() << Q_FUNC_INFO << "unsupported codec:" << m_settings.codec();
- return;
+ return false;
}
QSize res(m_settings.resolution());
if (res.isNull()) {
qDebugCamera() << Q_FUNC_INFO << "invalid resolution:" << res;
- return;
+ return false;
}
if (!res.isValid()) {
@@ -204,9 +209,11 @@ void AVFImageEncoderControl::applySettings()
// 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;
+ return false;
}
+ bool activeFormatChanged = false;
+
#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0)
if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_7, QSysInfo::MV_IOS_7_0)) {
AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice();
@@ -215,16 +222,17 @@ void AVFImageEncoderControl::applySettings()
if (!match) {
qDebugCamera() << Q_FUNC_INFO << "unsupported resolution:" << res;
- return;
+ return false;
}
if (match != captureDevice.activeFormat) {
const AVFConfigurationLock lock(captureDevice);
if (!lock) {
qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration";
- return;
+ return false;
}
captureDevice.activeFormat = match;
+ activeFormatChanged = true;
}
#if defined(Q_OS_IOS) && QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0)
@@ -242,6 +250,8 @@ void AVFImageEncoderControl::applySettings()
#endif
// TODO: resolution without capture device format ...
}
+
+ return activeFormatChanged;
}
bool AVFImageEncoderControl::videoCaptureDeviceIsValid() const
diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm b/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm
index 412dab76c..1b6e23ee5 100644
--- a/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm
+++ b/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm
@@ -37,6 +37,7 @@
#include "avfcameraservice.h"
#include "avfcameracontrol.h"
#include "avfaudioinputselectorcontrol.h"
+#include "avfcamerautility.h"
#include <QtCore/qurl.h>
#include <QtCore/qfileinfo.h>
@@ -330,6 +331,9 @@ void AVFMediaRecorderControl::setupSessionForCapture()
&& 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();
@@ -359,7 +363,10 @@ void AVFMediaRecorderControl::setupSessionForCapture()
}
} else if (m_connected
&& (!m_cameraControl->captureMode().testFlag(QCamera::CaptureVideo)
- || m_session->state() != QCamera::ActiveState)) {
+ || 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];
diff --git a/src/plugins/common/evr.pri b/src/plugins/common/evr.pri
index f951f6730..2a1b383df 100644
--- a/src/plugins/common/evr.pri
+++ b/src/plugins/common/evr.pri
@@ -1,8 +1,20 @@
INCLUDEPATH += $$PWD/evr
qtHaveModule(widgets): QT += widgets
+QT += gui-private
-HEADERS += $$PWD/evr/evrvideowindowcontrol.h \
- $$PWD/evr/evrdefs.h
+LIBS += -lmf -lmfplat -lmfuuid -ld3d9 -ldxva2 -lwinmm -levr
-SOURCES += $$PWD/evr/evrvideowindowcontrol.cpp
+HEADERS += \
+ $$PWD/evr/evrvideowindowcontrol.h \
+ $$PWD/evr/evrcustompresenter.h \
+ $$PWD/evr/evrd3dpresentengine.h \
+ $$PWD/evr/evrhelpers.h \
+ $$PWD/evr/evrdefs.h
+
+SOURCES += \
+ $$PWD/evr/evrvideowindowcontrol.cpp \
+ $$PWD/evr/evrcustompresenter.cpp \
+ $$PWD/evr/evrd3dpresentengine.cpp \
+ $$PWD/evr/evrhelpers.cpp \
+ $$PWD/evr/evrdefs.cpp
diff --git a/src/plugins/wmf/evrcustompresenter.cpp b/src/plugins/common/evr/evrcustompresenter.cpp
index 967095b20..73d032aa6 100644
--- a/src/plugins/wmf/evrcustompresenter.cpp
+++ b/src/plugins/common/evr/evrcustompresenter.cpp
@@ -33,8 +33,8 @@
#include "evrcustompresenter.h"
-#include "mfglobal.h"
#include "evrd3dpresentengine.h"
+#include "evrhelpers.h"
#include <QtCore/qmutex.h>
#include <QtCore/qvarlengtharray.h>
@@ -44,10 +44,8 @@
#include <qcoreapplication.h>
#include <qmath.h>
#include <QtCore/qdebug.h>
-#include <d3d9.h>
-#include <dshow.h>
-
-QT_USE_NAMESPACE
+#include <float.h>
+#include <evcode.h>
const static MFRatio g_DefaultFrameRate = { 30, 1 };
static const DWORD SCHEDULER_TIMEOUT = 5000;
@@ -58,7 +56,6 @@ static const LONG ONE_MSEC = 1000;
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 DWORD getFourCCFromPixelFormat(QVideoFrame::PixelFormat pixelFormat);
static QVideoFrame::PixelFormat pixelFormatFromMediaType(IMFMediaType *type);
static inline LONG MFTimeToMsec(const LONGLONG& time)
@@ -66,24 +63,62 @@ 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();
+ }
-Scheduler::Scheduler()
- : m_CB(NULL)
+ ~PresentSampleEvent()
+ {
+ 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_lastSampleTime(0)
, m_perFrameInterval(0)
, m_perFrame_1_4th(0)
+ , m_lastSampleTime(0)
{
}
Scheduler::~Scheduler()
{
- qt_wmf_safeRelease(&m_clock);
+ qt_evr_safe_release(&m_clock);
for (int i = 0; i < m_scheduledSamples.size(); ++i)
m_scheduledSamples[i]->Release();
m_scheduledSamples.clear();
@@ -109,8 +144,14 @@ HRESULT Scheduler::startScheduler(IMFClock *clock)
HRESULT hr = S_OK;
DWORD dwID = 0;
+ HANDLE hObjects[2];
+ DWORD dwWait = 0;
- qt_wmf_copyComPointer(m_clock, clock);
+ 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);
@@ -136,10 +177,9 @@ HRESULT Scheduler::startScheduler(IMFClock *clock)
goto done;
}
- HANDLE hObjects[] = { m_threadReadyEvent, m_schedulerThread };
- DWORD dwWait = 0;
-
// 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.
@@ -210,9 +250,6 @@ HRESULT Scheduler::flush()
HRESULT Scheduler::scheduleSample(IMFSample *sample, bool presentNow)
{
- if (!m_CB)
- return MF_E_NOT_INITIALIZED;
-
if (!m_schedulerThread)
return MF_E_NOT_INITIALIZED;
@@ -224,13 +261,7 @@ HRESULT Scheduler::scheduleSample(IMFSample *sample, bool presentNow)
return E_FAIL;
if (presentNow || !m_clock) {
- // Present the sample immediately.
- sample->AddRef();
- QMetaObject::invokeMethod(m_CB,
- "presentSample",
- Qt::QueuedConnection,
- Q_ARG(void*, sample),
- Q_ARG(qint64, 0));
+ m_presenter->presentSample(sample);
} else {
// Queue the sample and ask the scheduler thread to wake up.
m_mutex.lock();
@@ -262,7 +293,7 @@ HRESULT Scheduler::processSamplesInQueue(LONG *nextSleep)
// means the scheduler should sleep for that amount of time.
hr = processSample(sample, &wait);
- qt_wmf_safeRelease(&sample);
+ qt_evr_safe_release(&sample);
if (FAILED(hr) || wait > 0)
break;
@@ -326,12 +357,7 @@ HRESULT Scheduler::processSample(IMFSample *sample, LONG *pNextSleep)
}
if (presentNow) {
- sample->AddRef();
- QMetaObject::invokeMethod(m_CB,
- "presentSample",
- Qt::QueuedConnection,
- Q_ARG(void*, sample),
- Q_ARG(qint64, hnsPresentationTime));
+ m_presenter->presentSample(sample);
} else {
// The sample is not ready yet. Return it to the queue.
m_mutex.lock();
@@ -401,7 +427,7 @@ DWORD Scheduler::schedulerThreadProcPrivate()
hr = processSamplesInQueue(&wait);
if (FAILED(hr))
exitThread = true;
- processSamples = (wait != INFINITE);
+ processSamples = (wait != (LONG)INFINITE);
}
break;
}
@@ -520,24 +546,27 @@ HRESULT SamplePool::clear()
}
-EVRCustomPresenter::EVRCustomPresenter()
+EVRCustomPresenter::EVRCustomPresenter(QAbstractVideoSurface *surface)
: QObject()
, m_sampleFreeCB(this, &EVRCustomPresenter::onSampleFree)
, m_refCount(1)
, m_renderState(RenderShutdown)
, m_mutex(QMutex::Recursive)
+ , m_scheduler(this)
, m_tokenCounter(0)
, m_sampleNotify(false)
, m_repaint(false)
, m_prerolled(false)
, m_endStreaming(false)
, m_playbackRate(1.0f)
- , m_D3DPresentEngine(0)
+ , m_presentEngine(new D3DPresentEngine)
, m_clock(0)
, m_mixer(0)
, m_mediaEventSink(0)
, m_mediaType(0)
, m_surface(0)
+ , m_canRenderToSurface(false)
+ , m_sampleToPresent(0)
{
// Initial source rectangle = (0,0,1,1)
m_sourceRect.top = 0;
@@ -545,18 +574,21 @@ EVRCustomPresenter::EVRCustomPresenter()
m_sourceRect.bottom = 1;
m_sourceRect.right = 1;
- m_D3DPresentEngine = new D3DPresentEngine;
- m_scheduler.setCallback(m_D3DPresentEngine);
+ setSurface(surface);
}
EVRCustomPresenter::~EVRCustomPresenter()
{
- qt_wmf_safeRelease(&m_clock);
- qt_wmf_safeRelease(&m_mixer);
- qt_wmf_safeRelease(&m_mediaEventSink);
- qt_wmf_safeRelease(&m_mediaType);
+ 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);
- m_D3DPresentEngine->deleteLater();
+ delete m_presentEngine;
}
HRESULT EVRCustomPresenter::QueryInterface(REFIID riid, void ** ppvObject)
@@ -606,11 +638,11 @@ HRESULT EVRCustomPresenter::GetService(REFGUID guidService, REFIID riid, LPVOID
return E_POINTER;
// The only service GUID that we support is MR_VIDEO_RENDER_SERVICE.
- if (guidService != 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_D3DPresentEngine->getService(guidService, riid, ppvObject);
+ hr = m_presentEngine->getService(guidService, riid, ppvObject);
if (FAILED(hr))
// Next, check if this object supports the interface.
hr = QueryInterface(riid, ppvObject);
@@ -623,7 +655,7 @@ HRESULT EVRCustomPresenter::GetDeviceID(IID* deviceID)
if (!deviceID)
return E_POINTER;
- *deviceID = IID_IDirect3DDevice9;
+ *deviceID = iid_IDirect3DDevice9;
return S_OK;
}
@@ -642,15 +674,15 @@ HRESULT EVRCustomPresenter::InitServicePointers(IMFTopologyServiceLookup *lookup
if (isActive())
return MF_E_INVALIDREQUEST;
- qt_wmf_safeRelease(&m_clock);
- qt_wmf_safeRelease(&m_mixer);
- qt_wmf_safeRelease(&m_mediaEventSink);
+ 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),
+ mr_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_clock),
&objectCount
);
@@ -658,7 +690,7 @@ HRESULT EVRCustomPresenter::InitServicePointers(IMFTopologyServiceLookup *lookup
objectCount = 1;
hr = lookup->LookupService(MF_SERVICE_LOOKUP_GLOBAL, 0,
- MR_VIDEO_MIXER_SERVICE, IID_PPV_ARGS(&m_mixer),
+ mr_VIDEO_MIXER_SERVICE, IID_PPV_ARGS(&m_mixer),
&objectCount
);
@@ -674,7 +706,7 @@ HRESULT EVRCustomPresenter::InitServicePointers(IMFTopologyServiceLookup *lookup
objectCount = 1;
hr = lookup->LookupService(MF_SERVICE_LOOKUP_GLOBAL, 0,
- MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_mediaEventSink),
+ mr_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_mediaEventSink),
&objectCount
);
@@ -700,13 +732,18 @@ HRESULT EVRCustomPresenter::ReleaseServicePointers()
setMediaType(NULL);
// Release all services that were acquired from InitServicePointers.
- qt_wmf_safeRelease(&m_clock);
- qt_wmf_safeRelease(&m_mixer);
- qt_wmf_safeRelease(&m_mediaEventSink);
+ 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;
@@ -819,11 +856,7 @@ HRESULT EVRCustomPresenter::OnClockStart(MFTIME, LONGLONG clockStartOffset)
return hr;
}
- // Start the video surface in the main thread
- if (thread() == QThread::currentThread())
- startSurface();
- else
- QMetaObject::invokeMethod(this, "startSurface", Qt::QueuedConnection);
+ startSurface();
// Now try to get new output samples from the mixer.
processOutputLoop();
@@ -872,11 +905,7 @@ HRESULT EVRCustomPresenter::OnClockStop(MFTIME)
cancelFrameStep();
}
- // Stop the video surface in the main thread
- if (thread() == QThread::currentThread())
- stopSurface();
- else
- QMetaObject::invokeMethod(this, "stopSurface", Qt::QueuedConnection);
+ stopSurface();
return S_OK;
}
@@ -1003,26 +1032,44 @@ void EVRCustomPresenter::supportedFormatsChanged()
{
QMutexLocker locker(&m_mutex);
- m_supportedGLFormats.clear();
- if (!m_surface)
- return;
+ m_canRenderToSurface = false;
+ m_presentEngine->setHint(D3DPresentEngine::RenderToTexture, false);
- QList<QVideoFrame::PixelFormat> formats = m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle);
- for (int i = 0; i < formats.size(); ++i) {
- DWORD fourCC = getFourCCFromPixelFormat(formats.at(i));
- if (fourCC)
- m_supportedGLFormats.append(fourCC);
+ // 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);
+ Q_FOREACH (QVideoFrame::PixelFormat format, 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_D3DPresentEngine)
- m_D3DPresentEngine->setSurface(surface);
+ if (m_surface) {
+ connect(m_surface, &QAbstractVideoSurface::supportedFormatsChanged,
+ this, &EVRCustomPresenter::supportedFormatsChanged);
+ }
m_mutex.unlock();
@@ -1049,8 +1096,8 @@ HRESULT EVRCustomPresenter::renegotiateMediaType()
// Loop through all of the mixer's proposed output types.
DWORD typeIndex = 0;
while (!foundMediaType && (hr != MF_E_NO_MORE_TYPES)) {
- qt_wmf_safeRelease(&mixerType);
- qt_wmf_safeRelease(&optimalType);
+ 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);
@@ -1089,8 +1136,8 @@ HRESULT EVRCustomPresenter::renegotiateMediaType()
foundMediaType = true;
}
- qt_wmf_safeRelease(&mixerType);
- qt_wmf_safeRelease(&optimalType);
+ qt_evr_safe_release(&mixerType);
+ qt_evr_safe_release(&optimalType);
return hr;
}
@@ -1112,11 +1159,7 @@ HRESULT EVRCustomPresenter::flush()
if (m_renderState == RenderStopped) {
// Repaint with black.
- QMetaObject::invokeMethod(m_D3DPresentEngine,
- "presentSample",
- Qt::QueuedConnection,
- Q_ARG(void*, 0),
- Q_ARG(qint64, 0));
+ presentSample(NULL);
}
return S_OK;
@@ -1218,7 +1261,7 @@ HRESULT EVRCustomPresenter::startFrameStep()
if (FAILED(hr))
goto done;
- qt_wmf_safeRelease(&sample);
+ qt_evr_safe_release(&sample);
// We break from this loop when:
// (a) the frame-step queue is empty, or
@@ -1234,12 +1277,12 @@ HRESULT EVRCustomPresenter::startFrameStep()
if (FAILED(hr))
goto done;
- qt_wmf_safeRelease(&sample);
+ qt_evr_safe_release(&sample);
}
}
done:
- qt_wmf_safeRelease(&sample);
+ qt_evr_safe_release(&sample);
return hr;
}
@@ -1251,7 +1294,7 @@ HRESULT EVRCustomPresenter::completeFrameStep(IMFSample *sample)
// Update our state.
m_frameStep.state = FrameStepComplete;
- m_frameStep.sampleNoRef = NULL;
+ m_frameStep.sampleNoRef = 0;
// Notify the EVR that the frame-step is complete.
notifyEvent(EC_STEP_COMPLETE, FALSE, 0); // FALSE = completed (not cancelled)
@@ -1279,7 +1322,7 @@ HRESULT EVRCustomPresenter::cancelFrameStep()
m_frameStep.state = FrameStepNone;
m_frameStep.steps = 0;
- m_frameStep.sampleNoRef = NULL;
+ m_frameStep.sampleNoRef = 0;
// Don't clear the frame-step queue yet, because we might frame step again.
if (oldState > FrameStepNone && oldState < FrameStepComplete) {
@@ -1302,6 +1345,10 @@ HRESULT EVRCustomPresenter::createOptimalVideoType(IMFMediaType *proposedType, I
IMFMediaType *mtOptimal = NULL;
+ UINT64 size;
+ int width;
+ int height;
+
// Clone the proposed type.
hr = MFCreateMediaType(&mtOptimal);
@@ -1315,21 +1362,22 @@ HRESULT EVRCustomPresenter::createOptimalVideoType(IMFMediaType *proposedType, I
// Modify the new type.
// Set the pixel aspect ratio (PAR) to 1:1 (see assumption #1, above)
- hr = MFSetAttributeRatio(mtOptimal, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
+ // The ratio is packed in a single UINT64. A helper function is normally available for
+ // that (MFSetAttributeRatio) but it's not correctly defined in MinGW 4.9.1.
+ hr = mtOptimal->SetUINT64(MF_MT_PIXEL_ASPECT_RATIO, (((UINT64) 1) << 32) | ((UINT64) 1));
if (FAILED(hr))
goto done;
- UINT64 size;
hr = proposedType->GetUINT64(MF_MT_FRAME_SIZE, &size);
- int width = int(HI32(size));
- int height = int(LO32(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_wmf_makeMFArea(0, 0, rcOutput.right, rcOutput.bottom);
+ displayArea = qt_evr_makeMFArea(0, 0, rcOutput.right, rcOutput.bottom);
hr = mtOptimal->SetUINT32(MF_MT_PAN_SCAN_ENABLED, FALSE);
if (FAILED(hr))
@@ -1355,7 +1403,7 @@ HRESULT EVRCustomPresenter::createOptimalVideoType(IMFMediaType *proposedType, I
(*optimalType)->AddRef();
done:
- qt_wmf_safeRelease(&mtOptimal);
+ qt_evr_safe_release(&mtOptimal);
return hr;
}
@@ -1366,9 +1414,8 @@ HRESULT EVRCustomPresenter::setMediaType(IMFMediaType *mediaType)
// Clearing the media type is allowed in any state (including shutdown).
if (!mediaType) {
- qt_wmf_safeRelease(&m_mediaType);
+ qt_evr_safe_release(&m_mediaType);
releaseResources();
- m_D3DPresentEngine->setSurfaceFormat(QVideoSurfaceFormat());
return S_OK;
}
@@ -1377,8 +1424,6 @@ HRESULT EVRCustomPresenter::setMediaType(IMFMediaType *mediaType)
IMFSample *sample = NULL;
- QVideoSurfaceFormat surfaceFormat;
-
// Cannot set the media type after shutdown.
HRESULT hr = checkShutdown();
if (FAILED(hr))
@@ -1386,17 +1431,17 @@ HRESULT EVRCustomPresenter::setMediaType(IMFMediaType *mediaType)
// Check if the new type is actually different.
// Note: This function safely handles NULL input parameters.
- if (qt_wmf_areMediaTypesEqual(m_mediaType, mediaType))
+ 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_wmf_safeRelease(&m_mediaType);
+ qt_evr_safe_release(&m_mediaType);
releaseResources();
// Initialize the presenter engine with the new media type.
// The presenter engine allocates the samples.
- hr = m_D3DPresentEngine->createVideoSamples(mediaType, sampleQueue);
+ hr = m_presentEngine->createVideoSamples(mediaType, sampleQueue);
if (FAILED(hr))
goto done;
@@ -1416,7 +1461,7 @@ HRESULT EVRCustomPresenter::setMediaType(IMFMediaType *mediaType)
goto done;
// Set the frame rate on the scheduler.
- if (SUCCEEDED(qt_wmf_getFrameRate(mediaType, &fps)) && (fps.Numerator != 0) && (fps.Denominator != 0)) {
+ 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
@@ -1429,16 +1474,6 @@ HRESULT EVRCustomPresenter::setMediaType(IMFMediaType *mediaType)
m_mediaType = mediaType;
m_mediaType->AddRef();
- // Create the surface format
- UINT64 size;
- hr = m_mediaType->GetUINT64(MF_MT_FRAME_SIZE, &size);
- int width = int(HI32(size));
- int height = int(LO32(size));
- surfaceFormat = QVideoSurfaceFormat(QSize(width, height),
- pixelFormatFromMediaType(m_mediaType),
- QAbstractVideoBuffer::GLTextureHandle);
- m_D3DPresentEngine->setSurfaceFormat(surfaceFormat);
-
done:
if (FAILED(hr))
releaseResources();
@@ -1454,14 +1489,21 @@ HRESULT EVRCustomPresenter::isMediaTypeSupported(IMFMediaType *proposed)
UINT32 width = 0, height = 0;
// Validate the format.
- HRESULT hr = qt_wmf_getFourCC(proposed, (DWORD*)&d3dFormat);
+ HRESULT hr = qt_evr_getFourCC(proposed, (DWORD*)&d3dFormat);
if (FAILED(hr))
return hr;
- // Only accept pixel formats supported by the video surface
- if (!m_supportedGLFormats.contains((DWORD)d3dFormat))
+ 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))
@@ -1470,9 +1512,8 @@ HRESULT EVRCustomPresenter::isMediaTypeSupported(IMFMediaType *proposed)
if (compressed)
return MF_E_INVALIDMEDIATYPE;
- // The D3DPresentEngine checks whether the format can be used as
- // the back-buffer format for the swap chains.
- hr = m_D3DPresentEngine->checkFormat(d3dFormat);
+ // The D3DPresentEngine checks whether surfaces can be created using this format
+ hr = m_presentEngine->checkFormat(d3dFormat);
if (FAILED(hr))
return hr;
@@ -1493,13 +1534,13 @@ HRESULT EVRCustomPresenter::isMediaTypeSupported(IMFMediaType *proposed)
// we ignore it. We just want to reject invalid apertures.
if (SUCCEEDED(proposed->GetBlob(MF_MT_PAN_SCAN_APERTURE, (UINT8*)&videoCropArea, sizeof(videoCropArea), NULL)))
- hr = qt_wmf_validateVideoArea(videoCropArea, width, height);
+ hr = qt_evr_validateVideoArea(videoCropArea, width, height);
if (SUCCEEDED(proposed->GetBlob(MF_MT_GEOMETRIC_APERTURE, (UINT8*)&videoCropArea, sizeof(videoCropArea), NULL)))
- hr = qt_wmf_validateVideoArea(videoCropArea, width, height);
+ hr = qt_evr_validateVideoArea(videoCropArea, width, height);
if (SUCCEEDED(proposed->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, (UINT8*)&videoCropArea, sizeof(videoCropArea), NULL)))
- hr = qt_wmf_validateVideoArea(videoCropArea, width, height);
+ hr = qt_evr_validateVideoArea(videoCropArea, width, height);
return hr;
}
@@ -1640,10 +1681,10 @@ HRESULT EVRCustomPresenter::processOutput()
}
done:
- qt_wmf_safeRelease(&sample);
+ qt_evr_safe_release(&sample);
// Important: Release any events returned from the ProcessOutput method.
- qt_wmf_safeRelease(&dataBuffer.pEvents);
+ qt_evr_safe_release(&dataBuffer.pEvents);
return hr;
}
@@ -1673,7 +1714,7 @@ HRESULT EVRCustomPresenter::deliverFrameStepSample(IMFSample *sample)
IUnknown *unk = NULL;
// For rate 0, discard any sample that ends earlier than the clock time.
- if (isScrubbing() && m_clock && qt_wmf_isSampleTimePassed(m_clock, sample)) {
+ 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,
@@ -1719,7 +1760,7 @@ HRESULT EVRCustomPresenter::deliverFrameStepSample(IMFSample *sample)
}
}
done:
- qt_wmf_safeRelease(&unk);
+ qt_evr_safe_release(&unk);
return hr;
}
@@ -1732,7 +1773,7 @@ HRESULT EVRCustomPresenter::trackSample(IMFSample *sample)
if (SUCCEEDED(hr))
hr = tracked->SetAllocator(&m_sampleFreeCB, NULL);
- qt_wmf_safeRelease(&tracked);
+ qt_evr_safe_release(&tracked);
return hr;
}
@@ -1753,7 +1794,7 @@ void EVRCustomPresenter::releaseResources()
m_samplePool.clear();
- m_D3DPresentEngine->releaseResources();
+ m_presentEngine->releaseResources();
}
HRESULT EVRCustomPresenter::onSampleFree(IMFAsyncResult *result)
@@ -1761,6 +1802,7 @@ 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);
@@ -1795,7 +1837,7 @@ HRESULT EVRCustomPresenter::onSampleFree(IMFAsyncResult *result)
m_mutex.lock();
- UINT32 token = MFGetAttributeUINT32(sample, MFSamplePresenter_SampleCounter, (UINT32)-1);
+ token = MFGetAttributeUINT32(sample, MFSamplePresenter_SampleCounter, (UINT32)-1);
if (token == m_tokenCounter) {
// Return the sample to the sample pool.
@@ -1811,24 +1853,12 @@ HRESULT EVRCustomPresenter::onSampleFree(IMFAsyncResult *result)
done:
if (FAILED(hr))
notifyEvent(EC_ERRORABORT, hr, 0);
- qt_wmf_safeRelease(&object);
- qt_wmf_safeRelease(&sample);
- qt_wmf_safeRelease(&unk);
+ qt_evr_safe_release(&object);
+ qt_evr_safe_release(&sample);
+ qt_evr_safe_release(&unk);
return hr;
}
-void EVRCustomPresenter::startSurface()
-{
- if (m_D3DPresentEngine)
- m_D3DPresentEngine->start();
-}
-
-void EVRCustomPresenter::stopSurface()
-{
- if (m_D3DPresentEngine)
- m_D3DPresentEngine->stop();
-}
-
float EVRCustomPresenter::getMaxRate(bool thin)
{
// Non-thinned:
@@ -1843,8 +1873,8 @@ float EVRCustomPresenter::getMaxRate(bool thin)
UINT monitorRateHz = 0;
if (!thin && m_mediaType) {
- qt_wmf_getFrameRate(m_mediaType, &fps);
- monitorRateHz = m_D3DPresentEngine->refreshRate();
+ qt_evr_getFrameRate(m_mediaType, &fps);
+ monitorRateHz = m_presentEngine->refreshRate();
if (fps.Denominator && fps.Numerator && monitorRateHz) {
// Max Rate = Refresh Rate / Frame Rate
@@ -1855,6 +1885,74 @@ float EVRCustomPresenter::getMaxRate(bool thin)
return maxRate;
}
+bool EVRCustomPresenter::event(QEvent *e)
+{
+ if (e->type() == StartSurface) {
+ startSurface();
+ return true;
+ } else if (e->type() == StopSurface) {
+ stopSurface();
+ return true;
+ } else if (e->type() == PresentSample) {
+ PresentSampleEvent *ev = static_cast<PresentSampleEvent *>(e);
+ presentSample(ev->sample());
+ return true;
+ }
+
+ 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_surface->isActive() || !m_presentEngine->videoSurfaceFormat().isValid())
+ return;
+
+ QVideoFrame frame = m_presentEngine->makeVideoFrame(sample);
+
+ 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);
+}
+
HRESULT setDesiredSampleTime(IMFSample *sample, const LONGLONG &sampleTime, const LONGLONG &duration)
{
if (!sample)
@@ -1867,7 +1965,7 @@ HRESULT setDesiredSampleTime(IMFSample *sample, const LONGLONG &sampleTime, cons
if (SUCCEEDED(hr))
desired->SetDesiredSampleTimeAndDuration(sampleTime, duration);
- qt_wmf_safeRelease(&desired);
+ qt_evr_safe_release(&desired);
return hr;
}
@@ -1889,8 +1987,6 @@ HRESULT clearDesiredSampleTime(IMFSample *sample)
UINT32 counter = MFGetAttributeUINT32(sample, MFSamplePresenter_SampleCounter, (UINT32)-1);
- sample->GetUnknown(MFSamplePresenter_SampleSwapChain, IID_IUnknown, (void**)&unkSwapChain);
-
hr = sample->QueryInterface(IID_PPV_ARGS(&desired));
if (SUCCEEDED(hr)) {
desired->Clear();
@@ -1898,17 +1994,11 @@ HRESULT clearDesiredSampleTime(IMFSample *sample)
hr = sample->SetUINT32(MFSamplePresenter_SampleCounter, counter);
if (FAILED(hr))
goto done;
-
- if (unkSwapChain) {
- hr = sample->SetUnknown(MFSamplePresenter_SampleSwapChain, unkSwapChain);
- if (FAILED(hr))
- goto done;
- }
}
done:
- qt_wmf_safeRelease(&unkSwapChain);
- qt_wmf_safeRelease(&desired);
+ qt_evr_safe_release(&unkSwapChain);
+ qt_evr_safe_release(&desired);
return hr;
}
@@ -1921,54 +2011,12 @@ HRESULT setMixerSourceRect(IMFTransform *mixer, const MFVideoNormalizedRect &sou
HRESULT hr = mixer->GetAttributes(&attributes);
if (SUCCEEDED(hr)) {
- hr = attributes->SetBlob(VIDEO_ZOOM_RECT, (const UINT8*)&sourceRect, sizeof(sourceRect));
+ hr = attributes->SetBlob(video_ZOOM_RECT, (const UINT8*)&sourceRect, sizeof(sourceRect));
attributes->Release();
}
return hr;
}
-DWORD getFourCCFromPixelFormat(QVideoFrame::PixelFormat pixelFormat)
-{
- DWORD fourCC = 0;
- switch (pixelFormat) {
- case QVideoFrame::Format_ARGB32:
- case QVideoFrame::Format_ARGB32_Premultiplied:
- fourCC = MFVideoFormat_ARGB32.Data1;
- break;
- case QVideoFrame::Format_RGB32:
- fourCC = MFVideoFormat_RGB32.Data1;
- break;
- case QVideoFrame::Format_RGB24:
- fourCC = MFVideoFormat_RGB24.Data1;
- break;
- case QVideoFrame::Format_RGB565:
- fourCC = MFVideoFormat_RGB565.Data1;
- break;
- case QVideoFrame::Format_RGB555:
- fourCC = MFVideoFormat_RGB555.Data1;
- break;
- case QVideoFrame::Format_AYUV444:
- case QVideoFrame::Format_AYUV444_Premultiplied:
- fourCC = MFVideoFormat_AYUV.Data1;
- break;
- case QVideoFrame::Format_YUV420P:
- fourCC = MFVideoFormat_I420.Data1;
- break;
- case QVideoFrame::Format_UYVY:
- fourCC = MFVideoFormat_UYVY.Data1;
- break;
- case QVideoFrame::Format_YV12:
- fourCC = MFVideoFormat_YV12.Data1;
- break;
- case QVideoFrame::Format_NV12:
- fourCC = MFVideoFormat_NV12.Data1;
- break;
- default:
- break;
- }
- return fourCC;
-}
-
static QVideoFrame::PixelFormat pixelFormatFromMediaType(IMFMediaType *type)
{
GUID majorType;
@@ -1977,69 +2025,30 @@ static QVideoFrame::PixelFormat pixelFormatFromMediaType(IMFMediaType *type)
if (majorType != MFMediaType_Video)
return QVideoFrame::Format_Invalid;
- GUID subType;
- if (FAILED(type->GetGUID(MF_MT_SUBTYPE, &subType)))
+ GUID subtype;
+ if (FAILED(type->GetGUID(MF_MT_SUBTYPE, &subtype)))
return QVideoFrame::Format_Invalid;
- if (subType == MFVideoFormat_RGB32)
+ if (subtype == MFVideoFormat_RGB32)
return QVideoFrame::Format_RGB32;
+ else if (subtype == MFVideoFormat_ARGB32)
+ return QVideoFrame::Format_ARGB32;
+ 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;
}
-
-
-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);
-}
-
-void EVRCustomPresenterActivate::supportedFormatsChanged()
-{
- QMutexLocker locker(&m_mutex);
-
- if (m_presenter)
- m_presenter->supportedFormatsChanged();
-}
diff --git a/src/plugins/wmf/evrcustompresenter.h b/src/plugins/common/evr/evrcustompresenter.h
index 1d24feaa5..e25780140 100644
--- a/src/plugins/wmf/evrcustompresenter.h
+++ b/src/plugins/common/evr/evrcustompresenter.h
@@ -37,13 +37,72 @@
#include <QObject>
#include <qmutex.h>
#include <qqueue.h>
-#include <evr.h>
-#include "mfactivate.h"
+#include <qevent.h>
+#include <qvideosurfaceformat.h>
+
+#include "evrdefs.h"
QT_BEGIN_NAMESPACE
+class QAbstractVideoSurface;
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+class EVRCustomPresenter;
class D3DPresentEngine;
-class QAbstractVideoSurface;
+
+template<class T>
+class AsyncCallback : public IMFAsyncCallback
+{
+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)
+ {
+ 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() {
+ // Delegate to parent class.
+ return m_parent->AddRef();
+ }
+ STDMETHODIMP_(ULONG) Release() {
+ // Delegate to parent class.
+ return m_parent->Release();
+ }
+
+ // IMFAsyncCallback methods
+ STDMETHODIMP GetParameters(DWORD*, DWORD*)
+ {
+ // Implementation of this method is optional.
+ return E_NOTIMPL;
+ }
+
+ STDMETHODIMP Invoke(IMFAsyncResult* asyncResult)
+ {
+ return (m_parent->*m_invokeFn)(asyncResult);
+ }
+
+ T *m_parent;
+ InvokeFn m_invokeFn;
+};
class Scheduler
{
@@ -55,13 +114,9 @@ public:
Flush = WM_USER + 2
};
- Scheduler();
+ Scheduler(EVRCustomPresenter *presenter);
~Scheduler();
- void setCallback(QObject *cb) {
- m_CB = cb;
- }
-
void setFrameRate(const MFRatio &fps);
void setClockRate(float rate) { m_playbackRate = rate; }
@@ -82,10 +137,11 @@ public:
private:
DWORD schedulerThreadProcPrivate();
+ EVRCustomPresenter *m_presenter;
+
QQueue<IMFSample*> m_scheduledSamples; // Samples waiting to be presented.
IMFClock *m_clock; // Presentation clock. Can be NULL.
- QObject *m_CB; // Weak reference; do not delete.
DWORD m_threadID;
HANDLE m_schedulerThread;
@@ -128,8 +184,6 @@ class EVRCustomPresenter
, public IMFGetService
, public IMFTopologyServiceLookupClient
{
- Q_OBJECT
-
public:
// Defines the state of the presenter.
enum RenderState
@@ -150,9 +204,18 @@ public:
FrameStepComplete // Sample was rendered.
};
- EVRCustomPresenter();
+ enum PresenterEvents
+ {
+ StartSurface = QEvent::User,
+ StopSurface = QEvent::User + 1,
+ PresentSample = QEvent::User + 2
+ };
+
+ EVRCustomPresenter(QAbstractVideoSurface *surface = 0);
~EVRCustomPresenter();
+ bool isValid() const;
+
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
STDMETHODIMP_(ULONG) AddRef();
@@ -187,9 +250,11 @@ public:
void supportedFormatsChanged();
void setSurface(QAbstractVideoSurface *surface);
-private Q_SLOTS:
void startSurface();
void stopSurface();
+ void presentSample(IMFSample *sample);
+
+ bool event(QEvent *);
private:
HRESULT checkShutdown() const
@@ -250,7 +315,7 @@ private:
// Callback when a video sample is released.
HRESULT onSampleFree(IMFAsyncResult *result);
- AsyncCallback<EVRCustomPresenter> m_sampleFreeCB;
+ AsyncCallback<EVRCustomPresenter> m_sampleFreeCB;
// Holds information related to frame-stepping.
struct FrameStep
@@ -258,7 +323,7 @@ private:
FrameStep()
: state(FrameStepNone)
, steps(0)
- , sampleNoRef(NULL)
+ , sampleNoRef(0)
{
}
@@ -289,7 +354,7 @@ private:
MFVideoNormalizedRect m_sourceRect;
float m_playbackRate;
- D3DPresentEngine *m_D3DPresentEngine; // Rendering engine. (Never null if the constructor succeeds.)
+ 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.
@@ -297,29 +362,11 @@ private:
IMFMediaType *m_mediaType; // Output media type
QAbstractVideoSurface *m_surface;
- QList<DWORD> m_supportedGLFormats;
-};
+ bool m_canRenderToSurface;
-class EVRCustomPresenterActivate : public MFAbstractActivate
-{
-public:
- EVRCustomPresenterActivate();
- ~EVRCustomPresenterActivate()
- { }
-
- STDMETHODIMP ActivateObject(REFIID riid, void **ppv);
- STDMETHODIMP ShutdownObject();
- STDMETHODIMP DetachObject();
-
- void setSurface(QAbstractVideoSurface *surface);
- void supportedFormatsChanged();
-
-private:
- EVRCustomPresenter *m_presenter;
- QAbstractVideoSurface *m_surface;
- QMutex m_mutex;
+ IMFSample *m_sampleToPresent;
};
-QT_END_NAMESPACE
+bool qt_evr_setCustomPresenter(IUnknown *evr, EVRCustomPresenter *presenter);
#endif // EVRCUSTOMPRESENTER_H
diff --git a/src/plugins/wmf/evrd3dpresentengine.cpp b/src/plugins/common/evr/evrd3dpresentengine.cpp
index d66918a99..77cd7e0c8 100644
--- a/src/plugins/wmf/evrd3dpresentengine.cpp
+++ b/src/plugins/common/evr/evrd3dpresentengine.cpp
@@ -33,52 +33,25 @@
#include "evrd3dpresentengine.h"
-#include "mfglobal.h"
+#include "evrhelpers.h"
-#include <qtgui/qguiapplication.h>
-#include <qpa/qplatformnativeinterface.h>
-#include <qtgui/qopenglcontext.h>
#include <qabstractvideobuffer.h>
#include <QAbstractVideoSurface>
#include <qvideoframe.h>
#include <QDebug>
-#include <qopenglcontext.h>
-#include <qopenglfunctions.h>
-#include <qwindow.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <d3d9.h>
-#include <dxva2api.h>
-#include <WinUser.h>
-#include <evr.h>
-
-QT_USE_NAMESPACE
-
-static const DWORD PRESENTER_BUFFER_COUNT = 3;
-
-class TextureVideoBuffer : public QAbstractVideoBuffer
-{
-public:
- TextureVideoBuffer(GLuint textureId)
- : QAbstractVideoBuffer(GLTextureHandle)
- , m_textureId(textureId)
- {}
-
- ~TextureVideoBuffer() {}
-
- MapMode mapMode() const { return NotMapped; }
- uchar *map(MapMode, int*, int*) { return 0; }
- void unmap() {}
+#include <qthread.h>
+#include <private/qmediaopenglhelper_p.h>
+
+#ifdef MAYBE_ANGLE
+# include <qtgui/qguiapplication.h>
+# include <qpa/qplatformnativeinterface.h>
+# include <qopenglfunctions.h>
+# include <EGL/eglext.h>
+#endif
- QVariant handle() const
- {
- return QVariant::fromValue<unsigned int>(m_textureId);
- }
+static const int PRESENTER_BUFFER_COUNT = 3;
-private:
- GLuint m_textureId;
-};
+#ifdef MAYBE_ANGLE
EGLWrapper::EGLWrapper()
{
@@ -142,22 +115,160 @@ EGLBoolean EGLWrapper::releaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLin
return m_eglReleaseTexImage(dpy, surface, buffer);
}
+
+class OpenGLResources : public QObject
+{
+public:
+ OpenGLResources()
+ : egl(new EGLWrapper)
+ , eglDisplay(0)
+ , eglSurface(0)
+ , glTexture(0)
+ {}
+
+ void release()
+ {
+ if (thread() == QThread::currentThread())
+ delete this;
+ else
+ deleteLater();
+ }
+
+ EGLWrapper *egl;
+ EGLDisplay *eglDisplay;
+ EGLSurface eglSurface;
+ unsigned int glTexture;
+
+private:
+ ~OpenGLResources()
+ {
+ Q_ASSERT(QOpenGLContext::currentContext() != NULL);
+
+ if (eglSurface && egl) {
+ egl->releaseTexImage(eglDisplay, eglSurface, EGL_BACK_BUFFER);
+ egl->destroySurface(eglDisplay, eglSurface);
+ }
+ if (glTexture)
+ QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &glTexture);
+
+ delete egl;
+ }
+};
+
+#endif // MAYBE_ANGLE
+
+
+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)
+ , m_textureUpdated(false)
+ {
+ 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()
+ {
+ if (m_surface) {
+ if (m_mapMode != NotMapped)
+ m_surface->UnlockRect();
+ m_surface->Release();
+ }
+ if (m_sample)
+ m_sample->Release();
+ }
+
+ QVariant handle() const;
+
+ MapMode mapMode() const { return m_mapMode; }
+ uchar *map(MapMode, int*, int*);
+ void unmap();
+
+private:
+ mutable D3DPresentEngine *m_engine;
+ IMFSample *m_sample;
+ IDirect3DSurface9 *m_surface;
+ MapMode m_mapMode;
+ mutable bool m_textureUpdated;
+};
+
+uchar *IMFSampleVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine)
+{
+ if (!m_surface || m_mapMode != NotMapped)
+ return 0;
+
+ D3DSURFACE_DESC desc;
+ if (FAILED(m_surface->GetDesc(&desc)))
+ return 0;
+
+ D3DLOCKED_RECT rect;
+ if (FAILED(m_surface->LockRect(&rect, NULL, mode == ReadOnly ? D3DLOCK_READONLY : 0)))
+ return 0;
+
+ m_mapMode = mode;
+
+ if (numBytes)
+ *numBytes = (int)(rect.Pitch * desc.Height);
+
+ if (bytesPerLine)
+ *bytesPerLine = (int)rect.Pitch;
+
+ return reinterpret_cast<uchar *>(rect.pBits);
+}
+
+void IMFSampleVideoBuffer::unmap()
+{
+ if (m_mapMode == NotMapped)
+ return;
+
+ m_mapMode = NotMapped;
+ m_surface->UnlockRect();
+}
+
+QVariant IMFSampleVideoBuffer::handle() const
+{
+ QVariant handle;
+
+#ifdef MAYBE_ANGLE
+ if (handleType() != GLTextureHandle)
+ return handle;
+
+ if (m_textureUpdated || m_engine->updateTexture(m_surface)) {
+ m_textureUpdated = true;
+ handle = QVariant::fromValue<unsigned int>(m_engine->m_glResources->glTexture);
+ }
+#endif
+
+ return handle;
+}
+
+
D3DPresentEngine::D3DPresentEngine()
- : QObject()
- , m_mutex(QMutex::Recursive)
- , m_deviceResetToken(0)
+ : m_deviceResetToken(0)
, m_D3D9(0)
, m_device(0)
, m_deviceManager(0)
- , m_surface(0)
- , m_glContext(0)
- , m_offscreenSurface(0)
- , m_eglDisplay(0)
- , m_eglConfig(0)
- , m_eglSurface(0)
- , m_glTexture(0)
+ , m_useTextureRendering(false)
+#ifdef MAYBE_ANGLE
+ , m_glResources(0)
, m_texture(0)
- , m_egl(0)
+#endif
{
ZeroMemory(&m_displayMode, sizeof(m_displayMode));
@@ -174,47 +285,110 @@ D3DPresentEngine::D3DPresentEngine()
D3DPresentEngine::~D3DPresentEngine()
{
- qt_wmf_safeRelease(&m_texture);
- qt_wmf_safeRelease(&m_device);
- qt_wmf_safeRelease(&m_deviceManager);
- qt_wmf_safeRelease(&m_D3D9);
-
- if (m_eglSurface) {
- m_egl->releaseTexImage(m_eglDisplay, m_eglSurface, EGL_BACK_BUFFER);
- m_egl->destroySurface(m_eglDisplay, m_eglSurface);
- m_eglSurface = NULL;
- }
- if (m_glTexture) {
- if (QOpenGLContext *current = QOpenGLContext::currentContext())
- current->functions()->glDeleteTextures(1, &m_glTexture);
- else
- qWarning() << "D3DPresentEngine: Cannot obtain GL context, unable to delete textures";
- }
+ releaseResources();
- delete m_glContext;
- delete m_offscreenSurface;
- delete m_egl;
+ qt_evr_safe_release(&m_device);
+ qt_evr_safe_release(&m_deviceManager);
+ qt_evr_safe_release(&m_D3D9);
}
-void D3DPresentEngine::start()
+HRESULT D3DPresentEngine::initializeD3D()
{
- QMutexLocker locker(&m_mutex);
+ HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, &m_D3D9);
- if (!m_surfaceFormat.isValid())
- return;
+ if (SUCCEEDED(hr))
+ hr = DXVA2CreateDirect3DDeviceManager9(&m_deviceResetToken, &m_deviceManager);
+
+ return hr;
+}
- if (!m_texture)
- createOffscreenTexture();
+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);
- if (m_surface && !m_surface->isActive())
- m_surface->start(m_surfaceFormat);
+ m_device = device;
+ m_device->AddRef();
+
+done:
+ qt_evr_safe_release(&device);
+ return hr;
}
-void D3DPresentEngine::stop()
+bool D3DPresentEngine::isValid() const
{
- QMutexLocker locker(&m_mutex);
- if (m_surface && m_surface->isActive())
- m_surface->stop();
+ return m_device != NULL;
+}
+
+void D3DPresentEngine::releaseResources()
+{
+ m_surfaceFormat = QVideoSurfaceFormat();
+
+#ifdef MAYBE_ANGLE
+ qt_evr_safe_release(&m_texture);
+
+ if (m_glResources) {
+ m_glResources->release(); // deleted in GL thread
+ m_glResources = NULL;
+ }
+#endif
}
HRESULT D3DPresentEngine::getService(REFGUID, REFIID riid, void** ppv)
@@ -237,33 +411,52 @@ HRESULT D3DPresentEngine::getService(REFGUID, REFIID riid, void** ppv)
HRESULT D3DPresentEngine::checkFormat(D3DFORMAT format)
{
- HRESULT hr = S_OK;
+ if (!m_D3D9 || !m_device)
+ return E_FAIL;
- UINT uAdapter = D3DADAPTER_DEFAULT;
- D3DDEVTYPE type = D3DDEVTYPE_HAL;
+ HRESULT hr = S_OK;
D3DDISPLAYMODE mode;
D3DDEVICE_CREATION_PARAMETERS params;
- // Our shared D3D/EGL surface only supports RGB32,
- // reject all other formats
- if (format != D3DFMT_X8R8G8B8)
- return MF_E_INVALIDMEDIATYPE;
-
- if (m_device) {
- hr = m_device->GetCreationParameters(&params);
- if (FAILED(hr))
- return hr;
+ hr = m_device->GetCreationParameters(&params);
+ if (FAILED(hr))
+ return hr;
- uAdapter = params.AdapterOrdinal;
- type = params.DeviceType;
- }
+ UINT uAdapter = params.AdapterOrdinal;
+ D3DDEVTYPE type = params.DeviceType;
hr = m_D3D9->GetAdapterDisplayMode(uAdapter, &mode);
if (FAILED(hr))
return hr;
- return m_D3D9->CheckDeviceType(uAdapter, type, mode.Format, format, TRUE);
+ 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
+{
+#ifdef MAYBE_ANGLE
+ return QMediaOpenGLHelper::isANGLE();
+#else
+ return false;
+#endif
+}
+
+void D3DPresentEngine::setHint(Hint hint, bool enable)
+{
+ if (hint == RenderToTexture)
+ m_useTextureRendering = enable && supportsTextureRendering();
}
HRESULT D3DPresentEngine::createVideoSamples(IMFMediaType *format, QList<IMFSample*> &videoSampleQueue)
@@ -272,211 +465,148 @@ HRESULT D3DPresentEngine::createVideoSamples(IMFMediaType *format, QList<IMFSamp
return MF_E_UNEXPECTED;
HRESULT hr = S_OK;
- D3DPRESENT_PARAMETERS pp;
- IDirect3DSwapChain9 *swapChain = NULL;
+ IDirect3DSurface9 *surface = NULL;
IMFSample *videoSample = NULL;
- QMutexLocker locker(&m_mutex);
-
releaseResources();
- // Get the swap chain parameters from the media type.
- hr = getSwapChainPresentParameters(format, &pp);
+ UINT32 width = 0, height = 0;
+ hr = MFGetAttributeSize(format, MF_MT_FRAME_SIZE, &width, &height);
if (FAILED(hr))
- goto done;
+ 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++) {
- // Create a new swap chain.
- hr = m_device->CreateAdditionalSwapChain(&pp, &swapChain);
+ hr = m_device->CreateRenderTarget(width, height,
+ (D3DFORMAT)d3dFormat,
+ D3DMULTISAMPLE_NONE,
+ 0,
+ TRUE,
+ &surface, NULL);
if (FAILED(hr))
goto done;
- // Create the video sample from the swap chain.
- hr = createD3DSample(swapChain, &videoSample);
+ hr = MFCreateVideoSampleFromSurface(surface, &videoSample);
if (FAILED(hr))
goto done;
- // Add it to the list.
videoSample->AddRef();
videoSampleQueue.append(videoSample);
- // Set the swap chain pointer as a custom attribute on the sample. This keeps
- // a reference count on the swap chain, so that the swap chain is kept alive
- // for the duration of the sample's lifetime.
- hr = videoSample->SetUnknown(MFSamplePresenter_SampleSwapChain, swapChain);
- if (FAILED(hr))
- goto done;
-
- qt_wmf_safeRelease(&videoSample);
- qt_wmf_safeRelease(&swapChain);
+ qt_evr_safe_release(&videoSample);
+ qt_evr_safe_release(&surface);
}
done:
- if (FAILED(hr))
+ if (SUCCEEDED(hr)) {
+ m_surfaceFormat = QVideoSurfaceFormat(QSize(width, height),
+ m_useTextureRendering ? QVideoFrame::Format_RGB32
+ : qt_evr_pixelFormatFromD3DFormat((D3DFORMAT)d3dFormat),
+ m_useTextureRendering ? QAbstractVideoBuffer::GLTextureHandle
+ : QAbstractVideoBuffer::NoHandle);
+ } else {
releaseResources();
+ }
- qt_wmf_safeRelease(&swapChain);
- qt_wmf_safeRelease(&videoSample);
+ qt_evr_safe_release(&videoSample);
+ qt_evr_safe_release(&surface);
return hr;
}
-void D3DPresentEngine::releaseResources()
+QVideoFrame D3DPresentEngine::makeVideoFrame(IMFSample *sample)
{
-}
+ if (!sample)
+ return QVideoFrame();
-void D3DPresentEngine::presentSample(void *opaque, qint64)
-{
- HRESULT hr = S_OK;
+ QVideoFrame frame(new IMFSampleVideoBuffer(this, sample, m_surfaceFormat.handleType()),
+ m_surfaceFormat.frameSize(),
+ m_surfaceFormat.pixelFormat());
- IMFSample *sample = reinterpret_cast<IMFSample*>(opaque);
- IMFMediaBuffer* buffer = NULL;
- IDirect3DSurface9* surface = NULL;
-
- if (m_surface && m_surface->isActive()) {
- if (sample) {
- // Get the buffer from the sample.
- hr = sample->GetBufferByIndex(0, &buffer);
- if (FAILED(hr))
- goto done;
-
- // Get the surface from the buffer.
- hr = MFGetService(buffer, MR_BUFFER_SERVICE, IID_PPV_ARGS(&surface));
- if (FAILED(hr))
- goto done;
- }
+ // WMF uses 100-nanosecond units, Qt uses microseconds
+ LONGLONG startTime = -1;
+ if (SUCCEEDED(sample->GetSampleTime(&startTime))) {
+ frame.setStartTime(startTime * 0.1);
- if (surface && updateTexture(surface)) {
- QVideoFrame frame = QVideoFrame(new TextureVideoBuffer(m_glTexture),
- m_surfaceFormat.frameSize(),
- m_surfaceFormat.pixelFormat());
-
- // WMF uses 100-nanosecond units, Qt uses microseconds
- LONGLONG startTime = -1;
- if (SUCCEEDED(sample->GetSampleTime(&startTime))) {
- frame.setStartTime(startTime * 0.1);
-
- LONGLONG duration = -1;
- if (SUCCEEDED(sample->GetSampleDuration(&duration)))
- frame.setEndTime((startTime + duration) * 0.1);
- }
-
- m_surface->present(frame);
- }
+ LONGLONG duration = -1;
+ if (SUCCEEDED(sample->GetSampleDuration(&duration)))
+ frame.setEndTime((startTime + duration) * 0.1);
}
-done:
- qt_wmf_safeRelease(&surface);
- qt_wmf_safeRelease(&buffer);
- qt_wmf_safeRelease(&sample);
+ return frame;
}
-void D3DPresentEngine::setSurface(QAbstractVideoSurface *surface)
-{
- QMutexLocker locker(&m_mutex);
- m_surface = surface;
-}
-
-void D3DPresentEngine::setSurfaceFormat(const QVideoSurfaceFormat &format)
-{
- QMutexLocker locker(&m_mutex);
- m_surfaceFormat = format;
-}
+#ifdef MAYBE_ANGLE
-void D3DPresentEngine::createOffscreenTexture()
+bool D3DPresentEngine::createRenderTexture()
{
- // First, check if we have a context on this thread
- QOpenGLContext *currentContext = QOpenGLContext::currentContext();
+ if (m_texture)
+ return true;
- if (!currentContext) {
- //Create OpenGL context and set share context from surface
- QOpenGLContext *shareContext = qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>());
- if (!shareContext)
- return;
-
- m_offscreenSurface = new QWindow;
- m_offscreenSurface->setSurfaceType(QWindow::OpenGLSurface);
- //Needs geometry to be a valid surface, but size is not important
- m_offscreenSurface->setGeometry(-1, -1, 1, 1);
- m_offscreenSurface->create();
-
- m_glContext = new QOpenGLContext;
- m_glContext->setFormat(m_offscreenSurface->requestedFormat());
- m_glContext->setShareContext(shareContext);
-
- if (!m_glContext->create()) {
- delete m_glContext;
- delete m_offscreenSurface;
- m_glContext = 0;
- m_offscreenSurface = 0;
- return;
- }
-
- currentContext = m_glContext;
- }
+ Q_ASSERT(QOpenGLContext::currentContext() != NULL);
- if (m_glContext)
- m_glContext->makeCurrent(m_offscreenSurface);
+ if (!m_glResources)
+ m_glResources = new OpenGLResources;
- if (!m_egl)
- m_egl = new EGLWrapper;
+ QOpenGLContext *currentContext = QOpenGLContext::currentContext();
+ if (!currentContext)
+ return false;
QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
- m_eglDisplay = static_cast<EGLDisplay*>(
+ m_glResources->eglDisplay = static_cast<EGLDisplay*>(
nativeInterface->nativeResourceForContext("eglDisplay", currentContext));
- m_eglConfig = static_cast<EGLConfig*>(
+ EGLConfig *eglConfig = static_cast<EGLConfig*>(
nativeInterface->nativeResourceForContext("eglConfig", currentContext));
- currentContext->functions()->glGenTextures(1, &m_glTexture);
+ currentContext->functions()->glGenTextures(1, &m_glResources->glTexture);
- int w = m_surfaceFormat.frameWidth();
- int h = m_surfaceFormat.frameHeight();
bool hasAlpha = currentContext->format().hasAlpha();
EGLint attribs[] = {
- EGL_WIDTH, w,
- EGL_HEIGHT, h,
+ EGL_WIDTH, m_surfaceFormat.frameWidth(),
+ EGL_HEIGHT, m_surfaceFormat.frameHeight(),
EGL_TEXTURE_FORMAT, hasAlpha ? EGL_TEXTURE_RGBA : EGL_TEXTURE_RGB,
EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
EGL_NONE
};
- EGLSurface pbuffer = m_egl->createPbufferSurface(m_eglDisplay, m_eglConfig, attribs);
+ EGLSurface pbuffer = m_glResources->egl->createPbufferSurface(m_glResources->eglDisplay, eglConfig, attribs);
HANDLE share_handle = 0;
PFNEGLQUERYSURFACEPOINTERANGLEPROC eglQuerySurfacePointerANGLE =
- reinterpret_cast<PFNEGLQUERYSURFACEPOINTERANGLEPROC>(m_egl->getProcAddress("eglQuerySurfacePointerANGLE"));
+ reinterpret_cast<PFNEGLQUERYSURFACEPOINTERANGLEPROC>(m_glResources->egl->getProcAddress("eglQuerySurfacePointerANGLE"));
Q_ASSERT(eglQuerySurfacePointerANGLE);
eglQuerySurfacePointerANGLE(
- m_eglDisplay,
+ m_glResources->eglDisplay,
pbuffer,
EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, &share_handle);
- m_device->CreateTexture(w, h, 1,
+ m_device->CreateTexture(m_surfaceFormat.frameWidth(), m_surfaceFormat.frameHeight(), 1,
D3DUSAGE_RENDERTARGET,
hasAlpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8,
D3DPOOL_DEFAULT,
&m_texture,
&share_handle);
- m_eglSurface = pbuffer;
+ m_glResources->eglSurface = pbuffer;
+
+ QOpenGLContext::currentContext()->functions()->glBindTexture(GL_TEXTURE_2D, m_glResources->glTexture);
+ m_glResources->egl->bindTexImage(m_glResources->eglDisplay, m_glResources->eglSurface, EGL_BACK_BUFFER);
- if (m_glContext)
- m_glContext->doneCurrent();
+ return m_texture != NULL;
}
bool D3DPresentEngine::updateTexture(IDirect3DSurface9 *src)
{
- if (!m_texture)
+ if (!m_texture && !createRenderTexture())
return false;
- if (m_glContext)
- m_glContext->makeCurrent(m_offscreenSurface);
-
- QOpenGLContext::currentContext()->functions()->glBindTexture(GL_TEXTURE_2D, m_glTexture);
-
IDirect3DSurface9 *dest = NULL;
// Copy the sample surface to the shared D3D/EGL surface
@@ -485,173 +615,24 @@ bool D3DPresentEngine::updateTexture(IDirect3DSurface9 *src)
goto done;
hr = m_device->StretchRect(src, NULL, dest, NULL, D3DTEXF_NONE);
- if (FAILED(hr))
+ if (FAILED(hr)) {
qWarning("Failed to copy D3D surface");
-
- if (hr == S_OK)
- m_egl->bindTexImage(m_eglDisplay, m_eglSurface, EGL_BACK_BUFFER);
+ } else {
+ // Shared surfaces are not synchronized, there's no guarantee that
+ // StretchRect is complete when the texture is later rendered by Qt.
+ // To make sure the next rendered frame is up to date, flush the command pipeline
+ // using an event query.
+ IDirect3DQuery9 *eventQuery = NULL;
+ m_device->CreateQuery(D3DQUERYTYPE_EVENT, &eventQuery);
+ eventQuery->Issue(D3DISSUE_END);
+ while (eventQuery->GetData(NULL, 0, D3DGETDATA_FLUSH) == S_FALSE);
+ eventQuery->Release();
+ }
done:
- qt_wmf_safeRelease(&dest);
-
- if (m_glContext)
- m_glContext->doneCurrent();
+ qt_evr_safe_release(&dest);
return SUCCEEDED(hr);
}
-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;
-
- // Hold the lock because we might be discarding an existing device.
- QMutexLocker locker(&m_mutex);
-
- if (!m_D3D9 || !m_deviceManager)
- return MF_E_NOT_INITIALIZED;
-
- hwnd = ::GetShellWindow();
-
- // Note: The presenter creates additional swap chains to present the
- // video frames. Therefore, it does not use the device's implicit
- // swap chain, so the size of the back buffer here is 1 x 1.
-
- 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_wmf_safeRelease(&m_device);
-
- m_device = device;
- m_device->AddRef();
-
-done:
- qt_wmf_safeRelease(&device);
- return hr;
-}
-
-HRESULT D3DPresentEngine::createD3DSample(IDirect3DSwapChain9 *swapChain, IMFSample **videoSample)
-{
- D3DCOLOR clrBlack = D3DCOLOR_ARGB(0xFF, 0x00, 0x00, 0x00);
-
- IDirect3DSurface9* surface = NULL;
- IMFSample* sample = NULL;
-
- // Get the back buffer surface.
- HRESULT hr = swapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &surface);
- if (FAILED(hr))
- goto done;
-
- // Fill it with black.
- hr = m_device->ColorFill(surface, NULL, clrBlack);
- if (FAILED(hr))
- goto done;
-
- hr = MFCreateVideoSampleFromSurface(surface, &sample);
- if (FAILED(hr))
- goto done;
-
- *videoSample = sample;
- (*videoSample)->AddRef();
-
-done:
- qt_wmf_safeRelease(&surface);
- qt_wmf_safeRelease(&sample);
- return hr;
-}
-
-HRESULT D3DPresentEngine::getSwapChainPresentParameters(IMFMediaType *type, D3DPRESENT_PARAMETERS* pp)
-{
- ZeroMemory(pp, sizeof(D3DPRESENT_PARAMETERS));
-
- // Get some information about the video format.
-
- UINT32 width = 0, height = 0;
-
- HRESULT hr = MFGetAttributeSize(type, MF_MT_FRAME_SIZE, &width, &height);
- if (FAILED(hr))
- return hr;
-
- DWORD d3dFormat = 0;
-
- hr = qt_wmf_getFourCC(type, &d3dFormat);
- if (FAILED(hr))
- return hr;
-
- ZeroMemory(pp, sizeof(D3DPRESENT_PARAMETERS));
- pp->BackBufferWidth = width;
- pp->BackBufferHeight = height;
- pp->Windowed = TRUE;
- pp->SwapEffect = D3DSWAPEFFECT_DISCARD;
- pp->BackBufferFormat = (D3DFORMAT)d3dFormat;
- pp->hDeviceWindow = ::GetShellWindow();
- pp->Flags = D3DPRESENTFLAG_VIDEO;
- pp->PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
-
- D3DDEVICE_CREATION_PARAMETERS params;
- hr = m_device->GetCreationParameters(&params);
- if (FAILED(hr))
- return hr;
-
- if (params.DeviceType != D3DDEVTYPE_HAL)
- pp->Flags |= D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
-
- return S_OK;
-}
+#endif // MAYBE_ANGLE
diff --git a/src/plugins/wmf/evrd3dpresentengine.h b/src/plugins/common/evr/evrd3dpresentengine.h
index 7a88ee555..f3bbb8b3e 100644
--- a/src/plugins/wmf/evrd3dpresentengine.h
+++ b/src/plugins/common/evr/evrd3dpresentengine.h
@@ -34,33 +34,36 @@
#ifndef EVRD3DPRESENTENGINE_H
#define EVRD3DPRESENTENGINE_H
-#include <QObject>
#include <EGL/egl.h>
#include <QMutex>
#include <d3d9types.h>
#include <QVideoSurfaceFormat>
+#if defined(QT_OPENGL_ES_2) || defined(QT_OPENGL_DYNAMIC)
+#define MAYBE_ANGLE
+#endif
+
+QT_BEGIN_NAMESPACE
+class QAbstractVideoSurface;
+QT_END_NAMESPACE
+
struct IDirect3D9Ex;
-struct IDirect3DDevice9;
struct IDirect3DDevice9Ex;
struct IDirect3DDeviceManager9;
struct IDirect3DSurface9;
struct IDirect3DTexture9;
struct IMFSample;
struct IMFMediaType;
-struct IDirect3DSwapChain9;
// Randomly generated GUIDs
static const GUID MFSamplePresenter_SampleCounter =
{ 0xb0bb83cc, 0xf10f, 0x4e2e, { 0xaa, 0x2b, 0x29, 0xea, 0x5e, 0x92, 0xef, 0x85 } };
-static const GUID MFSamplePresenter_SampleSwapChain =
-{ 0xad885bd1, 0x7def, 0x414a, { 0xb5, 0xb0, 0xd3, 0xd2, 0x63, 0xd6, 0xe9, 0x6d } };
+QT_USE_NAMESPACE
-QT_BEGIN_NAMESPACE
+#ifdef MAYBE_ANGLE
-class QAbstractVideoSurface;
-class QOpenGLContext;
+class OpenGLResources;
class EGLWrapper
{
@@ -87,40 +90,39 @@ private:
EglReleaseTexImage m_eglReleaseTexImage;
};
-class D3DPresentEngine : public QObject
+#endif // MAYBE_ANGLE
+
+class D3DPresentEngine
{
- Q_OBJECT
public:
+ enum Hint
+ {
+ RenderToTexture
+ };
+
D3DPresentEngine();
virtual ~D3DPresentEngine();
- void start();
- void stop();
+ bool isValid() const;
+ void setHint(Hint hint, bool enable = true);
HRESULT getService(REFGUID guidService, REFIID riid, void** ppv);
HRESULT checkFormat(D3DFORMAT format);
-
- HRESULT createVideoSamples(IMFMediaType *format, QList<IMFSample*>& videoSampleQueue);
- void releaseResources();
-
UINT refreshRate() const { return m_displayMode.RefreshRate; }
- void setSurface(QAbstractVideoSurface *surface);
- void setSurfaceFormat(const QVideoSurfaceFormat &format);
+ bool supportsTextureRendering() const;
+ bool isTextureRenderingEnabled() const { return m_useTextureRendering; }
- void createOffscreenTexture();
- bool updateTexture(IDirect3DSurface9 *src);
+ HRESULT createVideoSamples(IMFMediaType *format, QList<IMFSample*>& videoSampleQueue);
+ QVideoSurfaceFormat videoSurfaceFormat() const { return m_surfaceFormat; }
+ QVideoFrame makeVideoFrame(IMFSample* sample);
-public Q_SLOTS:
- void presentSample(void* sample, qint64 llTarget);
+ void releaseResources();
private:
HRESULT initializeD3D();
- HRESULT getSwapChainPresentParameters(IMFMediaType *type, D3DPRESENT_PARAMETERS *pp);
HRESULT createD3DDevice();
- HRESULT createD3DSample(IDirect3DSwapChain9 *swapChain, IMFSample **videoSample);
- QMutex m_mutex;
UINT m_deviceResetToken;
D3DDISPLAYMODE m_displayMode;
@@ -130,19 +132,18 @@ private:
IDirect3DDeviceManager9 *m_deviceManager;
QVideoSurfaceFormat m_surfaceFormat;
- QAbstractVideoSurface *m_surface;
- QOpenGLContext *m_glContext;
- QWindow *m_offscreenSurface;
+ bool m_useTextureRendering;
- EGLDisplay *m_eglDisplay;
- EGLConfig *m_eglConfig;
- EGLSurface m_eglSurface;
- unsigned int m_glTexture;
+#ifdef MAYBE_ANGLE
+ bool createRenderTexture();
+ bool updateTexture(IDirect3DSurface9 *src);
+
+ OpenGLResources *m_glResources;
IDirect3DTexture9 *m_texture;
- EGLWrapper *m_egl;
-};
+#endif
-QT_END_NAMESPACE
+ friend class IMFSampleVideoBuffer;
+};
#endif // EVRD3DPRESENTENGINE_H
diff --git a/src/plugins/common/evr/evrdefs.cpp b/src/plugins/common/evr/evrdefs.cpp
new file mode 100644
index 000000000..07d1c11eb
--- /dev/null
+++ b/src/plugins/common/evr/evrdefs.cpp
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "evrdefs.h"
+
+const CLSID clsid_EnhancedVideoRenderer = { 0xfa10746c, 0x9b63, 0x4b6c, {0xbc, 0x49, 0xfc, 0x30, 0xe, 0xa5, 0xf2, 0x56} };
+const GUID mr_VIDEO_RENDER_SERVICE = { 0x1092a86c, 0xab1a, 0x459a, {0xa3, 0x36, 0x83, 0x1f, 0xbc, 0x4d, 0x11, 0xff} };
+const GUID mr_VIDEO_MIXER_SERVICE = { 0x73cd2fc, 0x6cf4, 0x40b7, {0x88, 0x59, 0xe8, 0x95, 0x52, 0xc8, 0x41, 0xf8} };
+const GUID mr_BUFFER_SERVICE = { 0xa562248c, 0x9ac6, 0x4ffc, {0x9f, 0xba, 0x3a, 0xf8, 0xf8, 0xad, 0x1a, 0x4d} };
+const GUID video_ZOOM_RECT = { 0x7aaa1638, 0x1b7f, 0x4c93, {0xbd, 0x89, 0x5b, 0x9c, 0x9f, 0xb6, 0xfc, 0xf0} };
+const GUID iid_IDirect3DDevice9 = { 0xd0223b96, 0xbf7a, 0x43fd, {0x92, 0xbd, 0xa4, 0x3b, 0xd, 0x82, 0xb9, 0xeb} };
+const GUID iid_IDirect3DSurface9 = { 0xcfbaf3a, 0x9ff6, 0x429a, {0x99, 0xb3, 0xa2, 0x79, 0x6a, 0xf8, 0xb8, 0x9b} };
diff --git a/src/plugins/common/evr/evrdefs.h b/src/plugins/common/evr/evrdefs.h
index ce6ca6584..3b2c2530a 100644
--- a/src/plugins/common/evr/evrdefs.h
+++ b/src/plugins/common/evr/evrdefs.h
@@ -36,10 +36,47 @@
#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
+
#ifdef __GNUC__
typedef struct MFVideoNormalizedRect {
float left;
@@ -49,6 +86,8 @@ typedef struct MFVideoNormalizedRect {
} 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);
@@ -123,5 +162,185 @@ __CRT_UUID_DECL(IMFVideoProcessor, 0x6AB0000C, 0xFECE, 0x4d1f, 0xA2,0xAC, 0xA9,0
#endif
#endif // __IMFVideoProcessor_INTERFACE_DEFINED__
+#ifndef __IMFVideoDeviceID_INTERFACE_DEFINED__
+#define __IMFVideoDeviceID_INTERFACE_DEFINED__
+DEFINE_GUID(IID_IMFVideoDeviceID, 0xA38D9567, 0x5A9C, 0x4f3c, 0xB2,0x93, 0x8E,0xB4,0x15,0xB2,0x79,0xBA);
+MIDL_INTERFACE("A38D9567-5A9C-4f3c-B293-8EB415B279BA")
+IMFVideoDeviceID : public IUnknown
+{
+public:
+ virtual HRESULT STDMETHODCALLTYPE GetDeviceID(IID *pDeviceID) = 0;
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IMFVideoDeviceID, 0xA38D9567, 0x5A9C, 0x4f3c, 0xB2,0x93, 0x8E,0xB4,0x15,0xB2,0x79,0xBA)
+#endif
+#endif // __IMFVideoDeviceID_INTERFACE_DEFINED__
+
+#ifndef __IMFClockStateSink_INTERFACE_DEFINED__
+#define __IMFClockStateSink_INTERFACE_DEFINED__
+DEFINE_GUID(IID_IMFClockStateSink, 0xF6696E82, 0x74F7, 0x4f3d, 0xA1,0x78, 0x8A,0x5E,0x09,0xC3,0x65,0x9F);
+MIDL_INTERFACE("F6696E82-74F7-4f3d-A178-8A5E09C3659F")
+IMFClockStateSink : public IUnknown
+{
+public:
+ virtual HRESULT STDMETHODCALLTYPE OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset) = 0;
+ virtual HRESULT STDMETHODCALLTYPE OnClockStop(MFTIME hnsSystemTime) = 0;
+ virtual HRESULT STDMETHODCALLTYPE OnClockPause(MFTIME hnsSystemTime) = 0;
+ virtual HRESULT STDMETHODCALLTYPE OnClockRestart(MFTIME hnsSystemTime) = 0;
+ virtual HRESULT STDMETHODCALLTYPE OnClockSetRate(MFTIME hnsSystemTime, float flRate) = 0;
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IMFClockStateSink, 0xF6696E82, 0x74F7, 0x4f3d, 0xA1,0x78, 0x8A,0x5E,0x09,0xC3,0x65,0x9F)
+#endif
+#endif // __IMFClockStateSink_INTERFACE_DEFINED__
+
+#ifndef __IMFVideoPresenter_INTERFACE_DEFINED__
+#define __IMFVideoPresenter_INTERFACE_DEFINED__
+typedef enum MFVP_MESSAGE_TYPE
+{
+ MFVP_MESSAGE_FLUSH = 0,
+ MFVP_MESSAGE_INVALIDATEMEDIATYPE = 0x1,
+ MFVP_MESSAGE_PROCESSINPUTNOTIFY = 0x2,
+ MFVP_MESSAGE_BEGINSTREAMING = 0x3,
+ MFVP_MESSAGE_ENDSTREAMING = 0x4,
+ MFVP_MESSAGE_ENDOFSTREAM = 0x5,
+ MFVP_MESSAGE_STEP = 0x6,
+ MFVP_MESSAGE_CANCELSTEP = 0x7
+} MFVP_MESSAGE_TYPE;
+
+DEFINE_GUID(IID_IMFVideoPresenter, 0x29AFF080, 0x182A, 0x4a5d, 0xAF,0x3B, 0x44,0x8F,0x3A,0x63,0x46,0xCB);
+MIDL_INTERFACE("29AFF080-182A-4a5d-AF3B-448F3A6346CB")
+IMFVideoPresenter : public IMFClockStateSink
+{
+public:
+ virtual HRESULT STDMETHODCALLTYPE ProcessMessage(MFVP_MESSAGE_TYPE eMessage, ULONG_PTR ulParam) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetCurrentMediaType(IMFVideoMediaType **ppMediaType) = 0;
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IMFVideoPresenter, 0x29AFF080, 0x182A, 0x4a5d, 0xAF,0x3B, 0x44,0x8F,0x3A,0x63,0x46,0xCB)
+#endif
+#endif // __IMFVideoPresenter_INTERFACE_DEFINED__
+
+#ifndef __IMFRateSupport_INTERFACE_DEFINED__
+#define __IMFRateSupport_INTERFACE_DEFINED__
+DEFINE_GUID(IID_IMFRateSupport, 0x0a9ccdbc, 0xd797, 0x4563, 0x96,0x67, 0x94,0xec,0x5d,0x79,0x29,0x2d);
+MIDL_INTERFACE("0a9ccdbc-d797-4563-9667-94ec5d79292d")
+IMFRateSupport : public IUnknown
+{
+public:
+ virtual HRESULT STDMETHODCALLTYPE GetSlowestRate(MFRATE_DIRECTION eDirection, BOOL fThin, float *pflRate) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetFastestRate(MFRATE_DIRECTION eDirection, BOOL fThin, float *pflRate) = 0;
+ virtual HRESULT STDMETHODCALLTYPE IsRateSupported(BOOL fThin, float flRate, float *pflNearestSupportedRate) = 0;
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IMFRateSupport, 0x0a9ccdbc, 0xd797, 0x4563, 0x96,0x67, 0x94,0xec,0x5d,0x79,0x29,0x2d)
+#endif
+#endif // __IMFRateSupport_INTERFACE_DEFINED__
+
+#ifndef __IMFTopologyServiceLookup_INTERFACE_DEFINED__
+#define __IMFTopologyServiceLookup_INTERFACE_DEFINED__
+typedef enum _MF_SERVICE_LOOKUP_TYPE
+{
+ MF_SERVICE_LOOKUP_UPSTREAM = 0,
+ MF_SERVICE_LOOKUP_UPSTREAM_DIRECT = (MF_SERVICE_LOOKUP_UPSTREAM + 1),
+ MF_SERVICE_LOOKUP_DOWNSTREAM = (MF_SERVICE_LOOKUP_UPSTREAM_DIRECT + 1),
+ MF_SERVICE_LOOKUP_DOWNSTREAM_DIRECT = (MF_SERVICE_LOOKUP_DOWNSTREAM + 1),
+ MF_SERVICE_LOOKUP_ALL = (MF_SERVICE_LOOKUP_DOWNSTREAM_DIRECT + 1),
+ MF_SERVICE_LOOKUP_GLOBAL = (MF_SERVICE_LOOKUP_ALL + 1)
+} MF_SERVICE_LOOKUP_TYPE;
+
+DEFINE_GUID(IID_IMFTopologyServiceLookup, 0xfa993889, 0x4383, 0x415a, 0xa9,0x30, 0xdd,0x47,0x2a,0x8c,0xf6,0xf7);
+MIDL_INTERFACE("fa993889-4383-415a-a930-dd472a8cf6f7")
+IMFTopologyServiceLookup : public IUnknown
+{
+public:
+ virtual HRESULT STDMETHODCALLTYPE LookupService(MF_SERVICE_LOOKUP_TYPE Type,
+ DWORD dwIndex,
+ REFGUID guidService,
+ REFIID riid,
+ LPVOID *ppvObjects,
+ DWORD *pnObjects) = 0;
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IMFTopologyServiceLookup, 0xfa993889, 0x4383, 0x415a, 0xa9,0x30, 0xdd,0x47,0x2a,0x8c,0xf6,0xf7)
+#endif
+#endif // __IMFTopologyServiceLookup_INTERFACE_DEFINED__
+
+#ifndef __IMFTopologyServiceLookupClient_INTERFACE_DEFINED__
+#define __IMFTopologyServiceLookupClient_INTERFACE_DEFINED__
+DEFINE_GUID(IID_IMFTopologyServiceLookupClient, 0xfa99388a, 0x4383, 0x415a, 0xa9,0x30, 0xdd,0x47,0x2a,0x8c,0xf6,0xf7);
+MIDL_INTERFACE("fa99388a-4383-415a-a930-dd472a8cf6f7")
+IMFTopologyServiceLookupClient : public IUnknown
+{
+public:
+ virtual HRESULT STDMETHODCALLTYPE InitServicePointers(IMFTopologyServiceLookup *pLookup) = 0;
+ virtual HRESULT STDMETHODCALLTYPE ReleaseServicePointers(void) = 0;
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IMFTopologyServiceLookupClient, 0xfa99388a, 0x4383, 0x415a, 0xa9,0x30, 0xdd,0x47,0x2a,0x8c,0xf6,0xf7)
+#endif
+#endif // __IMFTopologyServiceLookupClient_INTERFACE_DEFINED__
+
+#ifndef __IMediaEventSink_INTERFACE_DEFINED__
+#define __IMediaEventSink_INTERFACE_DEFINED__
+DEFINE_GUID(IID_IMediaEventSink, 0x56a868a2, 0x0ad4, 0x11ce, 0xb0,0x3a, 0x00,0x20,0xaf,0x0b,0xa7,0x70);
+MIDL_INTERFACE("56a868a2-0ad4-11ce-b03a-0020af0ba770")
+IMediaEventSink : public IUnknown
+{
+public:
+ virtual HRESULT STDMETHODCALLTYPE Notify(long EventCode, LONG_PTR EventParam1, LONG_PTR EventParam2) = 0;
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IMediaEventSink, 0x56a868a2, 0x0ad4, 0x11ce, 0xb0,0x3a, 0x00,0x20,0xaf,0x0b,0xa7,0x70)
+#endif
+#endif // __IMediaEventSink_INTERFACE_DEFINED__
+
+#ifndef __IMFVideoRenderer_INTERFACE_DEFINED__
+#define __IMFVideoRenderer_INTERFACE_DEFINED__
+DEFINE_GUID(IID_IMFVideoRenderer, 0xDFDFD197, 0xA9CA, 0x43d8, 0xB3,0x41, 0x6A,0xF3,0x50,0x37,0x92,0xCD);
+MIDL_INTERFACE("DFDFD197-A9CA-43d8-B341-6AF3503792CD")
+IMFVideoRenderer : public IUnknown
+{
+public:
+ virtual HRESULT STDMETHODCALLTYPE InitializeRenderer(IMFTransform *pVideoMixer,
+ IMFVideoPresenter *pVideoPresenter) = 0;
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IMFVideoRenderer, 0xDFDFD197, 0xA9CA, 0x43d8, 0xB3,0x41, 0x6A,0xF3,0x50,0x37,0x92,0xCD)
+#endif
+#endif // __IMFVideoRenderer_INTERFACE_DEFINED__
+
+#ifndef __IMFTrackedSample_INTERFACE_DEFINED__
+#define __IMFTrackedSample_INTERFACE_DEFINED__
+DEFINE_GUID(IID_IMFTrackedSample, 0x245BF8E9, 0x0755, 0x40f7, 0x88,0xA5, 0xAE,0x0F,0x18,0xD5,0x5E,0x17);
+MIDL_INTERFACE("245BF8E9-0755-40f7-88A5-AE0F18D55E17")
+IMFTrackedSample : public IUnknown
+{
+public:
+ virtual HRESULT STDMETHODCALLTYPE SetAllocator(IMFAsyncCallback *pSampleAllocator, IUnknown *pUnkState) = 0;
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IMFTrackedSample, 0x245BF8E9, 0x0755, 0x40f7, 0x88,0xA5, 0xAE,0x0F,0x18,0xD5,0x5E,0x17)
+#endif
+#endif // __IMFTrackedSample_INTERFACE_DEFINED__
+
+#ifndef __IMFDesiredSample_INTERFACE_DEFINED__
+#define __IMFDesiredSample_INTERFACE_DEFINED__
+DEFINE_GUID(IID_IMFDesiredSample, 0x56C294D0, 0x753E, 0x4260, 0x8D,0x61, 0xA3,0xD8,0x82,0x0B,0x1D,0x54);
+MIDL_INTERFACE("56C294D0-753E-4260-8D61-A3D8820B1D54")
+IMFDesiredSample : public IUnknown
+{
+public:
+ virtual HRESULT STDMETHODCALLTYPE GetDesiredSampleTimeAndDuration(LONGLONG *phnsSampleTime,
+ LONGLONG *phnsSampleDuration) = 0;
+ virtual void STDMETHODCALLTYPE SetDesiredSampleTimeAndDuration(LONGLONG hnsSampleTime,
+ LONGLONG hnsSampleDuration) = 0;
+ virtual void STDMETHODCALLTYPE Clear( void) = 0;
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IMFDesiredSample, 0x56C294D0, 0x753E, 0x4260, 0x8D,0x61, 0xA3,0xD8,0x82,0x0B,0x1D,0x54)
+#endif
+#endif
+
#endif // EVRDEFS_H
diff --git a/src/plugins/wmf/mfglobal.cpp b/src/plugins/common/evr/evrhelpers.cpp
index 55f2882db..2338d40fc 100644
--- a/src/plugins/wmf/mfglobal.cpp
+++ b/src/plugins/common/evr/evrhelpers.cpp
@@ -31,9 +31,16 @@
**
****************************************************************************/
-#include "mfglobal.h"
+#include "evrhelpers.h"
-HRESULT qt_wmf_getFourCC(IMFMediaType *type, DWORD *fourCC)
+#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
+
+HRESULT qt_evr_getFourCC(IMFMediaType *type, DWORD *fourCC)
{
if (!fourCC)
return E_POINTER;
@@ -50,20 +57,7 @@ HRESULT qt_wmf_getFourCC(IMFMediaType *type, DWORD *fourCC)
return hr;
}
-MFRatio qt_wmf_getPixelAspectRatio(IMFMediaType *type)
-{
- MFRatio ratio = { 0, 0 };
- HRESULT hr = S_OK;
-
- hr = MFGetAttributeRatio(type, MF_MT_PIXEL_ASPECT_RATIO, (UINT32*)&ratio.Numerator, (UINT32*)&ratio.Denominator);
- if (FAILED(hr)) {
- ratio.Numerator = 1;
- ratio.Denominator = 1;
- }
- return ratio;
-}
-
-bool qt_wmf_areMediaTypesEqual(IMFMediaType *type1, IMFMediaType *type2)
+bool qt_evr_areMediaTypesEqual(IMFMediaType *type1, IMFMediaType *type2)
{
if (!type1 && !type2)
return true;
@@ -76,10 +70,10 @@ bool qt_wmf_areMediaTypesEqual(IMFMediaType *type1, IMFMediaType *type2)
return (hr == S_OK);
}
-HRESULT qt_wmf_validateVideoArea(const MFVideoArea& area, UINT32 width, UINT32 height)
+HRESULT qt_evr_validateVideoArea(const MFVideoArea& area, UINT32 width, UINT32 height)
{
- float fOffsetX = qt_wmf_MFOffsetToFloat(area.OffsetX);
- float fOffsetY = qt_wmf_MFOffsetToFloat(area.OffsetY);
+ 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) )
@@ -88,7 +82,7 @@ HRESULT qt_wmf_validateVideoArea(const MFVideoArea& area, UINT32 width, UINT32 h
return S_OK;
}
-bool qt_wmf_isSampleTimePassed(IMFClock *clock, IMFSample *sample)
+bool qt_evr_isSampleTimePassed(IMFClock *clock, IMFSample *sample)
{
if (!sample || !clock)
return false;
@@ -114,3 +108,69 @@ bool qt_wmf_isSampleTimePassed(IMFClock *clock, IMFSample *sample)
return false;
}
+
+QVideoFrame::PixelFormat qt_evr_pixelFormatFromD3DFormat(D3DFORMAT 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;
+ }
+}
diff --git a/src/plugins/common/evr/evrhelpers.h b/src/plugins/common/evr/evrhelpers.h
new file mode 100644
index 000000000..1b1f64194
--- /dev/null
+++ b/src/plugins/common/evr/evrhelpers.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef EVRHELPERS_H
+#define EVRHELPERS_H
+
+#include "evrdefs.h"
+#include <qvideoframe.h>
+
+QT_USE_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, (UINT32*)&pRatio->Numerator, (UINT32*)&pRatio->Denominator);
+}
+
+QVideoFrame::PixelFormat qt_evr_pixelFormatFromD3DFormat(D3DFORMAT format);
+D3DFORMAT qt_evr_D3DFormatFromPixelFormat(QVideoFrame::PixelFormat format);
+
+#endif // EVRHELPERS_H
+
diff --git a/src/plugins/common/evr/evrvideowindowcontrol.cpp b/src/plugins/common/evr/evrvideowindowcontrol.cpp
index faa23d6e5..dae6583ff 100644
--- a/src/plugins/common/evr/evrvideowindowcontrol.cpp
+++ b/src/plugins/common/evr/evrvideowindowcontrol.cpp
@@ -65,8 +65,6 @@ bool EvrVideoWindowControl::setEvr(IUnknown *evr)
if (!evr)
return true;
- static const GUID mr_VIDEO_RENDER_SERVICE = { 0x1092a86c, 0xab1a, 0x459a, {0xa3, 0x36, 0x83, 0x1f, 0xbc, 0x4d, 0x11, 0xff} };
- static const GUID mr_VIDEO_MIXER_SERVICE = { 0x73cd2fc, 0x6cf4, 0x40b7, {0x88, 0x59, 0xe8, 0x95, 0x52, 0xc8, 0x41, 0xf8} };
IMFGetService *service = NULL;
if (SUCCEEDED(evr->QueryInterface(IID_PPV_ARGS(&service)))
diff --git a/src/plugins/directshow/camera/camera.pri b/src/plugins/directshow/camera/camera.pri
index 3a532f472..c6b16da59 100644
--- a/src/plugins/directshow/camera/camera.pri
+++ b/src/plugins/directshow/camera/camera.pri
@@ -14,7 +14,8 @@ HEADERS += \
$$PWD/dsimagecapturecontrol.h \
$$PWD/dscamerasession.h \
$$PWD/directshowglobal.h \
- $$PWD/dscameraviewfindersettingscontrol.h
+ $$PWD/dscameraviewfindersettingscontrol.h \
+ $$PWD/dscameraimageprocessingcontrol.h
SOURCES += \
$$PWD/dscameraservice.cpp \
@@ -23,7 +24,8 @@ SOURCES += \
$$PWD/dsvideodevicecontrol.cpp \
$$PWD/dsimagecapturecontrol.cpp \
$$PWD/dscamerasession.cpp \
- $$PWD/dscameraviewfindersettingscontrol.cpp
+ $$PWD/dscameraviewfindersettingscontrol.cpp \
+ $$PWD/dscameraimageprocessingcontrol.cpp
*-msvc*:INCLUDEPATH += $$(DXSDK_DIR)/include
LIBS += -lstrmiids -ldmoguids -luuid -lmsdmo -lole32 -loleaut32
diff --git a/src/plugins/directshow/camera/dscameraimageprocessingcontrol.cpp b/src/plugins/directshow/camera/dscameraimageprocessingcontrol.cpp
new file mode 100644
index 000000000..39fa471ec
--- /dev/null
+++ b/src/plugins/directshow/camera/dscameraimageprocessingcontrol.cpp
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "dscameraimageprocessingcontrol.h"
+#include "dscamerasession.h"
+
+QT_BEGIN_NAMESPACE
+
+DSCameraImageProcessingControl::DSCameraImageProcessingControl(DSCameraSession *session)
+ : QCameraImageProcessingControl(session)
+ , m_session(session)
+{
+}
+
+DSCameraImageProcessingControl::~DSCameraImageProcessingControl()
+{
+}
+
+bool DSCameraImageProcessingControl::isParameterSupported(
+ QCameraImageProcessingControl::ProcessingParameter parameter) const
+{
+ return m_session->isImageProcessingParameterSupported(parameter);
+}
+
+bool DSCameraImageProcessingControl::isParameterValueSupported(
+ QCameraImageProcessingControl::ProcessingParameter parameter,
+ const QVariant &value) const
+{
+ return m_session->isImageProcessingParameterValueSupported(parameter, value);
+}
+
+QVariant DSCameraImageProcessingControl::parameter(
+ QCameraImageProcessingControl::ProcessingParameter parameter) const
+{
+ return m_session->imageProcessingParameter(parameter);
+}
+
+void DSCameraImageProcessingControl::setParameter(QCameraImageProcessingControl::ProcessingParameter parameter,
+ const QVariant &value)
+{
+ m_session->setImageProcessingParameter(parameter, value);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/dscameraimageprocessingcontrol.h b/src/plugins/directshow/camera/dscameraimageprocessingcontrol.h
new file mode 100644
index 000000000..2e50fe14d
--- /dev/null
+++ b/src/plugins/directshow/camera/dscameraimageprocessingcontrol.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DSCAMERAIMAGEPROCESSINGCONTROL_H
+#define DSCAMERAIMAGEPROCESSINGCONTROL_H
+
+#include <qcamera.h>
+#include <qcameraimageprocessingcontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class DSCameraSession;
+
+class DSCameraImageProcessingControl : public QCameraImageProcessingControl
+{
+ Q_OBJECT
+
+public:
+ DSCameraImageProcessingControl(DSCameraSession *session);
+ virtual ~DSCameraImageProcessingControl();
+
+ bool isParameterSupported(ProcessingParameter) const;
+ bool isParameterValueSupported(ProcessingParameter parameter, const QVariant &value) const;
+ QVariant parameter(ProcessingParameter parameter) const;
+ void setParameter(ProcessingParameter parameter, const QVariant &value);
+
+private:
+ DSCameraSession *m_session;
+};
+
+QT_END_NAMESPACE
+
+#endif // DSCAMERAIMAGEPROCESSINGCONTROL_H
diff --git a/src/plugins/directshow/camera/dscameraservice.cpp b/src/plugins/directshow/camera/dscameraservice.cpp
index 9fcd4de70..6c92df04b 100644
--- a/src/plugins/directshow/camera/dscameraservice.cpp
+++ b/src/plugins/directshow/camera/dscameraservice.cpp
@@ -41,6 +41,7 @@
#include "dsvideodevicecontrol.h"
#include "dsimagecapturecontrol.h"
#include "dscameraviewfindersettingscontrol.h"
+#include "dscameraimageprocessingcontrol.h"
QT_BEGIN_NAMESPACE
@@ -53,12 +54,14 @@ DSCameraService::DSCameraService(QObject *parent):
m_videoDevice = new DSVideoDeviceControl(m_session);
m_imageCapture = new DSImageCaptureControl(m_session);
m_viewfinderSettings = new DSCameraViewfinderSettingsControl(m_session);
+ m_imageProcessingControl = new DSCameraImageProcessingControl(m_session);
}
DSCameraService::~DSCameraService()
{
delete m_control;
delete m_viewfinderSettings;
+ delete m_imageProcessingControl;
delete m_videoDevice;
delete m_videoRenderer;
delete m_imageCapture;
@@ -86,6 +89,9 @@ QMediaControl* DSCameraService::requestControl(const char *name)
if (qstrcmp(name, QCameraViewfinderSettingsControl2_iid) == 0)
return m_viewfinderSettings;
+ if (qstrcmp(name, QCameraImageProcessingControl_iid) == 0)
+ return m_imageProcessingControl;
+
return 0;
}
diff --git a/src/plugins/directshow/camera/dscameraservice.h b/src/plugins/directshow/camera/dscameraservice.h
index c3c881d0e..05222ebc4 100644
--- a/src/plugins/directshow/camera/dscameraservice.h
+++ b/src/plugins/directshow/camera/dscameraservice.h
@@ -46,6 +46,7 @@ class DSVideoOutputControl;
class DSVideoDeviceControl;
class DSImageCaptureControl;
class DSCameraViewfinderSettingsControl;
+class DSCameraImageProcessingControl;
class DSCameraService : public QMediaService
{
@@ -66,6 +67,7 @@ private:
QMediaControl *m_videoRenderer;
DSImageCaptureControl *m_imageCapture;
DSCameraViewfinderSettingsControl *m_viewfinderSettings;
+ DSCameraImageProcessingControl *m_imageProcessingControl;
};
QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/dscamerasession.cpp b/src/plugins/directshow/camera/dscamerasession.cpp
index 24dad94c5..ede5e4763 100644
--- a/src/plugins/directshow/camera/dscamerasession.cpp
+++ b/src/plugins/directshow/camera/dscamerasession.cpp
@@ -230,6 +230,240 @@ void DSCameraSession::setViewfinderSettings(const QCameraViewfinderSettings &set
m_viewfinderSettings = settings;
}
+qreal DSCameraSession::scaledImageProcessingParameterValue(
+ const ImageProcessingParameterInfo &sourceValueInfo)
+{
+ if (sourceValueInfo.currentValue == sourceValueInfo.defaultValue) {
+ return 0.0f;
+ } else if (sourceValueInfo.currentValue < sourceValueInfo.defaultValue) {
+ return ((sourceValueInfo.currentValue - sourceValueInfo.minimumValue)
+ / qreal(sourceValueInfo.defaultValue - sourceValueInfo.minimumValue))
+ + (-1.0f);
+ } else {
+ return ((sourceValueInfo.currentValue - sourceValueInfo.defaultValue)
+ / qreal(sourceValueInfo.maximumValue - sourceValueInfo.defaultValue));
+ }
+}
+
+qint32 DSCameraSession::sourceImageProcessingParameterValue(
+ qreal scaledValue, const ImageProcessingParameterInfo &valueRange)
+{
+ if (qFuzzyIsNull(scaledValue)) {
+ return valueRange.defaultValue;
+ } else if (scaledValue < 0.0f) {
+ return ((scaledValue - (-1.0f)) * (valueRange.defaultValue - valueRange.minimumValue))
+ + valueRange.minimumValue;
+ } else {
+ return (scaledValue * (valueRange.maximumValue - valueRange.defaultValue))
+ + valueRange.defaultValue;
+ }
+}
+
+static QCameraImageProcessingControl::ProcessingParameter searchRelatedResultingParameter(
+ QCameraImageProcessingControl::ProcessingParameter sourceParameter)
+{
+ if (sourceParameter == QCameraImageProcessingControl::WhiteBalancePreset)
+ return QCameraImageProcessingControl::ColorTemperature;
+ return sourceParameter;
+}
+
+bool DSCameraSession::isImageProcessingParameterSupported(
+ QCameraImageProcessingControl::ProcessingParameter parameter) const
+{
+ const QCameraImageProcessingControl::ProcessingParameter resultingParameter =
+ searchRelatedResultingParameter(parameter);
+
+ return m_imageProcessingParametersInfos.contains(resultingParameter);
+}
+
+bool DSCameraSession::isImageProcessingParameterValueSupported(
+ QCameraImageProcessingControl::ProcessingParameter parameter,
+ const QVariant &value) const
+{
+ const QCameraImageProcessingControl::ProcessingParameter resultingParameter =
+ searchRelatedResultingParameter(parameter);
+
+ QMap<QCameraImageProcessingControl::ProcessingParameter,
+ ImageProcessingParameterInfo>::const_iterator sourceValueInfo =
+ m_imageProcessingParametersInfos.constFind(resultingParameter);
+
+ if (sourceValueInfo == m_imageProcessingParametersInfos.constEnd())
+ return false;
+
+ switch (parameter) {
+
+ case QCameraImageProcessingControl::WhiteBalancePreset: {
+ const QCameraImageProcessing::WhiteBalanceMode checkedValue =
+ value.value<QCameraImageProcessing::WhiteBalanceMode>();
+ // Supports only the Manual and the Auto values
+ if (checkedValue != QCameraImageProcessing::WhiteBalanceManual
+ && checkedValue != QCameraImageProcessing::WhiteBalanceAuto) {
+ return false;
+ }
+ }
+ break;
+
+ case QCameraImageProcessingControl::ColorTemperature: {
+ const qint32 checkedValue = value.toInt();
+ if (checkedValue < (*sourceValueInfo).minimumValue
+ || checkedValue > (*sourceValueInfo).maximumValue) {
+ return false;
+ }
+ }
+ break;
+
+ case QCameraImageProcessingControl::ContrastAdjustment: // falling back
+ case QCameraImageProcessingControl::SaturationAdjustment: // falling back
+ case QCameraImageProcessingControl::BrightnessAdjustment: // falling back
+ case QCameraImageProcessingControl::SharpeningAdjustment: {
+ const qint32 sourceValue = sourceImageProcessingParameterValue(
+ value.toReal(), (*sourceValueInfo));
+ if (sourceValue < (*sourceValueInfo).minimumValue
+ || sourceValue > (*sourceValueInfo).maximumValue)
+ return false;
+ }
+ break;
+
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+QVariant DSCameraSession::imageProcessingParameter(
+ QCameraImageProcessingControl::ProcessingParameter parameter) const
+{
+ if (!m_graphBuilder) {
+ qWarning() << "failed to access to the graph builder";
+ return QVariant();
+ }
+
+ const QCameraImageProcessingControl::ProcessingParameter resultingParameter =
+ searchRelatedResultingParameter(parameter);
+
+ QMap<QCameraImageProcessingControl::ProcessingParameter,
+ ImageProcessingParameterInfo>::const_iterator sourceValueInfo =
+ m_imageProcessingParametersInfos.constFind(resultingParameter);
+
+ if (sourceValueInfo == m_imageProcessingParametersInfos.constEnd())
+ return QVariant();
+
+ switch (parameter) {
+
+ case QCameraImageProcessingControl::WhiteBalancePreset:
+ return QVariant::fromValue<QCameraImageProcessing::WhiteBalanceMode>(
+ (*sourceValueInfo).capsFlags == VideoProcAmp_Flags_Auto
+ ? QCameraImageProcessing::WhiteBalanceAuto
+ : QCameraImageProcessing::WhiteBalanceManual);
+
+ case QCameraImageProcessingControl::ColorTemperature:
+ return QVariant::fromValue<qint32>((*sourceValueInfo).currentValue);
+
+ case QCameraImageProcessingControl::ContrastAdjustment: // falling back
+ case QCameraImageProcessingControl::SaturationAdjustment: // falling back
+ case QCameraImageProcessingControl::BrightnessAdjustment: // falling back
+ case QCameraImageProcessingControl::SharpeningAdjustment:
+ return scaledImageProcessingParameterValue((*sourceValueInfo));
+
+ default:
+ return QVariant();
+ }
+}
+
+void DSCameraSession::setImageProcessingParameter(
+ QCameraImageProcessingControl::ProcessingParameter parameter,
+ const QVariant &value)
+{
+ if (!m_graphBuilder) {
+ qWarning() << "failed to access to the graph builder";
+ return;
+ }
+
+ const QCameraImageProcessingControl::ProcessingParameter resultingParameter =
+ searchRelatedResultingParameter(parameter);
+
+ QMap<QCameraImageProcessingControl::ProcessingParameter,
+ ImageProcessingParameterInfo>::iterator sourceValueInfo =
+ m_imageProcessingParametersInfos.find(resultingParameter);
+
+ if (sourceValueInfo == m_imageProcessingParametersInfos.constEnd())
+ return;
+
+ LONG sourceValue = 0;
+ LONG capsFlags = VideoProcAmp_Flags_Manual;
+
+ switch (parameter) {
+
+ case QCameraImageProcessingControl::WhiteBalancePreset: {
+ const QCameraImageProcessing::WhiteBalanceMode checkedValue =
+ value.value<QCameraImageProcessing::WhiteBalanceMode>();
+ // Supports only the Manual and the Auto values
+ if (checkedValue == QCameraImageProcessing::WhiteBalanceManual)
+ capsFlags = VideoProcAmp_Flags_Manual;
+ else if (checkedValue == QCameraImageProcessing::WhiteBalanceAuto)
+ capsFlags = VideoProcAmp_Flags_Auto;
+ else
+ return;
+
+ sourceValue = ((*sourceValueInfo).hasBeenExplicitlySet)
+ ? (*sourceValueInfo).currentValue
+ : (*sourceValueInfo).defaultValue;
+ }
+ break;
+
+ case QCameraImageProcessingControl::ColorTemperature:
+ sourceValue = value.isValid() ?
+ value.value<qint32>() : (*sourceValueInfo).defaultValue;
+ capsFlags = (*sourceValueInfo).capsFlags;
+ break;
+
+ case QCameraImageProcessingControl::ContrastAdjustment: // falling back
+ case QCameraImageProcessingControl::SaturationAdjustment: // falling back
+ case QCameraImageProcessingControl::BrightnessAdjustment: // falling back
+ case QCameraImageProcessingControl::SharpeningAdjustment:
+ if (value.isValid()) {
+ sourceValue = sourceImageProcessingParameterValue(
+ value.toReal(), (*sourceValueInfo));
+ } else {
+ sourceValue = (*sourceValueInfo).defaultValue;
+ }
+ break;
+
+ default:
+ return;
+ }
+
+ IAMVideoProcAmp *pVideoProcAmp = NULL;
+ HRESULT hr = m_graphBuilder->FindInterface(
+ NULL,
+ NULL,
+ m_sourceFilter,
+ IID_IAMVideoProcAmp,
+ reinterpret_cast<void**>(&pVideoProcAmp)
+ );
+
+ if (FAILED(hr) || !pVideoProcAmp) {
+ qWarning() << "failed to find the video proc amp";
+ return;
+ }
+
+ hr = pVideoProcAmp->Set(
+ (*sourceValueInfo).videoProcAmpProperty,
+ sourceValue,
+ capsFlags);
+
+ pVideoProcAmp->Release();
+
+ if (FAILED(hr)) {
+ qWarning() << "failed to set the parameter value";
+ } else {
+ (*sourceValueInfo).capsFlags = capsFlags;
+ (*sourceValueInfo).hasBeenExplicitlySet = true;
+ (*sourceValueInfo).currentValue = sourceValue;
+ }
+}
+
bool DSCameraSession::load()
{
unload();
@@ -720,6 +954,81 @@ bool DSCameraSession::configurePreviewFormat()
return true;
}
+void DSCameraSession::updateImageProcessingParametersInfos()
+{
+ if (!m_graphBuilder) {
+ qWarning() << "failed to access to the graph builder";
+ return;
+ }
+
+ IAMVideoProcAmp *pVideoProcAmp = NULL;
+ const HRESULT hr = m_graphBuilder->FindInterface(
+ NULL,
+ NULL,
+ m_sourceFilter,
+ IID_IAMVideoProcAmp,
+ reinterpret_cast<void**>(&pVideoProcAmp)
+ );
+
+ if (FAILED(hr) || !pVideoProcAmp) {
+ qWarning() << "failed to find the video proc amp";
+ return;
+ }
+
+ for (int property = VideoProcAmp_Brightness; property <= VideoProcAmp_Gain; ++property) {
+
+ QCameraImageProcessingControl::ProcessingParameter processingParameter; // not initialized
+
+ switch (property) {
+ case VideoProcAmp_Brightness:
+ processingParameter = QCameraImageProcessingControl::BrightnessAdjustment;
+ break;
+ case VideoProcAmp_Contrast:
+ processingParameter = QCameraImageProcessingControl::ContrastAdjustment;
+ break;
+ case VideoProcAmp_Saturation:
+ processingParameter = QCameraImageProcessingControl::SaturationAdjustment;
+ break;
+ case VideoProcAmp_Sharpness:
+ processingParameter = QCameraImageProcessingControl::SharpeningAdjustment;
+ break;
+ case VideoProcAmp_WhiteBalance:
+ processingParameter = QCameraImageProcessingControl::ColorTemperature;
+ break;
+ default: // unsupported or not implemented yet parameter
+ continue;
+ }
+
+ ImageProcessingParameterInfo sourceValueInfo;
+ LONG steppingDelta = 0;
+
+ HRESULT hr = pVideoProcAmp->GetRange(
+ property,
+ &sourceValueInfo.minimumValue,
+ &sourceValueInfo.maximumValue,
+ &steppingDelta,
+ &sourceValueInfo.defaultValue,
+ &sourceValueInfo.capsFlags);
+
+ if (FAILED(hr))
+ continue;
+
+ hr = pVideoProcAmp->Get(
+ property,
+ &sourceValueInfo.currentValue,
+ &sourceValueInfo.capsFlags);
+
+ if (FAILED(hr))
+ continue;
+
+ sourceValueInfo.videoProcAmpProperty = static_cast<VideoProcAmpProperty>(property);
+
+ m_imageProcessingParametersInfos.insert(processingParameter, sourceValueInfo);
+ }
+
+ pVideoProcAmp->Release();
+}
+
bool DSCameraSession::connectGraph()
{
HRESULT hr = m_filterGraph->AddFilter(m_sourceFilter, L"Capture Filter");
@@ -806,6 +1115,7 @@ void DSCameraSession::updateSourceCapabilities()
for (AM_MEDIA_TYPE f : qAsConst(m_supportedFormats))
_FreeMediaType(f);
m_supportedFormats.clear();
+ m_imageProcessingParametersInfos.clear();
IAMVideoControl *pVideoControl = 0;
hr = m_graphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
@@ -915,6 +1225,8 @@ void DSCameraSession::updateSourceCapabilities()
}
pConfig->Release();
+
+ updateImageProcessingParametersInfos();
}
HRESULT getPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin)
diff --git a/src/plugins/directshow/camera/dscamerasession.h b/src/plugins/directshow/camera/dscamerasession.h
index 9ac121463..768e3583a 100644
--- a/src/plugins/directshow/camera/dscamerasession.h
+++ b/src/plugins/directshow/camera/dscamerasession.h
@@ -43,6 +43,7 @@
#include <QtMultimedia/qvideoframe.h>
#include <QtMultimedia/qabstractvideosurface.h>
#include <QtMultimedia/qvideosurfaceformat.h>
+#include <QtMultimedia/qcameraimageprocessingcontrol.h>
#include <private/qmediastoragelocation_p.h>
#include <tchar.h>
@@ -97,6 +98,20 @@ public:
QList<QCameraViewfinderSettings> supportedViewfinderSettings() const
{ return m_supportedViewfinderSettings; }
+ bool isImageProcessingParameterSupported(
+ QCameraImageProcessingControl::ProcessingParameter) const;
+
+ bool isImageProcessingParameterValueSupported(
+ QCameraImageProcessingControl::ProcessingParameter,
+ const QVariant &) const;
+
+ QVariant imageProcessingParameter(
+ QCameraImageProcessingControl::ProcessingParameter) const;
+
+ void setImageProcessingParameter(
+ QCameraImageProcessingControl::ProcessingParameter,
+ const QVariant &);
+
Q_SIGNALS:
void statusChanged(QCamera::Status);
void imageExposed(int id);
@@ -110,6 +125,27 @@ private Q_SLOTS:
void updateReadyForCapture();
private:
+ struct ImageProcessingParameterInfo {
+ ImageProcessingParameterInfo()
+ : minimumValue(0)
+ , maximumValue(0)
+ , defaultValue(0)
+ , currentValue(0)
+ , capsFlags(0)
+ , hasBeenExplicitlySet(false)
+ , videoProcAmpProperty(VideoProcAmp_Brightness)
+ {
+ }
+
+ LONG minimumValue;
+ LONG maximumValue;
+ LONG defaultValue;
+ LONG currentValue;
+ LONG capsFlags;
+ bool hasBeenExplicitlySet;
+ VideoProcAmpProperty videoProcAmpProperty;
+ };
+
void setStatus(QCamera::Status status);
void onFrameAvailable(const char *frameData, long len);
@@ -120,6 +156,14 @@ private:
void disconnectGraph();
void updateSourceCapabilities();
bool configurePreviewFormat();
+ void updateImageProcessingParametersInfos();
+
+ // These static functions are used for scaling of adjustable parameters,
+ // which have the ranges from -1.0 to +1.0 in the QCameraImageProcessing API.
+ static qreal scaledImageProcessingParameterValue(
+ const ImageProcessingParameterInfo &sourceValueInfo);
+ static qint32 sourceImageProcessingParameterValue(
+ qreal scaledValue, const ImageProcessingParameterInfo &sourceValueInfo);
QMutex m_presentMutex;
QMutex m_captureMutex;
@@ -135,6 +179,7 @@ private:
QList<AM_MEDIA_TYPE> m_supportedFormats;
QList<QCameraViewfinderSettings> m_supportedViewfinderSettings;
AM_MEDIA_TYPE m_sourceFormat;
+ QMap<QCameraImageProcessingControl::ProcessingParameter, ImageProcessingParameterInfo> m_imageProcessingParametersInfos;
// Preview
IBaseFilter *m_previewFilter;
diff --git a/src/plugins/directshow/player/directshowevrvideowindowcontrol.cpp b/src/plugins/directshow/player/directshowevrvideowindowcontrol.cpp
index 7bffe47d2..22771bd4c 100644
--- a/src/plugins/directshow/player/directshowevrvideowindowcontrol.cpp
+++ b/src/plugins/directshow/player/directshowevrvideowindowcontrol.cpp
@@ -49,10 +49,8 @@ DirectShowEvrVideoWindowControl::~DirectShowEvrVideoWindowControl()
IBaseFilter *DirectShowEvrVideoWindowControl::filter()
{
- static const GUID clsid_EnhancendVideoRenderer = { 0xfa10746c, 0x9b63, 0x4b6c, {0xbc, 0x49, 0xfc, 0x30, 0xe, 0xa5, 0xf2, 0x56} };
-
if (!m_evrFilter) {
- m_evrFilter = com_new<IBaseFilter>(clsid_EnhancendVideoRenderer);
+ m_evrFilter = com_new<IBaseFilter>(clsid_EnhancedVideoRenderer);
if (!setEvr(m_evrFilter)) {
m_evrFilter->Release();
m_evrFilter = NULL;
diff --git a/src/plugins/directshow/player/directshowmetadatacontrol.cpp b/src/plugins/directshow/player/directshowmetadatacontrol.cpp
index 3da02aa45..f1581e129 100644
--- a/src/plugins/directshow/player/directshowmetadatacontrol.cpp
+++ b/src/plugins/directshow/player/directshowmetadatacontrol.cpp
@@ -362,7 +362,18 @@ static QString convertBSTR(BSTR *string)
return value;
}
-void DirectShowMetaDataControl::updateGraph(IFilterGraph2 *graph, IBaseFilter *source, const QString &fileSrc)
+void DirectShowMetaDataControl::reset()
+{
+ bool hadMetadata = !m_metadata.isEmpty();
+ m_metadata.clear();
+
+ setMetadataAvailable(false);
+
+ if (hadMetadata)
+ emit metaDataChanged();
+}
+
+void DirectShowMetaDataControl::updateMetadata(IFilterGraph2 *graph, IBaseFilter *source, const QString &fileSrc)
{
m_metadata.clear();
@@ -569,13 +580,19 @@ void DirectShowMetaDataControl::customEvent(QEvent *event)
if (event->type() == QEvent::Type(MetaDataChanged)) {
event->accept();
- bool oldAvailable = m_available;
- m_available = !m_metadata.isEmpty();
- if (m_available != oldAvailable)
- emit metaDataAvailableChanged(m_available);
+ setMetadataAvailable(!m_metadata.isEmpty());
emit metaDataChanged();
} else {
QMetaDataReaderControl::customEvent(event);
}
}
+
+void DirectShowMetaDataControl::setMetadataAvailable(bool available)
+{
+ if (m_available == available)
+ return;
+
+ m_available = available;
+ emit metaDataAvailableChanged(m_available);
+}
diff --git a/src/plugins/directshow/player/directshowmetadatacontrol.h b/src/plugins/directshow/player/directshowmetadatacontrol.h
index d32ae8508..55504ba4b 100644
--- a/src/plugins/directshow/player/directshowmetadatacontrol.h
+++ b/src/plugins/directshow/player/directshowmetadatacontrol.h
@@ -56,13 +56,16 @@ public:
QVariant metaData(const QString &key) const;
QStringList availableMetaData() const;
- void updateGraph(IFilterGraph2 *graph, IBaseFilter *source,
- const QString &fileSrc = QString());
+ void reset();
+ void updateMetadata(IFilterGraph2 *graph, IBaseFilter *source,
+ const QString &fileSrc = QString());
protected:
void customEvent(QEvent *event);
private:
+ void setMetadataAvailable(bool available);
+
enum Event
{
MetaDataChanged = QEvent::User
diff --git a/src/plugins/directshow/player/directshowplayercontrol.cpp b/src/plugins/directshow/player/directshowplayercontrol.cpp
index e988cbdd3..3449c9270 100644
--- a/src/plugins/directshow/player/directshowplayercontrol.cpp
+++ b/src/plugins/directshow/player/directshowplayercontrol.cpp
@@ -53,17 +53,6 @@ static int volumeToDecibels(int volume)
}
}
-static int decibelsToVolume(int dB)
-{
- if (dB == -10000) {
- return 0;
- } else if (dB == 0) {
- return 100;
- } else {
- return qRound(100 * qPow(10, qreal(dB) / 5000));
- }
-}
-
DirectShowPlayerControl::DirectShowPlayerControl(DirectShowPlayerService *service, QObject *parent)
: QMediaPlayerControl(parent)
, m_service(service)
@@ -75,7 +64,7 @@ DirectShowPlayerControl::DirectShowPlayerControl(DirectShowPlayerService *servic
, m_streamTypes(0)
, m_volume(100)
, m_muted(false)
- , m_position(0)
+ , m_emitPosition(-1)
, m_pendingPosition(-1)
, m_duration(0)
, m_playbackRate(0)
@@ -109,7 +98,7 @@ qint64 DirectShowPlayerControl::position() const
if (m_pendingPosition != -1)
return m_pendingPosition;
- return const_cast<qint64 &>(m_position) = m_service->position();
+ return m_service->position();
}
void DirectShowPlayerControl::setPosition(qint64 position)
@@ -226,6 +215,7 @@ const QIODevice *DirectShowPlayerControl::mediaStream() const
void DirectShowPlayerControl::setMedia(const QMediaContent &media, QIODevice *stream)
{
m_pendingPosition = -1;
+ m_emitPosition = -1;
m_media = media;
m_stream = stream;
@@ -258,6 +248,7 @@ void DirectShowPlayerControl::playOrPause(QMediaPlayer::State state)
return;
}
+ m_emitPosition = -1;
m_state = state;
if (m_pendingPosition != -1)
@@ -273,6 +264,7 @@ void DirectShowPlayerControl::playOrPause(QMediaPlayer::State state)
void DirectShowPlayerControl::stop()
{
+ m_emitPosition = -1;
m_service->stop();
emit stateChanged(m_state = QMediaPlayer::StoppedState);
}
@@ -304,8 +296,8 @@ void DirectShowPlayerControl::emitPropertyChanges()
emit videoAvailableChanged(m_streamTypes & DirectShowPlayerService::VideoStream);
}
- if (properties & PositionProperty)
- emit positionChanged(m_position);
+ if (properties & PositionProperty && m_emitPosition != -1)
+ emit positionChanged(m_emitPosition);
if (properties & DurationProperty)
emit durationChanged(m_duration);
@@ -400,8 +392,8 @@ void DirectShowPlayerControl::updateError(QMediaPlayer::Error error, const QStri
void DirectShowPlayerControl::updatePosition(qint64 position)
{
- if (m_position != position) {
- m_position = position;
+ if (m_emitPosition != position) {
+ m_emitPosition = position;
scheduleUpdate(PositionProperty);
}
diff --git a/src/plugins/directshow/player/directshowplayercontrol.h b/src/plugins/directshow/player/directshowplayercontrol.h
index ab842f511..f67c4108b 100644
--- a/src/plugins/directshow/player/directshowplayercontrol.h
+++ b/src/plugins/directshow/player/directshowplayercontrol.h
@@ -130,7 +130,7 @@ private:
int m_streamTypes;
int m_volume;
bool m_muted;
- qint64 m_position;
+ qint64 m_emitPosition;
qint64 m_pendingPosition;
qint64 m_duration;
qreal m_playbackRate;
diff --git a/src/plugins/directshow/player/directshowplayerservice.cpp b/src/plugins/directshow/player/directshowplayerservice.cpp
index 07427583d..8e9e50cbf 100644
--- a/src/plugins/directshow/player/directshowplayerservice.cpp
+++ b/src/plugins/directshow/player/directshowplayerservice.cpp
@@ -103,6 +103,7 @@ DirectShowPlayerService::DirectShowPlayerService(QObject *parent)
, m_buffering(false)
, m_seekable(false)
, m_atEnd(false)
+ , m_dontCacheNextSeekResult(false)
{
m_playerControl = new DirectShowPlayerControl(this);
m_metaDataControl = new DirectShowMetaDataControl(this);
@@ -229,7 +230,8 @@ void DirectShowPlayerService::load(const QMediaContent &media, QIODevice *stream
m_buffering = false;
m_seekable = false;
m_atEnd = false;
- m_metaDataControl->updateGraph(0, 0);
+ m_dontCacheNextSeekResult = false;
+ m_metaDataControl->reset();
if (m_resources.isEmpty() && !stream) {
m_pendingTasks = 0;
@@ -568,9 +570,6 @@ void DirectShowPlayerService::doReleaseGraph(QMutexLocker *locker)
control->Release();
}
- //release m_headerInfo -> decrease ref counter of m_source
- m_metaDataControl->updateGraph(0, 0);
-
if (m_source) {
m_source->Release();
m_source = 0;
@@ -672,7 +671,9 @@ void DirectShowPlayerService::play()
if (m_executedTasks & Stop) {
m_atEnd = false;
if (m_seekPosition == -1) {
+ m_dontCacheNextSeekResult = true;
m_seekPosition = 0;
+ m_position = 0;
m_pendingTasks |= Seek;
}
m_executedTasks ^= Stop;
@@ -718,7 +719,9 @@ void DirectShowPlayerService::pause()
if (m_executedTasks & Stop) {
m_atEnd = false;
if (m_seekPosition == -1) {
+ m_dontCacheNextSeekResult = true;
m_seekPosition = 0;
+ m_position = 0;
m_pendingTasks |= Seek;
}
m_executedTasks ^= Stop;
@@ -791,6 +794,8 @@ void DirectShowPlayerService::doStop(QMutexLocker *locker)
}
m_seekPosition = 0;
+ m_position = 0;
+ m_dontCacheNextSeekResult = true;
m_pendingTasks |= Seek;
m_executedTasks &= ~(Play | Pause);
@@ -913,8 +918,10 @@ void DirectShowPlayerService::doSeek(QMutexLocker *locker)
// Cache current values as we can't query IMediaSeeking during a seek due to the
// possibility of a deadlock when flushing the VideoSurfaceFilter.
LONGLONG currentPosition = 0;
- seeking->GetCurrentPosition(&currentPosition);
- m_position = currentPosition / qt_directShowTimeScale;
+ if (!m_dontCacheNextSeekResult) {
+ seeking->GetCurrentPosition(&currentPosition);
+ m_position = currentPosition / qt_directShowTimeScale;
+ }
LONGLONG minimum = 0;
LONGLONG maximum = 0;
@@ -928,8 +935,10 @@ void DirectShowPlayerService::doSeek(QMutexLocker *locker)
&seekPosition, AM_SEEKING_AbsolutePositioning, 0, AM_SEEKING_NoPositioning);
locker->relock();
- seeking->GetCurrentPosition(&currentPosition);
- m_position = currentPosition / qt_directShowTimeScale;
+ if (!m_dontCacheNextSeekResult) {
+ seeking->GetCurrentPosition(&currentPosition);
+ m_position = currentPosition / qt_directShowTimeScale;
+ }
seeking->Release();
@@ -937,6 +946,7 @@ void DirectShowPlayerService::doSeek(QMutexLocker *locker)
}
m_seekPosition = -1;
+ m_dontCacheNextSeekResult = false;
}
int DirectShowPlayerService::bufferStatus() const
@@ -1125,7 +1135,7 @@ void DirectShowPlayerService::customEvent(QEvent *event)
QMutexLocker locker(&m_mutex);
m_playerControl->updateMediaInfo(m_duration, m_streamTypes, m_seekable);
- m_metaDataControl->updateGraph(m_graph, m_source, m_url.toString());
+ m_metaDataControl->updateMetadata(m_graph, m_source, m_url.toString());
updateStatus();
} else if (event->type() == QEvent::Type(Error)) {
diff --git a/src/plugins/directshow/player/directshowplayerservice.h b/src/plugins/directshow/player/directshowplayerservice.h
index edfde105e..4d3762f74 100644
--- a/src/plugins/directshow/player/directshowplayerservice.h
+++ b/src/plugins/directshow/player/directshowplayerservice.h
@@ -195,6 +195,7 @@ private:
bool m_buffering;
bool m_seekable;
bool m_atEnd;
+ bool m_dontCacheNextSeekResult;
QMediaTimeRange m_playbackRange;
QUrl m_url;
QMediaResourceList m_resources;
diff --git a/src/plugins/directshow/player/directshowvideorenderercontrol.cpp b/src/plugins/directshow/player/directshowvideorenderercontrol.cpp
index 6a1580ea5..027d2ce55 100644
--- a/src/plugins/directshow/player/directshowvideorenderercontrol.cpp
+++ b/src/plugins/directshow/player/directshowvideorenderercontrol.cpp
@@ -35,6 +35,12 @@
#include "videosurfacefilter.h"
+#ifdef HAVE_EVR
+#include "evrcustompresenter.h"
+#endif
+
+#include <qabstractvideosurface.h>
+
DirectShowVideoRendererControl::DirectShowVideoRendererControl(DirectShowEventLoop *loop, QObject *parent)
: QVideoRendererControl(parent)
, m_loop(loop)
@@ -56,22 +62,34 @@ QAbstractVideoSurface *DirectShowVideoRendererControl::surface() const
void DirectShowVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
{
- if (surface != m_surface) {
- m_surface = surface;
+ if (m_surface == surface)
+ return;
+
+ if (m_filter) {
+ m_filter->Release();
+ m_filter = 0;
+ }
- VideoSurfaceFilter *existingFilter = m_filter;
+ m_surface = surface;
- if (surface) {
- m_filter = new VideoSurfaceFilter(surface, m_loop);
- } else {
+ if (m_surface) {
+#ifdef HAVE_EVR
+ m_filter = com_new<IBaseFilter>(clsid_EnhancedVideoRenderer);
+ EVRCustomPresenter *evrPresenter = new EVRCustomPresenter(m_surface);
+ if (!evrPresenter->isValid() || !qt_evr_setCustomPresenter(m_filter, evrPresenter)) {
+ m_filter->Release();
m_filter = 0;
}
+ evrPresenter->Release();
- emit filterChanged();
-
- if (existingFilter)
- existingFilter->Release();
+ if (!m_filter)
+#endif
+ {
+ m_filter = new VideoSurfaceFilter(m_surface, m_loop);
+ }
}
+
+ emit filterChanged();
}
IBaseFilter *DirectShowVideoRendererControl::filter()
diff --git a/src/plugins/directshow/player/directshowvideorenderercontrol.h b/src/plugins/directshow/player/directshowvideorenderercontrol.h
index b4828d1b0..484fda263 100644
--- a/src/plugins/directshow/player/directshowvideorenderercontrol.h
+++ b/src/plugins/directshow/player/directshowvideorenderercontrol.h
@@ -39,7 +39,6 @@
#include <dshow.h>
class DirectShowEventLoop;
-class VideoSurfaceFilter;
QT_USE_NAMESPACE
@@ -61,7 +60,7 @@ Q_SIGNALS:
private:
DirectShowEventLoop *m_loop;
QAbstractVideoSurface *m_surface;
- VideoSurfaceFilter *m_filter;
+ IBaseFilter *m_filter;
};
#endif
diff --git a/src/plugins/directshow/player/player.pri b/src/plugins/directshow/player/player.pri
index 8586ea5da..5ecb912b2 100644
--- a/src/plugins/directshow/player/player.pri
+++ b/src/plugins/directshow/player/player.pri
@@ -46,8 +46,11 @@ config_evr {
include($$PWD/../../common/evr.pri)
- HEADERS += $$PWD/directshowevrvideowindowcontrol.h
- SOURCES += $$PWD/directshowevrvideowindowcontrol.cpp
+ HEADERS += \
+ $$PWD/directshowevrvideowindowcontrol.h
+
+ SOURCES += \
+ $$PWD/directshowevrvideowindowcontrol.cpp
}
config_wshellitem {
diff --git a/src/plugins/gstreamer/camerabin/camerabin.pro b/src/plugins/gstreamer/camerabin/camerabin.pro
index 80d992960..b807071f2 100644
--- a/src/plugins/gstreamer/camerabin/camerabin.pro
+++ b/src/plugins/gstreamer/camerabin/camerabin.pro
@@ -89,6 +89,15 @@ config_gstreamer_encodingprofiles {
DEFINES += HAVE_GST_ENCODING_PROFILES
}
+config_linux_v4l: {
+ DEFINES += USE_V4L
+
+ HEADERS += \
+ $$PWD/camerabinv4limageprocessing.h
+
+ SOURCES += \
+ $$PWD/camerabinv4limageprocessing.cpp
+}
+
OTHER_FILES += \
camerabin.json
-
diff --git a/src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp b/src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp
index 633662c70..2d1659900 100644
--- a/src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp
+++ b/src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp
@@ -34,6 +34,10 @@
#include "camerabinimageprocessing.h"
#include "camerabinsession.h"
+#ifdef USE_V4L
+#include "camerabinv4limageprocessing.h"
+#endif
+
#if GST_CHECK_VERSION(1,0,0)
# include <gst/video/colorbalance.h>
#else
@@ -43,9 +47,12 @@
QT_BEGIN_NAMESPACE
CameraBinImageProcessing::CameraBinImageProcessing(CameraBinSession *session)
- :QCameraImageProcessingControl(session),
- m_session(session),
- m_whiteBalanceMode(QCameraImageProcessing::WhiteBalanceAuto)
+ : QCameraImageProcessingControl(session)
+ , m_session(session)
+ , m_whiteBalanceMode(QCameraImageProcessing::WhiteBalanceAuto)
+#ifdef USE_V4L
+ , m_v4lImageControl(Q_NULLPTR)
+#endif
{
#ifdef HAVE_GST_PHOTOGRAPHY
if (m_session->photography()) {
@@ -83,6 +90,12 @@ CameraBinImageProcessing::CameraBinImageProcessing(CameraBinSession *session)
#endif
#endif
+#ifdef USE_V4L
+ m_v4lImageControl = new CameraBinV4LImageProcessing(m_session);
+ connect(m_session, &CameraBinSession::statusChanged,
+ m_v4lImageControl, &CameraBinV4LImageProcessing::updateParametersInfo);
+#endif
+
updateColorBalanceValues();
}
@@ -160,7 +173,7 @@ QCameraImageProcessing::WhiteBalanceMode CameraBinImageProcessing::whiteBalanceM
return m_whiteBalanceMode;
}
-void CameraBinImageProcessing::setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceMode mode)
+bool CameraBinImageProcessing::setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceMode mode)
{
#ifdef HAVE_GST_PHOTOGRAPHY
if (isWhiteBalanceModeSupported(mode)) {
@@ -172,11 +185,13 @@ void CameraBinImageProcessing::setWhiteBalanceMode(QCameraImageProcessing::White
#endif
{
unlockWhiteBalance();
+ return true;
}
}
#else
Q_UNUSED(mode);
#endif
+ return false;
}
bool CameraBinImageProcessing::isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceMode mode) const
@@ -184,7 +199,8 @@ bool CameraBinImageProcessing::isWhiteBalanceModeSupported(QCameraImageProcessin
#ifdef HAVE_GST_PHOTOGRAPHY
return m_mappedWbValues.values().contains(mode);
#else
- return mode == QCameraImageProcessing::WhiteBalanceAuto;
+ Q_UNUSED(mode);
+ return false;
#endif
}
@@ -192,16 +208,24 @@ bool CameraBinImageProcessing::isParameterSupported(QCameraImageProcessingContro
{
#ifdef HAVE_GST_PHOTOGRAPHY
if (parameter == QCameraImageProcessingControl::WhiteBalancePreset
- || parameter == QCameraImageProcessingControl::ColorFilter)
- return m_session->photography();
+ || parameter == QCameraImageProcessingControl::ColorFilter) {
+ if (m_session->photography())
+ return true;
+ }
#endif
if (parameter == QCameraImageProcessingControl::Contrast
|| parameter == QCameraImageProcessingControl::Brightness
|| parameter == QCameraImageProcessingControl::Saturation) {
- return GST_IS_COLOR_BALANCE(m_session->cameraBin());
+ if (GST_IS_COLOR_BALANCE(m_session->cameraBin()))
+ return true;
}
+#ifdef USE_V4L
+ if (m_v4lImageControl->isParameterSupported(parameter))
+ return true;
+#endif
+
return false;
}
@@ -210,10 +234,39 @@ bool CameraBinImageProcessing::isParameterValueSupported(QCameraImageProcessingC
switch (parameter) {
case ContrastAdjustment:
case BrightnessAdjustment:
- case SaturationAdjustment:
- return GST_IS_COLOR_BALANCE(m_session->cameraBin()) && qAbs(value.toReal()) <= 1.0;
- case WhiteBalancePreset:
- return isWhiteBalanceModeSupported(value.value<QCameraImageProcessing::WhiteBalanceMode>());
+ case SaturationAdjustment: {
+ const bool isGstColorBalanceValueSupported = GST_IS_COLOR_BALANCE(m_session->cameraBin())
+ && qAbs(value.toReal()) <= 1.0;
+#ifdef USE_V4L
+ if (!isGstColorBalanceValueSupported)
+ return m_v4lImageControl->isParameterValueSupported(parameter, value);
+#endif
+ return isGstColorBalanceValueSupported;
+ }
+ case SharpeningAdjustment: {
+#ifdef USE_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);
+#ifdef USE_V4L
+ if (!isPhotographyWhiteBalanceSupported)
+ return m_v4lImageControl->isParameterValueSupported(parameter, value);
+#endif
+ return isPhotographyWhiteBalanceSupported;
+ }
+ case ColorTemperature: {
+#ifdef USE_V4L
+ return m_v4lImageControl->isParameterValueSupported(parameter, value);
+#else
+ return false;
+#endif
+ }
case ColorFilter: {
const QCameraImageProcessing::ColorFilter filter = value.value<QCameraImageProcessing::ColorFilter>();
#ifdef HAVE_GST_PHOTOGRAPHY
@@ -233,8 +286,23 @@ QVariant CameraBinImageProcessing::parameter(
QCameraImageProcessingControl::ProcessingParameter parameter) const
{
switch (parameter) {
- case QCameraImageProcessingControl::WhiteBalancePreset:
- return QVariant::fromValue<QCameraImageProcessing::WhiteBalanceMode>(whiteBalanceMode());
+ case QCameraImageProcessingControl::WhiteBalancePreset: {
+ const QCameraImageProcessing::WhiteBalanceMode mode = whiteBalanceMode();
+#ifdef USE_V4L
+ if (mode == QCameraImageProcessing::WhiteBalanceAuto
+ || mode == QCameraImageProcessing::WhiteBalanceManual) {
+ return m_v4lImageControl->parameter(parameter);
+ }
+#endif
+ return QVariant::fromValue<QCameraImageProcessing::WhiteBalanceMode>(mode);
+ }
+ case QCameraImageProcessingControl::ColorTemperature: {
+#ifdef USE_V4L
+ return m_v4lImageControl->parameter(parameter);
+#else
+ return QVariant();
+#endif
+ }
case QCameraImageProcessingControl::ColorFilter:
#ifdef HAVE_GST_PHOTOGRAPHY
if (GstPhotography *photography = m_session->photography()) {
@@ -249,29 +317,79 @@ QVariant CameraBinImageProcessing::parameter(
}
#endif
return QVariant::fromValue(QCameraImageProcessing::ColorFilterNone);
- default:
- return m_values.contains(parameter)
+ default: {
+ const bool isGstParameterSupported = m_values.contains(parameter);
+#ifdef USE_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:
- setColorBalanceValue("contrast", value.toReal());
+ case ContrastAdjustment: {
+ if (!setColorBalanceValue("contrast", value.toReal())) {
+#ifdef USE_V4L
+ m_v4lImageControl->setParameter(parameter, value);
+#endif
+ }
+ }
break;
- case BrightnessAdjustment:
- setColorBalanceValue("brightness", value.toReal());
+ case BrightnessAdjustment: {
+ if (!setColorBalanceValue("brightness", value.toReal())) {
+#ifdef USE_V4L
+ m_v4lImageControl->setParameter(parameter, value);
+#endif
+ }
+ }
+ break;
+ case SaturationAdjustment: {
+ if (!setColorBalanceValue("saturation", value.toReal())) {
+#ifdef USE_V4L
+ m_v4lImageControl->setParameter(parameter, value);
+#endif
+ }
+ }
+ break;
+ case SharpeningAdjustment: {
+#ifdef USE_V4L
+ m_v4lImageControl->setParameter(parameter, value);
+#endif
+ }
break;
- case SaturationAdjustment:
- setColorBalanceValue("saturation", value.toReal());
+ case WhiteBalancePreset: {
+ if (!setWhiteBalanceMode(value.value<QCameraImageProcessing::WhiteBalanceMode>())) {
+#ifdef USE_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 WhiteBalancePreset:
- setWhiteBalanceMode(value.value<QCameraImageProcessing::WhiteBalanceMode>());
+ case QCameraImageProcessingControl::ColorTemperature: {
+#ifdef USE_V4L
+ m_v4lImageControl->setParameter(parameter, value);
+#endif
break;
+ }
case QCameraImageProcessingControl::ColorFilter:
#ifdef HAVE_GST_PHOTOGRAPHY
if (GstPhotography *photography = m_session->photography()) {
diff --git a/src/plugins/gstreamer/camerabin/camerabinimageprocessing.h b/src/plugins/gstreamer/camerabin/camerabinimageprocessing.h
index 9f280c485..6b2a114ec 100644
--- a/src/plugins/gstreamer/camerabin/camerabinimageprocessing.h
+++ b/src/plugins/gstreamer/camerabin/camerabinimageprocessing.h
@@ -50,6 +50,10 @@ typedef GstColourToneMode GstPhotographyColorToneMode;
QT_BEGIN_NAMESPACE
+#ifdef USE_V4L
+class CameraBinV4LImageProcessing;
+#endif
+
class CameraBinSession;
class CameraBinImageProcessing : public QCameraImageProcessingControl
@@ -61,7 +65,7 @@ public:
virtual ~CameraBinImageProcessing();
QCameraImageProcessing::WhiteBalanceMode whiteBalanceMode() const;
- void setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceMode mode);
+ bool setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceMode mode);
bool isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceMode mode) const;
bool isParameterSupported(ProcessingParameter) const;
@@ -86,6 +90,10 @@ private:
QMap<QCameraImageProcessing::ColorFilter, GstPhotographyColorToneMode> m_filterMap;
#endif
QCameraImageProcessing::WhiteBalanceMode m_whiteBalanceMode;
+
+#ifdef USE_V4L
+ CameraBinV4LImageProcessing *m_v4lImageControl;
+#endif
};
QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinsession.h b/src/plugins/gstreamer/camerabin/camerabinsession.h
index 1c5c9899d..dda900a8b 100644
--- a/src/plugins/gstreamer/camerabin/camerabinsession.h
+++ b/src/plugins/gstreamer/camerabin/camerabinsession.h
@@ -153,6 +153,8 @@ public:
bool isMuted() const;
+ QString device() const { return m_inputDevice; }
+
bool processSyncMessage(const QGstreamerMessage &message);
bool processBusMessage(const QGstreamerMessage &message);
diff --git a/src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.cpp b/src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.cpp
new file mode 100644
index 000000000..bf51cbfd0
--- /dev/null
+++ b/src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.cpp
@@ -0,0 +1,304 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $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()) {
+ qWarning() << "Unable to get the parameter value: the parameter is not supported.";
+ 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:" << 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()) {
+ qWarning() << "Unable to set the parameter value: the parameter is not supported.";
+ 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)
+ return;
+
+ control.value = (m == QCameraImageProcessing::WhiteBalanceAuto) ? true : false;
+ }
+ break;
+
+ case QCameraImageProcessingControl::ColorTemperature:
+ control.value = value.toInt();
+ break;
+
+ case QCameraImageProcessingControl::ContrastAdjustment: // falling back
+ case QCameraImageProcessingControl::SaturationAdjustment: // falling back
+ case QCameraImageProcessingControl::BrightnessAdjustment: // falling back
+ case QCameraImageProcessingControl::SharpeningAdjustment:
+ control.value = sourceImageProcessingParameterValue(
+ value.toReal(), (*sourceValueInfo));
+ break;
+
+ default:
+ return;
+ }
+
+ if (::ioctl(fd, VIDIOC_S_CTRL, &control) != 0)
+ qWarning() << "Unable to set the parameter value:" << qt_error_string(errno);
+
+ qt_safe_close(fd);
+}
+
+void CameraBinV4LImageProcessing::updateParametersInfo(
+ QCamera::Status cameraStatus)
+{
+ if (cameraStatus == QCamera::UnloadedStatus)
+ m_parametersInfo.clear();
+ else if (cameraStatus == QCamera::LoadedStatus) {
+ const QString deviceName = m_session->device();
+ const int fd = qt_safe_open(deviceName.toLocal8Bit().constData(), O_RDONLY);
+ if (fd == -1) {
+ qWarning() << "Unable to open the camera" << deviceName
+ << "for read to query the parameter info:" << qt_error_string(errno);
+ return;
+ }
+
+ static const struct SupportedParameterEntry {
+ quint32 cid;
+ QCameraImageProcessingControl::ProcessingParameter parameter;
+ } supportedParametersEntries[] = {
+ { V4L2_CID_AUTO_WHITE_BALANCE, QCameraImageProcessingControl::WhiteBalancePreset },
+ { V4L2_CID_WHITE_BALANCE_TEMPERATURE, QCameraImageProcessingControl::ColorTemperature },
+ { V4L2_CID_CONTRAST, QCameraImageProcessingControl::ContrastAdjustment },
+ { V4L2_CID_SATURATION, QCameraImageProcessingControl::SaturationAdjustment },
+ { V4L2_CID_BRIGHTNESS, QCameraImageProcessingControl::BrightnessAdjustment },
+ { V4L2_CID_SHARPNESS, QCameraImageProcessingControl::SharpeningAdjustment }
+ };
+
+ for (int i = 0; i < int(sizeof(supportedParametersEntries) / sizeof(SupportedParameterEntry)); ++i) {
+ struct v4l2_queryctrl queryControl;
+ ::memset(&queryControl, 0, sizeof(queryControl));
+ queryControl.id = supportedParametersEntries[i].cid;
+
+ if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) != 0) {
+ qWarning() << "Unable to query the parameter info:" << qt_error_string(errno);
+ continue;
+ }
+
+ SourceParameterValueInfo sourceValueInfo;
+ sourceValueInfo.cid = queryControl.id;
+ sourceValueInfo.defaultValue = queryControl.default_value;
+ sourceValueInfo.maximumValue = queryControl.maximum;
+ sourceValueInfo.minimumValue = queryControl.minimum;
+
+ m_parametersInfo.insert(supportedParametersEntries[i].parameter, sourceValueInfo);
+ }
+
+ qt_safe_close(fd);
+ }
+}
+
+qreal CameraBinV4LImageProcessing::scaledImageProcessingParameterValue(
+ qint32 sourceValue, const SourceParameterValueInfo &sourceValueInfo)
+{
+ if (sourceValue == sourceValueInfo.defaultValue) {
+ return 0.0f;
+ } else if (sourceValue < sourceValueInfo.defaultValue) {
+ return ((sourceValue - sourceValueInfo.minimumValue)
+ / qreal(sourceValueInfo.defaultValue - sourceValueInfo.minimumValue))
+ + (-1.0f);
+ } else {
+ return ((sourceValue - sourceValueInfo.defaultValue)
+ / qreal(sourceValueInfo.maximumValue - sourceValueInfo.defaultValue));
+ }
+}
+
+qint32 CameraBinV4LImageProcessing::sourceImageProcessingParameterValue(
+ qreal scaledValue, const SourceParameterValueInfo &valueRange)
+{
+ if (qFuzzyIsNull(scaledValue)) {
+ return valueRange.defaultValue;
+ } else if (scaledValue < 0.0f) {
+ return ((scaledValue - (-1.0f)) * (valueRange.defaultValue - valueRange.minimumValue))
+ + valueRange.minimumValue;
+ } else {
+ return (scaledValue * (valueRange.maximumValue - valueRange.defaultValue))
+ + valueRange.defaultValue;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.h b/src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.h
new file mode 100644
index 000000000..7961d6c0d
--- /dev/null
+++ b/src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CAMERABINV4LIMAGEPROCESSINGCONTROL_H
+#define CAMERABINV4LIMAGEPROCESSINGCONTROL_H
+
+#include <qcamera.h>
+#include <qcameraimageprocessingcontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class CameraBinSession;
+
+class CameraBinV4LImageProcessing : public QCameraImageProcessingControl
+{
+ Q_OBJECT
+
+public:
+ CameraBinV4LImageProcessing(CameraBinSession *session);
+ virtual ~CameraBinV4LImageProcessing();
+
+ bool isParameterSupported(ProcessingParameter) const;
+ bool isParameterValueSupported(ProcessingParameter parameter, const QVariant &value) const;
+ QVariant parameter(ProcessingParameter parameter) const;
+ void setParameter(ProcessingParameter parameter, const QVariant &value);
+
+public slots:
+ void updateParametersInfo(QCamera::Status cameraStatus);
+
+private:
+ struct SourceParameterValueInfo {
+ SourceParameterValueInfo()
+ : cid(0)
+ {
+ }
+
+ qint32 defaultValue;
+ qint32 minimumValue;
+ qint32 maximumValue;
+ quint32 cid; // V4L control id
+ };
+
+ static qreal scaledImageProcessingParameterValue(
+ qint32 sourceValue, const SourceParameterValueInfo &sourceValueInfo);
+ static qint32 sourceImageProcessingParameterValue(
+ qreal scaledValue, const SourceParameterValueInfo &valueRange);
+private:
+ CameraBinSession *m_session;
+ QMap<ProcessingParameter, SourceParameterValueInfo> m_parametersInfo;
+};
+
+QT_END_NAMESPACE
+
+#endif // CAMERABINV4LIMAGEPROCESSINGCONTROL_H
diff --git a/src/plugins/opensles/opensles.pro b/src/plugins/opensles/opensles.pro
index 53c5a120b..aa8e05444 100644
--- a/src/plugins/opensles/opensles.pro
+++ b/src/plugins/opensles/opensles.pro
@@ -1,5 +1,5 @@
TARGET = qtaudio_opensles
-QT += multimedia-private
+QT += multimedia-private core-private
PLUGIN_TYPE = audio
PLUGIN_CLASS_NAME = QOpenSLESPlugin
diff --git a/src/plugins/opensles/qopenslesaudiooutput.cpp b/src/plugins/opensles/qopenslesaudiooutput.cpp
index d17363d20..3af4e8bb2 100644
--- a/src/plugins/opensles/qopenslesaudiooutput.cpp
+++ b/src/plugins/opensles/qopenslesaudiooutput.cpp
@@ -42,13 +42,23 @@
#endif // ANDROID
#define BUFFER_COUNT 2
-#define DEFAULT_PERIOD_TIME_MS 50
-#define MINIMUM_PERIOD_TIME_MS 5
#define EBASE 2.302585093
#define LOG10(x) qLn(x)/qreal(EBASE)
QT_BEGIN_NAMESPACE
+static inline void openSlDebugInfo()
+{
+ const QAudioFormat &format = QAudioDeviceInfo::defaultOutputDevice().preferredFormat();
+ qDebug() << "======= OpenSL ES Device info ======="
+ << "\nSupports low-latency playback: " << (QOpenSLESEngine::supportsLowLatency() ? "YES" : "NO")
+ << "\nPreferred sample rate: " << QOpenSLESEngine::getOutputValue(QOpenSLESEngine::SampleRate, -1)
+ << "\nFrames per buffer: " << QOpenSLESEngine::getOutputValue(QOpenSLESEngine::FramesPerBuffer, -1)
+ << "\nPreferred Format: " << format
+ << "\nLow-latency buffer size: " << QOpenSLESEngine::getLowLatencyBufferSize(format)
+ << "\nDefault buffer size: " << QOpenSLESEngine::getDefaultBufferSize(format);
+}
+
QMap<QString, qint32> QOpenSLESAudioOutput::m_categories;
QOpenSLESAudioOutput::QOpenSLESAudioOutput(const QByteArray &device)
@@ -531,13 +541,17 @@ bool QOpenSLESAudioOutput::preparePlayer()
setVolume(m_volume);
+ const int lowLatencyBufferSize = QOpenSLESEngine::getLowLatencyBufferSize(m_format);
+ const int defaultBufferSize = QOpenSLESEngine::getDefaultBufferSize(m_format);
+
// Buffer size
if (m_bufferSize <= 0) {
- m_bufferSize = m_format.bytesForDuration(DEFAULT_PERIOD_TIME_MS * 1000);
- } else {
- const int minimumBufSize = m_format.bytesForDuration(MINIMUM_PERIOD_TIME_MS * 1000);
- if (m_bufferSize < minimumBufSize)
- m_bufferSize = minimumBufSize;
+ m_bufferSize = defaultBufferSize;
+ } else if (QOpenSLESEngine::supportsLowLatency()) {
+ if (m_bufferSize < lowLatencyBufferSize)
+ m_bufferSize = lowLatencyBufferSize;
+ } else if (m_bufferSize < defaultBufferSize) {
+ m_bufferSize = defaultBufferSize;
}
m_periodSize = m_bufferSize;
@@ -598,6 +612,9 @@ void QOpenSLESAudioOutput::stopPlayer()
void QOpenSLESAudioOutput::startPlayer()
{
+ if (QOpenSLESEngine::printDebugInfo())
+ openSlDebugInfo();
+
if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) {
setError(QAudio::FatalError);
destroyPlayer();
diff --git a/src/plugins/opensles/qopenslesdeviceinfo.cpp b/src/plugins/opensles/qopenslesdeviceinfo.cpp
index c93a66e1e..0dd8183ef 100644
--- a/src/plugins/opensles/qopenslesdeviceinfo.cpp
+++ b/src/plugins/opensles/qopenslesdeviceinfo.cpp
@@ -61,7 +61,7 @@ QAudioFormat QOpenSLESDeviceInfo::preferredFormat() const
format.setCodec(QStringLiteral("audio/pcm"));
format.setSampleSize(16);
format.setSampleType(QAudioFormat::SignedInt);
- format.setSampleRate(44100);
+ format.setSampleRate(QOpenSLESEngine::getOutputValue(QOpenSLESEngine::SampleRate, 48000));
format.setChannelCount(m_mode == QAudio::AudioInput ? 1 : 2);
return format;
}
diff --git a/src/plugins/opensles/qopenslesengine.cpp b/src/plugins/opensles/qopenslesengine.cpp
index 7689533e6..a30c90d84 100644
--- a/src/plugins/opensles/qopenslesengine.cpp
+++ b/src/plugins/opensles/qopenslesengine.cpp
@@ -38,8 +38,13 @@
#ifdef ANDROID
#include <SLES/OpenSLES_Android.h>
+#include <QtCore/private/qjnihelpers_p.h>
+#include <QtCore/private/qjni_p.h>
#endif
+#define MINIMUM_PERIOD_TIME_MS 5
+#define DEFAULT_PERIOD_TIME_MS 50
+
#define CheckError(message) if (result != SL_RESULT_SUCCESS) { qWarning(message); return; }
Q_GLOBAL_STATIC(QOpenSLESEngine, openslesEngine);
@@ -130,6 +135,151 @@ QList<int> QOpenSLESEngine::supportedSampleRates(QAudio::Mode mode) const
}
}
+int QOpenSLESEngine::getOutputValue(QOpenSLESEngine::OutputValue type, int defaultValue)
+{
+#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_NO_SDK)
+ static int sampleRate = 0;
+ static int framesPerBuffer = 0;
+ static const int sdkVersion = QtAndroidPrivate::androidSdkVersion();
+
+ if (sdkVersion < 17) // getProperty() was added in API level 17...
+ return defaultValue;
+
+ if (type == FramesPerBuffer && framesPerBuffer != 0)
+ return framesPerBuffer;
+
+ if (type == SampleRate && sampleRate != 0)
+ return sampleRate;
+
+ QJNIObjectPrivate ctx(QtAndroidPrivate::activity());
+ if (!ctx.isValid())
+ return defaultValue;
+
+
+ QJNIObjectPrivate audioServiceString = ctx.getStaticObjectField("android/content/Context",
+ "AUDIO_SERVICE",
+ "Ljava/lang/String;");
+ QJNIObjectPrivate am = ctx.callObjectMethod("getSystemService",
+ "(Ljava/lang/String;)Ljava/lang/Object;",
+ audioServiceString.object());
+ if (!am.isValid())
+ return defaultValue;
+
+ QJNIObjectPrivate sampleRateField = QJNIObjectPrivate::getStaticObjectField("android/media/AudioManager",
+ "PROPERTY_OUTPUT_SAMPLE_RATE",
+ "Ljava/lang/String;");
+ QJNIObjectPrivate framesPerBufferField = QJNIObjectPrivate::getStaticObjectField("android/media/AudioManager",
+ "PROPERTY_OUTPUT_FRAMES_PER_BUFFER",
+ "Ljava/lang/String;");
+
+ QJNIObjectPrivate sampleRateString = am.callObjectMethod("getProperty",
+ "(Ljava/lang/String;)Ljava/lang/String;",
+ sampleRateField.object());
+ QJNIObjectPrivate framesPerBufferString = am.callObjectMethod("getProperty",
+ "(Ljava/lang/String;)Ljava/lang/String;",
+ framesPerBufferField.object());
+
+ if (!sampleRateString.isValid() || !framesPerBufferString.isValid())
+ return defaultValue;
+
+ framesPerBuffer = framesPerBufferString.toString().toInt();
+ sampleRate = sampleRateString.toString().toInt();
+
+ if (type == FramesPerBuffer)
+ return framesPerBuffer;
+
+ if (type == SampleRate)
+ return sampleRate;
+
+#endif // Q_OS_ANDROID
+
+ return defaultValue;
+}
+
+int QOpenSLESEngine::getDefaultBufferSize(const QAudioFormat &format)
+{
+#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_NO_SDK)
+ if (!format.isValid())
+ return 0;
+
+ const int channelConfig = [&format]() -> int
+ {
+ if (format.channelCount() == 1)
+ return 4; /* MONO */
+ else if (format.channelCount() == 2)
+ return 12; /* STEREO */
+ else if (format.channelCount() > 2)
+ return 1052; /* SURROUND */
+ else
+ return 1; /* DEFAULT */
+ }();
+
+ const int audioFormat = [&format]() -> int
+ {
+ if (format.sampleType() == QAudioFormat::Float && QtAndroidPrivate::androidSdkVersion() >= 21)
+ return 4; /* PCM_FLOAT */
+ else if (format.sampleSize() == 8)
+ return 3; /* PCM_8BIT */
+ else if (format.sampleSize() == 16)
+ return 2; /* PCM_16BIT*/
+ else
+ return 1; /* DEFAULT */
+ }();
+
+ const int sampleRate = format.sampleRate();
+ return QJNIObjectPrivate::callStaticMethod<jint>("android/media/AudioTrack",
+ "getMinBufferSize",
+ "(III)I",
+ sampleRate,
+ channelConfig,
+ audioFormat);
+#else
+ return format.bytesForDuration(DEFAULT_PERIOD_TIME_MS);
+#endif // Q_OS_ANDROID
+}
+
+int QOpenSLESEngine::getLowLatencyBufferSize(const QAudioFormat &format)
+{
+ return format.bytesForFrames(QOpenSLESEngine::getOutputValue(QOpenSLESEngine::FramesPerBuffer,
+ format.framesForDuration(MINIMUM_PERIOD_TIME_MS)));
+}
+
+bool QOpenSLESEngine::supportsLowLatency()
+{
+#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_NO_SDK)
+ static int isSupported = -1;
+
+ if (isSupported != -1)
+ return (isSupported == 1);
+
+ QJNIObjectPrivate ctx(QtAndroidPrivate::activity());
+ if (!ctx.isValid())
+ return false;
+
+ QJNIObjectPrivate pm = ctx.callObjectMethod("getPackageManager", "()Landroid/content/pm/PackageManager;");
+ if (!pm.isValid())
+ return false;
+
+ QJNIObjectPrivate audioFeatureField = QJNIObjectPrivate::getStaticObjectField("android/content/pm/PackageManager",
+ "FEATURE_AUDIO_LOW_LATENCY",
+ "Ljava/lang/String;");
+ if (!audioFeatureField.isValid())
+ return false;
+
+ isSupported = pm.callMethod<jboolean>("hasSystemFeature",
+ "(Ljava/lang/String;)Z",
+ audioFeatureField.object());
+ return (isSupported == 1);
+#else
+ return true;
+#endif // Q_OS_ANDROID
+}
+
+bool QOpenSLESEngine::printDebugInfo()
+{
+ return qEnvironmentVariableIsSet("QT_OPENSL_INFO");
+}
+
void QOpenSLESEngine::checkSupportedInputFormats()
{
m_supportedInputChannelCounts = QList<int>() << 1;
diff --git a/src/plugins/opensles/qopenslesengine.h b/src/plugins/opensles/qopenslesengine.h
index 0c2042f50..cbc3ca115 100644
--- a/src/plugins/opensles/qopenslesengine.h
+++ b/src/plugins/opensles/qopenslesengine.h
@@ -45,6 +45,8 @@ QT_BEGIN_NAMESPACE
class QOpenSLESEngine
{
public:
+ enum OutputValue { FramesPerBuffer, SampleRate };
+
QOpenSLESEngine();
~QOpenSLESEngine();
@@ -58,6 +60,12 @@ public:
QList<int> supportedChannelCounts(QAudio::Mode mode) const;
QList<int> supportedSampleRates(QAudio::Mode mode) const;
+ static int getOutputValue(OutputValue type, int defaultValue = 0);
+ static int getDefaultBufferSize(const QAudioFormat &format);
+ static int getLowLatencyBufferSize(const QAudioFormat &format);
+ static bool supportsLowLatency();
+ static bool printDebugInfo();
+
private:
void checkSupportedInputFormats();
bool inputFormatIsSupported(SLDataFormat_PCM format);
diff --git a/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp b/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp
index 3ba640cd6..1cccbfa01 100644
--- a/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp
+++ b/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp
@@ -508,6 +508,9 @@ void MmRendererMediaPlayerControl::play()
return;
}
+ if (m_mediaStatus == QMediaPlayer::EndOfMedia)
+ m_position = 0;
+
setPositionInternal(m_position);
setVolumeInternal(m_muted ? 0 : m_volume);
setPlaybackRateInternal(m_rate);
diff --git a/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp b/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp
index 7313ae7a2..fe07581c9 100644
--- a/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp
+++ b/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp
@@ -291,8 +291,9 @@ QWinRTCameraVideoRendererControl::QWinRTCameraVideoRendererControl(const QSize &
HString deviceModel;
hr = deviceInfo->get_SystemProductName(deviceModel.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
- // Blacklist Lumia 1520
- setBlitMode(blacklisted(L"RM-937", deviceModel) ? MediaFoundation : DirectVideo);
+ const bool blacklist = blacklisted(L"RM-1045", deviceModel) // Lumia 930
+ || blacklisted(L"RM-937", deviceModel); // Lumia 1520
+ setBlitMode(blacklist ? MediaFoundation : DirectVideo);
#endif
}
diff --git a/src/plugins/wmf/mfactivate.h b/src/plugins/wmf/mfactivate.h
index 878e30d4d..8b8e51b56 100644
--- a/src/plugins/wmf/mfactivate.h
+++ b/src/plugins/wmf/mfactivate.h
@@ -34,8 +34,6 @@
#ifndef MFACTIVATE_H
#define MFACTIVATE_H
-#include "mfglobal.h"
-
#include <mfidl.h>
class MFAbstractActivate : public IMFActivate
diff --git a/src/plugins/wmf/mfglobal.h b/src/plugins/wmf/mfglobal.h
deleted file mode 100644
index 073f959f7..000000000
--- a/src/plugins/wmf/mfglobal.h
+++ /dev/null
@@ -1,149 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL21$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** As a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef MFGLOBAL_H
-#define MFGLOBAL_H
-
-#include <mfapi.h>
-#include <mfidl.h>
-#include <Mferror.h>
-
-
-template<class T>
-class AsyncCallback : public IMFAsyncCallback
-{
-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)
- {
- 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() {
- // Delegate to parent class.
- return m_parent->AddRef();
- }
- STDMETHODIMP_(ULONG) Release() {
- // Delegate to parent class.
- return m_parent->Release();
- }
-
-
- // IMFAsyncCallback methods
- STDMETHODIMP GetParameters(DWORD*, DWORD*)
- {
- // Implementation of this method is optional.
- return E_NOTIMPL;
- }
-
- STDMETHODIMP Invoke(IMFAsyncResult* asyncResult)
- {
- return (m_parent->*m_invokeFn)(asyncResult);
- }
-
- T *m_parent;
- InvokeFn m_invokeFn;
-};
-
-template <class T> void qt_wmf_safeRelease(T **ppT)
-{
- if (*ppT) {
- (*ppT)->Release();
- *ppT = NULL;
- }
-}
-
-template <class T>
-void qt_wmf_copyComPointer(T* &dest, T *src)
-{
- if (dest)
- dest->Release();
- dest = src;
- if (dest)
- dest->AddRef();
-}
-
-HRESULT qt_wmf_getFourCC(IMFMediaType *type, DWORD *fourCC);
-MFRatio qt_wmf_getPixelAspectRatio(IMFMediaType *type);
-bool qt_wmf_areMediaTypesEqual(IMFMediaType *type1, IMFMediaType *type2);
-HRESULT qt_wmf_validateVideoArea(const MFVideoArea& area, UINT32 width, UINT32 height);
-bool qt_wmf_isSampleTimePassed(IMFClock *clock, IMFSample *sample);
-
-inline float qt_wmf_MFOffsetToFloat(const MFOffset& offset)
-{
- return offset.value + (float(offset.fract) / 65536);
-}
-
-inline MFOffset qt_wmf_makeMFOffset(float v)
-{
- MFOffset offset;
- offset.value = short(v);
- offset.fract = WORD(65536 * (v-offset.value));
- return offset;
-}
-
-inline MFVideoArea qt_wmf_makeMFArea(float x, float y, DWORD width, DWORD height)
-{
- MFVideoArea area;
- area.OffsetX = qt_wmf_makeMFOffset(x);
- area.OffsetY = qt_wmf_makeMFOffset(y);
- area.Area.cx = width;
- area.Area.cy = height;
- return area;
-}
-
-inline HRESULT qt_wmf_getFrameRate(IMFMediaType *pType, MFRatio *pRatio)
-{
- return MFGetAttributeRatio(pType, MF_MT_FRAME_RATE, (UINT32*)&pRatio->Numerator, (UINT32*)&pRatio->Denominator);
-}
-
-
-#endif // MFGLOBAL_H
diff --git a/src/plugins/wmf/mftvideo.cpp b/src/plugins/wmf/mftvideo.cpp
index 6faa8604c..b7a416213 100644
--- a/src/plugins/wmf/mftvideo.cpp
+++ b/src/plugins/wmf/mftvideo.cpp
@@ -52,6 +52,7 @@ MFTransform::MFTransform():
m_inputType(0),
m_outputType(0),
m_sample(0),
+ m_videoSinkTypeHandler(0),
m_bytesPerLine(0)
{
}
@@ -64,8 +65,8 @@ MFTransform::~MFTransform()
if (m_outputType)
m_outputType->Release();
- for (int i = 0; i < m_mediaTypes.size(); ++i)
- m_mediaTypes[i]->Release();
+ if (m_videoSinkTypeHandler)
+ m_videoSinkTypeHandler->Release();
}
void MFTransform::addProbe(MFVideoProbeControl *probe)
@@ -84,12 +85,18 @@ void MFTransform::removeProbe(MFVideoProbeControl *probe)
m_videoProbes.removeOne(probe);
}
-void MFTransform::addSupportedMediaType(IMFMediaType *type)
+void MFTransform::setVideoSink(IUnknown *videoSink)
{
- if (!type)
- return;
- QMutexLocker locker(&m_mutex);
- m_mediaTypes.append(type);
+ // 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)
@@ -165,9 +172,12 @@ STDMETHODIMP MFTransform::GetInputStreamInfo(DWORD dwInputStreamID, MFT_INPUT_ST
pStreamInfo->cbSize = 0;
pStreamInfo->hnsMaxLatency = 0;
- pStreamInfo->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER;
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;
}
@@ -182,8 +192,11 @@ STDMETHODIMP MFTransform::GetOutputStreamInfo(DWORD dwOutputStreamID, MFT_OUTPUT
return E_POINTER;
pStreamInfo->cbSize = 0;
- pStreamInfo->dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER;
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;
}
@@ -228,20 +241,42 @@ STDMETHODIMP MFTransform::AddInputStreams(DWORD cStreams, DWORD *adwStreamIDs)
STDMETHODIMP MFTransform::GetInputAvailableType(DWORD dwInputStreamID, DWORD dwTypeIndex, IMFMediaType **ppType)
{
- // This MFT does not have a list of preferred input types
- Q_UNUSED(dwInputStreamID);
- Q_UNUSED(dwTypeIndex);
- Q_UNUSED(ppType);
- return E_NOTIMPL;
+ // 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)
+STDMETHODIMP MFTransform::GetOutputAvailableType(DWORD dwOutputStreamID, DWORD dwTypeIndex, IMFMediaType **ppType)
{
- // This MFT does not have a list of preferred output types
- Q_UNUSED(dwOutputStreamID);
- Q_UNUSED(dwTypeIndex);
- Q_UNUSED(ppType);
- return E_NOTIMPL;
+ // 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)
@@ -257,17 +292,14 @@ STDMETHODIMP MFTransform::SetInputType(DWORD dwInputStreamID, IMFMediaType *pTyp
if (!isMediaTypeSupported(pType))
return MF_E_INVALIDMEDIATYPE;
- DWORD flags = 0;
- if (pType && !m_inputType && m_outputType && m_outputType->IsEqual(pType, &flags) != S_OK)
- 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
- if (m_outputType && m_outputType->IsEqual(pType, &flags) != S_OK) {
+ DWORD flags = 0;
+ if (m_outputType && m_outputType->IsEqual(pType, &flags) != S_OK) {
m_outputType->Release();
m_outputType = 0;
}
@@ -286,29 +318,27 @@ STDMETHODIMP MFTransform::SetOutputType(DWORD dwOutputStreamID, IMFMediaType *pT
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;
- if (!isMediaTypeSupported(pType))
- return MF_E_INVALIDMEDIATYPE;
-
DWORD flags = 0;
- if (pType && !m_outputType && m_inputType && m_inputType->IsEqual(pType, &flags) != S_OK)
+ 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) {
+ if (m_outputType)
m_outputType->Release();
- // Output type has changed, discard input type (if it's set) so it's reset later on
- if (m_inputType && m_inputType->IsEqual(pType, &flags) != S_OK) {
- m_inputType->Release();
- m_inputType = 0;
- }
- }
m_outputType = pType;
@@ -333,10 +363,11 @@ STDMETHODIMP MFTransform::GetInputCurrentType(DWORD dwInputStreamID, IMFMediaTyp
if (!m_inputType)
return MF_E_TRANSFORM_TYPE_NOT_SET;
- *ppType = m_inputType;
- (*ppType)->AddRef();
+ // Return a copy to make sure our type is not modified
+ if (FAILED(MFCreateMediaType(ppType)))
+ return E_OUTOFMEMORY;
- return S_OK;
+ return m_inputType->CopyAllItems(*ppType);
}
STDMETHODIMP MFTransform::GetOutputCurrentType(DWORD dwOutputStreamID, IMFMediaType **ppType)
@@ -349,19 +380,14 @@ STDMETHODIMP MFTransform::GetOutputCurrentType(DWORD dwOutputStreamID, IMFMediaT
QMutexLocker locker(&m_mutex);
- if (!m_outputType) {
- if (m_inputType) {
- *ppType = m_inputType;
- (*ppType)->AddRef();
- return S_OK;
- }
+ if (!m_outputType)
return MF_E_TRANSFORM_TYPE_NOT_SET;
- }
- *ppType = m_outputType;
- (*ppType)->AddRef();
+ // Return a copy to make sure our type is not modified
+ if (FAILED(MFCreateMediaType(ppType)))
+ return E_OUTOFMEMORY;
- return S_OK;
+ return m_outputType->CopyAllItems(*ppType);
}
STDMETHODIMP MFTransform::GetInputStatus(DWORD dwInputStreamID, DWORD *pdwFlags)
@@ -374,7 +400,7 @@ STDMETHODIMP MFTransform::GetInputStatus(DWORD dwInputStreamID, DWORD *pdwFlags)
QMutexLocker locker(&m_mutex);
- if (!m_inputType)
+ if (!m_inputType || !m_outputType)
return MF_E_TRANSFORM_TYPE_NOT_SET;
if (m_sample)
@@ -392,7 +418,7 @@ STDMETHODIMP MFTransform::GetOutputStatus(DWORD *pdwFlags)
QMutexLocker locker(&m_mutex);
- if (!m_outputType)
+ if (!m_inputType || !m_outputType)
return MF_E_TRANSFORM_TYPE_NOT_SET;
if (m_sample)
@@ -464,7 +490,7 @@ STDMETHODIMP MFTransform::ProcessInput(DWORD dwInputStreamID, IMFSample *pSample
QMutexLocker locker(&m_mutex);
- if (!m_inputType || !m_outputType)
+ if (!m_inputType)
return MF_E_TRANSFORM_TYPE_NOT_SET;
if (m_sample)
@@ -499,9 +525,6 @@ STDMETHODIMP MFTransform::ProcessInput(DWORD dwInputStreamID, IMFSample *pSample
STDMETHODIMP MFTransform::ProcessOutput(DWORD dwFlags, DWORD cOutputBufferCount, MFT_OUTPUT_DATA_BUFFER *pOutputSamples, DWORD *pdwStatus)
{
- if (dwFlags != 0)
- return E_INVALIDARG;
-
if (pOutputSamples == NULL || pdwStatus == NULL)
return E_POINTER;
@@ -510,57 +533,44 @@ STDMETHODIMP MFTransform::ProcessOutput(DWORD dwFlags, DWORD cOutputBufferCount,
QMutexLocker locker(&m_mutex);
- if (!m_sample)
- return MF_E_TRANSFORM_NEED_MORE_INPUT;
+ 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;
- DWORD sampleLength = 0;
- m_sample->GetTotalLength(&sampleLength);
-
- // If the sample length is null, it means we're getting DXVA buffers.
- // In that case just pass on the sample we got as input.
- // Otherwise we need to copy the input buffer into the buffer the sink
- // is giving us.
- if (pOutputSamples[0].pSample && sampleLength > 0) {
-
- if (FAILED(m_sample->ConvertToContiguousBuffer(&input)))
- goto done;
-
- if (FAILED(pOutputSamples[0].pSample->ConvertToContiguousBuffer(&output)))
- goto done;
-
- DWORD inputLength = 0;
- DWORD outputLength = 0;
- input->GetMaxLength(&inputLength);
- output->GetMaxLength(&outputLength);
+ if (dwFlags == MFT_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER)
+ goto done;
+ else if (dwFlags != 0)
+ return E_INVALIDARG;
- if (outputLength < inputLength) {
- pOutputSamples[0].pSample->RemoveAllBuffers();
- output->Release();
- output = NULL;
- if (SUCCEEDED(MFCreateMemoryBuffer(inputLength, &output)))
- pOutputSamples[0].pSample->AddBuffer(output);
- }
+ if (!m_sample)
+ return MF_E_TRANSFORM_NEED_MORE_INPUT;
- if (output)
- m_sample->CopyToBuffer(output);
+ // 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;
- LONGLONG hnsDuration = 0;
- LONGLONG hnsTime = 0;
- if (SUCCEEDED(m_sample->GetSampleDuration(&hnsDuration)))
- pOutputSamples[0].pSample->SetSampleDuration(hnsDuration);
- if (SUCCEEDED(m_sample->GetSampleTime(&hnsTime)))
- pOutputSamples[0].pSample->SetSampleTime(hnsTime);
+ 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();
- } else {
- if (pOutputSamples[0].pSample)
- pOutputSamples[0].pSample->Release();
- pOutputSamples[0].pSample = m_sample;
- pOutputSamples[0].pSample->AddRef();
+ foreach (MFVideoProbeControl* probe, m_videoProbes)
+ probe->bufferProbed(frame);
}
+ m_videoProbeMutex.unlock();
done:
pOutputSamples[0].dwStatus = 0;
@@ -728,16 +738,10 @@ QByteArray MFTransform::dataFromBuffer(IMFMediaBuffer *buffer, int height, int *
bool MFTransform::isMediaTypeSupported(IMFMediaType *type)
{
- // if the list is empty, it supports all formats
- if (!type || m_mediaTypes.isEmpty())
+ // If we don't have the video sink's type handler,
+ // assume it supports anything...
+ if (!m_videoSinkTypeHandler || !type)
return true;
- for (int i = 0; i < m_mediaTypes.size(); ++i) {
- DWORD flags = 0;
- m_mediaTypes.at(i)->IsEqual(type, &flags);
- if (flags & MF_MEDIATYPE_EQUAL_FORMAT_TYPES)
- return true;
- }
-
- return false;
+ return m_videoSinkTypeHandler->IsMediaTypeSupported(type, NULL) == S_OK;
}
diff --git a/src/plugins/wmf/mftvideo.h b/src/plugins/wmf/mftvideo.h
index 1a188c4db..c37c8f700 100644
--- a/src/plugins/wmf/mftvideo.h
+++ b/src/plugins/wmf/mftvideo.h
@@ -53,7 +53,7 @@ public:
void addProbe(MFVideoProbeControl* probe);
void removeProbe(MFVideoProbeControl* probe);
- void addSupportedMediaType(IMFMediaType *type);
+ void setVideoSink(IUnknown *videoSink);
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID iid, void** ppv);
@@ -99,7 +99,7 @@ private:
IMFSample *m_sample;
QMutex m_mutex;
- QList<IMFMediaType*> m_mediaTypes;
+ IMFMediaTypeHandler *m_videoSinkTypeHandler;
QList<MFVideoProbeControl*> m_videoProbes;
QMutex m_videoProbeMutex;
diff --git a/src/plugins/wmf/player/mfplayersession.cpp b/src/plugins/wmf/player/mfplayersession.cpp
index e4c498b76..0ac1c3d66 100644
--- a/src/plugins/wmf/player/mfplayersession.cpp
+++ b/src/plugins/wmf/player/mfplayersession.cpp
@@ -266,6 +266,25 @@ void MFPlayerSession::handleMediaSourceReady()
}
}
+MFPlayerSession::MediaType MFPlayerSession::getStreamType(IMFStreamDescriptor *stream) const
+{
+ if (!stream)
+ return Unknown;
+
+ IMFMediaTypeHandler *typeHandler = NULL;
+ if (SUCCEEDED(stream->GetMediaTypeHandler(&typeHandler))) {
+ GUID guidMajorType;
+ if (SUCCEEDED(typeHandler->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;
@@ -294,45 +313,58 @@ void MFPlayerSession::setupPlaybackTopology(IMFMediaSource *source, IMFPresentat
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)) {
- MediaType mediaType = Unknown;
- IMFTopologyNode *sourceNode = addSourceNode(topology, source, sourcePD, streamDesc);
- if (sourceNode) {
- IMFTopologyNode *outputNode = addOutputNode(streamDesc, 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);
- }
+ // 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 {
- succeededCount++;
- m_mediaTypes |= mediaType;
- switch (mediaType) {
- case Audio:
- emit audioAvailable();
- break;
- case Video:
- emit videoAvailable();
- break;
+ 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();
}
- outputNode->Release();
+ sourceNode->Release();
}
- sourceNode->Release();
}
+
+ if (fSelected && !streamAdded)
+ sourcePD->DeselectStream(i);
+
streamDesc->Release();
}
}
@@ -377,56 +409,38 @@ IMFTopologyNode* MFPlayerSession::addSourceNode(IMFTopology* topology, IMFMediaS
return NULL;
}
-IMFTopologyNode* MFPlayerSession::addOutputNode(IMFStreamDescriptor *streamDesc, MediaType& mediaType, IMFTopology* topology, DWORD sinkID)
+IMFTopologyNode* MFPlayerSession::addOutputNode(MediaType mediaType, IMFTopology* topology, DWORD sinkID)
{
IMFTopologyNode *node = NULL;
- HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node);
- if (FAILED(hr))
+ if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node)))
return NULL;
- node->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE);
- mediaType = Unknown;
- IMFMediaTypeHandler *handler = NULL;
- hr = streamDesc->GetMediaTypeHandler(&handler);
- if (SUCCEEDED(hr)) {
- GUID guidMajorType;
- hr = handler->GetMajorType(&guidMajorType);
- if (SUCCEEDED(hr)) {
- IMFActivate *activate = NULL;
- if (MFMediaType_Audio == guidMajorType) {
- mediaType = Audio;
- activate = m_playerService->audioEndpointControl()->createActivate();
- } else if (MFMediaType_Video == guidMajorType) {
- 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) {
- hr = node->SetObject(activate);
- if (SUCCEEDED(hr)) {
- hr = node->SetUINT32(MF_TOPONODE_STREAMID, sinkID);
- if (SUCCEEDED(hr)) {
- if (SUCCEEDED(topology->AddNode(node))) {
- handler->Release();
- return node;
- }
- }
- }
- }
+ 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";
}
- handler->Release();
+ } else {
+ // Unknown stream type.
+ emit error(QMediaPlayer::FormatError, tr("Unknown stream type."), false);
}
- node->Release();
- return NULL;
+
+ 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)
@@ -692,7 +706,6 @@ IMFTopology *MFPlayerSession::insertMFT(IMFTopology *topology, TOPOID outputNode
IUnknown *element = 0;
IMFTopologyNode *node = 0;
IUnknown *outputObject = 0;
- IMFMediaTypeHandler *videoSink = 0;
IMFTopologyNode *inputNode = 0;
IMFTopologyNode *mftNode = 0;
bool mftAdded = false;
@@ -711,22 +724,10 @@ IMFTopology *MFPlayerSession::insertMFT(IMFTopology *topology, TOPOID outputNode
if (id != outputNodeId)
break;
- // Use output supported media types for the MFT
if (FAILED(node->GetObject(&outputObject)))
break;
- if (FAILED(outputObject->QueryInterface(IID_IMFMediaTypeHandler, (void**)&videoSink)))
- break;
-
- DWORD mtCount;
- if (FAILED(videoSink->GetMediaTypeCount(&mtCount)))
- break;
-
- for (DWORD i = 0; i < mtCount; ++i) {
- IMFMediaType *type = 0;
- if (SUCCEEDED(videoSink->GetMediaTypeByIndex(i, &type)))
- m_videoProbeMFT->addSupportedMediaType(type);
- }
+ m_videoProbeMFT->setVideoSink(outputObject);
// Insert MFT between the output node and the node connected to it.
DWORD outputIndex = 0;
@@ -760,13 +761,13 @@ IMFTopology *MFPlayerSession::insertMFT(IMFTopology *topology, TOPOID outputNode
node->Release();
if (element)
element->Release();
- if (videoSink)
- videoSink->Release();
if (outputObject)
outputObject->Release();
if (mftAdded)
break;
+ else
+ m_videoProbeMFT->setVideoSink(NULL);
}
} while (false);
diff --git a/src/plugins/wmf/player/mfplayersession.h b/src/plugins/wmf/player/mfplayersession.h
index 1d136ba55..5bbf8e212 100644
--- a/src/plugins/wmf/player/mfplayersession.h
+++ b/src/plugins/wmf/player/mfplayersession.h
@@ -215,9 +215,10 @@ private:
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(IMFStreamDescriptor *streamDesc, MediaType& mediaType, IMFTopology* topology, DWORD sinkID);
+ IMFTopologyNode* addOutputNode(MediaType mediaType, IMFTopology* topology, DWORD sinkID);
bool addAudioSampleGrabberNode(IMFTopology* topology);
bool setupAudioSampleGrabber(IMFTopology *topology, IMFTopologyNode *sourceNode, IMFTopologyNode *outputNode);
diff --git a/src/plugins/wmf/player/mfvideorenderercontrol.cpp b/src/plugins/wmf/player/mfvideorenderercontrol.cpp
index 683dd4b71..222d74ef2 100644
--- a/src/plugins/wmf/player/mfvideorenderercontrol.cpp
+++ b/src/plugins/wmf/player/mfvideorenderercontrol.cpp
@@ -32,15 +32,9 @@
****************************************************************************/
#include "mfvideorenderercontrol.h"
-#include "mfglobal.h"
+#include "mfactivate.h"
-#if defined(QT_OPENGL_ES_2) || defined(QT_OPENGL_DYNAMIC)
-#define MAYBE_ANGLE
-#endif
-
-#ifdef MAYBE_ANGLE
#include "evrcustompresenter.h"
-#endif
#include <qabstractvideosurface.h>
#include <qvideosurfaceformat.h>
@@ -2226,6 +2220,27 @@ namespace
};
}
+
+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)
@@ -2245,13 +2260,11 @@ void MFVideoRendererControl::clear()
if (m_surface)
m_surface->stop();
-#ifdef MAYBE_ANGLE
if (m_presenterActivate) {
m_presenterActivate->ShutdownObject();
m_presenterActivate->Release();
m_presenterActivate = NULL;
}
-#endif
if (m_currentActivate) {
m_currentActivate->ShutdownObject();
@@ -2280,12 +2293,9 @@ void MFVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
connect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(supportedFormatsChanged()));
}
-#ifdef MAYBE_ANGLE
if (m_presenterActivate)
m_presenterActivate->setSurface(m_surface);
- else
-#endif
- if (m_currentActivate)
+ else if (m_currentActivate)
static_cast<VideoRendererActivate*>(m_currentActivate)->setSurface(m_surface);
}
@@ -2323,11 +2333,9 @@ void MFVideoRendererControl::customEvent(QEvent *event)
void MFVideoRendererControl::supportedFormatsChanged()
{
-#ifdef MAYBE_ANGLE
if (m_presenterActivate)
- m_presenterActivate->supportedFormatsChanged();
- else
-#endif
+ return;
+
if (m_currentActivate)
static_cast<VideoRendererActivate*>(m_currentActivate)->supportedFormatsChanged();
}
@@ -2347,26 +2355,67 @@ IMFActivate* MFVideoRendererControl::createActivate()
clear();
-#ifdef MAYBE_ANGLE
- // We can use the EVR with our custom presenter only if the surface supports OpenGL
- // texture handles. We also require ANGLE (due to the D3D interop).
- if (!m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty()
- && QMediaOpenGLHelper::isANGLE()) {
- // 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);
- }
- }
-#endif
-
- if (!m_currentActivate)
+ // Create the EVR media sink, but replace the presenter with our own
+ if (SUCCEEDED(MFCreateVideoRendererActivate(::GetShellWindow(), &m_currentActivate))) {
+ m_presenterActivate = new EVRCustomPresenterActivate;
+ m_currentActivate->SetUnknown(MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_ACTIVATE, m_presenterActivate);
+ } else {
m_currentActivate = new VideoRendererActivate(this);
+ }
setSurface(m_surface);
return m_currentActivate;
}
+
+EVRCustomPresenterActivate::EVRCustomPresenterActivate()
+ : MFAbstractActivate()
+ , m_presenter(0)
+ , m_surface(0)
+{ }
+
+HRESULT EVRCustomPresenterActivate::ActivateObject(REFIID riid, void **ppv)
+{
+ if (!ppv)
+ return E_INVALIDARG;
+ QMutexLocker locker(&m_mutex);
+ if (!m_presenter) {
+ m_presenter = new EVRCustomPresenter;
+ if (m_surface)
+ m_presenter->setSurface(m_surface);
+ }
+ return m_presenter->QueryInterface(riid, ppv);
+}
+
+HRESULT EVRCustomPresenterActivate::ShutdownObject()
+{
+ // The presenter does not implement IMFShutdown so
+ // this function is the same as DetachObject()
+ return DetachObject();
+}
+
+HRESULT EVRCustomPresenterActivate::DetachObject()
+{
+ QMutexLocker locker(&m_mutex);
+ if (m_presenter) {
+ m_presenter->Release();
+ m_presenter = 0;
+ }
+ return S_OK;
+}
+
+void EVRCustomPresenterActivate::setSurface(QAbstractVideoSurface *surface)
+{
+ QMutexLocker locker(&m_mutex);
+ if (m_surface == surface)
+ return;
+
+ m_surface = surface;
+
+ if (m_presenter)
+ m_presenter->setSurface(surface);
+}
+
#include "moc_mfvideorenderercontrol.cpp"
#include "mfvideorenderercontrol.moc"
diff --git a/src/plugins/wmf/player/mfvideorenderercontrol.h b/src/plugins/wmf/player/mfvideorenderercontrol.h
index ca3b95d10..224fcea51 100644
--- a/src/plugins/wmf/player/mfvideorenderercontrol.h
+++ b/src/plugins/wmf/player/mfvideorenderercontrol.h
@@ -38,14 +38,10 @@
#include <mfapi.h>
#include <mfidl.h>
-QT_BEGIN_NAMESPACE
+QT_USE_NAMESPACE
class EVRCustomPresenterActivate;
-QT_END_NAMESPACE
-
-QT_USE_NAMESPACE
-
class MFVideoRendererControl : public QVideoRendererControl
{
Q_OBJECT
diff --git a/src/plugins/wmf/wmf.pro b/src/plugins/wmf/wmf.pro
index 68a777f37..1f43bb128 100644
--- a/src/plugins/wmf/wmf.pro
+++ b/src/plugins/wmf/wmf.pro
@@ -17,7 +17,6 @@ HEADERS += \
sourceresolver.h \
samplegrabber.h \
mftvideo.h \
- mfglobal.h \
mfactivate.h
SOURCES += \
@@ -26,21 +25,7 @@ SOURCES += \
sourceresolver.cpp \
samplegrabber.cpp \
mftvideo.cpp \
- mfactivate.cpp \
- mfglobal.cpp
-
-contains(QT_CONFIG, angle)|contains(QT_CONFIG, dynamicgl) {
- LIBS += -ld3d9 -ldxva2 -lwinmm -levr
- QT += gui-private
-
- HEADERS += \
- $$PWD/evrcustompresenter.h \
- $$PWD/evrd3dpresentengine.h
-
- SOURCES += \
- $$PWD/evrcustompresenter.cpp \
- $$PWD/evrd3dpresentengine.cpp
-}
+ mfactivate.cpp
include (player/player.pri)
include (decoder/decoder.pri)
diff --git a/src/qtmultimediaquicktools/qdeclarativevideooutput_render_p.h b/src/qtmultimediaquicktools/qdeclarativevideooutput_render_p.h
index d33bc0b11..e05adc3b1 100644
--- a/src/qtmultimediaquicktools/qdeclarativevideooutput_render_p.h
+++ b/src/qtmultimediaquicktools/qdeclarativevideooutput_render_p.h
@@ -46,10 +46,10 @@
// We mean it.
//
-#include "qdeclarativevideooutput_backend_p.h"
-#include "qsgvideonode_yuv.h"
-#include "qsgvideonode_rgb.h"
-#include "qsgvideonode_texture.h"
+#include <private/qdeclarativevideooutput_backend_p.h>
+#include <private/qsgvideonode_yuv_p.h>
+#include <private/qsgvideonode_rgb_p.h>
+#include <private/qsgvideonode_texture_p.h>
#include <QtCore/qmutex.h>
#include <QtMultimedia/qabstractvideosurface.h>
diff --git a/src/qtmultimediaquicktools/qdeclarativevideooutput_window_p.h b/src/qtmultimediaquicktools/qdeclarativevideooutput_window_p.h
index 68403852e..a7c5ee810 100644
--- a/src/qtmultimediaquicktools/qdeclarativevideooutput_window_p.h
+++ b/src/qtmultimediaquicktools/qdeclarativevideooutput_window_p.h
@@ -45,7 +45,7 @@
// We mean it.
//
-#include "qdeclarativevideooutput_backend_p.h"
+#include <private/qdeclarativevideooutput_backend_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/qtmultimediaquicktools/qsgvideonode_p.cpp b/src/qtmultimediaquicktools/qsgvideonode_p.cpp
index 0fb4239d6..28d11e239 100644
--- a/src/qtmultimediaquicktools/qsgvideonode_p.cpp
+++ b/src/qtmultimediaquicktools/qsgvideonode_p.cpp
@@ -118,4 +118,8 @@ void QSGVideoNode::setTexturedRectGeometry(const QRectF &rect, const QRectF &tex
markDirty(DirtyGeometry);
}
+QSGVideoNodeFactoryInterface::~QSGVideoNodeFactoryInterface()
+{
+}
+
QT_END_NAMESPACE
diff --git a/src/qtmultimediaquicktools/qsgvideonode_rgb.cpp b/src/qtmultimediaquicktools/qsgvideonode_rgb.cpp
index 5093acb59..1ba8f98bf 100644
--- a/src/qtmultimediaquicktools/qsgvideonode_rgb.cpp
+++ b/src/qtmultimediaquicktools/qsgvideonode_rgb.cpp
@@ -30,7 +30,7 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-#include "qsgvideonode_rgb.h"
+#include "qsgvideonode_rgb_p.h"
#include <QtQuick/qsgtexturematerial.h>
#include <QtQuick/qsgmaterial.h>
#include <QtCore/qmutex.h>
diff --git a/src/qtmultimediaquicktools/qsgvideonode_rgb.h b/src/qtmultimediaquicktools/qsgvideonode_rgb_p.h
index 77d11b6d8..501df9cbc 100644
--- a/src/qtmultimediaquicktools/qsgvideonode_rgb.h
+++ b/src/qtmultimediaquicktools/qsgvideonode_rgb_p.h
@@ -34,6 +34,17 @@
#ifndef QSGVIDEONODE_RGB_H
#define QSGVIDEONODE_RGB_H
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/qsgvideonode_p.h>
#include <QtMultimedia/qvideosurfaceformat.h>
diff --git a/src/qtmultimediaquicktools/qsgvideonode_texture.cpp b/src/qtmultimediaquicktools/qsgvideonode_texture.cpp
index 500c1c62f..1170af792 100644
--- a/src/qtmultimediaquicktools/qsgvideonode_texture.cpp
+++ b/src/qtmultimediaquicktools/qsgvideonode_texture.cpp
@@ -30,7 +30,7 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-#include "qsgvideonode_texture.h"
+#include "qsgvideonode_texture_p.h"
#include <QtQuick/qsgtexturematerial.h>
#include <QtQuick/qsgmaterial.h>
#include <QtCore/qmutex.h>
diff --git a/src/qtmultimediaquicktools/qsgvideonode_texture.h b/src/qtmultimediaquicktools/qsgvideonode_texture_p.h
index 0849c574f..0304b2efe 100644
--- a/src/qtmultimediaquicktools/qsgvideonode_texture.h
+++ b/src/qtmultimediaquicktools/qsgvideonode_texture_p.h
@@ -34,6 +34,17 @@
#ifndef QSGVIDEONODE_TEXTURE_H
#define QSGVIDEONODE_TEXTURE_H
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/qsgvideonode_p.h>
#include <QtMultimedia/qvideosurfaceformat.h>
diff --git a/src/qtmultimediaquicktools/qsgvideonode_yuv.cpp b/src/qtmultimediaquicktools/qsgvideonode_yuv.cpp
index af1e98cdf..b2979f50b 100644
--- a/src/qtmultimediaquicktools/qsgvideonode_yuv.cpp
+++ b/src/qtmultimediaquicktools/qsgvideonode_yuv.cpp
@@ -30,7 +30,7 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-#include "qsgvideonode_yuv.h"
+#include "qsgvideonode_yuv_p.h"
#include <QtCore/qmutex.h>
#include <QtQuick/qsgtexturematerial.h>
#include <QtQuick/qsgmaterial.h>
diff --git a/src/qtmultimediaquicktools/qsgvideonode_yuv.h b/src/qtmultimediaquicktools/qsgvideonode_yuv_p.h
index 776f0a5a2..9c0a1f36a 100644
--- a/src/qtmultimediaquicktools/qsgvideonode_yuv.h
+++ b/src/qtmultimediaquicktools/qsgvideonode_yuv_p.h
@@ -34,6 +34,17 @@
#ifndef QSGVIDEONODE_YUV_H
#define QSGVIDEONODE_YUV_H
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists 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/qsgvideonode_p.h>
#include <QtMultimedia/qvideosurfaceformat.h>
diff --git a/src/qtmultimediaquicktools/qtmultimediaquicktools.pro b/src/qtmultimediaquicktools/qtmultimediaquicktools.pro
index 61c5f7b20..b38f209e3 100644
--- a/src/qtmultimediaquicktools/qtmultimediaquicktools.pro
+++ b/src/qtmultimediaquicktools/qtmultimediaquicktools.pro
@@ -9,12 +9,21 @@ DEFINES += QT_BUILD_QTMM_QUICK_LIB
# Header files must go inside source directory of a module
# to be installed by syncqt.
INCLUDEPATH += ../multimedia/qtmultimediaquicktools_headers/
+VPATH += ../multimedia/qtmultimediaquicktools_headers/
PRIVATE_HEADERS += \
- ../multimedia/qtmultimediaquicktools_headers/qdeclarativevideooutput_p.h \
- ../multimedia/qtmultimediaquicktools_headers/qdeclarativevideooutput_backend_p.h \
- ../multimedia/qtmultimediaquicktools_headers/qsgvideonode_p.h \
- ../multimedia/qtmultimediaquicktools_headers/qtmultimediaquickdefs_p.h
+ qdeclarativevideooutput_p.h \
+ qdeclarativevideooutput_backend_p.h \
+ qsgvideonode_p.h \
+ qtmultimediaquickdefs_p.h
+
+HEADERS += \
+ $$PRIVATE_HEADERS \
+ qdeclarativevideooutput_render_p.h \
+ qdeclarativevideooutput_window_p.h \
+ qsgvideonode_yuv_p.h \
+ qsgvideonode_rgb_p.h \
+ qsgvideonode_texture_p.h
SOURCES += \
qsgvideonode_p.cpp \
@@ -25,14 +34,6 @@ SOURCES += \
qsgvideonode_rgb.cpp \
qsgvideonode_texture.cpp
-HEADERS += \
- $$PRIVATE_HEADERS \
- qdeclarativevideooutput_render_p.h \
- qdeclarativevideooutput_window_p.h \
- qsgvideonode_yuv.h \
- qsgvideonode_rgb.h \
- qsgvideonode_texture.h
-
RESOURCES += \
qtmultimediaquicktools.qrc
diff --git a/tests/auto/integration/qaudiodecoderbackend/BLACKLIST b/tests/auto/integration/qaudiodecoderbackend/BLACKLIST
index bd2349568..038b89022 100644
--- a/tests/auto/integration/qaudiodecoderbackend/BLACKLIST
+++ b/tests/auto/integration/qaudiodecoderbackend/BLACKLIST
@@ -6,6 +6,7 @@ redhatenterpriselinuxworkstation-6.6
osx-10.8
osx-10.9
osx-10.10
+osx-10.11
windows 32bit developer-build
windows 64bit developer-build
@@ -15,6 +16,7 @@ redhatenterpriselinuxworkstation-6.6
osx-10.8
osx-10.9
osx-10.10
+osx-10.11
windows 32bit developer-build
windows 64bit developer-build
@@ -24,6 +26,7 @@ redhatenterpriselinuxworkstation-6.6
osx-10.8
osx-10.9
osx-10.10
+osx-10.11
windows 32bit developer-build
windows 64bit developer-build
@@ -33,5 +36,6 @@ redhatenterpriselinuxworkstation-6.6
osx-10.8
osx-10.9
osx-10.10
+osx-10.11
windows 32bit developer-build
windows 64bit developer-build
diff --git a/tests/auto/integration/qmediaplayerbackend/BLACKLIST b/tests/auto/integration/qmediaplayerbackend/BLACKLIST
index b9907f305..9f0d4b746 100644
--- a/tests/auto/integration/qmediaplayerbackend/BLACKLIST
+++ b/tests/auto/integration/qmediaplayerbackend/BLACKLIST
@@ -3,6 +3,7 @@
osx-10.8
osx-10.9
osx-10.10
+osx-10.11
windows 32bit developer-build
windows 64bit developer-build
@@ -48,6 +49,9 @@ redhatenterpriselinuxworkstation-6.6
[seekPauseSeek]
redhatenterpriselinuxworkstation-6.6
+[seekInStoppedState]
+redhatenterpriselinuxworkstation-6.6
+
[subsequentPlayback]
redhatenterpriselinuxworkstation-6.6
diff --git a/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp b/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp
index 916f70eff..c032734c5 100644
--- a/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp
+++ b/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp
@@ -220,13 +220,13 @@ void tst_QMediaPlayerBackend::initTestCase()
qRegisterMetaType<QMediaContent>();
QStringList mediaCandidates;
- mediaCandidates << QFINDTESTDATA("testdata/colors.ogv");
mediaCandidates << QFINDTESTDATA("testdata/colors.mp4");
+ mediaCandidates << QFINDTESTDATA("testdata/colors.ogv");
localVideoFile = selectMediaFile(mediaCandidates);
mediaCandidates.clear();
- mediaCandidates << QFINDTESTDATA("testdata/nokia-tune.mkv");
mediaCandidates << QFINDTESTDATA("testdata/nokia-tune.mp3");
+ mediaCandidates << QFINDTESTDATA("testdata/nokia-tune.mkv");
localCompressedSoundFile = selectMediaFile(mediaCandidates);
localFileWithMetadata = selectMediaFile(QStringList() << QFINDTESTDATA("testdata/nokia-tune.mp3"));
@@ -478,7 +478,7 @@ void tst_QMediaPlayerBackend::processEOS()
//position is reset to start
QTRY_VERIFY(player.position() < 100);
- QVERIFY(positionSpy.count() > 0);
+ QTRY_VERIFY(positionSpy.count() > 0);
QCOMPARE(positionSpy.first()[0].value<qint64>(), 0);
QCOMPARE(player.state(), QMediaPlayer::PlayingState);
@@ -720,7 +720,7 @@ void tst_QMediaPlayerBackend::seekPauseSeek()
player.pause();
QTRY_COMPARE(player.state(), QMediaPlayer::PausedState); // it might take some time for the operation to be completed
- QTRY_COMPARE(surface->m_frameList.size(), 1); // we must see a frame at position 7000 here
+ QTRY_VERIFY(!surface->m_frameList.isEmpty()); // we must see a frame at position 7000 here
{
QVideoFrame frame = surface->m_frameList.back();
@@ -739,12 +739,13 @@ void tst_QMediaPlayerBackend::seekPauseSeek()
frame.unmap();
}
+ surface->m_frameList.clear();
positionSpy.clear();
position = 12000;
player.setPosition(position);
QTRY_VERIFY(!positionSpy.isEmpty() && qAbs(player.position() - position) < (qint64)500);
QCOMPARE(player.state(), QMediaPlayer::PausedState);
- QCOMPARE(surface->m_frameList.size(), 2);
+ QVERIFY(!surface->m_frameList.isEmpty());
{
QVideoFrame frame = surface->m_frameList.back();
diff --git a/tests/auto/unit/qmediaplaylist/tst_qmediaplaylist.cpp b/tests/auto/unit/qmediaplaylist/tst_qmediaplaylist.cpp
index daecda82b..104046d0e 100644
--- a/tests/auto/unit/qmediaplaylist/tst_qmediaplaylist.cpp
+++ b/tests/auto/unit/qmediaplaylist/tst_qmediaplaylist.cpp
@@ -51,14 +51,16 @@
QT_USE_NAMESPACE
-class MockReadOnlyPlaylistObject : public QMediaObject
+class MockPlaylistObject : public QMediaObject
{
Q_OBJECT
public:
- MockReadOnlyPlaylistObject(QObject *parent = 0)
- :QMediaObject(parent, new MockPlaylistService)
+ MockPlaylistObject(QObject *parent = 0)
+ : QMediaObject(parent, mockService = new MockPlaylistService)
{
}
+
+ MockPlaylistService *mockService;
};
class tst_QMediaPlaylist : public QObject
@@ -711,7 +713,9 @@ void tst_QMediaPlaylist::shuffle()
void tst_QMediaPlaylist::readOnlyPlaylist()
{
- MockReadOnlyPlaylistObject mediaObject;
+ MockPlaylistObject mediaObject;
+ mediaObject.mockService->mockControl->setReadOnly(true);
+
QMediaPlaylist playlist;
mediaObject.bind(&playlist);
@@ -777,26 +781,280 @@ void tst_QMediaPlaylist::readOnlyPlaylist()
void tst_QMediaPlaylist::setMediaObject()
{
- MockReadOnlyPlaylistObject mediaObject;
-
- QMediaPlaylist playlist;
- QVERIFY(playlist.mediaObject() == 0);
- QVERIFY(!playlist.isReadOnly());
-
- mediaObject.bind(&playlist);
- QCOMPARE(playlist.mediaObject(), qobject_cast<QMediaObject*>(&mediaObject));
- QCOMPARE(playlist.mediaCount(), 3);
- QVERIFY(playlist.isReadOnly());
-
- mediaObject.unbind(&playlist);
- QVERIFY(playlist.mediaObject() == 0);
- QCOMPARE(playlist.mediaCount(), 0);
- QVERIFY(!playlist.isReadOnly());
+ QMediaContent content0(QUrl(QLatin1String("test://audio/song1.mp3")));
+ QMediaContent content1(QUrl(QLatin1String("test://audio/song2.mp3")));
+ QMediaContent content2(QUrl(QLatin1String("test://video/movie1.mp4")));
+ QMediaContent content3(QUrl(QLatin1String("test://video/movie2.mp4")));
- mediaObject.bind(&playlist);
- QCOMPARE(playlist.mediaObject(), qobject_cast<QMediaObject*>(&mediaObject));
- QCOMPARE(playlist.mediaCount(), 3);
- QVERIFY(playlist.isReadOnly());
+ {
+ MockPlaylistObject mediaObject;
+
+ QMediaPlaylist playlist;
+ QSignalSpy currentIndexSpy(&playlist, SIGNAL(currentIndexChanged(int)));
+ QSignalSpy playbackModeSpy(&playlist, SIGNAL(playbackModeChanged(QMediaPlaylist::PlaybackMode)));
+ QSignalSpy mediaAboutToBeInsertedSpy(&playlist, SIGNAL(mediaAboutToBeInserted(int, int)));
+ QSignalSpy mediaInsertedSpy(&playlist, SIGNAL(mediaInserted(int, int)));
+ QSignalSpy mediaAboutToBeRemovedSpy(&playlist, SIGNAL(mediaAboutToBeRemoved(int, int)));
+ QSignalSpy mediaRemovedSpy(&playlist, SIGNAL(mediaRemoved(int, int)));
+ QSignalSpy mediaChangedSpy(&playlist, SIGNAL(mediaChanged(int, int)));
+
+ QVERIFY(playlist.isEmpty());
+
+ mediaObject.bind(&playlist);
+
+ // Playlist is now using the service's control, nothing should have changed
+ QVERIFY(playlist.isEmpty());
+ QCOMPARE(playlist.currentIndex(), -1);
+ QCOMPARE(playlist.playbackMode(), QMediaPlaylist::Sequential);
+ QCOMPARE(currentIndexSpy.count(), 0);
+ QCOMPARE(playbackModeSpy.count(), 0);
+ QCOMPARE(mediaAboutToBeInsertedSpy.count(), 0);
+ QCOMPARE(mediaInsertedSpy.count(), 0);
+ QCOMPARE(mediaAboutToBeRemovedSpy.count(), 0);
+ QCOMPARE(mediaRemovedSpy.count(), 0);
+ QCOMPARE(mediaChangedSpy.count(), 0);
+
+ // add items to service's playlist control
+ playlist.addMedia(content0);
+ playlist.addMedia(content1);
+ playlist.setCurrentIndex(1);
+ playlist.setPlaybackMode(QMediaPlaylist::Random);
+ QCOMPARE(playlist.mediaCount(), 2);
+ QCOMPARE(playlist.currentIndex(), 1);
+ QCOMPARE(playlist.currentMedia(), content1);
+ QCOMPARE(playlist.playbackMode(), QMediaPlaylist::Random);
+
+ currentIndexSpy.clear();
+ playbackModeSpy.clear();
+ mediaAboutToBeInsertedSpy.clear();
+ mediaInsertedSpy.clear();
+ mediaAboutToBeRemovedSpy.clear();
+ mediaRemovedSpy.clear();
+ mediaChangedSpy.clear();
+
+ // unbind the playlist, reverting back to the internal control.
+ // playlist content should't have changed.
+ mediaObject.unbind(&playlist);
+ QCOMPARE(playlist.mediaCount(), 2);
+ QCOMPARE(playlist.currentIndex(), 1);
+ QCOMPARE(playlist.currentMedia(), content1);
+ QCOMPARE(playlist.playbackMode(), QMediaPlaylist::Random);
+ QCOMPARE(playbackModeSpy.count(), 0);
+ QCOMPARE(mediaAboutToBeInsertedSpy.count(), 0);
+ QCOMPARE(mediaInsertedSpy.count(), 0);
+ QCOMPARE(mediaAboutToBeRemovedSpy.count(), 0);
+ QCOMPARE(mediaRemovedSpy.count(), 0);
+ QCOMPARE(mediaChangedSpy.count(), 0);
+ }
+ {
+ MockPlaylistObject mediaObject;
+
+ QMediaPlaylist playlist;
+ QVERIFY(playlist.isEmpty());
+ // Add items to playlist before binding to the service (internal control)
+ playlist.addMedia(content0);
+ playlist.addMedia(content1);
+ playlist.addMedia(content2);
+ playlist.setCurrentIndex(2);
+ playlist.setPlaybackMode(QMediaPlaylist::CurrentItemOnce);
+
+ QSignalSpy currentIndexSpy(&playlist, SIGNAL(currentIndexChanged(int)));
+ QSignalSpy playbackModeSpy(&playlist, SIGNAL(playbackModeChanged(QMediaPlaylist::PlaybackMode)));
+ QSignalSpy mediaAboutToBeInsertedSpy(&playlist, SIGNAL(mediaAboutToBeInserted(int, int)));
+ QSignalSpy mediaInsertedSpy(&playlist, SIGNAL(mediaInserted(int, int)));
+ QSignalSpy mediaAboutToBeRemovedSpy(&playlist, SIGNAL(mediaAboutToBeRemoved(int, int)));
+ QSignalSpy mediaRemovedSpy(&playlist, SIGNAL(mediaRemoved(int, int)));
+ QSignalSpy mediaChangedSpy(&playlist, SIGNAL(mediaChanged(int, int)));
+
+ // Bind playlist, content should be unchanged
+ mediaObject.bind(&playlist);
+ QCOMPARE(playlist.mediaCount(), 3);
+ QCOMPARE(playlist.currentIndex(), 2);
+ QCOMPARE(playlist.currentMedia(), content2);
+ QCOMPARE(playlist.playbackMode(), QMediaPlaylist::CurrentItemOnce);
+ QCOMPARE(currentIndexSpy.count(), 0);
+ QCOMPARE(playbackModeSpy.count(), 0);
+ QCOMPARE(mediaAboutToBeInsertedSpy.count(), 0);
+ QCOMPARE(mediaInsertedSpy.count(), 0);
+ QCOMPARE(mediaAboutToBeRemovedSpy.count(), 0);
+ QCOMPARE(mediaRemovedSpy.count(), 0);
+ QCOMPARE(mediaChangedSpy.count(), 0);
+
+ // Clear playlist content (service's playlist control)
+ playlist.clear();
+ playlist.setCurrentIndex(-1);
+ playlist.setPlaybackMode(QMediaPlaylist::Random);
+
+ currentIndexSpy.clear();
+ playbackModeSpy.clear();
+ mediaAboutToBeInsertedSpy.clear();
+ mediaInsertedSpy.clear();
+ mediaAboutToBeRemovedSpy.clear();
+ mediaRemovedSpy.clear();
+ mediaChangedSpy.clear();
+
+ // unbind playlist from service, reverting back to the internal control.
+ // playlist should still be empty
+ mediaObject.unbind(&playlist);
+ QCOMPARE(playlist.mediaCount(), 0);
+ QCOMPARE(playlist.currentIndex(), -1);
+ QCOMPARE(playlist.currentMedia(), QMediaContent());
+ QCOMPARE(playlist.playbackMode(), QMediaPlaylist::Random);
+ QCOMPARE(playbackModeSpy.count(), 0);
+ QCOMPARE(mediaAboutToBeInsertedSpy.count(), 0);
+ QCOMPARE(mediaInsertedSpy.count(), 0);
+ QCOMPARE(mediaAboutToBeRemovedSpy.count(), 0);
+ QCOMPARE(mediaRemovedSpy.count(), 0);
+ QCOMPARE(mediaChangedSpy.count(), 0);
+ }
+ {
+ MockPlaylistObject mediaObject;
+
+ QMediaPlaylist playlist;
+ QVERIFY(playlist.isEmpty());
+ // Add items to playlist before attaching to media player (internal control)
+ playlist.addMedia(content0);
+ playlist.addMedia(content1);
+ playlist.setCurrentIndex(-1);
+ playlist.setPlaybackMode(QMediaPlaylist::CurrentItemOnce);
+
+ // Add items to service's playlist before binding
+ QMediaPlaylistProvider *pp = mediaObject.mockService->mockControl->playlistProvider();
+ pp->addMedia(content2);
+ pp->addMedia(content3);
+ mediaObject.mockService->mockControl->setCurrentIndex(1);
+ mediaObject.mockService->mockControl->setPlaybackMode(QMediaPlaylist::Random);
+
+ QSignalSpy currentIndexSpy(&playlist, SIGNAL(currentIndexChanged(int)));
+ QSignalSpy playbackModeSpy(&playlist, SIGNAL(playbackModeChanged(QMediaPlaylist::PlaybackMode)));
+ QSignalSpy mediaAboutToBeInsertedSpy(&playlist, SIGNAL(mediaAboutToBeInserted(int, int)));
+ QSignalSpy mediaInsertedSpy(&playlist, SIGNAL(mediaInserted(int, int)));
+ QSignalSpy mediaAboutToBeRemovedSpy(&playlist, SIGNAL(mediaAboutToBeRemoved(int, int)));
+ QSignalSpy mediaRemovedSpy(&playlist, SIGNAL(mediaRemoved(int, int)));
+ QSignalSpy mediaChangedSpy(&playlist, SIGNAL(mediaChanged(int, int)));
+
+ // Bind playlist, it should contain only what was explicitly added to the playlist.
+ // Anything that was present in the service's control should have been cleared
+ mediaObject.bind(&playlist);
+ QCOMPARE(playlist.mediaCount(), 2);
+ QCOMPARE(playlist.currentIndex(), -1);
+ QCOMPARE(playlist.playbackMode(), QMediaPlaylist::CurrentItemOnce);
+ QCOMPARE(currentIndexSpy.count(), 0);
+ QCOMPARE(playbackModeSpy.count(), 0);
+ QCOMPARE(mediaAboutToBeInsertedSpy.count(), 0);
+ QCOMPARE(mediaInsertedSpy.count(), 0);
+ QCOMPARE(mediaAboutToBeRemovedSpy.count(), 0);
+ QCOMPARE(mediaRemovedSpy.count(), 0);
+ QCOMPARE(mediaChangedSpy.count(), 0);
+
+ // do some changes
+ playlist.removeMedia(0); // content0
+ playlist.addMedia(content3);
+ playlist.setCurrentIndex(0);
+
+ currentIndexSpy.clear();
+ playbackModeSpy.clear();
+ mediaAboutToBeInsertedSpy.clear();
+ mediaInsertedSpy.clear();
+ mediaAboutToBeRemovedSpy.clear();
+ mediaRemovedSpy.clear();
+ mediaChangedSpy.clear();
+
+ // unbind playlist from service
+ mediaObject.unbind(&playlist);
+ QCOMPARE(playlist.mediaCount(), 2);
+ QCOMPARE(playlist.currentIndex(), 0);
+ QCOMPARE(playlist.currentMedia(), content1);
+ QCOMPARE(playlist.playbackMode(), QMediaPlaylist::CurrentItemOnce);
+ QCOMPARE(currentIndexSpy.count(), 0);
+ QCOMPARE(playbackModeSpy.count(), 0);
+ QCOMPARE(mediaAboutToBeInsertedSpy.count(), 0);
+ QCOMPARE(mediaInsertedSpy.count(), 0);
+ QCOMPARE(mediaAboutToBeRemovedSpy.count(), 0);
+ QCOMPARE(mediaRemovedSpy.count(), 0);
+ QCOMPARE(mediaChangedSpy.count(), 0);
+
+ // bind again, nothing should have changed
+ mediaObject.bind(&playlist);
+ QCOMPARE(playlist.mediaCount(), 2);
+ QCOMPARE(playlist.currentIndex(), 0);
+ QCOMPARE(playlist.currentMedia(), content1);
+ QCOMPARE(playlist.playbackMode(), QMediaPlaylist::CurrentItemOnce);
+ QCOMPARE(currentIndexSpy.count(), 0);
+ QCOMPARE(playbackModeSpy.count(), 0);
+ QCOMPARE(mediaAboutToBeInsertedSpy.count(), 0);
+ QCOMPARE(mediaInsertedSpy.count(), 0);
+ QCOMPARE(mediaAboutToBeRemovedSpy.count(), 0);
+ QCOMPARE(mediaRemovedSpy.count(), 0);
+ QCOMPARE(mediaChangedSpy.count(), 0);
+ }
+ {
+ MockPlaylistObject mediaObject;
+ mediaObject.mockService->mockControl->setReadOnly(true);
+
+ QMediaPlaylist playlist;
+ QVERIFY(playlist.isEmpty());
+ // Add items to playlist before binding to the service internal control)
+ playlist.addMedia(content0);
+ playlist.addMedia(content1);
+ playlist.setCurrentIndex(-1);
+ playlist.setPlaybackMode(QMediaPlaylist::CurrentItemOnce);
+
+ QSignalSpy currentIndexSpy(&playlist, SIGNAL(currentIndexChanged(int)));
+ QSignalSpy playbackModeSpy(&playlist, SIGNAL(playbackModeChanged(QMediaPlaylist::PlaybackMode)));
+ QSignalSpy mediaAboutToBeInsertedSpy(&playlist, SIGNAL(mediaAboutToBeInserted(int, int)));
+ QSignalSpy mediaInsertedSpy(&playlist, SIGNAL(mediaInserted(int, int)));
+ QSignalSpy mediaAboutToBeRemovedSpy(&playlist, SIGNAL(mediaAboutToBeRemoved(int, int)));
+ QSignalSpy mediaRemovedSpy(&playlist, SIGNAL(mediaRemoved(int, int)));
+ QSignalSpy mediaChangedSpy(&playlist, SIGNAL(mediaChanged(int, int)));
+
+ // Bind playlist. Since the service's control is read-only, no synchronization
+ // should be done with the internal control. The mediaRemoved() and mediaInserted()
+ // should be emitted to notify about the change.
+ mediaObject.bind(&playlist);
+ QCOMPARE(playlist.mediaCount(), 3);
+ QCOMPARE(playlist.currentIndex(), -1);
+ QCOMPARE(playlist.playbackMode(), QMediaPlaylist::CurrentItemOnce);
+ QCOMPARE(currentIndexSpy.count(), 0);
+ QCOMPARE(playbackModeSpy.count(), 0);
+
+ QCOMPARE(mediaAboutToBeRemovedSpy.count(), 1);
+ QCOMPARE(mediaAboutToBeRemovedSpy.last().at(0).toInt(), 0);
+ QCOMPARE(mediaAboutToBeRemovedSpy.last().at(1).toInt(), 1);
+ QCOMPARE(mediaRemovedSpy.count(), 1);
+ QCOMPARE(mediaRemovedSpy.last().at(0).toInt(), 0);
+ QCOMPARE(mediaRemovedSpy.last().at(1).toInt(), 1);
+
+ QCOMPARE(mediaAboutToBeInsertedSpy.count(), 1);
+ QCOMPARE(mediaAboutToBeInsertedSpy.last().at(0).toInt(), 0);
+ QCOMPARE(mediaAboutToBeInsertedSpy.last().at(1).toInt(), 2);
+ QCOMPARE(mediaInsertedSpy.count(), 1);
+ QCOMPARE(mediaInsertedSpy.last().at(0).toInt(), 0);
+ QCOMPARE(mediaInsertedSpy.last().at(1).toInt(), 2);
+
+ QCOMPARE(mediaChangedSpy.count(), 0);
+
+ currentIndexSpy.clear();
+ playbackModeSpy.clear();
+ mediaAboutToBeInsertedSpy.clear();
+ mediaInsertedSpy.clear();
+ mediaAboutToBeRemovedSpy.clear();
+ mediaRemovedSpy.clear();
+ mediaChangedSpy.clear();
+
+ // detach playlist from player
+ mediaObject.unbind(&playlist);
+ QCOMPARE(playlist.mediaCount(), 3);
+ QCOMPARE(playlist.currentIndex(), -1);
+ QCOMPARE(playlist.playbackMode(), QMediaPlaylist::CurrentItemOnce);
+ QCOMPARE(currentIndexSpy.count(), 0);
+ QCOMPARE(playbackModeSpy.count(), 0);
+ QCOMPARE(mediaAboutToBeInsertedSpy.count(), 0);
+ QCOMPARE(mediaInsertedSpy.count(), 0);
+ QCOMPARE(mediaAboutToBeRemovedSpy.count(), 0);
+ QCOMPARE(mediaRemovedSpy.count(), 0);
+ QCOMPARE(mediaChangedSpy.count(), 0);
+ }
}
void tst_QMediaPlaylist::testCurrentIndexChanged_signal()
diff --git a/tests/auto/unit/qmultimedia_common/mockmediaplaylistcontrol.h b/tests/auto/unit/qmultimedia_common/mockmediaplaylistcontrol.h
index 6620aa763..9f4eabf38 100644
--- a/tests/auto/unit/qmultimedia_common/mockmediaplaylistcontrol.h
+++ b/tests/auto/unit/qmultimedia_common/mockmediaplaylistcontrol.h
@@ -36,24 +36,52 @@
#include <private/qmediaplaylistcontrol_p.h>
#include <private/qmediaplaylistnavigator_p.h>
+#include <private/qmedianetworkplaylistprovider_p.h>
#include "mockreadonlyplaylistprovider.h"
-// Hmm, read only.
class MockMediaPlaylistControl : public QMediaPlaylistControl
{
- Q_OBJECT
public:
- MockMediaPlaylistControl(QObject *parent) : QMediaPlaylistControl(parent)
+ MockMediaPlaylistControl(bool readonly = false, QObject *parent = 0)
+ : QMediaPlaylistControl(parent)
+ , m_navigator(0)
+ , m_playlist(0)
+ , m_ownsProvider(false)
+ , m_readOnly(readonly)
{
- m_navigator = new QMediaPlaylistNavigator(new MockReadOnlyPlaylistProvider(this), this);
+ reset();
}
~MockMediaPlaylistControl()
{
}
- QMediaPlaylistProvider* playlistProvider() const { return m_navigator->playlist(); }
+ void reset()
+ {
+ delete m_navigator;
+ if (m_ownsProvider)
+ delete m_playlist;
+
+ if (m_readOnly)
+ m_playlist = new MockReadOnlyPlaylistProvider(this);
+ else
+ m_playlist = new QMediaNetworkPlaylistProvider(this);
+
+ m_ownsProvider = true;
+ m_navigator = new QMediaPlaylistNavigator(m_playlist, this);
+ }
+
+ void setReadOnly(bool ro)
+ {
+ if (m_readOnly == ro)
+ return;
+
+ m_readOnly = ro;
+ reset();
+ }
+
+ QMediaPlaylistProvider* playlistProvider() const { return m_playlist; }
bool setPlaylistProvider(QMediaPlaylistProvider *newProvider)
{
bool bMediaContentChanged = false;
@@ -70,6 +98,11 @@ public:
emit currentMediaChanged(newProvider->media(i));
}
+ if (m_ownsProvider)
+ delete m_playlist;
+ m_playlist = newProvider;
+ m_ownsProvider = false;
+
m_navigator->setPlaylist(newProvider);
return true;
}
@@ -99,6 +132,9 @@ public:
private:
QMediaPlaylistNavigator *m_navigator;
+ QMediaPlaylistProvider *m_playlist;
+ bool m_ownsProvider;
+ bool m_readOnly;
};
#endif // MOCKMEDIAPLAYLISTCONTROL_H
diff --git a/tests/auto/unit/qmultimedia_common/mockplaylistservice.h b/tests/auto/unit/qmultimedia_common/mockplaylistservice.h
index 95d95dbff..6c038abfe 100644
--- a/tests/auto/unit/qmultimedia_common/mockplaylistservice.h
+++ b/tests/auto/unit/qmultimedia_common/mockplaylistservice.h
@@ -44,7 +44,7 @@ class MockPlaylistService : public QMediaService
public:
MockPlaylistService():QMediaService(0)
{
- mockControl = new MockMediaPlaylistControl(this);
+ mockControl = new MockMediaPlaylistControl(false, this);
}
~MockPlaylistService()
diff --git a/tests/auto/unit/qmultimedia_common/mockreadonlyplaylistprovider.h b/tests/auto/unit/qmultimedia_common/mockreadonlyplaylistprovider.h
index 50747c1ad..39b1598b0 100644
--- a/tests/auto/unit/qmultimedia_common/mockreadonlyplaylistprovider.h
+++ b/tests/auto/unit/qmultimedia_common/mockreadonlyplaylistprovider.h
@@ -38,7 +38,6 @@
class MockReadOnlyPlaylistProvider : public QMediaPlaylistProvider
{
- Q_OBJECT
public:
MockReadOnlyPlaylistProvider(QObject *parent)
:QMediaPlaylistProvider(parent)