summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Kiagiadakis <george.kiagiadakis@collabora.com>2011-11-01 14:42:43 +0200
committerGeorge Kiagiadakis <george.kiagiadakis@collabora.com>2012-01-06 17:51:15 +0200
commit50ba34fd0aa175e11c819994a64d38358bffc02c (patch)
treedca284105ea3d531108c4ba755b3e8c5ec9ee6ba
parentfdcd7ff4302af28de7f8d04b76ca944a01156f33 (diff)
qtvideosink: Initial refactoring.
* Eliminated QtMultimedia dependencies. * Eliminated the need to link to the internal classes of the sink by adding a standard properties/signals interface to the gst element. * Simplified some parts of the initial code, which were there only to support plugging the components individually to different parts of QtMultimedia.
-rw-r--r--elements/gstqtvideosink/abstractsurfacepainter.h43
-rw-r--r--elements/gstqtvideosink/bufferformat.cpp246
-rw-r--r--elements/gstqtvideosink/bufferformat.h95
-rw-r--r--elements/gstqtvideosink/genericsurfacepainter.cpp68
-rw-r--r--elements/gstqtvideosink/genericsurfacepainter.h50
-rw-r--r--elements/gstqtvideosink/gstqtvideosink.cpp925
-rw-r--r--elements/gstqtvideosink/gstqtvideosink.h163
-rw-r--r--elements/gstqtvideosink/gstqtvideosinkplugin.cpp42
-rw-r--r--elements/gstqtvideosink/gstqtvideosinkplugin.h25
-rw-r--r--elements/gstqtvideosink/gstqtvideosinksurface.cpp1842
-rw-r--r--elements/gstqtvideosink/gstqtvideosinksurface.h215
-rw-r--r--elements/gstqtvideosink/openglsurfacepainter.cpp832
-rw-r--r--elements/gstqtvideosink/openglsurfacepainter.h132
13 files changed, 2235 insertions, 2443 deletions
diff --git a/elements/gstqtvideosink/abstractsurfacepainter.h b/elements/gstqtvideosink/abstractsurfacepainter.h
new file mode 100644
index 0000000..87fcd13
--- /dev/null
+++ b/elements/gstqtvideosink/abstractsurfacepainter.h
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
+ Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef ABSTRACTSURFACEPAINTER_H
+#define ABSTRACTSURFACEPAINTER_H
+
+#include "bufferformat.h"
+#include <QtCore/QSet>
+
+class QPainter;
+
+/** Common interface for all the painters */
+class AbstractSurfacePainter
+{
+public:
+ virtual ~AbstractSurfacePainter() {}
+
+ virtual bool supportsFormat(BufferFormat::PixelFormat format) const = 0;
+
+ virtual void init(const BufferFormat & format) = 0;
+ virtual void cleanup() = 0;
+
+ virtual void paint(quint8 *data, const BufferFormat & frameFormat,
+ QPainter *painter, const QRect & videoArea, const QRect & clipRect) = 0;
+
+ virtual void updateColors(int brightness, int contrast, int hue, int saturation,
+ BufferFormat::YCbCrColorSpace colorSpace) = 0;
+};
+
+#endif
diff --git a/elements/gstqtvideosink/bufferformat.cpp b/elements/gstqtvideosink/bufferformat.cpp
new file mode 100644
index 0000000..a18dd65
--- /dev/null
+++ b/elements/gstqtvideosink/bufferformat.cpp
@@ -0,0 +1,246 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
+ Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "bufferformat.h"
+#include <gst/gst.h>
+
+//BEGIN YuvFormat
+
+struct YuvFormat
+{
+ BufferFormat::PixelFormat pixelFormat;
+ guint32 fourcc;
+ int bitsPerPixel;
+};
+
+static const YuvFormat qt_yuvColorLookup[] =
+{
+ { BufferFormat::YUV420P, GST_MAKE_FOURCC('I','4','2','0'), 8 },
+ { BufferFormat::YV12, GST_MAKE_FOURCC('Y','V','1','2'), 8 },
+ { BufferFormat::UYVY, GST_MAKE_FOURCC('U','Y','V','Y'), 16 },
+ { BufferFormat::YUYV, GST_MAKE_FOURCC('Y','U','Y','2'), 16 },
+ { BufferFormat::NV12, GST_MAKE_FOURCC('N','V','1','2'), 8 },
+ { BufferFormat::NV21, GST_MAKE_FOURCC('N','V','2','1'), 8 },
+ { BufferFormat::AYUV444, GST_MAKE_FOURCC('A','Y','U','V'), 32 }
+};
+
+static int indexOfYuvColor(BufferFormat::PixelFormat format)
+{
+ const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat);
+
+ for (int i = 0; i < count; ++i)
+ if (qt_yuvColorLookup[i].pixelFormat == format)
+ return i;
+
+ return -1;
+}
+
+static int indexOfYuvColor(guint32 fourcc)
+{
+ const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat);
+
+ for (int i = 0; i < count; ++i)
+ if (qt_yuvColorLookup[i].fourcc == fourcc)
+ return i;
+
+ return -1;
+}
+
+//END YuvFormat
+//BEGIN RgbFormat
+
+struct RgbFormat
+{
+ BufferFormat::PixelFormat pixelFormat;
+ int bitsPerPixel;
+ int depth;
+ int endianness;
+ int red;
+ int green;
+ int blue;
+ int alpha;
+};
+
+static const RgbFormat qt_rgbColorLookup[] =
+{
+ { BufferFormat::RGB32 , 32, 24, 4321, 0x0000FF00, 0x00FF0000, 0xFF000000, 0x00000000 },
+ { BufferFormat::RGB32 , 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 },
+ { BufferFormat::BGR32 , 32, 24, 4321, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x00000000 },
+ { BufferFormat::BGR32 , 32, 24, 1234, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 },
+ { BufferFormat::ARGB32, 32, 24, 4321, 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF },
+ { BufferFormat::ARGB32, 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000 },
+ { BufferFormat::RGB24 , 24, 24, 4321, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 },
+ { BufferFormat::BGR24 , 24, 24, 4321, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 },
+ { BufferFormat::RGB565, 16, 16, 1234, 0x0000F800, 0x000007E0, 0x0000001F, 0x00000000 }
+};
+
+static int indexOfRgbColor(
+ int bits, int depth, int endianness, int red, int green, int blue, int alpha)
+{
+ const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat);
+
+ for (int i = 0; i < count; ++i) {
+ if (qt_rgbColorLookup[i].bitsPerPixel == bits
+ && qt_rgbColorLookup[i].depth == depth
+ && qt_rgbColorLookup[i].endianness == endianness
+ && qt_rgbColorLookup[i].red == red
+ && qt_rgbColorLookup[i].green == green
+ && qt_rgbColorLookup[i].blue == blue
+ && qt_rgbColorLookup[i].alpha == alpha) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+//END RgbFormat
+//BEGIN BufferFormat
+
+QImage::Format BufferFormat::pixelFormatToImageFormat(PixelFormat format)
+{
+ switch (format) {
+ case ARGB32:
+ return QImage::Format_ARGB32;
+ case RGB32:
+ return QImage::Format_RGB32;
+ case RGB24:
+ return QImage::Format_RGB888;
+ case RGB565:
+ return QImage::Format_RGB16;
+ case RGB555:
+ return QImage::Format_RGB555;
+ case BGRA32:
+ case BGR32:
+ case BGR24:
+ case BGR565:
+ case BGR555:
+ case AYUV444:
+ case YUV444:
+ case YUV420P:
+ case YV12:
+ case UYVY:
+ case YUYV:
+ case NV12:
+ case NV21:
+ case Invalid:
+ return QImage::Format_Invalid;
+ }
+ return QImage::Format_Invalid;
+}
+
+GstCaps *BufferFormat::pixelFormatToCaps(PixelFormat format)
+{
+ GstCaps *caps = gst_caps_new_empty();
+
+ if (int index = indexOfYuvColor(format) != -1)
+ {
+ GstStructure *structure = gst_structure_new(
+ "video/x-raw-yuv",
+ "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1,
+ "width" , GST_TYPE_INT_RANGE, 1, INT_MAX,
+ "height" , GST_TYPE_INT_RANGE, 1, INT_MAX,
+ "format" , GST_TYPE_FOURCC, qt_yuvColorLookup[index].fourcc,
+ NULL);
+ gst_caps_append_structure(caps, structure);
+ }
+ else
+ {
+ const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat);
+
+ for (int i = 0; i < count; ++i) {
+ if (qt_rgbColorLookup[i].pixelFormat == format) {
+ GstStructure *structure = gst_structure_new(
+ "video/x-raw-rgb",
+ "framerate" , GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1,
+ "width" , GST_TYPE_INT_RANGE, 1, INT_MAX,
+ "height" , GST_TYPE_INT_RANGE, 1, INT_MAX,
+ "bpp" , G_TYPE_INT, qt_rgbColorLookup[i].bitsPerPixel,
+ "depth" , G_TYPE_INT, qt_rgbColorLookup[i].depth,
+ "endianness", G_TYPE_INT, qt_rgbColorLookup[i].endianness,
+ "red_mask" , G_TYPE_INT, qt_rgbColorLookup[i].red,
+ "green_mask", G_TYPE_INT, qt_rgbColorLookup[i].green,
+ "blue_mask" , G_TYPE_INT, qt_rgbColorLookup[i].blue,
+ NULL);
+
+ if (qt_rgbColorLookup[i].alpha != 0) {
+ gst_structure_set(
+ structure, "alpha_mask", G_TYPE_INT, qt_rgbColorLookup[i].alpha, NULL);
+ }
+ gst_caps_append_structure(caps, structure);
+ }
+ }
+ }
+
+ return caps;
+}
+
+BufferFormat BufferFormat::fromCaps(const GstCaps *caps)
+{
+ BufferFormat result;
+ if (caps) {
+ const GstStructure *structure = gst_caps_get_structure(caps, 0);
+ int bitsPerPixel = 0;
+
+ if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) {
+ guint32 fourcc = 0;
+ gst_structure_get_fourcc(structure, "format", &fourcc);
+
+ int index = indexOfYuvColor(fourcc);
+ if (index != -1) {
+ result.d->pixelFormat = qt_yuvColorLookup[index].pixelFormat;
+ bitsPerPixel = qt_yuvColorLookup[index].bitsPerPixel;
+ }
+ } else if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) {
+ int depth = 0;
+ int endianness = 0;
+ int red = 0;
+ int green = 0;
+ int blue = 0;
+ int alpha = 0;
+
+ gst_structure_get_int(structure, "bpp", &bitsPerPixel);
+ gst_structure_get_int(structure, "depth", &depth);
+ gst_structure_get_int(structure, "endianness", &endianness);
+ gst_structure_get_int(structure, "red_mask", &red);
+ gst_structure_get_int(structure, "green_mask", &green);
+ gst_structure_get_int(structure, "blue_mask", &blue);
+ gst_structure_get_int(structure, "alpha_mask", &alpha);
+
+ int index = indexOfRgbColor(bitsPerPixel, depth, endianness, red, green, blue, alpha);
+
+ if (index != -1) {
+ result.d->pixelFormat = qt_rgbColorLookup[index].pixelFormat;
+ }
+ }
+
+ if (result.d->pixelFormat != BufferFormat::Invalid) {
+ gst_structure_get_int(structure, "width", &result.d->frameSize.rwidth());
+ gst_structure_get_int(structure, "height", &result.d->frameSize.rheight());
+ gst_structure_get_fraction(structure, "pixel-aspect-ratio",
+ &result.d->pixelAspectRatio.rwidth(),
+ &result.d->pixelAspectRatio.rheight());
+
+ result.d->bytesPerLine = ((result.d->frameSize.width() * bitsPerPixel / 8) + 3) & ~3;
+
+ //FIXME not sure if this should be read from caps somehow
+ //The original QtMM code always uses this colorspace
+ result.d->colorSpace = BufferFormat::YCbCr_BT601;
+ }
+ }
+ return result;
+}
+
+//END BufferFormat
diff --git a/elements/gstqtvideosink/bufferformat.h b/elements/gstqtvideosink/bufferformat.h
new file mode 100644
index 0000000..2c30f84
--- /dev/null
+++ b/elements/gstqtvideosink/bufferformat.h
@@ -0,0 +1,95 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
+ Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef BUFFERFORMAT_H
+#define BUFFERFORMAT_H
+
+#include <QtCore/QSharedData>
+#include <QtCore/QSize>
+#include <QtGui/QImage>
+
+typedef struct _GstCaps GstCaps;
+
+
+/**
+ * This class is a cheap way to represent Caps.
+ * Based on QVideoSurfaceFormat.
+ */
+class BufferFormat
+{
+public:
+ enum PixelFormat {
+ Invalid,
+ ARGB32,
+ RGB32,
+ RGB24,
+ RGB565,
+ RGB555, /* unused */
+ BGRA32, /* unused */
+ BGR32,
+ BGR24,
+ BGR565, /* unused */
+ BGR555, /* unused */
+ AYUV444,
+ YUV444, /* unused */
+ YUV420P,
+ YV12,
+ UYVY,
+ YUYV,
+ NV12,
+ NV21
+ };
+
+ enum YCbCrColorSpace {
+ YCbCr_Undefined,
+ YCbCr_BT601,
+ YCbCr_BT709,
+ YCbCr_xvYCC601,
+ YCbCr_xvYCC709,
+ YCbCr_JPEG
+ };
+
+ static QImage::Format pixelFormatToImageFormat(PixelFormat format);
+ static GstCaps *pixelFormatToCaps(PixelFormat format);
+
+ static BufferFormat fromCaps(const GstCaps *caps);
+ inline BufferFormat() : d(new Data) {}
+
+ inline PixelFormat pixelFormat() const { return d->pixelFormat; }
+ inline YCbCrColorSpace yCbCrColorSpace() const { return d->colorSpace; }
+ inline QSize frameSize() const { return d->frameSize; }
+ inline QSize pixelAspectRatio() const { return d->pixelAspectRatio; }
+ inline int bytesPerLine() const { return d->bytesPerLine; }
+
+private:
+ struct Data : public QSharedData
+ {
+ Data() :
+ pixelFormat(Invalid),
+ colorSpace(BufferFormat::YCbCr_Undefined),
+ bytesPerLine(0)
+ {}
+
+ PixelFormat pixelFormat;
+ YCbCrColorSpace colorSpace;
+ QSize frameSize;
+ QSize pixelAspectRatio;
+ int bytesPerLine;
+ };
+ QSharedDataPointer<Data> d;
+};
+
+#endif // BUFFERFORMAT_H
diff --git a/elements/gstqtvideosink/genericsurfacepainter.cpp b/elements/gstqtvideosink/genericsurfacepainter.cpp
new file mode 100644
index 0000000..fb45978
--- /dev/null
+++ b/elements/gstqtvideosink/genericsurfacepainter.cpp
@@ -0,0 +1,68 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
+ Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "genericsurfacepainter.h"
+#include <gst/gst.h>
+#include <QtGui/QPainter>
+
+GenericSurfacePainter::GenericSurfacePainter()
+ : m_imageFormat(QImage::Format_Invalid)
+{
+}
+
+//static
+QSet<BufferFormat::PixelFormat> GenericSurfacePainter::supportedPixelFormats()
+{
+ return QSet<BufferFormat::PixelFormat>()
+ << BufferFormat::RGB32
+#ifndef QT_OPENGL_ES // The raster formats should be a subset of the GL formats.
+ << BufferFormat::RGB24
+#endif
+ << BufferFormat::ARGB32
+ << BufferFormat::RGB565;
+}
+
+void GenericSurfacePainter::init(const BufferFormat &format)
+{
+ m_imageFormat = BufferFormat::pixelFormatToImageFormat(format.pixelFormat());
+}
+
+void GenericSurfacePainter::cleanup()
+{
+ m_imageFormat = QImage::Format_Invalid;
+}
+
+void GenericSurfacePainter::paint(quint8 *data,
+ const BufferFormat & frameFormat,
+ QPainter *painter,
+ const QRect & videoArea,
+ const QRect & clipRect)
+{
+ Q_ASSERT(m_imageFormat != QImage::Format_Invalid);
+
+ QImage image(
+ data,
+ frameFormat.frameSize().width(),
+ frameFormat.frameSize().height(),
+ frameFormat.bytesPerLine(),
+ m_imageFormat);
+
+ painter->drawImage(videoArea, image, clipRect);
+}
+
+void GenericSurfacePainter::updateColors(int, int, int, int, BufferFormat::YCbCrColorSpace)
+{
+}
diff --git a/elements/gstqtvideosink/genericsurfacepainter.h b/elements/gstqtvideosink/genericsurfacepainter.h
new file mode 100644
index 0000000..4ca037c
--- /dev/null
+++ b/elements/gstqtvideosink/genericsurfacepainter.h
@@ -0,0 +1,50 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
+ Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef GENERICSURFACEPAINTER_H
+#define GENERICSURFACEPAINTER_H
+
+#include "abstractsurfacepainter.h"
+
+/**
+ * Generic painter that paints using the QPainter API.
+ * No colorspace conversion is done and no colors adjustment either.
+ */
+class GenericSurfacePainter : public AbstractSurfacePainter
+{
+public:
+ GenericSurfacePainter();
+
+ static QSet<BufferFormat::PixelFormat> supportedPixelFormats();
+
+ virtual bool supportsFormat(BufferFormat::PixelFormat format) const {
+ return supportedPixelFormats().contains(format);
+ }
+
+ virtual void init(const BufferFormat &format);
+ virtual void cleanup();
+
+ virtual void paint(quint8 *data, const BufferFormat & frameFormat,
+ QPainter *painter, const QRect & videoArea, const QRect & clipRect);
+
+ virtual void updateColors(int brightness, int contrast, int hue, int saturation,
+ BufferFormat::YCbCrColorSpace colorSpace);
+
+private:
+ QImage::Format m_imageFormat;
+};
+
+#endif // GENERICSURFACEPAINTER_H
diff --git a/elements/gstqtvideosink/gstqtvideosink.cpp b/elements/gstqtvideosink/gstqtvideosink.cpp
index e42293f..3ffc2f1 100644
--- a/elements/gstqtvideosink/gstqtvideosink.cpp
+++ b/elements/gstqtvideosink/gstqtvideosink.cpp
@@ -1,749 +1,306 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 Collabora Ltd
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file was originally part of the Qt Mobility Components.
-**
-** 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.
-**
-****************************************************************************/
-
-#include <qabstractvideosurface.h>
-#include <qmlpaintervideosurface.h>
-#include <qvideoframe.h>
-#include <QDebug>
-#include <QMap>
-#include <QDebug>
-#include <QThread>
-
-#include "qmlgstvideobuffer.h"
-
-#if defined(Q_WS_X11) && !defined(QT_NO_XVIDEO)
-#include <QtGui/qx11info_x11.h>
-#include "qmlgstxvimagebuffer.h"
-#endif
-
-#include "qmlvideosurfacegstsink.h"
-
-//#define DEBUG_VIDEO_SURFACE_SINK
-
-
-Q_DECLARE_METATYPE(QVideoSurfaceFormat)
-
-QmlVideoSurfaceGstDelegate::QmlVideoSurfaceGstDelegate(QAbstractVideoSurface *surface)
- : m_surface(surface)
- , m_renderReturn(GST_FLOW_ERROR)
- , m_bytesPerLine(0)
-{
- if (m_surface) {
- m_supportedPixelFormats = m_surface->supportedPixelFormats();
- m_supportedXVideoPixelFormats = m_surface->supportedPixelFormats(QAbstractVideoBuffer::XvShmImageHandle);
- connect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(supportedFormatsChanged()));
- }
-}
-
-QList<QVideoFrame::PixelFormat> QmlVideoSurfaceGstDelegate::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
-{
- QMutexLocker locker(const_cast<QMutex *>(&m_mutex));
-
- if (!m_surface)
- return QList<QVideoFrame::PixelFormat>();
- else if (handleType == QAbstractVideoBuffer::NoHandle)
- return m_supportedPixelFormats;
- else if (handleType == QAbstractVideoBuffer::XvShmImageHandle)
- return m_supportedXVideoPixelFormats;
- else
- return m_surface->supportedPixelFormats(handleType);
-}
-
-QVideoSurfaceFormat QmlVideoSurfaceGstDelegate::surfaceFormat() const
-{
- QMutexLocker locker(const_cast<QMutex *>(&m_mutex));
- return m_format;
-}
-
-bool QmlVideoSurfaceGstDelegate::start(const QVideoSurfaceFormat &format, int bytesPerLine)
-{
- if (!m_surface)
- return false;
-
- QMutexLocker locker(&m_mutex);
-
- m_format = format;
- m_bytesPerLine = bytesPerLine;
-
- if (QThread::currentThread() == thread()) {
- m_started = !m_surface.isNull() ? m_surface->start(m_format) : false;
- } else {
- QMetaObject::invokeMethod(this, "queuedStart", Qt::QueuedConnection);
-
- m_setupCondition.wait(&m_mutex);
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
+ Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * SECTION:element-qtvideosink
+ *
+ * qtvideosink is a video sink element that can draw directly on a Qt
+ * surface, such as QWidget or QGraphicsItem, using either the QPainter API
+ * or the OpenGL/OpenGLES API.
+ */
+
+#include "gstqtvideosink.h"
+#include "gstqtvideosinksurface.h"
+#include "gstqtvideosinkmarshal.h"
+
+#include <QtCore/QCoreApplication>
+
+guint GstQtVideoSink::s_signals[];
+GstVideoSinkClass *GstQtVideoSink::s_parent_class = NULL;
+
+GType GstQtVideoSink::get_type()
+{
+ /* The typedef for GType may be gulong or gsize, depending on the
+ * system and whether the compiler is c++ or not. The g_once_init_*
+ * functions always take a gsize * though ... */
+ static volatile gsize gonce_data = 0;
+ if (g_once_init_enter(&gonce_data)) {
+ GType type;
+ type = gst_type_register_static_full(
+ GST_TYPE_VIDEO_SINK,
+ g_intern_static_string("GstQtVideoSink"),
+ sizeof(GstQtVideoSinkClass),
+ &GstQtVideoSink::base_init,
+ NULL, /* base_finalize */
+ &GstQtVideoSink::class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof(GstQtVideoSink),
+ 0, /* n_preallocs */
+ &GstQtVideoSink::init,
+ NULL,
+ (GTypeFlags) 0);
+ g_once_init_leave(&gonce_data, (gsize) type);
}
-
- m_format = m_surface->surfaceFormat();
-
- return m_started;
+ return (GType) gonce_data;
}
-void QmlVideoSurfaceGstDelegate::stop()
+void GstQtVideoSink::base_init(gpointer g_class)
{
- if (!m_surface)
- return;
+ GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
- QMutexLocker locker(&m_mutex);
+ static GstStaticPadTemplate sink_pad_template =
+ GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+ GST_STATIC_CAPS(
+ "video/x-raw-rgb, "
+ "framerate = (fraction) [ 0, MAX ], "
+ "width = (int) [ 1, MAX ], "
+ "height = (int) [ 1, MAX ]"
+ "; "
+ "video/x-raw-yuv, "
+ "framerate = (fraction) [ 0, MAX ], "
+ "width = (int) [ 1, MAX ], "
+ "height = (int) [ 1, MAX ]"
+ "; "
+ )
+ );
- if (QThread::currentThread() == thread()) {
- if (!m_surface.isNull())
- m_surface->stop();
- } else {
- QMetaObject::invokeMethod(this, "queuedStop", Qt::QueuedConnection);
+ gst_element_class_set_details_simple(element_class, "Qt video sink", "Sink/Video",
+ "A video sink that can draw on any Qt surface (QPaintDevice/QWidget/QGraphicsItem/QML)",
+ "George Kiagiadakis <george.kiagiadakis@collabora.com>");
- m_setupCondition.wait(&m_mutex);
- }
-
- m_started = false;
-}
-
-bool QmlVideoSurfaceGstDelegate::isActive()
-{
- QMutexLocker locker(&m_mutex);
- return !m_surface.isNull() && m_surface->isActive();
-}
-
-void QmlVideoSurfaceGstDelegate::setPlaying(bool playing)
-{
- if (!m_surface.isNull()) {
- QmlPainterVideoSurface *surface = qobject_cast<QmlPainterVideoSurface*>(m_surface.data());
- if (surface)
- surface->setPlaying(playing);
- }
+ gst_element_class_add_pad_template(
+ element_class, gst_static_pad_template_get(&sink_pad_template));
}
-GstFlowReturn QmlVideoSurfaceGstDelegate::render(GstBuffer *buffer)
+void GstQtVideoSink::class_init(gpointer g_class, gpointer class_data)
{
- if (!m_surface) {
- qWarning() << "Rendering video frame to deleted surface, skip.";
- //return GST_FLOW_NOT_NEGOTIATED;
- return GST_FLOW_OK;
- }
-
- QMutexLocker locker(&m_mutex);
-
- QmlGstVideoBuffer *videoBuffer = 0;
+ Q_UNUSED(class_data);
-#if defined(Q_WS_X11) && !defined(QT_NO_XVIDEO)
- if (G_TYPE_CHECK_INSTANCE_TYPE(buffer, QmlGstXvImageBuffer::get_type())) {
- QmlGstXvImageBuffer *xvBuffer = reinterpret_cast<QmlGstXvImageBuffer *>(buffer);
- QVariant handle = QVariant::fromValue(xvBuffer->xvImage);
- videoBuffer = new QmlGstVideoBuffer(buffer, m_bytesPerLine, QAbstractVideoBuffer::XvShmImageHandle, handle);
- } else
+ s_parent_class = reinterpret_cast<GstVideoSinkClass*>(g_type_class_peek_parent(g_class));
+
+ GObjectClass *object_class = G_OBJECT_CLASS(g_class);
+ object_class->finalize = GstQtVideoSink::finalize;
+ object_class->set_property = GstQtVideoSink::set_property;
+ object_class->get_property = GstQtVideoSink::get_property;
+
+ GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
+ element_class->change_state = GstQtVideoSink::change_state;
+
+ GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS(g_class);
+ base_sink_class->get_caps = GstQtVideoSink::get_caps;
+ base_sink_class->set_caps = GstQtVideoSink::set_caps;
+ base_sink_class->buffer_alloc = GstQtVideoSink::buffer_alloc;
+
+ GstVideoSinkClass *video_sink_class = GST_VIDEO_SINK_CLASS(g_class);
+ video_sink_class->show_frame = GstQtVideoSink::show_frame;
+
+ GstQtVideoSinkClass *qt_video_sink_class = GST_QT_VIDEO_SINK_CLASS(g_class);
+ qt_video_sink_class->paint = GstQtVideoSink::paint;
+
+
+ /**
+ * GstQtVideoSink::force-aspect-ratio
+ *
+ * If set to TRUE, the sink will scale the video respecting its original aspect ratio
+ * and any remaining space will be filled with black.
+ * If set to FALSE, the sink will scale the video to fit the whole drawing area.
+ **/
+ g_object_class_install_property(object_class, PROP_FORCE_ASPECT_RATIO,
+ g_param_spec_boolean("force-aspect-ratio", "Force aspect ratio",
+ "When enabled, scaling will respect original aspect ratio",
+ FALSE, static_cast<GParamFlags>(G_PARAM_READWRITE)));
+
+#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
+ /**
+ * GstQtVideoSink::glcontext
+ *
+ * This property holds a pointer to the QGLContext that will be used to render
+ * the video using OpenGL acceleration. You must set this to a valid QGLContext
+ * pointer before linking this element, or else OpenGL acceleration will be disabled.
+ **/
+ g_object_class_install_property(object_class, PROP_GLCONTEXT,
+ g_param_spec_pointer("glcontext", "GL context",
+ "The QGLContext that will be used to do OpenGL-accelerated rendering",
+ static_cast<GParamFlags>(G_PARAM_READWRITE)));
#endif
- videoBuffer = new QmlGstVideoBuffer(buffer, m_bytesPerLine);
-
- m_frame = QVideoFrame(
- videoBuffer,
- m_format.frameSize(),
- m_format.pixelFormat());
-
- qint64 startTime = GST_BUFFER_TIMESTAMP(buffer);
- if (startTime >= 0) {
- m_frame.setStartTime(startTime/G_GINT64_CONSTANT (1000000));
-
- qint64 duration = GST_BUFFER_DURATION(buffer);
-
- if (duration >= 0)
- m_frame.setEndTime((startTime + duration)/G_GINT64_CONSTANT (1000000));
- }
-
- QMetaObject::invokeMethod(this, "queuedRender", Qt::QueuedConnection);
-
- if (!m_renderCondition.wait(&m_mutex, 300)) {
- m_frame = QVideoFrame();
-
- return GST_FLOW_OK;
- } else {
- return m_renderReturn;
- }
-}
-
-void QmlVideoSurfaceGstDelegate::queuedStart()
-{
- QMutexLocker locker(&m_mutex);
-
- m_started = m_surface->start(m_format);
-
- m_setupCondition.wakeAll();
-}
-
-void QmlVideoSurfaceGstDelegate::queuedStop()
-{
- QMutexLocker locker(&m_mutex);
-
- m_surface->stop();
-
- m_setupCondition.wakeAll();
-}
-
-void QmlVideoSurfaceGstDelegate::queuedRender()
-{
- QMutexLocker locker(&m_mutex);
-
- if (m_surface.isNull()) {
- qWarning() << "Rendering video frame to deleted surface, skip the frame";
- m_renderReturn = GST_FLOW_OK;
- } else if (m_surface->present(m_frame)) {
- m_renderReturn = GST_FLOW_OK;
- } else {
- switch (m_surface->error()) {
- case QAbstractVideoSurface::NoError:
- m_renderReturn = GST_FLOW_OK;
- break;
- case QAbstractVideoSurface::StoppedError:
- //It's likely we are in process of changing video output
- //and the surface is already stopped, ignore the frame
- m_renderReturn = GST_FLOW_OK;
- break;
- default:
- qWarning() << "Failed to render video frame:" << m_surface->error();
- m_renderReturn = GST_FLOW_OK;
- break;
- }
- }
-
- m_renderCondition.wakeAll();
-}
-
-void QmlVideoSurfaceGstDelegate::supportedFormatsChanged()
-{
- QMutexLocker locker(&m_mutex);
-
- m_supportedPixelFormats.clear();
- m_supportedXVideoPixelFormats.clear();
- if (m_surface) {
- m_supportedPixelFormats = m_surface->supportedPixelFormats();
- m_supportedXVideoPixelFormats = m_surface->supportedPixelFormats(QAbstractVideoBuffer::XvShmImageHandle);
- }
-}
-
-struct YuvFormat
-{
- QVideoFrame::PixelFormat pixelFormat;
- guint32 fourcc;
- int bitsPerPixel;
-};
-
-static const YuvFormat qt_yuvColorLookup[] =
-{
- { QVideoFrame::Format_YUV420P, GST_MAKE_FOURCC('I','4','2','0'), 8 },
- { QVideoFrame::Format_YV12, GST_MAKE_FOURCC('Y','V','1','2'), 8 },
- { QVideoFrame::Format_UYVY, GST_MAKE_FOURCC('U','Y','V','Y'), 16 },
- { QVideoFrame::Format_YUYV, GST_MAKE_FOURCC('Y','U','Y','2'), 16 },
- { QVideoFrame::Format_NV12, GST_MAKE_FOURCC('N','V','1','2'), 8 },
- { QVideoFrame::Format_NV21, GST_MAKE_FOURCC('N','V','2','1'), 8 },
- { QVideoFrame::Format_AYUV444, GST_MAKE_FOURCC('A','Y','U','V'), 32 }
-};
-
-static int indexOfYuvColor(QVideoFrame::PixelFormat format)
-{
- const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat);
-
- for (int i = 0; i < count; ++i)
- if (qt_yuvColorLookup[i].pixelFormat == format)
- return i;
+ /**
+ * GstQtVideoSink::paint
+ * @painter: A valid QPainter pointer that will be used to paint the video
+ * @x: The x coordinate of the target area rectangle
+ * @y: The y coordinate of the target area rectangle
+ * @width: The width of the target area rectangle
+ * @height: The height of the target area rectangle
+ *
+ * This is an action signal that you can call from your Qt surface class inside
+ * its paint function to render the video. It takes a QPainter* and the target
+ * area rectangle as arguments. You should schedule to call this function to
+ * repaint the surface whenever the ::update signal is emited.
+ */
+ s_signals[PAINT_SIGNAL] =
+ g_signal_new("paint", G_TYPE_FROM_CLASS(g_class),
+ static_cast<GSignalFlags>(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
+ G_STRUCT_OFFSET(GstQtVideoSinkClass, paint),
+ NULL, NULL,
+ g_cclosure_user_marshal_VOID__POINTER_INT_INT_INT_INT,
+ G_TYPE_NONE, 5,
+ G_TYPE_POINTER, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
+
+ /**
+ * GstQtVideoSink::update
+ *
+ * This signal is emited when the surface should be repainted. It should
+ * be connected to QWidget::update() or QGraphicsItem::update() or any
+ * other similar function in your surface.
+ */
+ s_signals[UPDATE_SIGNAL] =
+ g_signal_new("update", G_TYPE_FROM_CLASS(g_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+void GstQtVideoSink::init(GTypeInstance *instance, gpointer g_class)
+{
+ GstQtVideoSink *sink = GST_QT_VIDEO_SINK(instance);
+ Q_UNUSED(g_class);
- return -1;
+ sink->surface = new GstQtVideoSinkSurface(sink);
+ sink->formatDirty = true;
}
-static int indexOfYuvColor(guint32 fourcc)
+void GstQtVideoSink::finalize(GObject *object)
{
- const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat);
-
- for (int i = 0; i < count; ++i)
- if (qt_yuvColorLookup[i].fourcc == fourcc)
- return i;
+ GstQtVideoSink *sink = GST_QT_VIDEO_SINK(object);
- return -1;
+ delete sink->surface;
+ sink->surface = 0;
}
-struct RgbFormat
-{
- QVideoFrame::PixelFormat pixelFormat;
- int bitsPerPixel;
- int depth;
- int endianness;
- int red;
- int green;
- int blue;
- int alpha;
-};
-
-static const RgbFormat qt_rgbColorLookup[] =
+void GstQtVideoSink::set_property(GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
{
- { QVideoFrame::Format_RGB32 , 32, 24, 4321, 0x0000FF00, 0x00FF0000, 0xFF000000, 0x00000000 },
- { QVideoFrame::Format_RGB32 , 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 },
- { QVideoFrame::Format_BGR32 , 32, 24, 4321, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x00000000 },
- { QVideoFrame::Format_BGR32 , 32, 24, 1234, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 },
- { QVideoFrame::Format_ARGB32, 32, 24, 4321, 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF },
- { QVideoFrame::Format_ARGB32, 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000 },
- { QVideoFrame::Format_RGB24 , 24, 24, 4321, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 },
- { QVideoFrame::Format_BGR24 , 24, 24, 4321, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 },
- { QVideoFrame::Format_RGB565, 16, 16, 1234, 0x0000F800, 0x000007E0, 0x0000001F, 0x00000000 }
-};
-
-static int indexOfRgbColor(
- int bits, int depth, int endianness, int red, int green, int blue, int alpha)
-{
- const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat);
-
- for (int i = 0; i < count; ++i) {
- if (qt_rgbColorLookup[i].bitsPerPixel == bits
- && qt_rgbColorLookup[i].depth == depth
- && qt_rgbColorLookup[i].endianness == endianness
- && qt_rgbColorLookup[i].red == red
- && qt_rgbColorLookup[i].green == green
- && qt_rgbColorLookup[i].blue == blue
- && qt_rgbColorLookup[i].alpha == alpha) {
- return i;
- }
- }
- return -1;
-}
+ GstQtVideoSink *sink = GST_QT_VIDEO_SINK(object);
-static GstVideoSinkClass *sink_parent_class;
-
-#define VO_SINK(s) QmlVideoSurfaceGstSink *sink(reinterpret_cast<QmlVideoSurfaceGstSink *>(s))
-
-QmlVideoSurfaceGstSink *QmlVideoSurfaceGstSink::createSink(QAbstractVideoSurface *surface)
-{
- QmlVideoSurfaceGstSink *sink = reinterpret_cast<QmlVideoSurfaceGstSink *>(
- g_object_new(QmlVideoSurfaceGstSink::get_type(), 0));
-
- sink->delegate = new QmlVideoSurfaceGstDelegate(surface);
-
- return sink;
-}
-
-GType QmlVideoSurfaceGstSink::get_type()
-{
- static GType type = 0;
-
- if (type == 0) {
- static const GTypeInfo info =
- {
- sizeof(QmlVideoSurfaceGstSinkClass), // class_size
- base_init, // base_init
- NULL, // base_finalize
- class_init, // class_init
- NULL, // class_finalize
- NULL, // class_data
- sizeof(QmlVideoSurfaceGstSink), // instance_size
- 0, // n_preallocs
- instance_init, // instance_init
- 0 // value_table
- };
-
- type = g_type_register_static(
- GST_TYPE_VIDEO_SINK, "QmlVideoSurfaceGstSink", &info, GTypeFlags(0));
+ switch (prop_id) {
+ case PROP_FORCE_ASPECT_RATIO:
+ sink->surface->setForceAspectRatio(g_value_get_boolean(value));
+ break;
+#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
+ case PROP_GLCONTEXT:
+ sink->surface->setGLContext(static_cast<QGLContext*>(g_value_get_pointer(value)));
+ break;
+#endif
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
}
-
- return type;
-}
-
-void QmlVideoSurfaceGstSink::class_init(gpointer g_class, gpointer class_data)
-{
- Q_UNUSED(class_data);
-
- sink_parent_class = reinterpret_cast<GstVideoSinkClass *>(g_type_class_peek_parent(g_class));
-
- GstBaseSinkClass *base_sink_class = reinterpret_cast<GstBaseSinkClass *>(g_class);
- base_sink_class->get_caps = QmlVideoSurfaceGstSink::get_caps;
- base_sink_class->set_caps = QmlVideoSurfaceGstSink::set_caps;
- base_sink_class->buffer_alloc = QmlVideoSurfaceGstSink::buffer_alloc;
- base_sink_class->start = QmlVideoSurfaceGstSink::start;
- base_sink_class->stop = QmlVideoSurfaceGstSink::stop;
- // base_sink_class->unlock = QmlVideoSurfaceGstSink::unlock; // Not implemented.
- // base_sink_class->event = QmlVideoSurfaceGstSink::event; // Not implemented.
- base_sink_class->preroll = QmlVideoSurfaceGstSink::preroll;
- base_sink_class->render = QmlVideoSurfaceGstSink::render;
-
- GstElementClass *element_class = reinterpret_cast<GstElementClass *>(g_class);
- element_class->change_state = QmlVideoSurfaceGstSink::change_state;
-
- GObjectClass *object_class = reinterpret_cast<GObjectClass *>(g_class);
- object_class->finalize = QmlVideoSurfaceGstSink::finalize;
-}
-
-void QmlVideoSurfaceGstSink::base_init(gpointer g_class)
-{
- static GstStaticPadTemplate sink_pad_template = GST_STATIC_PAD_TEMPLATE(
- "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS(
- "video/x-raw-rgb, "
- "framerate = (fraction) [ 0, MAX ], "
- "width = (int) [ 1, MAX ], "
- "height = (int) [ 1, MAX ]; "
- "video/x-raw-yuv, "
- "framerate = (fraction) [ 0, MAX ], "
- "width = (int) [ 1, MAX ], "
- "height = (int) [ 1, MAX ]"));
-
- gst_element_class_add_pad_template(
- GST_ELEMENT_CLASS(g_class), gst_static_pad_template_get(&sink_pad_template));
}
-void QmlVideoSurfaceGstSink::instance_init(GTypeInstance *instance, gpointer g_class)
+void GstQtVideoSink::get_property(GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
{
- VO_SINK(instance);
+ GstQtVideoSink *sink = GST_QT_VIDEO_SINK(object);
- Q_UNUSED(g_class);
-
- sink->delegate = 0;
-#if defined(Q_WS_X11) && !defined(QT_NO_XVIDEO)
- sink->pool = new QmlGstXvImageBufferPool();
+ switch (prop_id) {
+ case PROP_FORCE_ASPECT_RATIO:
+ g_value_set_boolean(value, sink->surface->forceAspectRatio());
+ break;
+#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
+ case PROP_GLCONTEXT:
+ g_value_set_pointer(value, sink->surface->glContext());
+ break;
#endif
- sink->lastRequestedCaps = 0;
- sink->lastBufferCaps = 0;
- sink->lastSurfaceFormat = new QVideoSurfaceFormat;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
}
-void QmlVideoSurfaceGstSink::finalize(GObject *object)
+GstStateChangeReturn GstQtVideoSink::change_state(GstElement *element, GstStateChange transition)
{
- VO_SINK(object);
-#if defined(Q_WS_X11) && !defined(QT_NO_XVIDEO)
- delete sink->pool;
- sink->pool = 0;
-#endif
-
- delete sink->lastSurfaceFormat;
- sink->lastSurfaceFormat = 0;
-
- if (sink->lastBufferCaps)
- gst_caps_unref(sink->lastBufferCaps);
- sink->lastBufferCaps = 0;
+ GstQtVideoSink *sink = GST_QT_VIDEO_SINK(element);
- if (sink->lastRequestedCaps)
- gst_caps_unref(sink->lastRequestedCaps);
- sink->lastRequestedCaps = 0;
-}
-
-GstStateChangeReturn QmlVideoSurfaceGstSink::change_state(
- GstElement *element, GstStateChange transition)
-{
- VO_SINK(element);
-
- if (sink->delegate != NULL) {
- switch (transition) {
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- sink->delegate->setPlaying(true);
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- sink->delegate->setPlaying(false);
- break;
- default:
- break;
- }
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ sink->surface->setActive(true);
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ sink->surface->setActive(false);
+ break;
+ default:
+ break;
}
- return GST_ELEMENT_CLASS(sink_parent_class)->change_state(
- element, transition);
+ return GST_ELEMENT_CLASS(s_parent_class)->change_state(element, transition);
}
-GstCaps *QmlVideoSurfaceGstSink::get_caps(GstBaseSink *base)
+GstCaps *GstQtVideoSink::get_caps(GstBaseSink *base)
{
- VO_SINK(base);
-
- qDebug() << "QmlVideoSurfaceGstSink::get_caps";
-
+ GstQtVideoSink *sink = GST_QT_VIDEO_SINK(base);
GstCaps *caps = gst_caps_new_empty();
- foreach (QVideoFrame::PixelFormat format, sink->delegate->supportedPixelFormats()) {
- int index = indexOfYuvColor(format);
-
- if (index != -1) {
- GstStructure *structure = gst_structure_new(
- "video/x-raw-yuv",
- "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1,
- "width" , GST_TYPE_INT_RANGE, 1, INT_MAX,
- "height" , GST_TYPE_INT_RANGE, 1, INT_MAX,
- "format" , GST_TYPE_FOURCC, qt_yuvColorLookup[index].fourcc,
- NULL);
-
- gst_caps_append_structure(caps, structure);
- GST_DEBUG("%" GST_PTR_FORMAT, structure);
- continue;
- }
-
- const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat);
-
- for (int i = 0; i < count; ++i) {
- if (qt_rgbColorLookup[i].pixelFormat == format) {
- GstStructure *structure = gst_structure_new(
- "video/x-raw-rgb",
- "framerate" , GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1,
- "width" , GST_TYPE_INT_RANGE, 1, INT_MAX,
- "height" , GST_TYPE_INT_RANGE, 1, INT_MAX,
- "bpp" , G_TYPE_INT, qt_rgbColorLookup[i].bitsPerPixel,
- "depth" , G_TYPE_INT, qt_rgbColorLookup[i].depth,
- "endianness", G_TYPE_INT, qt_rgbColorLookup[i].endianness,
- "red_mask" , G_TYPE_INT, qt_rgbColorLookup[i].red,
- "green_mask", G_TYPE_INT, qt_rgbColorLookup[i].green,
- "blue_mask" , G_TYPE_INT, qt_rgbColorLookup[i].blue,
- NULL);
-
- if (qt_rgbColorLookup[i].alpha != 0) {
- gst_structure_set(
- structure, "alpha_mask", G_TYPE_INT, qt_rgbColorLookup[i].alpha, NULL);
- }
- gst_caps_append_structure(caps, structure);
- GST_DEBUG("%" GST_PTR_FORMAT, structure);
- }
- }
+ Q_FOREACH(BufferFormat::PixelFormat format, sink->surface->supportedPixelFormats()) {
+ gst_caps_append(caps, BufferFormat::pixelFormatToCaps(format));
}
return caps;
}
-gboolean QmlVideoSurfaceGstSink::set_caps(GstBaseSink *base, GstCaps *caps)
+gboolean GstQtVideoSink::set_caps(GstBaseSink *base, GstCaps *caps)
{
- VO_SINK(base);
-
-#ifdef DEBUG_VIDEO_SURFACE_SINK
- qDebug() << "set_caps:";
- qDebug() << gst_caps_to_string(caps);
-#endif
-
- if (!caps) {
- sink->delegate->stop();
-
- return TRUE;
- } else {
- int bytesPerLine = 0;
- QVideoSurfaceFormat format = formatForCaps(caps, &bytesPerLine);
+ GstQtVideoSink *sink = GST_QT_VIDEO_SINK(base);
- if (sink->delegate->isActive()) {
- QVideoSurfaceFormat surfaceFormst = sink->delegate->surfaceFormat();
-
- if (format.pixelFormat() == surfaceFormst.pixelFormat() &&
- format.frameSize() == surfaceFormst.frameSize())
- return TRUE;
- else
- sink->delegate->stop();
- }
-
- if (sink->lastRequestedCaps)
- gst_caps_unref(sink->lastRequestedCaps);
- sink->lastRequestedCaps = 0;
-
-#ifdef DEBUG_VIDEO_SURFACE_SINK
- qDebug() << "Staring video surface, format:";
- qDebug() << format;
- qDebug() << "bytesPerLine:" << bytesPerLine;
-#endif
-
- if (sink->delegate->start(format, bytesPerLine))
- return TRUE;
- else
- qWarning() << "Failed to start video surface";
- }
-
- return FALSE;
-}
-
-QVideoSurfaceFormat QmlVideoSurfaceGstSink::formatForCaps(GstCaps *caps, int *bytesPerLine)
-{
- const GstStructure *structure = gst_caps_get_structure(caps, 0);
-
- QVideoFrame::PixelFormat pixelFormat = QVideoFrame::Format_Invalid;
- int bitsPerPixel = 0;
-
- QSize size;
- gst_structure_get_int(structure, "width", &size.rwidth());
- gst_structure_get_int(structure, "height", &size.rheight());
-
- if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) {
- guint32 fourcc = 0;
- gst_structure_get_fourcc(structure, "format", &fourcc);
-
- int index = indexOfYuvColor(fourcc);
- if (index != -1) {
- pixelFormat = qt_yuvColorLookup[index].pixelFormat;
- bitsPerPixel = qt_yuvColorLookup[index].bitsPerPixel;
- }
- } else if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) {
- int depth = 0;
- int endianness = 0;
- int red = 0;
- int green = 0;
- int blue = 0;
- int alpha = 0;
-
- gst_structure_get_int(structure, "bpp", &bitsPerPixel);
- gst_structure_get_int(structure, "depth", &depth);
- gst_structure_get_int(structure, "endianness", &endianness);
- gst_structure_get_int(structure, "red_mask", &red);
- gst_structure_get_int(structure, "green_mask", &green);
- gst_structure_get_int(structure, "blue_mask", &blue);
- gst_structure_get_int(structure, "alpha_mask", &alpha);
-
- int index = indexOfRgbColor(bitsPerPixel, depth, endianness, red, green, blue, alpha);
-
- if (index != -1)
- pixelFormat = qt_rgbColorLookup[index].pixelFormat;
- }
-
- if (pixelFormat != QVideoFrame::Format_Invalid) {
- QVideoSurfaceFormat format(size, pixelFormat);
-
- QPair<int, int> rate;
- gst_structure_get_fraction(structure, "framerate", &rate.first, &rate.second);
-
- if (rate.second)
- format.setFrameRate(qreal(rate.first)/rate.second);
-
- gint aspectNum = 0;
- gint aspectDenum = 0;
- if (gst_structure_get_fraction(
- structure, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) {
- if (aspectDenum > 0)
- format.setPixelAspectRatio(aspectNum, aspectDenum);
- }
-
- if (bytesPerLine)
- *bytesPerLine = ((size.width() * bitsPerPixel / 8) + 3) & ~3;
-
- return format;
- }
-
- return QVideoSurfaceFormat();
+ GST_LOG_OBJECT(sink, "new caps %" GST_PTR_FORMAT, caps);
+ sink->formatDirty = true;
+ return TRUE;
}
-
-GstFlowReturn QmlVideoSurfaceGstSink::buffer_alloc(
- GstBaseSink *base, guint64 offset, guint size, GstCaps *caps, GstBuffer **buffer)
+GstFlowReturn GstQtVideoSink::buffer_alloc(GstBaseSink *base, guint64 offset, guint size,
+ GstCaps *caps, GstBuffer **buffer)
{
- VO_SINK(base);
-
+ Q_UNUSED(base);
Q_UNUSED(offset);
Q_UNUSED(size);
+ Q_UNUSED(caps);
*buffer = 0;
-#if defined(Q_WS_X11) && !defined(QT_NO_XVIDEO)
-
- if (sink->lastRequestedCaps && gst_caps_is_equal(sink->lastRequestedCaps, caps)) {
- //qDebug() << "reusing last caps";
- *buffer = GST_BUFFER(sink->pool->takeBuffer(*sink->lastSurfaceFormat, sink->lastBufferCaps));
- return GST_FLOW_OK;
- }
-
- if (sink->delegate->supportedPixelFormats(QAbstractVideoBuffer::XvShmImageHandle).isEmpty()) {
- //qDebug() << "sink doesn't support Xv buffers, skip buffers allocation";
- return GST_FLOW_OK;
- }
-
- GstCaps *intersection = gst_caps_intersect(get_caps(GST_BASE_SINK(sink)), caps);
-
- if (gst_caps_is_empty (intersection)) {
- gst_caps_unref(intersection);
- return GST_FLOW_NOT_NEGOTIATED;
- }
-
- if (sink->delegate->isActive()) {
- //if format was changed, restart the surface
- QVideoSurfaceFormat format = formatForCaps(intersection);
- QVideoSurfaceFormat surfaceFormat = sink->delegate->surfaceFormat();
-
- if (format.pixelFormat() != surfaceFormat.pixelFormat() ||
- format.frameSize() != surfaceFormat.frameSize()) {
-#ifdef DEBUG_VIDEO_SURFACE_SINK
- qDebug() << "new format requested, restart video surface";
-#endif
- sink->delegate->stop();
- }
- }
-
- if (!sink->delegate->isActive()) {
- int bytesPerLine = 0;
- QVideoSurfaceFormat format = formatForCaps(intersection, &bytesPerLine);
-
- if (!sink->delegate->start(format, bytesPerLine)) {
- qWarning() << "failed to start video surface";
- return GST_FLOW_NOT_NEGOTIATED;
- }
- }
-
- QVideoSurfaceFormat surfaceFormat = sink->delegate->surfaceFormat();
-
- if (!sink->pool->isFormatSupported(surfaceFormat)) {
- //qDebug() << "sink doesn't provide Xv buffer details, skip buffers allocation";
- return GST_FLOW_OK;
- }
-
- if (sink->lastRequestedCaps)
- gst_caps_unref(sink->lastRequestedCaps);
- sink->lastRequestedCaps = caps;
- gst_caps_ref(sink->lastRequestedCaps);
-
- if (sink->lastBufferCaps)
- gst_caps_unref(sink->lastBufferCaps);
- sink->lastBufferCaps = intersection;
- gst_caps_ref(sink->lastBufferCaps);
-
- *sink->lastSurfaceFormat = surfaceFormat;
-
- *buffer = GST_BUFFER(sink->pool->takeBuffer(surfaceFormat, intersection));
-
-#endif
return GST_FLOW_OK;
}
-gboolean QmlVideoSurfaceGstSink::start(GstBaseSink *base)
+GstFlowReturn GstQtVideoSink::show_frame(GstVideoSink *video_sink, GstBuffer *buffer)
{
- Q_UNUSED(base);
-
- return TRUE;
-}
-
-gboolean QmlVideoSurfaceGstSink::stop(GstBaseSink *base)
-{
- Q_UNUSED(base);
+ GstQtVideoSink *sink = GST_QT_VIDEO_SINK(video_sink);
- return TRUE;
-}
+ GST_TRACE_OBJECT(sink, "Posting new buffer (%"GST_PTR_FORMAT") for rendering. "
+ "Format dirty: %d", buffer, (int)sink->formatDirty);
-gboolean QmlVideoSurfaceGstSink::unlock(GstBaseSink *base)
-{
- Q_UNUSED(base);
+ QCoreApplication::postEvent(sink->surface,
+ new GstQtVideoSinkSurface::BufferEvent(buffer, sink->formatDirty));
- return TRUE;
-}
-
-gboolean QmlVideoSurfaceGstSink::event(GstBaseSink *base, GstEvent *event)
-{
- Q_UNUSED(base);
- Q_UNUSED(event);
-
- return TRUE;
-}
-
-GstFlowReturn QmlVideoSurfaceGstSink::preroll(GstBaseSink *base, GstBuffer *buffer)
-{
- VO_SINK(base);
- return sink->delegate->render(buffer);
+ sink->formatDirty = false;
+ return GST_FLOW_OK;
}
-GstFlowReturn QmlVideoSurfaceGstSink::render(GstBaseSink *base, GstBuffer *buffer)
+void GstQtVideoSink::paint(GstQtVideoSink *sink, gpointer painter,
+ gint x, gint y, gint width, gint height)
{
- VO_SINK(base);
- return sink->delegate->render(buffer);
+ sink->surface->paint(static_cast<QPainter*>(painter), x, y, width, height);
}
-
diff --git a/elements/gstqtvideosink/gstqtvideosink.h b/elements/gstqtvideosink/gstqtvideosink.h
index 49453ff..3f85ada 100644
--- a/elements/gstqtvideosink/gstqtvideosink.h
+++ b/elements/gstqtvideosink/gstqtvideosink.h
@@ -1,136 +1,97 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 Collabora Ltd
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file was originally part of the Qt Mobility Components.
-**
-** 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.
-**
-****************************************************************************/
-
-#ifndef VIDEOSURFACEGSTSINK_H
-#define VIDEOSURFACEGSTSINK_H
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
+ Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
-#include <gst/video/gstvideosink.h>
-
-#include <QtCore/qlist.h>
-#include <QtCore/qmutex.h>
-#include <QtCore/qqueue.h>
-#include <QtCore/qpointer.h>
-#include <QtCore/qwaitcondition.h>
-#include <qvideosurfaceformat.h>
-#include <qvideoframe.h>
-#include <qabstractvideobuffer.h>
-
-QT_BEGIN_NAMESPACE
-class QAbstractVideoSurface;
-QT_END_NAMESPACE
-
-#if defined(Q_WS_X11) && !defined(QT_NO_XVIDEO)
-class QmlGstXvImageBuffer;
-class QmlGstXvImageBufferPool;
-#endif
-
-class QmlVideoSurfaceGstDelegate : public QObject
-{
- Q_OBJECT
-public:
- QmlVideoSurfaceGstDelegate(QAbstractVideoSurface *surface);
-
- QList<QVideoFrame::PixelFormat> supportedPixelFormats(
- QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const;
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1
+ as published by the Free Software Foundation.
- QVideoSurfaceFormat surfaceFormat() const;
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
- bool start(const QVideoSurfaceFormat &format, int bytesPerLine);
- void stop();
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
- bool isActive();
+#ifndef GST_QT_VIDEO_SINK_H
+#define GST_QT_VIDEO_SINK_H
- void setPlaying(bool playing);
-
- GstFlowReturn render(GstBuffer *buffer);
-
-private slots:
- void queuedStart();
- void queuedStop();
- void queuedRender();
+#include "gstqtvideosinkplugin.h"
+#include <gst/video/gstvideosink.h>
- void supportedFormatsChanged();
+#define GST_TYPE_QT_VIDEO_SINK \
+ (GstQtVideoSink::get_type())
+#define GST_QT_VIDEO_SINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_QT_VIDEO_SINK,GstQtVideoSink))
+#define GST_QT_VIDEO_SINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_QT_VIDEO_SINK,GstQtVideoSinkClass))
-private:
- QPointer<QAbstractVideoSurface> m_surface;
- QList<QVideoFrame::PixelFormat> m_supportedPixelFormats;
- QList<QVideoFrame::PixelFormat> m_supportedXVideoPixelFormats;
- QMutex m_mutex;
- QWaitCondition m_setupCondition;
- QWaitCondition m_renderCondition;
- QVideoSurfaceFormat m_format;
- QVideoFrame m_frame;
- GstFlowReturn m_renderReturn;
- int m_bytesPerLine;
- bool m_started;
-};
+class GstQtVideoSinkSurface;
-class QmlVideoSurfaceGstSink
+struct GstQtVideoSink
{
public:
GstVideoSink parent;
- static QmlVideoSurfaceGstSink *createSink(QAbstractVideoSurface *surface);
- static QVideoSurfaceFormat formatForCaps(GstCaps *caps, int *bytesPerLine = 0);
+ static GType get_type();
private:
- static GType get_type();
- static void class_init(gpointer g_class, gpointer class_data);
static void base_init(gpointer g_class);
- static void instance_init(GTypeInstance *instance, gpointer g_class);
-
+ static void class_init(gpointer g_class, gpointer class_data);
+ static void init(GTypeInstance *instance, gpointer g_class);
static void finalize(GObject *object);
+ static void set_property(GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+ static void get_property(GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition);
static GstCaps *get_caps(GstBaseSink *sink);
static gboolean set_caps(GstBaseSink *sink, GstCaps *caps);
+ static GstFlowReturn buffer_alloc(GstBaseSink *sink, guint64 offset, guint size,
+ GstCaps *caps, GstBuffer **buffer);
- static GstFlowReturn buffer_alloc(
- GstBaseSink *sink, guint64 offset, guint size, GstCaps *caps, GstBuffer **buffer);
-
- static gboolean start(GstBaseSink *sink);
- static gboolean stop(GstBaseSink *sink);
+ static GstFlowReturn show_frame(GstVideoSink *sink, GstBuffer *buffer);
- static gboolean unlock(GstBaseSink *sink);
-
- static gboolean event(GstBaseSink *sink, GstEvent *event);
- static GstFlowReturn preroll(GstBaseSink *sink, GstBuffer *buffer);
- static GstFlowReturn render(GstBaseSink *sink, GstBuffer *buffer);
+ static void paint(GstQtVideoSink *sink, gpointer painter,
+ gint x, gint y, gint width, gint height);
private:
- QmlVideoSurfaceGstDelegate *delegate;
-
-#if defined(Q_WS_X11) && !defined(QT_NO_XVIDEO)
- QmlGstXvImageBufferPool *pool;
+ GstQtVideoSinkSurface *surface;
+ bool formatDirty;
+
+ enum {
+ PROP_0,
+ PROP_FORCE_ASPECT_RATIO,
+#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
+ PROP_GLCONTEXT
#endif
+ };
+
+ enum {
+ PAINT_SIGNAL,
+ UPDATE_SIGNAL,
+ LAST_SIGNAL
+ };
- GstCaps *lastRequestedCaps;
- GstCaps *lastBufferCaps;
- QVideoSurfaceFormat *lastSurfaceFormat;
+ static guint s_signals[LAST_SIGNAL];
+ static GstVideoSinkClass *s_parent_class;
+
+ friend class GstQtVideoSinkSurface;
};
-class QmlVideoSurfaceGstSinkClass
+struct GstQtVideoSinkClass
{
-public:
GstVideoSinkClass parent_class;
+
+ /* paint action signal */
+ void (*paint) (GstQtVideoSink *sink, gpointer painter,
+ gint x, gint y, gint width, gint height);
};
#endif
diff --git a/elements/gstqtvideosink/gstqtvideosinkplugin.cpp b/elements/gstqtvideosink/gstqtvideosinkplugin.cpp
new file mode 100644
index 0000000..b694609
--- /dev/null
+++ b/elements/gstqtvideosink/gstqtvideosinkplugin.cpp
@@ -0,0 +1,42 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
+ Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "gstqtvideosinkplugin.h"
+#include "gstqtvideosink.h"
+
+GST_DEBUG_CATEGORY(gst_qt_video_sink_debug);
+
+/* entry point to initialize the plug-in */
+static gboolean plugin_init(GstPlugin *plugin)
+{
+ GST_DEBUG_CATEGORY_INIT(gst_qt_video_sink_debug, "qtvideosink", 0,
+ "Debug category for GstQtVideoSink");
+
+ return gst_element_register(plugin, "qtvideosink", GST_RANK_NONE, GST_TYPE_QT_VIDEO_SINK);
+}
+
+GST_PLUGIN_DEFINE (
+ GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "qtvideosink",
+ "A video sink that can draw on any Qt surface",
+ plugin_init,
+ PACKAGE_VERSION,
+ "LGPL",
+ PACKAGE_NAME,
+ PACKAGE_ORIGIN
+)
diff --git a/elements/gstqtvideosink/gstqtvideosinkplugin.h b/elements/gstqtvideosink/gstqtvideosinkplugin.h
new file mode 100644
index 0000000..3869ad6
--- /dev/null
+++ b/elements/gstqtvideosink/gstqtvideosinkplugin.h
@@ -0,0 +1,25 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
+ Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef GST_QT_VIDEO_SINK_PLUGIN_H
+#define GST_QT_VIDEO_SINK_PLUGIN_H
+
+#include <gst/gst.h>
+
+GST_DEBUG_CATEGORY_EXTERN(gst_qt_video_sink_debug);
+#define GST_CAT_DEFAULT gst_qt_video_sink_debug
+
+#endif
diff --git a/elements/gstqtvideosink/gstqtvideosinksurface.cpp b/elements/gstqtvideosink/gstqtvideosinksurface.cpp
index 734a629..0b686ae 100644
--- a/elements/gstqtvideosink/gstqtvideosinksurface.cpp
+++ b/elements/gstqtvideosink/gstqtvideosinksurface.cpp
@@ -1,1698 +1,434 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 Collabora Ltd
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file was originally part of the Qt Mobility Components.
-**
-** 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.
-**
-****************************************************************************/
-
-#include "qmlpaintervideosurface.h"
-//#include <qpaintervideosurface_mac_p.h>
-
-#include <qmath.h>
-
-#include <qpainter.h>
-#include <qvariant.h>
-#include <qvideosurfaceformat.h>
-
-#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_1_CL) && !defined(QT_OPENGL_ES_1)
-#include <qglshaderprogram.h>
-#endif
-
-#include <QtDebug>
-QT_BEGIN_NAMESPACE
-
-QmlVideoSurfacePainter::~QmlVideoSurfacePainter()
-{
-}
-
-class QmlVideoSurfaceGenericPainter : public QmlVideoSurfacePainter
-{
-public:
- QmlVideoSurfaceGenericPainter();
-
- QList<QVideoFrame::PixelFormat> supportedPixelFormats(
- QAbstractVideoBuffer::HandleType handleType) const;
-
- bool isFormatSupported(
- const QVideoSurfaceFormat &format, QVideoSurfaceFormat *similar) const;
-
- QAbstractVideoSurface::Error start(const QVideoSurfaceFormat &format);
- void stop();
-
- QAbstractVideoSurface::Error setCurrentFrame(const QVideoFrame &frame);
-
- QAbstractVideoSurface::Error paint(
- const QRectF &target, QPainter *painter, const QRectF &source);
-
- void updateColors(int brightness, int contrast, int hue, int saturation);
-
-private:
- QList<QVideoFrame::PixelFormat> m_imagePixelFormats;
- QVideoFrame m_frame;
- QSize m_imageSize;
- QImage::Format m_imageFormat;
- QVideoSurfaceFormat::Direction m_scanLineDirection;
-};
-
-QmlVideoSurfaceGenericPainter::QmlVideoSurfaceGenericPainter()
- : m_imageFormat(QImage::Format_Invalid)
- , m_scanLineDirection(QVideoSurfaceFormat::TopToBottom)
-{
- qDebug() << "GenericPainter";
- m_imagePixelFormats
- << QVideoFrame::Format_RGB32
-#ifndef QT_OPENGL_ES // The raster formats should be a subset of the GL formats.
- << QVideoFrame::Format_RGB24
-#endif
- << QVideoFrame::Format_ARGB32
- << QVideoFrame::Format_RGB565;
-}
-
-QList<QVideoFrame::PixelFormat> QmlVideoSurfaceGenericPainter::supportedPixelFormats(
- QAbstractVideoBuffer::HandleType handleType) const
-{
- qDebug() << "GenericPainter::supportedPixelFormats";
- switch (handleType) {
- case QAbstractVideoBuffer::QPixmapHandle:
- case QAbstractVideoBuffer::NoHandle:
- return m_imagePixelFormats;
- default:
- ;
- }
- return QList<QVideoFrame::PixelFormat>();
-}
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
+ Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
-bool QmlVideoSurfaceGenericPainter::isFormatSupported(
- const QVideoSurfaceFormat &format, QVideoSurfaceFormat *) const
-{
- switch (format.handleType()) {
- case QAbstractVideoBuffer::QPixmapHandle:
- return true;
- case QAbstractVideoBuffer::NoHandle:
- return m_imagePixelFormats.contains(format.pixelFormat())
- && !format.frameSize().isEmpty();
- default:
- ;
- }
- return false;
-}
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1
+ as published by the Free Software Foundation.
-QAbstractVideoSurface::Error QmlVideoSurfaceGenericPainter::start(const QVideoSurfaceFormat &format)
-{
- m_frame = QVideoFrame();
- m_imageFormat = QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat());
- m_imageSize = format.frameSize();
- m_scanLineDirection = format.scanLineDirection();
-
- const QAbstractVideoBuffer::HandleType t = format.handleType();
- if (t == QAbstractVideoBuffer::NoHandle) {
- if (m_imageFormat != QImage::Format_Invalid
-#ifdef QT_OPENGL_ES
- && format.pixelFormat() != QVideoFrame::Format_RGB24
-#endif
- && !m_imageSize.isEmpty()) {
- return QAbstractVideoSurface::NoError;
- }
- } else if (t == QAbstractVideoBuffer::QPixmapHandle) {
- return QAbstractVideoSurface::NoError;
- }
- return QAbstractVideoSurface::UnsupportedFormatError;
-}
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
-void QmlVideoSurfaceGenericPainter::stop()
-{
- m_frame = QVideoFrame();
-}
-
-QAbstractVideoSurface::Error QmlVideoSurfaceGenericPainter::setCurrentFrame(const QVideoFrame &frame)
-{
- m_frame = frame;
-
- return QAbstractVideoSurface::NoError;
-}
-
-QAbstractVideoSurface::Error QmlVideoSurfaceGenericPainter::paint(
- const QRectF &target, QPainter *painter, const QRectF &source)
-{
- if (m_frame.handleType() == QAbstractVideoBuffer::QPixmapHandle) {
- painter->drawPixmap(target, m_frame.handle().value<QPixmap>(), source);
- } else if (m_frame.map(QAbstractVideoBuffer::ReadOnly)) {
- QImage image(
- m_frame.bits(),
- m_imageSize.width(),
- m_imageSize.height(),
- m_frame.bytesPerLine(),
- m_imageFormat);
-
- if (m_scanLineDirection == QVideoSurfaceFormat::BottomToTop) {
- const QTransform oldTransform = painter->transform();
-
- painter->scale(1, -1);
- painter->translate(0, -target.bottom());
- painter->drawImage(
- QRectF(target.x(), 0, target.width(), target.height()), image, source);
- painter->setTransform(oldTransform);
- } else {
- painter->drawImage(target, image, source);
- }
-
- m_frame.unmap();
- } else if (m_frame.isValid()) {
- return QAbstractVideoSurface::IncorrectFormatError;
- } else {
- painter->fillRect(target, Qt::black);
- }
- return QAbstractVideoSurface::NoError;
-}
-
-void QmlVideoSurfaceGenericPainter::updateColors(int, int, int, int)
-{
-}
-
-#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_1_CL) && !defined(QT_OPENGL_ES_1)
-
-#ifndef Q_WS_MAC
-# ifndef APIENTRYP
-# ifdef APIENTRY
-# define APIENTRYP APIENTRY *
-# else
-# define APIENTRY
-# define APIENTRYP *
-# endif
-# endif
-#else
-# define APIENTRY
-# define APIENTRYP *
-#endif
-
-#ifndef GL_TEXTURE0
-# define GL_TEXTURE0 0x84C0
-# define GL_TEXTURE1 0x84C1
-# define GL_TEXTURE2 0x84C2
-#endif
-#ifndef GL_PROGRAM_ERROR_STRING_ARB
-# define GL_PROGRAM_ERROR_STRING_ARB 0x8874
-#endif
-
-#ifndef GL_UNSIGNED_SHORT_5_6_5
-# define GL_UNSIGNED_SHORT_5_6_5 33635
-#endif
-
-class QmlVideoSurfaceGLPainter : public QmlVideoSurfacePainter
-{
-public:
- QmlVideoSurfaceGLPainter(QGLContext *context);
- ~QmlVideoSurfaceGLPainter();
- QList<QVideoFrame::PixelFormat> supportedPixelFormats(
- QAbstractVideoBuffer::HandleType handleType) const;
-
- bool isFormatSupported(
- const QVideoSurfaceFormat &format, QVideoSurfaceFormat *similar) const;
-
- QAbstractVideoSurface::Error setCurrentFrame(const QVideoFrame &frame);
-
- QAbstractVideoSurface::Error paint(
- const QRectF &target, QPainter *painter, const QRectF &source);
-
- void updateColors(int brightness, int contrast, int hue, int saturation);
- void viewportDestroyed();
-
-protected:
- void initRgbTextureInfo(GLenum internalFormat, GLuint format, GLenum type, const QSize &size);
- void initYuv420PTextureInfo(const QSize &size);
- void initYv12TextureInfo(const QSize &size);
-
-#ifndef QT_OPENGL_ES
- typedef void (APIENTRY *_glActiveTexture) (GLenum);
- _glActiveTexture glActiveTexture;
-#endif
-
- QList<QVideoFrame::PixelFormat> m_imagePixelFormats;
- QList<QVideoFrame::PixelFormat> m_glPixelFormats;
- QMatrix4x4 m_colorMatrix;
- QVideoFrame m_frame;
-
- QGLContext *m_context;
- QAbstractVideoBuffer::HandleType m_handleType;
- QVideoSurfaceFormat::Direction m_scanLineDirection;
- QVideoSurfaceFormat::YCbCrColorSpace m_colorSpace;
- GLenum m_textureFormat;
- GLuint m_textureInternalFormat;
- GLenum m_textureType;
- int m_textureCount;
- GLuint m_textureIds[3];
- int m_textureWidths[3];
- int m_textureHeights[3];
- int m_textureOffsets[3];
- bool m_yuv;
-};
-
-QmlVideoSurfaceGLPainter::QmlVideoSurfaceGLPainter(QGLContext *context)
- : m_context(context)
- , m_handleType(QAbstractVideoBuffer::NoHandle)
- , m_scanLineDirection(QVideoSurfaceFormat::TopToBottom)
- , m_colorSpace(QVideoSurfaceFormat::YCbCr_BT601)
- , m_textureFormat(0)
- , m_textureInternalFormat(0)
- , m_textureType(0)
- , m_textureCount(0)
- , m_yuv(false)
-{
-#ifndef QT_OPENGL_ES
- glActiveTexture = (_glActiveTexture)m_context->getProcAddress(QLatin1String("glActiveTexture"));
-#endif
-}
-
-QmlVideoSurfaceGLPainter::~QmlVideoSurfaceGLPainter()
-{
-}
-
-void QmlVideoSurfaceGLPainter::viewportDestroyed()
-{
- m_context = 0;
-}
-
-QList<QVideoFrame::PixelFormat> QmlVideoSurfaceGLPainter::supportedPixelFormats(
- QAbstractVideoBuffer::HandleType handleType) const
-{
- qDebug() << "GLPainter::supportedPixelFormats";
- switch (handleType) {
- case QAbstractVideoBuffer::NoHandle:
- return m_imagePixelFormats;
- case QAbstractVideoBuffer::QPixmapHandle:
- case QAbstractVideoBuffer::GLTextureHandle:
- return m_glPixelFormats;
- default:
- ;
- }
- return QList<QVideoFrame::PixelFormat>();
-}
-
-bool QmlVideoSurfaceGLPainter::isFormatSupported(
- const QVideoSurfaceFormat &format, QVideoSurfaceFormat *) const
-{
- if (format.frameSize().isEmpty()) {
- return false;
- } else {
- switch (format.handleType()) {
- case QAbstractVideoBuffer::NoHandle:
- return m_imagePixelFormats.contains(format.pixelFormat());
- case QAbstractVideoBuffer::QPixmapHandle:
- case QAbstractVideoBuffer::GLTextureHandle:
- return m_glPixelFormats.contains(format.pixelFormat());
- default:
- ;
- }
- }
- return false;
-}
-
-QAbstractVideoSurface::Error QmlVideoSurfaceGLPainter::setCurrentFrame(const QVideoFrame &frame)
-{
- m_frame = frame;
-
- if (m_handleType == QAbstractVideoBuffer::GLTextureHandle) {
- m_textureIds[0] = frame.handle().toInt();
- } else if (m_frame.map(QAbstractVideoBuffer::ReadOnly)) {
- m_context->makeCurrent();
-
- for (int i = 0; i < m_textureCount; ++i) {
- glBindTexture(GL_TEXTURE_2D, m_textureIds[i]);
- glTexImage2D(
- GL_TEXTURE_2D,
- 0,
- m_textureInternalFormat,
- m_textureWidths[i],
- m_textureHeights[i],
- 0,
- m_textureFormat,
- m_textureType,
- m_frame.bits() + m_textureOffsets[i]);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- }
- m_frame.unmap();
- } else if (m_handleType != QAbstractVideoBuffer::QPixmapHandle && m_frame.isValid()) {
- return QAbstractVideoSurface::IncorrectFormatError;
- }
-
- return QAbstractVideoSurface::NoError;
-}
-
-QAbstractVideoSurface::Error QmlVideoSurfaceGLPainter::paint(
- const QRectF &target, QPainter *painter, const QRectF &source)
-{
- if (m_frame.handleType() == QAbstractVideoBuffer::QPixmapHandle) {
- painter->drawPixmap(target, m_frame.handle().value<QPixmap>(), source);
- } else if (m_frame.isValid()) {
- return QAbstractVideoSurface::IncorrectFormatError;
- } else {
- painter->fillRect(target, Qt::black);
- }
- return QAbstractVideoSurface::NoError;
-}
-
-void QmlVideoSurfaceGLPainter::updateColors(int brightness, int contrast, int hue, int saturation)
-{
- const qreal b = brightness / 200.0;
- const qreal c = contrast / 100.0 + 1.0;
- const qreal h = hue / 100.0;
- const qreal s = saturation / 100.0 + 1.0;
-
- const qreal cosH = qCos(M_PI * h);
- const qreal sinH = qSin(M_PI * h);
-
- const qreal h11 = 0.787 * cosH - 0.213 * sinH + 0.213;
- const qreal h21 = -0.213 * cosH + 0.143 * sinH + 0.213;
- const qreal h31 = -0.213 * cosH - 0.787 * sinH + 0.213;
-
- const qreal h12 = -0.715 * cosH - 0.715 * sinH + 0.715;
- const qreal h22 = 0.285 * cosH + 0.140 * sinH + 0.715;
- const qreal h32 = -0.715 * cosH + 0.715 * sinH + 0.715;
-
- const qreal h13 = -0.072 * cosH + 0.928 * sinH + 0.072;
- const qreal h23 = -0.072 * cosH - 0.283 * sinH + 0.072;
- const qreal h33 = 0.928 * cosH + 0.072 * sinH + 0.072;
-
- const qreal sr = (1.0 - s) * 0.3086;
- const qreal sg = (1.0 - s) * 0.6094;
- const qreal sb = (1.0 - s) * 0.0820;
-
- const qreal sr_s = sr + s;
- const qreal sg_s = sg + s;
- const qreal sb_s = sr + s;
-
- const float m4 = (s + sr + sg + sb) * (0.5 - 0.5 * c + b);
-
- m_colorMatrix(0, 0) = c * (sr_s * h11 + sg * h21 + sb * h31);
- m_colorMatrix(0, 1) = c * (sr_s * h12 + sg * h22 + sb * h32);
- m_colorMatrix(0, 2) = c * (sr_s * h13 + sg * h23 + sb * h33);
- m_colorMatrix(0, 3) = m4;
-
- m_colorMatrix(1, 0) = c * (sr * h11 + sg_s * h21 + sb * h31);
- m_colorMatrix(1, 1) = c * (sr * h12 + sg_s * h22 + sb * h32);
- m_colorMatrix(1, 2) = c * (sr * h13 + sg_s * h23 + sb * h33);
- m_colorMatrix(1, 3) = m4;
-
- m_colorMatrix(2, 0) = c * (sr * h11 + sg * h21 + sb_s * h31);
- m_colorMatrix(2, 1) = c * (sr * h12 + sg * h22 + sb_s * h32);
- m_colorMatrix(2, 2) = c * (sr * h13 + sg * h23 + sb_s * h33);
- m_colorMatrix(2, 3) = m4;
-
- m_colorMatrix(3, 0) = 0.0;
- m_colorMatrix(3, 1) = 0.0;
- m_colorMatrix(3, 2) = 0.0;
- m_colorMatrix(3, 3) = 1.0;
-
- if (m_yuv) {
- QMatrix4x4 colorSpaceMatrix;
-
- switch (m_colorSpace) {
- case QVideoSurfaceFormat::YCbCr_JPEG:
- colorSpaceMatrix = QMatrix4x4(
- 1.0, 0.000, 1.402, -0.701,
- 1.0, -0.344, -0.714, 0.529,
- 1.0, 1.772, 0.000, -0.886,
- 0.0, 0.000, 0.000, 1.0000);
- break;
- case QVideoSurfaceFormat::YCbCr_BT709:
- case QVideoSurfaceFormat::YCbCr_xvYCC709:
- colorSpaceMatrix = QMatrix4x4(
- 1.164, 0.000, 1.793, -0.5727,
- 1.164, -0.534, -0.213, 0.3007,
- 1.164, 2.115, 0.000, -1.1302,
- 0.0, 0.000, 0.000, 1.0000);
- break;
- default: //BT 601:
- colorSpaceMatrix = QMatrix4x4(
- 1.164, 0.000, 1.596, -0.8708,
- 1.164, -0.392, -0.813, 0.5296,
- 1.164, 2.017, 0.000, -1.081,
- 0.0, 0.000, 0.000, 1.0000);
- }
-
- m_colorMatrix = m_colorMatrix * colorSpaceMatrix;
- }
-}
-
-void QmlVideoSurfaceGLPainter::initRgbTextureInfo(
- GLenum internalFormat, GLuint format, GLenum type, const QSize &size)
-{
- m_yuv = false;
- m_textureInternalFormat = internalFormat;
- m_textureFormat = format;
- m_textureType = type;
- m_textureCount = 1;
- m_textureWidths[0] = size.width();
- m_textureHeights[0] = size.height();
- m_textureOffsets[0] = 0;
-}
-
-void QmlVideoSurfaceGLPainter::initYuv420PTextureInfo(const QSize &size)
-{
- int bytesPerLine = (size.width() + 3) & ~3;
- int bytesPerLine2 = (size.width() / 2 + 3) & ~3;
-
- m_yuv = true;
- m_textureInternalFormat = GL_LUMINANCE;
- m_textureFormat = GL_LUMINANCE;
- m_textureType = GL_UNSIGNED_BYTE;
- m_textureCount = 3;
- m_textureWidths[0] = bytesPerLine;
- m_textureHeights[0] = size.height();
- m_textureOffsets[0] = 0;
- m_textureWidths[1] = bytesPerLine2;
- m_textureHeights[1] = size.height() / 2;
- m_textureOffsets[1] = bytesPerLine * size.height();
- m_textureWidths[2] = bytesPerLine2;
- m_textureHeights[2] = size.height() / 2;
- m_textureOffsets[2] = bytesPerLine * size.height() + bytesPerLine2 * size.height()/2;
-}
-
-void QmlVideoSurfaceGLPainter::initYv12TextureInfo(const QSize &size)
-{
- int bytesPerLine = (size.width() + 3) & ~3;
- int bytesPerLine2 = (size.width() / 2 + 3) & ~3;
-
- m_yuv = true;
- m_textureInternalFormat = GL_LUMINANCE;
- m_textureFormat = GL_LUMINANCE;
- m_textureType = GL_UNSIGNED_BYTE;
- m_textureCount = 3;
- m_textureWidths[0] = bytesPerLine;
- m_textureHeights[0] = size.height();
- m_textureOffsets[0] = 0;
- m_textureWidths[1] = bytesPerLine2;
- m_textureHeights[1] = size.height() / 2;
- m_textureOffsets[1] = bytesPerLine * size.height() + bytesPerLine2 * size.height()/2;
- m_textureWidths[2] = bytesPerLine2;
- m_textureHeights[2] = size.height() / 2;
- m_textureOffsets[2] = bytesPerLine * size.height();
-}
-
-#ifndef QT_OPENGL_ES
-
-# ifndef GL_FRAGMENT_PROGRAM_ARB
-# define GL_FRAGMENT_PROGRAM_ARB 0x8804
-# define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875
-# endif
-
-// Paints an RGB32 frame
-static const char *qt_arbfp_xrgbShaderProgram =
- "!!ARBfp1.0\n"
- "PARAM matrix[4] = { program.local[0..2],"
- "{ 0.0, 0.0, 0.0, 1.0 } };\n"
- "TEMP xrgb;\n"
- "TEX xrgb.xyz, fragment.texcoord[0], texture[0], 2D;\n"
- "MOV xrgb.w, matrix[3].w;\n"
- "DP4 result.color.x, xrgb.zyxw, matrix[0];\n"
- "DP4 result.color.y, xrgb.zyxw, matrix[1];\n"
- "DP4 result.color.z, xrgb.zyxw, matrix[2];\n"
- "END";
-
-// Paints an ARGB frame.
-static const char *qt_arbfp_argbShaderProgram =
- "!!ARBfp1.0\n"
- "PARAM matrix[4] = { program.local[0..2],"
- "{ 0.0, 0.0, 0.0, 1.0 } };\n"
- "TEMP argb;\n"
- "TEX argb, fragment.texcoord[0], texture[0], 2D;\n"
- "MOV argb.w, matrix[3].w;\n"
- "DP4 result.color.x, argb.zyxw, matrix[0];\n"
- "DP4 result.color.y, argb.zyxw, matrix[1];\n"
- "DP4 result.color.z, argb.zyxw, matrix[2];\n"
- "TEX result.color.w, fragment.texcoord[0], texture, 2D;\n"
- "END";
-
-// Paints an RGB(A) frame.
-static const char *qt_arbfp_rgbShaderProgram =
- "!!ARBfp1.0\n"
- "PARAM matrix[4] = { program.local[0..2],"
- "{ 0.0, 0.0, 0.0, 1.0 } };\n"
- "TEMP rgb;\n"
- "TEX rgb, fragment.texcoord[0], texture[0], 2D;\n"
- "MOV rgb.w, matrix[3].w;\n"
- "DP4 result.color.x, rgb, matrix[0];\n"
- "DP4 result.color.y, rgb, matrix[1];\n"
- "DP4 result.color.z, rgb, matrix[2];\n"
- "TEX result.color.w, fragment.texcoord[0], texture, 2D;\n"
- "END";
-
-// Paints a YUV420P or YV12 frame.
-static const char *qt_arbfp_yuvPlanarShaderProgram =
- "!!ARBfp1.0\n"
- "PARAM matrix[4] = { program.local[0..2],"
- "{ 0.0, 0.0, 0.0, 1.0 } };\n"
- "TEMP yuv;\n"
- "TEX yuv.x, fragment.texcoord[0], texture[0], 2D;\n"
- "TEX yuv.y, fragment.texcoord[0], texture[1], 2D;\n"
- "TEX yuv.z, fragment.texcoord[0], texture[2], 2D;\n"
- "MOV yuv.w, matrix[3].w;\n"
- "DP4 result.color.x, yuv, matrix[0];\n"
- "DP4 result.color.y, yuv, matrix[1];\n"
- "DP4 result.color.z, yuv, matrix[2];\n"
- "END";
-
-// Paints a YUV444 frame.
-static const char *qt_arbfp_xyuvShaderProgram =
- "!!ARBfp1.0\n"
- "PARAM matrix[4] = { program.local[0..2],"
- "{ 0.0, 0.0, 0.0, 1.0 } };\n"
- "TEMP ayuv;\n"
- "TEX ayuv, fragment.texcoord[0], texture[0], 2D;\n"
- "MOV ayuv.x, matrix[3].w;\n"
- "DP4 result.color.x, ayuv.yzwx, matrix[0];\n"
- "DP4 result.color.y, ayuv.yzwx, matrix[1];\n"
- "DP4 result.color.z, ayuv.yzwx, matrix[2];\n"
- "END";
-
-// Paints a AYUV444 frame.
-static const char *qt_arbfp_ayuvShaderProgram =
- "!!ARBfp1.0\n"
- "PARAM matrix[4] = { program.local[0..2],"
- "{ 0.0, 0.0, 0.0, 1.0 } };\n"
- "TEMP ayuv;\n"
- "TEX ayuv, fragment.texcoord[0], texture[0], 2D;\n"
- "MOV ayuv.x, matrix[3].w;\n"
- "DP4 result.color.x, ayuv.yzwx, matrix[0];\n"
- "DP4 result.color.y, ayuv.yzwx, matrix[1];\n"
- "DP4 result.color.z, ayuv.yzwx, matrix[2];\n"
- "TEX result.color.w, fragment.texcoord[0], texture, 2D;\n"
- "END";
-
-class QVideoSurfaceArbFpPainter : public QmlVideoSurfaceGLPainter
-{
-public:
- QVideoSurfaceArbFpPainter(QGLContext *context);
-
- QAbstractVideoSurface::Error start(const QVideoSurfaceFormat &format);
- void stop();
-
- QAbstractVideoSurface::Error paint(
- const QRectF &target, QPainter *painter, const QRectF &source);
-
-private:
- typedef void (APIENTRY *_glProgramStringARB) (GLenum, GLenum, GLsizei, const GLvoid *);
- typedef void (APIENTRY *_glBindProgramARB) (GLenum, GLuint);
- typedef void (APIENTRY *_glDeleteProgramsARB) (GLsizei, const GLuint *);
- typedef void (APIENTRY *_glGenProgramsARB) (GLsizei, GLuint *);
- typedef void (APIENTRY *_glProgramLocalParameter4fARB) (
- GLenum, GLuint, GLfloat, GLfloat, GLfloat, GLfloat);
- typedef void (APIENTRY *_glActiveTexture) (GLenum);
-
- _glProgramStringARB glProgramStringARB;
- _glBindProgramARB glBindProgramARB;
- _glDeleteProgramsARB glDeleteProgramsARB;
- _glGenProgramsARB glGenProgramsARB;
- _glProgramLocalParameter4fARB glProgramLocalParameter4fARB;
-
- GLuint m_programId;
- QSize m_frameSize;
-};
-
-QVideoSurfaceArbFpPainter::QVideoSurfaceArbFpPainter(QGLContext *context)
- : QmlVideoSurfaceGLPainter(context)
- , m_programId(0)
-{
- qDebug() << "ArbFpPainter";
- glProgramStringARB = (_glProgramStringARB) m_context->getProcAddress(
- QLatin1String("glProgramStringARB"));
- glBindProgramARB = (_glBindProgramARB) m_context->getProcAddress(
- QLatin1String("glBindProgramARB"));
- glDeleteProgramsARB = (_glDeleteProgramsARB) m_context->getProcAddress(
- QLatin1String("glDeleteProgramsARB"));
- glGenProgramsARB = (_glGenProgramsARB) m_context->getProcAddress(
- QLatin1String("glGenProgramsARB"));
- glProgramLocalParameter4fARB = (_glProgramLocalParameter4fARB) m_context->getProcAddress(
- QLatin1String("glProgramLocalParameter4fARB"));
-
- m_imagePixelFormats
- << QVideoFrame::Format_RGB32
- << QVideoFrame::Format_BGR32
- << QVideoFrame::Format_ARGB32
- << QVideoFrame::Format_RGB24
- << QVideoFrame::Format_BGR24
- << QVideoFrame::Format_RGB565
- << QVideoFrame::Format_AYUV444
- << QVideoFrame::Format_YUV444
- << QVideoFrame::Format_YV12
- << QVideoFrame::Format_YUV420P;
- m_glPixelFormats
- << QVideoFrame::Format_RGB32
- << QVideoFrame::Format_ARGB32;
-}
-
-QAbstractVideoSurface::Error QVideoSurfaceArbFpPainter::start(const QVideoSurfaceFormat &format)
-{
- Q_ASSERT(m_textureCount == 0);
-
- QAbstractVideoSurface::Error error = QAbstractVideoSurface::NoError;
-
- m_context->makeCurrent();
-
- const char *program = 0;
-
- if (format.handleType() == QAbstractVideoBuffer::NoHandle) {
- switch (format.pixelFormat()) {
- case QVideoFrame::Format_RGB32:
- initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
- program = qt_arbfp_xrgbShaderProgram;
- break;
- case QVideoFrame::Format_BGR32:
- initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
- program = qt_arbfp_rgbShaderProgram;
- break;
- case QVideoFrame::Format_ARGB32:
- initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
- program = qt_arbfp_argbShaderProgram;
- break;
- case QVideoFrame::Format_RGB24:
- initRgbTextureInfo(GL_RGB8, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
- program = qt_arbfp_rgbShaderProgram;
- break;
- case QVideoFrame::Format_BGR24:
- initRgbTextureInfo(GL_RGB8, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
- program = qt_arbfp_xrgbShaderProgram;
- break;
- case QVideoFrame::Format_RGB565:
- initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, format.frameSize());
- program = qt_arbfp_rgbShaderProgram;
- break;
- case QVideoFrame::Format_YUV444:
- initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize());
- program = qt_arbfp_xyuvShaderProgram;
- m_yuv = true;
- break;
- case QVideoFrame::Format_AYUV444:
- initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
- program = qt_arbfp_ayuvShaderProgram;
- m_yuv = true;
- break;
- case QVideoFrame::Format_YV12:
- initYv12TextureInfo(format.frameSize());
- program = qt_arbfp_yuvPlanarShaderProgram;
- break;
- case QVideoFrame::Format_YUV420P:
- initYuv420PTextureInfo(format.frameSize());
- program = qt_arbfp_yuvPlanarShaderProgram;
- break;
- default:
- break;
- }
- } else if (format.handleType() == QAbstractVideoBuffer::GLTextureHandle) {
- switch (format.pixelFormat()) {
- case QVideoFrame::Format_RGB32:
- case QVideoFrame::Format_ARGB32:
- m_yuv = false;
- m_textureCount = 1;
- program = qt_arbfp_rgbShaderProgram;
- break;
- default:
- break;
- }
- } else if (format.handleType() == QAbstractVideoBuffer::QPixmapHandle) {
- m_handleType = QAbstractVideoBuffer::QPixmapHandle;
- return QAbstractVideoSurface::NoError;
- }
-
- if (!program) {
- error = QAbstractVideoSurface::UnsupportedFormatError;
- } else {
- glGenProgramsARB(1, &m_programId);
-
- GLenum glError = glGetError();
- if (glError != GL_NO_ERROR) {
- qWarning("QmlPainterVideoSurface: ARBfb Shader allocation error %x", int(glError));
- m_textureCount = 0;
- m_programId = 0;
-
- error = QAbstractVideoSurface::ResourceError;
- } else {
- glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_programId);
- glProgramStringARB(
- GL_FRAGMENT_PROGRAM_ARB,
- GL_PROGRAM_FORMAT_ASCII_ARB,
- qstrlen(program),
- reinterpret_cast<const GLvoid *>(program));
-
- if ((glError = glGetError()) != GL_NO_ERROR) {
- const GLubyte* errorString = glGetString(GL_PROGRAM_ERROR_STRING_ARB);
-
- qWarning("QmlPainterVideoSurface: ARBfp Shader compile error %x, %s",
- int(glError),
- reinterpret_cast<const char *>(errorString));
- glDeleteProgramsARB(1, &m_programId);
-
- m_textureCount = 0;
- m_programId = 0;
-
- error = QAbstractVideoSurface::ResourceError;
- } else {
- m_handleType = format.handleType();
- m_scanLineDirection = format.scanLineDirection();
- m_frameSize = format.frameSize();
- m_colorSpace = format.yCbCrColorSpace();
-
- if (m_handleType == QAbstractVideoBuffer::NoHandle)
- glGenTextures(m_textureCount, m_textureIds);
- }
- }
- }
-
- return error;
-}
-
-void QVideoSurfaceArbFpPainter::stop()
-{
- if (m_context) {
- m_context->makeCurrent();
-
- if (m_handleType != QAbstractVideoBuffer::GLTextureHandle)
- glDeleteTextures(m_textureCount, m_textureIds);
- glDeleteProgramsARB(1, &m_programId);
- }
-
- m_textureCount = 0;
- m_programId = 0;
- m_handleType = QAbstractVideoBuffer::NoHandle;
-}
-
-QAbstractVideoSurface::Error QVideoSurfaceArbFpPainter::paint(
- const QRectF &target, QPainter *painter, const QRectF &source)
-{
- const QAbstractVideoBuffer::HandleType h = m_frame.handleType();
- if (h == QAbstractVideoBuffer::NoHandle || h == QAbstractVideoBuffer::GLTextureHandle) {
- bool stencilTestEnabled = glIsEnabled(GL_STENCIL_TEST);
- bool scissorTestEnabled = glIsEnabled(GL_SCISSOR_TEST);
-
- painter->beginNativePainting();
-
- if (stencilTestEnabled)
- glEnable(GL_STENCIL_TEST);
- if (scissorTestEnabled)
- glEnable(GL_SCISSOR_TEST);
-
- const float txLeft = source.left() / m_frameSize.width();
- const float txRight = source.right() / m_frameSize.width();
- const float txTop = m_scanLineDirection == QVideoSurfaceFormat::TopToBottom
- ? source.top() / m_frameSize.height()
- : source.bottom() / m_frameSize.height();
- const float txBottom = m_scanLineDirection == QVideoSurfaceFormat::TopToBottom
- ? source.bottom() / m_frameSize.height()
- : source.top() / m_frameSize.height();
-
- const float tx_array[] =
- {
- txLeft , txBottom,
- txRight, txBottom,
- txLeft , txTop,
- txRight, txTop
- };
-
- const GLfloat vTop = m_scanLineDirection == QVideoSurfaceFormat::TopToBottom
- ? target.top()
- : target.bottom() + 1;
- const GLfloat vBottom = m_scanLineDirection == QVideoSurfaceFormat::TopToBottom
- ? target.bottom() + 1
- : target.top();
-
- const GLfloat v_array[] =
- {
- GLfloat(target.left()) , GLfloat(vBottom),
- GLfloat(target.right() + 1), GLfloat(vBottom),
- GLfloat(target.left()) , GLfloat(vTop),
- GLfloat(target.right() + 1), GLfloat(vTop)
- };
-
- glEnable(GL_FRAGMENT_PROGRAM_ARB);
- glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_programId);
-
- glProgramLocalParameter4fARB(
- GL_FRAGMENT_PROGRAM_ARB,
- 0,
- m_colorMatrix(0, 0),
- m_colorMatrix(0, 1),
- m_colorMatrix(0, 2),
- m_colorMatrix(0, 3));
- glProgramLocalParameter4fARB(
- GL_FRAGMENT_PROGRAM_ARB,
- 1,
- m_colorMatrix(1, 0),
- m_colorMatrix(1, 1),
- m_colorMatrix(1, 2),
- m_colorMatrix(1, 3));
- glProgramLocalParameter4fARB(
- GL_FRAGMENT_PROGRAM_ARB,
- 2,
- m_colorMatrix(2, 0),
- m_colorMatrix(2, 1),
- m_colorMatrix(2, 2),
- m_colorMatrix(2, 3));
-
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, m_textureIds[0]);
-
- if (m_textureCount == 3) {
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, m_textureIds[1]);
- glActiveTexture(GL_TEXTURE2);
- glBindTexture(GL_TEXTURE_2D, m_textureIds[2]);
- glActiveTexture(GL_TEXTURE0);
- }
-
- glVertexPointer(2, GL_FLOAT, 0, v_array);
- glTexCoordPointer(2, GL_FLOAT, 0, tx_array);
-
- glEnableClientState(GL_VERTEX_ARRAY);
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- glDisableClientState(GL_VERTEX_ARRAY);
- glDisable(GL_FRAGMENT_PROGRAM_ARB);
-
- painter->endNativePainting();
-
- return QAbstractVideoSurface::NoError;
- }
-
- return QmlVideoSurfaceGLPainter::paint(target, painter, source);
-}
-
-#endif
-
-static const char *qt_glsl_vertexShaderProgram =
- "attribute highp vec4 vertexCoordArray;\n"
- "attribute highp vec2 textureCoordArray;\n"
- "uniform highp mat4 positionMatrix;\n"
- "varying highp vec2 textureCoord;\n"
- "void main(void)\n"
- "{\n"
- " gl_Position = positionMatrix * vertexCoordArray;\n"
- " textureCoord = textureCoordArray;\n"
- "}\n";
-
-// Paints an RGB32 frame
-static const char *qt_glsl_xrgbShaderProgram =
- "uniform sampler2D texRgb;\n"
- "uniform mediump mat4 colorMatrix;\n"
- "varying highp vec2 textureCoord;\n"
- "void main(void)\n"
- "{\n"
- " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).bgr, 1.0);\n"
- " gl_FragColor = colorMatrix * color;\n"
- "}\n";
-
-// Paints an ARGB frame.
-static const char *qt_glsl_argbShaderProgram =
- "uniform sampler2D texRgb;\n"
- "uniform mediump mat4 colorMatrix;\n"
- "varying highp vec2 textureCoord;\n"
- "void main(void)\n"
- "{\n"
- " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).bgr, 1.0);\n"
- " color = colorMatrix * color;\n"
- " gl_FragColor = vec4(color.rgb, texture2D(texRgb, textureCoord.st).a);\n"
- "}\n";
-
-// Paints an RGB(A) frame.
-static const char *qt_glsl_rgbShaderProgram =
- "uniform sampler2D texRgb;\n"
- "uniform mediump mat4 colorMatrix;\n"
- "varying highp vec2 textureCoord;\n"
- "void main(void)\n"
- "{\n"
- " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).rgb, 1.0);\n"
- " color = colorMatrix * color;\n"
- " gl_FragColor = vec4(color.rgb, texture2D(texRgb, textureCoord.st).a);\n"
- "}\n";
-
-// Paints a YUV420P or YV12 frame.
-static const char *qt_glsl_yuvPlanarShaderProgram =
- "uniform sampler2D texY;\n"
- "uniform sampler2D texU;\n"
- "uniform sampler2D texV;\n"
- "uniform mediump mat4 colorMatrix;\n"
- "varying highp vec2 textureCoord;\n"
- "void main(void)\n"
- "{\n"
- " highp vec4 color = vec4(\n"
- " texture2D(texY, textureCoord.st).r,\n"
- " texture2D(texU, textureCoord.st).r,\n"
- " texture2D(texV, textureCoord.st).r,\n"
- " 1.0);\n"
- " gl_FragColor = colorMatrix * color;\n"
- "}\n";
-
-// Paints a YUV444 frame.
-static const char *qt_glsl_xyuvShaderProgram =
- "uniform sampler2D texRgb;\n"
- "uniform mediump mat4 colorMatrix;\n"
- "varying highp vec2 textureCoord;\n"
- "void main(void)\n"
- "{\n"
- " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).gba, 1.0);\n"
- " gl_FragColor = colorMatrix * color;\n"
- "}\n";
-
-// Paints a AYUV444 frame.
-static const char *qt_glsl_ayuvShaderProgram =
- "uniform sampler2D texRgb;\n"
- "uniform mediump mat4 colorMatrix;\n"
- "varying highp vec2 textureCoord;\n"
- "void main(void)\n"
- "{\n"
- " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).gba, 1.0);\n"
- " color = colorMatrix * color;\n"
- " gl_FragColor = vec4(color.rgb, texture2D(texRgb, textureCoord.st).r);\n"
- "}\n";
-
-class QmlVideoSurfaceGlslPainter : public QmlVideoSurfaceGLPainter
-{
-public:
- QmlVideoSurfaceGlslPainter(QGLContext *context);
-
- QAbstractVideoSurface::Error start(const QVideoSurfaceFormat &format);
- void stop();
-
- QAbstractVideoSurface::Error paint(
- const QRectF &target, QPainter *painter, const QRectF &source);
-
-private:
- QGLShaderProgram m_program;
- QSize m_frameSize;
-};
-
-QmlVideoSurfaceGlslPainter::QmlVideoSurfaceGlslPainter(QGLContext *context)
- : QmlVideoSurfaceGLPainter(context)
- , m_program(context)
-{
- qDebug() << "GlslPainter";
- m_imagePixelFormats
- << QVideoFrame::Format_YUV420P
- << QVideoFrame::Format_YV12
- << QVideoFrame::Format_RGB32
- << QVideoFrame::Format_BGR32
-#if !defined(QT_OPENGL_ES) && !defined(QT_OPENGL_ES_2)
- << QVideoFrame::Format_RGB24
- << QVideoFrame::Format_BGR24
-
- << QVideoFrame::Format_RGB565
- << QVideoFrame::Format_AYUV444
- << QVideoFrame::Format_YUV444
-#endif
- << QVideoFrame::Format_ARGB32;
-
-
- m_glPixelFormats
- << QVideoFrame::Format_RGB32
- << QVideoFrame::Format_ARGB32;
-}
-
-QAbstractVideoSurface::Error QmlVideoSurfaceGlslPainter::start(const QVideoSurfaceFormat &format)
-{
- Q_ASSERT(m_textureCount == 0);
-
- QAbstractVideoSurface::Error error = QAbstractVideoSurface::NoError;
-
- m_context->makeCurrent();
-
- const char *fragmentProgram = 0;
-
- if (format.handleType() == QAbstractVideoBuffer::NoHandle) {
- switch (format.pixelFormat()) {
- case QVideoFrame::Format_RGB32:
- initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
- fragmentProgram = qt_glsl_xrgbShaderProgram;
- break;
- case QVideoFrame::Format_BGR32:
- initRgbTextureInfo(GL_RGB, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
- fragmentProgram = qt_glsl_rgbShaderProgram;
- break;
- case QVideoFrame::Format_ARGB32:
- initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
- fragmentProgram = qt_glsl_argbShaderProgram;
- break;
-#ifndef QT_OPENGL_ES
- case QVideoFrame::Format_RGB24:
- initRgbTextureInfo(GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize());
- fragmentProgram = qt_glsl_rgbShaderProgram;
- break;
- case QVideoFrame::Format_BGR24:
- initRgbTextureInfo(GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize());
- fragmentProgram = qt_glsl_argbShaderProgram;
- break;
-#endif
- case QVideoFrame::Format_RGB565:
- initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, format.frameSize());
- fragmentProgram = qt_glsl_rgbShaderProgram;
- break;
- case QVideoFrame::Format_YUV444:
- initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize());
- fragmentProgram = qt_glsl_xyuvShaderProgram;
- m_yuv = true;
- break;
- case QVideoFrame::Format_AYUV444:
- initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
- fragmentProgram = qt_glsl_ayuvShaderProgram;
- m_yuv = true;
- break;
- case QVideoFrame::Format_YV12:
- initYv12TextureInfo(format.frameSize());
- fragmentProgram = qt_glsl_yuvPlanarShaderProgram;
- break;
- case QVideoFrame::Format_YUV420P:
- initYuv420PTextureInfo(format.frameSize());
- fragmentProgram = qt_glsl_yuvPlanarShaderProgram;
- break;
- default:
- break;
- }
- } else if (format.handleType() == QAbstractVideoBuffer::GLTextureHandle) {
- switch (format.pixelFormat()) {
- case QVideoFrame::Format_RGB32:
- case QVideoFrame::Format_ARGB32:
- m_yuv = false;
- m_textureCount = 1;
- fragmentProgram = qt_glsl_rgbShaderProgram;
- break;
- default:
- break;
- }
- } else if (format.handleType() == QAbstractVideoBuffer::QPixmapHandle) {
- m_handleType = QAbstractVideoBuffer::QPixmapHandle;
- return QAbstractVideoSurface::NoError;
- }
-
- if (!fragmentProgram) {
- error = QAbstractVideoSurface::UnsupportedFormatError;
- } else if (!m_program.addShaderFromSourceCode(QGLShader::Vertex, qt_glsl_vertexShaderProgram)) {
- qWarning("QmlPainterVideoSurface: Vertex shader compile error %s",
- qPrintable(m_program.log()));
- error = QAbstractVideoSurface::ResourceError;
- } else if (!m_program.addShaderFromSourceCode(QGLShader::Fragment, fragmentProgram)) {
- qWarning("QmlPainterVideoSurface: Shader compile error %s", qPrintable(m_program.log()));
- error = QAbstractVideoSurface::ResourceError;
- m_program.removeAllShaders();
- } else if(!m_program.link()) {
- qWarning("QmlPainterVideoSurface: Shader link error %s", qPrintable(m_program.log()));
- m_program.removeAllShaders();
- error = QAbstractVideoSurface::ResourceError;
- } else {
- m_handleType = format.handleType();
- m_scanLineDirection = format.scanLineDirection();
- m_frameSize = format.frameSize();
- m_colorSpace = format.yCbCrColorSpace();
-
- if (m_handleType == QAbstractVideoBuffer::NoHandle)
- glGenTextures(m_textureCount, m_textureIds);
- }
-
- return error;
-}
-
-void QmlVideoSurfaceGlslPainter::stop()
-{
- if (m_context) {
- m_context->makeCurrent();
-
- if (m_handleType != QAbstractVideoBuffer::GLTextureHandle)
- glDeleteTextures(m_textureCount, m_textureIds);
- }
-
- m_program.removeAllShaders();
-
- m_textureCount = 0;
- m_handleType = QAbstractVideoBuffer::NoHandle;
-}
-
-QAbstractVideoSurface::Error QmlVideoSurfaceGlslPainter::paint(
- const QRectF &target, QPainter *painter, const QRectF &source)
-{
- const QAbstractVideoBuffer::HandleType h = m_frame.handleType();
- if (h == QAbstractVideoBuffer::NoHandle || h == QAbstractVideoBuffer::GLTextureHandle) {
- bool stencilTestEnabled = glIsEnabled(GL_STENCIL_TEST);
- bool scissorTestEnabled = glIsEnabled(GL_SCISSOR_TEST);
-
- painter->beginNativePainting();
-
- if (stencilTestEnabled)
- glEnable(GL_STENCIL_TEST);
- if (scissorTestEnabled)
- glEnable(GL_SCISSOR_TEST);
-
- const int width = QGLContext::currentContext()->device()->width();
- const int height = QGLContext::currentContext()->device()->height();
-
- const QTransform transform = painter->deviceTransform();
-
- const GLfloat wfactor = 2.0 / width;
- const GLfloat hfactor = -2.0 / height;
-
- const GLfloat positionMatrix[4][4] =
- {
- {
- /*(0,0)*/ GLfloat(wfactor * transform.m11() - transform.m13()),
- /*(0,1)*/ GLfloat(hfactor * transform.m12() + transform.m13()),
- /*(0,2)*/ 0.0,
- /*(0,3)*/ GLfloat(transform.m13())
- }, {
- /*(1,0)*/ GLfloat(wfactor * transform.m21() - transform.m23()),
- /*(1,1)*/ GLfloat(hfactor * transform.m22() + transform.m23()),
- /*(1,2)*/ 0.0,
- /*(1,3)*/ GLfloat(transform.m23())
- }, {
- /*(2,0)*/ 0.0,
- /*(2,1)*/ 0.0,
- /*(2,2)*/ -1.0,
- /*(2,3)*/ 0.0
- }, {
- /*(3,0)*/ GLfloat(wfactor * transform.dx() - transform.m33()),
- /*(3,1)*/ GLfloat(hfactor * transform.dy() + transform.m33()),
- /*(3,2)*/ 0.0,
- /*(3,3)*/ GLfloat(transform.m33())
- }
- };
-
- const GLfloat vTop = m_scanLineDirection == QVideoSurfaceFormat::TopToBottom
- ? target.top()
- : target.bottom() + 1;
- const GLfloat vBottom = m_scanLineDirection == QVideoSurfaceFormat::TopToBottom
- ? target.bottom() + 1
- : target.top();
-
-
- const GLfloat vertexCoordArray[] =
- {
- GLfloat(target.left()) , GLfloat(vBottom),
- GLfloat(target.right() + 1), GLfloat(vBottom),
- GLfloat(target.left()) , GLfloat(vTop),
- GLfloat(target.right() + 1), GLfloat(vTop)
- };
-
- const GLfloat txLeft = source.left() / m_frameSize.width();
- const GLfloat txRight = source.right() / m_frameSize.width();
- const GLfloat txTop = m_scanLineDirection == QVideoSurfaceFormat::TopToBottom
- ? source.top() / m_frameSize.height()
- : source.bottom() / m_frameSize.height();
- const GLfloat txBottom = m_scanLineDirection == QVideoSurfaceFormat::TopToBottom
- ? source.bottom() / m_frameSize.height()
- : source.top() / m_frameSize.height();
-
- const GLfloat textureCoordArray[] =
- {
- txLeft , txBottom,
- txRight, txBottom,
- txLeft , txTop,
- txRight, txTop
- };
-
- m_program.bind();
-
- m_program.enableAttributeArray("vertexCoordArray");
- m_program.enableAttributeArray("textureCoordArray");
- m_program.setAttributeArray("vertexCoordArray", vertexCoordArray, 2);
- m_program.setAttributeArray("textureCoordArray", textureCoordArray, 2);
- m_program.setUniformValue("positionMatrix", positionMatrix);
-
- if (m_textureCount == 3) {
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, m_textureIds[0]);
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, m_textureIds[1]);
- glActiveTexture(GL_TEXTURE2);
- glBindTexture(GL_TEXTURE_2D, m_textureIds[2]);
- glActiveTexture(GL_TEXTURE0);
-
- m_program.setUniformValue("texY", 0);
- m_program.setUniformValue("texU", 1);
- m_program.setUniformValue("texV", 2);
- } else {
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, m_textureIds[0]);
-
- m_program.setUniformValue("texRgb", 0);
- }
- m_program.setUniformValue("colorMatrix", m_colorMatrix);
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
- m_program.release();
-
- painter->endNativePainting();
-
- return QAbstractVideoSurface::NoError;
- }
-
- return QmlVideoSurfaceGLPainter::paint(target, painter, source);
-}
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
-#endif
+#include "gstqtvideosinksurface.h"
+#include "genericsurfacepainter.h"
+#include "openglsurfacepainter.h"
-/*!
- \class QmlPainterVideoSurface
- \internal
-*/
+#include <QtCore/QCoreApplication>
+#include <QtCore/QStack>
+#include <QtGui/QPainter>
-/*!
-*/
-QmlPainterVideoSurface::QmlPainterVideoSurface(QObject *parent)
- : QAbstractVideoSurface(parent)
+GstQtVideoSinkSurface::GstQtVideoSinkSurface(GstQtVideoSink *sink, QObject *parent)
+ : QObject(parent)
, m_painter(0)
-#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_1_CL) && !defined(QT_OPENGL_ES_1)
+#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
, m_glContext(0)
- , m_shaderTypes(NoShaders)
- , m_shaderType(NoShaders)
+ , m_supportedShaderTypes(NoShaders)
#endif
+ , m_colorsDirty(true)
, m_brightness(0)
, m_contrast(0)
, m_hue(0)
, m_saturation(0)
- , m_pixelFormat(QVideoFrame::Format_Invalid)
- , m_colorsDirty(true)
- , m_ready(false)
- , m_playing(false)
+ , m_forceAspectRatioDirty(true)
+ , m_forceAspectRatio(false)
+ , m_formatDirty(true)
+ , m_isActive(false)
+ , m_buffer(NULL)
+ , m_sink(sink)
{
-#if !defined(QT_NO_OPENGL)
- setGLContext(const_cast<QGLContext *> (QGLContext::currentContext()));
-#endif
}
-/*!
-*/
-QmlPainterVideoSurface::~QmlPainterVideoSurface()
+GstQtVideoSinkSurface::~GstQtVideoSinkSurface()
{
- if (isActive())
- m_painter->stop();
-
- delete m_painter;
+ Q_ASSERT(!isActive());
+ destroyPainter();
}
-/*!
-*/
-QList<QVideoFrame::PixelFormat> QmlPainterVideoSurface::supportedPixelFormats(
- QAbstractVideoBuffer::HandleType handleType) const
-{
- if (!m_painter)
- const_cast<QmlPainterVideoSurface *>(this)->createPainter();
-
- return m_painter->supportedPixelFormats(handleType);
-}
+//-------------------------------------
-/*!
-*/
-bool QmlPainterVideoSurface::isFormatSupported(
- const QVideoSurfaceFormat &format, QVideoSurfaceFormat *similar) const
+QSet<BufferFormat::PixelFormat> GstQtVideoSinkSurface::supportedPixelFormats() const
{
- if (!m_painter)
- const_cast<QmlPainterVideoSurface *>(this)->createPainter();
-
- return m_painter->isFormatSupported(format, similar);
-}
-
-/*!
-*/
-bool QmlPainterVideoSurface::start(const QVideoSurfaceFormat &format)
-{
- if (isActive())
- m_painter->stop();
-
- if (!m_painter)
- createPainter();
-
- if (format.frameSize().isEmpty()) {
- setError(UnsupportedFormatError);
- } else {
- QAbstractVideoSurface::Error error = m_painter->start(format);
-
- if (error != QAbstractVideoSurface::NoError) {
- setError(error);
- } else {
- m_pixelFormat = format.pixelFormat();
- m_frameSize = format.frameSize();
- m_sourceRect = format.viewport();
- m_colorsDirty = true;
- m_ready = true;
-
- return QAbstractVideoSurface::start(format);
- }
+ QSet<BufferFormat::PixelFormat> result;
+#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
+ if (m_glContext) {
+ result += GlslSurfacePainter::supportedPixelFormats();
+ result += ArbFpSurfacePainter::supportedPixelFormats();
}
-
- QAbstractVideoSurface::stop();
-
- return false;
+#endif
+ result += GenericSurfacePainter::supportedPixelFormats();
+ return result;
}
-/*!
-*/
-void QmlPainterVideoSurface::stop()
+bool GstQtVideoSinkSurface::isActive() const
{
- if (isActive()) {
- m_painter->stop();
- m_ready = false;
-
- QAbstractVideoSurface::stop();
- }
+ QReadLocker l(&m_isActiveLock);
+ return m_isActive;
}
-/*!
-*/
-bool QmlPainterVideoSurface::present(const QVideoFrame &frame)
+void GstQtVideoSinkSurface::setActive(bool active)
{
- if (!m_ready) {
- if (!isActive())
- setError(StoppedError);
- } else if (frame.isValid()
- && (frame.pixelFormat() != m_pixelFormat || frame.size() != m_frameSize)) {
- setError(IncorrectFormatError);
-
- stop();
- } else {
- QAbstractVideoSurface::Error error = m_painter->setCurrentFrame(frame);
-
- if (error != QAbstractVideoSurface::NoError) {
- setError(error);
-
- stop();
- } else {
- m_ready = false;
+ GST_INFO_OBJECT(m_sink, active ? "Activating" : "Deactivating");
- emit frameChanged();
-
- return true;
- }
+ QWriteLocker l(&m_isActiveLock);
+ m_isActive = active;
+ if (!active) {
+ QCoreApplication::postEvent(this, new DeactivateEvent());
}
- return false;
}
-/*!
-*/
-int QmlPainterVideoSurface::brightness() const
+//-------------------------------------
+
+int GstQtVideoSinkSurface::brightness() const
{
+ QReadLocker l(&m_colorsLock);
return m_brightness;
}
-/*!
-*/
-void QmlPainterVideoSurface::setBrightness(int brightness)
+void GstQtVideoSinkSurface::setBrightness(int brightness)
{
+ QWriteLocker l(&m_colorsLock);
m_brightness = brightness;
-
m_colorsDirty = true;
}
-/*!
-*/
-int QmlPainterVideoSurface::contrast() const
+int GstQtVideoSinkSurface::contrast() const
{
+ QReadLocker l(&m_colorsLock);
return m_contrast;
}
-/*!
-*/
-void QmlPainterVideoSurface::setContrast(int contrast)
+void GstQtVideoSinkSurface::setContrast(int contrast)
{
+ QWriteLocker l(&m_colorsLock);
m_contrast = contrast;
-
m_colorsDirty = true;
}
-/*!
-*/
-int QmlPainterVideoSurface::hue() const
+int GstQtVideoSinkSurface::hue() const
{
+ QReadLocker l(&m_colorsLock);
return m_hue;
}
-/*!
-*/
-void QmlPainterVideoSurface::setHue(int hue)
+void GstQtVideoSinkSurface::setHue(int hue)
{
+ QWriteLocker l(&m_colorsLock);
m_hue = hue;
-
m_colorsDirty = true;
}
-/*!
-*/
-int QmlPainterVideoSurface::saturation() const
+int GstQtVideoSinkSurface::saturation() const
{
+ QReadLocker l(&m_colorsLock);
return m_saturation;
}
-/*!
-*/
-void QmlPainterVideoSurface::setSaturation(int saturation)
+void GstQtVideoSinkSurface::setSaturation(int saturation)
{
+ QWriteLocker l(&m_colorsLock);
m_saturation = saturation;
-
m_colorsDirty = true;
}
-/*!
-*/
-bool QmlPainterVideoSurface::isReady() const
-{
- return m_ready;
-}
+//-------------------------------------
-/*!
-*/
-void QmlPainterVideoSurface::setReady(bool ready)
+bool GstQtVideoSinkSurface::forceAspectRatio() const
{
- m_ready = ready;
+ QReadLocker l(&m_forceAspectRatioLock);
+ return m_forceAspectRatio;
}
-/*!
-*/
-bool QmlPainterVideoSurface::isPlaying() const
+void GstQtVideoSinkSurface::setForceAspectRatio(bool force)
{
- return m_playing;
+ QWriteLocker l(&m_forceAspectRatioLock);
+ if (m_forceAspectRatio != force) {
+ m_forceAspectRatio = force;
+ m_forceAspectRatioDirty = true;
+ }
}
-/*!
-*/
-void QmlPainterVideoSurface::setPlaying(bool playing)
-{
- m_playing = playing;
-}
+//-------------------------------------
-/*!
-*/
-void QmlPainterVideoSurface::paint(QPainter *painter, const QRectF &target, const QRectF &source)
+void GstQtVideoSinkSurface::paint(QPainter *painter, int x, int y, int width, int height)
{
- if (!isActive() || !isPlaying()) {
- painter->fillRect(target, QBrush(Qt::black));
+ QRect targetArea(x, y, width, height);
+
+ if (!isActive()) {
+ painter->fillRect(targetArea, Qt::black);
} else {
- if (m_colorsDirty) {
- m_painter->updateColors(m_brightness, m_contrast, m_hue, m_saturation);
- m_colorsDirty = false;
+ BufferFormat format = m_formatDirty ?
+ BufferFormat::fromCaps(GST_BUFFER_CAPS(m_buffer)) : m_bufferFormat;
+
+ //recalculate the video area if needed
+ QReadLocker forceAspectRatioLocker(&m_forceAspectRatioLock);
+ if (targetArea != m_targetArea
+ || (m_formatDirty && (format.frameSize() != m_bufferFormat.frameSize()
+ || format.pixelAspectRatio() != m_bufferFormat.pixelAspectRatio()))
+ || m_forceAspectRatioDirty)
+ {
+ m_targetArea = targetArea;
+ m_forceAspectRatioDirty = false;
+
+ if (m_forceAspectRatio) {
+ GstVideoRectangle srcRect;
+ srcRect.x = srcRect.y = 0;
+ srcRect.w = format.frameSize().width() *
+ format.pixelAspectRatio().width() / format.pixelAspectRatio().height();
+ srcRect.h = format.frameSize().height() *
+ format.pixelAspectRatio().height() / format.pixelAspectRatio().width();
+
+ GstVideoRectangle destRect;
+ destRect.x = destRect.y = 0;
+ destRect.w = m_targetArea.width();
+ destRect.h = m_targetArea.height();
+
+ GstVideoRectangle resultRect;
+ gst_video_sink_center_rect(srcRect, destRect, &resultRect, TRUE);
+ m_videoArea = QRect(resultRect.x, resultRect.y, resultRect.w, resultRect.h);
+
+ //Is this really worth it, or should I paint everything black?
+ QRect blackArea1(
+ m_targetArea.left(),
+ m_targetArea.top(),
+ m_videoArea.left() == m_targetArea.left() ?
+ m_targetArea.width() : m_videoArea.left() - m_targetArea.left(),
+ m_videoArea.top() == m_targetArea.top() ?
+ m_targetArea.height() : m_videoArea.top() - m_targetArea.top()
+ );
+
+ QRect blackArea2(
+ m_videoArea.right() == m_targetArea.right() ?
+ m_targetArea.left() : m_videoArea.right() + 1,
+ m_videoArea.bottom() == m_targetArea.bottom() ?
+ m_targetArea.top() : m_videoArea.bottom() + 1,
+ m_videoArea.right() == m_targetArea.right() ?
+ m_targetArea.width() : m_targetArea.right() - m_videoArea.right(),
+ m_videoArea.bottom() == m_targetArea.bottom() ?
+ m_targetArea.height() : m_targetArea.bottom() - m_videoArea.bottom()
+ );
+
+ painter->fillRect(blackArea1, Qt::black);
+ painter->fillRect(blackArea2, Qt::black);
+ } else {
+ m_videoArea = m_targetArea;
+ }
}
+ forceAspectRatioLocker.unlock();
- const QRectF sourceRect(
- m_sourceRect.x() + m_sourceRect.width() * source.x(),
- m_sourceRect.y() + m_sourceRect.height() * source.y(),
- m_sourceRect.width() * source.width(),
- m_sourceRect.height() * source.height());
+ //TODO add properties for modifying clipRect
- QAbstractVideoSurface::Error error = m_painter->paint(target, painter, sourceRect);
+ if (m_formatDirty) {
+ //if either pixelFormat or frameSize have changed, we need to reset the painter
+ //and/or change painter, in case the current one does not handle the requested format
+ if (format.pixelFormat() != m_bufferFormat.pixelFormat()
+ || format.frameSize() != m_bufferFormat.frameSize())
+ {
+ changePainter(format);
+ }
- if (error != QAbstractVideoSurface::NoError) {
- setError(error);
+ //if we have a different colorspace, we need to update the colors
+ if (format.yCbCrColorSpace() != m_bufferFormat.yCbCrColorSpace()) {
+ QReadLocker l(&m_colorsLock);
+ m_colorsDirty = true;
+ }
- stop();
+ m_bufferFormat = format;
+ m_formatDirty = false;
+ }
+
+ GST_TRACE_OBJECT(m_sink,
+ "Rendering buffer %"GST_PTR_FORMAT". "
+ "Frame size is (%d, %d), "
+ "widget size is (%d, %d), "
+ "calculated video area is (%d, %d)",
+ m_buffer,
+ m_bufferFormat.frameSize().width(), m_bufferFormat.frameSize().height(),
+ m_targetArea.width(), m_targetArea.height(),
+ m_videoArea.width(), m_videoArea.height());
+
+ if (m_painter) {
+ QReadLocker colorsLocker(&m_colorsLock);
+ if (m_colorsDirty) {
+ m_painter->updateColors(m_brightness, m_contrast, m_hue, m_saturation,
+ m_bufferFormat.yCbCrColorSpace());
+ m_colorsDirty = false;
+ }
+ colorsLocker.unlock();
+
+ m_painter->paint(m_buffer->data, m_bufferFormat, painter, m_videoArea, m_clipRect);
}
}
}
-/*!
- \fn QmlPainterVideoSurface::frameChanged()
-*/
-
-#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_1_CL) && !defined(QT_OPENGL_ES_1)
+#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
-/*!
-*/
-const QGLContext *QmlPainterVideoSurface::glContext() const
+QGLContext *GstQtVideoSinkSurface::glContext() const
{
return m_glContext;
}
-/*!
-*/
-void QmlPainterVideoSurface::setGLContext(QGLContext *context)
+void GstQtVideoSinkSurface::setGLContext(QGLContext *context)
{
- qDebug() << "QmlPainterVideoSurface::setGLContext context="
- << context
- << " m_glContext=" << m_glContext
- << " m_painter=" << m_painter;
+ GST_LOG_OBJECT(m_sink, "Setting GL context. context=%p m_glContext=%p m_painter=%p",
+ context, m_glContext, m_painter);
if (m_glContext == context)
return;
m_glContext = context;
- m_shaderTypes = NoShaders;
+ m_supportedShaderTypes = NoShaders;
if (m_glContext) {
m_glContext->makeCurrent();
const QByteArray extensions(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)));
-#ifndef QT_OPENGL_ES
+ GST_LOG_OBJECT(m_sink, "Available GL extensions: %s", extensions.constData());
+#ifndef QT_OPENGL_ES
if (extensions.contains("ARB_fragment_program"))
- m_shaderTypes |= FragmentProgramShader;
+ m_supportedShaderTypes |= FragmentProgramShader;
#endif
#ifndef QT_OPENGL_ES_2
-
if (QGLShaderProgram::hasOpenGLShaderPrograms(m_glContext)
&& extensions.contains("ARB_shader_objects"))
#endif
- m_shaderTypes |= GlslShader;
+ m_supportedShaderTypes |= GlslShader;
}
- qDebug() << "QVideoSurfacePainter::setGLContext"
- << " m_shaderType=" << m_shaderType
- << " m_shaderTypes=" << m_shaderTypes;
-
- if (supportedShaderTypes() & QmlPainterVideoSurface::GlslShader) {
- setShaderType(QmlPainterVideoSurface::GlslShader);
- } else {
- setShaderType(QmlPainterVideoSurface::FragmentProgramShader);
- }
+ GST_LOG_OBJECT(m_sink, "Done setting GL context. m_shaderTypes=%x", (int) m_supportedShaderTypes);
}
-/*!
- \enum QmlPainterVideoSurface::ShaderType
-
- \value NoShaders
- \value FragmentProgramShader
- \value HlslShader
-*/
+#endif
-/*!
- \typedef QmlPainterVideoSurface::ShaderTypes
-*/
+enum PainterType { Glsl, ArbFp, Generic };
-/*!
-*/
-QmlPainterVideoSurface::ShaderTypes QmlPainterVideoSurface::supportedShaderTypes() const
+void GstQtVideoSinkSurface::changePainter(const BufferFormat & format)
{
- return m_shaderTypes;
-}
-
-/*!
-*/
-QmlPainterVideoSurface::ShaderType QmlPainterVideoSurface::shaderType() const
-{
- return m_shaderType;
-}
+ if (m_painter) {
+ m_painter->cleanup();
+ if (!m_painter->supportsFormat(format.pixelFormat())) {
+ delete m_painter;
+ m_painter = 0;
+ }
+ }
-/*!
-*/
-void QmlPainterVideoSurface::setShaderType(ShaderType type)
-{
- qDebug() << "QmlPainterVideoSurface::setShaderType"
- << " type=" << type
- << " m_shaderType" << m_shaderType
- << " m_shaderTypes" << m_shaderTypes;
+ QStack<PainterType> possiblePainters;
+ if (GenericSurfacePainter::supportedPixelFormats().contains(format.pixelFormat())) {
+ possiblePainters.push(Generic);
+ }
- if (!(type & m_shaderTypes))
- type = NoShaders;
+#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
+ if (m_supportedShaderTypes & GstQtVideoSinkSurface::FragmentProgramShader
+ && ArbFpSurfacePainter::supportedPixelFormats().contains(format.pixelFormat())) {
+ possiblePainters.push(ArbFp);
+ }
- if (type != m_shaderType) {
- m_shaderType = type;
+ if (m_supportedShaderTypes & GstQtVideoSinkSurface::GlslShader
+ && GlslSurfacePainter::supportedPixelFormats().contains(format.pixelFormat())) {
+ possiblePainters.push(Glsl);
+ }
+#endif
- if (isActive()) {
- m_painter->stop();
- delete m_painter;
- m_painter = 0;
- m_ready = false;
+ while (!possiblePainters.isEmpty()) {
+ if (!m_painter) {
+ PainterType type = possiblePainters.pop();
+ switch(type) {
+#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
+ case Glsl:
+ GST_LOG_OBJECT(m_sink, "Creating GLSL painter");
+ m_painter = new GlslSurfacePainter(m_glContext);
+ break;
+ case ArbFp:
+ GST_LOG_OBJECT(m_sink, "Creating ARB Fragment Shader painter");
+ m_painter = new ArbFpSurfacePainter(m_glContext);
+ break;
+#endif
+ case Generic:
+ GST_LOG_OBJECT(m_sink, "Creating Generic painter");
+ m_painter = new GenericSurfacePainter;
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+ }
- setError(ResourceError);
- QAbstractVideoSurface::stop();
- } else {
+ try {
+ m_painter->init(format);
+ return;
+ } catch (const QString & error) {
+ GST_WARNING_OBJECT(m_sink, "Failed to start painter: %s", error.toUtf8().constData());
delete m_painter;
m_painter = 0;
}
- emit supportedFormatsChanged();
}
- if (!m_painter)
- createPainter();
+ GST_ERROR_OBJECT(m_sink, "Failed to create a painter for the given format");
}
-#endif
-
-void QmlPainterVideoSurface::viewportDestroyed()
+void GstQtVideoSinkSurface::destroyPainter()
{
- if (m_painter) {
- m_painter->viewportDestroyed();
+ GST_LOG_OBJECT(m_sink, "Destroying painter");
- setError(ResourceError);
- stop();
- delete m_painter;
- m_painter = 0;
- }
+ delete m_painter;
+ m_painter = 0;
}
-void QmlPainterVideoSurface::createPainter()
+bool GstQtVideoSinkSurface::event(QEvent *event)
{
- qDebug() << "QmlPainterVideoSurface::createPainter "
- << " m_glContext=" << m_glContext
- << " m_painter=" << m_painter;
+ switch((int) event->type()) {
+ case BufferEventType:
+ {
+ BufferEvent *bufEvent = dynamic_cast<BufferEvent*>(event);
+ Q_ASSERT(bufEvent);
- Q_ASSERT(!m_painter);
+ GST_TRACE_OBJECT(m_sink, "Received buffer %"GST_PTR_FORMAT, bufEvent->buffer);
-#ifdef Q_WS_MAC
- if (m_glContext)
- m_glContext->makeCurrent();
+ if (m_buffer) {
+ //free the previous buffer
+ gst_buffer_unref(m_buffer);
+ m_buffer = NULL;
+ }
- m_painter = new QVideoSurfaceCoreGraphicsPainter(m_glContext != 0);
- return;
-#endif
+ if (isActive()) {
+ //schedule this frame for rendering
+ m_buffer = bufEvent->buffer;
+ m_formatDirty = bufEvent->formatDirty;
+ g_signal_emit(m_sink, GstQtVideoSink::s_signals[GstQtVideoSink::UPDATE_SIGNAL], 0);
+ } else {
+ //not active, drop the frame
+ gst_buffer_unref(bufEvent->buffer);
+ }
-#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_1_CL) && !defined(QT_OPENGL_ES_1)
- switch (m_shaderType) {
-#ifndef QT_OPENGL_ES
- case FragmentProgramShader:
- Q_ASSERT(m_glContext);
- m_glContext->makeCurrent();
- m_painter = new QVideoSurfaceArbFpPainter(m_glContext);
- break;
-#endif
- case GlslShader:
- Q_ASSERT(m_glContext);
- m_glContext->makeCurrent();
- m_painter = new QmlVideoSurfaceGlslPainter(m_glContext);
- break;
- default:
- m_painter = new QmlVideoSurfaceGenericPainter;
- break;
+ return true;
}
-#else
- m_painter = new QmlVideoSurfaceGenericPainter;
-#endif
-}
+ case DeactivateEventType:
+ {
+ GST_LOG_OBJECT(m_sink, "Received deactivate event");
+
+ if (m_buffer) {
+ gst_buffer_unref(m_buffer);
+ m_buffer = NULL;
+ }
+
+ m_painter->cleanup();
+ destroyPainter();
-//#include "moc_qmlpaintervideosurface_p.cpp"
-QT_END_NAMESPACE
+ g_signal_emit(m_sink, GstQtVideoSink::s_signals[GstQtVideoSink::UPDATE_SIGNAL], 0);
+ return true;
+ }
+ default:
+ return QObject::event(event);
+ }
+}
+#include "gstqtvideosinksurface.moc"
diff --git a/elements/gstqtvideosink/gstqtvideosinksurface.h b/elements/gstqtvideosink/gstqtvideosinksurface.h
index 2f0879c..6176444 100644
--- a/elements/gstqtvideosink/gstqtvideosinksurface.h
+++ b/elements/gstqtvideosink/gstqtvideosinksurface.h
@@ -1,93 +1,84 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 Collabora Ltd
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file was originally part of the Qt Mobility Components.
-**
-** 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.
-**
-****************************************************************************/
-
-#ifndef QPAINTERVIDEOSURFACE_P_H
-#define QPAINTERVIDEOSURFACE_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <qmobilityglobal.h>
-#include <QtCore/qsize.h>
-#include <QtGui/qimage.h>
-#include <QtGui/qmatrix4x4.h>
-#include <QtGui/qpaintengine.h>
-#include <qabstractvideosurface.h>
-#include <qvideoframe.h>
-
-QT_BEGIN_NAMESPACE
-class QGLContext;
-QT_END_NAMESPACE
-
-QT_USE_NAMESPACE
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
+ Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
-QT_BEGIN_NAMESPACE
-
-class QmlVideoSurfacePainter
-{
-public:
- virtual ~QmlVideoSurfacePainter();
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1
+ as published by the Free Software Foundation.
- virtual QList<QVideoFrame::PixelFormat> supportedPixelFormats(
- QAbstractVideoBuffer::HandleType handleType) const = 0;
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
- virtual bool isFormatSupported(
- const QVideoSurfaceFormat &format, QVideoSurfaceFormat *similar) const = 0;
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
- virtual QAbstractVideoSurface::Error start(const QVideoSurfaceFormat &format) = 0;
- virtual void stop() = 0;
+#ifndef GST_QT_VIDEO_SINK_SURFACE_H
+#define GST_QT_VIDEO_SINK_SURFACE_H
- virtual QAbstractVideoSurface::Error setCurrentFrame(const QVideoFrame &frame) = 0;
+#include "gstqtvideosink.h"
+#include "bufferformat.h"
+#include "abstractsurfacepainter.h"
- virtual QAbstractVideoSurface::Error paint(
- const QRectF &target, QPainter *painter, const QRectF &source) = 0;
+#include <QtCore/QObject>
+#include <QtCore/QEvent>
+#include <QtCore/QSet>
+#include <QtCore/QReadWriteLock>
- virtual void updateColors(int brightness, int contrast, int hue, int saturation) = 0;
- virtual void viewportDestroyed() {}
-};
+class QGLContext;
-class QM_AUTOTEST_EXPORT QmlPainterVideoSurface : public QAbstractVideoSurface
+class GstQtVideoSinkSurface : public QObject
{
Q_OBJECT
public:
- explicit QmlPainterVideoSurface(QObject *parent = 0);
- ~QmlPainterVideoSurface();
+ enum EventType {
+ BufferEventType = QEvent::User,
+ DeactivateEventType
+ };
+
+ //-------------------------------------
+
+ class BufferEvent : public QEvent
+ {
+ public:
+ inline BufferEvent(GstBuffer *buf, bool formatDirty)
+ : QEvent(static_cast<QEvent::Type>(BufferEventType)),
+ buffer(gst_buffer_ref(buf)),
+ formatDirty(formatDirty)
+ {
+ }
+
+ GstBuffer *buffer;
+ bool formatDirty;
+ };
+
+ //-------------------------------------
+
+ class DeactivateEvent : public QEvent
+ {
+ public:
+ inline DeactivateEvent()
+ : QEvent(static_cast<QEvent::Type>(DeactivateEventType))
+ {
+ }
+ };
- QList<QVideoFrame::PixelFormat> supportedPixelFormats(
- QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const;
+ //-------------------------------------
- bool isFormatSupported(
- const QVideoSurfaceFormat &format, QVideoSurfaceFormat *similar = 0) const;
+ explicit GstQtVideoSinkSurface(GstQtVideoSink *sink, QObject *parent = 0);
+ ~GstQtVideoSinkSurface();
- bool start(const QVideoSurfaceFormat &format);
- void stop();
+ // API for GstQtVideoSink
- bool present(const QVideoFrame &frame);
+ QSet<BufferFormat::PixelFormat> supportedPixelFormats() const;
+
+ bool isActive() const;
+ void setActive(bool playing);
+
+ // GstColorBalance interface
int brightness() const;
void setBrightness(int brightness);
@@ -101,16 +92,15 @@ public:
int saturation() const;
void setSaturation(int saturation);
- bool isReady() const;
- void setReady(bool ready);
+ // force-aspect-ratio property
- bool isPlaying() const;
- void setPlaying(bool playing);
+ bool forceAspectRatio() const;
+ void setForceAspectRatio(bool force);
- void paint(QPainter *painter, const QRectF &target, const QRectF &source = QRectF(0, 0, 1, 1));
+ // glcontext property
-#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_1_CL) && !defined(QT_OPENGL_ES_1)
- const QGLContext *glContext() const;
+#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
+ QGLContext *glContext() const;
void setGLContext(QGLContext *context);
enum ShaderType
@@ -121,45 +111,60 @@ public:
};
Q_DECLARE_FLAGS(ShaderTypes, ShaderType)
-
- ShaderTypes supportedShaderTypes() const;
-
- ShaderType shaderType() const;
- void setShaderType(ShaderType type);
#endif
-public Q_SLOTS:
- void viewportDestroyed();
+ // paint action signal
-Q_SIGNALS:
- void frameChanged();
+ void paint(QPainter *painter, int x, int y, int width, int height);
+
+protected:
+ bool event(QEvent *event);
private:
- void createPainter();
+ void changePainter(const BufferFormat & format);
+ void destroyPainter();
+
- QmlVideoSurfacePainter *m_painter;
-#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_1_CL) && !defined(QT_OPENGL_ES_1)
+ AbstractSurfacePainter *m_painter;
+
+#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
QGLContext *m_glContext;
- ShaderTypes m_shaderTypes;
+ ShaderTypes m_supportedShaderTypes;
ShaderType m_shaderType;
#endif
+
+ // colorbalance interface properties
+ mutable QReadWriteLock m_colorsLock;
+ bool m_colorsDirty;
int m_brightness;
int m_contrast;
int m_hue;
int m_saturation;
- QVideoFrame::PixelFormat m_pixelFormat;
- QSize m_frameSize;
- QRect m_sourceRect;
- bool m_colorsDirty;
- bool m_ready;
- bool m_playing;
-};
+ // force-aspect-ratio property
+ mutable QReadWriteLock m_forceAspectRatioLock;
+ bool m_forceAspectRatioDirty;
+ bool m_forceAspectRatio;
-#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_1_CL) && !defined(QT_OPENGL_ES_1)
-Q_DECLARE_OPERATORS_FOR_FLAGS(QmlPainterVideoSurface::ShaderTypes)
-#endif
+ // format caching
+ bool m_formatDirty;
+ BufferFormat m_bufferFormat;
+ QRect m_targetArea;
+ QRect m_videoArea;
+ QRect m_clipRect;
-QT_END_NAMESPACE
+ // whether the sink is active (PAUSED or PLAYING)
+ mutable QReadWriteLock m_isActiveLock;
+ bool m_isActive;
+ // the buffer to be drawn next
+ GstBuffer *m_buffer;
+
+ GstQtVideoSink *m_sink;
+};
+
+#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
+Q_DECLARE_OPERATORS_FOR_FLAGS(GstQtVideoSinkSurface::ShaderTypes)
#endif
+
+#endif // GST_QT_VIDEO_SINK_SURFACE_H
diff --git a/elements/gstqtvideosink/openglsurfacepainter.cpp b/elements/gstqtvideosink/openglsurfacepainter.cpp
new file mode 100644
index 0000000..e35c220
--- /dev/null
+++ b/elements/gstqtvideosink/openglsurfacepainter.cpp
@@ -0,0 +1,832 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
+ Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "openglsurfacepainter.h"
+#include <QtCore/qmath.h>
+
+#ifndef GL_TEXTURE0
+# define GL_TEXTURE0 0x84C0
+# define GL_TEXTURE1 0x84C1
+# define GL_TEXTURE2 0x84C2
+#endif
+#ifndef GL_PROGRAM_ERROR_STRING_ARB
+# define GL_PROGRAM_ERROR_STRING_ARB 0x8874
+#endif
+
+#ifndef GL_UNSIGNED_SHORT_5_6_5
+# define GL_UNSIGNED_SHORT_5_6_5 33635
+#endif
+
+
+OpenGLSurfacePainter::OpenGLSurfacePainter(QGLContext *context)
+ : m_context(context)
+ , m_textureFormat(0)
+ , m_textureInternalFormat(0)
+ , m_textureType(0)
+ , m_textureCount(0)
+ , m_yuv(false)
+{
+#ifndef QT_OPENGL_ES
+ glActiveTexture = (_glActiveTexture)m_context->getProcAddress(QLatin1String("glActiveTexture"));
+#endif
+}
+
+void OpenGLSurfacePainter::updateColors(int brightness, int contrast, int hue, int saturation,
+ BufferFormat::YCbCrColorSpace colorSpace)
+{
+ const qreal b = brightness / 200.0;
+ const qreal c = contrast / 100.0 + 1.0;
+ const qreal h = hue / 100.0;
+ const qreal s = saturation / 100.0 + 1.0;
+
+ const qreal cosH = qCos(M_PI * h);
+ const qreal sinH = qSin(M_PI * h);
+
+ const qreal h11 = 0.787 * cosH - 0.213 * sinH + 0.213;
+ const qreal h21 = -0.213 * cosH + 0.143 * sinH + 0.213;
+ const qreal h31 = -0.213 * cosH - 0.787 * sinH + 0.213;
+
+ const qreal h12 = -0.715 * cosH - 0.715 * sinH + 0.715;
+ const qreal h22 = 0.285 * cosH + 0.140 * sinH + 0.715;
+ const qreal h32 = -0.715 * cosH + 0.715 * sinH + 0.715;
+
+ const qreal h13 = -0.072 * cosH + 0.928 * sinH + 0.072;
+ const qreal h23 = -0.072 * cosH - 0.283 * sinH + 0.072;
+ const qreal h33 = 0.928 * cosH + 0.072 * sinH + 0.072;
+
+ const qreal sr = (1.0 - s) * 0.3086;
+ const qreal sg = (1.0 - s) * 0.6094;
+ const qreal sb = (1.0 - s) * 0.0820;
+
+ const qreal sr_s = sr + s;
+ const qreal sg_s = sg + s;
+ const qreal sb_s = sr + s;
+
+ const float m4 = (s + sr + sg + sb) * (0.5 - 0.5 * c + b);
+
+ m_colorMatrix(0, 0) = c * (sr_s * h11 + sg * h21 + sb * h31);
+ m_colorMatrix(0, 1) = c * (sr_s * h12 + sg * h22 + sb * h32);
+ m_colorMatrix(0, 2) = c * (sr_s * h13 + sg * h23 + sb * h33);
+ m_colorMatrix(0, 3) = m4;
+
+ m_colorMatrix(1, 0) = c * (sr * h11 + sg_s * h21 + sb * h31);
+ m_colorMatrix(1, 1) = c * (sr * h12 + sg_s * h22 + sb * h32);
+ m_colorMatrix(1, 2) = c * (sr * h13 + sg_s * h23 + sb * h33);
+ m_colorMatrix(1, 3) = m4;
+
+ m_colorMatrix(2, 0) = c * (sr * h11 + sg * h21 + sb_s * h31);
+ m_colorMatrix(2, 1) = c * (sr * h12 + sg * h22 + sb_s * h32);
+ m_colorMatrix(2, 2) = c * (sr * h13 + sg * h23 + sb_s * h33);
+ m_colorMatrix(2, 3) = m4;
+
+ m_colorMatrix(3, 0) = 0.0;
+ m_colorMatrix(3, 1) = 0.0;
+ m_colorMatrix(3, 2) = 0.0;
+ m_colorMatrix(3, 3) = 1.0;
+
+ if (m_yuv) {
+ QMatrix4x4 colorSpaceMatrix;
+
+ switch (colorSpace) {
+ case BufferFormat::YCbCr_JPEG:
+ colorSpaceMatrix = QMatrix4x4(
+ 1.0, 0.000, 1.402, -0.701,
+ 1.0, -0.344, -0.714, 0.529,
+ 1.0, 1.772, 0.000, -0.886,
+ 0.0, 0.000, 0.000, 1.0000);
+ break;
+ case BufferFormat::YCbCr_BT709:
+ case BufferFormat::YCbCr_xvYCC709:
+ colorSpaceMatrix = QMatrix4x4(
+ 1.164, 0.000, 1.793, -0.5727,
+ 1.164, -0.534, -0.213, 0.3007,
+ 1.164, 2.115, 0.000, -1.1302,
+ 0.0, 0.000, 0.000, 1.0000);
+ break;
+ default: //BT 601:
+ colorSpaceMatrix = QMatrix4x4(
+ 1.164, 0.000, 1.596, -0.8708,
+ 1.164, -0.392, -0.813, 0.5296,
+ 1.164, 2.017, 0.000, -1.081,
+ 0.0, 0.000, 0.000, 1.0000);
+ }
+
+ m_colorMatrix = m_colorMatrix * colorSpaceMatrix;
+ }
+}
+
+void OpenGLSurfacePainter::setCurrentFrame(quint8 *data)
+{
+ m_context->makeCurrent();
+
+ for (int i = 0; i < m_textureCount; ++i) {
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[i]);
+ glTexImage2D(
+ GL_TEXTURE_2D,
+ 0,
+ m_textureInternalFormat,
+ m_textureWidths[i],
+ m_textureHeights[i],
+ 0,
+ m_textureFormat,
+ m_textureType,
+ data + m_textureOffsets[i]);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+}
+
+void OpenGLSurfacePainter::initRgbTextureInfo(
+ GLenum internalFormat, GLuint format, GLenum type, const QSize &size)
+{
+ m_yuv = false;
+ m_textureInternalFormat = internalFormat;
+ m_textureFormat = format;
+ m_textureType = type;
+ m_textureCount = 1;
+ m_textureWidths[0] = size.width();
+ m_textureHeights[0] = size.height();
+ m_textureOffsets[0] = 0;
+}
+
+void OpenGLSurfacePainter::initYuv420PTextureInfo(const QSize &size)
+{
+ int bytesPerLine = (size.width() + 3) & ~3;
+ int bytesPerLine2 = (size.width() / 2 + 3) & ~3;
+
+ m_yuv = true;
+ m_textureInternalFormat = GL_LUMINANCE;
+ m_textureFormat = GL_LUMINANCE;
+ m_textureType = GL_UNSIGNED_BYTE;
+ m_textureCount = 3;
+ m_textureWidths[0] = bytesPerLine;
+ m_textureHeights[0] = size.height();
+ m_textureOffsets[0] = 0;
+ m_textureWidths[1] = bytesPerLine2;
+ m_textureHeights[1] = size.height() / 2;
+ m_textureOffsets[1] = bytesPerLine * size.height();
+ m_textureWidths[2] = bytesPerLine2;
+ m_textureHeights[2] = size.height() / 2;
+ m_textureOffsets[2] = bytesPerLine * size.height() + bytesPerLine2 * size.height()/2;
+}
+
+void OpenGLSurfacePainter::initYv12TextureInfo(const QSize &size)
+{
+ int bytesPerLine = (size.width() + 3) & ~3;
+ int bytesPerLine2 = (size.width() / 2 + 3) & ~3;
+
+ m_yuv = true;
+ m_textureInternalFormat = GL_LUMINANCE;
+ m_textureFormat = GL_LUMINANCE;
+ m_textureType = GL_UNSIGNED_BYTE;
+ m_textureCount = 3;
+ m_textureWidths[0] = bytesPerLine;
+ m_textureHeights[0] = size.height();
+ m_textureOffsets[0] = 0;
+ m_textureWidths[1] = bytesPerLine2;
+ m_textureHeights[1] = size.height() / 2;
+ m_textureOffsets[1] = bytesPerLine * size.height() + bytesPerLine2 * size.height()/2;
+ m_textureWidths[2] = bytesPerLine2;
+ m_textureHeights[2] = size.height() / 2;
+ m_textureOffsets[2] = bytesPerLine * size.height();
+}
+
+#ifndef QT_OPENGL_ES
+
+# ifndef GL_FRAGMENT_PROGRAM_ARB
+# define GL_FRAGMENT_PROGRAM_ARB 0x8804
+# define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875
+# endif
+
+// Paints an RGB32 frame
+static const char *qt_arbfp_xrgbShaderProgram =
+ "!!ARBfp1.0\n"
+ "PARAM matrix[4] = { program.local[0..2],"
+ "{ 0.0, 0.0, 0.0, 1.0 } };\n"
+ "TEMP xrgb;\n"
+ "TEX xrgb.xyz, fragment.texcoord[0], texture[0], 2D;\n"
+ "MOV xrgb.w, matrix[3].w;\n"
+ "DP4 result.color.x, xrgb.zyxw, matrix[0];\n"
+ "DP4 result.color.y, xrgb.zyxw, matrix[1];\n"
+ "DP4 result.color.z, xrgb.zyxw, matrix[2];\n"
+ "END";
+
+// Paints an ARGB frame.
+static const char *qt_arbfp_argbShaderProgram =
+ "!!ARBfp1.0\n"
+ "PARAM matrix[4] = { program.local[0..2],"
+ "{ 0.0, 0.0, 0.0, 1.0 } };\n"
+ "TEMP argb;\n"
+ "TEX argb, fragment.texcoord[0], texture[0], 2D;\n"
+ "MOV argb.w, matrix[3].w;\n"
+ "DP4 result.color.x, argb.zyxw, matrix[0];\n"
+ "DP4 result.color.y, argb.zyxw, matrix[1];\n"
+ "DP4 result.color.z, argb.zyxw, matrix[2];\n"
+ "TEX result.color.w, fragment.texcoord[0], texture, 2D;\n"
+ "END";
+
+// Paints an RGB(A) frame.
+static const char *qt_arbfp_rgbShaderProgram =
+ "!!ARBfp1.0\n"
+ "PARAM matrix[4] = { program.local[0..2],"
+ "{ 0.0, 0.0, 0.0, 1.0 } };\n"
+ "TEMP rgb;\n"
+ "TEX rgb, fragment.texcoord[0], texture[0], 2D;\n"
+ "MOV rgb.w, matrix[3].w;\n"
+ "DP4 result.color.x, rgb, matrix[0];\n"
+ "DP4 result.color.y, rgb, matrix[1];\n"
+ "DP4 result.color.z, rgb, matrix[2];\n"
+ "TEX result.color.w, fragment.texcoord[0], texture, 2D;\n"
+ "END";
+
+// Paints a YUV420P or YV12 frame.
+static const char *qt_arbfp_yuvPlanarShaderProgram =
+ "!!ARBfp1.0\n"
+ "PARAM matrix[4] = { program.local[0..2],"
+ "{ 0.0, 0.0, 0.0, 1.0 } };\n"
+ "TEMP yuv;\n"
+ "TEX yuv.x, fragment.texcoord[0], texture[0], 2D;\n"
+ "TEX yuv.y, fragment.texcoord[0], texture[1], 2D;\n"
+ "TEX yuv.z, fragment.texcoord[0], texture[2], 2D;\n"
+ "MOV yuv.w, matrix[3].w;\n"
+ "DP4 result.color.x, yuv, matrix[0];\n"
+ "DP4 result.color.y, yuv, matrix[1];\n"
+ "DP4 result.color.z, yuv, matrix[2];\n"
+ "END";
+
+// Paints a YUV444 frame.
+static const char *qt_arbfp_xyuvShaderProgram =
+ "!!ARBfp1.0\n"
+ "PARAM matrix[4] = { program.local[0..2],"
+ "{ 0.0, 0.0, 0.0, 1.0 } };\n"
+ "TEMP ayuv;\n"
+ "TEX ayuv, fragment.texcoord[0], texture[0], 2D;\n"
+ "MOV ayuv.x, matrix[3].w;\n"
+ "DP4 result.color.x, ayuv.yzwx, matrix[0];\n"
+ "DP4 result.color.y, ayuv.yzwx, matrix[1];\n"
+ "DP4 result.color.z, ayuv.yzwx, matrix[2];\n"
+ "END";
+
+// Paints a AYUV444 frame.
+static const char *qt_arbfp_ayuvShaderProgram =
+ "!!ARBfp1.0\n"
+ "PARAM matrix[4] = { program.local[0..2],"
+ "{ 0.0, 0.0, 0.0, 1.0 } };\n"
+ "TEMP ayuv;\n"
+ "TEX ayuv, fragment.texcoord[0], texture[0], 2D;\n"
+ "MOV ayuv.x, matrix[3].w;\n"
+ "DP4 result.color.x, ayuv.yzwx, matrix[0];\n"
+ "DP4 result.color.y, ayuv.yzwx, matrix[1];\n"
+ "DP4 result.color.z, ayuv.yzwx, matrix[2];\n"
+ "TEX result.color.w, fragment.texcoord[0], texture, 2D;\n"
+ "END";
+
+
+
+ArbFpSurfacePainter::ArbFpSurfacePainter(QGLContext *context)
+ : OpenGLSurfacePainter(context)
+ , m_programId(0)
+{
+ glProgramStringARB = (_glProgramStringARB) m_context->getProcAddress(
+ QLatin1String("glProgramStringARB"));
+ glBindProgramARB = (_glBindProgramARB) m_context->getProcAddress(
+ QLatin1String("glBindProgramARB"));
+ glDeleteProgramsARB = (_glDeleteProgramsARB) m_context->getProcAddress(
+ QLatin1String("glDeleteProgramsARB"));
+ glGenProgramsARB = (_glGenProgramsARB) m_context->getProcAddress(
+ QLatin1String("glGenProgramsARB"));
+ glProgramLocalParameter4fARB = (_glProgramLocalParameter4fARB) m_context->getProcAddress(
+ QLatin1String("glProgramLocalParameter4fARB"));
+}
+
+//static
+QSet<BufferFormat::PixelFormat> ArbFpSurfacePainter::supportedPixelFormats()
+{
+ return QSet<BufferFormat::PixelFormat>()
+ << BufferFormat::RGB32
+ << BufferFormat::BGR32
+ << BufferFormat::ARGB32
+ << BufferFormat::RGB24
+ << BufferFormat::BGR24
+ << BufferFormat::RGB565
+ << BufferFormat::AYUV444
+ << BufferFormat::YUV444
+ << BufferFormat::YV12
+ << BufferFormat::YUV420P;
+}
+
+void ArbFpSurfacePainter::init(const BufferFormat &format)
+{
+ Q_ASSERT(m_textureCount == 0);
+
+ m_context->makeCurrent();
+
+ const char *program = 0;
+
+ switch (format.pixelFormat()) {
+ case BufferFormat::RGB32:
+ initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ program = qt_arbfp_xrgbShaderProgram;
+ break;
+ case BufferFormat::BGR32:
+ initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ program = qt_arbfp_rgbShaderProgram;
+ break;
+ case BufferFormat::ARGB32:
+ initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ program = qt_arbfp_argbShaderProgram;
+ break;
+ case BufferFormat::RGB24:
+ initRgbTextureInfo(GL_RGB8, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ program = qt_arbfp_rgbShaderProgram;
+ break;
+ case BufferFormat::BGR24:
+ initRgbTextureInfo(GL_RGB8, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ program = qt_arbfp_xrgbShaderProgram;
+ break;
+ case BufferFormat::RGB565:
+ initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, format.frameSize());
+ program = qt_arbfp_rgbShaderProgram;
+ break;
+ case BufferFormat::YUV444:
+ initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize());
+ program = qt_arbfp_xyuvShaderProgram;
+ m_yuv = true;
+ break;
+ case BufferFormat::AYUV444:
+ initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ program = qt_arbfp_ayuvShaderProgram;
+ m_yuv = true;
+ break;
+ case BufferFormat::YV12:
+ initYv12TextureInfo(format.frameSize());
+ program = qt_arbfp_yuvPlanarShaderProgram;
+ break;
+ case BufferFormat::YUV420P:
+ initYuv420PTextureInfo(format.frameSize());
+ program = qt_arbfp_yuvPlanarShaderProgram;
+ break;
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+
+ glGenProgramsARB(1, &m_programId);
+
+ GLenum glError = glGetError();
+ if (glError != GL_NO_ERROR) {
+ throw QString("ARBfb Shader allocation error ") +
+ QString::number(static_cast<int>(glError), 16);
+ } else {
+ glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_programId);
+ glProgramStringARB(
+ GL_FRAGMENT_PROGRAM_ARB,
+ GL_PROGRAM_FORMAT_ASCII_ARB,
+ qstrlen(program),
+ reinterpret_cast<const GLvoid *>(program));
+
+ if ((glError = glGetError()) != GL_NO_ERROR) {
+ const GLubyte* errorString = glGetString(GL_PROGRAM_ERROR_STRING_ARB);
+
+ glDeleteProgramsARB(1, &m_programId);
+ m_textureCount = 0;
+ m_programId = 0;
+
+ throw QString("ARBfp Shader compile error ") +
+ QString::number(static_cast<int>(glError), 16) +
+ reinterpret_cast<const char *>(errorString);
+ } else {
+ glGenTextures(m_textureCount, m_textureIds);
+ }
+ }
+}
+
+void ArbFpSurfacePainter::cleanup()
+{
+ m_context->makeCurrent();
+
+ glDeleteTextures(m_textureCount, m_textureIds);
+ glDeleteProgramsARB(1, &m_programId);
+
+ m_textureCount = 0;
+ m_programId = 0;
+}
+
+void ArbFpSurfacePainter::paint(quint8 *data,
+ const BufferFormat & frameFormat,
+ QPainter *painter,
+ const QRect & videoArea,
+ const QRect & clipRect)
+{
+ setCurrentFrame(data);
+
+ bool stencilTestEnabled = glIsEnabled(GL_STENCIL_TEST);
+ bool scissorTestEnabled = glIsEnabled(GL_SCISSOR_TEST);
+
+ painter->beginNativePainting();
+
+ if (stencilTestEnabled)
+ glEnable(GL_STENCIL_TEST);
+ if (scissorTestEnabled)
+ glEnable(GL_SCISSOR_TEST);
+
+ const float txLeft = clipRect.left() / frameFormat.frameSize().width();
+ const float txRight = clipRect.right() / frameFormat.frameSize().width();
+ const float txTop = clipRect.top() / frameFormat.frameSize().height();
+ const float txBottom = clipRect.bottom() / frameFormat.frameSize().height();
+
+ const float tx_array[] =
+ {
+ txLeft , txBottom,
+ txRight, txBottom,
+ txLeft , txTop,
+ txRight, txTop
+ };
+
+ const GLfloat vTop = videoArea.top();
+ const GLfloat vBottom = videoArea.bottom() + 1;
+
+ const GLfloat v_array[] =
+ {
+ GLfloat(videoArea.left()) , GLfloat(vBottom),
+ GLfloat(videoArea.right() + 1), GLfloat(vBottom),
+ GLfloat(videoArea.left()) , GLfloat(vTop),
+ GLfloat(videoArea.right() + 1), GLfloat(vTop)
+ };
+
+ glEnable(GL_FRAGMENT_PROGRAM_ARB);
+ glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_programId);
+
+ glProgramLocalParameter4fARB(
+ GL_FRAGMENT_PROGRAM_ARB,
+ 0,
+ m_colorMatrix(0, 0),
+ m_colorMatrix(0, 1),
+ m_colorMatrix(0, 2),
+ m_colorMatrix(0, 3));
+ glProgramLocalParameter4fARB(
+ GL_FRAGMENT_PROGRAM_ARB,
+ 1,
+ m_colorMatrix(1, 0),
+ m_colorMatrix(1, 1),
+ m_colorMatrix(1, 2),
+ m_colorMatrix(1, 3));
+ glProgramLocalParameter4fARB(
+ GL_FRAGMENT_PROGRAM_ARB,
+ 2,
+ m_colorMatrix(2, 0),
+ m_colorMatrix(2, 1),
+ m_colorMatrix(2, 2),
+ m_colorMatrix(2, 3));
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[0]);
+
+ if (m_textureCount == 3) {
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[1]);
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[2]);
+ glActiveTexture(GL_TEXTURE0);
+ }
+
+ glVertexPointer(2, GL_FLOAT, 0, v_array);
+ glTexCoordPointer(2, GL_FLOAT, 0, tx_array);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisable(GL_FRAGMENT_PROGRAM_ARB);
+
+ painter->endNativePainting();
+}
+
+#endif
+
+static const char *qt_glsl_vertexShaderProgram =
+ "attribute highp vec4 vertexCoordArray;\n"
+ "attribute highp vec2 textureCoordArray;\n"
+ "uniform highp mat4 positionMatrix;\n"
+ "varying highp vec2 textureCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " gl_Position = positionMatrix * vertexCoordArray;\n"
+ " textureCoord = textureCoordArray;\n"
+ "}\n";
+
+// Paints an RGB32 frame
+static const char *qt_glsl_xrgbShaderProgram =
+ "uniform sampler2D texRgb;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "varying highp vec2 textureCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).bgr, 1.0);\n"
+ " gl_FragColor = colorMatrix * color;\n"
+ "}\n";
+
+// Paints an ARGB frame.
+static const char *qt_glsl_argbShaderProgram =
+ "uniform sampler2D texRgb;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "varying highp vec2 textureCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).bgr, 1.0);\n"
+ " color = colorMatrix * color;\n"
+ " gl_FragColor = vec4(color.rgb, texture2D(texRgb, textureCoord.st).a);\n"
+ "}\n";
+
+// Paints an RGB(A) frame.
+static const char *qt_glsl_rgbShaderProgram =
+ "uniform sampler2D texRgb;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "varying highp vec2 textureCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).rgb, 1.0);\n"
+ " color = colorMatrix * color;\n"
+ " gl_FragColor = vec4(color.rgb, texture2D(texRgb, textureCoord.st).a);\n"
+ "}\n";
+
+// Paints a YUV420P or YV12 frame.
+static const char *qt_glsl_yuvPlanarShaderProgram =
+ "uniform sampler2D texY;\n"
+ "uniform sampler2D texU;\n"
+ "uniform sampler2D texV;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "varying highp vec2 textureCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(\n"
+ " texture2D(texY, textureCoord.st).r,\n"
+ " texture2D(texU, textureCoord.st).r,\n"
+ " texture2D(texV, textureCoord.st).r,\n"
+ " 1.0);\n"
+ " gl_FragColor = colorMatrix * color;\n"
+ "}\n";
+
+// Paints a YUV444 frame.
+static const char *qt_glsl_xyuvShaderProgram =
+ "uniform sampler2D texRgb;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "varying highp vec2 textureCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).gba, 1.0);\n"
+ " gl_FragColor = colorMatrix * color;\n"
+ "}\n";
+
+// Paints a AYUV444 frame.
+static const char *qt_glsl_ayuvShaderProgram =
+ "uniform sampler2D texRgb;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "varying highp vec2 textureCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(texture2D(texRgb, textureCoord.st).gba, 1.0);\n"
+ " color = colorMatrix * color;\n"
+ " gl_FragColor = vec4(color.rgb, texture2D(texRgb, textureCoord.st).r);\n"
+ "}\n";
+
+
+
+GlslSurfacePainter::GlslSurfacePainter(QGLContext *context)
+ : OpenGLSurfacePainter(context)
+ , m_program(context)
+{
+}
+
+//static
+QSet<BufferFormat::PixelFormat> GlslSurfacePainter::supportedPixelFormats()
+{
+ return QSet<BufferFormat::PixelFormat>()
+ << BufferFormat::YUV420P
+ << BufferFormat::YV12
+ << BufferFormat::RGB32
+ << BufferFormat::BGR32
+#if !defined(QT_OPENGL_ES) && !defined(QT_OPENGL_ES_2)
+ << BufferFormat::RGB24
+ << BufferFormat::BGR24
+
+ << BufferFormat::RGB565
+ << BufferFormat::AYUV444
+ << BufferFormat::YUV444
+#endif
+ << BufferFormat::ARGB32;
+}
+
+void GlslSurfacePainter::init(const BufferFormat &format)
+{
+ Q_ASSERT(m_textureCount == 0);
+
+ m_context->makeCurrent();
+
+ const char *fragmentProgram = 0;
+
+ switch (format.pixelFormat()) {
+ case BufferFormat::RGB32:
+ initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ fragmentProgram = qt_glsl_xrgbShaderProgram;
+ break;
+ case BufferFormat::BGR32:
+ initRgbTextureInfo(GL_RGB, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ fragmentProgram = qt_glsl_rgbShaderProgram;
+ break;
+ case BufferFormat::ARGB32:
+ initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ fragmentProgram = qt_glsl_argbShaderProgram;
+ break;
+#ifndef QT_OPENGL_ES
+ case BufferFormat::RGB24:
+ initRgbTextureInfo(GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize());
+ fragmentProgram = qt_glsl_rgbShaderProgram;
+ break;
+ case BufferFormat::BGR24:
+ initRgbTextureInfo(GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize());
+ fragmentProgram = qt_glsl_argbShaderProgram;
+ break;
+#endif
+ case BufferFormat::RGB565:
+ initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, format.frameSize());
+ fragmentProgram = qt_glsl_rgbShaderProgram;
+ break;
+ case BufferFormat::YUV444:
+ initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize());
+ fragmentProgram = qt_glsl_xyuvShaderProgram;
+ m_yuv = true;
+ break;
+ case BufferFormat::AYUV444:
+ initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ fragmentProgram = qt_glsl_ayuvShaderProgram;
+ m_yuv = true;
+ break;
+ case BufferFormat::YV12:
+ initYv12TextureInfo(format.frameSize());
+ fragmentProgram = qt_glsl_yuvPlanarShaderProgram;
+ break;
+ case BufferFormat::YUV420P:
+ initYuv420PTextureInfo(format.frameSize());
+ fragmentProgram = qt_glsl_yuvPlanarShaderProgram;
+ break;
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+
+ if (!m_program.addShaderFromSourceCode(QGLShader::Vertex, qt_glsl_vertexShaderProgram))
+ {
+ throw QString("Vertex shader compile error ") + m_program.log();
+ }
+ else if (!m_program.addShaderFromSourceCode(QGLShader::Fragment, fragmentProgram))
+ {
+ throw QString("Shader compile error ") + m_program.log();
+ }
+ else if(!m_program.link())
+ {
+ throw QString("Shader link error ") + m_program.log();
+ }
+ else
+ {
+ glGenTextures(m_textureCount, m_textureIds);
+ }
+}
+
+void GlslSurfacePainter::cleanup()
+{
+ m_context->makeCurrent();
+
+ glDeleteTextures(m_textureCount, m_textureIds);
+ m_program.removeAllShaders();
+
+ m_textureCount = 0;
+}
+
+void GlslSurfacePainter::paint(quint8 *data,
+ const BufferFormat & frameFormat,
+ QPainter *painter,
+ const QRect & videoArea,
+ const QRect & clipRect)
+{
+ setCurrentFrame(data);
+
+ bool stencilTestEnabled = glIsEnabled(GL_STENCIL_TEST);
+ bool scissorTestEnabled = glIsEnabled(GL_SCISSOR_TEST);
+
+ painter->beginNativePainting();
+
+ if (stencilTestEnabled)
+ glEnable(GL_STENCIL_TEST);
+ if (scissorTestEnabled)
+ glEnable(GL_SCISSOR_TEST);
+
+ const int width = QGLContext::currentContext()->device()->width();
+ const int height = QGLContext::currentContext()->device()->height();
+
+ const QTransform transform = painter->deviceTransform();
+
+ const GLfloat wfactor = 2.0 / width;
+ const GLfloat hfactor = -2.0 / height;
+
+ const GLfloat positionMatrix[4][4] =
+ {
+ {
+ /*(0,0)*/ GLfloat(wfactor * transform.m11() - transform.m13()),
+ /*(0,1)*/ GLfloat(hfactor * transform.m12() + transform.m13()),
+ /*(0,2)*/ 0.0,
+ /*(0,3)*/ GLfloat(transform.m13())
+ }, {
+ /*(1,0)*/ GLfloat(wfactor * transform.m21() - transform.m23()),
+ /*(1,1)*/ GLfloat(hfactor * transform.m22() + transform.m23()),
+ /*(1,2)*/ 0.0,
+ /*(1,3)*/ GLfloat(transform.m23())
+ }, {
+ /*(2,0)*/ 0.0,
+ /*(2,1)*/ 0.0,
+ /*(2,2)*/ -1.0,
+ /*(2,3)*/ 0.0
+ }, {
+ /*(3,0)*/ GLfloat(wfactor * transform.dx() - transform.m33()),
+ /*(3,1)*/ GLfloat(hfactor * transform.dy() + transform.m33()),
+ /*(3,2)*/ 0.0,
+ /*(3,3)*/ GLfloat(transform.m33())
+ }
+ };
+
+ const GLfloat vTop = videoArea.top();
+ const GLfloat vBottom = videoArea.bottom() + 1;
+
+ const GLfloat vertexCoordArray[] =
+ {
+ GLfloat(videoArea.left()) , GLfloat(vBottom),
+ GLfloat(videoArea.right() + 1), GLfloat(vBottom),
+ GLfloat(videoArea.left()) , GLfloat(vTop),
+ GLfloat(videoArea.right() + 1), GLfloat(vTop)
+ };
+
+ const GLfloat txLeft = clipRect.left() / frameFormat.frameSize().width();
+ const GLfloat txRight = clipRect.right() / frameFormat.frameSize().width();
+ const GLfloat txTop = clipRect.top() / frameFormat.frameSize().height();
+ const GLfloat txBottom = clipRect.bottom() / frameFormat.frameSize().height();
+
+ const GLfloat textureCoordArray[] =
+ {
+ txLeft , txBottom,
+ txRight, txBottom,
+ txLeft , txTop,
+ txRight, txTop
+ };
+
+ m_program.bind();
+
+ m_program.enableAttributeArray("vertexCoordArray");
+ m_program.enableAttributeArray("textureCoordArray");
+ m_program.setAttributeArray("vertexCoordArray", vertexCoordArray, 2);
+ m_program.setAttributeArray("textureCoordArray", textureCoordArray, 2);
+ m_program.setUniformValue("positionMatrix", positionMatrix);
+
+ if (m_textureCount == 3) {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[0]);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[1]);
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[2]);
+ glActiveTexture(GL_TEXTURE0);
+
+ m_program.setUniformValue("texY", 0);
+ m_program.setUniformValue("texU", 1);
+ m_program.setUniformValue("texV", 2);
+ } else {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[0]);
+
+ m_program.setUniformValue("texRgb", 0);
+ }
+ m_program.setUniformValue("colorMatrix", m_colorMatrix);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ m_program.release();
+
+ painter->endNativePainting();
+}
diff --git a/elements/gstqtvideosink/openglsurfacepainter.h b/elements/gstqtvideosink/openglsurfacepainter.h
new file mode 100644
index 0000000..a9cf72b
--- /dev/null
+++ b/elements/gstqtvideosink/openglsurfacepainter.h
@@ -0,0 +1,132 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
+ Copyright (C) 2011 Collabora Ltd. <info@collabora.com>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef OPENGLSURFACEPAINTER_H
+#define OPENGLSURFACEPAINTER_H
+
+#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
+
+#include "abstractsurfacepainter.h"
+#include <QtOpenGL/QGLShaderProgram>
+
+#ifndef Q_WS_MAC
+# ifndef APIENTRYP
+# ifdef APIENTRY
+# define APIENTRYP APIENTRY *
+# else
+# define APIENTRY
+# define APIENTRYP *
+# endif
+# endif
+#else
+# define APIENTRY
+# define APIENTRYP *
+#endif
+
+class OpenGLSurfacePainter : public AbstractSurfacePainter
+{
+public:
+ OpenGLSurfacePainter(QGLContext *context);
+
+ virtual void updateColors(int brightness, int contrast, int hue, int saturation,
+ BufferFormat::YCbCrColorSpace colorSpace);
+
+protected:
+ void setCurrentFrame(quint8 *data);
+ void initRgbTextureInfo(GLenum internalFormat, GLuint format, GLenum type, const QSize &size);
+ void initYuv420PTextureInfo(const QSize &size);
+ void initYv12TextureInfo(const QSize &size);
+
+#ifndef QT_OPENGL_ES
+ typedef void (APIENTRY *_glActiveTexture) (GLenum);
+ _glActiveTexture glActiveTexture;
+#endif
+
+ QMatrix4x4 m_colorMatrix;
+
+ QGLContext *m_context;
+ GLenum m_textureFormat;
+ GLuint m_textureInternalFormat;
+ GLenum m_textureType;
+ int m_textureCount;
+ GLuint m_textureIds[3];
+ int m_textureWidths[3];
+ int m_textureHeights[3];
+ int m_textureOffsets[3];
+ bool m_yuv;
+};
+
+
+class ArbFpSurfacePainter : public OpenGLSurfacePainter
+{
+public:
+ ArbFpSurfacePainter(QGLContext *context);
+
+ static QSet<BufferFormat::PixelFormat> supportedPixelFormats();
+
+ virtual bool supportsFormat(BufferFormat::PixelFormat format) const {
+ return supportedPixelFormats().contains(format);
+ }
+
+ virtual void init(const BufferFormat & format);
+ virtual void cleanup();
+
+ virtual void paint(quint8 *data, const BufferFormat & frameFormat,
+ QPainter *painter, const QRect & videoArea, const QRect & clipRect);
+
+private:
+ typedef void (APIENTRY *_glProgramStringARB) (GLenum, GLenum, GLsizei, const GLvoid *);
+ typedef void (APIENTRY *_glBindProgramARB) (GLenum, GLuint);
+ typedef void (APIENTRY *_glDeleteProgramsARB) (GLsizei, const GLuint *);
+ typedef void (APIENTRY *_glGenProgramsARB) (GLsizei, GLuint *);
+ typedef void (APIENTRY *_glProgramLocalParameter4fARB) (
+ GLenum, GLuint, GLfloat, GLfloat, GLfloat, GLfloat);
+ typedef void (APIENTRY *_glActiveTexture) (GLenum);
+
+ _glProgramStringARB glProgramStringARB;
+ _glBindProgramARB glBindProgramARB;
+ _glDeleteProgramsARB glDeleteProgramsARB;
+ _glGenProgramsARB glGenProgramsARB;
+ _glProgramLocalParameter4fARB glProgramLocalParameter4fARB;
+
+ GLuint m_programId;
+};
+
+
+class GlslSurfacePainter : public OpenGLSurfacePainter
+{
+public:
+ GlslSurfacePainter(QGLContext *context);
+
+ static QSet<BufferFormat::PixelFormat> supportedPixelFormats();
+
+ virtual bool supportsFormat(BufferFormat::PixelFormat format) const {
+ return supportedPixelFormats().contains(format);
+ }
+
+ virtual void init(const BufferFormat & format);
+ virtual void cleanup();
+
+ virtual void paint(quint8 *data, const BufferFormat & frameFormat,
+ QPainter *painter, const QRect & videoArea, const QRect & clipRect);
+
+private:
+ QGLShaderProgram m_program;
+};
+
+#endif // GST_QT_VIDEO_SINK_NO_OPENGL
+
+#endif // OPENGLSURFACEPAINTER_H