From 30078eeee13b60af65deb14a6ac1d6deca53003c Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Wed, 15 Jan 2014 18:35:07 +0100 Subject: [PATCH 01/11] Android: fixes some issues with JCamera. - Quit the worker thread only when the worker is deleted. This makes sure all events are processed before terminating the worker. - Correctly cache the preview size even when it's an invalid one. Task-number: QTBUG-36204 Change-Id: I76055984e8ece3f7f40dba7dd89d28a4faa1e72e Reviewed-by: Denis Kormalev Reviewed-by: Christian Stromme --- src/plugins/android/src/wrappers/jcamera.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/plugins/android/src/wrappers/jcamera.cpp b/src/plugins/android/src/wrappers/jcamera.cpp index d24a59a3..3a0585f3 100644 --- a/src/plugins/android/src/wrappers/jcamera.cpp +++ b/src/plugins/android/src/wrappers/jcamera.cpp @@ -151,6 +151,7 @@ class JCameraWorker : public QObject, public QJNIObjectPrivate friend class JCamera; JCameraWorker(JCamera *camera, int cameraId, jobject cam, QThread *workerThread); + ~JCameraWorker(); Q_INVOKABLE void release(); @@ -275,9 +276,7 @@ JCamera::~JCamera() g_objectMap.remove(d->m_cameraId); g_objectMapMutex.unlock(); } - QThread *workerThread = d->m_workerThread; d->deleteLater(); - workerThread->quit(); } JCamera *JCamera::open(int cameraId) @@ -372,7 +371,7 @@ void JCamera::setPreviewSize(const QSize &size) d->m_parametersMutex.lock(); bool areParametersValid = d->m_parameters.isValid(); d->m_parametersMutex.unlock(); - if (!areParametersValid || !size.isValid()) + if (!areParametersValid) return; d->m_previewSize = size; @@ -661,6 +660,11 @@ JCameraWorker::JCameraWorker(JCamera *camera, int cameraId, jobject cam, QThread } } +JCameraWorker::~JCameraWorker() +{ + m_workerThread->quit(); +} + void JCameraWorker::release() { m_previewSize = QSize(); From ff2cac464cd12de8fa2079e78db3527184695ad5 Mon Sep 17 00:00:00 2001 From: Andrew den Exter Date: Tue, 10 Dec 2013 14:25:47 +1000 Subject: [PATCH 02/11] Allow the user to specify the viewfinder resolution instead of guessing Guessing badly prevents the camerabin pipeline from loading at all and the fallbacks used are not representive of the capabilities of most cameras. So either stay out of the process and let gstreamer negotiate a resolution if it can, or use a resolution supplied through the viewfinder settings control by someone hopefully better informed. Task-number: QTBUG-30842 Change-Id: Iec2dcc2476f38822f9e0d02301e46a1f49b7c6da Reviewed-by: Yoann Lopes --- src/plugins/gstreamer/camerabin/camerabin.pro | 4 +- .../gstreamer/camerabin/camerabinservice.cpp | 4 + .../gstreamer/camerabin/camerabinsession.cpp | 130 ++++++++---------- .../gstreamer/camerabin/camerabinsession.h | 4 +- .../camerabin/camerabinviewfindersettings.cpp | 106 ++++++++++++++ .../camerabin/camerabinviewfindersettings.h | 70 ++++++++++ 6 files changed, 247 insertions(+), 71 deletions(-) create mode 100644 src/plugins/gstreamer/camerabin/camerabinviewfindersettings.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabinviewfindersettings.h diff --git a/src/plugins/gstreamer/camerabin/camerabin.pro b/src/plugins/gstreamer/camerabin/camerabin.pro index e18da8e6..9efa0812 100644 --- a/src/plugins/gstreamer/camerabin/camerabin.pro +++ b/src/plugins/gstreamer/camerabin/camerabin.pro @@ -29,7 +29,8 @@ HEADERS += \ $$PWD/camerabinvideoencoder.h \ $$PWD/camerabinresourcepolicy.h \ $$PWD/camerabincapturedestination.h \ - $$PWD/camerabincapturebufferformat.h + $$PWD/camerabincapturebufferformat.h \ + $$PWD/camerabinviewfindersettings.h SOURCES += \ $$PWD/camerabinserviceplugin.cpp \ @@ -46,6 +47,7 @@ SOURCES += \ $$PWD/camerabinvideoencoder.cpp \ $$PWD/camerabinresourcepolicy.cpp \ $$PWD/camerabincapturedestination.cpp \ + $$PWD/camerabinviewfindersettings.cpp \ $$PWD/camerabincapturebufferformat.cpp maemo6 { diff --git a/src/plugins/gstreamer/camerabin/camerabinservice.cpp b/src/plugins/gstreamer/camerabin/camerabinservice.cpp index a916ee88..df02a9ec 100644 --- a/src/plugins/gstreamer/camerabin/camerabinservice.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinservice.cpp @@ -61,6 +61,7 @@ #include "camerabinimageprocessing.h" #include "camerabincapturebufferformat.h" #include "camerabincapturedestination.h" +#include "camerabinviewfindersettings.h" #include #include @@ -240,6 +241,9 @@ QMediaControl *CameraBinService::requestControl(const char *name) if (qstrcmp(name, QCameraCaptureBufferFormatControl_iid) == 0) return m_captureSession->captureBufferFormatControl(); + if (qstrcmp(name, QCameraViewfinderSettingsControl_iid) == 0) + return m_captureSession->viewfinderSettingsControl(); + return 0; } diff --git a/src/plugins/gstreamer/camerabin/camerabinsession.cpp b/src/plugins/gstreamer/camerabin/camerabinsession.cpp index 18f6d54d..63536d91 100644 --- a/src/plugins/gstreamer/camerabin/camerabinsession.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinsession.cpp @@ -55,6 +55,7 @@ #endif #include "camerabinimageprocessing.h" +#include "camerabinviewfindersettings.h" #include "camerabincapturedestination.h" #include "camerabincapturebufferformat.h" @@ -91,6 +92,7 @@ #define AUDIO_SOURCE_PROPERTY "audio-source" #define SUPPORTED_IMAGE_CAPTURE_CAPS_PROPERTY "image-capture-supported-caps" #define SUPPORTED_VIDEO_CAPTURE_CAPS_PROPERTY "video-capture-supported-caps" +#define SUPPORTED_VIEWFINDER_CAPS_PROPERTY "viewfinder-supported-caps" #define IMAGE_CAPTURE_CAPS_PROPERTY "image-capture-caps" #define VIDEO_CAPTURE_CAPS_PROPERTY "video-capture-caps" #define VIEWFINDER_CAPS_PROPERTY "viewfinder-caps" @@ -110,10 +112,6 @@ #define PREVIEW_CAPS_4_3 \ "video/x-raw-rgb, width = (int) 640, height = (int) 480" -#define VIEWFINDER_RESOLUTION_4x3 QSize(640, 480) -#define VIEWFINDER_RESOLUTION_3x2 QSize(720, 480) -#define VIEWFINDER_RESOLUTION_16x9 QSize(800, 450) - //using GST_STATE_READY for QCamera::LoadedState //may not work reliably at least with some webcams. @@ -170,6 +168,7 @@ CameraBinSession::CameraBinSession(QObject *parent) m_imageProcessingControl = new CameraBinImageProcessing(this); m_captureDestinationControl = new CameraBinCaptureDestination(this); m_captureBufferFormatControl = new CameraBinCaptureBufferFormat(this); + m_viewfinderSettingsControl = new CameraBinViewfinderSettings(this); QByteArray envFlags = qgetenv("QT_GSTREAMER_CAMERABIN_FLAGS"); if (!envFlags.isEmpty()) @@ -246,8 +245,7 @@ bool CameraBinSession::setupCameraBin() return true; } -static GstCaps *resolutionToCaps(const QSize &resolution, - const QPair &rate = qMakePair(0,0)) +static GstCaps *resolutionToCaps(const QSize &resolution, const QPair &rate = qMakePair(0,0)) { if (resolution.isEmpty()) return gst_caps_new_any(); @@ -263,7 +261,23 @@ static GstCaps *resolutionToCaps(const QSize &resolution, "width", G_TYPE_INT, resolution.width(), "height", G_TYPE_INT, resolution.height(), "framerate", GST_TYPE_FRACTION, rate.first, rate.second, - NULL), NULL); + NULL), + gst_structure_new("video/x-raw-data", + "width", G_TYPE_INT, resolution.width(), + "height", G_TYPE_INT, resolution.height(), + "framerate", GST_TYPE_FRACTION, rate.first, rate.second, + NULL), + gst_structure_new("video/x-android-buffer", + "width", G_TYPE_INT, resolution.width(), + "height", G_TYPE_INT, resolution.height(), + "framerate", GST_TYPE_FRACTION, rate.first, rate.second, + NULL), + gst_structure_new("image/jpeg", + "width", G_TYPE_INT, resolution.width(), + "height", G_TYPE_INT, resolution.height(), + "framerate", GST_TYPE_FRACTION, rate.first, rate.second, + NULL), + NULL); } else { caps = gst_caps_new_full (gst_structure_new ("video/x-raw-yuv", "width", G_TYPE_INT, resolution.width(), @@ -271,85 +285,63 @@ static GstCaps *resolutionToCaps(const QSize &resolution, NULL), gst_structure_new ("video/x-raw-rgb", "width", G_TYPE_INT, resolution.width(), - "height", G_TYPE_INT, resolution.height(), NULL), NULL); + "height", G_TYPE_INT, resolution.height(), + NULL), + gst_structure_new("video/x-raw-data", + "width", G_TYPE_INT, resolution.width(), + "height", G_TYPE_INT, resolution.height(), + NULL), + gst_structure_new ("video/x-android-buffer", + "width", G_TYPE_INT, resolution.width(), + "height", G_TYPE_INT, resolution.height(), + NULL), + gst_structure_new ("image/jpeg", + "width", G_TYPE_INT, resolution.width(), + "height", G_TYPE_INT, resolution.height(), + NULL), + NULL); } + return caps; } void CameraBinSession::setupCaptureResolution() { - if (m_captureMode == QCamera::CaptureStillImage) { - QSize resolution = m_imageEncodeControl->imageSettings().resolution(); - - //by default select the maximum supported resolution - if (resolution.isEmpty()) { - bool continuous = false; - QList resolutions = supportedResolutions(qMakePair(0,0), - &continuous, - QCamera::CaptureStillImage); - if (!resolutions.isEmpty()) - resolution = resolutions.last(); - } - - QSize viewfinderResolution = VIEWFINDER_RESOLUTION_4x3; - - if (!resolution.isEmpty()) { - GstCaps *caps = resolutionToCaps(resolution); + QSize resolution = m_imageEncodeControl->imageSettings().resolution(); + if (!resolution.isEmpty()) { + GstCaps *caps = resolutionToCaps(resolution); #if CAMERABIN_DEBUG - qDebug() << Q_FUNC_INFO << "set image resolution" << resolution << gst_caps_to_string(caps); + qDebug() << Q_FUNC_INFO << "set image resolution" << resolution << gst_caps_to_string(caps); #endif - g_object_set(m_camerabin, IMAGE_CAPTURE_CAPS_PROPERTY, caps, NULL); - gst_caps_unref(caps); - - if (!resolution.isEmpty()) { - qreal aspectRatio = qreal(resolution.width()) / resolution.height(); - if (aspectRatio < 1.4) - viewfinderResolution = VIEWFINDER_RESOLUTION_4x3; - else if (aspectRatio > 1.7) - viewfinderResolution = VIEWFINDER_RESOLUTION_16x9; - else - viewfinderResolution = VIEWFINDER_RESOLUTION_3x2; - } - } else { - g_object_set(m_camerabin, IMAGE_CAPTURE_CAPS_PROPERTY, GST_CAPS_ANY, NULL); - } - - //on low res cameras the viewfinder resolution should not be bigger - //then capture resolution - if (viewfinderResolution.width() > resolution.width() && !resolution.isEmpty()) - viewfinderResolution = resolution; - - GstCaps *viewfinderCaps = resolutionToCaps(viewfinderResolution); -#if CAMERABIN_DEBUG - qDebug() << "Set viewfinder resolution" << viewfinderResolution <actualVideoSettings().resolution(); - //qreal framerate = m_videoEncodeControl->videoSettings().frameRate(); - - if (resolution.isEmpty()) { - //select the hightest supported resolution - bool continuous = false; - QList resolutions = supportedResolutions(qMakePair(0,0), - &continuous, - QCamera::CaptureVideo); - if (!resolutions.isEmpty()) - resolution = resolutions.last(); - } - + resolution = m_videoEncodeControl->actualVideoSettings().resolution(); + //qreal framerate = m_videoEncodeControl->videoSettings().frameRate(); + if (!resolution.isEmpty()) { GstCaps *caps = resolutionToCaps(resolution /*, framerate*/); //convert to rational #if CAMERABIN_DEBUG qDebug() << Q_FUNC_INFO << "set video resolution" << resolution << gst_caps_to_string(caps); #endif - - //Use the same resolution for viewfinder and video capture g_object_set(m_camerabin, VIDEO_CAPTURE_CAPS_PROPERTY, caps, NULL); + gst_caps_unref(caps); + } else { + g_object_set(m_camerabin, VIDEO_CAPTURE_CAPS_PROPERTY, NULL, NULL); + } + + resolution = m_viewfinderSettingsControl->resolution(); + if (!resolution.isEmpty()) { + GstCaps *caps = resolutionToCaps(resolution); +#if CAMERABIN_DEBUG + qDebug() << Q_FUNC_INFO << "set viewfinder resolution" << resolution << gst_caps_to_string(caps); +#endif g_object_set(m_camerabin, VIEWFINDER_CAPS_PROPERTY, caps, NULL); gst_caps_unref(caps); + } else { + g_object_set(m_camerabin, VIEWFINDER_CAPS_PROPERTY, NULL, NULL); } } diff --git a/src/plugins/gstreamer/camerabin/camerabinsession.h b/src/plugins/gstreamer/camerabin/camerabinsession.h index dab8d84c..fe419c12 100644 --- a/src/plugins/gstreamer/camerabin/camerabinsession.h +++ b/src/plugins/gstreamer/camerabin/camerabinsession.h @@ -74,6 +74,7 @@ class CameraBinZoom; class CameraBinCaptureDestination; class CameraBinCaptureBufferFormat; class QGstreamerVideoRendererInterface; +class CameraBinViewfinderSettings; class QGstreamerElementFactory { @@ -136,7 +137,7 @@ public: CameraBinImageProcessing *imageProcessingControl() const { return m_imageProcessingControl; } CameraBinCaptureDestination *captureDestinationControl() const { return m_captureDestinationControl; } CameraBinCaptureBufferFormat *captureBufferFormatControl() const { return m_captureBufferFormatControl; } - + CameraBinViewfinderSettings *viewfinderSettingsControl() const { return m_viewfinderSettingsControl; } CameraBinRecorder *recorderControl() const { return m_recorderControl; } CameraBinContainer *mediaContainerControl() const { return m_mediaContainerControl; } @@ -229,6 +230,7 @@ private: CameraBinImageProcessing *m_imageProcessingControl; CameraBinCaptureDestination *m_captureDestinationControl; CameraBinCaptureBufferFormat *m_captureBufferFormatControl; + CameraBinViewfinderSettings *m_viewfinderSettingsControl; QGstreamerBusHelper *m_busHelper; GstBus* m_bus; diff --git a/src/plugins/gstreamer/camerabin/camerabinviewfindersettings.cpp b/src/plugins/gstreamer/camerabin/camerabinviewfindersettings.cpp new file mode 100644 index 00000000..373dbee7 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinviewfindersettings.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Jolla Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt 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 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "camerabinviewfindersettings.h" + + +QT_BEGIN_NAMESPACE + +CameraBinViewfinderSettings::CameraBinViewfinderSettings(QObject *parent) + : QCameraViewfinderSettingsControl(parent) +{ +} + +CameraBinViewfinderSettings::~CameraBinViewfinderSettings() +{ +} + +bool CameraBinViewfinderSettings::isViewfinderParameterSupported(ViewfinderParameter parameter) const +{ + switch (parameter) { + case Resolution: + return true; + case PixelAspectRatio: + case MinimumFrameRate: + case MaximumFrameRate: + case PixelFormat: + case UserParameter: + return false; + } + return false; +} + +QVariant CameraBinViewfinderSettings::viewfinderParameter(ViewfinderParameter parameter) const +{ + switch (parameter) { + case Resolution: + return m_resolution; + case PixelAspectRatio: + case MinimumFrameRate: + case MaximumFrameRate: + case PixelFormat: + case UserParameter: + return QVariant(); + } + return false; +} + +void CameraBinViewfinderSettings::setViewfinderParameter(ViewfinderParameter parameter, const QVariant &value) +{ + switch (parameter) { + case Resolution: + m_resolution = value.toSize(); + case PixelAspectRatio: + case MinimumFrameRate: + case MaximumFrameRate: + case PixelFormat: + case UserParameter: + break; + } +} + +QSize CameraBinViewfinderSettings::resolution() const +{ + return m_resolution; +} + +QT_END_NAMESPACE diff --git a/src/plugins/gstreamer/camerabin/camerabinviewfindersettings.h b/src/plugins/gstreamer/camerabin/camerabinviewfindersettings.h new file mode 100644 index 00000000..835f532d --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinviewfindersettings.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Jolla Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt 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 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CAMERABINVIEWFINDERSETTINGS_H +#define CAMERABINVIEWFINDERSETTINGS_H + +#include + +#include + +QT_BEGIN_NAMESPACE + +class CameraBinViewfinderSettings : public QCameraViewfinderSettingsControl +{ + Q_OBJECT +public: + CameraBinViewfinderSettings(QObject *parent); + ~CameraBinViewfinderSettings(); + + bool isViewfinderParameterSupported(ViewfinderParameter parameter) const; + QVariant viewfinderParameter(ViewfinderParameter parameter) const; + void setViewfinderParameter(ViewfinderParameter parameter, const QVariant &value); + + QSize resolution() const; + +private: + QSize m_resolution; +}; + +QT_END_NAMESPACE + +#endif From 0be45b5669a54b5c26ea4dfb037f8bb515d57bff Mon Sep 17 00:00:00 2001 From: Andrew den Exter Date: Tue, 10 Dec 2013 14:59:38 +1000 Subject: [PATCH 03/11] Remove workaround for resolved gstreamer bug. Reloading the capture pipeline before recording is unnecessary and introduces a big pause. Don't do it. https://bugzilla.gnome.org/show_bug.cgi?id=649832 was resolved in gstreamer 0.10.23 Change-Id: I0de02af52dcc193bce7a4e3e61407ae1c2d3818c Reviewed-by: Yoann Lopes --- src/plugins/gstreamer/camerabin/camerabincontrol.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/gstreamer/camerabin/camerabincontrol.cpp b/src/plugins/gstreamer/camerabin/camerabincontrol.cpp index 8c63959c..c84ebc41 100644 --- a/src/plugins/gstreamer/camerabin/camerabincontrol.cpp +++ b/src/plugins/gstreamer/camerabin/camerabincontrol.cpp @@ -115,10 +115,11 @@ void CameraBinControl::setCaptureMode(QCamera::CaptureModes mode) captureMode() == QCamera::CaptureStillImage ? CamerabinResourcePolicy::ImageCaptureResources : CamerabinResourcePolicy::VideoCaptureResources); - +#if (GST_VERSION_MAJOR == 0) && ((GST_VERSION_MINOR < 10) || (GST_VERSION_MICRO < 23)) //due to bug in v4l2src, it's necessary to reload camera on video caps changes //https://bugzilla.gnome.org/show_bug.cgi?id=649832 reloadLater(); +#endif } emit captureModeChanged(mode); } From 4565cf26af8131a5c060598fd22e597f8aa9ba5d Mon Sep 17 00:00:00 2001 From: Andrew den Exter Date: Thu, 5 Dec 2013 17:30:04 +1000 Subject: [PATCH 04/11] Fix writing orientation and date exif tags in camerabin. Orientation tags need to be transformed from the string tag returned by gstreamer to the orientation in degrees. Date tags need to be inserted with gst_date_time_new_local_time. Finally setting a tag value shouldn't clear all other tags. Change-Id: I28922148251084c12cf6c93d9b097fa5df41da9d Reviewed-by: Yoann Lopes --- .../gstreamer/camerabin/camerabinmetadata.cpp | 48 ++++++++++++++++++- .../gstreamer/camerabin/camerabinsession.cpp | 19 ++++++-- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/src/plugins/gstreamer/camerabin/camerabinmetadata.cpp b/src/plugins/gstreamer/camerabin/camerabinmetadata.cpp index 353fd8be..1e55e9e8 100644 --- a/src/plugins/gstreamer/camerabin/camerabinmetadata.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinmetadata.cpp @@ -54,12 +54,43 @@ struct QGstreamerMetaDataKeyLookup const char *token; }; +static QVariant fromGStreamerOrientation(const QVariant &value) +{ + // Note gstreamer tokens either describe the counter clockwise rotation of the + // image or the clockwise transform to apply to correct the image. The orientation + // value returned is the clockwise rotation of the image. + const QString token = value.toString(); + if (token == QStringLiteral("rotate-90")) + return 270; + else if (token == QStringLiteral("rotate-180")) + return 180; + else if (token == QStringLiteral("rotate-270")) + return 90; + else + return 0; +} + +static QVariant toGStreamerOrientation(const QVariant &value) +{ + switch (value.toInt()) { + case 90: + return QStringLiteral("rotate-270"); + case 180: + return QStringLiteral("rotate-180"); + case 270: + return QStringLiteral("rotate-90"); + default: + return QStringLiteral("rotate-0"); + } +} + static const QGstreamerMetaDataKeyLookup qt_gstreamerMetaDataKeys[] = { { QMediaMetaData::Title, GST_TAG_TITLE }, //{ QMediaMetaData::SubTitle, 0 }, //{ QMediaMetaData::Author, 0 }, { QMediaMetaData::Comment, GST_TAG_COMMENT }, + { QMediaMetaData::Date, GST_TAG_DATE_TIME }, { QMediaMetaData::Description, GST_TAG_DESCRIPTION }, //{ QMediaMetaData::Category, 0 }, { QMediaMetaData::Genre, GST_TAG_GENRE }, @@ -120,7 +151,9 @@ static const QGstreamerMetaDataKeyLookup qt_gstreamerMetaDataKeys[] = //{ QMediaMetaData::CameraManufacturer, 0 }, //{ QMediaMetaData::CameraModel, 0 }, //{ QMediaMetaData::Event, 0 }, - //{ QMediaMetaData::Subject, 0 } + //{ QMediaMetaData::Subject, 0 }, + + { QMediaMetaData::Orientation, GST_TAG_IMAGE_ORIENTATION } }; CameraBinMetaData::CameraBinMetaData(QObject *parent) @@ -130,6 +163,10 @@ CameraBinMetaData::CameraBinMetaData(QObject *parent) QVariant CameraBinMetaData::metaData(const QString &key) const { + if (key == QMediaMetaData::Orientation) { + return fromGStreamerOrientation(m_values.value(QByteArray(GST_TAG_IMAGE_ORIENTATION))); + } + static const int count = sizeof(qt_gstreamerMetaDataKeys) / sizeof(QGstreamerMetaDataKeyLookup); for (int i = 0; i < count; ++i) { @@ -144,6 +181,15 @@ QVariant CameraBinMetaData::metaData(const QString &key) const void CameraBinMetaData::setMetaData(const QString &key, const QVariant &value) { + if (key == QMediaMetaData::Orientation) { + m_values.insert(QByteArray(GST_TAG_IMAGE_ORIENTATION), toGStreamerOrientation(value)); + + emit QMetaDataWriterControl::metaDataChanged(); + emit metaDataChanged(m_values); + + return; + } + static const int count = sizeof(qt_gstreamerMetaDataKeys) / sizeof(QGstreamerMetaDataKeyLookup); for (int i = 0; i < count; ++i) { diff --git a/src/plugins/gstreamer/camerabin/camerabinsession.cpp b/src/plugins/gstreamer/camerabin/camerabinsession.cpp index 63536d91..f85811e2 100644 --- a/src/plugins/gstreamer/camerabin/camerabinsession.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinsession.cpp @@ -76,6 +76,7 @@ #include #include +#include //#define CAMERABIN_DEBUG 1 //#define CAMERABIN_DEBUG_DUMP_BIN 1 @@ -737,7 +738,7 @@ void CameraBinSession::setMetaData(const QMap &data) switch(tagValue.type()) { case QVariant::String: gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE_ALL, + GST_TAG_MERGE_REPLACE, tagName.toUtf8().constData(), tagValue.toString().toUtf8().constData(), NULL); @@ -745,18 +746,29 @@ void CameraBinSession::setMetaData(const QMap &data) case QVariant::Int: case QVariant::LongLong: gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE_ALL, + GST_TAG_MERGE_REPLACE, tagName.toUtf8().constData(), tagValue.toInt(), NULL); break; case QVariant::Double: gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE_ALL, + GST_TAG_MERGE_REPLACE, tagName.toUtf8().constData(), tagValue.toDouble(), NULL); break; + case QVariant::DateTime: { + QDateTime date = tagValue.toDateTime().toLocalTime(); + gst_tag_setter_add_tags(GST_TAG_SETTER(element), + GST_TAG_MERGE_REPLACE, + tagName.toUtf8().constData(), + gst_date_time_new_local_time( + date.date().year(), date.date().month(), date.date().day(), + date.time().hour(), date.time().minute(), date.time().second()), + NULL); + break; + } default: break; } @@ -932,6 +944,7 @@ bool CameraBinSession::processBusMessage(const QGstreamerMessage &message) emit stateChanged(m_state = QCamera::UnloadedState); break; case GST_STATE_READY: + setMetaData(m_metaData); if (m_state != QCamera::LoadedState) emit stateChanged(m_state = QCamera::LoadedState); break; From dda1bb47163a39e07ba559d16684b9891193cc85 Mon Sep 17 00:00:00 2001 From: Andrew den Exter Date: Tue, 10 Dec 2013 17:20:59 +1000 Subject: [PATCH 05/11] Fix incorrect/missing application of recording settings in camerabin. Don't set profiles if no settings are specified. Apply all settings before starting a pipeline as the mode can switch without being restarted and incompatible video recording settings can prevent the pipeline starting even in image capture mode. Set audio encoding settings and encoder profiles if they are supplied. Change-Id: I06febf977c2cae306383f9dbaae0f81f531b4757 Reviewed-by: Yoann Lopes --- .../camerabin/camerabinaudioencoder.cpp | 9 ++-- .../gstreamer/camerabin/camerabinrecorder.cpp | 6 ++- .../gstreamer/camerabin/camerabinsession.cpp | 41 +++++++++++++++---- .../gstreamer/camerabin/camerabinsession.h | 1 + .../camerabin/camerabinvideoencoder.cpp | 15 +++++-- 5 files changed, 55 insertions(+), 17 deletions(-) diff --git a/src/plugins/gstreamer/camerabin/camerabinaudioencoder.cpp b/src/plugins/gstreamer/camerabin/camerabinaudioencoder.cpp index 78750f03..0fa854cc 100644 --- a/src/plugins/gstreamer/camerabin/camerabinaudioencoder.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinaudioencoder.cpp @@ -106,18 +106,19 @@ void CameraBinAudioEncoder::resetActualSettings() GstEncodingProfile *CameraBinAudioEncoder::createProfile() { QString codec = m_actualAudioSettings.codec(); + QString preset = m_actualAudioSettings.encodingOption(QStringLiteral("preset")).toString(); GstCaps *caps; if (codec.isEmpty()) - caps = gst_caps_new_any(); + return 0; else caps = gst_caps_from_string(codec.toLatin1()); return (GstEncodingProfile *)gst_encoding_audio_profile_new( caps, - NULL, //preset - NULL, //restriction - 0); //presence + !preset.isEmpty() ? preset.toLatin1().constData() : NULL, //preset + NULL, //restriction + 0); //presence } QT_END_NAMESPACE diff --git a/src/plugins/gstreamer/camerabin/camerabinrecorder.cpp b/src/plugins/gstreamer/camerabin/camerabinrecorder.cpp index c8967dfb..4ac0d942 100644 --- a/src/plugins/gstreamer/camerabin/camerabinrecorder.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinrecorder.cpp @@ -191,8 +191,10 @@ GstEncodingContainerProfile *CameraBinRecorder::videoProfile() GstEncodingProfile *audioProfile = m_session->audioEncodeControl()->createProfile(); GstEncodingProfile *videoProfile = m_session->videoEncodeControl()->createProfile(); - gst_encoding_container_profile_add_profile(containerProfile, audioProfile); - gst_encoding_container_profile_add_profile(containerProfile, videoProfile); + if (audioProfile) + gst_encoding_container_profile_add_profile(containerProfile, audioProfile); + if (videoProfile) + gst_encoding_container_profile_add_profile(containerProfile, videoProfile); } return containerProfile; diff --git a/src/plugins/gstreamer/camerabin/camerabinsession.cpp b/src/plugins/gstreamer/camerabin/camerabinsession.cpp index f85811e2..70ed3e23 100644 --- a/src/plugins/gstreamer/camerabin/camerabinsession.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinsession.cpp @@ -94,6 +94,7 @@ #define SUPPORTED_IMAGE_CAPTURE_CAPS_PROPERTY "image-capture-supported-caps" #define SUPPORTED_VIDEO_CAPTURE_CAPS_PROPERTY "video-capture-supported-caps" #define SUPPORTED_VIEWFINDER_CAPS_PROPERTY "viewfinder-supported-caps" +#define AUDIO_CAPTURE_CAPS_PROPERTY "audio-capture-caps" #define IMAGE_CAPTURE_CAPS_PROPERTY "image-capture-caps" #define VIDEO_CAPTURE_CAPS_PROPERTY "video-capture-caps" #define VIEWFINDER_CAPS_PROPERTY "viewfinder-caps" @@ -346,6 +347,32 @@ void CameraBinSession::setupCaptureResolution() } } +void CameraBinSession::setAudioCaptureCaps() +{ + QAudioEncoderSettings settings = m_audioEncodeControl->audioSettings(); + const int sampleRate = settings.sampleRate(); + const int channelCount = settings.channelCount(); + + if (sampleRate == -1 && channelCount == -1) + return; + + GstStructure *structure = gst_structure_new( + "audio/x-raw-int", + "endianness", G_TYPE_INT, 1234, + "signed", G_TYPE_BOOLEAN, TRUE, + "width", G_TYPE_INT, 16, + "depth", G_TYPE_INT, 16, + NULL); + if (sampleRate != -1) + gst_structure_set(structure, "rate", G_TYPE_INT, sampleRate, NULL); + if (channelCount != -1) + gst_structure_set(structure, "channels", G_TYPE_INT, channelCount, NULL); + + GstCaps *caps = gst_caps_new_full(structure, NULL); + g_object_set(G_OBJECT(m_camerabin), AUDIO_CAPTURE_CAPS_PROPERTY, caps, NULL); + gst_caps_unref(caps); +} + GstElement *CameraBinSession::buildCameraSource() { #if CAMERABIN_DEBUG @@ -651,14 +678,14 @@ void CameraBinSession::setState(QCamera::State newState) GstState pending = GST_STATE_NULL; gst_element_get_state(m_camerabin, &binState, &pending, 0); - if (captureMode() == QCamera::CaptureVideo) { - m_recorderControl->applySettings(); + m_recorderControl->applySettings(); - g_object_set (G_OBJECT(m_camerabin), - "video-profile", - m_recorderControl->videoProfile(), - NULL); - } + g_object_set (G_OBJECT(m_camerabin), + "video-profile", + m_recorderControl->videoProfile(), + NULL); + + setAudioCaptureCaps(); setupCaptureResolution(); diff --git a/src/plugins/gstreamer/camerabin/camerabinsession.h b/src/plugins/gstreamer/camerabin/camerabinsession.h index fe419c12..3332f4c7 100644 --- a/src/plugins/gstreamer/camerabin/camerabinsession.h +++ b/src/plugins/gstreamer/camerabin/camerabinsession.h @@ -193,6 +193,7 @@ private slots: private: bool setupCameraBin(); void setupCaptureResolution(); + void setAudioCaptureCaps(); static void updateBusyStatus(GObject *o, GParamSpec *p, gpointer d); QUrl m_sink; diff --git a/src/plugins/gstreamer/camerabin/camerabinvideoencoder.cpp b/src/plugins/gstreamer/camerabin/camerabinvideoencoder.cpp index 47a61c9a..cb479d8d 100644 --- a/src/plugins/gstreamer/camerabin/camerabinvideoencoder.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinvideoencoder.cpp @@ -160,18 +160,25 @@ QPair CameraBinVideoEncoder::rateAsRational(qreal frameRate) const GstEncodingProfile *CameraBinVideoEncoder::createProfile() { QString codec = m_actualVideoSettings.codec(); + QString preset = m_actualVideoSettings.encodingOption(QStringLiteral("preset")).toString(); + GstCaps *caps; if (codec.isEmpty()) - caps = gst_caps_new_any(); + caps = 0; else caps = gst_caps_from_string(codec.toLatin1()); - return (GstEncodingProfile *)gst_encoding_video_profile_new( + GstEncodingVideoProfile *profile = gst_encoding_video_profile_new( caps, - NULL, //preset + !preset.isEmpty() ? preset.toLatin1().constData() : NULL, //preset NULL, //restriction - 0); //presence + 1); //presence + + gst_encoding_video_profile_set_pass(profile, 0); + gst_encoding_video_profile_set_variableframerate(profile, TRUE); + + return (GstEncodingProfile *)profile; } QT_END_NAMESPACE From a52f552d4274eeaeb3a7362c509e77df501b1349 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Tue, 14 Jan 2014 18:42:09 +0100 Subject: [PATCH 06/11] Android: optimized NV21 top ARGB32 conversion. Now based on fixed-point arithmetic and uses pre-generated lookup tables for YUV coefficients and RGB clamping. The new implementation is on average 2x-3x faster than the previous one (tested on the Samsung Galaxy S4). Change-Id: I1daf12f7f9b2f2334e90e0ede79e6d83800f1db6 Reviewed-by: Denis Kormalev Reviewed-by: Christian Stromme --- .../src/common/qandroidmultimediautils.cpp | 565 +++++++++++++++++- 1 file changed, 550 insertions(+), 15 deletions(-) diff --git a/src/plugins/android/src/common/qandroidmultimediautils.cpp b/src/plugins/android/src/common/qandroidmultimediautils.cpp index 9bf38a86..230dfe65 100644 --- a/src/plugins/android/src/common/qandroidmultimediautils.cpp +++ b/src/plugins/android/src/common/qandroidmultimediautils.cpp @@ -76,28 +76,563 @@ bool qt_sizeLessThan(const QSize &s1, const QSize &s2) return s1.width() * s1.height() < s2.width() * s2.height(); } +// Pre-computed Y coefficients for all possible y values (0-255). Stored as fixed-point (16:16) +// Y = 1.164 * (y - 16) +static const int coefficientsY[256] = { + -593888, -555746, -517604, -479462, -441320, -403178, -365036, -326894, -288752, -250610, + -212468, -174326, -136184, -98042, -59900, -21758, 16384, 54526, 92668, 130810, + 168952, 207094, 245236, 283378, 321520, 359662, 397804, 435946, 474088, 512230, + 550372, 588514, 626656, 664798, 702940, 741082, 779224, 817366, 855508, 893650, + 931792, 969934, 1008076, 1046218, 1084360, 1122502, 1160644, 1198786, 1236928, 1275070, + 1313212, 1351354, 1389496, 1427638, 1465780, 1503922, 1542064, 1580206, 1618348, 1656490, + 1694632, 1732774, 1770916, 1809058, 1847200, 1885342, 1923484, 1961626, 1999768, 2037910, + 2076052, 2114194, 2152336, 2190478, 2228620, 2266762, 2304904, 2343046, 2381188, 2419330, + 2457472, 2495614, 2533756, 2571898, 2610040, 2648182, 2686324, 2724466, 2762608, 2800750, + 2838892, 2877034, 2915176, 2953318, 2991460, 3029602, 3067744, 3105886, 3144028, 3182170, + 3220312, 3258454, 3296596, 3334738, 3372880, 3411022, 3449164, 3487306, 3525448, 3563590, + 3601732, 3639874, 3678016, 3716158, 3754300, 3792442, 3830584, 3868726, 3906868, 3945010, + 3983152, 4021294, 4059436, 4097578, 4135720, 4173862, 4212004, 4250146, 4288288, 4326430, + 4364572, 4402714, 4440856, 4478998, 4517140, 4555282, 4593424, 4631566, 4669708, 4707850, + 4745992, 4784134, 4822276, 4860418, 4898560, 4936702, 4974844, 5012986, 5051128, 5089270, + 5127412, 5165554, 5203696, 5241838, 5279980, 5318122, 5356264, 5394406, 5432548, 5470690, + 5508832, 5546974, 5585116, 5623258, 5661400, 5699542, 5737684, 5775826, 5813968, 5852110, + 5890252, 5928394, 5966536, 6004678, 6042820, 6080962, 6119104, 6157246, 6195388, 6233530, + 6271672, 6309814, 6347956, 6386098, 6424240, 6462382, 6500524, 6538666, 6576808, 6614950, + 6653092, 6691234, 6729376, 6767518, 6805660, 6843802, 6881944, 6920086, 6958228, 6996370, + 7034512, 7072654, 7110796, 7148938, 7187080, 7225222, 7263364, 7301506, 7339648, 7377790, + 7415932, 7454074, 7492216, 7530358, 7568500, 7606642, 7644784, 7682926, 7721068, 7759210, + 7797352, 7835494, 7873636, 7911778, 7949920, 7988062, 8026204, 8064346, 8102488, 8140630, + 8178772, 8216914, 8255056, 8293198, 8331340, 8369482, 8407624, 8445766, 8483908, 8522050, + 8560192, 8598334, 8636476, 8674618, 8712760, 8750902, 8789044, 8827186, 8865328, 8903470, + 8941612, 8979754, 9017896, 9056038, 9094180, 9132322 +}; + +// V lookup table for the Red component. Stored as fixed-point (16:16). +// V = 1.596 * (v - 128) +static const int coefficientsRV[256] = { + -6694144, -6641846, -6589548, -6537250, -6484952, -6432654, -6380356, -6328058, -6275760, + -6223462, -6171164, -6118866, -6066568, -6014270, -5961972, -5909674, -5857376, -5805078, + -5752780, -5700482, -5648184, -5595886, -5543588, -5491290, -5438992, -5386694, -5334396, + -5282098, -5229800, -5177502, -5125204, -5072906, -5020608, -4968310, -4916012, -4863714, + -4811416, -4759118, -4706820, -4654522, -4602224, -4549926, -4497628, -4445330, -4393032, + -4340734, -4288436, -4236138, -4183840, -4131542, -4079244, -4026946, -3974648, -3922350, + -3870052, -3817754, -3765456, -3713158, -3660860, -3608562, -3556264, -3503966, -3451668, + -3399370, -3347072, -3294774, -3242476, -3190178, -3137880, -3085582, -3033284, -2980986, + -2928688, -2876390, -2824092, -2771794, -2719496, -2667198, -2614900, -2562602, -2510304, + -2458006, -2405708, -2353410, -2301112, -2248814, -2196516, -2144218, -2091920, -2039622, + -1987324, -1935026, -1882728, -1830430, -1778132, -1725834, -1673536, -1621238, -1568940, + -1516642, -1464344, -1412046, -1359748, -1307450, -1255152, -1202854, -1150556, -1098258, + -1045960, -993662, -941364, -889066, -836768, -784470, -732172, -679874, -627576, + -575278, -522980, -470682, -418384, -366086, -313788, -261490, -209192, -156894, + -104596, -52298, 0, 52298, 104596, 156894, 209192, 261490, 313788, + 366086, 418384, 470682, 522980, 575278, 627576, 679874, 732172, 784470, + 836768, 889066, 941364, 993662, 1045960, 1098258, 1150556, 1202854, 1255152, + 1307450, 1359748, 1412046, 1464344, 1516642, 1568940, 1621238, 1673536, 1725834, + 1778132, 1830430, 1882728, 1935026, 1987324, 2039622, 2091920, 2144218, 2196516, + 2248814, 2301112, 2353410, 2405708, 2458006, 2510304, 2562602, 2614900, 2667198, + 2719496, 2771794, 2824092, 2876390, 2928688, 2980986, 3033284, 3085582, 3137880, + 3190178, 3242476, 3294774, 3347072, 3399370, 3451668, 3503966, 3556264, 3608562, + 3660860, 3713158, 3765456, 3817754, 3870052, 3922350, 3974648, 4026946, 4079244, + 4131542, 4183840, 4236138, 4288436, 4340734, 4393032, 4445330, 4497628, 4549926, + 4602224, 4654522, 4706820, 4759118, 4811416, 4863714, 4916012, 4968310, 5020608, + 5072906, 5125204, 5177502, 5229800, 5282098, 5334396, 5386694, 5438992, 5491290, + 5543588, 5595886, 5648184, 5700482, 5752780, 5805078, 5857376, 5909674, 5961972, + 6014270, 6066568, 6118866, 6171164, 6223462, 6275760, 6328058, 6380356, 6432654, + 6484952, 6537250, 6589548, 6641846 +}; + +// U lookup table for the Green component. Stored as fixed-point (16:16). +// U = 0.391 * (u - 128) +static const int coefficientsGU[256] = { + 1639936, 1627124, 1614312, 1601500, 1588688, 1575876, 1563064, 1550252, 1537440, + 1524628, 1511816, 1499004, 1486192, 1473380, 1460568, 1447756, 1434944, 1422132, + 1409320, 1396508, 1383696, 1370884, 1358072, 1345260, 1332448, 1319636, 1306824, + 1294012, 1281200, 1268388, 1255576, 1242764, 1229952, 1217140, 1204328, 1191516, + 1178704, 1165892, 1153080, 1140268, 1127456, 1114644, 1101832, 1089020, 1076208, + 1063396, 1050584, 1037772, 1024960, 1012148, 999336, 986524, 973712, 960900, + 948088, 935276, 922464, 909652, 896840, 884028, 871216, 858404, 845592, + 832780, 819968, 807156, 794344, 781532, 768720, 755908, 743096, 730284, + 717472, 704660, 691848, 679036, 666224, 653412, 640600, 627788, 614976, + 602164, 589352, 576540, 563728, 550916, 538104, 525292, 512480, 499668, + 486856, 474044, 461232, 448420, 435608, 422796, 409984, 397172, 384360, + 371548, 358736, 345924, 333112, 320300, 307488, 294676, 281864, 269052, + 256240, 243428, 230616, 217804, 204992, 192180, 179368, 166556, 153744, + 140932, 128120, 115308, 102496, 89684, 76872, 64060, 51248, 38436, + 25624, 12812, 0, -12812, -25624, -38436, -51248, -64060, -76872, + -89684, -102496, -115308, -128120, -140932, -153744, -166556, -179368, -192180, + -204992, -217804, -230616, -243428, -256240, -269052, -281864, -294676, -307488, + -320300, -333112, -345924, -358736, -371548, -384360, -397172, -409984, -422796, + -435608, -448420, -461232, -474044, -486856, -499668, -512480, -525292, -538104, + -550916, -563728, -576540, -589352, -602164, -614976, -627788, -640600, -653412, + -666224, -679036, -691848, -704660, -717472, -730284, -743096, -755908, -768720, + -781532, -794344, -807156, -819968, -832780, -845592, -858404, -871216, -884028, + -896840, -909652, -922464, -935276, -948088, -960900, -973712, -986524, -999336, + -1012148, -1024960, -1037772, -1050584, -1063396, -1076208, -1089020, -1101832, -1114644, + -1127456, -1140268, -1153080, -1165892, -1178704, -1191516, -1204328, -1217140, -1229952, + -1242764, -1255576, -1268388, -1281200, -1294012, -1306824, -1319636, -1332448, -1345260, + -1358072, -1370884, -1383696, -1396508, -1409320, -1422132, -1434944, -1447756, -1460568, + -1473380, -1486192, -1499004, -1511816, -1524628, -1537440, -1550252, -1563064, -1575876, + -1588688, -1601500, -1614312, -1627124 +}; + +// V lookup table for the Green component. Stored as fixed-point (16:16). +// V = 0.813 * (v - 128) +static const int coefficientsGV[256] = { + 3409920, 3383280, 3356640, 3330000, 3303360, 3276720, 3250080, 3223440, 3196800, + 3170160, 3143520, 3116880, 3090240, 3063600, 3036960, 3010320, 2983680, 2957040, + 2930400, 2903760, 2877120, 2850480, 2823840, 2797200, 2770560, 2743920, 2717280, + 2690640, 2664000, 2637360, 2610720, 2584080, 2557440, 2530800, 2504160, 2477520, + 2450880, 2424240, 2397600, 2370960, 2344320, 2317680, 2291040, 2264400, 2237760, + 2211120, 2184480, 2157840, 2131200, 2104560, 2077920, 2051280, 2024640, 1998000, + 1971360, 1944720, 1918080, 1891440, 1864800, 1838160, 1811520, 1784880, 1758240, + 1731600, 1704960, 1678320, 1651680, 1625040, 1598400, 1571760, 1545120, 1518480, + 1491840, 1465200, 1438560, 1411920, 1385280, 1358640, 1332000, 1305360, 1278720, + 1252080, 1225440, 1198800, 1172160, 1145520, 1118880, 1092240, 1065600, 1038960, + 1012320, 985680, 959040, 932400, 905760, 879120, 852480, 825840, 799200, + 772560, 745920, 719280, 692640, 666000, 639360, 612720, 586080, 559440, + 532800, 506160, 479520, 452880, 426240, 399600, 372960, 346320, 319680, + 293040, 266400, 239760, 213120, 186480, 159840, 133200, 106560, 79920, + 53280, 26640, 0, -26640, -53280, -79920, -106560, -133200, -159840, + -186480, -213120, -239760, -266400, -293040, -319680, -346320, -372960, -399600, + -426240, -452880, -479520, -506160, -532800, -559440, -586080, -612720, -639360, + -666000, -692640, -719280, -745920, -772560, -799200, -825840, -852480, -879120, + -905760, -932400, -959040, -985680, -1012320, -1038960, -1065600, -1092240, -1118880, + -1145520, -1172160, -1198800, -1225440, -1252080, -1278720, -1305360, -1332000, -1358640, + -1385280, -1411920, -1438560, -1465200, -1491840, -1518480, -1545120, -1571760, -1598400, + -1625040, -1651680, -1678320, -1704960, -1731600, -1758240, -1784880, -1811520, -1838160, + -1864800, -1891440, -1918080, -1944720, -1971360, -1998000, -2024640, -2051280, -2077920, + -2104560, -2131200, -2157840, -2184480, -2211120, -2237760, -2264400, -2291040, -2317680, + -2344320, -2370960, -2397600, -2424240, -2450880, -2477520, -2504160, -2530800, -2557440, + -2584080, -2610720, -2637360, -2664000, -2690640, -2717280, -2743920, -2770560, -2797200, + -2823840, -2850480, -2877120, -2903760, -2930400, -2957040, -2983680, -3010320, -3036960, + -3063600, -3090240, -3116880, -3143520, -3170160, -3196800, -3223440, -3250080, -3276720, + -3303360, -3330000, -3356640, -3383280 +}; + +// U lookup table for the Blue component. Stored as fixed-point (16:16). +// U = 2.018 * (u - 128) +static const int coefficientsBU[256] = { + -8464128, -8398002, -8331876, -8265750, -8199624, -8133498, -8067372, -8001246, -7935120, + -7868994, -7802868, -7736742, -7670616, -7604490, -7538364, -7472238, -7406112, -7339986, + -7273860, -7207734, -7141608, -7075482, -7009356, -6943230, -6877104, -6810978, -6744852, + -6678726, -6612600, -6546474, -6480348, -6414222, -6348096, -6281970, -6215844, -6149718, + -6083592, -6017466, -5951340, -5885214, -5819088, -5752962, -5686836, -5620710, -5554584, + -5488458, -5422332, -5356206, -5290080, -5223954, -5157828, -5091702, -5025576, -4959450, + -4893324, -4827198, -4761072, -4694946, -4628820, -4562694, -4496568, -4430442, -4364316, + -4298190, -4232064, -4165938, -4099812, -4033686, -3967560, -3901434, -3835308, -3769182, + -3703056, -3636930, -3570804, -3504678, -3438552, -3372426, -3306300, -3240174, -3174048, + -3107922, -3041796, -2975670, -2909544, -2843418, -2777292, -2711166, -2645040, -2578914, + -2512788, -2446662, -2380536, -2314410, -2248284, -2182158, -2116032, -2049906, -1983780, + -1917654, -1851528, -1785402, -1719276, -1653150, -1587024, -1520898, -1454772, -1388646, + -1322520, -1256394, -1190268, -1124142, -1058016, -991890, -925764, -859638, -793512, + -727386, -661260, -595134, -529008, -462882, -396756, -330630, -264504, -198378, + -132252, -66126, 0, 66126, 132252, 198378, 264504, 330630, 396756, + 462882, 529008, 595134, 661260, 727386, 793512, 859638, 925764, 991890, + 1058016, 1124142, 1190268, 1256394, 1322520, 1388646, 1454772, 1520898, 1587024, + 1653150, 1719276, 1785402, 1851528, 1917654, 1983780, 2049906, 2116032, 2182158, + 2248284, 2314410, 2380536, 2446662, 2512788, 2578914, 2645040, 2711166, 2777292, + 2843418, 2909544, 2975670, 3041796, 3107922, 3174048, 3240174, 3306300, 3372426, + 3438552, 3504678, 3570804, 3636930, 3703056, 3769182, 3835308, 3901434, 3967560, + 4033686, 4099812, 4165938, 4232064, 4298190, 4364316, 4430442, 4496568, 4562694, + 4628820, 4694946, 4761072, 4827198, 4893324, 4959450, 5025576, 5091702, 5157828, + 5223954, 5290080, 5356206, 5422332, 5488458, 5554584, 5620710, 5686836, 5752962, + 5819088, 5885214, 5951340, 6017466, 6083592, 6149718, 6215844, 6281970, 6348096, + 6414222, 6480348, 6546474, 6612600, 6678726, 6744852, 6810978, 6877104, 6943230, + 7009356, 7075482, 7141608, 7207734, 7273860, 7339986, 7406112, 7472238, 7538364, + 7604490, 7670616, 7736742, 7802868, 7868994, 7935120, 8001246, 8067372, 8133498, + 8199624, 8265750, 8331876, 8398002 +}; + +// R = min(max(r, 0), 255) << 16 +// where 'r' is the converted red component from YUV, which is always in the range -320 <= r < 704 +// and needs to be clamped to 0-255. It also precomputes the bitshift needed to create an RGB value. +static const quint32 _clampedR[1024] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 65536, 131072, 196608, 262144, 327680, 393216, 458752, + 524288, 589824, 655360, 720896, 786432, 851968, 917504, 983040, + 1048576, 1114112, 1179648, 1245184, 1310720, 1376256, 1441792, 1507328, + 1572864, 1638400, 1703936, 1769472, 1835008, 1900544, 1966080, 2031616, + 2097152, 2162688, 2228224, 2293760, 2359296, 2424832, 2490368, 2555904, + 2621440, 2686976, 2752512, 2818048, 2883584, 2949120, 3014656, 3080192, + 3145728, 3211264, 3276800, 3342336, 3407872, 3473408, 3538944, 3604480, + 3670016, 3735552, 3801088, 3866624, 3932160, 3997696, 4063232, 4128768, + 4194304, 4259840, 4325376, 4390912, 4456448, 4521984, 4587520, 4653056, + 4718592, 4784128, 4849664, 4915200, 4980736, 5046272, 5111808, 5177344, + 5242880, 5308416, 5373952, 5439488, 5505024, 5570560, 5636096, 5701632, + 5767168, 5832704, 5898240, 5963776, 6029312, 6094848, 6160384, 6225920, + 6291456, 6356992, 6422528, 6488064, 6553600, 6619136, 6684672, 6750208, + 6815744, 6881280, 6946816, 7012352, 7077888, 7143424, 7208960, 7274496, + 7340032, 7405568, 7471104, 7536640, 7602176, 7667712, 7733248, 7798784, + 7864320, 7929856, 7995392, 8060928, 8126464, 8192000, 8257536, 8323072, + 8388608, 8454144, 8519680, 8585216, 8650752, 8716288, 8781824, 8847360, + 8912896, 8978432, 9043968, 9109504, 9175040, 9240576, 9306112, 9371648, + 9437184, 9502720, 9568256, 9633792, 9699328, 9764864, 9830400, 9895936, + 9961472, 10027008, 10092544, 10158080, 10223616, 10289152, 10354688, 10420224, + 10485760, 10551296, 10616832, 10682368, 10747904, 10813440, 10878976, 10944512, + 11010048, 11075584, 11141120, 11206656, 11272192, 11337728, 11403264, 11468800, + 11534336, 11599872, 11665408, 11730944, 11796480, 11862016, 11927552, 11993088, + 12058624, 12124160, 12189696, 12255232, 12320768, 12386304, 12451840, 12517376, + 12582912, 12648448, 12713984, 12779520, 12845056, 12910592, 12976128, 13041664, + 13107200, 13172736, 13238272, 13303808, 13369344, 13434880, 13500416, 13565952, + 13631488, 13697024, 13762560, 13828096, 13893632, 13959168, 14024704, 14090240, + 14155776, 14221312, 14286848, 14352384, 14417920, 14483456, 14548992, 14614528, + 14680064, 14745600, 14811136, 14876672, 14942208, 15007744, 15073280, 15138816, + 15204352, 15269888, 15335424, 15400960, 15466496, 15532032, 15597568, 15663104, + 15728640, 15794176, 15859712, 15925248, 15990784, 16056320, 16121856, 16187392, + 16252928, 16318464, 16384000, 16449536, 16515072, 16580608, 16646144, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, + 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680, 16711680 +}; + +// G = min(max(g, 0), 255) << 8 +// where 'g' is the converted green component from YUV, which is always in the range -320 <= r < 704 +// and needs to be clamped to 0-255. It also precomputes the bitshift needed to create an RGB value. +static const quint32 _clampedG[1024] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 256, 512, 768, 1024, 1280, 1536, 1792, + 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, + 4096, 4352, 4608, 4864, 5120, 5376, 5632, 5888, + 6144, 6400, 6656, 6912, 7168, 7424, 7680, 7936, + 8192, 8448, 8704, 8960, 9216, 9472, 9728, 9984, + 10240, 10496, 10752, 11008, 11264, 11520, 11776, 12032, + 12288, 12544, 12800, 13056, 13312, 13568, 13824, 14080, + 14336, 14592, 14848, 15104, 15360, 15616, 15872, 16128, + 16384, 16640, 16896, 17152, 17408, 17664, 17920, 18176, + 18432, 18688, 18944, 19200, 19456, 19712, 19968, 20224, + 20480, 20736, 20992, 21248, 21504, 21760, 22016, 22272, + 22528, 22784, 23040, 23296, 23552, 23808, 24064, 24320, + 24576, 24832, 25088, 25344, 25600, 25856, 26112, 26368, + 26624, 26880, 27136, 27392, 27648, 27904, 28160, 28416, + 28672, 28928, 29184, 29440, 29696, 29952, 30208, 30464, + 30720, 30976, 31232, 31488, 31744, 32000, 32256, 32512, + 32768, 33024, 33280, 33536, 33792, 34048, 34304, 34560, + 34816, 35072, 35328, 35584, 35840, 36096, 36352, 36608, + 36864, 37120, 37376, 37632, 37888, 38144, 38400, 38656, + 38912, 39168, 39424, 39680, 39936, 40192, 40448, 40704, + 40960, 41216, 41472, 41728, 41984, 42240, 42496, 42752, + 43008, 43264, 43520, 43776, 44032, 44288, 44544, 44800, + 45056, 45312, 45568, 45824, 46080, 46336, 46592, 46848, + 47104, 47360, 47616, 47872, 48128, 48384, 48640, 48896, + 49152, 49408, 49664, 49920, 50176, 50432, 50688, 50944, + 51200, 51456, 51712, 51968, 52224, 52480, 52736, 52992, + 53248, 53504, 53760, 54016, 54272, 54528, 54784, 55040, + 55296, 55552, 55808, 56064, 56320, 56576, 56832, 57088, + 57344, 57600, 57856, 58112, 58368, 58624, 58880, 59136, + 59392, 59648, 59904, 60160, 60416, 60672, 60928, 61184, + 61440, 61696, 61952, 62208, 62464, 62720, 62976, 63232, + 63488, 63744, 64000, 64256, 64512, 64768, 65024, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280, + 65280, 65280, 65280, 65280, 65280, 65280, 65280, 65280 +}; + +// B = min(max(b, 0), 255) +// where 'b' is the converted blue component from YUV, which is always in the range -320 <= r < 704 +static const quint32 _clampedB[1024] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 +}; + +static const quint32 *clampedR = _clampedR + 320; +static const quint32 *clampedG = _clampedG + 320; +static const quint32 *clampedB = _clampedB + 320; + +#define MAKE_RGB(r, g, b) 0xff000000 | clampedR[r] | clampedG[g] | clampedB[b] + void qt_convert_NV21_to_ARGB32(const uchar *yuv, quint32 *rgb, int width, int height) { - const int frameSize = width * height; + const uchar *y0 = yuv; + const uchar *y1 = yuv + width; + const uchar *vu = yuv + width * height; - int a = 0; - for (int i = 0, ci = 0; i < height; ++i, ci += 1) { - for (int j = 0, cj = 0; j < width; ++j, cj += 1) { - int y = (0xff & ((int) yuv[ci * width + cj])); - int v = (0xff & ((int) yuv[frameSize + (ci >> 1) * width + (cj & ~1) + 0])); - int u = (0xff & ((int) yuv[frameSize + (ci >> 1) * width + (cj & ~1) + 1])); - y = y < 16 ? 16 : y; + quint32 *rgb0 = rgb; + quint32 *rgb1 = rgb + width; - int r = (int) (1.164f * (y - 16) + 1.596f * (v - 128)); - int g = (int) (1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128)); - int b = (int) (1.164f * (y - 16) + 2.018f * (u - 128)); + for (int i = 0; i < height; i += 2) { + for (int j = 0; j < width; j += 2) { + int v = *vu++; + int u = *vu++; - r = qBound(0, r, 255); - g = qBound(0, g, 255); - b = qBound(0, b, 255); + int ruv = coefficientsRV[v] >> 15; + int guv = (coefficientsGU[u] + coefficientsGV[v]) >> 15; + int buv = coefficientsBU[u] >> 15; - rgb[a++] = 0xff000000 | (r << 16) | (g << 8) | b; + int y = coefficientsY[*y0++] >> 15; + int r = y + ruv; + int g = y + guv; + int b = y + buv; + *rgb0++ = MAKE_RGB(r, g, b); + + y = coefficientsY[*y0++] >> 15; + r = y + ruv; + g = y + guv; + b = y + buv; + *rgb0++ = MAKE_RGB(r, g, b); + + y = coefficientsY[*y1++] >> 15; + r = y + ruv; + g = y + guv; + b = y + buv; + *rgb1++ = MAKE_RGB(r, g, b); + + y = coefficientsY[*y1++] >> 15; + r = y + ruv; + g = y + guv; + b = y + buv; + *rgb1++ = MAKE_RGB(r, g, b); } + + rgb0 += width; + rgb1 += width; + y0 += width; + y1 += width; } } From 437db8df51c8ce11f6d856d1c0441572cf218e1f Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Wed, 15 Jan 2014 20:55:03 +0100 Subject: [PATCH 07/11] Android: fixes custom camera focus point. The custom focus point passed to the camera was in viewport orientation but should be in sensor orientation. The two might differ. Task-number: QTBUG-36208 Change-Id: Id272402090c2814e02abc527c0f6a6e932a60081 Reviewed-by: Christian Stromme --- .../qandroidcamerafocuscontrol.cpp | 43 ++++++++++++++++--- src/plugins/android/src/wrappers/jcamera.cpp | 8 ++++ src/plugins/android/src/wrappers/jcamera.h | 1 + 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.cpp index 345a2917..ccf02852 100644 --- a/src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.cpp +++ b/src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.cpp @@ -46,15 +46,36 @@ QT_BEGIN_NAMESPACE -static QRect adjustedArea(const QRectF &area) +static QPointF rotateNormalizedPoint(const QPointF &point, int rotation) +{ + const qreal one(1.0f); + + switch (rotation) { + case 0: + default: + return point; + case 90: + return QPointF(point.y(), one - point.x()); + case 180: + return QPointF(one - point.x(), one - point.y()); + case 270: + return QPointF(one - point.y(), point.x()); + } +} + +static QRect adjustedArea(const QRectF &area, int rotation) { // Qt maps focus points in the range (0.0, 0.0) -> (1.0, 1.0) // Android maps focus points in the range (-1000, -1000) -> (1000, 1000) // Converts an area in Qt coordinates to Android coordinates - return QRect(-1000 + qRound(area.x() * 2000), - -1000 + qRound(area.y() * 2000), - qRound(area.width() * 2000), - qRound(area.height() * 2000)) + // Applies 'rotation' in the counter-clockwise direction + QRectF rotated(rotateNormalizedPoint(area.topLeft(), rotation), + rotateNormalizedPoint(area.bottomRight(), rotation)); + + return QRect(-1000 + qRound(rotated.x() * 2000), + -1000 + qRound(rotated.y() * 2000), + qRound(rotated.width() * 2000), + qRound(rotated.height() * 2000)) .intersected(QRect(-1000, -1000, 2000, 2000)); } @@ -242,6 +263,9 @@ void QAndroidCameraFocusControl::updateFocusZones(QCameraFocusZone::FocusZoneSta if (!viewportSize.isValid()) return; + if (m_session->camera()->getDisplayOrientation() % 180) + viewportSize.transpose(); + QSizeF focusSize(50.f / viewportSize.width(), 50.f / viewportSize.height()); float x = qBound(qreal(0), m_actualFocusPoint.x() - (focusSize.width() / 2), @@ -264,8 +288,13 @@ void QAndroidCameraFocusControl::setCameraFocusArea() // in FocusPointAuto mode, leave the area list empty // to let the driver choose the focus point. - for (int i = 0; i < m_focusZones.size(); ++i) - areas.append(adjustedArea(m_focusZones.at(i).area())); + for (int i = 0; i < m_focusZones.size(); ++i) { + // The area passed to Android should be in sensor orientation. + // What we have in m_focusZones is in viewport orientation, so revert the rotation set + // on the viewport to get sensor coordinates. + areas.append(adjustedArea(m_focusZones.at(i).area(), + m_session->camera()->getDisplayOrientation())); + } } m_session->camera()->setFocusAreas(areas); diff --git a/src/plugins/android/src/wrappers/jcamera.cpp b/src/plugins/android/src/wrappers/jcamera.cpp index 3a0585f3..c880141a 100644 --- a/src/plugins/android/src/wrappers/jcamera.cpp +++ b/src/plugins/android/src/wrappers/jcamera.cpp @@ -231,6 +231,7 @@ class JCameraWorker : public QObject, public QJNIObjectPrivate QSize m_previewSize; int m_rotation; + int m_displayOrientation; bool m_hasAPI14; @@ -336,8 +337,14 @@ int JCamera::getNativeOrientation() return d->getNativeOrientation(); } +int JCamera::getDisplayOrientation() const +{ + return d->m_displayOrientation; +} + void JCamera::setDisplayOrientation(int degrees) { + d->m_displayOrientation = degrees; QMetaObject::invokeMethod(d, "setDisplayOrientation", Q_ARG(int, degrees)); } @@ -619,6 +626,7 @@ JCameraWorker::JCameraWorker(JCamera *camera, int cameraId, jobject cam, QThread , QJNIObjectPrivate(cam) , m_cameraId(cameraId) , m_rotation(0) + , m_displayOrientation(0) , m_hasAPI14(false) , m_parametersMutex(QMutex::Recursive) { diff --git a/src/plugins/android/src/wrappers/jcamera.h b/src/plugins/android/src/wrappers/jcamera.h index 535efe21..e9063f12 100644 --- a/src/plugins/android/src/wrappers/jcamera.h +++ b/src/plugins/android/src/wrappers/jcamera.h @@ -88,6 +88,7 @@ public: CameraFacing getFacing(); int getNativeOrientation(); + int getDisplayOrientation() const; void setDisplayOrientation(int degrees); QSize getPreferredPreviewSizeForVideo(); From 60fb11d9a2f5ff659a6ccffe01b4de16c1fb5929 Mon Sep 17 00:00:00 2001 From: Andrew den Exter Date: Thu, 5 Dec 2013 15:38:14 +1000 Subject: [PATCH 08/11] Allow plugins to override the QML VideoOutput type. Move QDeclarativeVideoOutput to the private QtMultimediaQuickTools library to make the QDeclarativeVideoOutputBackend interface implementable by a plugin. Change-Id: I763c483a1fc9ec56dc7b8be0bc71523f029a36ee Reviewed-by: Yoann Lopes --- src/imports/multimedia/multimedia.cpp | 3 ++- src/imports/multimedia/multimedia.pro | 13 ----------- .../qdeclarativevideooutput_backend_p.h | 12 +++++++++- .../qdeclarativevideooutput_p.h | 4 +++- .../qsgvideonode_p.h | 2 +- .../qdeclarativevideooutput.cpp | 23 ++++++++++++++++--- .../qdeclarativevideooutput_render.cpp | 0 .../qdeclarativevideooutput_render_p.h | 0 .../qdeclarativevideooutput_window.cpp | 0 .../qdeclarativevideooutput_window_p.h | 0 .../qsgvideonode_i420.cpp | 4 ++++ .../qsgvideonode_i420.h | 3 +++ .../qsgvideonode_rgb.cpp | 4 ++++ .../qsgvideonode_rgb.h | 3 +++ .../qsgvideonode_texture.cpp | 4 ++++ .../qsgvideonode_texture.h | 3 +++ .../qtmultimediaquicktools.pro | 18 +++++++++++++-- .../qdeclarativevideooutput.pro | 3 --- .../tst_qdeclarativevideooutput.cpp | 2 +- .../qdeclarativevideooutput_window.pro | 3 --- .../tst_qdeclarativevideooutput_window.cpp | 2 +- 21 files changed, 76 insertions(+), 30 deletions(-) rename src/{imports/multimedia => multimedia/qtmultimediaquicktools_headers}/qdeclarativevideooutput_backend_p.h (88%) rename src/{imports/multimedia => multimedia/qtmultimediaquicktools_headers}/qdeclarativevideooutput_p.h (97%) rename src/{imports/multimedia => qtmultimediaquicktools}/qdeclarativevideooutput.cpp (96%) rename src/{imports/multimedia => qtmultimediaquicktools}/qdeclarativevideooutput_render.cpp (100%) rename src/{imports/multimedia => qtmultimediaquicktools}/qdeclarativevideooutput_render_p.h (100%) rename src/{imports/multimedia => qtmultimediaquicktools}/qdeclarativevideooutput_window.cpp (100%) rename src/{imports/multimedia => qtmultimediaquicktools}/qdeclarativevideooutput_window_p.h (100%) rename src/{imports/multimedia => qtmultimediaquicktools}/qsgvideonode_i420.cpp (99%) rename src/{imports/multimedia => qtmultimediaquicktools}/qsgvideonode_i420.h (98%) rename src/{imports/multimedia => qtmultimediaquicktools}/qsgvideonode_rgb.cpp (99%) rename src/{imports/multimedia => qtmultimediaquicktools}/qsgvideonode_rgb.h (98%) rename src/{imports/multimedia => qtmultimediaquicktools}/qsgvideonode_texture.cpp (99%) rename src/{imports/multimedia => qtmultimediaquicktools}/qsgvideonode_texture.h (98%) diff --git a/src/imports/multimedia/multimedia.cpp b/src/imports/multimedia/multimedia.cpp index f05252f5..94b697e8 100644 --- a/src/imports/multimedia/multimedia.cpp +++ b/src/imports/multimedia/multimedia.cpp @@ -45,9 +45,10 @@ #include #include "qsoundeffect.h" +#include + #include "qdeclarativemediametadata_p.h" #include "qdeclarativeaudio_p.h" -#include "qdeclarativevideooutput_p.h" #include "qdeclarativeradio_p.h" #include "qdeclarativeradiodata_p.h" #include "qdeclarativecamera_p.h" diff --git a/src/imports/multimedia/multimedia.pro b/src/imports/multimedia/multimedia.pro index d738dd4f..f6fdfe9c 100644 --- a/src/imports/multimedia/multimedia.pro +++ b/src/imports/multimedia/multimedia.pro @@ -3,13 +3,6 @@ QT += qml quick network multimedia-private qtmultimediaquicktools-private HEADERS += \ qdeclarativeaudio_p.h \ qdeclarativemediametadata_p.h \ - qdeclarativevideooutput_p.h \ - qdeclarativevideooutput_backend_p.h \ - qdeclarativevideooutput_render_p.h \ - qdeclarativevideooutput_window_p.h \ - qsgvideonode_i420.h \ - qsgvideonode_rgb.h \ - qsgvideonode_texture.h \ qdeclarativeradio_p.h \ qdeclarativeradiodata_p.h \ qdeclarativecamera_p.h \ @@ -25,12 +18,6 @@ HEADERS += \ SOURCES += \ multimedia.cpp \ qdeclarativeaudio.cpp \ - qdeclarativevideooutput.cpp \ - qdeclarativevideooutput_render.cpp \ - qdeclarativevideooutput_window.cpp \ - qsgvideonode_i420.cpp \ - qsgvideonode_rgb.cpp \ - qsgvideonode_texture.cpp \ qdeclarativeradio.cpp \ qdeclarativeradiodata.cpp \ qdeclarativecamera.cpp \ diff --git a/src/imports/multimedia/qdeclarativevideooutput_backend_p.h b/src/multimedia/qtmultimediaquicktools_headers/qdeclarativevideooutput_backend_p.h similarity index 88% rename from src/imports/multimedia/qdeclarativevideooutput_backend_p.h rename to src/multimedia/qtmultimediaquicktools_headers/qdeclarativevideooutput_backend_p.h index f731b77f..f7235b51 100644 --- a/src/imports/multimedia/qdeclarativevideooutput_backend_p.h +++ b/src/multimedia/qtmultimediaquicktools_headers/qdeclarativevideooutput_backend_p.h @@ -47,6 +47,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -54,7 +55,7 @@ class QAbstractVideoSurface; class QDeclarativeVideoOutput; class QMediaService; -class QDeclarativeVideoBackend +class Q_MULTIMEDIAQUICK_EXPORT QDeclarativeVideoBackend { public: explicit QDeclarativeVideoBackend(QDeclarativeVideoOutput *parent) @@ -82,6 +83,15 @@ protected: QPointer m_service; }; +class QDeclarativeVideoBackendFactoryInterface +{ +public: + virtual QDeclarativeVideoBackend *create(QDeclarativeVideoOutput *parent) = 0; +}; + +#define QDeclarativeVideoBackendFactoryInterface_iid "org.qt-project.qt.declarativevideobackendfactory/5.2" +Q_DECLARE_INTERFACE(QDeclarativeVideoBackendFactoryInterface, QDeclarativeVideoBackendFactoryInterface_iid) + /* * Helper - returns true if the given orientation has the same aspect as the default (e.g. 180*n) */ diff --git a/src/imports/multimedia/qdeclarativevideooutput_p.h b/src/multimedia/qtmultimediaquicktools_headers/qdeclarativevideooutput_p.h similarity index 97% rename from src/imports/multimedia/qdeclarativevideooutput_p.h rename to src/multimedia/qtmultimediaquicktools_headers/qdeclarativevideooutput_p.h index 07fdb41e..2ca7c293 100644 --- a/src/imports/multimedia/qdeclarativevideooutput_p.h +++ b/src/multimedia/qtmultimediaquicktools_headers/qdeclarativevideooutput_p.h @@ -48,6 +48,8 @@ #include #include +#include + QT_BEGIN_NAMESPACE class QMediaObject; @@ -55,7 +57,7 @@ class QMediaService; class QDeclarativeVideoBackend; class QVideoOutputOrientationHandler; -class QDeclarativeVideoOutput : public QQuickItem +class Q_MULTIMEDIAQUICK_EXPORT QDeclarativeVideoOutput : public QQuickItem { Q_OBJECT Q_DISABLE_COPY(QDeclarativeVideoOutput) diff --git a/src/multimedia/qtmultimediaquicktools_headers/qsgvideonode_p.h b/src/multimedia/qtmultimediaquicktools_headers/qsgvideonode_p.h index 71230ca3..f8cca4fc 100644 --- a/src/multimedia/qtmultimediaquicktools_headers/qsgvideonode_p.h +++ b/src/multimedia/qtmultimediaquicktools_headers/qsgvideonode_p.h @@ -76,7 +76,7 @@ public: virtual QSGVideoNode *createNode(const QVideoSurfaceFormat &format) = 0; }; -#define QSGVideoNodeFactoryInterface_iid "org.qt-project.qt.sgvideonodefactory/5.0" +#define QSGVideoNodeFactoryInterface_iid "org.qt-project.qt.sgvideonodefactory/5.2" Q_DECLARE_INTERFACE(QSGVideoNodeFactoryInterface, QSGVideoNodeFactoryInterface_iid) class Q_MULTIMEDIAQUICK_EXPORT QSGVideoNodeFactoryPlugin : public QObject, public QSGVideoNodeFactoryInterface diff --git a/src/imports/multimedia/qdeclarativevideooutput.cpp b/src/qtmultimediaquicktools/qdeclarativevideooutput.cpp similarity index 96% rename from src/imports/multimedia/qdeclarativevideooutput.cpp rename to src/qtmultimediaquicktools/qdeclarativevideooutput.cpp index 6d6107e5..321fd5e8 100644 --- a/src/imports/multimedia/qdeclarativevideooutput.cpp +++ b/src/qtmultimediaquicktools/qdeclarativevideooutput.cpp @@ -46,6 +46,7 @@ #include #include #include +#include //#define DEBUG_VIDEOITEM @@ -211,12 +212,28 @@ void QDeclarativeVideoOutput::setSource(QObject *source) emit sourceChanged(); } +Q_GLOBAL_STATIC_WITH_ARGS(QMediaPluginLoader, videoBackendFactoryLoader, + (QDeclarativeVideoBackendFactoryInterface_iid, QLatin1String("video/declarativevideobackend"), Qt::CaseInsensitive)) + bool QDeclarativeVideoOutput::createBackend(QMediaService *service) { bool backendAvailable = false; - m_backend.reset(new QDeclarativeVideoRendererBackend(this)); - if (m_backend->init(service)) - backendAvailable = true; + + foreach (QObject *instance, videoBackendFactoryLoader()->instances(QLatin1String("declarativevideobackend"))) { + if (QDeclarativeVideoBackendFactoryInterface *plugin = qobject_cast(instance)) { + m_backend.reset(plugin->create(this)); + if (m_backend && m_backend->init(service)) { + backendAvailable = true; + break; + } + } + } + + if (!backendAvailable) { + m_backend.reset(new QDeclarativeVideoRendererBackend(this)); + if (m_backend->init(service)) + backendAvailable = true; + } // QDeclarativeVideoWindowBackend only works when there is a service with a QVideoWindowControl. // Without service, the QDeclarativeVideoRendererBackend should always work. diff --git a/src/imports/multimedia/qdeclarativevideooutput_render.cpp b/src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp similarity index 100% rename from src/imports/multimedia/qdeclarativevideooutput_render.cpp rename to src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp diff --git a/src/imports/multimedia/qdeclarativevideooutput_render_p.h b/src/qtmultimediaquicktools/qdeclarativevideooutput_render_p.h similarity index 100% rename from src/imports/multimedia/qdeclarativevideooutput_render_p.h rename to src/qtmultimediaquicktools/qdeclarativevideooutput_render_p.h diff --git a/src/imports/multimedia/qdeclarativevideooutput_window.cpp b/src/qtmultimediaquicktools/qdeclarativevideooutput_window.cpp similarity index 100% rename from src/imports/multimedia/qdeclarativevideooutput_window.cpp rename to src/qtmultimediaquicktools/qdeclarativevideooutput_window.cpp diff --git a/src/imports/multimedia/qdeclarativevideooutput_window_p.h b/src/qtmultimediaquicktools/qdeclarativevideooutput_window_p.h similarity index 100% rename from src/imports/multimedia/qdeclarativevideooutput_window_p.h rename to src/qtmultimediaquicktools/qdeclarativevideooutput_window_p.h diff --git a/src/imports/multimedia/qsgvideonode_i420.cpp b/src/qtmultimediaquicktools/qsgvideonode_i420.cpp similarity index 99% rename from src/imports/multimedia/qsgvideonode_i420.cpp rename to src/qtmultimediaquicktools/qsgvideonode_i420.cpp index f91fb5a0..d7eb0486 100644 --- a/src/imports/multimedia/qsgvideonode_i420.cpp +++ b/src/qtmultimediaquicktools/qsgvideonode_i420.cpp @@ -46,6 +46,8 @@ #include #include +QT_BEGIN_NAMESPACE + QList QSGVideoNodeFactory_I420::supportedPixelFormats( QAbstractVideoBuffer::HandleType handleType) const { @@ -324,3 +326,5 @@ void QSGVideoMaterialShader_YUV420::updateState(const RenderState &state, if (state.isMatrixDirty()) program()->setUniformValue(m_id_matrix, state.combinedMatrix()); } + +QT_END_NAMESPACE diff --git a/src/imports/multimedia/qsgvideonode_i420.h b/src/qtmultimediaquicktools/qsgvideonode_i420.h similarity index 98% rename from src/imports/multimedia/qsgvideonode_i420.h rename to src/qtmultimediaquicktools/qsgvideonode_i420.h index 96050abb..a9def551 100644 --- a/src/imports/multimedia/qsgvideonode_i420.h +++ b/src/qtmultimediaquicktools/qsgvideonode_i420.h @@ -45,6 +45,8 @@ #include #include +QT_BEGIN_NAMESPACE + class QSGVideoMaterial_YUV420; class QSGVideoNode_I420 : public QSGVideoNode { @@ -70,5 +72,6 @@ public: QSGVideoNode *createNode(const QVideoSurfaceFormat &format); }; +QT_END_NAMESPACE #endif // QSGVIDEONODE_I420_H diff --git a/src/imports/multimedia/qsgvideonode_rgb.cpp b/src/qtmultimediaquicktools/qsgvideonode_rgb.cpp similarity index 99% rename from src/imports/multimedia/qsgvideonode_rgb.cpp rename to src/qtmultimediaquicktools/qsgvideonode_rgb.cpp index b0fb7dcf..fbe60c9a 100644 --- a/src/imports/multimedia/qsgvideonode_rgb.cpp +++ b/src/qtmultimediaquicktools/qsgvideonode_rgb.cpp @@ -46,6 +46,8 @@ #include #include +QT_BEGIN_NAMESPACE + QList QSGVideoNodeFactory_RGB::supportedPixelFormats( QAbstractVideoBuffer::HandleType handleType) const { @@ -283,3 +285,5 @@ void QSGVideoMaterialShader_RGB::updateState(const RenderState &state, if (state.isMatrixDirty()) program()->setUniformValue(m_id_matrix, state.combinedMatrix()); } + +QT_END_NAMESPACE diff --git a/src/imports/multimedia/qsgvideonode_rgb.h b/src/qtmultimediaquicktools/qsgvideonode_rgb.h similarity index 98% rename from src/imports/multimedia/qsgvideonode_rgb.h rename to src/qtmultimediaquicktools/qsgvideonode_rgb.h index ffec4195..141a00e9 100644 --- a/src/imports/multimedia/qsgvideonode_rgb.h +++ b/src/qtmultimediaquicktools/qsgvideonode_rgb.h @@ -45,6 +45,8 @@ #include #include +QT_BEGIN_NAMESPACE + class QSGVideoMaterial_RGB; class QSGVideoNode_RGB : public QSGVideoNode @@ -70,5 +72,6 @@ public: QSGVideoNode *createNode(const QVideoSurfaceFormat &format); }; +QT_END_NAMESPACE #endif // QSGVIDEONODE_RGB_H diff --git a/src/imports/multimedia/qsgvideonode_texture.cpp b/src/qtmultimediaquicktools/qsgvideonode_texture.cpp similarity index 99% rename from src/imports/multimedia/qsgvideonode_texture.cpp rename to src/qtmultimediaquicktools/qsgvideonode_texture.cpp index e0d9737b..2320387b 100644 --- a/src/imports/multimedia/qsgvideonode_texture.cpp +++ b/src/qtmultimediaquicktools/qsgvideonode_texture.cpp @@ -46,6 +46,8 @@ #include #include +QT_BEGIN_NAMESPACE + QList QSGVideoNodeFactory_Texture::supportedPixelFormats( QAbstractVideoBuffer::HandleType handleType) const { @@ -265,3 +267,5 @@ void QSGVideoMaterialShader_Texture::updateState(const RenderState &state, if (state.isMatrixDirty()) program()->setUniformValue(m_id_matrix, state.combinedMatrix()); } + +QT_END_NAMESPACE diff --git a/src/imports/multimedia/qsgvideonode_texture.h b/src/qtmultimediaquicktools/qsgvideonode_texture.h similarity index 98% rename from src/imports/multimedia/qsgvideonode_texture.h rename to src/qtmultimediaquicktools/qsgvideonode_texture.h index 8d369ebc..42dadbcb 100644 --- a/src/imports/multimedia/qsgvideonode_texture.h +++ b/src/qtmultimediaquicktools/qsgvideonode_texture.h @@ -45,6 +45,8 @@ #include #include +QT_BEGIN_NAMESPACE + class QSGVideoMaterial_Texture; class QSGVideoNode_Texture : public QSGVideoNode @@ -70,5 +72,6 @@ public: QSGVideoNode *createNode(const QVideoSurfaceFormat &format); }; +QT_END_NAMESPACE #endif diff --git a/src/qtmultimediaquicktools/qtmultimediaquicktools.pro b/src/qtmultimediaquicktools/qtmultimediaquicktools.pro index da4d0dc1..6fd38be8 100644 --- a/src/qtmultimediaquicktools/qtmultimediaquicktools.pro +++ b/src/qtmultimediaquicktools/qtmultimediaquicktools.pro @@ -11,10 +11,24 @@ DEFINES += QT_BUILD_QTMM_QUICK_LIB INCLUDEPATH += ../multimedia/qtmultimediaquicktools_headers/ PRIVATE_HEADERS += \ + ../multimedia/qtmultimediaquicktools_headers/qdeclarativevideooutput_p.h \ + ../multimedia/qtmultimediaquicktools_headers/qdeclarativevideooutput_backend_p.h \ ../multimedia/qtmultimediaquicktools_headers/qsgvideonode_p.h \ ../multimedia/qtmultimediaquicktools_headers/qtmultimediaquickdefs_p.h SOURCES += \ - qsgvideonode_p.cpp + qsgvideonode_p.cpp \ + qdeclarativevideooutput.cpp \ + qdeclarativevideooutput_render.cpp \ + qdeclarativevideooutput_window.cpp \ + qsgvideonode_i420.cpp \ + qsgvideonode_rgb.cpp \ + qsgvideonode_texture.cpp -HEADERS += $$PRIVATE_HEADERS +HEADERS += \ + $$PRIVATE_HEADERS \ + qdeclarativevideooutput_render_p.h \ + qdeclarativevideooutput_window_p.h \ + qsgvideonode_i420.h \ + qsgvideonode_rgb.h \ + qsgvideonode_texture.h diff --git a/tests/auto/integration/qdeclarativevideooutput/qdeclarativevideooutput.pro b/tests/auto/integration/qdeclarativevideooutput/qdeclarativevideooutput.pro index b9be929e..573e5559 100644 --- a/tests/auto/integration/qdeclarativevideooutput/qdeclarativevideooutput.pro +++ b/tests/auto/integration/qdeclarativevideooutput/qdeclarativevideooutput.pro @@ -3,9 +3,6 @@ TARGET = tst_qdeclarativevideooutput QT += multimedia-private qml testlib quick CONFIG += testcase -OTHER_FILES += \ - ../../../../src/imports/multimedia/qdeclarativevideooutput_p.h - SOURCES += \ tst_qdeclarativevideooutput.cpp diff --git a/tests/auto/integration/qdeclarativevideooutput/tst_qdeclarativevideooutput.cpp b/tests/auto/integration/qdeclarativevideooutput/tst_qdeclarativevideooutput.cpp index 05c507f2..5ee2481f 100644 --- a/tests/auto/integration/qdeclarativevideooutput/tst_qdeclarativevideooutput.cpp +++ b/tests/auto/integration/qdeclarativevideooutput/tst_qdeclarativevideooutput.cpp @@ -46,7 +46,7 @@ #include #include -#include "qdeclarativevideooutput_p.h" +#include "private/qdeclarativevideooutput_p.h" #include #include diff --git a/tests/auto/integration/qdeclarativevideooutput_window/qdeclarativevideooutput_window.pro b/tests/auto/integration/qdeclarativevideooutput_window/qdeclarativevideooutput_window.pro index 3cc5e098..655fbc7b 100644 --- a/tests/auto/integration/qdeclarativevideooutput_window/qdeclarativevideooutput_window.pro +++ b/tests/auto/integration/qdeclarativevideooutput_window/qdeclarativevideooutput_window.pro @@ -3,9 +3,6 @@ TARGET = tst_qdeclarativevideooutput_window QT += multimedia-private qml testlib quick CONFIG += testcase -OTHER_FILES += \ - ../../../../src/imports/multimedia/qdeclarativevideooutput_p.h - SOURCES += \ tst_qdeclarativevideooutput_window.cpp diff --git a/tests/auto/integration/qdeclarativevideooutput_window/tst_qdeclarativevideooutput_window.cpp b/tests/auto/integration/qdeclarativevideooutput_window/tst_qdeclarativevideooutput_window.cpp index b4bcd504..97a441ae 100644 --- a/tests/auto/integration/qdeclarativevideooutput_window/tst_qdeclarativevideooutput_window.cpp +++ b/tests/auto/integration/qdeclarativevideooutput_window/tst_qdeclarativevideooutput_window.cpp @@ -42,7 +42,7 @@ //TESTED_COMPONENT=plugins/declarative/multimedia -#include "qdeclarativevideooutput_p.h" +#include "private/qdeclarativevideooutput_p.h" #include #include #include From a0746fe49c186566fc8c619daf0b8bb82929ac55 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Mon, 20 Jan 2014 17:20:26 +0100 Subject: [PATCH 09/11] Android: fixed video rendering with multiple media players. It seems all Android media players share the same video buffers, which results in textures containing frames for the wrong media player. When getting a new frame, we now copy it into a FBO in order to avoid that another media player overwrites the frame being shown on screen. Task-number: QTBUG-35868 Change-Id: I6701cf7368a3ef9e73d649c3ece1f206cafd5bb3 Reviewed-by: Christian Stromme --- .../src/common/qandroidvideorendercontrol.cpp | 167 +++++++++++++++--- .../src/common/qandroidvideorendercontrol.h | 36 +++- .../android/videonode/qandroidsgvideonode.cpp | 164 +++++++++-------- .../android/videonode/qandroidsgvideonode.h | 9 +- .../videonode/qandroidsgvideonodeplugin.cpp | 4 +- 5 files changed, 270 insertions(+), 110 deletions(-) diff --git a/src/plugins/android/src/common/qandroidvideorendercontrol.cpp b/src/plugins/android/src/common/qandroidvideorendercontrol.cpp index 5306fe91..55f71d73 100644 --- a/src/plugins/android/src/common/qandroidvideorendercontrol.cpp +++ b/src/plugins/android/src/common/qandroidvideorendercontrol.cpp @@ -49,22 +49,39 @@ #include #include #include +#include +#include QT_BEGIN_NAMESPACE -#define ExternalGLTextureHandle QAbstractVideoBuffer::HandleType(QAbstractVideoBuffer::UserHandle + 1) +static const GLfloat g_vertex_data[] = { + -1.f, 1.f, + 1.f, 1.f, + 1.f, -1.f, + -1.f, -1.f +}; -TextureDeleter::~TextureDeleter() +static const GLfloat g_texture_data[] = { + 0.f, 0.f, + 1.f, 0.f, + 1.f, 1.f, + 0.f, 1.f +}; + +OpenGLResourcesDeleter::~OpenGLResourcesDeleter() { - glDeleteTextures(1, &m_id); + glDeleteTextures(1, &m_textureID); + delete m_fbo; + delete m_program; } class AndroidTextureVideoBuffer : public QAbstractVideoBuffer { public: - AndroidTextureVideoBuffer(JSurfaceTexture *surface) - : QAbstractVideoBuffer(ExternalGLTextureHandle) - , m_surfaceTexture(surface) + AndroidTextureVideoBuffer(QAndroidVideoRendererControl *control) + : QAbstractVideoBuffer(GLTextureHandle) + , m_control(control) + , m_textureUpdated(false) { } @@ -76,18 +93,18 @@ public: QVariant handle() const { - if (m_data.isEmpty()) { + if (!m_textureUpdated) { // update the video texture (called from the render thread) - m_surfaceTexture->updateTexImage(); - m_data << (uint)m_surfaceTexture->textureID() << m_surfaceTexture->getTransformMatrix(); + m_control->renderFrameToFbo(); + m_textureUpdated = true; } - return m_data; + return m_control->m_fbo->texture(); } private: - mutable JSurfaceTexture *m_surfaceTexture; - mutable QVariantList m_data; + mutable QAndroidVideoRendererControl *m_control; + mutable bool m_textureUpdated; }; QAndroidVideoRendererControl::QAndroidVideoRendererControl(QObject *parent) @@ -97,7 +114,9 @@ QAndroidVideoRendererControl::QAndroidVideoRendererControl(QObject *parent) , m_surfaceTexture(0) , m_surfaceHolder(0) , m_externalTex(0) - , m_textureDeleter(0) + , m_fbo(0) + , m_program(0) + , m_glDeleter(0) { } @@ -117,8 +136,8 @@ QAndroidVideoRendererControl::~QAndroidVideoRendererControl() delete m_surfaceHolder; m_surfaceHolder = 0; } - if (m_textureDeleter) - m_textureDeleter->deleteLater(); + if (m_glDeleter) + m_glDeleter->deleteLater(); } QAbstractVideoSurface *QAndroidVideoRendererControl::surface() const @@ -162,7 +181,8 @@ bool QAndroidVideoRendererControl::initSurfaceTexture() // for the GL render thread to call us back to do it. if (QOpenGLContext::currentContext()) { glGenTextures(1, &m_externalTex); - m_textureDeleter = new TextureDeleter(m_externalTex); + m_glDeleter = new OpenGLResourcesDeleter; + m_glDeleter->setTexture(m_externalTex); } else if (!m_externalTex) { return false; } @@ -174,9 +194,9 @@ bool QAndroidVideoRendererControl::initSurfaceTexture() } else { delete m_surfaceTexture; m_surfaceTexture = 0; - m_textureDeleter->deleteLater(); + m_glDeleter->deleteLater(); m_externalTex = 0; - m_textureDeleter = 0; + m_glDeleter = 0; } return m_surfaceTexture != 0; @@ -208,6 +228,8 @@ jobject QAndroidVideoRendererControl::surfaceTexture() void QAndroidVideoRendererControl::setVideoSize(const QSize &size) { + QMutexLocker locker(&m_mutex); + if (m_nativeSize == size) return; @@ -228,7 +250,7 @@ void QAndroidVideoRendererControl::onFrameAvailable() if (!m_nativeSize.isValid() || !m_surface) return; - QAbstractVideoBuffer *buffer = new AndroidTextureVideoBuffer(m_surfaceTexture); + QAbstractVideoBuffer *buffer = new AndroidTextureVideoBuffer(this); QVideoFrame frame(buffer, m_nativeSize, QVideoFrame::Format_BGR32); if (m_surface->isActive() && (m_surface->surfaceFormat().pixelFormat() != frame.pixelFormat() @@ -237,8 +259,8 @@ void QAndroidVideoRendererControl::onFrameAvailable() } if (!m_surface->isActive()) { - QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), ExternalGLTextureHandle); - format.setScanLineDirection(QVideoSurfaceFormat::BottomToTop); + QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), + QAbstractVideoBuffer::GLTextureHandle); m_surface->start(format); } @@ -247,13 +269,114 @@ void QAndroidVideoRendererControl::onFrameAvailable() m_surface->present(frame); } +void QAndroidVideoRendererControl::renderFrameToFbo() +{ + QMutexLocker locker(&m_mutex); + + createGLResources(); + + m_surfaceTexture->updateTexImage(); + + // save current render states + GLboolean stencilTestEnabled; + GLboolean depthTestEnabled; + GLboolean scissorTestEnabled; + GLboolean blendEnabled; + glGetBooleanv(GL_STENCIL_TEST, &stencilTestEnabled); + glGetBooleanv(GL_DEPTH_TEST, &depthTestEnabled); + glGetBooleanv(GL_SCISSOR_TEST, &scissorTestEnabled); + glGetBooleanv(GL_BLEND, &blendEnabled); + + if (stencilTestEnabled) + glDisable(GL_STENCIL_TEST); + if (depthTestEnabled) + glDisable(GL_DEPTH_TEST); + if (scissorTestEnabled) + glDisable(GL_SCISSOR_TEST); + if (blendEnabled) + glDisable(GL_BLEND); + + m_fbo->bind(); + + glViewport(0, 0, m_nativeSize.width(), m_nativeSize.height()); + + m_program->bind(); + m_program->enableAttributeArray(0); + m_program->enableAttributeArray(1); + m_program->setUniformValue("frameTexture", GLuint(0)); + m_program->setUniformValue("texMatrix", m_surfaceTexture->getTransformMatrix()); + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, g_vertex_data); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, g_texture_data); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + m_program->disableAttributeArray(0); + m_program->disableAttributeArray(1); + + glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0); + m_fbo->release(); + + // restore render states + if (stencilTestEnabled) + glEnable(GL_STENCIL_TEST); + if (depthTestEnabled) + glEnable(GL_DEPTH_TEST); + if (scissorTestEnabled) + glEnable(GL_SCISSOR_TEST); + if (blendEnabled) + glEnable(GL_BLEND); +} + +void QAndroidVideoRendererControl::createGLResources() +{ + if (!m_fbo || m_fbo->size() != m_nativeSize) { + delete m_fbo; + m_fbo = new QOpenGLFramebufferObject(m_nativeSize); + m_glDeleter->setFbo(m_fbo); + } + + if (!m_program) { + m_program = new QOpenGLShaderProgram; + + QOpenGLShader *vertexShader = new QOpenGLShader(QOpenGLShader::Vertex, m_program); + vertexShader->compileSourceCode("attribute highp vec4 vertexCoordsArray; \n" \ + "attribute highp vec2 textureCoordArray; \n" \ + "uniform highp mat4 texMatrix; \n" \ + "varying highp vec2 textureCoords; \n" \ + "void main(void) \n" \ + "{ \n" \ + " gl_Position = vertexCoordsArray; \n" \ + " textureCoords = (texMatrix * vec4(textureCoordArray, 0.0, 1.0)).xy; \n" \ + "}\n"); + m_program->addShader(vertexShader); + + QOpenGLShader *fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment, m_program); + fragmentShader->compileSourceCode("#extension GL_OES_EGL_image_external : require \n" \ + "varying highp vec2 textureCoords; \n" \ + "uniform samplerExternalOES frameTexture; \n" \ + "void main() \n" \ + "{ \n" \ + " gl_FragColor = texture2D(frameTexture, textureCoords); \n" \ + "}\n"); + m_program->addShader(fragmentShader); + + m_program->bindAttributeLocation("vertexCoordsArray", 0); + m_program->bindAttributeLocation("textureCoordArray", 1); + m_program->link(); + + m_glDeleter->setShaderProgram(m_program); + } +} + void QAndroidVideoRendererControl::customEvent(QEvent *e) { if (e->type() == QEvent::User) { // This is running in the render thread (OpenGL enabled) if (!m_externalTex) { glGenTextures(1, &m_externalTex); - m_textureDeleter = new TextureDeleter(m_externalTex); // will be deleted in the correct thread + m_glDeleter = new OpenGLResourcesDeleter; // will cleanup GL resources in the correct thread + m_glDeleter->setTexture(m_externalTex); emit readyChanged(true); } } diff --git a/src/plugins/android/src/common/qandroidvideorendercontrol.h b/src/plugins/android/src/common/qandroidvideorendercontrol.h index 5d9130c0..6ce1e2dd 100644 --- a/src/plugins/android/src/common/qandroidvideorendercontrol.h +++ b/src/plugins/android/src/common/qandroidvideorendercontrol.h @@ -43,22 +43,37 @@ #define QANDROIDVIDEORENDERCONTROL_H #include +#include #include "qandroidvideooutput.h" #include "jsurfacetexture.h" QT_BEGIN_NAMESPACE class JSurfaceTextureHolder; +class QOpenGLTexture; +class QOpenGLFramebufferObject; +class QOpenGLShaderProgram; -class TextureDeleter : public QObject +class OpenGLResourcesDeleter : public QObject { Q_OBJECT public: - TextureDeleter(uint id) : m_id(id) { } - ~TextureDeleter(); + OpenGLResourcesDeleter() + : m_textureID(0) + , m_fbo(0) + , m_program(0) + { } + + ~OpenGLResourcesDeleter(); + + void setTexture(quint32 id) { m_textureID = id; } + void setFbo(QOpenGLFramebufferObject *fbo) { m_fbo = fbo; } + void setShaderProgram(QOpenGLShaderProgram *prog) { m_program = prog; } private: - uint m_id; + quint32 m_textureID; + QOpenGLFramebufferObject *m_fbo; + QOpenGLShaderProgram *m_program; }; class QAndroidVideoRendererControl : public QVideoRendererControl, public QAndroidVideoOutput @@ -88,6 +103,10 @@ private Q_SLOTS: private: bool initSurfaceTexture(); + void renderFrameToFbo(); + void createGLResources(); + + QMutex m_mutex; QAbstractVideoSurface *m_surface; QSize m_nativeSize; @@ -95,8 +114,13 @@ private: QJNIObjectPrivate *m_androidSurface; JSurfaceTexture *m_surfaceTexture; JSurfaceTextureHolder *m_surfaceHolder; - uint m_externalTex; - TextureDeleter *m_textureDeleter; + + quint32 m_externalTex; + QOpenGLFramebufferObject *m_fbo; + QOpenGLShaderProgram *m_program; + OpenGLResourcesDeleter *m_glDeleter; + + friend class AndroidTextureVideoBuffer; }; QT_END_NAMESPACE diff --git a/src/plugins/android/videonode/qandroidsgvideonode.cpp b/src/plugins/android/videonode/qandroidsgvideonode.cpp index 7f13dc98..8c441a74 100644 --- a/src/plugins/android/videonode/qandroidsgvideonode.cpp +++ b/src/plugins/android/videonode/qandroidsgvideonode.cpp @@ -61,41 +61,42 @@ public: } protected: + const char *vertexShader() const { - return - "uniform highp mat4 qt_Matrix; \n" - "uniform highp mat4 texMatrix; \n" - "attribute highp vec4 qt_VertexPosition; \n" - "attribute highp vec2 qt_VertexTexCoord; \n" - "varying highp vec2 qt_TexCoord; \n" - "void main() { \n" - " qt_TexCoord = (texMatrix * vec4(qt_VertexTexCoord, 0.0, 1.0)).xy; \n" - " gl_Position = qt_Matrix * qt_VertexPosition; \n" - "}"; + const char *shader = + "uniform highp mat4 qt_Matrix; \n" + "attribute highp vec4 qt_VertexPosition; \n" + "attribute highp vec2 qt_VertexTexCoord; \n" + "varying highp vec2 qt_TexCoord; \n" + "void main() { \n" + " qt_TexCoord = qt_VertexTexCoord; \n" + " gl_Position = qt_Matrix * qt_VertexPosition; \n" + "}"; + return shader; } const char *fragmentShader() const { - return - "#extension GL_OES_EGL_image_external : require \n" - "uniform samplerExternalOES videoTexture; \n" - "uniform lowp float opacity; \n" - "varying highp vec2 qt_TexCoord; \n" - "void main() \n" - "{ \n" - " gl_FragColor = texture2D(videoTexture, qt_TexCoord) * opacity; \n" - "}"; + static const char *shader = + "uniform sampler2D rgbTexture;" + "uniform lowp float opacity;" + "" + "varying highp vec2 qt_TexCoord;" + "" + "void main()" + "{" + " gl_FragColor = texture2D(rgbTexture, qt_TexCoord) * opacity;" + "}"; + return shader; } void initialize() { m_id_matrix = program()->uniformLocation("qt_Matrix"); - m_id_texMatrix = program()->uniformLocation("texMatrix"); - m_id_texture = program()->uniformLocation("videoTexture"); + m_id_Texture = program()->uniformLocation("rgbTexture"); m_id_opacity = program()->uniformLocation("opacity"); } int m_id_matrix; - int m_id_texMatrix; - int m_id_texture; + int m_id_Texture; int m_id_opacity; }; @@ -104,15 +105,12 @@ class QAndroidSGVideoNodeMaterial : public QSGMaterial public: QAndroidSGVideoNodeMaterial() : m_textureId(0) + , m_textureUpdated(false) + , m_opacity(1.0) { setFlag(Blending, false); } - ~QAndroidSGVideoNodeMaterial() - { - m_frame = QVideoFrame(); - } - QSGMaterialType *type() const { static QSGMaterialType theType; return &theType; @@ -124,81 +122,93 @@ public: int compare(const QSGMaterial *other) const { const QAndroidSGVideoNodeMaterial *m = static_cast(other); - return m_textureId - m->m_textureId; + int diff = m_textureId - m->m_textureId; + if (diff) + return diff; + + return (m_opacity > m->m_opacity) ? 1 : -1; } - void setVideoFrame(const QVideoFrame &frame) { - QMutexLocker lock(&m_frameMutex); - m_frame = frame; + void updateBlending() { + setFlag(Blending, qFuzzyCompare(m_opacity, qreal(1.0)) ? false : true); } - bool updateTexture() - { - QMutexLocker lock(&m_frameMutex); - bool texMatrixDirty = false; - - if (m_frame.isValid()) { - QVariantList list = m_frame.handle().toList(); - - GLuint texId = list.at(0).toUInt(); - QMatrix4x4 mat = qvariant_cast(list.at(1)); - - texMatrixDirty = texId != m_textureId || mat != m_texMatrix; - - m_textureId = texId; - m_texMatrix = mat; - - // the texture is already bound and initialized at this point, - // no need to call glTexParams - - } else { - m_textureId = 0; + void updateTexture(GLuint id, const QSize &size) { + if (m_textureId != id || m_textureSize != size) { + m_textureId = id; + m_textureSize = size; + m_textureUpdated = true; } - - return texMatrixDirty; } - QVideoFrame m_frame; - QMutex m_frameMutex; + void bind() + { + glBindTexture(GL_TEXTURE_2D, m_textureId); + if (m_textureUpdated) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + m_textureUpdated = false; + } + } + + QSize m_textureSize; GLuint m_textureId; - QMatrix4x4 m_texMatrix; + bool m_textureUpdated; + qreal m_opacity; }; + +QAndroidSGVideoNode::QAndroidSGVideoNode(const QVideoSurfaceFormat &format) + : m_format(format) +{ + setFlags(OwnsMaterial | UsePreprocess); + m_material = new QAndroidSGVideoNodeMaterial; + setMaterial(m_material); +} + +QAndroidSGVideoNode::~QAndroidSGVideoNode() +{ + m_frame = QVideoFrame(); +} + +void QAndroidSGVideoNode::setCurrentFrame(const QVideoFrame &frame) +{ + QMutexLocker lock(&m_frameMutex); + m_frame = frame; + markDirty(DirtyMaterial); +} + void QAndroidSGVideoNodeMaterialShader::updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) { Q_UNUSED(oldMaterial); QAndroidSGVideoNodeMaterial *mat = static_cast(newMaterial); - program()->setUniformValue(m_id_texture, 0); + program()->setUniformValue(m_id_Texture, 0); - if (mat->updateTexture()) - program()->setUniformValue(m_id_texMatrix, mat->m_texMatrix); + mat->bind(); - if (state.isOpacityDirty()) - program()->setUniformValue(m_id_opacity, state.opacity()); + if (state.isOpacityDirty()) { + mat->m_opacity = state.opacity(); + mat->updateBlending(); + program()->setUniformValue(m_id_opacity, GLfloat(mat->m_opacity)); + } if (state.isMatrixDirty()) program()->setUniformValue(m_id_matrix, state.combinedMatrix()); } -QAndroidSGVideoNode::QAndroidSGVideoNode(const QVideoSurfaceFormat &format) - : m_format(format) +void QAndroidSGVideoNode::preprocess() { - setFlag(QSGNode::OwnsMaterial); - m_material = new QAndroidSGVideoNodeMaterial; - setMaterial(m_material); -} + QMutexLocker lock(&m_frameMutex); -void QAndroidSGVideoNode::setCurrentFrame(const QVideoFrame &frame) -{ - m_material->setVideoFrame(frame); - markDirty(DirtyMaterial); -} + GLuint texId = 0; + if (m_frame.isValid()) + texId = m_frame.handle().toUInt(); -QVideoFrame::PixelFormat QAndroidSGVideoNode::pixelFormat() const -{ - return m_format.pixelFormat(); + m_material->updateTexture(texId, m_frame.size()); } QT_END_NAMESPACE diff --git a/src/plugins/android/videonode/qandroidsgvideonode.h b/src/plugins/android/videonode/qandroidsgvideonode.h index b5b383fb..5da72dd2 100644 --- a/src/plugins/android/videonode/qandroidsgvideonode.h +++ b/src/plugins/android/videonode/qandroidsgvideonode.h @@ -43,6 +43,7 @@ #define QANDROIDSGVIDEONODE_H #include +#include QT_BEGIN_NAMESPACE @@ -52,14 +53,18 @@ class QAndroidSGVideoNode : public QSGVideoNode { public: QAndroidSGVideoNode(const QVideoSurfaceFormat &format); + ~QAndroidSGVideoNode(); void setCurrentFrame(const QVideoFrame &frame); - QVideoFrame::PixelFormat pixelFormat() const; + QVideoFrame::PixelFormat pixelFormat() const { return m_format.pixelFormat(); } + + void preprocess(); private: - QVideoSurfaceFormat m_format; QAndroidSGVideoNodeMaterial *m_material; + QMutex m_frameMutex; QVideoFrame m_frame; + QVideoSurfaceFormat m_format; }; QT_END_NAMESPACE diff --git a/src/plugins/android/videonode/qandroidsgvideonodeplugin.cpp b/src/plugins/android/videonode/qandroidsgvideonodeplugin.cpp index 155c66ad..e1fb286a 100644 --- a/src/plugins/android/videonode/qandroidsgvideonodeplugin.cpp +++ b/src/plugins/android/videonode/qandroidsgvideonodeplugin.cpp @@ -44,14 +44,12 @@ QT_BEGIN_NAMESPACE -#define ExternalGLTextureHandle (QAbstractVideoBuffer::UserHandle + 1) - QList QAndroidSGVideoNodeFactoryPlugin::supportedPixelFormats( QAbstractVideoBuffer::HandleType handleType) const { QList pixelFormats; - if (handleType == ExternalGLTextureHandle) + if (handleType == QAbstractVideoBuffer::GLTextureHandle) pixelFormats.append(QVideoFrame::Format_BGR32); return pixelFormats; From d26fcf043db5afba9c38653531794b6cf8f13e54 Mon Sep 17 00:00:00 2001 From: tom Date: Fri, 24 Jan 2014 11:54:43 +0100 Subject: [PATCH 10/11] Fixes mediaplayer crashes on OSX Fixes the "libqavfmediaplayer.dylib 0x0000000110fa7c8c -[AVFMediaPlayerSessionObserver unloadMedia]" crash on Macs. The problem was: writing to memory that had already been released. If not sure, one should always check if the objects exist before deleting it. Solution tested on OSX 10.7, 10.8, 10.9 [ChangeLog][qtmultimedia][avfmediaplayersession] Task-number: QTBUG-34213 Change-Id: Iac108711851c348e96e73542b4e71653007eeb54 Reviewed-by: Andy Nichols --- .../mediaplayer/avfmediaplayersession.mm | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm index bb2bc75c..cf2ad307 100644 --- a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm @@ -134,15 +134,17 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe - (void) unloadMedia { [m_player setRate:0.0]; - [m_playerItem removeObserver:self forKeyPath:AVF_STATUS_KEY]; + if (m_playerItem) { + [m_playerItem removeObserver:self forKeyPath:AVF_STATUS_KEY]; - [[NSNotificationCenter defaultCenter] removeObserver:self + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:m_playerItem]; - [[NSNotificationCenter defaultCenter] removeObserver:self + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemTimeJumpedNotification object:m_playerItem]; - m_playerItem = 0; + m_playerItem = 0; + } } - (void) prepareToPlayAsset:(AVURLAsset *)asset @@ -232,8 +234,11 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe [m_player removeObserver:self forKeyPath:AVF_RATE_KEY]; [m_player release]; m_player = 0; - [m_playerLayer release]; - m_playerLayer = 0; //Will have been released + + if (m_playerLayer) { + [m_playerLayer release]; + m_playerLayer = 0; //Will have been released + } } //Get a new AVPlayer initialized to play the specified player item. @@ -398,14 +403,21 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe #ifdef QT_DEBUG_AVF qDebug() << Q_FUNC_INFO; #endif - [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_KEY]; - [m_player removeObserver:self forKeyPath:AVF_RATE_KEY]; - [m_player release]; + if (m_player) { + [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_KEY]; + [m_player removeObserver:self forKeyPath:AVF_RATE_KEY]; + [m_player release]; + } - [m_playerLayer release]; + if (m_playerLayer) { + [m_playerLayer release]; + } [self unloadMedia]; - [m_URL release]; + + if (m_URL) { + [m_URL release]; + } [super dealloc]; } From 7451906b71e7271238ecf9446b7960ab2c647cc9 Mon Sep 17 00:00:00 2001 From: Daniel Nicoletti Date: Mon, 27 Jan 2014 11:44:22 -0200 Subject: [PATCH 11/11] Fix sending End Of Stream on gst plugin When streaming to gst a typefind operation is performed which jumps around to find which codec the file provides, this fix the call to send an EOS before we try to read and not after which causes confusion to gst. Task-number: QTBUG-32963 Change-Id: I2658b6a4e960430c8ab422a3bee5e11956663116 Reviewed-by: Yoann Lopes --- src/gsttools/qgstappsrc.cpp | 39 +++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/gsttools/qgstappsrc.cpp b/src/gsttools/qgstappsrc.cpp index c74639ef..8917bda8 100644 --- a/src/gsttools/qgstappsrc.cpp +++ b/src/gsttools/qgstappsrc.cpp @@ -149,28 +149,29 @@ void QGstAppSrc::pushDataToAppSrc() size = qMin(m_stream->bytesAvailable(), queueSize()); else size = qMin(m_stream->bytesAvailable(), (qint64)m_dataRequestSize); - void *data = g_malloc(size); - GstBuffer* buffer = gst_app_buffer_new(data, size, g_free, data); - buffer->offset = m_stream->pos(); - qint64 bytesRead = m_stream->read((char*)GST_BUFFER_DATA(buffer), size); - buffer->offset_end = buffer->offset + bytesRead - 1; - if (bytesRead > 0) { - m_dataRequested = false; - m_enoughData = false; - GstFlowReturn ret = gst_app_src_push_buffer (GST_APP_SRC (element()), buffer); - if (ret == GST_FLOW_ERROR) { - qWarning()<<"appsrc: push buffer error"; - } else if (ret == GST_FLOW_WRONG_STATE) { - qWarning()<<"appsrc: push buffer wrong state"; - } else if (ret == GST_FLOW_RESEND) { - qWarning()<<"appsrc: push buffer resend"; + if (size) { + void *data = g_malloc(size); + GstBuffer* buffer = gst_app_buffer_new(data, size, g_free, data); + buffer->offset = m_stream->pos(); + qint64 bytesRead = m_stream->read((char*)GST_BUFFER_DATA(buffer), size); + buffer->offset_end = buffer->offset + bytesRead - 1; + + if (bytesRead > 0) { + m_dataRequested = false; + m_enoughData = false; + GstFlowReturn ret = gst_app_src_push_buffer (GST_APP_SRC (element()), buffer); + if (ret == GST_FLOW_ERROR) { + qWarning()<<"appsrc: push buffer error"; + } else if (ret == GST_FLOW_WRONG_STATE) { + qWarning()<<"appsrc: push buffer wrong state"; + } else if (ret == GST_FLOW_RESEND) { + qWarning()<<"appsrc: push buffer resend"; + } } - } - - // After reading we might be all done - if (m_stream->atEnd()) + } else { sendEOS(); + } } else if (m_stream->atEnd()) { sendEOS(); }