From 2463a5f216de1b1e71314414dc0d43214abe55db Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Thu, 7 May 2015 12:56:27 +0200 Subject: [PATCH 01/21] OSX/iOS: Fix volume and mute My last fix for iOS (4e07ff99) introduced this regression of passing the outdated value to the native player object. Change-Id: I01b0df8c7a0fe1382ef73b55d288a40daf024e3d Reviewed-by: James Turner Reviewed-by: Yoann Lopes --- src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm index 005c00a1..5ef1f9ba 100644 --- a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm @@ -728,7 +728,7 @@ void AVFMediaPlayerSession::setVolume(int volume) return; } - [player setVolume:m_volume / 100.0f]; + [player setVolume:volume / 100.0f]; m_volume = volume; Q_EMIT volumeChanged(m_volume); @@ -752,7 +752,7 @@ void AVFMediaPlayerSession::setMuted(bool muted) return; } - [player setMuted:m_muted]; + [player setMuted:muted]; m_muted = muted; Q_EMIT mutedChanged(muted); From ab860d8b7f58bb68f102dfb3b3ee0b6488c811a1 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Mon, 11 May 2015 18:33:05 +0200 Subject: [PATCH 02/21] Fix texture unit bug in the YUV video node. fe13f5bb introduced a bug which could leave the active texture unit set to GL_TEXTURE1 or GL_TEXTURE2. When the material is done updating its state, it needs to make sure the active texture unit is reset to 0 since other materials might assume that's the current value. Change-Id: I64a6a0cd9fcecdf0fa483909193f70045ff0e9fe Reviewed-by: Laszlo Agocs --- src/qtmultimediaquicktools/qsgvideonode_yuv.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qtmultimediaquicktools/qsgvideonode_yuv.cpp b/src/qtmultimediaquicktools/qsgvideonode_yuv.cpp index 9da1023b..c920ba3b 100644 --- a/src/qtmultimediaquicktools/qsgvideonode_yuv.cpp +++ b/src/qtmultimediaquicktools/qsgvideonode_yuv.cpp @@ -318,7 +318,8 @@ void QSGVideoMaterial_YUV::bind() m_frame = QVideoFrame(); } else { - for (int i = 0; i < m_planeCount; ++i) { + // Go backwards to finish with GL_TEXTURE0 + for (int i = m_planeCount - 1; i >= 0; --i) { functions->glActiveTexture(GL_TEXTURE0 + i); functions->glBindTexture(GL_TEXTURE_2D, m_textureIds[i]); } From f61c89a1b3e858614d7a6e856da3726c086402c9 Mon Sep 17 00:00:00 2001 From: Venugopal Shivashankar Date: Tue, 12 May 2015 15:12:52 +0200 Subject: [PATCH 03/21] Doc: Added a link to the wiki with plugin info. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The wiki summarizes each platform plugin and the supported features. Change-Id: Id7707306880d7e0612e73b879fc48201e0a97417 Task-number: QTBUG-30381 Reviewed-by: Leena Miettinen Reviewed-by: Yoann Lopes Reviewed-by: Topi Reiniƶ --- src/multimedia/doc/src/qtmultimedia-index.qdoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/multimedia/doc/src/qtmultimedia-index.qdoc b/src/multimedia/doc/src/qtmultimedia-index.qdoc index 39e7cbc4..6dc89429 100644 --- a/src/multimedia/doc/src/qtmultimedia-index.qdoc +++ b/src/multimedia/doc/src/qtmultimedia-index.qdoc @@ -130,6 +130,11 @@ \endlist \section2 Platform Notes + + The \l{Qt Multimedia Backends} wiki provides a summary of features + supported by each platform plugin made available by this module. The + following topics provide more platform-specific information. + \list \li \l{Qt Multimedia on BlackBerry}{BlackBerry} \li \l{Qt Multimedia on Windows}{Windows} From 695399c5f5d6790c6598e80c3e91b35f994f6dd7 Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Mon, 27 Apr 2015 01:06:32 +0200 Subject: [PATCH 04/21] Fix typo in QCameraViewfinderSettingsControl documentation Change-Id: Iae4b064f554f501bfce9bc7b811c56685e8ece0d Reviewed-by: Sze Howe Koh --- src/multimedia/controls/qcameraviewfindersettingscontrol.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/multimedia/controls/qcameraviewfindersettingscontrol.cpp b/src/multimedia/controls/qcameraviewfindersettingscontrol.cpp index 49bc81ab..95b48696 100644 --- a/src/multimedia/controls/qcameraviewfindersettingscontrol.cpp +++ b/src/multimedia/controls/qcameraviewfindersettingscontrol.cpp @@ -96,7 +96,7 @@ QCameraViewfinderSettingsControl::~QCameraViewfinderSettingsControl() Viewfinder pixel format, QVideoFrame::PixelFormat \value UserParameter The base value for platform specific extended parameters. - For such parameters the sequential values starting from UserParameter shuld be used. + For such parameters the sequential values starting from UserParameter should be used. */ /*! From 9faaf425e908c150ad2192949d1ec261e1a4328b Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Wed, 13 May 2015 17:20:34 +0200 Subject: [PATCH 05/21] Fix documentation for Camera flash mode enum. Added missing documentation for Camera.FlashVideoLight and corrected it for Camera.FlashTorch. Change-Id: Ib1823b6554bd2f9db92e047fd4755ae7fb3e0f96 Reviewed-by: Venugopal Shivashankar --- src/imports/multimedia/qdeclarativecameraflash.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/imports/multimedia/qdeclarativecameraflash.cpp b/src/imports/multimedia/qdeclarativecameraflash.cpp index 0bde737e..ae4fd939 100644 --- a/src/imports/multimedia/qdeclarativecameraflash.cpp +++ b/src/imports/multimedia/qdeclarativecameraflash.cpp @@ -113,7 +113,10 @@ bool QDeclarativeCameraFlash::isFlashReady() const \row \li Camera.FlashAuto \li Automatic flash. \row \li Camera.FlashRedEyeReduction \li Red eye reduction flash. \row \li Camera.FlashFill \li Use flash to fillin shadows. - \row \li Camera.FlashTorch \li Constant light source, useful for focusing and video capture. + \row \li Camera.FlashTorch \li Constant light source. If supported, torch can be + enabled without loading the camera. + \row \li Camera.FlashVideoLight \li Constant light source, useful for video capture. + The light is turned on only while the camera is active. \row \li Camera.FlashSlowSyncFrontCurtain \li Use the flash in conjunction with a slow shutter speed. This mode allows better exposure of distant objects and/or motion blur effect. From aaff15aec613fa2e2717a8d7b96e25e319a1fbc7 Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Mon, 18 May 2015 12:10:44 +0200 Subject: [PATCH 06/21] Doc: Use the screenshot in the Declarative Radio example documentation Also, crop the screenshot image to not show any window decoration, and fix the example \brief. Change-Id: I86310bdc5a4f124180eac1a1d27064286ba34f59 Reviewed-by: Venugopal Shivashankar --- .../doc/images/declarative-radio-example.png | Bin 16734 -> 12113 bytes .../doc/src/declarative-radio.qdoc | 4 +++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/multimedia/declarative-radio/doc/images/declarative-radio-example.png b/examples/multimedia/declarative-radio/doc/images/declarative-radio-example.png index 6c039349940e98ca8108b88f5deeb31693e1af92..5b29174c58df81650ef06fa9ecd1ba44c905d67c 100644 GIT binary patch literal 12113 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 literal 16734 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 diff --git a/examples/multimedia/declarative-radio/doc/src/declarative-radio.qdoc b/examples/multimedia/declarative-radio/doc/src/declarative-radio.qdoc index 42cebe2f..62687f03 100644 --- a/examples/multimedia/declarative-radio/doc/src/declarative-radio.qdoc +++ b/examples/multimedia/declarative-radio/doc/src/declarative-radio.qdoc @@ -28,11 +28,13 @@ /*! \example declarative-radio \title Declarative Radio Example - \brief Demonstrates the radio functionality + \brief Demonstrates using the Radio QML type from Qt Multimedia. \ingroup multimedia_examples This examples uses the \l{Qt Multimedia} \l Radio QML type to list the available channels on the FM frequency. + \image declarative-radio-example.png + \include examples-run.qdocinc */ From b0111ce5f59d813e01a5e6d3e22189343579ebbf Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Thu, 16 Apr 2015 11:30:08 +0200 Subject: [PATCH 07/21] WMF: fix start time of buffers provided by QAudioDecoder. The time was in milliseconds but should be in microseconds. Task-number: QTBUG-45571 Change-Id: I54f07975e7a6233254a338dcde8075f740b5455c Reviewed-by: Christian Stromme --- src/plugins/wmf/decoder/mfaudiodecodercontrol.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/wmf/decoder/mfaudiodecodercontrol.cpp b/src/plugins/wmf/decoder/mfaudiodecodercontrol.cpp index e3d1ebde..55fae714 100644 --- a/src/plugins/wmf/decoder/mfaudiodecodercontrol.cpp +++ b/src/plugins/wmf/decoder/mfaudiodecodercontrol.cpp @@ -389,7 +389,8 @@ void MFAudioDecoderControl::handleSampleAdded() s->Release(); } } - m_cachedAudioBuffer = QAudioBuffer(abuf, m_audioFormat, qint64(sampleStartTime / 10000)); + // WMF uses 100-nanosecond units, QAudioDecoder uses milliseconds, QAudioBuffer uses microseconds... + m_cachedAudioBuffer = QAudioBuffer(abuf, m_audioFormat, qint64(sampleStartTime / 10)); m_bufferReady = true; emit positionChanged(m_position); emit bufferAvailableChanged(m_bufferReady); From 0df8d83932a8cdc84bbf4dc7b31e2c1767a13844 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Tue, 19 May 2015 13:43:14 +0200 Subject: [PATCH 08/21] AVFoundation: fix compilation on OSX < 10.9. AVCaptureConnection.videoMaxFrameDuration is supported only since 10.9. Task-number: QTBUG-46159 Change-Id: I8ea57b69e97ea3802b5c444c57ab090c4edf31e2 Reviewed-by: Timur Pocheptsov --- .../avfoundation/camera/avfcamerautility.mm | 17 +++++--- .../avfcameraviewfindersettingscontrol.mm | 41 +++++++++++-------- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/plugins/avfoundation/camera/avfcamerautility.mm b/src/plugins/avfoundation/camera/avfcamerautility.mm index f8d5647e..f45169d0 100644 --- a/src/plugins/avfoundation/camera/avfcamerautility.mm +++ b/src/plugins/avfoundation/camera/avfcamerautility.mm @@ -59,13 +59,20 @@ AVFPSRange qt_connection_framerates(AVCaptureConnection *videoConnection) } } - if (videoConnection.supportsVideoMaxFrameDuration) { - const CMTime cmMax = videoConnection.videoMaxFrameDuration; - if (CMTimeCompare(cmMax, kCMTimeInvalid)) { - if (const Float64 maxSeconds = CMTimeGetSeconds(cmMax)) - newRange.first = 1. / maxSeconds; +#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_5_0) +#if QT_OSX_DEPLOYMENT_TARGET_BELOW(__MAC_10_9) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_9) +#endif + { + if (videoConnection.supportsVideoMaxFrameDuration) { + const CMTime cmMax = videoConnection.videoMaxFrameDuration; + if (CMTimeCompare(cmMax, kCMTimeInvalid)) { + if (const Float64 maxSeconds = CMTimeGetSeconds(cmMax)) + newRange.first = 1. / maxSeconds; + } } } +#endif return newRange; } diff --git a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm index 60df1e2e..ec572171 100644 --- a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm +++ b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm @@ -76,31 +76,40 @@ void qt_set_framerate_limits(AVCaptureConnection *videoConnection, return; } - const qreal minFPS = settings.minimumFrameRate(); const qreal maxFPS = settings.maximumFrameRate(); - CMTime minDuration = kCMTimeInvalid; - CMTime maxDuration = kCMTimeInvalid; - if (minFPS > 0. || maxFPS > 0.) { - if (maxFPS) { - if (!videoConnection.supportsVideoMinFrameDuration) - qDebugCamera() << Q_FUNC_INFO << "maximum framerate is not supported"; - else - minDuration = CMTimeMake(1, maxFPS); - } + if (maxFPS > 0.) { + if (!videoConnection.supportsVideoMinFrameDuration) + qDebugCamera() << Q_FUNC_INFO << "maximum framerate is not supported"; + else + minDuration = CMTimeMake(1, maxFPS); + } + if (videoConnection.supportsVideoMinFrameDuration) + videoConnection.videoMinFrameDuration = minDuration; - if (minFPS) { + const qreal minFPS = settings.minimumFrameRate(); +#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_5_0) +#if QT_OSX_DEPLOYMENT_TARGET_BELOW(__MAC_10_9) + if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_9) { + if (minFPS > 0.) + qDebugCamera() << Q_FUNC_INFO << "minimum framerate is not supported"; + } else +#endif + { + CMTime maxDuration = kCMTimeInvalid; + if (minFPS > 0.) { if (!videoConnection.supportsVideoMaxFrameDuration) qDebugCamera() << Q_FUNC_INFO << "minimum framerate is not supported"; else maxDuration = CMTimeMake(1, minFPS); } + if (videoConnection.supportsVideoMaxFrameDuration) + videoConnection.videoMaxFrameDuration = maxDuration; } - - if (videoConnection.supportsVideoMinFrameDuration) - videoConnection.videoMinFrameDuration = minDuration; - if (videoConnection.supportsVideoMaxFrameDuration) - videoConnection.videoMaxFrameDuration = maxDuration; +#else + if (minFPS > 0.) + qDebugCamera() << Q_FUNC_INFO << "minimum framerate is not supported"; +#endif } #if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0) From 5fab28b58128f6424eca0f63147f384fd916704f Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Tue, 19 May 2015 16:54:05 +0200 Subject: [PATCH 09/21] AVFoundation: fix retrieving maximum camera zoom value. Maximum zoom value was incorrectly reset to 1.0 when doing the transition Active -> Loaded -> Active. Change-Id: I799900b1597637039d6c28d1d694fb6340b10540 Reviewed-by: Timur Pocheptsov --- .../avfoundation/camera/avfcamerazoomcontrol.mm | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/avfoundation/camera/avfcamerazoomcontrol.mm b/src/plugins/avfoundation/camera/avfcamerazoomcontrol.mm index 8206112b..158073dc 100644 --- a/src/plugins/avfoundation/camera/avfcamerazoomcontrol.mm +++ b/src/plugins/avfoundation/camera/avfcamerazoomcontrol.mm @@ -128,11 +128,11 @@ void AVFCameraZoomControl::cameraStateChanged() return; } - if (captureDevice.activeFormat.videoMaxZoomFactor > 1. - && !qFuzzyCompare(m_maxZoomFactor, captureDevice.activeFormat.videoMaxZoomFactor)) { - m_maxZoomFactor = captureDevice.activeFormat.videoMaxZoomFactor; - - Q_EMIT maximumDigitalZoomChanged(m_maxZoomFactor); + if (captureDevice.activeFormat.videoMaxZoomFactor > 1.) { + if (!qFuzzyCompare(m_maxZoomFactor, captureDevice.activeFormat.videoMaxZoomFactor)) { + m_maxZoomFactor = captureDevice.activeFormat.videoMaxZoomFactor; + Q_EMIT maximumDigitalZoomChanged(m_maxZoomFactor); + } } else if (!qFuzzyCompare(m_maxZoomFactor, CGFloat(1.))) { m_maxZoomFactor = 1.; From 4be995d251c63a60c6f85813636c85a721ee84e8 Mon Sep 17 00:00:00 2001 From: Andras Mantia Date: Fri, 15 May 2015 10:49:50 +0300 Subject: [PATCH 10/21] Initialize the resource, so it can be used from static libraries. Change-Id: If28509a991fb464dc587705ce62af66b528fdd97 Reviewed-by: Yoann Lopes --- src/qtmultimediaquicktools/qdeclarativevideooutput.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/qtmultimediaquicktools/qdeclarativevideooutput.cpp b/src/qtmultimediaquicktools/qdeclarativevideooutput.cpp index c638b306..7a9dfdaa 100644 --- a/src/qtmultimediaquicktools/qdeclarativevideooutput.cpp +++ b/src/qtmultimediaquicktools/qdeclarativevideooutput.cpp @@ -41,6 +41,10 @@ #include #include +static void initResource() { + Q_INIT_RESOURCE(qtmultimediaquicktools); +} + QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(qLcVideo, "qt.multimedia.video") @@ -130,6 +134,7 @@ QDeclarativeVideoOutput::QDeclarativeVideoOutput(QQuickItem *parent) : m_autoOrientation(false), m_screenOrientationHandler(0) { + initResource(); setFlag(ItemHasContents, true); } From 10b3466ce43dc650d1abc46ffe2cbe81905547bb Mon Sep 17 00:00:00 2001 From: Jani Heikkinen Date: Wed, 20 May 2015 14:58:28 +0300 Subject: [PATCH 11/21] Updated WinRT license headers to use LGPLv3 instead of LGPLv21 From 5.5.0 -> WinRT port is licensed with LGPLv3, see http://blog.qt.io/blog/2015/04/29/windows-10-support-in-qt/ Change-Id: I86e43ba051e3bc1dfb3eb9e1d442a9a12e9efdb7 Reviewed-by: Maurice Kalinowski --- .../qwinrtabstractvideorenderercontrol.cpp | 23 +++++++++++-------- .../qwinrtabstractvideorenderercontrol.h | 23 +++++++++++-------- src/plugins/winrt/qwinrtcameracontrol.cpp | 23 +++++++++++-------- src/plugins/winrt/qwinrtcameracontrol.h | 23 +++++++++++-------- .../winrt/qwinrtcameraimagecapturecontrol.cpp | 23 +++++++++++-------- .../winrt/qwinrtcameraimagecapturecontrol.h | 23 +++++++++++-------- src/plugins/winrt/qwinrtcamerainfocontrol.cpp | 23 +++++++++++-------- src/plugins/winrt/qwinrtcamerainfocontrol.h | 23 +++++++++++-------- src/plugins/winrt/qwinrtcameraservice.cpp | 23 +++++++++++-------- src/plugins/winrt/qwinrtcameraservice.h | 23 +++++++++++-------- .../qwinrtcameravideorenderercontrol.cpp | 23 +++++++++++-------- .../winrt/qwinrtcameravideorenderercontrol.h | 23 +++++++++++-------- .../winrt/qwinrtmediaplayercontrol.cpp | 23 +++++++++++-------- src/plugins/winrt/qwinrtmediaplayercontrol.h | 23 +++++++++++-------- .../winrt/qwinrtmediaplayerservice.cpp | 23 +++++++++++-------- src/plugins/winrt/qwinrtmediaplayerservice.h | 23 +++++++++++-------- .../winrt/qwinrtplayerrenderercontrol.cpp | 23 +++++++++++-------- .../winrt/qwinrtplayerrenderercontrol.h | 23 +++++++++++-------- src/plugins/winrt/qwinrtserviceplugin.cpp | 23 +++++++++++-------- src/plugins/winrt/qwinrtserviceplugin.h | 23 +++++++++++-------- .../qwinrtvideodeviceselectorcontrol.cpp | 23 +++++++++++-------- .../winrt/qwinrtvideodeviceselectorcontrol.h | 23 +++++++++++-------- 22 files changed, 286 insertions(+), 220 deletions(-) diff --git a/src/plugins/winrt/qwinrtabstractvideorenderercontrol.cpp b/src/plugins/winrt/qwinrtabstractvideorenderercontrol.cpp index 0f46e585..2d0b5413 100644 --- a/src/plugins/winrt/qwinrtabstractvideorenderercontrol.cpp +++ b/src/plugins/winrt/qwinrtabstractvideorenderercontrol.cpp @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtabstractvideorenderercontrol.h b/src/plugins/winrt/qwinrtabstractvideorenderercontrol.h index 278ec179..ed8b76a6 100644 --- a/src/plugins/winrt/qwinrtabstractvideorenderercontrol.h +++ b/src/plugins/winrt/qwinrtabstractvideorenderercontrol.h @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtcameracontrol.cpp b/src/plugins/winrt/qwinrtcameracontrol.cpp index e8f433d4..e4a57764 100644 --- a/src/plugins/winrt/qwinrtcameracontrol.cpp +++ b/src/plugins/winrt/qwinrtcameracontrol.cpp @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtcameracontrol.h b/src/plugins/winrt/qwinrtcameracontrol.h index a90d482e..95692cbc 100644 --- a/src/plugins/winrt/qwinrtcameracontrol.h +++ b/src/plugins/winrt/qwinrtcameracontrol.h @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtcameraimagecapturecontrol.cpp b/src/plugins/winrt/qwinrtcameraimagecapturecontrol.cpp index 1e6050a3..104484a6 100644 --- a/src/plugins/winrt/qwinrtcameraimagecapturecontrol.cpp +++ b/src/plugins/winrt/qwinrtcameraimagecapturecontrol.cpp @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtcameraimagecapturecontrol.h b/src/plugins/winrt/qwinrtcameraimagecapturecontrol.h index 38dbbf2c..dc880255 100644 --- a/src/plugins/winrt/qwinrtcameraimagecapturecontrol.h +++ b/src/plugins/winrt/qwinrtcameraimagecapturecontrol.h @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtcamerainfocontrol.cpp b/src/plugins/winrt/qwinrtcamerainfocontrol.cpp index 0a741eaa..69fc0a9a 100644 --- a/src/plugins/winrt/qwinrtcamerainfocontrol.cpp +++ b/src/plugins/winrt/qwinrtcamerainfocontrol.cpp @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtcamerainfocontrol.h b/src/plugins/winrt/qwinrtcamerainfocontrol.h index 7020b12d..5bad6cb0 100644 --- a/src/plugins/winrt/qwinrtcamerainfocontrol.h +++ b/src/plugins/winrt/qwinrtcamerainfocontrol.h @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtcameraservice.cpp b/src/plugins/winrt/qwinrtcameraservice.cpp index 275fbccf..09b56575 100644 --- a/src/plugins/winrt/qwinrtcameraservice.cpp +++ b/src/plugins/winrt/qwinrtcameraservice.cpp @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtcameraservice.h b/src/plugins/winrt/qwinrtcameraservice.h index 9565de62..eb3b0d01 100644 --- a/src/plugins/winrt/qwinrtcameraservice.h +++ b/src/plugins/winrt/qwinrtcameraservice.h @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp b/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp index bcae8a20..3b8a38bb 100644 --- a/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp +++ b/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtcameravideorenderercontrol.h b/src/plugins/winrt/qwinrtcameravideorenderercontrol.h index 55ea1266..68e9bd9f 100644 --- a/src/plugins/winrt/qwinrtcameravideorenderercontrol.h +++ b/src/plugins/winrt/qwinrtcameravideorenderercontrol.h @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtmediaplayercontrol.cpp b/src/plugins/winrt/qwinrtmediaplayercontrol.cpp index 333a6d1e..0402553d 100644 --- a/src/plugins/winrt/qwinrtmediaplayercontrol.cpp +++ b/src/plugins/winrt/qwinrtmediaplayercontrol.cpp @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtmediaplayercontrol.h b/src/plugins/winrt/qwinrtmediaplayercontrol.h index f2899531..34abcc12 100644 --- a/src/plugins/winrt/qwinrtmediaplayercontrol.h +++ b/src/plugins/winrt/qwinrtmediaplayercontrol.h @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtmediaplayerservice.cpp b/src/plugins/winrt/qwinrtmediaplayerservice.cpp index c8c34d83..a6b3d1f3 100644 --- a/src/plugins/winrt/qwinrtmediaplayerservice.cpp +++ b/src/plugins/winrt/qwinrtmediaplayerservice.cpp @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtmediaplayerservice.h b/src/plugins/winrt/qwinrtmediaplayerservice.h index 039ca309..7a2771fd 100644 --- a/src/plugins/winrt/qwinrtmediaplayerservice.h +++ b/src/plugins/winrt/qwinrtmediaplayerservice.h @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtplayerrenderercontrol.cpp b/src/plugins/winrt/qwinrtplayerrenderercontrol.cpp index cf815fcd..5a8f4b87 100644 --- a/src/plugins/winrt/qwinrtplayerrenderercontrol.cpp +++ b/src/plugins/winrt/qwinrtplayerrenderercontrol.cpp @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtplayerrenderercontrol.h b/src/plugins/winrt/qwinrtplayerrenderercontrol.h index 85fc00c5..c6d410b1 100644 --- a/src/plugins/winrt/qwinrtplayerrenderercontrol.h +++ b/src/plugins/winrt/qwinrtplayerrenderercontrol.h @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtserviceplugin.cpp b/src/plugins/winrt/qwinrtserviceplugin.cpp index c0e51907..fc0103d1 100644 --- a/src/plugins/winrt/qwinrtserviceplugin.cpp +++ b/src/plugins/winrt/qwinrtserviceplugin.cpp @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtserviceplugin.h b/src/plugins/winrt/qwinrtserviceplugin.h index 004ad89c..5f5cf559 100644 --- a/src/plugins/winrt/qwinrtserviceplugin.h +++ b/src/plugins/winrt/qwinrtserviceplugin.h @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.cpp b/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.cpp index 0352aaab..582a9fc6 100644 --- a/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.cpp +++ b/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.cpp @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.h b/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.h index df655f44..3b3e7c74 100644 --- a/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.h +++ b/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.h @@ -5,7 +5,7 @@ ** ** This file is part of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -16,16 +16,19 @@ ** ** 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. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.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. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** From a3f82a8995543288dd004349bbe94beece170d6a Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Tue, 19 May 2015 15:50:10 +0200 Subject: [PATCH 12/21] GStreamer: don't use xvimagesink when Qt is not using Xcb. We need a native X window handle to be able to use xvimagesink. If Qt doesn't use the Xcb platform plugin, don't try to use that element. This patch makes QVideoWidget usable when the system doesn't run X11 but xvimagesink is installed anyway. Widget and window controls become invalid and the plugin therefore falls back to the renderer control. Change-Id: I6efd410508f35b06f00f6e4e5149c97cbd280d0d Task-number: QTBUG-46169 Reviewed-by: Laszlo Agocs --- src/gsttools/qgstreamervideowidget.cpp | 5 ++++- src/gsttools/qgstreamervideowindow.cpp | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/gsttools/qgstreamervideowidget.cpp b/src/gsttools/qgstreamervideowidget.cpp index 9a29d327..6e65b306 100644 --- a/src/gsttools/qgstreamervideowidget.cpp +++ b/src/gsttools/qgstreamervideowidget.cpp @@ -98,7 +98,10 @@ QGstreamerVideoWidgetControl::QGstreamerVideoWidgetControl(QObject *parent) , m_widget(0) , m_fullScreen(false) { - m_videoSink = gst_element_factory_make ("xvimagesink", NULL); + // The QWidget needs to have a native X window handle to be able to use xvimagesink. + // Bail out if Qt is not using xcb (the control will then be ignored by the plugin) + if (QGuiApplication::platformName().compare(QLatin1String("xcb"), Qt::CaseInsensitive) == 0) + m_videoSink = gst_element_factory_make ("xvimagesink", NULL); if (m_videoSink) { // Check if the xv sink is usable diff --git a/src/gsttools/qgstreamervideowindow.cpp b/src/gsttools/qgstreamervideowindow.cpp index 7dd6ab25..a0697003 100644 --- a/src/gsttools/qgstreamervideowindow.cpp +++ b/src/gsttools/qgstreamervideowindow.cpp @@ -35,6 +35,7 @@ #include #include +#include #include @@ -57,7 +58,9 @@ QGstreamerVideoWindow::QGstreamerVideoWindow(QObject *parent, const char *elemen { if (elementName) { m_videoSink = gst_element_factory_make(elementName, NULL); - } else { + } else if (QGuiApplication::platformName().compare(QLatin1String("xcb"), Qt::CaseInsensitive) == 0) { + // We need a native X window handle to be able to use xvimagesink. + // Bail out if Qt is not using xcb (the control will then be ignored by the plugin) m_videoSink = gst_element_factory_make("xvimagesink", NULL); } From 288d54947406caa9b9d6326a88a7dc8afc64ee04 Mon Sep 17 00:00:00 2001 From: Carlos Rafael Giani Date: Tue, 10 Mar 2015 00:07:40 +0100 Subject: [PATCH 13/21] videonode: imx6: Ensure the video node excludes padding pixels Padding rows & columns are included in the video frame that is sent to the VIV direct texture. Scale the UV coordinates to ensure the padding pixels aren't shown. Change-Id: I8b870a95ff786f9f80f42f0cc33f468b0c9c3863 Reviewed-by: Laszlo Agocs Reviewed-by: Yoann Lopes --- .../imx6/qsgvivantevideomaterial.cpp | 39 +++++++++++++++++-- .../videonode/imx6/qsgvivantevideomaterial.h | 4 ++ .../imx6/qsgvivantevideomaterialshader.cpp | 22 ++++++++++- .../imx6/qsgvivantevideomaterialshader.h | 9 +++++ .../videonode/imx6/qsgvivantevideonode.cpp | 19 +++++++++ .../videonode/imx6/qsgvivantevideonode.h | 1 + 6 files changed, 90 insertions(+), 4 deletions(-) diff --git a/src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp b/src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp index ab6bcd52..2e2092be 100644 --- a/src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp +++ b/src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp @@ -60,6 +60,8 @@ QSGVivanteVideoMaterial::QSGVivanteVideoMaterial() : #endif setFlag(Blending, false); + + mShader = new QSGVivanteVideoMaterialShader; } QSGVivanteVideoMaterial::~QSGVivanteVideoMaterial() @@ -73,7 +75,7 @@ QSGMaterialType *QSGVivanteVideoMaterial::type() const { } QSGMaterialShader *QSGVivanteVideoMaterial::createShader() const { - return new QSGVivanteVideoMaterialShader; + return mShader; } int QSGVivanteVideoMaterial::compare(const QSGMaterial *other) const { @@ -175,18 +177,49 @@ GLuint QSGVivanteVideoMaterial::vivanteMapping(QVideoFrame vF) glGenTextures(1, &tmpTexId); mBitsToTextureMap.insert(vF.bits(), tmpTexId); + // Determine the full width & height. Full means: actual width/height plus extra padding pixels. + // The full width can be deduced from the bytesPerLine value. The full height is calculated + // by calculating the distance between the start of the first and second planes, and dividing + // it by the stride (= the bytesPerLine). If there is only one plane, we don't worry about + // extra padding rows, since there are no adjacent extra planes. + // XXX: This assumes the distance between bits(1) and bits(0) is exactly the size of the first + // plane (the Y plane in the case of YUV data). A better way would be to have a dedicated + // planeSize() or planeOffset() getter. + // Also, this assumes that planes are tightly packed, that is, there is no space between them. + // It is okay to assume this here though, because the Vivante direct textures also assume that. + // In other words, if the planes aren't tightly packed, then the direct textures won't be able + // to render the frame correctly anyway. + int fullWidth = vF.bytesPerLine() / QSGVivanteVideoNode::getBytesForPixelFormat(vF.pixelFormat()); + int fullHeight = (vF.planeCount() > 1) ? ((vF.bits(1) - vF.bits(0)) / vF.bytesPerLine()) : vF.height(); + + // The uscale is the ratio of actual width to the full width (same for vscale and height). + // Since the vivante direct textures do not offer a way to explicitly specify the amount of padding + // columns and rows, we use a trick. We show the full frame - including the padding pixels - in the + // texture, but render only a subset of that texture. This subset goes from (0,0) to (uScale, vScale). + // In the shader, the texture coordinates (which go from (0.0, 0.0) to (1.0, 1.0)) are multiplied by + // the u/v scale values. Since 1.0 * x = x, this effectively limits the texture coordinates from + // (0.0, 0.0) - (1.0, 1.0) to (0.0, 0.0) - (uScale, vScale). + float uScale = float(vF.width()) / float(fullWidth); + float vScale = float(vF.height()) / float(fullHeight); + mShader->setUVScale(uScale, vScale); + const uchar *constBits = vF.bits(); void *bits = (void*)constBits; #ifdef QT_VIVANTE_VIDEO_DEBUG - qDebug() << Q_FUNC_INFO << "new texture, texId: " << tmpTexId << "; constBits: " << constBits; + qDebug() << Q_FUNC_INFO + << "new texture, texId: " << tmpTexId + << "; constBits: " << constBits + << "; actual/full width: " << vF.width() << "/" << fullWidth + << "; actual/full height: " << vF.height() << "/" << fullHeight + << "; UV scale: U " << uScale << " V " << vScale; #endif GLuint physical = ~0U; glBindTexture(GL_TEXTURE_2D, tmpTexId); glTexDirectVIVMap_LOCAL(GL_TEXTURE_2D, - vF.width(), vF.height(), + fullWidth, fullHeight, QSGVivanteVideoNode::getVideoFormat2GLFormatMap().value(vF.pixelFormat()), &bits, &physical); diff --git a/src/plugins/videonode/imx6/qsgvivantevideomaterial.h b/src/plugins/videonode/imx6/qsgvivantevideomaterial.h index faefa1c9..faf4e8d2 100644 --- a/src/plugins/videonode/imx6/qsgvivantevideomaterial.h +++ b/src/plugins/videonode/imx6/qsgvivantevideomaterial.h @@ -43,6 +43,8 @@ #include +class QSGVivanteVideoMaterialShader; + class QSGVivanteVideoMaterial : public QSGMaterial { public: @@ -78,6 +80,8 @@ private: GLuint mTexDirectTexture; GLvoid *mTexDirectPlanes[3]; + + QSGVivanteVideoMaterialShader *mShader; }; #endif // QSGVIDEOMATERIAL_VIVMAP_H diff --git a/src/plugins/videonode/imx6/qsgvivantevideomaterialshader.cpp b/src/plugins/videonode/imx6/qsgvivantevideomaterialshader.cpp index b51acfe6..a0a41773 100644 --- a/src/plugins/videonode/imx6/qsgvivantevideomaterialshader.cpp +++ b/src/plugins/videonode/imx6/qsgvivantevideomaterialshader.cpp @@ -35,6 +35,13 @@ #include "qsgvivantevideonode.h" #include "qsgvivantevideomaterial.h" +QSGVivanteVideoMaterialShader::QSGVivanteVideoMaterialShader() : + mUScale(1), + mVScale(1), + mNewUVScale(true) +{ +} + void QSGVivanteVideoMaterialShader::updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) @@ -48,6 +55,10 @@ void QSGVivanteVideoMaterialShader::updateState(const RenderState &state, mat->setOpacity(state.opacity()); program()->setUniformValue(mIdOpacity, state.opacity()); } + if (mNewUVScale) { + program()->setUniformValue(mIdUVScale, mUScale, mVScale); + mNewUVScale = false; + } if (state.isMatrixDirty()) program()->setUniformValue(mIdMatrix, state.combinedMatrix()); } @@ -61,6 +72,13 @@ const char * const *QSGVivanteVideoMaterialShader::attributeNames() const { return names; } +void QSGVivanteVideoMaterialShader::setUVScale(float uScale, float vScale) +{ + mUScale = uScale; + mVScale = vScale; + mNewUVScale = true; +} + const char *QSGVivanteVideoMaterialShader::vertexShader() const { static const char *shader = "uniform highp mat4 qt_Matrix; \n" @@ -78,12 +96,13 @@ const char *QSGVivanteVideoMaterialShader::fragmentShader() const { static const char *shader = "uniform sampler2D texture;" "uniform lowp float opacity;" + "uniform highp vec2 uvScale;" "" "varying highp vec2 qt_TexCoord;" "" "void main()" "{" - " gl_FragColor = vec4(texture2D( texture, qt_TexCoord ).rgb, 1.0) * opacity;\n" + " gl_FragColor = vec4(texture2D( texture, qt_TexCoord * uvScale ).rgb, 1.0) * opacity;\n" "}"; return shader; } @@ -93,4 +112,5 @@ void QSGVivanteVideoMaterialShader::initialize() { mIdMatrix = program()->uniformLocation("qt_Matrix"); mIdTexture = program()->uniformLocation("texture"); mIdOpacity = program()->uniformLocation("opacity"); + mIdUVScale = program()->uniformLocation("uvScale"); } diff --git a/src/plugins/videonode/imx6/qsgvivantevideomaterialshader.h b/src/plugins/videonode/imx6/qsgvivantevideomaterialshader.h index f902f763..f6e707da 100644 --- a/src/plugins/videonode/imx6/qsgvivantevideomaterialshader.h +++ b/src/plugins/videonode/imx6/qsgvivantevideomaterialshader.h @@ -39,9 +39,13 @@ class QSGVivanteVideoMaterialShader : public QSGMaterialShader { public: + QSGVivanteVideoMaterialShader(); + void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial); virtual char const *const *attributeNames() const; + void setUVScale(float uScale, float vScale); + protected: virtual const char *vertexShader() const; virtual const char *fragmentShader() const; @@ -51,6 +55,11 @@ private: int mIdMatrix; int mIdTexture; int mIdOpacity; + int mIdUVScale; + + float mUScale; + float mVScale; + bool mNewUVScale; }; #endif // QSGVIDEOMATERIALSHADER_VIVANTE_H diff --git a/src/plugins/videonode/imx6/qsgvivantevideonode.cpp b/src/plugins/videonode/imx6/qsgvivantevideonode.cpp index 42058f1b..229a69bc 100644 --- a/src/plugins/videonode/imx6/qsgvivantevideonode.cpp +++ b/src/plugins/videonode/imx6/qsgvivantevideonode.cpp @@ -78,4 +78,23 @@ const QMap& QSGVivanteVideoNode::getVideoForma } +int QSGVivanteVideoNode::getBytesForPixelFormat(QVideoFrame::PixelFormat pixelformat) +{ + switch (pixelformat) { + case QVideoFrame::Format_YUV420P: return 1; + case QVideoFrame::Format_YV12: return 1; + case QVideoFrame::Format_NV12: return 1; + case QVideoFrame::Format_NV21: return 1; + case QVideoFrame::Format_UYVY: return 2; + case QVideoFrame::Format_YUYV: return 2; + case QVideoFrame::Format_RGB32: return 4; + case QVideoFrame::Format_ARGB32: return 4; + case QVideoFrame::Format_BGR32: return 4; + case QVideoFrame::Format_BGRA32: return 4; + case QVideoFrame::Format_RGB565: return 2; + default: return 1; + } +} + + diff --git a/src/plugins/videonode/imx6/qsgvivantevideonode.h b/src/plugins/videonode/imx6/qsgvivantevideonode.h index ca088e21..c65fc11e 100644 --- a/src/plugins/videonode/imx6/qsgvivantevideonode.h +++ b/src/plugins/videonode/imx6/qsgvivantevideonode.h @@ -49,6 +49,7 @@ public: void setCurrentFrame(const QVideoFrame &frame, FrameFlags flags); static const QMap& getVideoFormat2GLFormatMap(); + static int getBytesForPixelFormat(QVideoFrame::PixelFormat pixelformat); private: QVideoSurfaceFormat mFormat; From a26df92bac147b5d9fa24caf95952010d3a76f2f Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 22 May 2015 10:05:33 +0200 Subject: [PATCH 14/21] Add CONFIG += testcase to auto/unit/qaudiobuffer/qaudiobuffer.pro. When missing, make check is an empty target, meaning the test is never run in the CI. Change-Id: I6f09e0f78c26b631fdfe89d2520dc0012d177843 Reviewed-by: Yoann Lopes --- tests/auto/unit/qaudiobuffer/qaudiobuffer.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/auto/unit/qaudiobuffer/qaudiobuffer.pro b/tests/auto/unit/qaudiobuffer/qaudiobuffer.pro index 0694bf82..cd3b42f5 100644 --- a/tests/auto/unit/qaudiobuffer/qaudiobuffer.pro +++ b/tests/auto/unit/qaudiobuffer/qaudiobuffer.pro @@ -8,7 +8,7 @@ QT += multimedia testlib QT -= gui TARGET = tst_qaudiobuffer -CONFIG += console +CONFIG += testcase CONFIG -= app_bundle TEMPLATE = app From 008d57e1d7c8208d5ac415db5a4bdd558688d638 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Thu, 21 May 2015 13:33:19 +0200 Subject: [PATCH 15/21] AVFoundation: additional build fixes for OSX < 10.9. 0df8d839 didn't fix all of the problems. Task-number: QTBUG-46159 Change-Id: I4f2a390da1cca13775302aeef60c522e0da55a1a Reviewed-by: Timur Pocheptsov --- .../avfcameraviewfindersettingscontrol.mm | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm index ec572171..e71e6b16 100644 --- a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm +++ b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm @@ -180,12 +180,21 @@ void qt_set_framerate_limits(AVCaptureDevice *captureDevice, #ifdef Q_OS_IOS [captureDevice setActiveVideoMinFrameDuration:minFrameDuration]; [captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration]; -#else +#else // Q_OS_OSX + if (CMTimeCompare(minFrameDuration, kCMTimeInvalid)) [captureDevice setActiveVideoMinFrameDuration:minFrameDuration]; - if (CMTimeCompare(maxFrameDuration, kCMTimeInvalid)) - [captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration]; + +#if QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9) +#if QT_OSX_DEPLOYMENT_TARGET_BELOW(__MAC_10_9) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_9) #endif + { + if (CMTimeCompare(maxFrameDuration, kCMTimeInvalid)) + [captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration]; + } +#endif // QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9) +#endif // Q_OS_OSX } #endif // Platform SDK >= 10.9, >= 7.0. @@ -206,15 +215,23 @@ AVFPSRange qt_current_framerates(AVCaptureDevice *captureDevice, AVCaptureConnec fps.second = 1. / minSeconds; // Max FPS = 1 / MinDuration. } - const CMTime maxDuration = captureDevice.activeVideoMaxFrameDuration; - if (CMTimeCompare(maxDuration, kCMTimeInvalid)) { - if (const Float64 maxSeconds = CMTimeGetSeconds(maxDuration)) - fps.first = 1. / maxSeconds; // Min FPS = 1 / MaxDuration. - } - } else { -#else - { +#if QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9) +#if QT_OSX_DEPLOYMENT_TARGET_BELOW(__MAC_10_9) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_9) #endif + { + const CMTime maxDuration = captureDevice.activeVideoMaxFrameDuration; + if (CMTimeCompare(maxDuration, kCMTimeInvalid)) { + if (const Float64 maxSeconds = CMTimeGetSeconds(maxDuration)) + fps.first = 1. / maxSeconds; // Min FPS = 1 / MaxDuration. + } + } +#endif // QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9) + + } else { +#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); } From 83d1255080ab2be678efbbc42e259c9681619619 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Tue, 5 May 2015 16:40:05 +0200 Subject: [PATCH 16/21] Android: fix setting the camera preview resolution. - When the video capture resolution or the image capture resolution changes, we now always set the viewfinder resolution to the highest available one with the same aspect ratio as the capture resolution. We were previously not doing anything if the new capture resolution had the same aspect ratio as the current viewfinder resolution. - Some devices don't support using a viewfinder resolution different from the video capture resolution. Make sure we handle this case. Change-Id: I8d3ab7b01c56ed78d1ca838a522ba459692fc332 Reviewed-by: Christian Stromme --- .../mediacapture/qandroidcamerasession.cpp | 38 ++++++++++--------- .../mediacapture/qandroidcapturesession.cpp | 10 ----- .../src/mediacapture/qandroidcapturesession.h | 1 - .../src/wrappers/jni/androidcamera.cpp | 3 ++ 4 files changed, 23 insertions(+), 29 deletions(-) diff --git a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp index 2ff17592..4a64e1b1 100644 --- a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp +++ b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp @@ -277,26 +277,28 @@ void QAndroidCameraSession::adjustViewfinderSize(const QSize &captureSize, bool return; QSize currentViewfinderResolution = m_camera->previewSize(); - const qreal aspectRatio = qreal(captureSize.width()) / qreal(captureSize.height()); - if (currentViewfinderResolution.isValid() && - qAbs(aspectRatio - (qreal(currentViewfinderResolution.width()) / currentViewfinderResolution.height())) < 0.01) { - return; - } - QSize adjustedViewfinderResolution; - QList previewSizes = m_camera->getSupportedPreviewSizes(); - for (int i = previewSizes.count() - 1; i >= 0; --i) { - const QSize &size = previewSizes.at(i); - // search for viewfinder resolution with the same aspect ratio - if (qAbs(aspectRatio - (qreal(size.width()) / size.height())) < 0.01) { - adjustedViewfinderResolution = size; - break; - } - } - if (!adjustedViewfinderResolution.isValid()) { - qWarning("Cannot find a viewfinder resolution matching the capture aspect ratio."); - return; + if (m_captureMode.testFlag(QCamera::CaptureVideo) && m_camera->getPreferredPreviewSizeForVideo().isEmpty()) { + // According to the Android doc, if getPreferredPreviewSizeForVideo() returns null, it means + // the preview size cannot be different from the capture size + adjustedViewfinderResolution = captureSize; + } else { + // search for viewfinder resolution with the same aspect ratio + const qreal aspectRatio = qreal(captureSize.width()) / qreal(captureSize.height()); + QList previewSizes = m_camera->getSupportedPreviewSizes(); + for (int i = previewSizes.count() - 1; i >= 0; --i) { + const QSize &size = previewSizes.at(i); + if (qAbs(aspectRatio - (qreal(size.width()) / size.height())) < 0.01) { + adjustedViewfinderResolution = size; + break; + } + } + + if (!adjustedViewfinderResolution.isValid()) { + qWarning("Cannot find a viewfinder resolution matching the capture aspect ratio."); + return; + } } if (currentViewfinderResolution != adjustedViewfinderResolution) { diff --git a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp index af803302..aaad8fd8 100644 --- a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp +++ b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp @@ -48,7 +48,6 @@ QAndroidCaptureSession::QAndroidCaptureSession(QAndroidCameraSession *cameraSess , m_duration(0) , m_state(QMediaRecorder::StoppedState) , m_status(QMediaRecorder::UnloadedStatus) - , m_resolutionDirty(false) , m_containerFormatDirty(true) , m_videoSettingsDirty(true) , m_audioSettingsDirty(true) @@ -321,9 +320,6 @@ void QAndroidCaptureSession::setVideoSettings(const QVideoEncoderSettings &setti if (!m_cameraSession || m_videoSettings == settings) return; - if (m_videoSettings.resolution() != settings.resolution()) - m_resolutionDirty = true; - m_videoSettings = settings; m_videoSettingsDirty = true; } @@ -376,7 +372,6 @@ void QAndroidCaptureSession::applySettings() if (m_cameraSession && m_cameraSession->camera() && m_videoSettingsDirty) { if (m_videoSettings.resolution().isEmpty()) { m_videoSettings.setResolution(m_defaultSettings.videoResolution); - m_resolutionDirty = true; } else if (!m_supportedResolutions.contains(m_videoSettings.resolution())) { // if the requested resolution is not supported, find the closest one QSize reqSize = m_videoSettings.resolution(); @@ -388,7 +383,6 @@ void QAndroidCaptureSession::applySettings() } int closestIndex = qt_findClosestValue(supportedPixelCounts, reqPixelCount); m_videoSettings.setResolution(m_supportedResolutions.at(closestIndex)); - m_resolutionDirty = true; } if (m_videoSettings.frameRate() <= 0) @@ -413,12 +407,8 @@ void QAndroidCaptureSession::applySettings() void QAndroidCaptureSession::updateViewfinder() { - if (!m_resolutionDirty) - return; - m_cameraSession->camera()->stopPreview(); m_cameraSession->adjustViewfinderSize(m_videoSettings.resolution(), false); - m_resolutionDirty = false; } void QAndroidCaptureSession::restartViewfinder() diff --git a/src/plugins/android/src/mediacapture/qandroidcapturesession.h b/src/plugins/android/src/mediacapture/qandroidcapturesession.h index 90af39fd..a0e7a894 100644 --- a/src/plugins/android/src/mediacapture/qandroidcapturesession.h +++ b/src/plugins/android/src/mediacapture/qandroidcapturesession.h @@ -161,7 +161,6 @@ private: QString m_containerFormat; QAudioEncoderSettings m_audioSettings; QVideoEncoderSettings m_videoSettings; - bool m_resolutionDirty; bool m_containerFormatDirty; bool m_videoSettingsDirty; bool m_audioSettingsDirty; diff --git a/src/plugins/android/src/wrappers/jni/androidcamera.cpp b/src/plugins/android/src/wrappers/jni/androidcamera.cpp index d9c8befa..7496e9cd 100644 --- a/src/plugins/android/src/wrappers/jni/androidcamera.cpp +++ b/src/plugins/android/src/wrappers/jni/androidcamera.cpp @@ -786,6 +786,9 @@ QSize AndroidCameraPrivate::getPreferredPreviewSizeForVideo() QJNIObjectPrivate size = m_parameters.callObjectMethod("getPreferredPreviewSizeForVideo", "()Landroid/hardware/Camera$Size;"); + if (!size.isValid()) + return QSize(); + return QSize(size.getField("width"), size.getField("height")); } From 8143aff1b293501f2ea37b98affbadd936e55f9b Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Tue, 24 Mar 2015 17:32:28 +0100 Subject: [PATCH 17/21] GStreamer: flush the current frame when stopping a media player. When stopping, we don't actually stop the GStreamer pipeline, we just pause it and prevent preroll frames from being shown. We also need to make sure the last presented frame is cleared in that case, otherwise it stays on screen. Fixed for both 0.10 and 1.0. Change-Id: Ibe26a7567f271ae0c3d8819eb9d35d6a95da1c6a Reviewed-by: Christian Stromme --- src/gsttools/qgstvideorenderersink.cpp | 47 +++++++++++++- src/gsttools/qvideosurfacegstsink.cpp | 62 +++++++++++++++++-- .../qgstvideorenderersink_p.h | 4 ++ .../gsttools_headers/qvideosurfacegstsink_p.h | 5 ++ 4 files changed, 110 insertions(+), 8 deletions(-) diff --git a/src/gsttools/qgstvideorenderersink.cpp b/src/gsttools/qgstvideorenderersink.cpp index 615348c1..31ac94e8 100644 --- a/src/gsttools/qgstvideorenderersink.cpp +++ b/src/gsttools/qgstvideorenderersink.cpp @@ -222,6 +222,17 @@ bool QVideoSurfaceGstDelegate::proposeAllocation(GstQuery *query) } } +void QVideoSurfaceGstDelegate::flush() +{ + QMutexLocker locker(&m_mutex); + + m_flush = true; + m_renderBuffer = 0; + m_renderCondition.wakeAll(); + + notify(); +} + GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer) { QMutexLocker locker(&m_mutex); @@ -388,6 +399,8 @@ QGstVideoRendererSink *QGstVideoRendererSink::createSink(QAbstractVideoSurface * sink->delegate = new QVideoSurfaceGstDelegate(surface); + g_signal_connect(G_OBJECT(sink), "notify::show-preroll-frame", G_CALLBACK(handleShowPrerollChange), sink); + return sink; } @@ -472,13 +485,41 @@ void QGstVideoRendererSink::finalize(GObject *object) G_OBJECT_CLASS(sink_parent_class)->finalize(object); } +void QGstVideoRendererSink::handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d) +{ + Q_UNUSED(o); + Q_UNUSED(p); + QGstVideoRendererSink *sink = reinterpret_cast(d); + + gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default + g_object_get(G_OBJECT(sink), "show-preroll-frame", &showPrerollFrame, NULL); + + if (!showPrerollFrame) { + GstState state = GST_STATE_VOID_PENDING; + gst_element_get_state(GST_ELEMENT(sink), &state, NULL, GST_CLOCK_TIME_NONE); + // show-preroll-frame being set to 'false' while in GST_STATE_PAUSED means + // the QMediaPlayer was stopped from the paused state. + // We need to flush the current frame. + if (state == GST_STATE_PAUSED) + sink->delegate->flush(); + } +} + GstStateChangeReturn QGstVideoRendererSink::change_state( GstElement *element, GstStateChange transition) { - Q_UNUSED(element); + QGstVideoRendererSink *sink = reinterpret_cast(element); - return GST_ELEMENT_CLASS(sink_parent_class)->change_state( - element, transition); + gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default + g_object_get(G_OBJECT(element), "show-preroll-frame", &showPrerollFrame, NULL); + + // If show-preroll-frame is 'false' when transitioning from GST_STATE_PLAYING to + // GST_STATE_PAUSED, it means the QMediaPlayer was stopped. + // We need to flush the current frame. + if (transition == GST_STATE_CHANGE_PLAYING_TO_PAUSED && !showPrerollFrame) + sink->delegate->flush(); + + return GST_ELEMENT_CLASS(sink_parent_class)->change_state(element, transition); } GstCaps *QGstVideoRendererSink::get_caps(GstBaseSink *base, GstCaps *filter) diff --git a/src/gsttools/qvideosurfacegstsink.cpp b/src/gsttools/qvideosurfacegstsink.cpp index 4a786ea1..737bc648 100644 --- a/src/gsttools/qvideosurfacegstsink.cpp +++ b/src/gsttools/qvideosurfacegstsink.cpp @@ -184,6 +184,21 @@ void QVideoSurfaceGstDelegate::clearPoolBuffers() m_pool->clear(); } +void QVideoSurfaceGstDelegate::flush() +{ + QMutexLocker locker(&m_mutex); + + m_frame = QVideoFrame(); + m_renderCondition.wakeAll(); + + if (QThread::currentThread() == thread()) { + if (!m_surface.isNull()) + m_surface->present(m_frame); + } else { + QMetaObject::invokeMethod(this, "queuedFlush", Qt::QueuedConnection); + } +} + GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer) { if (!m_surface) { @@ -244,6 +259,14 @@ void QVideoSurfaceGstDelegate::queuedStop() m_setupCondition.wakeAll(); } +void QVideoSurfaceGstDelegate::queuedFlush() +{ + QMutexLocker locker(&m_mutex); + + if (!m_surface.isNull()) + m_surface->present(QVideoFrame()); +} + void QVideoSurfaceGstDelegate::queuedRender() { QMutexLocker locker(&m_mutex); @@ -316,6 +339,8 @@ QVideoSurfaceGstSink *QVideoSurfaceGstSink::createSink(QAbstractVideoSurface *su sink->delegate = new QVideoSurfaceGstDelegate(surface); + g_signal_connect(G_OBJECT(sink), "notify::show-preroll-frame", G_CALLBACK(handleShowPrerollChange), sink); + return sink; } @@ -420,13 +445,40 @@ void QVideoSurfaceGstSink::finalize(GObject *object) G_OBJECT_CLASS(sink_parent_class)->finalize(object); } -GstStateChangeReturn QVideoSurfaceGstSink::change_state( - GstElement *element, GstStateChange transition) +void QVideoSurfaceGstSink::handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d) { - Q_UNUSED(element); + Q_UNUSED(o); + Q_UNUSED(p); + QVideoSurfaceGstSink *sink = reinterpret_cast(d); - return GST_ELEMENT_CLASS(sink_parent_class)->change_state( - element, transition); + gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default + g_object_get(G_OBJECT(sink), "show-preroll-frame", &showPrerollFrame, NULL); + + if (!showPrerollFrame) { + GstState state = GST_STATE_VOID_PENDING; + gst_element_get_state(GST_ELEMENT(sink), &state, NULL, GST_CLOCK_TIME_NONE); + // show-preroll-frame being set to 'false' while in GST_STATE_PAUSED means + // the QMediaPlayer was stopped from the paused state. + // We need to flush the current frame. + if (state == GST_STATE_PAUSED) + sink->delegate->flush(); + } +} + +GstStateChangeReturn QVideoSurfaceGstSink::change_state(GstElement *element, GstStateChange transition) +{ + QVideoSurfaceGstSink *sink = reinterpret_cast(element); + + gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default + g_object_get(G_OBJECT(element), "show-preroll-frame", &showPrerollFrame, NULL); + + // If show-preroll-frame is 'false' when transitioning from GST_STATE_PLAYING to + // GST_STATE_PAUSED, it means the QMediaPlayer was stopped. + // We need to flush the current frame. + if (transition == GST_STATE_CHANGE_PLAYING_TO_PAUSED && !showPrerollFrame) + sink->delegate->flush(); + + return GST_ELEMENT_CLASS(sink_parent_class)->change_state(element, transition); } GstCaps *QVideoSurfaceGstSink::get_caps(GstBaseSink *base) diff --git a/src/multimedia/gsttools_headers/qgstvideorenderersink_p.h b/src/multimedia/gsttools_headers/qgstvideorenderersink_p.h index 18670887..78bdf8cb 100644 --- a/src/multimedia/gsttools_headers/qgstvideorenderersink_p.h +++ b/src/multimedia/gsttools_headers/qgstvideorenderersink_p.h @@ -99,6 +99,8 @@ public: void unlock(); bool proposeAllocation(GstQuery *query); + void flush(); + GstFlowReturn render(GstBuffer *buffer); bool event(QEvent *event); @@ -145,6 +147,8 @@ private: static void finalize(GObject *object); + static void handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d); + static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition); static GstCaps *get_caps(GstBaseSink *sink, GstCaps *filter); diff --git a/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h b/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h index e8f61afe..a1ef5616 100644 --- a/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h +++ b/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h @@ -96,11 +96,14 @@ public: QMutex *poolMutex() { return &m_poolMutex; } void clearPoolBuffers(); + void flush(); + GstFlowReturn render(GstBuffer *buffer); private slots: void queuedStart(); void queuedStop(); + void queuedFlush(); void queuedRender(); void updateSupportedFormats(); @@ -139,6 +142,8 @@ private: static void finalize(GObject *object); + static void handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d); + static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition); static GstCaps *get_caps(GstBaseSink *sink); From aeb79d4a8bcb291822d74d923f7e68fb02ce96fe Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Thu, 28 May 2015 16:40:08 +0200 Subject: [PATCH 18/21] AVFoundation: fix default camera viewfinder pixel format. It was hardcoded to ARGB32, which is not a good idea, at least on iOS where the necessary conversion is slow. We now pick the QAbstractVideoSurface's preferred format, or if no surface is set, we pick the default from AVFoundation. As a result, the QML VideoOutput will now always use the NV12 format. Change-Id: I65205c706455502883b8098f0b5c0577b4106e01 Reviewed-by: Timur Pocheptsov --- .../camera/avfcamerarenderercontrol.mm | 22 +++------ .../avfoundation/camera/avfcamerasession.mm | 7 ++- .../avfcameraviewfindersettingscontrol.mm | 45 ++++++++++--------- 3 files changed, 33 insertions(+), 41 deletions(-) diff --git a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm index cf13635f..1fa1df99 100644 --- a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm +++ b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm @@ -167,19 +167,13 @@ private: int width = CVPixelBufferGetWidth(imageBuffer); int height = CVPixelBufferGetHeight(imageBuffer); + QVideoFrame::PixelFormat format = + AVFCameraViewfinderSettingsControl2::QtPixelFormatFromCVFormat(CVPixelBufferGetPixelFormatType(imageBuffer)); - QAbstractVideoBuffer *buffer = new CVPixelBufferVideoBuffer(imageBuffer); + if (format == QVideoFrame::Format_Invalid) + return; - QVideoFrame::PixelFormat format = QVideoFrame::Format_RGB32; - if ([captureOutput isKindOfClass:[AVCaptureVideoDataOutput class]]) { - NSDictionary *settings = ((AVCaptureVideoDataOutput *)captureOutput).videoSettings; - if (settings && [settings objectForKey:(id)kCVPixelBufferPixelFormatTypeKey]) { - NSNumber *avf = [settings objectForKey:(id)kCVPixelBufferPixelFormatTypeKey]; - format = AVFCameraViewfinderSettingsControl2::QtPixelFormatFromCVFormat([avf unsignedIntValue]); - } - } - - QVideoFrame frame(buffer, QSize(width, height), format); + QVideoFrame frame(new CVPixelBufferVideoBuffer(imageBuffer), QSize(width, height), format); m_renderer->syncHandleViewfinderFrame(frame); } @end @@ -229,12 +223,6 @@ void AVFCameraRendererControl::configureAVCaptureSession(AVFCameraSession *camer queue:queue]; dispatch_release(queue); - // Specify the pixel format - m_videoDataOutput.videoSettings = - [NSDictionary dictionaryWithObject: - [NSNumber numberWithInt:kCVPixelFormatType_32BGRA] - forKey:(id)kCVPixelBufferPixelFormatTypeKey]; - [m_cameraSession->captureSession() addOutput:m_videoDataOutput]; } diff --git a/src/plugins/avfoundation/camera/avfcamerasession.mm b/src/plugins/avfoundation/camera/avfcamerasession.mm index 6e4803f3..2cb3824d 100644 --- a/src/plugins/avfoundation/camera/avfcamerasession.mm +++ b/src/plugins/avfoundation/camera/avfcamerasession.mm @@ -283,12 +283,12 @@ void AVFCameraSession::setState(QCamera::State newState) if (m_state == QCamera::ActiveState) { Q_EMIT readyToConfigureConnections(); - [m_captureSession commitConfiguration]; - [m_captureSession startRunning]; m_defaultCodec = 0; defaultCodec(); applyImageEncoderSettings(); applyViewfinderSettings(); + [m_captureSession commitConfiguration]; + [m_captureSession startRunning]; } if (oldState == QCamera::ActiveState) { @@ -374,8 +374,7 @@ void AVFCameraSession::applyViewfinderSettings() } } - if (!vfSettings.isNull()) - vfControl->applySettings(); + vfControl->applySettings(); } } diff --git a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm index e71e6b16..b32c0cd6 100644 --- a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm +++ b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm @@ -485,13 +485,7 @@ QVector AVFCameraViewfinderSettingsControl2::viewfinde Q_ASSERT(m_videoOutput); QVector qtFormats; - QList filter; - NSArray *pixelFormats = [m_videoOutput availableVideoCVPixelFormatTypes]; - const QAbstractVideoSurface *surface = m_service->videoOutput() ? m_service->videoOutput()->surface() : 0; - - if (surface) - filter = surface->supportedPixelFormats(); for (NSObject *obj in pixelFormats) { if (![obj isKindOfClass:[NSNumber class]]) @@ -500,8 +494,8 @@ QVector AVFCameraViewfinderSettingsControl2::viewfinde NSNumber *formatAsNSNumber = static_cast(obj); // It's actually FourCharCode (== UInt32): const QVideoFrame::PixelFormat qtFormat(QtPixelFormatFromCVFormat([formatAsNSNumber unsignedIntValue])); - if (qtFormat != QVideoFrame::Format_Invalid && (!surface || filter.contains(qtFormat)) - && !qtFormats.contains(qtFormat)) { // Can happen, for example, with 8BiPlanar existing in video/full range. + if (qtFormat != QVideoFrame::Format_Invalid + && !qtFormats.contains(qtFormat)) { // Can happen, for example, with 8BiPlanar existing in video/full range. qtFormats << qtFormat; } } @@ -576,22 +570,33 @@ void AVFCameraViewfinderSettingsControl2::applySettings() #endif unsigned avfPixelFormat = 0; - if (m_settings.pixelFormat() != QVideoFrame::Format_Invalid && - convertPixelFormatIfSupported(m_settings.pixelFormat(), avfPixelFormat)) { - [videoSettings setObject:[NSNumber numberWithUnsignedInt:avfPixelFormat] - forKey:(id)kCVPixelBufferPixelFormatTypeKey]; - } else { - // We have to set the pixel format, otherwise AVFoundation can change it to something we do not support. - if (NSObject *oldFormat = [m_videoOutput.videoSettings objectForKey:(id)kCVPixelBufferPixelFormatTypeKey]) { - [videoSettings setObject:oldFormat forKey:(id)kCVPixelBufferPixelFormatTypeKey]; - } else { - [videoSettings setObject:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA] - forKey:(id)kCVPixelBufferPixelFormatTypeKey]; + 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 deviceFormats = viewfinderPixelFormats(); + QList surfaceFormats; + if (m_service->videoOutput() && m_service->videoOutput()->surface()) + surfaceFormats = m_service->videoOutput()->surface()->supportedPixelFormats(); + + QVideoFrame::PixelFormat format = deviceFormats.first(); + + for (int i = 0; i < surfaceFormats.count(); ++i) { + const QVideoFrame::PixelFormat surfaceFormat = surfaceFormats.at(i); + if (deviceFormats.contains(surfaceFormat)) { + format = surfaceFormat; + break; + } } + + CVPixelFormatFromQtFormat(format, avfPixelFormat); } - if (videoSettings.count) + if (avfPixelFormat != 0) { + [videoSettings setObject:[NSNumber numberWithUnsignedInt:avfPixelFormat] + forKey:(id)kCVPixelBufferPixelFormatTypeKey]; + m_videoOutput.videoSettings = videoSettings; + } qt_set_framerate_limits(m_captureDevice, m_videoConnection, m_settings); } From 1508f775acfd7aad18e71dde35c3ff0c9b073fc1 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Fri, 20 Mar 2015 18:33:28 +0100 Subject: [PATCH 19/21] Video asset writer for iOS AVFoundation on iOS lacks the ability to use AVCaptureVideoDataOutput and AVCaptureMovieFileOutput simultaneously. Right now viewfinder stops working as soon as we add movie file output. The only workaround we have now is to write video/audio 'maually' - creating asset writer and feeding it with audio/video samples. Change-Id: I33a63546783279c545f0433b5051287269825d3f Task-number: QTBUG-37655 Reviewed-by: Yoann Lopes --- .../camera/avfcamerarenderercontrol.h | 6 + .../camera/avfcamerarenderercontrol.mm | 26 +- .../avfoundation/camera/avfcameraservice.h | 9 +- .../avfoundation/camera/avfcameraservice.mm | 31 ++ .../avfoundation/camera/avfcamerasession.h | 2 + .../avfoundation/camera/avfcamerautility.h | 68 +++ .../avfoundation/camera/avfmediaassetwriter.h | 119 +++++ .../camera/avfmediaassetwriter.mm | 474 ++++++++++++++++++ .../camera/avfmediarecordercontrol_ios.h | 108 ++++ .../camera/avfmediarecordercontrol_ios.mm | 349 +++++++++++++ src/plugins/avfoundation/camera/camera.pro | 19 +- 11 files changed, 1200 insertions(+), 11 deletions(-) create mode 100644 src/plugins/avfoundation/camera/avfmediaassetwriter.h create mode 100644 src/plugins/avfoundation/camera/avfmediaassetwriter.mm create mode 100644 src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h create mode 100644 src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm diff --git a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.h b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.h index 92ab75bd..b8f92d9c 100644 --- a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.h +++ b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.h @@ -63,6 +63,11 @@ public: AVCaptureVideoDataOutput *videoDataOutput() const; +#ifdef Q_OS_IOS + AVFCaptureFramesDelegate *captureDelegate() const; + void resetCaptureDelegate() const; +#endif + Q_SIGNALS: void surfaceChanged(QAbstractVideoSurface *surface); @@ -80,6 +85,7 @@ private: QVideoFrame m_lastViewfinderFrame; QMutex m_vfMutex; + dispatch_queue_t m_delegateQueue; }; QT_END_NAMESPACE diff --git a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm index 1fa1df99..dd838d9b 100644 --- a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm +++ b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm @@ -143,6 +143,7 @@ private: - (void) captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection; + @end @implementation AVFCaptureFramesDelegate @@ -163,6 +164,9 @@ private: Q_UNUSED(connection); Q_UNUSED(captureOutput); + // NB: on iOS captureOutput/connection can be nil (when recording a video - + // avfmediaassetwriter). + CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); int width = CVPixelBufferGetWidth(imageBuffer); @@ -176,6 +180,7 @@ private: QVideoFrame frame(new CVPixelBufferVideoBuffer(imageBuffer), QSize(width, height), format); m_renderer->syncHandleViewfinderFrame(frame); } + @end @@ -191,6 +196,8 @@ AVFCameraRendererControl::~AVFCameraRendererControl() { [m_cameraSession->captureSession() removeOutput:m_videoDataOutput]; [m_viewfinderFramesDelegate release]; + if (m_delegateQueue) + dispatch_release(m_delegateQueue); } QAbstractVideoSurface *AVFCameraRendererControl::surface() const @@ -217,11 +224,10 @@ void AVFCameraRendererControl::configureAVCaptureSession(AVFCameraSession *camer m_videoDataOutput = [[[AVCaptureVideoDataOutput alloc] init] autorelease]; // Configure video output - dispatch_queue_t queue = dispatch_queue_create("vf_queue", NULL); + m_delegateQueue = dispatch_queue_create("vf_queue", NULL); [m_videoDataOutput setSampleBufferDelegate:m_viewfinderFramesDelegate - queue:queue]; - dispatch_release(queue); + queue:m_delegateQueue]; [m_cameraSession->captureSession() addOutput:m_videoDataOutput]; } @@ -279,6 +285,20 @@ AVCaptureVideoDataOutput *AVFCameraRendererControl::videoDataOutput() const return m_videoDataOutput; } +#ifdef Q_OS_IOS + +AVFCaptureFramesDelegate *AVFCameraRendererControl::captureDelegate() const +{ + return m_viewfinderFramesDelegate; +} + +void AVFCameraRendererControl::resetCaptureDelegate() const +{ + [m_videoDataOutput setSampleBufferDelegate:m_viewfinderFramesDelegate queue:m_delegateQueue]; +} + +#endif + void AVFCameraRendererControl::handleViewfinderFrame() { QVideoFrame frame; diff --git a/src/plugins/avfoundation/camera/avfcameraservice.h b/src/plugins/avfoundation/camera/avfcameraservice.h index d557872a..08b0ad26 100644 --- a/src/plugins/avfoundation/camera/avfcameraservice.h +++ b/src/plugins/avfoundation/camera/avfcameraservice.h @@ -41,13 +41,13 @@ QT_BEGIN_NAMESPACE class QCameraControl; +class QMediaRecorderControl; class AVFCameraControl; class AVFCameraInfoControl; class AVFCameraMetaDataControl; class AVFVideoWindowControl; class AVFVideoWidgetControl; class AVFCameraRendererControl; -class AVFMediaRecorderControl; class AVFImageCaptureControl; class AVFCameraSession; class AVFCameraDeviceControl; @@ -59,6 +59,8 @@ class AVFCameraViewfinderSettingsControl2; class AVFCameraViewfinderSettingsControl; class AVFImageEncoderControl; class AVFCameraFlashControl; +class AVFMediaRecorderControl; +class AVFMediaRecorderControlIOS; class AVFCameraService : public QMediaService { @@ -75,7 +77,8 @@ public: AVFCameraDeviceControl *videoDeviceControl() const { return m_videoDeviceControl; } AVFAudioInputSelectorControl *audioInputSelectorControl() const { return m_audioInputSelectorControl; } AVFCameraMetaDataControl *metaDataControl() const { return m_metaDataControl; } - AVFMediaRecorderControl *recorderControl() const { return m_recorderControl; } + AVFMediaRecorderControl *recorderControl() const; + AVFMediaRecorderControlIOS *recorderControlIOS() const; AVFImageCaptureControl *imageCaptureControl() const { return m_imageCaptureControl; } AVFCameraFocusControl *cameraFocusControl() const { return m_cameraFocusControl; } AVFCameraExposureControl *cameraExposureControl() const {return m_cameraExposureControl; } @@ -94,7 +97,7 @@ private: AVFAudioInputSelectorControl *m_audioInputSelectorControl; AVFCameraRendererControl *m_videoOutput; AVFCameraMetaDataControl *m_metaDataControl; - AVFMediaRecorderControl *m_recorderControl; + QMediaRecorderControl *m_recorderControl; AVFImageCaptureControl *m_imageCaptureControl; AVFCameraFocusControl *m_cameraFocusControl; AVFCameraExposureControl *m_cameraExposureControl; diff --git a/src/plugins/avfoundation/camera/avfcameraservice.mm b/src/plugins/avfoundation/camera/avfcameraservice.mm index f163e129..fd473b37 100644 --- a/src/plugins/avfoundation/camera/avfcameraservice.mm +++ b/src/plugins/avfoundation/camera/avfcameraservice.mm @@ -56,6 +56,7 @@ #ifdef Q_OS_IOS #include "avfcamerazoomcontrol.h" +#include "avfmediarecordercontrol_ios.h" #endif #include @@ -74,7 +75,14 @@ AVFCameraService::AVFCameraService(QObject *parent): m_audioInputSelectorControl = new AVFAudioInputSelectorControl(this); m_metaDataControl = new AVFCameraMetaDataControl(this); +#ifndef Q_OS_IOS + // This will connect a slot to 'captureModeChanged' + // and will break viewfinder by attaching AVCaptureMovieFileOutput + // in this slot. m_recorderControl = new AVFMediaRecorderControl(this); +#else + m_recorderControl = new AVFMediaRecorderControlIOS(this); +#endif m_imageCaptureControl = new AVFImageCaptureControl(this); m_cameraFocusControl = new AVFCameraFocusControl(this); m_cameraExposureControl = 0; @@ -97,6 +105,10 @@ AVFCameraService::~AVFCameraService() { m_cameraControl->setState(QCamera::UnloadedState); +#ifdef Q_OS_IOS + delete m_recorderControl; +#endif + if (m_videoOutput) { m_session->setVideoOutput(0); delete m_videoOutput; @@ -205,4 +217,23 @@ void AVFCameraService::releaseControl(QMediaControl *control) } +AVFMediaRecorderControl *AVFCameraService::recorderControl() const +{ +#ifdef Q_OS_IOS + return 0; +#else + return static_cast(m_recorderControl); +#endif +} + +AVFMediaRecorderControlIOS *AVFCameraService::recorderControlIOS() const +{ +#ifdef Q_OS_OSX + return 0; +#else + return static_cast(m_recorderControl); +#endif +} + + #include "moc_avfcameraservice.cpp" diff --git a/src/plugins/avfoundation/camera/avfcamerasession.h b/src/plugins/avfoundation/camera/avfcamerasession.h index 7b25a99c..2b322cfa 100644 --- a/src/plugins/avfoundation/camera/avfcamerasession.h +++ b/src/plugins/avfoundation/camera/avfcamerasession.h @@ -83,6 +83,8 @@ public: void removeProbe(AVFMediaVideoProbeControl *probe); FourCharCode defaultCodec(); + AVCaptureDeviceInput *videoInput() const {return m_videoInput;} + public Q_SLOTS: void setState(QCamera::State state); diff --git a/src/plugins/avfoundation/camera/avfcamerautility.h b/src/plugins/avfoundation/camera/avfcamerautility.h index 03a61460..42005a50 100644 --- a/src/plugins/avfoundation/camera/avfcamerautility.h +++ b/src/plugins/avfoundation/camera/avfcamerautility.h @@ -78,6 +78,74 @@ private: bool m_locked; }; +struct AVFObjectDeleter { + static void cleanup(NSObject *obj) + { + if (obj) + [obj release]; + } +}; + +template +class AVFScopedPointer : public QScopedPointer +{ +public: + AVFScopedPointer() {} + explicit AVFScopedPointer(T *ptr) : QScopedPointer(ptr) {} + operator T*() const + { + // Quite handy operator to enable Obj-C messages: [ptr someMethod]; + return data(); + } + + T *data() const + { + return static_cast(QScopedPointer::data()); + } + + T *take() + { + return static_cast(QScopedPointer::take()); + } +}; + +template<> +class AVFScopedPointer +{ +public: + AVFScopedPointer() : m_queue(0) {} + explicit AVFScopedPointer(dispatch_queue_t q) : m_queue(q) {} + + ~AVFScopedPointer() + { + if (m_queue) + dispatch_release(m_queue); + } + + operator dispatch_queue_t() const + { + // Quite handy operator to enable Obj-C messages: [ptr someMethod]; + return m_queue; + } + + dispatch_queue_t data() const + { + return m_queue; + } + + void reset(dispatch_queue_t q = 0) + { + if (m_queue) + dispatch_release(m_queue); + m_queue = q; + } + +private: + dispatch_queue_t m_queue; + + Q_DISABLE_COPY(AVFScopedPointer); +}; + inline QSysInfo::MacVersion qt_OS_limit(QSysInfo::MacVersion osxVersion, QSysInfo::MacVersion iosVersion) { diff --git a/src/plugins/avfoundation/camera/avfmediaassetwriter.h b/src/plugins/avfoundation/camera/avfmediaassetwriter.h new file mode 100644 index 00000000..eae70075 --- /dev/null +++ b/src/plugins/avfoundation/camera/avfmediaassetwriter.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** 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 AVFMEDIAASSETWRITER_H +#define AVFMEDIAASSETWRITER_H + +#include "avfcamerautility.h" + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class AVFCameraService; + +class AVFMediaAssetWriterDelegate +{ +public: + virtual ~AVFMediaAssetWriterDelegate(); + + virtual void assetWriterStarted() = 0; + virtual void assetWriterFailedToStart() = 0; + virtual void assetWriterFailedToStop() = 0; + virtual void assetWriterFinished() = 0; +}; + +typedef QAtomicInteger AVFAtomicBool; +typedef QAtomicInteger AVFAtomicInt64; + +QT_END_NAMESPACE + +// TODO: any reasonable error handling requires smart pointers, otherwise it's getting crappy immediately. + +@interface QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) : NSObject +{ +@private + AVFCameraService *m_service; + + QT_MANGLE_NAMESPACE(AVFScopedPointer) m_cameraWriterInput; + QT_MANGLE_NAMESPACE(AVFScopedPointer) m_audioInput; + QT_MANGLE_NAMESPACE(AVFScopedPointer) m_audioOutput; + QT_MANGLE_NAMESPACE(AVFScopedPointer) m_audioWriterInput; + + // High priority serial queue for video output: + QT_MANGLE_NAMESPACE(AVFScopedPointer) m_videoQueue; + // Serial queue for audio output: + QT_MANGLE_NAMESPACE(AVFScopedPointer) m_audioQueue; + // Queue to write sample buffers: + __weak dispatch_queue_t m_writerQueue; + + QT_MANGLE_NAMESPACE(AVFScopedPointer) m_assetWriter; + // Delegate's queue. + __weak dispatch_queue_t m_delegateQueue; + // TODO: QPointer?? + QT_PREPEND_NAMESPACE(AVFMediaAssetWriterDelegate) *m_delegate; + + bool m_setStartTime; + QT_MANGLE_NAMESPACE(AVFAtomicBool) m_stopped; + bool m_stoppedInternal; + bool m_aborted; + + QT_MANGLE_NAMESPACE(QMutex) m_writerMutex; +@public + QT_MANGLE_NAMESPACE(AVFAtomicInt64) m_durationInMs; +@private + CMTime m_startTime; + CMTime m_lastTimeStamp; +} + +- (id)initWithQueue:(dispatch_queue_t)writerQueue + delegate:(QT_PREPEND_NAMESPACE(AVFMediaAssetWriterDelegate) *)delegate + delegateQueue:(dispatch_queue_t)delegateQueue; + +- (bool)setupWithFileURL:(NSURL *)fileURL + cameraService:(QT_PREPEND_NAMESPACE(AVFCameraService) *)service; + +- (void)start; +- (void)stop; +// This to be called if control's dtor gets called, +// on the control's thread. +- (void)abort; + +@end + +#endif // AVFMEDIAASSETWRITER_H diff --git a/src/plugins/avfoundation/camera/avfmediaassetwriter.mm b/src/plugins/avfoundation/camera/avfmediaassetwriter.mm new file mode 100644 index 00000000..37004c1d --- /dev/null +++ b/src/plugins/avfoundation/camera/avfmediaassetwriter.mm @@ -0,0 +1,474 @@ +/**************************************************************************** +** +** 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 "avfaudioinputselectorcontrol.h" +#include "avfcamerarenderercontrol.h" +#include "avfmediaassetwriter.h" +#include "avfcameraservice.h" +#include "avfcamerasession.h" +#include "avfcameradebug.h" + +//#include +#include + +QT_USE_NAMESPACE + +namespace { + +bool qt_camera_service_isValid(AVFCameraService *service) +{ + if (!service || !service->session()) + return false; + + AVFCameraSession *session = service->session(); + if (!session->captureSession()) + return false; + + if (!session->videoInput()) + return false; + + if (!service->videoOutput() + || !service->videoOutput()->videoDataOutput()) { + return false; + } + + return true; +} + +} + +AVFMediaAssetWriterDelegate::~AVFMediaAssetWriterDelegate() +{ +} + +@interface QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) (PrivateAPI) +- (bool)addAudioCapture; +- (bool)addWriterInputs; +- (void)setQueues; +- (NSDictionary *)videoSettings; +- (NSDictionary *)audioSettings; +- (void)updateDuration:(CMTime)newTimeStamp; +@end + +@implementation QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) + +- (id)initWithQueue:(dispatch_queue_t)writerQueue + delegate:(AVFMediaAssetWriterDelegate *)delegate + delegateQueue:(dispatch_queue_t)delegateQueue +{ + Q_ASSERT(writerQueue); + Q_ASSERT(delegate); + Q_ASSERT(delegateQueue); + + if (self = [super init]) { + m_writerQueue = writerQueue; + m_delegate = delegate; + m_delegateQueue = delegateQueue; + m_setStartTime = true; + m_stopped.store(true); + m_stoppedInternal = false; + m_aborted = false; + m_startTime = kCMTimeInvalid; + m_lastTimeStamp = kCMTimeInvalid; + m_durationInMs.store(0); + } + + return self; +} + +- (bool)setupWithFileURL:(NSURL *)fileURL + cameraService:(AVFCameraService *)service +{ + Q_ASSERT(fileURL); + + if (!qt_camera_service_isValid(service)) { + qDebugCamera() << Q_FUNC_INFO << "invalid camera service"; + return false; + } + + m_service = service; + + m_videoQueue.reset(dispatch_queue_create("video-output-queue", DISPATCH_QUEUE_SERIAL)); + if (!m_videoQueue) { + qDebugCamera() << Q_FUNC_INFO << "failed to create video queue"; + return false; + } + dispatch_set_target_queue(m_videoQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)); + m_audioQueue.reset(dispatch_queue_create("audio-output-queue", DISPATCH_QUEUE_SERIAL)); + if (!m_audioQueue) { + qDebugCamera() << Q_FUNC_INFO << "failed to create audio queue"; + // But we still can write video! + } + + m_assetWriter.reset([[AVAssetWriter alloc] initWithURL:fileURL fileType:AVFileTypeQuickTimeMovie error:nil]); + if (!m_assetWriter) { + qDebugCamera() << Q_FUNC_INFO << "failed to create asset writer"; + return false; + } + + bool audioCaptureOn = false; + + if (m_audioQueue) + audioCaptureOn = [self addAudioCapture]; + + if (![self addWriterInputs]) { + if (audioCaptureOn) { + AVCaptureSession *session = m_service->session()->captureSession(); + [session removeOutput:m_audioOutput]; + [session removeInput:m_audioInput]; + m_audioOutput.reset(); + m_audioInput.reset(); + } + m_assetWriter.reset(); + return false; + } + // Ready to start ... + return true; +} + +- (void)start +{ + // To be executed on a writer's queue. + const QMutexLocker lock(&m_writerMutex); + if (m_aborted) + return; + + [self setQueues]; + + m_setStartTime = true; + m_stopped.store(false); + m_stoppedInternal = false; + [m_assetWriter startWriting]; + AVCaptureSession *session = m_service->session()->captureSession(); + if (!session.running) + [session startRunning]; +} + +- (void)stop +{ + // To be executed on a writer's queue. + const QMutexLocker lock(&m_writerMutex); + if (m_aborted) + return; + + if (m_stopped.load()) { + // Should never happen, but ... + // if something went wrong in a recorder control + // and we set state stopped without starting first ... + // m_stoppedIntenal will be false, but m_stopped - true. + return; + } + + m_stopped.store(true); + m_stoppedInternal = true; + [m_assetWriter finishWritingWithCompletionHandler:^{ + // TODO: make sure the session exist and we can call stop/remove on it. + AVCaptureSession *session = m_service->session()->captureSession(); + [session stopRunning]; + [session removeOutput:m_audioOutput]; + [session removeInput:m_audioInput]; + dispatch_async(m_delegateQueue, ^{ + m_delegate->assetWriterFinished(); + }); + }]; +} + +- (void)abort +{ + // To be executed on any thread, prevents writer from + // accessing any external object (probably deleted by this time) + const QMutexLocker lock(&m_writerMutex); + m_aborted = true; + if (m_stopped.load()) + return; + [m_assetWriter finishWritingWithCompletionHandler:^{ + }]; +} + +- (void)setStartTimeFrom:(CMSampleBufferRef)sampleBuffer +{ + // Writer's queue only. + Q_ASSERT(m_setStartTime); + Q_ASSERT(sampleBuffer); + + dispatch_async(m_delegateQueue, ^{ + m_delegate->assetWriterStarted(); + }); + + m_durationInMs.store(0); + m_startTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); + m_lastTimeStamp = m_startTime; + [m_assetWriter startSessionAtSourceTime:m_startTime]; + m_setStartTime = false; +} + +- (void)writeVideoSampleBuffer:(CMSampleBufferRef)sampleBuffer +{ + Q_ASSERT(sampleBuffer); + + // This code is executed only on a writer's queue, but + // it can access potentially deleted objects, so we + // need a lock and m_aborted flag test. + { + const QMutexLocker lock(&m_writerMutex); + if (!m_aborted && !m_stoppedInternal) { + if (m_setStartTime) + [self setStartTimeFrom:sampleBuffer]; + + if (m_cameraWriterInput.data().readyForMoreMediaData) { + [self updateDuration:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)]; + [m_cameraWriterInput appendSampleBuffer:sampleBuffer]; + } + } + } + + CFRelease(sampleBuffer); +} + +- (void)writeAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer +{ + // This code is executed only on a writer's queue. + // it does not touch any shared/external data. + Q_ASSERT(sampleBuffer); + + { + const QMutexLocker lock(&m_writerMutex); + if (!m_aborted && !m_stoppedInternal) { + if (m_setStartTime) + [self setStartTimeFrom:sampleBuffer]; + + if (m_audioWriterInput.data().readyForMoreMediaData) { + [self updateDuration:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)]; + [m_audioWriterInput appendSampleBuffer:sampleBuffer]; + } + } + } + + CFRelease(sampleBuffer); +} + +- (void)captureOutput:(AVCaptureOutput *)captureOutput + didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer + fromConnection:(AVCaptureConnection *)connection +{ + Q_UNUSED(connection) + + // This method can be called on either video or audio queue, never on a writer's + // queue - it does not access any shared data except this atomic flag below. + if (m_stopped.load()) + return; + + // Even if we are stopped now, we still do not access any data. + + if (!CMSampleBufferDataIsReady(sampleBuffer)) { + qDebugCamera() << Q_FUNC_INFO << "sample buffer is not ready, skipping."; + return; + } + + CFRetain(sampleBuffer); + + if (captureOutput != m_audioOutput.data()) { + { + const QMutexLocker lock(&m_writerMutex); + if (m_aborted || m_stoppedInternal) { + CFRelease(sampleBuffer); + return; + } + + // Find renderercontrol's delegate and invoke its method to + // show updated viewfinder's frame. + if (m_service && m_service->videoOutput()) { + NSObject *vfDelegate = + (NSObject *)m_service->videoOutput()->captureDelegate(); + if (vfDelegate) + [vfDelegate captureOutput:nil didOutputSampleBuffer:sampleBuffer fromConnection:nil]; + } + } + + dispatch_async(m_writerQueue, ^{ + [self writeVideoSampleBuffer:sampleBuffer]; + }); + } else { + dispatch_async(m_writerQueue, ^{ + [self writeAudioSampleBuffer:sampleBuffer]; + }); + } +} + +- (bool)addAudioCapture +{ + Q_ASSERT(m_service && m_service->session() && m_service->session()->captureSession()); + + if (!m_service->audioInputSelectorControl()) + return false; + + AVCaptureSession *captureSession = m_service->session()->captureSession(); + + AVCaptureDevice *audioDevice = m_service->audioInputSelectorControl()->createCaptureDevice(); + if (!audioDevice) { + qWarning() << Q_FUNC_INFO << "no audio input device available"; + return false; + } else { + NSError *error = nil; + m_audioInput.reset([[AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error] retain]); + + if (!m_audioInput || error) { + qWarning() << Q_FUNC_INFO << "failed to create audio device input"; + m_audioInput.reset(); + return false; + } else if (![captureSession canAddInput:m_audioInput]) { + qWarning() << Q_FUNC_INFO << "could not connect the audio input"; + m_audioInput.reset(); + return false; + } else { + [captureSession addInput:m_audioInput]; + } + } + + + m_audioOutput.reset([[AVCaptureAudioDataOutput alloc] init]); + if (m_audioOutput && [captureSession canAddOutput:m_audioOutput]) { + [captureSession addOutput:m_audioOutput]; + } else { + qDebugCamera() << Q_FUNC_INFO << "failed to add audio output"; + [captureSession removeInput:m_audioInput]; + m_audioInput.reset(); + m_audioOutput.reset(); + return false; + } + + return true; +} + +- (bool)addWriterInputs +{ + Q_ASSERT(m_service && m_service->videoOutput() + && m_service->videoOutput()->videoDataOutput()); + Q_ASSERT(m_assetWriter); + + m_cameraWriterInput.reset([[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:[self videoSettings]]); + if (!m_cameraWriterInput) { + qDebugCamera() << Q_FUNC_INFO << "failed to create camera writer input"; + return false; + } + + if ([m_assetWriter canAddInput:m_cameraWriterInput]) { + [m_assetWriter addInput:m_cameraWriterInput]; + } else { + qDebugCamera() << Q_FUNC_INFO << "failed to add camera writer input"; + m_cameraWriterInput.reset(); + return false; + } + + m_cameraWriterInput.data().expectsMediaDataInRealTime = YES; + + if (m_audioOutput) { + m_audioWriterInput.reset([[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio outputSettings:[self audioSettings]]); + if (!m_audioWriterInput) { + qDebugCamera() << Q_FUNC_INFO << "failed to create audio writer input"; + // But we still can record video. + } else if ([m_assetWriter canAddInput:m_audioWriterInput]) { + [m_assetWriter addInput:m_audioWriterInput]; + m_audioWriterInput.data().expectsMediaDataInRealTime = YES; + } else { + qDebugCamera() << Q_FUNC_INFO << "failed to add audio writer input"; + m_audioWriterInput.reset(); + // We can (still) write video though ... + } + } + + return true; +} + +- (void)setQueues +{ + Q_ASSERT(m_service && m_service->videoOutput() && m_service->videoOutput()->videoDataOutput()); + Q_ASSERT(m_videoQueue); + + [m_service->videoOutput()->videoDataOutput() setSampleBufferDelegate:self queue:m_videoQueue]; + + if (m_audioOutput) { + Q_ASSERT(m_audioQueue); + [m_audioOutput setSampleBufferDelegate:self queue:m_audioQueue]; + } +} + + +- (NSDictionary *)videoSettings +{ + // TODO: these settings should be taken from + // the video encoding settings control. + // For now we either take recommended (iOS >= 7.0) + // or some hardcoded values - they are still better than nothing (nil). +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_7_0) + AVCaptureVideoDataOutput *videoOutput = m_service->videoOutput()->videoDataOutput(); + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_7_0 && videoOutput) + return [videoOutput recommendedVideoSettingsForAssetWriterWithOutputFileType:AVFileTypeQuickTimeMovie]; +#endif + NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecH264, AVVideoCodecKey, + [NSNumber numberWithInt:1280], AVVideoWidthKey, + [NSNumber numberWithInt:720], AVVideoHeightKey, nil]; + + return videoSettings; +} + +- (NSDictionary *)audioSettings +{ + // TODO: these settings should be taken from + // the video/audio encoder settings control. + // For now we either take recommended (iOS >= 7.0) + // or nil - this seems to be good enough. +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_7_0) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_7_0 && m_audioOutput) + return [m_audioOutput recommendedAudioSettingsForAssetWriterWithOutputFileType:AVFileTypeQuickTimeMovie]; +#endif + + return nil; +} + +- (void)updateDuration:(CMTime)newTimeStamp +{ + Q_ASSERT(CMTimeCompare(m_startTime, kCMTimeInvalid)); + Q_ASSERT(CMTimeCompare(m_lastTimeStamp, kCMTimeInvalid)); + if (CMTimeCompare(newTimeStamp, m_lastTimeStamp) > 0) { + + const CMTime duration = CMTimeSubtract(newTimeStamp, m_startTime); + if (!CMTimeCompare(duration, kCMTimeInvalid)) + return; + + m_durationInMs.store(CMTimeGetSeconds(duration) * 1000); + m_lastTimeStamp = newTimeStamp; + } +} + +@end diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h b/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h new file mode 100644 index 00000000..78576948 --- /dev/null +++ b/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** 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 AVFMEDIARECORDERCONTROL_IOS_H +#define AVFMEDIARECORDERCONTROL_IOS_H + +#include "avfmediaassetwriter.h" +#include "avfstoragelocation.h" +#include "avfcamerautility.h" + +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class AVFCameraService; +class QString; +class QUrl; + +class AVFMediaRecorderControlIOS : public QMediaRecorderControl, public AVFMediaAssetWriterDelegate +{ + Q_OBJECT +public: + AVFMediaRecorderControlIOS(AVFCameraService *service, QObject *parent = 0); + ~AVFMediaRecorderControlIOS(); + + QUrl outputLocation() const Q_DECL_OVERRIDE; + bool setOutputLocation(const QUrl &location) Q_DECL_OVERRIDE; + + QMediaRecorder::State state() const Q_DECL_OVERRIDE; + QMediaRecorder::Status status() const Q_DECL_OVERRIDE; + + qint64 duration() const Q_DECL_OVERRIDE; + + bool isMuted() const Q_DECL_OVERRIDE; + qreal volume() const Q_DECL_OVERRIDE; + + void applySettings() Q_DECL_OVERRIDE; + +public Q_SLOTS: + void setState(QMediaRecorder::State state) Q_DECL_OVERRIDE; + void setMuted(bool muted) Q_DECL_OVERRIDE; + void setVolume(qreal volume) Q_DECL_OVERRIDE; + + // Writer delegate: +private: + + void assetWriterStarted() Q_DECL_OVERRIDE; + void assetWriterFailedToStart() Q_DECL_OVERRIDE; + void assetWriterFailedToStop() Q_DECL_OVERRIDE; + void assetWriterFinished() Q_DECL_OVERRIDE; + +private Q_SLOTS: + void captureModeChanged(QCamera::CaptureModes); + void cameraStatusChanged(QCamera::Status newStatus); + +private: + void stopWriter(); + + AVFCameraService *m_service; + + AVFScopedPointer m_writerQueue; + AVFScopedPointer m_writer; + + QUrl m_outputLocation; + AVFStorageLocation m_storageLocation; + + QMediaRecorder::State m_state; + QMediaRecorder::Status m_lastStatus; +}; + +QT_END_NAMESPACE + +#endif // AVFMEDIARECORDERCONTROL_IOS_H diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm b/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm new file mode 100644 index 00000000..b763dbcc --- /dev/null +++ b/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm @@ -0,0 +1,349 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies). +** 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 "avfmediarecordercontrol_ios.h" +#include "avfcamerarenderercontrol.h" +#include "avfcamerasession.h" +#include "avfcameracontrol.h" +#include "avfcameraservice.h" +#include "avfcameradebug.h" + +#include + +QT_USE_NAMESPACE + +namespace { + +bool qt_is_writable_file_URL(NSURL *fileURL) +{ + Q_ASSERT(fileURL); + + if (![fileURL isFileURL]) + return false; + + if (NSString *path = [[fileURL path] stringByExpandingTildeInPath]) { + return [[NSFileManager defaultManager] + isWritableFileAtPath:[path stringByDeletingLastPathComponent]]; + } + + return false; +} + +bool qt_file_exists(NSURL *fileURL) +{ + Q_ASSERT(fileURL); + + if (NSString *path = [[fileURL path] stringByExpandingTildeInPath]) + return [[NSFileManager defaultManager] fileExistsAtPath:path]; + + return false; +} + +} + +AVFMediaRecorderControlIOS::AVFMediaRecorderControlIOS(AVFCameraService *service, QObject *parent) + : QMediaRecorderControl(parent) + , m_service(service) + , m_state(QMediaRecorder::StoppedState) + , m_lastStatus(QMediaRecorder::UnloadedStatus) +{ + Q_ASSERT(service); + + m_writerQueue.reset(dispatch_queue_create("asset-writer-queue", DISPATCH_QUEUE_SERIAL)); + if (!m_writerQueue) { + qDebugCamera() << Q_FUNC_INFO << "failed to create an asset writer's queue"; + return; + } + + m_writer.reset([[QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) alloc] initWithQueue:m_writerQueue + delegate:this delegateQueue:dispatch_get_main_queue()]); + if (!m_writer) { + qDebugCamera() << Q_FUNC_INFO << "failed to create an asset writer"; + return; + } + + AVFCameraControl *cameraControl = m_service->cameraControl(); + if (!cameraControl) { + qDebugCamera() << Q_FUNC_INFO << "camera control is nil"; + return; + } + + connect(cameraControl, SIGNAL(captureModeChanged(QCamera::CaptureModes)), + SLOT(captureModeChanged(QCamera::CaptureModes))); + connect(cameraControl, SIGNAL(statusChanged(QCamera::Status)), + SLOT(cameraStatusChanged(QCamera::Status))); +} + +AVFMediaRecorderControlIOS::~AVFMediaRecorderControlIOS() +{ + [m_writer abort]; +} + +QUrl AVFMediaRecorderControlIOS::outputLocation() const +{ + return m_outputLocation; +} + +bool AVFMediaRecorderControlIOS::setOutputLocation(const QUrl &location) +{ + m_outputLocation = location; + return location.scheme() == QLatin1String("file") || location.scheme().isEmpty(); +} + +QMediaRecorder::State AVFMediaRecorderControlIOS::state() const +{ + return m_state; +} + +QMediaRecorder::Status AVFMediaRecorderControlIOS::status() const +{ + return m_lastStatus; +} + +qint64 AVFMediaRecorderControlIOS::duration() const +{ + return m_writer.data()->m_durationInMs.load(); +} + +bool AVFMediaRecorderControlIOS::isMuted() const +{ + return false; +} + +qreal AVFMediaRecorderControlIOS::volume() const +{ + return 1.; +} + +void AVFMediaRecorderControlIOS::applySettings() +{ +} + +void AVFMediaRecorderControlIOS::setState(QMediaRecorder::State state) +{ + Q_ASSERT(m_service->session() + && m_service->session()->captureSession()); + + if (!m_writer) { + qDebugCamera() << Q_FUNC_INFO << "Invalid recorder"; + return; + } + + if (state == m_state) + return; + + switch (state) { + case QMediaRecorder::RecordingState: + { + AVFCameraControl *cameraControl = m_service->cameraControl(); + Q_ASSERT(cameraControl); + + if (!(cameraControl->captureMode() & QCamera::CaptureVideo)) { + qDebugCamera() << Q_FUNC_INFO << "wrong capture mode, CaptureVideo expected"; + Q_EMIT error(QMediaRecorder::ResourceError, tr("Failed to start recording")); + return; + } + + if (cameraControl->status() != QCamera::ActiveStatus) { + qDebugCamera() << Q_FUNC_INFO << "can not start record while camera is not active"; + Q_EMIT error(QMediaRecorder::ResourceError, tr("Failed to start recording")); + return; + } + + const QString path(m_outputLocation.scheme() == QLatin1String("file") ? + m_outputLocation.path() : m_outputLocation.toString()); + const QUrl fileURL(QUrl::fromLocalFile(m_storageLocation.generateFileName(path, QCamera::CaptureVideo, + QLatin1String("clip_"), QLatin1String("mp4")))); + + NSURL *nsFileURL = fileURL.toNSURL(); + if (!nsFileURL) { + qWarning() << Q_FUNC_INFO << "invalid output URL:" << fileURL; + Q_EMIT error(QMediaRecorder::ResourceError, tr("Invalid output file URL")); + return; + } + if (!qt_is_writable_file_URL(nsFileURL)) { + qWarning() << Q_FUNC_INFO << "invalid output URL:" << fileURL + << "(the location is not writable)"; + Q_EMIT error(QMediaRecorder::ResourceError, tr("Non-writeable file location")); + return; + } + if (qt_file_exists(nsFileURL)) { + // We test for/handle this error here since AWAssetWriter will raise an + // Objective-C exception, which is not good at all. + qWarning() << Q_FUNC_INFO << "invalid output URL:" << fileURL + << "(file already exists)"; + Q_EMIT error(QMediaRecorder::ResourceError, tr("File already exists")); + return; + } + + AVCaptureSession *session = m_service->session()->captureSession(); + // We stop session now so that no more frames for renderer's queue + // generated, will restart in assetWriterStarted. + [session stopRunning]; + + if ([m_writer setupWithFileURL:nsFileURL cameraService:m_service]) { + m_state = QMediaRecorder::RecordingState; + m_lastStatus = QMediaRecorder::StartingStatus; + + Q_EMIT actualLocationChanged(fileURL); + Q_EMIT stateChanged(m_state); + Q_EMIT statusChanged(m_lastStatus); + + dispatch_async(m_writerQueue, ^{ + [m_writer start]; + }); + } else { + [session startRunning]; + Q_EMIT error(QMediaRecorder::FormatError, tr("Failed to start recording")); + } + } break; + case QMediaRecorder::PausedState: + { + Q_EMIT error(QMediaRecorder::FormatError, tr("Recording pause not supported")); + return; + } break; + case QMediaRecorder::StoppedState: + { + // Do not check the camera status, we can stop if we started. + stopWriter(); + } + } +} + +void AVFMediaRecorderControlIOS::setMuted(bool muted) +{ + Q_UNUSED(muted) + qDebugCamera() << Q_FUNC_INFO << "not implemented"; +} + +void AVFMediaRecorderControlIOS::setVolume(qreal volume) +{ + Q_UNUSED(volume); + qDebugCamera() << Q_FUNC_INFO << "not implemented"; +} + +void AVFMediaRecorderControlIOS::assetWriterStarted() +{ + m_lastStatus = QMediaRecorder::RecordingStatus; + Q_EMIT statusChanged(QMediaRecorder::RecordingStatus); +} + +void AVFMediaRecorderControlIOS::assetWriterFailedToStart() +{ +} + +void AVFMediaRecorderControlIOS::assetWriterFailedToStop() +{ +} + +void AVFMediaRecorderControlIOS::assetWriterFinished() +{ + AVFCameraControl *cameraControl = m_service->cameraControl(); + Q_ASSERT(cameraControl); + + const QMediaRecorder::Status lastStatus = m_lastStatus; + + if (cameraControl->captureMode() & QCamera::CaptureVideo) + m_lastStatus = QMediaRecorder::LoadedStatus; + else + m_lastStatus = QMediaRecorder::UnloadedStatus; + + m_service->videoOutput()->resetCaptureDelegate(); + [m_service->session()->captureSession() startRunning]; + + if (m_lastStatus != lastStatus) + Q_EMIT statusChanged(m_lastStatus); +} + +void AVFMediaRecorderControlIOS::captureModeChanged(QCamera::CaptureModes newMode) +{ + AVFCameraControl *cameraControl = m_service->cameraControl(); + Q_ASSERT(cameraControl); + + const QMediaRecorder::Status lastStatus = m_lastStatus; + + if (newMode & QCamera::CaptureVideo) { + if (cameraControl->status() == QCamera::ActiveStatus) + m_lastStatus = QMediaRecorder::LoadedStatus; + } else { + if (m_lastStatus == QMediaRecorder::RecordingStatus) + return stopWriter(); + else + m_lastStatus = QMediaRecorder::UnloadedStatus; + } + + if (m_lastStatus != lastStatus) + Q_EMIT statusChanged(m_lastStatus); +} + +void AVFMediaRecorderControlIOS::cameraStatusChanged(QCamera::Status newStatus) +{ + AVFCameraControl *cameraControl = m_service->cameraControl(); + Q_ASSERT(cameraControl); + + const QMediaRecorder::Status lastStatus = m_lastStatus; + const bool isCapture = cameraControl->captureMode() & QCamera::CaptureVideo; + if (newStatus == QCamera::StartingStatus) { + if (isCapture && m_lastStatus == QMediaRecorder::UnloadedStatus) + m_lastStatus = QMediaRecorder::LoadingStatus; + } else if (newStatus == QCamera::ActiveStatus) { + if (isCapture && m_lastStatus == QMediaRecorder::LoadingStatus) + m_lastStatus = QMediaRecorder::LoadedStatus; + } else { + if (m_lastStatus == QMediaRecorder::RecordingStatus) + return stopWriter(); + if (newStatus == QCamera::UnloadedStatus) + m_lastStatus = QMediaRecorder::UnloadedStatus; + } + + if (lastStatus != m_lastStatus) + Q_EMIT statusChanged(m_lastStatus); +} + +void AVFMediaRecorderControlIOS::stopWriter() +{ + if (m_lastStatus == QMediaRecorder::RecordingStatus) { + m_state = QMediaRecorder::StoppedState; + m_lastStatus = QMediaRecorder::FinalizingStatus; + + Q_EMIT stateChanged(m_state); + Q_EMIT statusChanged(m_lastStatus); + + dispatch_async(m_writerQueue, ^{ + [m_writer stop]; + }); + } +} + +#include "moc_avfmediarecordercontrol_ios.cpp" diff --git a/src/plugins/avfoundation/camera/camera.pro b/src/plugins/avfoundation/camera/camera.pro index ac389df7..62afdcd8 100644 --- a/src/plugins/avfoundation/camera/camera.pro +++ b/src/plugins/avfoundation/camera/camera.pro @@ -27,7 +27,6 @@ HEADERS += \ avfcameracontrol.h \ avfcamerametadatacontrol.h \ avfimagecapturecontrol.h \ - avfmediarecordercontrol.h \ avfcameraservice.h \ avfcamerasession.h \ avfstoragelocation.h \ @@ -49,7 +48,6 @@ OBJECTIVE_SOURCES += \ avfcameracontrol.mm \ avfcamerametadatacontrol.mm \ avfimagecapturecontrol.mm \ - avfmediarecordercontrol.mm \ avfcameraservice.mm \ avfcamerasession.mm \ avfstoragelocation.mm \ @@ -66,9 +64,20 @@ OBJECTIVE_SOURCES += \ avfimageencodercontrol.mm \ avfcameraflashcontrol.mm -ios { +osx { -HEADERS += avfcamerazoomcontrol.h -OBJECTIVE_SOURCES += avfcamerazoomcontrol.mm +HEADERS += avfmediarecordercontrol.h +OBJECTIVE_SOURCES += avfmediarecordercontrol.mm + +} + +ios { + +HEADERS += avfcamerazoomcontrol.h \ + avfmediaassetwriter.h \ + avfmediarecordercontrol_ios.h +OBJECTIVE_SOURCES += avfcamerazoomcontrol.mm \ + avfmediaassetwriter.mm \ + avfmediarecordercontrol_ios.mm } From 9f97abb0922285e26de8927ea8e526f455d36ef4 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Mon, 1 Jun 2015 16:31:44 +0200 Subject: [PATCH 20/21] AVFoundation: fix a problem when changing the camera viewfinder size. We need to restart the video surface when the frame size changes. Change-Id: I81af3cb40fb40f7d157174ac96d42213880fbacd Reviewed-by: Timur Pocheptsov --- src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm index dd838d9b..52954128 100644 --- a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm +++ b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm @@ -309,8 +309,10 @@ void AVFCameraRendererControl::handleViewfinderFrame() } if (m_surface && frame.isValid()) { - if (m_surface->isActive() && m_surface->surfaceFormat().pixelFormat() != frame.pixelFormat()) + if (m_surface->isActive() && (m_surface->surfaceFormat().pixelFormat() != frame.pixelFormat() + || m_surface->surfaceFormat().frameSize() != frame.size())) { m_surface->stop(); + } if (!m_surface->isActive()) { QVideoSurfaceFormat format(frame.size(), frame.pixelFormat()); From 7fd0fec5e5e2906ee7c9fdfce9181ef81c09d9f1 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Tue, 2 Jun 2015 14:56:56 +0200 Subject: [PATCH 21/21] GStreamer: remove unnecessary qWarning(). Change-Id: Ibbf5d5b7e3675fe6fee30e7486e3bc7b59fa231d Reviewed-by: Christian Stromme --- src/gsttools/qgstreamervideowindow.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/gsttools/qgstreamervideowindow.cpp b/src/gsttools/qgstreamervideowindow.cpp index a0697003..ee25acc4 100644 --- a/src/gsttools/qgstreamervideowindow.cpp +++ b/src/gsttools/qgstreamervideowindow.cpp @@ -71,8 +71,6 @@ QGstreamerVideoWindow::QGstreamerVideoWindow(QObject *parent, const char *elemen addProbeToPad(pad); gst_object_unref(GST_OBJECT(pad)); } - else - qDebug() << "No m_videoSink available!"; } QGstreamerVideoWindow::~QGstreamerVideoWindow()