summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Kiagiadakis <george.kiagiadakis@collabora.com>2013-10-21 14:15:41 +0200
committerGeorge Kiagiadakis <george.kiagiadakis@collabora.com>2013-10-28 13:17:27 +0100
commitb459cd8d32d70da017cef13528864e77bbf80c72 (patch)
tree4c1e1a8041986e4b92abd670153f424a92652ad2
parent48ea384835dd8dd75bfab196da56df274a161b0b (diff)
qtvideosink: add a qtquick2 video sink, based on patches by Benjamin Federau
-rw-r--r--CMakeLists.txt5
-rw-r--r--elements/gstqtvideosink/CMakeLists.txt42
-rw-r--r--elements/gstqtvideosink/delegates/basedelegate.h1
-rw-r--r--elements/gstqtvideosink/delegates/qtquick2videosinkdelegate.cpp106
-rw-r--r--elements/gstqtvideosink/delegates/qtquick2videosinkdelegate.h32
-rw-r--r--elements/gstqtvideosink/delegates/qtvideosinkdelegate.cpp8
-rw-r--r--elements/gstqtvideosink/gstqtquick2videosink.cpp484
-rw-r--r--elements/gstqtvideosink/gstqtquick2videosink.h57
-rw-r--r--elements/gstqtvideosink/gstqtvideosinkplugin.cpp9
-rw-r--r--elements/gstqtvideosink/painters/videomaterial.cpp450
-rw-r--r--elements/gstqtvideosink/painters/videomaterial.h78
-rw-r--r--elements/gstqtvideosink/painters/videonode.cpp112
-rw-r--r--elements/gstqtvideosink/painters/videonode.h50
-rw-r--r--elements/gstqtvideosink/utils/utils.h8
14 files changed, 1423 insertions, 19 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ccc4226..3cc1b9e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -21,7 +21,7 @@ include(MacroLogFeature)
set(Qt4_MIN_VERSION 4.7)
set(Qt5_MIN_VERSION 5.0.0)
-find_package(Qt4or5 COMPONENTS Core Gui Widgets OPTIONAL_COMPONENTS OpenGL Quick1 Test)
+find_package(Qt4or5 COMPONENTS Core Gui Widgets OPTIONAL_COMPONENTS OpenGL Quick1 Quick2 Test)
macro_log_feature(Qt4or5_FOUND "Qt" "Required for building everything"
"http://qt-project.org/" TRUE "${Qt4or5_MIN_VERSION}")
macro_log_feature(Qt4or5_OpenGL_FOUND "QtOpenGL"
@@ -30,6 +30,9 @@ macro_log_feature(Qt4or5_OpenGL_FOUND "QtOpenGL"
macro_log_feature(Qt4or5_Quick1_FOUND "QtQuick1 (QtDeclarative)"
"Required for building QtQuick1 support"
"http://qt-project.org/" FALSE "${Qt4or5_MIN_VERSION}")
+macro_log_feature(Qt4or5_Quick2_FOUND "QtQuick2 (QtQuick)"
+ "Required for building QtQuick2 support"
+ "http://qt-project.org/" FALSE "${Qt4or5_MIN_VERSION}")
if (QTGSTREAMER_TESTS)
macro_log_feature(Qt4or5_Test_FOUND "QtTest" "Required for building unit tests"
diff --git a/elements/gstqtvideosink/CMakeLists.txt b/elements/gstqtvideosink/CMakeLists.txt
index 901cb94..ddb3da9 100644
--- a/elements/gstqtvideosink/CMakeLists.txt
+++ b/elements/gstqtvideosink/CMakeLists.txt
@@ -1,6 +1,8 @@
glib2_genmarshal(gstqtvideosinkmarshal
VOID:POINTER,FLOAT,FLOAT,FLOAT,FLOAT
VOID:POINTER,DOUBLE,DOUBLE,DOUBLE,DOUBLE
+ POINTER:POINTER,FLOAT,FLOAT,FLOAT,FLOAT
+ POINTER:POINTER,DOUBLE,DOUBLE,DOUBLE,DOUBLE
)
set(GstQtVideoSink_SRCS
@@ -21,8 +23,22 @@ set(GstQtVideoSink_SRCS
${CMAKE_CURRENT_BINARY_DIR}/gstqtvideosinkmarshal.c
)
+if (Qt4or5_Quick2_FOUND AND (OPENGL_FOUND OR OPENGLES2_FOUND))
+ set(GstQtVideoSink_SRCS
+ ${GstQtVideoSink_SRCS}
+ painters/videomaterial.cpp
+ painters/videonode.cpp
+
+ delegates/qtquick2videosinkdelegate.cpp
+
+ gstqtquick2videosink.cpp
+ )
+ set(GstQtVideoSink_LINK_OPENGL TRUE)
+endif()
+
if (Qt4or5_OpenGL_FOUND AND (OPENGL_FOUND OR OPENGLES2_FOUND))
- set(GstQtVideoSink_GL_SRCS
+ set(GstQtVideoSink_SRCS
+ ${GstQtVideoSink_SRCS}
painters/openglsurfacepainter.cpp
gstqtglvideosinkbase.cpp
gstqtglvideosink.cpp
@@ -30,14 +46,7 @@ if (Qt4or5_OpenGL_FOUND AND (OPENGL_FOUND OR OPENGLES2_FOUND))
set(GstQtVideoSink_test_GL_SRCS
painters/openglsurfacepainter.cpp
)
-
- if (OPENGLES2_FOUND)
- set(GstQtVideoSink_GL_LIBS ${OPENGLES2_LIBRARY})
- include_directories(${OPENGLES2_INCLUDE_DIR})
- else()
- set(GstQtVideoSink_GL_LIBS ${OPENGL_gl_LIBRARY})
- include_directories(${OPENGL_INCLUDE_DIR})
- endif()
+ set(GstQtVideoSink_LINK_OPENGL TRUE)
else()
add_definitions(-DGST_QT_VIDEO_SINK_NO_OPENGL)
endif()
@@ -48,7 +57,17 @@ add_definitions(
-DQWIDGETVIDEOSINK_NAME="${QWIDGETVIDEOSINK_NAME}"
)
-add_library(gst${QTVIDEOSINK_NAME} MODULE ${GstQtVideoSink_SRCS} ${GstQtVideoSink_GL_SRCS})
+if (GstQtVideoSink_LINK_OPENGL)
+ if (OPENGLES2_FOUND)
+ set(GstQtVideoSink_GL_LIBS ${OPENGLES2_LIBRARY})
+ include_directories(${OPENGLES2_INCLUDE_DIR})
+ else()
+ set(GstQtVideoSink_GL_LIBS ${OPENGL_gl_LIBRARY})
+ include_directories(${OPENGL_INCLUDE_DIR})
+ endif()
+endif()
+
+add_library(gst${QTVIDEOSINK_NAME} MODULE ${GstQtVideoSink_SRCS})
target_link_libraries(gst${QTVIDEOSINK_NAME}
${GOBJECT_LIBRARIES}
${GSTREAMER_LIBRARY}
@@ -58,6 +77,9 @@ target_link_libraries(gst${QTVIDEOSINK_NAME}
${GstQtVideoSink_GL_LIBS}
)
qt4or5_use_modules(gst${QTVIDEOSINK_NAME} Core Gui Widgets)
+if (Qt4or5_Quick2_FOUND AND (OPENGL_FOUND OR OPENGLES2_FOUND))
+ qt4or5_use_modules(gst${QTVIDEOSINK_NAME} Quick2)
+endif()
if (Qt4or5_OpenGL_FOUND AND (OPENGL_FOUND OR OPENGLES2_FOUND))
qt4or5_use_modules(gst${QTVIDEOSINK_NAME} OpenGL)
endif()
diff --git a/elements/gstqtvideosink/delegates/basedelegate.h b/elements/gstqtvideosink/delegates/basedelegate.h
index 490240c..1e02f3f 100644
--- a/elements/gstqtvideosink/delegates/basedelegate.h
+++ b/elements/gstqtvideosink/delegates/basedelegate.h
@@ -20,6 +20,7 @@
#include <gst/gst.h>
+#include "../gstqtvideosinkplugin.h" //for debug category
#include "../utils/bufferformat.h"
#include "../utils/utils.h"
diff --git a/elements/gstqtvideosink/delegates/qtquick2videosinkdelegate.cpp b/elements/gstqtvideosink/delegates/qtquick2videosinkdelegate.cpp
new file mode 100644
index 0000000..f9db08b
--- /dev/null
+++ b/elements/gstqtvideosink/delegates/qtquick2videosinkdelegate.cpp
@@ -0,0 +1,106 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
+ Copyright (C) 2011-2013 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 Lesser 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 "qtquick2videosinkdelegate.h"
+#include "../painters/videonode.h"
+
+QtQuick2VideoSinkDelegate::QtQuick2VideoSinkDelegate(GstElement *sink, QObject *parent)
+ : BaseDelegate(sink, parent)
+{
+}
+
+QSGNode* QtQuick2VideoSinkDelegate::updateNode(QSGNode *node, const QRectF & targetArea)
+{
+ GST_TRACE_OBJECT(m_sink, "updateNode called");
+ bool sgnodeFormatChanged = false;
+
+ VideoNode *vnode = dynamic_cast<VideoNode*>(node);
+ if (!vnode) {
+ GST_INFO_OBJECT(m_sink, "creating new VideoNode");
+ vnode = new VideoNode;
+ }
+
+ if (!m_buffer) {
+ if (vnode->materialType() != VideoNode::MaterialTypeSolidBlack) {
+ vnode->setMaterialTypeSolidBlack();
+ sgnodeFormatChanged = true;
+ }
+ if (sgnodeFormatChanged || targetArea != m_areas.targetArea) {
+ m_areas.targetArea = targetArea;
+ vnode->updateGeometry(m_areas);
+ }
+ } else {
+ BufferFormat format = m_formatDirty ?
+ BufferFormat::fromCaps(GST_BUFFER_CAPS(m_buffer)) : m_bufferFormat;
+
+ //change format before geometry, so that we change QSGGeometry as well
+ if (m_formatDirty) {
+ vnode->changeFormat(format);
+ sgnodeFormatChanged = true;
+ }
+
+ //recalculate the video area if needed
+ QReadLocker forceAspectRatioLocker(&m_forceAspectRatioLock);
+ if (sgnodeFormatChanged || targetArea != m_areas.targetArea || m_forceAspectRatioDirty) {
+ m_forceAspectRatioDirty = false;
+
+ QReadLocker pixelAspectRatioLocker(&m_pixelAspectRatioLock);
+ Qt::AspectRatioMode aspectRatioMode = m_forceAspectRatio ?
+ Qt::KeepAspectRatio : Qt::IgnoreAspectRatio;
+ m_areas.calculate(targetArea, format.frameSize(),
+ format.pixelAspectRatio(), m_pixelAspectRatio,
+ aspectRatioMode);
+ pixelAspectRatioLocker.unlock();
+
+ GST_LOG_OBJECT(m_sink,
+ "Recalculated paint areas: "
+ "Frame size: " QSIZE_FORMAT ", "
+ "target area: " QRECTF_FORMAT ", "
+ "video area: " QRECTF_FORMAT ", "
+ "black1: " QRECTF_FORMAT ", "
+ "black2: " QRECTF_FORMAT,
+ QSIZE_FORMAT_ARGS(format.frameSize()),
+ QRECTF_FORMAT_ARGS(m_areas.targetArea),
+ QRECTF_FORMAT_ARGS(m_areas.videoArea),
+ QRECTF_FORMAT_ARGS(m_areas.blackArea1),
+ QRECTF_FORMAT_ARGS(m_areas.blackArea2)
+ );
+
+ vnode->updateGeometry(m_areas);
+ }
+ forceAspectRatioLocker.unlock();
+
+ if (m_formatDirty) {
+ m_bufferFormat = format;
+ m_formatDirty = false;
+
+ //make sure to update the colors after changing material
+ m_colorsDirty = true;
+ }
+
+ QReadLocker colorsLocker(&m_colorsLock);
+ if (m_colorsDirty) {
+ vnode->updateColors(m_brightness, m_contrast, m_hue, m_saturation);
+ m_colorsDirty = false;
+ }
+ colorsLocker.unlock();
+
+ vnode->setCurrentFrame(m_buffer);
+ }
+
+ return vnode;
+}
diff --git a/elements/gstqtvideosink/delegates/qtquick2videosinkdelegate.h b/elements/gstqtvideosink/delegates/qtquick2videosinkdelegate.h
new file mode 100644
index 0000000..b596658
--- /dev/null
+++ b/elements/gstqtvideosink/delegates/qtquick2videosinkdelegate.h
@@ -0,0 +1,32 @@
+/*
+ Copyright (C) 2013 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 Lesser 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 QTQUICK2VIDEOSINKDELEGATE_H
+#define QTQUICK2VIDEOSINKDELEGATE_H
+
+#include "basedelegate.h"
+#include <QtQuick/QSGNode>
+
+class QtQuick2VideoSinkDelegate : public BaseDelegate
+{
+ Q_OBJECT
+public:
+ explicit QtQuick2VideoSinkDelegate(GstElement * sink, QObject * parent = 0);
+
+ QSGNode *updateNode(QSGNode *node, const QRectF & targetArea);
+};
+
+#endif // QTQUICK2VIDEOSINKDELEGATE_H
diff --git a/elements/gstqtvideosink/delegates/qtvideosinkdelegate.cpp b/elements/gstqtvideosink/delegates/qtvideosinkdelegate.cpp
index b7769d4..1bc7b45 100644
--- a/elements/gstqtvideosink/delegates/qtvideosinkdelegate.cpp
+++ b/elements/gstqtvideosink/delegates/qtvideosinkdelegate.cpp
@@ -22,14 +22,6 @@
#include <QStack>
#include <QPainter>
-#define QSIZE_FORMAT "(%d x %d)"
-#define QSIZE_FORMAT_ARGS(size) \
- size.width(), size.height()
-#define QRECTF_FORMAT "(x: %f, y: %f, w: %f, h: %f)"
-#define QRECTF_FORMAT_ARGS(rect) \
- (float) rect.x(), (float) rect.y(), (float) rect.width(), (float) rect.height()
-
-
QtVideoSinkDelegate::QtVideoSinkDelegate(GstElement *sink, QObject *parent)
: BaseDelegate(sink, parent)
, m_painter(0)
diff --git a/elements/gstqtvideosink/gstqtquick2videosink.cpp b/elements/gstqtvideosink/gstqtquick2videosink.cpp
new file mode 100644
index 0000000..156f7ba
--- /dev/null
+++ b/elements/gstqtvideosink/gstqtquick2videosink.cpp
@@ -0,0 +1,484 @@
+/*
+ Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
+ Copyright (C) 2011-2013 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 Lesser 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 "gstqtquick2videosink.h"
+#include "gstqtvideosinkplugin.h"
+#include "gstqtvideosinkmarshal.h"
+#include "delegates/qtquick2videosinkdelegate.h"
+
+#include <gst/interfaces/colorbalance.h>
+
+#include <cstring>
+#include <QCoreApplication>
+
+#define GST_QT_QUICK2_VIDEO_SINK_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_QT_QUICK2_VIDEO_SINK, GstQtQuick2VideoSinkPrivate))
+
+struct _GstQtQuick2VideoSinkPrivate
+{
+ QtQuick2VideoSinkDelegate *delegate;
+ GList *channels_list;
+ bool formatDirty;
+};
+
+static void gst_qt_quick2_video_sink_init_interfaces (GType g_define_type_id);
+
+GST_BOILERPLATE_FULL (GstQtQuick2VideoSink, gst_qt_quick2_video_sink,
+ GstVideoSink, GST_TYPE_VIDEO_SINK,
+ gst_qt_quick2_video_sink_init_interfaces);
+
+enum {
+ PROP_0,
+ PROP_PIXEL_ASPECT_RATIO,
+ PROP_FORCE_ASPECT_RATIO,
+ PROP_CONTRAST,
+ PROP_BRIGHTNESS,
+ PROP_HUE,
+ PROP_SATURATION,
+};
+
+enum {
+ ACTION_UPDATE_NODE,
+ SIGNAL_UPDATE,
+ LAST_SIGNAL
+};
+
+static guint s_signals[LAST_SIGNAL] = { 0 };
+
+const char * const s_colorbalance_labels[] = {
+ "contrast", "brightness", "hue", "saturation"
+};
+
+//index for s_colorbalance_labels
+enum {
+ LABEL_CONTRAST = 0,
+ LABEL_BRIGHTNESS,
+ LABEL_HUE,
+ LABEL_SATURATION,
+ LABEL_LAST
+};
+
+static void
+gst_qt_quick2_video_sink_init (GstQtQuick2VideoSink *self,
+ GstQtQuick2VideoSinkClass *klass)
+{
+ Q_UNUSED(klass);
+ self->priv = GST_QT_QUICK2_VIDEO_SINK_GET_PRIVATE (self);
+
+ // delegate
+ self->priv->delegate = new QtQuick2VideoSinkDelegate(GST_ELEMENT(self));
+ self->priv->formatDirty = true;
+
+ // colorbalance
+ GstColorBalanceChannel *channel;
+ self->priv->channels_list = NULL;
+
+ for (int i=0; i < LABEL_LAST; i++) {
+ channel = GST_COLOR_BALANCE_CHANNEL(g_object_new(GST_TYPE_COLOR_BALANCE_CHANNEL, NULL));
+ channel->label = g_strdup(s_colorbalance_labels[i]);
+ channel->min_value = -100;
+ channel->max_value = 100;
+
+ self->priv->channels_list = g_list_append(self->priv->channels_list, channel);
+ }
+}
+
+static void
+gst_qt_quick2_video_sink_finalize (GObject *gobject)
+{
+ GstQtQuick2VideoSink *self = GST_QT_QUICK2_VIDEO_SINK (gobject);
+
+ delete self->priv->delegate;
+ self->priv->delegate = 0;
+
+ while (self->priv->channels_list) {
+ GstColorBalanceChannel *channel =
+ GST_COLOR_BALANCE_CHANNEL(self->priv->channels_list->data);
+ g_object_unref(channel);
+ self->priv->channels_list = g_list_next(self->priv->channels_list);
+ }
+
+ g_list_free(self->priv->channels_list);
+
+ G_OBJECT_CLASS (parent_class)->finalize (gobject);
+}
+
+static void
+gst_qt_quick2_video_sink_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GstQtQuick2VideoSink *self = GST_QT_QUICK2_VIDEO_SINK (object);
+
+ switch (property_id) {
+ case PROP_PIXEL_ASPECT_RATIO:
+ {
+ GValue tmp;
+ std::memset(&tmp, 0, sizeof(GValue));
+ g_value_init(&tmp, GST_TYPE_FRACTION);
+ if (g_value_transform(value, &tmp)) {
+ int n = gst_value_get_fraction_numerator(&tmp);
+ int d = gst_value_get_fraction_denominator(&tmp);
+ self->priv->delegate->setPixelAspectRatio(Fraction(n, d));
+ } else {
+ GST_WARNING_OBJECT(object, "Could not transform string to aspect ratio");
+ }
+ g_value_unset(&tmp);
+ break;
+ }
+ case PROP_FORCE_ASPECT_RATIO:
+ self->priv->delegate->setForceAspectRatio(g_value_get_boolean(value));
+ break;
+ case PROP_CONTRAST:
+ self->priv->delegate->setContrast(g_value_get_int(value));
+ break;
+ case PROP_BRIGHTNESS:
+ self->priv->delegate->setBrightness(g_value_get_int(value));
+ break;
+ case PROP_HUE:
+ self->priv->delegate->setHue(g_value_get_int(value));
+ break;
+ case PROP_SATURATION:
+ self->priv->delegate->setSaturation(g_value_get_int(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_qt_quick2_video_sink_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GstQtQuick2VideoSink *self = GST_QT_QUICK2_VIDEO_SINK (object);
+
+ switch (property_id) {
+ case PROP_PIXEL_ASPECT_RATIO:
+ {
+ GValue tmp;
+ Fraction par = self->priv->delegate->pixelAspectRatio();
+ std::memset(&tmp, 0, sizeof(GValue));
+ g_value_init(&tmp, GST_TYPE_FRACTION);
+ gst_value_set_fraction(&tmp, par.numerator, par.denominator);
+ g_value_transform(&tmp, value);
+ g_value_unset(&tmp);
+ break;
+ }
+ case PROP_FORCE_ASPECT_RATIO:
+ g_value_set_boolean(value, self->priv->delegate->forceAspectRatio());
+ break;
+ case PROP_CONTRAST:
+ g_value_set_int(value, self->priv->delegate->contrast());
+ break;
+ case PROP_BRIGHTNESS:
+ g_value_set_int(value, self->priv->delegate->brightness());
+ break;
+ case PROP_HUE:
+ g_value_set_int(value, self->priv->delegate->hue());
+ break;
+ case PROP_SATURATION:
+ g_value_set_int(value, self->priv->delegate->saturation());
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static GstStateChangeReturn
+gst_qt_quick2_video_sink_change_state(GstElement *element,
+ GstStateChange transition)
+{
+ GstQtQuick2VideoSink *self = GST_QT_QUICK2_VIDEO_SINK (element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ self->priv->delegate->setActive(true);
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ self->priv->delegate->setActive(false);
+ break;
+ default:
+ break;
+ }
+
+ return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+}
+
+static gboolean
+gst_qt_quick2_video_sink_set_caps(GstBaseSink *sink, GstCaps *caps)
+{
+ GstQtQuick2VideoSink *self = GST_QT_QUICK2_VIDEO_SINK (sink);
+
+ GST_LOG_OBJECT(self, "new caps %" GST_PTR_FORMAT, caps);
+ self->priv->formatDirty = true;
+ return TRUE;
+}
+
+static GstFlowReturn
+gst_qt_quick2_video_sink_show_frame(GstVideoSink *sink, GstBuffer *buffer)
+{
+ GstQtQuick2VideoSink *self = GST_QT_QUICK2_VIDEO_SINK (sink);
+
+ GST_TRACE_OBJECT(self, "Posting new buffer (%"GST_PTR_FORMAT") for rendering. "
+ "Format dirty: %d", buffer, (int)self->priv->formatDirty);
+
+ QCoreApplication::postEvent(self->priv->delegate,
+ new BaseDelegate::BufferEvent(buffer, self->priv->formatDirty));
+
+ self->priv->formatDirty = false;
+ return GST_FLOW_OK;
+}
+
+//------------------------------
+
+static gpointer
+gst_qt_quick2_video_sink_update_node(GstQtQuick2VideoSink *self, gpointer node,
+ qreal x, qreal y, qreal w, qreal h)
+{
+ return self->priv->delegate->updateNode(static_cast<QSGNode*>(node),
+ QRectF(x, y, w, h));
+}
+
+//------------------------------
+
+static gboolean
+gst_qt_quick2_video_sink_interface_supported(GstImplementsInterface *iface, GType type)
+{
+ Q_UNUSED(iface);
+ return type == GST_TYPE_COLOR_BALANCE;
+}
+
+static void
+gst_qt_quick2_video_sink_implementsiface_init(GstImplementsInterfaceClass *klass, gpointer data)
+{
+ Q_UNUSED(data);
+ klass->supported = gst_qt_quick2_video_sink_interface_supported;
+}
+
+//------------------------------
+
+static const GList *
+gst_qt_quick2_video_sink_colorbalance_list_channels(GstColorBalance *balance)
+{
+ return GST_QT_QUICK2_VIDEO_SINK (balance)->priv->channels_list;
+}
+
+static void
+gst_qt_quick2_video_sink_colorbalance_set_value(GstColorBalance *balance,
+ GstColorBalanceChannel *channel, gint value)
+{
+ GstQtQuick2VideoSink *self = GST_QT_QUICK2_VIDEO_SINK (balance);
+
+ if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_CONTRAST])) {
+ self->priv->delegate->setContrast(value);
+ } else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_BRIGHTNESS])) {
+ self->priv->delegate->setBrightness(value);
+ } else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_HUE])) {
+ self->priv->delegate->setHue(value);
+ } else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_SATURATION])) {
+ self->priv->delegate->setSaturation(value);
+ } else {
+ GST_WARNING_OBJECT(self, "Unknown colorbalance channel %s", channel->label);
+ }
+}
+
+static gint
+gst_qt_quick2_video_sink_colorbalance_get_value(GstColorBalance *balance,
+ GstColorBalanceChannel *channel)
+{
+ GstQtQuick2VideoSink *self = GST_QT_QUICK2_VIDEO_SINK (balance);
+
+ if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_CONTRAST])) {
+ return self->priv->delegate->contrast();
+ } else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_BRIGHTNESS])) {
+ return self->priv->delegate->brightness();
+ } else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_HUE])) {
+ return self->priv->delegate->hue();
+ } else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_SATURATION])) {
+ return self->priv->delegate->saturation();
+ } else {
+ GST_WARNING_OBJECT(self, "Unknown colorbalance channel %s", channel->label);
+ }
+
+ return 0;
+}
+
+static void
+gst_qt_quick2_video_sink_colorbalance_init(GstColorBalanceClass *klass, gpointer data)
+{
+ Q_UNUSED(data);
+ GST_COLOR_BALANCE_TYPE(klass) = GST_COLOR_BALANCE_HARDWARE;
+ klass->list_channels = gst_qt_quick2_video_sink_colorbalance_list_channels;
+ klass->set_value = gst_qt_quick2_video_sink_colorbalance_set_value;
+ klass->get_value = gst_qt_quick2_video_sink_colorbalance_get_value;
+}
+
+//------------------------------
+
+static void
+gst_qt_quick2_video_sink_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
+
+ static GstVideoFormat supportedFormats[] = {
+ GST_VIDEO_FORMAT_BGRA,
+ GST_VIDEO_FORMAT_BGRx,
+ GST_VIDEO_FORMAT_ARGB,
+ GST_VIDEO_FORMAT_xRGB,
+ GST_VIDEO_FORMAT_RGB,
+ GST_VIDEO_FORMAT_RGB16,
+ GST_VIDEO_FORMAT_BGR,
+ GST_VIDEO_FORMAT_v308,
+ GST_VIDEO_FORMAT_AYUV,
+ GST_VIDEO_FORMAT_YV12,
+ GST_VIDEO_FORMAT_I420
+ };
+
+ GstCaps *caps = gst_caps_new_empty();
+ for (uint i = 0; i < sizeof(supportedFormats) / sizeof(GstVideoFormat); i++) {
+ gst_caps_append(caps, BufferFormat::newTemplateCaps(supportedFormats[i]));
+ }
+
+ GstPadTemplate *pad_tmpl = gst_pad_template_new ("sink",
+ GST_PAD_SINK, GST_PAD_ALWAYS, caps);
+ gst_element_class_add_pad_template(element_class, pad_tmpl);
+
+ gst_element_class_set_details_simple(element_class,
+ "QtQuick2 video sink", "Sink/Video",
+ "A video sink that can draw on a QQuickItem",
+ "George Kiagiadakis <george.kiagiadakis@collabora.com>");
+}
+
+static void
+gst_qt_quick2_video_sink_class_init (GstQtQuick2VideoSinkClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = gst_qt_quick2_video_sink_finalize;
+ gobject_class->set_property = gst_qt_quick2_video_sink_set_property;
+ gobject_class->get_property = gst_qt_quick2_video_sink_get_property;
+
+ GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
+ element_class->change_state = gst_qt_quick2_video_sink_change_state;
+
+ GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS(klass);
+ base_sink_class->set_caps = gst_qt_quick2_video_sink_set_caps;
+
+ GstVideoSinkClass *video_sink_class = GST_VIDEO_SINK_CLASS(klass);
+ video_sink_class->show_frame = gst_qt_quick2_video_sink_show_frame;
+
+ GstQtQuick2VideoSinkClass *qtquick2_class = GST_QT_QUICK2_VIDEO_SINK_CLASS(klass);
+ qtquick2_class->update_node = gst_qt_quick2_video_sink_update_node;
+
+ /**
+ * GstQtQuick2VideoSink::pixel-aspect-ratio
+ *
+ * The pixel aspect ratio of the display device.
+ **/
+ g_object_class_install_property(gobject_class, PROP_PIXEL_ASPECT_RATIO,
+ g_param_spec_string("pixel-aspect-ratio", "Pixel aspect ratio",
+ "The pixel aspect ratio of the display device",
+ "1/1", static_cast<GParamFlags>(G_PARAM_READWRITE)));
+
+ /**
+ * GstQtQuick2VideoSink::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(gobject_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)));
+
+ g_object_class_install_property(gobject_class, PROP_CONTRAST,
+ g_param_spec_int("contrast", "Contrast", "The contrast of the video",
+ -100, 100, 0, static_cast<GParamFlags>(G_PARAM_READWRITE)));
+
+ g_object_class_install_property(gobject_class, PROP_BRIGHTNESS,
+ g_param_spec_int("brightness", "Brightness", "The brightness of the video",
+ -100, 100, 0, static_cast<GParamFlags>(G_PARAM_READWRITE)));
+
+ g_object_class_install_property(gobject_class, PROP_HUE,
+ g_param_spec_int("hue", "Hue", "The hue of the video",
+ -100, 100, 0, static_cast<GParamFlags>(G_PARAM_READWRITE)));
+
+ g_object_class_install_property(gobject_class, PROP_SATURATION,
+ g_param_spec_int("saturation", "Saturation", "The saturation of the video",
+ -100, 100, 0, static_cast<GParamFlags>(G_PARAM_READWRITE)));
+
+
+ /**
+ * GstQtQuick2VideoSink::update-node
+ * @node: The QSGNode to update
+ * @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
+ * @returns: The updated QGSNode
+ *
+ * This is an action signal that you can call from your QQuickItem subclass
+ * inside its updateNode function to render the video. It takes a QSGNode*
+ * and the item's area rectangle as arguments. You should schedule to call
+ * this function to repaint the surface whenever the ::update signal is
+ * emited.
+ *
+ * Note that the x,y,width and height arguments are actually qreal.
+ * This means that on architectures like arm they will be float instead
+ * of double. You should cast the arguments to qreal if they are not
+ * already when emitting this signal.
+ */
+ s_signals[ACTION_UPDATE_NODE] =
+ g_signal_new("update-node", G_TYPE_FROM_CLASS(klass),
+ static_cast<GSignalFlags>(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
+ G_STRUCT_OFFSET(GstQtQuick2VideoSinkClass, update_node),
+ NULL, NULL,
+ qRealIsDouble() ?
+ g_cclosure_user_marshal_POINTER__POINTER_DOUBLE_DOUBLE_DOUBLE_DOUBLE :
+ g_cclosure_user_marshal_POINTER__POINTER_FLOAT_FLOAT_FLOAT_FLOAT,
+ G_TYPE_POINTER, 5,
+ G_TYPE_POINTER, G_TYPE_QREAL, G_TYPE_QREAL, G_TYPE_QREAL, G_TYPE_QREAL);
+
+ /**
+ * GstQtQuick2VideoSink::update
+ *
+ * This signal is emited when the surface should be repainted. It should
+ * be connected to QQuickItem::update().
+ */
+ s_signals[SIGNAL_UPDATE] =
+ g_signal_new("update", G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_type_class_add_private (klass, sizeof (GstQtQuick2VideoSinkPrivate));
+}
+
+static void
+gst_qt_quick2_video_sink_init_interfaces (GType g_define_type_id)
+{
+ G_IMPLEMENT_INTERFACE (GST_TYPE_IMPLEMENTS_INTERFACE,
+ gst_qt_quick2_video_sink_implementsiface_init);
+ G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
+ gst_qt_quick2_video_sink_colorbalance_init);
+}
diff --git a/elements/gstqtvideosink/gstqtquick2videosink.h b/elements/gstqtvideosink/gstqtquick2videosink.h
new file mode 100644
index 0000000..e5122d5
--- /dev/null
+++ b/elements/gstqtvideosink/gstqtquick2videosink.h
@@ -0,0 +1,57 @@
+/*
+ Copyright (C) 2013 Collabora Ltd.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ 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 Lesser 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_QUICK2_VIDEO_SINK_H__
+#define __GST_QT_QUICK2_VIDEO_SINK_H__
+
+#include <gst/video/gstvideosink.h>
+#include <QtGlobal>
+
+#define GST_TYPE_QT_QUICK2_VIDEO_SINK \
+ (gst_qt_quick2_video_sink_get_type ())
+#define GST_QT_QUICK2_VIDEO_SINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_QT_QUICK2_VIDEO_SINK, GstQtQuick2VideoSink))
+#define GST_IS_QT_QUICK2_VIDEO_SINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_QT_QUICK2_VIDEO_SINK))
+#define GST_QT_QUICK2_VIDEO_SINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_QT_QUICK2_VIDEO_SINK, GstQtQuick2VideoSinkClass))
+#define GST_IS_QT_QUICK2_VIDEO_SINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_QT_QUICK2_VIDEO_SINK))
+#define GST_QT_QUICK2_VIDEO_SINK_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_QT_QUICK2_VIDEO_SINK, GstQtQuick2VideoSinkClass))
+
+typedef struct _GstQtQuick2VideoSink GstQtQuick2VideoSink;
+typedef struct _GstQtQuick2VideoSinkClass GstQtQuick2VideoSinkClass;
+typedef struct _GstQtQuick2VideoSinkPrivate GstQtQuick2VideoSinkPrivate;
+
+struct _GstQtQuick2VideoSink
+{
+ GstVideoSink parent_instance;
+ GstQtQuick2VideoSinkPrivate *priv;
+};
+
+struct _GstQtQuick2VideoSinkClass
+{
+ GstVideoSinkClass parent_class;
+
+ gpointer (*update_node)(GstQtQuick2VideoSink *self,
+ gpointer node, qreal x, qreal y, qreal w, qreal h);
+};
+
+GType gst_qt_quick2_video_sink_get_type (void);
+
+#endif /* __GST_QT_QUICK2_VIDEO_SINK_H__ */
diff --git a/elements/gstqtvideosink/gstqtvideosinkplugin.cpp b/elements/gstqtvideosink/gstqtvideosinkplugin.cpp
index 7a804d3..6b2e3e5 100644
--- a/elements/gstqtvideosink/gstqtvideosinkplugin.cpp
+++ b/elements/gstqtvideosink/gstqtvideosinkplugin.cpp
@@ -20,6 +20,10 @@
#include "gstqtglvideosink.h"
#include "gstqwidgetvideosink.h"
+#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
+# include "gstqtquick2videosink.h"
+#endif
+
GST_DEBUG_CATEGORY(gst_qt_video_sink_debug);
/* entry point to initialize the plug-in */
@@ -37,6 +41,11 @@ static gboolean plugin_init(GstPlugin *plugin)
gst_element_register(plugin, QWIDGETVIDEOSINK_NAME,
GST_RANK_NONE, GST_TYPE_QWIDGET_VIDEO_SINK);
+#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
+ gst_element_register(plugin, "qtquick2videosink",
+ GST_RANK_NONE, GST_TYPE_QT_QUICK2_VIDEO_SINK);
+#endif
+
return TRUE;
}
diff --git a/elements/gstqtvideosink/painters/videomaterial.cpp b/elements/gstqtvideosink/painters/videomaterial.cpp
new file mode 100644
index 0000000..385891f
--- /dev/null
+++ b/elements/gstqtvideosink/painters/videomaterial.cpp
@@ -0,0 +1,450 @@
+/*
+ Copyright (C) 2011-2013 Collabora Ltd. <info@collabora.com>
+ Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+ Copyright (C) 2013 basysKom GmbH <info@basyskom.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 Lesser 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 "videomaterial.h"
+
+#include <qmath.h>
+#include <QOpenGLContext>
+#include <QOpenGLFunctions>
+#include <QtQuick/QSGMaterialShader>
+
+static const char * const qtvideosink_glsl_vertexShader =
+ "uniform highp mat4 qt_Matrix; \n"
+ "attribute highp vec4 qt_VertexPosition; \n"
+ "attribute highp vec2 qt_VertexTexCoord; \n"
+ "varying highp vec2 qt_TexCoord; \n"
+ "void main() { \n"
+ " qt_TexCoord = qt_VertexTexCoord; \n"
+ " gl_Position = qt_Matrix * qt_VertexPosition; \n"
+ "}";
+
+inline const char * const qtvideosink_glsl_bgrxFragmentShader()
+{
+ return
+ "uniform sampler2D rgbTexture;\n"
+ "uniform lowp float opacity;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "varying highp vec2 qt_TexCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(texture2D(rgbTexture, qt_TexCoord.st).bgr, 1.0);\n"
+ " gl_FragColor = colorMatrix * color * opacity;\n"
+ "}\n";
+}
+
+inline const char * const qtvideosink_glsl_xrgbFragmentShader()
+{
+ return
+ "uniform sampler2D rgbTexture;\n"
+ "uniform lowp float opacity;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "varying highp vec2 qt_TexCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(texture2D(rgbTexture, qt_TexCoord.st).gba, 1.0);\n"
+ " gl_FragColor = colorMatrix * color * opacity;\n"
+ "}\n";
+}
+
+inline const char * const qtvideosink_glsl_rgbxFragmentShader()
+{
+ return
+ "uniform sampler2D rgbTexture;\n"
+ "uniform lowp float opacity;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "varying highp vec2 qt_TexCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(texture2D(rgbTexture, qt_TexCoord.st).rgb, 1.0);\n"
+ " gl_FragColor = colorMatrix * color * opacity;\n"
+ "}\n";
+}
+
+inline const char * const qtvideosink_glsl_yuvPlanarFragmentShader()
+{
+ return
+ "uniform sampler2D yTexture;\n"
+ "uniform sampler2D uTexture;\n"
+ "uniform sampler2D vTexture;\n"
+ "uniform mediump mat4 colorMatrix;\n"
+ "uniform lowp float opacity;\n"
+ "varying highp vec2 qt_TexCoord;\n"
+ "void main(void)\n"
+ "{\n"
+ " highp vec4 color = vec4(\n"
+ " texture2D(yTexture, qt_TexCoord.st).r,\n"
+ " texture2D(uTexture, qt_TexCoord.st).r,\n"
+ " texture2D(vTexture, qt_TexCoord.st).r,\n"
+ " 1.0);\n"
+ " gl_FragColor = colorMatrix * color * opacity;\n"
+ "}\n";
+}
+
+class VideoMaterialShader : public QSGMaterialShader
+{
+public:
+ virtual void updateState(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+ {
+ Q_UNUSED(oldMaterial);
+
+ VideoMaterial *material = static_cast<VideoMaterial *>(newMaterial);
+ if (m_id_rgbTexture) {
+ program()->setUniformValue(m_id_rgbTexture, 0);
+ } else {
+ program()->setUniformValue(m_id_yTexture, 0);
+ program()->setUniformValue(m_id_uTexture, 1);
+ program()->setUniformValue(m_id_vTexture, 2);
+ }
+
+ if (state.isOpacityDirty()) {
+ material->setFlag(QSGMaterial::Blending,
+ qFuzzyCompare(state.opacity(), 1.0f) ? false : true);
+ program()->setUniformValue(m_id_opacity, GLfloat(state.opacity()));
+ }
+
+ if (state.isMatrixDirty())
+ program()->setUniformValue(m_id_matrix, state.combinedMatrix());
+
+ program()->setUniformValue(m_id_colorMatrix, material->m_colorMatrix);
+
+ material->bind();
+ }
+
+ virtual char const *const *attributeNames() const {
+ static const char *names[] = {
+ "qt_VertexPosition",
+ "qt_VertexTexCoord",
+ 0
+ };
+ return names;
+ }
+
+protected:
+ virtual void initialize() {
+ m_id_matrix = program()->uniformLocation("qt_Matrix");
+ m_id_rgbTexture = program()->uniformLocation("rgbTexture");
+ m_id_yTexture = program()->uniformLocation("yTexture");
+ m_id_uTexture = program()->uniformLocation("uTexture");
+ m_id_vTexture = program()->uniformLocation("vTexture");
+ m_id_colorMatrix = program()->uniformLocation("colorMatrix");
+ m_id_opacity = program()->uniformLocation("opacity");
+ }
+
+ virtual const char *vertexShader() const {
+ return qtvideosink_glsl_vertexShader;
+ }
+
+ int m_id_matrix;
+ int m_id_rgbTexture;
+ int m_id_yTexture;
+ int m_id_uTexture;
+ int m_id_vTexture;
+ int m_id_colorMatrix;
+ int m_id_opacity;
+};
+
+template <const char * const (*FragmentShader)()>
+class VideoMaterialShaderImpl : public VideoMaterialShader
+{
+protected:
+ virtual const char *fragmentShader() const {
+ return FragmentShader();
+ }
+};
+
+template <const char * const (*FragmentShader)()>
+class VideoMaterialImpl : public VideoMaterial
+{
+public:
+ virtual QSGMaterialType *type() const {
+ static QSGMaterialType theType;
+ return &theType;
+ }
+
+ virtual QSGMaterialShader *createShader() const {
+ return new VideoMaterialShaderImpl<FragmentShader>;
+ }
+};
+
+VideoMaterial *VideoMaterial::create(const BufferFormat & format)
+{
+ VideoMaterial *material = NULL;
+
+ switch (format.videoFormat()) {
+ // BGRx
+ case GST_VIDEO_FORMAT_BGRx:
+ case GST_VIDEO_FORMAT_BGRA:
+ material = new VideoMaterialImpl<qtvideosink_glsl_bgrxFragmentShader>;
+ material->initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ break;
+ case GST_VIDEO_FORMAT_BGR:
+ material = new VideoMaterialImpl<qtvideosink_glsl_bgrxFragmentShader>;
+ material->initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize());
+ break;
+
+ // xRGB
+ case GST_VIDEO_FORMAT_xRGB:
+ case GST_VIDEO_FORMAT_ARGB:
+ case GST_VIDEO_FORMAT_AYUV:
+ material = new VideoMaterialImpl<qtvideosink_glsl_xrgbFragmentShader>;
+ material->initRgbTextureInfo(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, format.frameSize());
+ break;
+
+ // RGBx
+ case GST_VIDEO_FORMAT_RGB:
+ case GST_VIDEO_FORMAT_v308:
+ material = new VideoMaterialImpl<qtvideosink_glsl_rgbxFragmentShader>;
+ material->initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, format.frameSize());
+ break;
+ case GST_VIDEO_FORMAT_RGB16:
+ material = new VideoMaterialImpl<qtvideosink_glsl_rgbxFragmentShader>;
+ material->initRgbTextureInfo(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, format.frameSize());
+ break;
+
+ // YUV 420 planar
+ case GST_VIDEO_FORMAT_I420:
+ case GST_VIDEO_FORMAT_YV12:
+ material = new VideoMaterialImpl<qtvideosink_glsl_yuvPlanarFragmentShader>;
+ material->initYuv420PTextureInfo(
+ (format.videoFormat() == GST_VIDEO_FORMAT_YV12) /* uvSwapped */,
+ format.frameSize());
+ break;
+
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+
+ material->init(format.colorMatrix());
+ return material;
+}
+
+VideoMaterial::VideoMaterial() :
+ m_frame(0),
+ m_textureCount(0),
+ m_format(GST_VIDEO_FORMAT_UNKNOWN),
+ m_textureFormat(0),
+ m_textureInternalFormat(0),
+ m_textureType(0),
+ m_colorMatrixType(GST_VIDEO_COLOR_MATRIX_UNKNOWN)
+{
+ memset(m_textureIds, 0, sizeof(m_textureIds));
+ setFlag(Blending, false);
+}
+
+VideoMaterial::~VideoMaterial()
+{
+ if (!m_textureSize.isEmpty())
+ glDeleteTextures(m_textureCount, m_textureIds);
+}
+
+int VideoMaterial::compare(const QSGMaterial *other) const
+{
+ const VideoMaterial *m = static_cast<const VideoMaterial *>(other);
+ int d = m_textureIds[0] - m->m_textureIds[0];
+ if (d || m_textureCount == 1)
+ return d;
+ else if ((d = m_textureIds[1] - m->m_textureIds[1]) != 0)
+ return d;
+ else
+ return m_textureIds[2] - m->m_textureIds[2];
+}
+
+void VideoMaterial::initRgbTextureInfo(
+ GLenum internalFormat, GLuint format, GLenum type, const QSize &size)
+{
+#ifndef QT_OPENGL_ES
+ //make sure we get 8 bits per component, at least on the desktop GL where we can
+ switch(internalFormat) {
+ case GL_RGBA:
+ internalFormat = GL_RGBA8;
+ break;
+ case GL_RGB:
+ internalFormat = GL_RGB8;
+ break;
+ default:
+ break;
+ }
+#endif
+
+ 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 VideoMaterial::initYuv420PTextureInfo(bool uvSwapped, const QSize &size)
+{
+ int bytesPerLine = (size.width() + 3) & ~3;
+ int bytesPerLine2 = (size.width() / 2 + 3) & ~3;
+
+ 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;
+
+ if (uvSwapped)
+ qSwap (m_textureOffsets[1], m_textureOffsets[2]);
+}
+
+void VideoMaterial::init(GstVideoColorMatrix colorMatrixType)
+{
+ glGenTextures(m_textureCount, m_textureIds);
+ m_colorMatrixType = colorMatrixType;
+ updateColors(0, 0, 0, 0);
+}
+
+void VideoMaterial::setCurrentFrame(GstBuffer *buffer)
+{
+ QMutexLocker lock(&m_frameMutex);
+ gst_buffer_replace(&m_frame, buffer);
+}
+
+void VideoMaterial::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;
+
+ switch (m_colorMatrixType) {
+ case GST_VIDEO_COLOR_MATRIX_BT709:
+ m_colorMatrix *= 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;
+ case GST_VIDEO_COLOR_MATRIX_BT601:
+ m_colorMatrix *= 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);
+ break;
+ default:
+ break;
+ }
+}
+
+void VideoMaterial::bind()
+{
+ QOpenGLFunctions *functions = QOpenGLContext::currentContext()->functions();
+ GstBuffer *frame = NULL;
+
+ m_frameMutex.lock();
+ if (m_frame)
+ frame = gst_buffer_ref(m_frame);
+ m_frameMutex.unlock();
+
+ if (frame) {
+ const quint8 *data = GST_BUFFER_DATA (frame);
+ functions->glActiveTexture(GL_TEXTURE1);
+ bindTexture(1, data);
+ functions->glActiveTexture(GL_TEXTURE2);
+ bindTexture(2, data);
+ functions->glActiveTexture(GL_TEXTURE0); // Finish with 0 as default texture unit
+ bindTexture(0, data);
+ gst_buffer_unref(frame);
+ } else {
+ functions->glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[1]);
+ functions->glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[2]);
+ functions->glActiveTexture(GL_TEXTURE0); // Finish with 0 as default texture unit
+ glBindTexture(GL_TEXTURE_2D, m_textureIds[0]);
+ }
+}
+
+void VideoMaterial::bindTexture(int i, const quint8 *data)
+{
+ 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]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
diff --git a/elements/gstqtvideosink/painters/videomaterial.h b/elements/gstqtvideosink/painters/videomaterial.h
new file mode 100644
index 0000000..90a6379
--- /dev/null
+++ b/elements/gstqtvideosink/painters/videomaterial.h
@@ -0,0 +1,78 @@
+/*
+ Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+ Copyright (C) 2013 basysKom GmbH <info@basyskom.com>
+ Copyright (C) 2013 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 Lesser 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 VIDEOMATERIAL_H
+#define VIDEOMATERIAL_H
+
+#include "../utils/bufferformat.h"
+#include <QSize>
+#include <QMutex>
+#include <QMatrix4x4>
+
+#include <QtQuick/QSGMaterial>
+
+class VideoMaterialShader;
+
+class VideoMaterial : public QSGMaterial
+{
+public:
+ static VideoMaterial *create(const BufferFormat & format);
+
+ virtual ~VideoMaterial();
+
+ virtual int compare(const QSGMaterial *other) const;
+
+ void setCurrentFrame(GstBuffer *buffer);
+ void updateColors(int brightness, int contrast, int hue, int saturation);
+
+ void bind();
+
+protected:
+ VideoMaterial();
+ void initRgbTextureInfo(GLenum internalFormat, GLuint format,
+ GLenum type, const QSize &size);
+ void initYuv420PTextureInfo(bool uvSwapped, const QSize &size);
+ void init(GstVideoColorMatrix colorMatrixType);
+
+private:
+ void bindTexture(int i, const quint8 *data);
+
+
+ GstBuffer *m_frame;
+ QMutex m_frameMutex;
+
+ static const int Num_Texture_IDs = 3;
+ int m_textureCount;
+ GLuint m_textureIds[Num_Texture_IDs];
+ int m_textureWidths[Num_Texture_IDs];
+ int m_textureHeights[Num_Texture_IDs];
+ int m_textureOffsets[Num_Texture_IDs];
+ QSize m_textureSize;
+
+ GstVideoFormat m_format;
+ GLenum m_textureFormat;
+ GLuint m_textureInternalFormat;
+ GLenum m_textureType;
+
+ QMatrix4x4 m_colorMatrix;
+ GstVideoColorMatrix m_colorMatrixType;
+
+ friend class VideoMaterialShader;
+};
+
+#endif // VIDEOMATERIAL_H
diff --git a/elements/gstqtvideosink/painters/videonode.cpp b/elements/gstqtvideosink/painters/videonode.cpp
new file mode 100644
index 0000000..2afb3e5
--- /dev/null
+++ b/elements/gstqtvideosink/painters/videonode.cpp
@@ -0,0 +1,112 @@
+/*
+ Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+ Copyright (C) 2013 basysKom GmbH <info@basyskom.com>
+ Copyright (C) 2013 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 Lesser 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 "videonode.h"
+#include "videomaterial.h"
+
+#include <QtQuick/QSGFlatColorMaterial>
+
+VideoNode::VideoNode()
+ : QSGGeometryNode()
+{
+ setFlags(OwnsGeometry | OwnsMaterial, true);
+ setMaterialTypeSolidBlack();
+}
+
+void VideoNode::changeFormat(const BufferFormat & format)
+{
+ setMaterial(VideoMaterial::create(format));
+ setGeometry(0);
+ m_materialType = MaterialTypeVideo;
+}
+
+void VideoNode::setMaterialTypeSolidBlack()
+{
+ QSGFlatColorMaterial *m = new QSGFlatColorMaterial;
+ m->setColor(Qt::black);
+ setMaterial(m);
+ setGeometry(0);
+ m_materialType = MaterialTypeSolidBlack;
+}
+
+void VideoNode::setCurrentFrame(GstBuffer* buffer)
+{
+ Q_ASSERT (m_materialType == MaterialTypeVideo);
+ static_cast<VideoMaterial*>(material())->setCurrentFrame(buffer);
+ markDirty(DirtyMaterial);
+}
+
+void VideoNode::updateColors(int brightness, int contrast, int hue, int saturation)
+{
+ Q_ASSERT (m_materialType == MaterialTypeVideo);
+ static_cast<VideoMaterial*>(material())->updateColors(brightness, contrast, hue, saturation);
+ markDirty(DirtyMaterial);
+}
+
+/* Helpers */
+template <typename V>
+static inline void setGeom(V *v, const QPointF &p)
+{
+ v->x = p.x();
+ v->y = p.y();
+}
+
+static inline void setTex(QSGGeometry::TexturedPoint2D *v, const QPointF &p)
+{
+ v->tx = p.x();
+ v->ty = p.y();
+}
+
+void VideoNode::updateGeometry(const PaintAreas & areas)
+{
+ QSGGeometry *g = geometry();
+
+ if (m_materialType == MaterialTypeVideo) {
+ if (!g)
+ g = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
+
+ QSGGeometry::TexturedPoint2D *v = g->vertexDataAsTexturedPoint2D();
+
+ // Set geometry first
+ setGeom(v + 0, areas.videoArea.topLeft());
+ setGeom(v + 1, areas.videoArea.bottomLeft());
+ setGeom(v + 2, areas.videoArea.topRight());
+ setGeom(v + 3, areas.videoArea.bottomRight());
+
+ // and then texture coordinates
+ setTex(v + 0, areas.sourceRect.topLeft());
+ setTex(v + 1, areas.sourceRect.bottomLeft());
+ setTex(v + 2, areas.sourceRect.topRight());
+ setTex(v + 3, areas.sourceRect.bottomRight());
+ } else {
+ if (!g)
+ g = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 4);
+
+ QSGGeometry::Point2D *v = g->vertexDataAsPoint2D();
+
+ setGeom(v + 0, areas.videoArea.topLeft());
+ setGeom(v + 1, areas.videoArea.bottomLeft());
+ setGeom(v + 2, areas.videoArea.topRight());
+ setGeom(v + 3, areas.videoArea.bottomRight());
+ }
+
+ if (!geometry())
+ setGeometry(g);
+
+ markDirty(DirtyGeometry);
+}
diff --git a/elements/gstqtvideosink/painters/videonode.h b/elements/gstqtvideosink/painters/videonode.h
new file mode 100644
index 0000000..ed9d36f
--- /dev/null
+++ b/elements/gstqtvideosink/painters/videonode.h
@@ -0,0 +1,50 @@
+/*
+ Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+ Copyright (C) 2013 basysKom GmbH <info@basyskom.com>
+ Copyright (C) 2013 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 Lesser 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 VIDEONODE_H
+#define VIDEONODE_H
+
+#include "../utils/bufferformat.h"
+
+#include <QtQuick/QSGGeometryNode>
+
+class VideoNode : public QSGGeometryNode
+{
+public:
+ VideoNode();
+
+ enum MaterialType {
+ MaterialTypeVideo,
+ MaterialTypeSolidBlack
+ };
+
+ MaterialType materialType() const { return m_materialType; }
+
+ void changeFormat(const BufferFormat &format);
+ void setMaterialTypeSolidBlack();
+
+ void setCurrentFrame(GstBuffer *buffer);
+ void updateColors(int brightness, int contrast, int hue, int saturation);
+
+ void updateGeometry(const PaintAreas & areas);
+
+private:
+ MaterialType m_materialType;
+};
+
+#endif // VIDEONODE_H
diff --git a/elements/gstqtvideosink/utils/utils.h b/elements/gstqtvideosink/utils/utils.h
index 6e16c45..f1528e6 100644
--- a/elements/gstqtvideosink/utils/utils.h
+++ b/elements/gstqtvideosink/utils/utils.h
@@ -20,6 +20,14 @@
#include <QSize>
#include <QMetaType>
+// utilities for GST_DEBUG
+#define QSIZE_FORMAT "(%d x %d)"
+#define QSIZE_FORMAT_ARGS(size) \
+ size.width(), size.height()
+#define QRECTF_FORMAT "(x: %f, y: %f, w: %f, h: %f)"
+#define QRECTF_FORMAT_ARGS(rect) \
+ (float) rect.x(), (float) rect.y(), (float) rect.width(), (float) rect.height()
+
struct Fraction
{
inline Fraction() {}