Unverified Commit 1d5c87d7 authored by Gus Grubba's avatar Gus Grubba Committed by GitHub
Browse files

Merge pull request #8266 from andrewvoznytsa/PR-qmlglsink-v2

Switch from builtin video sink to qmlglsink (v2)
parents e36a8fbc 0f5be407
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
/**
* @file
* @brief QGC Video Surface
* @author Gus Grubba <gus@auterion.com>
*/
#if defined(QGC_GST_STREAMING)
#include "VideoSurface_p.h"
#endif
#include "VideoSurface.h"
#include <QtCore/QDebug>
#include <QtQuick/QQuickItem>
VideoSurface::VideoSurface(QObject *parent)
: QObject(parent)
#if defined(QGC_GST_STREAMING)
, _data(new VideoSurfacePrivate)
, _lastFrame(0)
, _refed(false)
#endif
{
}
VideoSurface::~VideoSurface()
{
#if defined(QGC_GST_STREAMING)
if (!_refed && _data->videoSink != nullptr) {
gst_element_set_state(_data->videoSink, GST_STATE_NULL);
}
delete _data;
#endif
}
#if defined(QGC_GST_STREAMING)
GstElement* VideoSurface::videoSink()
{
if (_data->videoSink == nullptr) {
if ((_data->videoSink = gst_element_factory_make("qtquick2videosink", nullptr)) == nullptr) {
qCritical("Failed to create qtquick2videosink. Make sure it is installed correctly");
return nullptr;
}
g_object_set(G_OBJECT(_data->videoSink), "sync", gboolean(false), nullptr);
g_signal_connect(_data->videoSink, "update", G_CALLBACK(onUpdateThunk), (void* )this);
_refed = true;
}
return _data->videoSink;
}
void VideoSurface::onUpdate()
{
_lastFrame = time(nullptr);
Q_FOREACH(QQuickItem *item, _data->items) {
item->update();
}
}
void VideoSurface::onUpdateThunk(GstElement* sink, gpointer data)
{
Q_UNUSED(sink);
VideoSurface* pThis = (VideoSurface* )data;
pThis->onUpdate();
}
#endif
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
/**
* @file
* @brief QGC Video Surface
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include <QtCore/QObject>
#if defined(QGC_GST_STREAMING)
#include <gst/gst.h>
#endif
#if defined(QGC_GST_STREAMING)
class VideoSurfacePrivate;
#endif
class VideoSurface : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(VideoSurface)
public:
explicit VideoSurface(QObject *parent = 0);
virtual ~VideoSurface();
/*! Returns the video sink element that provides this surface's image.
* The element will be constructed the first time that this function
* is called. The surface will always keep a reference to this element.
*/
#if defined(QGC_GST_STREAMING)
GstElement* videoSink();
time_t lastFrame() { return _lastFrame; }
void setLastFrame(time_t t) { _lastFrame = t; }
#endif
protected:
#if defined(QGC_GST_STREAMING)
void onUpdate();
static void onUpdateThunk(GstElement* sink, gpointer data);
#endif
private:
friend class VideoItem;
#if defined(QGC_GST_STREAMING)
VideoSurfacePrivate * const _data;
time_t _lastFrame;
bool _refed;
#endif
};
Q_DECLARE_METATYPE(VideoSurface*)
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
/**
* @file
* @brief QGC Video Surface (Private Interface)
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include "VideoSurface.h"
#include "VideoItem.h"
class VideoSurfacePrivate
{
public:
VideoSurfacePrivate()
: videoSink(nullptr)
{
}
QSet<VideoItem*> items;
GstElement* videoSink;
};
/*
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/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#include "basedelegate.h"
#include <QCoreApplication>
BaseDelegate::BaseDelegate(GstElement * sink, QObject * parent)
: QObject(parent)
, m_colorsDirty(true)
, m_brightness(0)
, m_contrast(0)
, m_hue(0)
, m_saturation(0)
, m_pixelAspectRatio(1, 1)
, m_forceAspectRatioDirty(true)
, m_forceAspectRatio(false)
, m_formatDirty(true)
, m_isActive(false)
, m_buffer(nullptr)
, m_sink(sink)
{
}
BaseDelegate::~BaseDelegate()
{
Q_ASSERT(!isActive());
}
//-------------------------------------
bool BaseDelegate::isActive() const
{
QReadLocker l(&m_isActiveLock);
return m_isActive;
}
void BaseDelegate::setActive(bool active)
{
GST_INFO_OBJECT(m_sink, active ? "Activating" : "Deactivating");
QWriteLocker l(&m_isActiveLock);
m_isActive = active;
if (!active) {
QCoreApplication::postEvent(this, new DeactivateEvent());
}
}
//-------------------------------------
int BaseDelegate::brightness() const
{
QReadLocker l(&m_colorsLock);
return m_brightness;
}
void BaseDelegate::setBrightness(int brightness)
{
QWriteLocker l(&m_colorsLock);
m_brightness = qBound(-100, brightness, 100);
m_colorsDirty = true;
}
int BaseDelegate::contrast() const
{
QReadLocker l(&m_colorsLock);
return m_contrast;
}
void BaseDelegate::setContrast(int contrast)
{
QWriteLocker l(&m_colorsLock);
m_contrast = qBound(-100, contrast, 100);
m_colorsDirty = true;
}
int BaseDelegate::hue() const
{
QReadLocker l(&m_colorsLock);
return m_hue;
}
void BaseDelegate::setHue(int hue)
{
QWriteLocker l(&m_colorsLock);
m_hue = qBound(-100, hue, 100);
m_colorsDirty = true;
}
int BaseDelegate::saturation() const
{
QReadLocker l(&m_colorsLock);
return m_saturation;
}
void BaseDelegate::setSaturation(int saturation)
{
QWriteLocker l(&m_colorsLock);
m_saturation = qBound(-100, saturation, 100);
m_colorsDirty = true;
}
//-------------------------------------
Fraction BaseDelegate::pixelAspectRatio() const
{
QReadLocker l(&m_pixelAspectRatioLock);
return m_pixelAspectRatio;
}
void BaseDelegate::setPixelAspectRatio(const Fraction & f)
{
QWriteLocker l(&m_pixelAspectRatioLock);
m_pixelAspectRatio = f;
}
//-------------------------------------
bool BaseDelegate::forceAspectRatio() const
{
QReadLocker l(&m_forceAspectRatioLock);
return m_forceAspectRatio;
}
void BaseDelegate::setForceAspectRatio(bool force)
{
QWriteLocker l(&m_forceAspectRatioLock);
if (m_forceAspectRatio != force) {
m_forceAspectRatio = force;
m_forceAspectRatioDirty = true;
}
}
//-------------------------------------
bool BaseDelegate::event(QEvent *event)
{
switch((int) event->type()) {
case BufferEventType:
{
BufferEvent *bufEvent = dynamic_cast<BufferEvent*>(event);
Q_ASSERT(bufEvent);
GST_TRACE_OBJECT(m_sink, "Received buffer %" GST_PTR_FORMAT, bufEvent->buffer);
if (isActive()) {
gst_buffer_replace (&m_buffer, bufEvent->buffer);
update();
}
return true;
}
case BufferFormatEventType:
{
BufferFormatEvent *bufFmtEvent = dynamic_cast<BufferFormatEvent*>(event);
Q_ASSERT(bufFmtEvent);
GST_TRACE_OBJECT (m_sink, "Received buffer format event. New format: %s",
gst_video_format_to_string(bufFmtEvent->format.videoFormat()));
m_formatDirty = true;
m_bufferFormat = bufFmtEvent->format;
return true;
}
case DeactivateEventType:
{
GST_LOG_OBJECT(m_sink, "Received deactivate event");
gst_buffer_replace (&m_buffer, nullptr);
update();
return true;
}
default:
return QObject::event(event);
}
}
void BaseDelegate::update()
{
g_signal_emit_by_name(m_sink, "update");
}
/*
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/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include <gst/gst.h>
#include "../gstqtvideosinkplugin.h" //for debug category
#include "../utils/bufferformat.h"
#include "../utils/utils.h"
#include <QObject>
#include <QEvent>
#include <QReadWriteLock>
class BaseDelegate : public QObject
{
Q_OBJECT
public:
enum EventType {
BufferEventType = QEvent::User,
BufferFormatEventType,
DeactivateEventType
};
//-------------------------------------
class BufferEvent : public QEvent
{
public:
inline BufferEvent(GstBuffer *buf)
: QEvent(static_cast<QEvent::Type>(BufferEventType)),
buffer(gst_buffer_ref(buf))
{}
virtual ~BufferEvent() {
gst_buffer_unref(buffer);
}
GstBuffer *buffer;
};
class BufferFormatEvent : public QEvent
{
public:
inline BufferFormatEvent(const BufferFormat &format)
: QEvent(static_cast<QEvent::Type>(BufferFormatEventType)),
format(format)
{}
BufferFormat format;
};
class DeactivateEvent : public QEvent
{
public:
inline DeactivateEvent()
: QEvent(static_cast<QEvent::Type>(DeactivateEventType))
{
}
};
//-------------------------------------
explicit BaseDelegate(GstElement *sink, QObject *parent = 0);
virtual ~BaseDelegate();
bool isActive() const;
void setActive(bool playing);
// GstColorBalance interface
int brightness() const;
void setBrightness(int brightness);
int contrast() const;
void setContrast(int contrast);
int hue() const;
void setHue(int hue);
int saturation() const;
void setSaturation(int saturation);
// pixel-aspect-ratio property
Fraction pixelAspectRatio() const;
void setPixelAspectRatio(const Fraction & f);
// force-aspect-ratio property
bool forceAspectRatio() const;
void setForceAspectRatio(bool force);
protected:
// internal event handling
virtual bool event(QEvent *event);
// tells the surface to repaint itself
virtual void update();
protected:
// colorbalance interface properties
mutable QReadWriteLock m_colorsLock;
bool m_colorsDirty;
int m_brightness;
int m_contrast;
int m_hue;
int m_saturation;
// pixel-aspect-ratio property
mutable QReadWriteLock m_pixelAspectRatioLock;
Fraction m_pixelAspectRatio;
// force-aspect-ratio property
mutable QReadWriteLock m_forceAspectRatioLock;
bool m_forceAspectRatioDirty;
bool m_forceAspectRatio;
// format caching
bool m_formatDirty;
BufferFormat m_bufferFormat;
PaintAreas m_areas;
// whether the sink is active (PAUSED or PLAYING)
mutable QReadWriteLock m_isActiveLock;
bool m_isActive;
// the buffer to be drawn next
GstBuffer *m_buffer;
// the video sink element
GstElement * const m_sink;
};
/*
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/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#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 {
//change format before geometry, so that we change QSGGeometry as well
if (m_formatDirty) {
vnode->changeFormat(m_bufferFormat);
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, m_bufferFormat.frameSize(),
m_bufferFormat.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(m_bufferFormat.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_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;
}
/*
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/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#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);
};
/*
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 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/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#include "qtvideosinkdelegate.h"
#include "../painters/genericsurfacepainter.h"
#include "../painters/openglsurfacepainter.h"
#include <QStack>
#include <QPainter>
#include "glutils.h"
QtVideoSinkDelegate::QtVideoSinkDelegate(GstElement *sink, QObject *parent)
: BaseDelegate(sink, parent)
, m_painter(0)
, m_supportedPainters(Generic)
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
, m_glContext(0)
#endif
{
}
QtVideoSinkDelegate::~QtVideoSinkDelegate()
{
destroyPainter();
}
void QtVideoSinkDelegate::paint(QPainter *painter, const QRectF & targetArea)
{
GST_TRACE_OBJECT(m_sink, "paint called");
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
if (m_glContext) {
Q_ASSERT_X(m_glContext == QGLContext::currentContext(),
"qtvideosink - paint",
"Please use a QPainter that is initialized to paint on the "
"GL surface that has the same context as the one given on the glcontext property"
);
}
#endif
if (!m_buffer) {
painter->fillRect(targetArea, Qt::black);
} else {
//recalculate the video area if needed
QReadLocker forceAspectRatioLocker(&m_forceAspectRatioLock);
if (targetArea != m_areas.targetArea || m_formatDirty
|| m_forceAspectRatioDirty)
{
m_forceAspectRatioDirty = false;
QReadLocker pixelAspectRatioLocker(&m_pixelAspectRatioLock);
Qt::AspectRatioMode aspectRatioMode = m_forceAspectRatio ?
Qt::KeepAspectRatio : Qt::IgnoreAspectRatio;
m_areas.calculate(targetArea, m_bufferFormat.frameSize(),
m_bufferFormat.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(m_bufferFormat.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)
);
}
forceAspectRatioLocker.unlock();
//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 ((m_formatDirty) || !m_painter)
{
changePainter(m_bufferFormat);
m_formatDirty = false;
//make sure to update the colors after changing painter
m_colorsDirty = true;
}
if (G_LIKELY(m_painter)) {
QReadLocker colorsLocker(&m_colorsLock);
if (m_colorsDirty) {
m_painter->updateColors(m_brightness, m_contrast, m_hue, m_saturation);
m_colorsDirty = false;
}
colorsLocker.unlock();
GstMapInfo mem_info;
if (gst_buffer_map(m_buffer, &mem_info, GST_MAP_READ)) {
m_painter->paint(mem_info.data, m_bufferFormat, painter, m_areas);
gst_buffer_unmap(m_buffer, &mem_info);
}
}
}
}
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
QGLContext *QtVideoSinkDelegate::glContext() const
{
return m_glContext;
}
void QtVideoSinkDelegate::setGLContext(QGLContext *context)
{
if (m_glContext == context)
return;
m_glContext = context;
m_supportedPainters = Generic;
if (m_glContext) {
m_glContext->makeCurrent();
QOpenGLFunctionsDef *funcs = getQOpenGLFunctions();
if (funcs) {
const QByteArray extensions(reinterpret_cast<const char *>(funcs->glGetString(GL_EXTENSIONS)));
GST_LOG_OBJECT(m_sink, "Available GL extensions: %s", extensions.constData());
#ifndef QT_OPENGL_ES
if (extensions.contains("ARB_fragment_program"))
m_supportedPainters |= ArbFp;
#endif
#ifndef QT_OPENGL_ES_2
if (QGLShaderProgram::hasOpenGLShaderPrograms(m_glContext)
&& extensions.contains("ARB_shader_objects"))
#endif
m_supportedPainters |= Glsl;
}
}
GST_LOG_OBJECT(m_sink, "Done setting GL context. m_supportedPainters=%x", (int) m_supportedPainters);
}
#endif
void QtVideoSinkDelegate::changePainter(const BufferFormat & format)
{
if (m_painter) {
m_painter->cleanup();
if (G_UNLIKELY(!m_painter->supportsFormat(format.videoFormat()))) {
destroyPainter();
}
}
QStack<PainterType> possiblePainters;
if (GenericSurfacePainter::supportedPixelFormats().contains(format.videoFormat())) {
possiblePainters.push(Generic);
}
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
if (OpenGLSurfacePainter::supportedPixelFormats().contains(format.videoFormat())) {
if (m_supportedPainters & ArbFp) {
possiblePainters.push(ArbFp);
}
if (m_supportedPainters & Glsl) {
possiblePainters.push(Glsl);
}
}
#endif
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;
break;
# ifndef QT_OPENGL_ES
case ArbFp:
GST_LOG_OBJECT(m_sink, "Creating ARB Fragment Shader painter");
m_painter = new ArbFpSurfacePainter;
break;
# endif
#endif
case Generic:
GST_LOG_OBJECT(m_sink, "Creating Generic painter");
m_painter = new GenericSurfacePainter;
break;
default:
Q_ASSERT(false);
}
}
try {
m_painter->init(format);
return;
} catch (const QString & error) {
GST_ELEMENT_WARNING(m_sink, RESOURCE, FAILED,
("Failed to start painter"), ("%s", error.toUtf8().constData()));
delete m_painter;
m_painter = 0;
}
}
GST_ELEMENT_ERROR(m_sink, RESOURCE, FAILED,
("Failed to create a painter for the given format"), (nullptr));
}
void QtVideoSinkDelegate::destroyPainter()
{
GST_LOG_OBJECT(m_sink, "Destroying painter");
delete m_painter;
m_painter = 0;
}
bool QtVideoSinkDelegate::event(QEvent *event)
{
if (event->type() == (QEvent::Type)DeactivateEventType) {
if (m_painter) {
m_painter->cleanup();
destroyPainter();
}
}
return BaseDelegate::event(event);
}
/*
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 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/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include "basedelegate.h"
#include "../painters/abstractsurfacepainter.h"
class QGLContext;
class QtVideoSinkDelegate : public BaseDelegate
{
Q_OBJECT
public:
enum PainterType {
Generic = 0x00,
ArbFp = 0x01,
Glsl = 0x02
};
Q_DECLARE_FLAGS(PainterTypes, PainterType);
explicit QtVideoSinkDelegate(GstElement *sink, QObject *parent = 0);
virtual ~QtVideoSinkDelegate();
PainterTypes supportedPainterTypes() const { return m_supportedPainters; }
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
// glcontext property
QGLContext *glContext() const;
void setGLContext(QGLContext *context);
#endif
// paint action
void paint(QPainter *painter, const QRectF & targetArea);
protected:
// internal event handling
virtual bool event(QEvent *event);
private:
void changePainter(const BufferFormat & format);
void destroyPainter();
AbstractSurfacePainter *m_painter;
PainterTypes m_supportedPainters;
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
QGLContext *m_glContext;
#endif
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QtVideoSinkDelegate::PainterTypes)
/*
Copyright (C) 2010 George Kiagiadakis <kiagiadakis.george@gmail.com>
Copyright (C) 2012 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 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/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#include "qwidgetvideosinkdelegate.h"
#include <QPainter>
QWidgetVideoSinkDelegate::QWidgetVideoSinkDelegate(GstElement * sink, QObject * parent)
: QtVideoSinkDelegate(sink, parent)
{
}
QWidgetVideoSinkDelegate::~QWidgetVideoSinkDelegate()
{
setWidget(nullptr);
}
QWidget *QWidgetVideoSinkDelegate::widget() const
{
return m_widget.data();
}
void QWidgetVideoSinkDelegate::setWidget(QWidget *widget)
{
GST_LOG_OBJECT(m_sink, "Setting \"widget\" property to %" GST_PTR_FORMAT, widget);
if (m_widget) {
m_widget.data()->removeEventFilter(this);
m_widget.data()->setAttribute(Qt::WA_OpaquePaintEvent, m_opaquePaintEventAttribute);
m_widget.data()->update();
m_widget = nullptr;
}
if (widget) {
widget->installEventFilter(this);
m_opaquePaintEventAttribute = widget->testAttribute(Qt::WA_OpaquePaintEvent);
widget->setAttribute(Qt::WA_OpaquePaintEvent, true);
widget->update();
m_widget = widget;
}
}
bool QWidgetVideoSinkDelegate::eventFilter(QObject *filteredObject, QEvent *event)
{
if (filteredObject == m_widget.data()) {
switch(event->type()) {
case QEvent::Paint:
{
QPainter painter(m_widget.data());
paint(&painter, m_widget.data()->rect());
return true;
}
default:
return false;
}
} else {
return QtVideoSinkDelegate::eventFilter(filteredObject, event);
}
}
void QWidgetVideoSinkDelegate::update()
{
if (m_widget) {
m_widget.data()->update();
}
}
/*
Copyright (C) 2010 George Kiagiadakis <kiagiadakis.george@gmail.com>
Copyright (C) 2012 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 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/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include "qtvideosinkdelegate.h"
#include <QEvent>
#include <QPointer>
#include <QWidget>
class QWidgetVideoSinkDelegate : public QtVideoSinkDelegate
{
Q_OBJECT
public:
explicit QWidgetVideoSinkDelegate(GstElement * sink, QObject * parent = 0);
virtual ~QWidgetVideoSinkDelegate();
// "widget" property
QWidget *widget() const;
void setWidget(QWidget *widget);
protected:
virtual bool eventFilter(QObject *filteredObject, QEvent *event);
virtual void update();
private:
// "widget" property
QPointer<QWidget> m_widget;
// original value of the Qt::WA_OpaquePaintEvent attribute
bool m_opaquePaintEventAttribute;
};
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2012 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/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
/*
* Note on the marshallers
*
* If it ever needs to be recreated, here is what you need to do:
*
* cat > marshaller.src
* VOID:POINTER,FLOAT,FLOAT,FLOAT,FLOAT
* VOID:POINTER,DOUBLE,DOUBLE,DOUBLE,DOUBLE
* POINTER:POINTER,FLOAT,FLOAT,FLOAT,FLOAT
* POINTER:POINTER,DOUBLE,DOUBLE,DOUBLE,DOUBLE
* Ctrl+D (EOF)
*
* glib-genmarshal --header marshaller.list > gstqtvideosinkmarshal.h
* glib-genmarshal --body marshaller.list > gstqtvideosinkmarshal.c
*
*/
#include "gstqtglvideosink.h"
#include "gstqtvideosinkmarshal.h"
#include "delegates/qtvideosinkdelegate.h"
guint GstQtGLVideoSink::s_signals[];
DEFINE_TYPE(GstQtGLVideoSink, GST_TYPE_QT_GL_VIDEO_SINK_BASE)
//------------------------------
void GstQtGLVideoSink::emit_update(gpointer sink)
{
g_signal_emit(sink, GstQtGLVideoSink::s_signals[UPDATE_SIGNAL], 0, NULL);
}
//------------------------------
void GstQtGLVideoSink::base_init(gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
gst_element_class_set_details_simple(element_class, "Qt GL video sink", "Sink/Video",
"A video sink that can draw on any Qt GL surface",
"George Kiagiadakis <george.kiagiadakis@collabora.com>");
}
void GstQtGLVideoSink::class_init(gpointer g_class, gpointer class_data)
{
Q_UNUSED(class_data);
GObjectClass *object_class = G_OBJECT_CLASS(g_class);
object_class->set_property = GstQtGLVideoSink::set_property;
GstQtGLVideoSinkClass *qt_video_sink_class = reinterpret_cast<GstQtGLVideoSinkClass*>(g_class);
qt_video_sink_class->paint = GstQtGLVideoSink::paint;
/**
* GstQtGLVideoSink::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 emitted.
*
* 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[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(GstQtGLVideoSinkClass, paint),
nullptr, nullptr,
qRealIsDouble() ?
g_cclosure_user_marshal_VOID__POINTER_DOUBLE_DOUBLE_DOUBLE_DOUBLE :
g_cclosure_user_marshal_VOID__POINTER_FLOAT_FLOAT_FLOAT_FLOAT,
G_TYPE_NONE, 5,
G_TYPE_POINTER, G_TYPE_QREAL, G_TYPE_QREAL, G_TYPE_QREAL, G_TYPE_QREAL);
/**
* GstQtGLVideoSink::update
*
* This signal is emitted 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, nullptr, nullptr,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* GstQtGLVideoSink::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 the element changes state to READY, or else the state change will fail.
**/
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_WRITABLE)));
}
void GstQtGLVideoSink::init(GTypeInstance *instance, gpointer g_class)
{
Q_UNUSED(g_class);
GstQtVideoSinkBase *sinkBase = GST_QT_VIDEO_SINK_BASE(instance);
sinkBase->delegate = new QtVideoSinkDelegate(GST_ELEMENT(sinkBase));
}
//------------------------------
void GstQtGLVideoSink::set_property(GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
GstQtVideoSinkBase *sinkBase = GST_QT_VIDEO_SINK_BASE(object);
switch (prop_id) {
case PROP_GLCONTEXT:
sinkBase->delegate->setGLContext(static_cast<QGLContext*>(g_value_get_pointer(value)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
//------------------------------
void GstQtGLVideoSink::paint(GstQtGLVideoSink *sink, gpointer painter,
qreal x, qreal y, qreal width, qreal height)
{
GST_QT_VIDEO_SINK_BASE(sink)->delegate->paint(static_cast<QPainter*>(painter),
QRectF(x, y, width, height));
}
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2012 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/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include "gstqtglvideosinkbase.h"
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
#define GST_TYPE_QT_GL_VIDEO_SINK \
(GstQtGLVideoSink::get_type())
struct GstQtGLVideoSink
{
public:
GstQtGLVideoSinkBase parent;
static GType get_type();
static void emit_update(gpointer sink);
private:
enum {
PROP_0,
PROP_GLCONTEXT
};
enum {
PAINT_SIGNAL,
UPDATE_SIGNAL,
LAST_SIGNAL
};
static void base_init(gpointer g_class);
static void class_init(gpointer g_class, gpointer class_data);
static void init(GTypeInstance *instance, gpointer g_class);
static void set_property(GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec);
static void paint(GstQtGLVideoSink *sink, gpointer painter,
qreal x, qreal y, qreal width, qreal height);
static guint s_signals[LAST_SIGNAL];
};
struct GstQtGLVideoSinkClass
{
GstQtGLVideoSinkBaseClass parent_class;
/* paint action signal */
void (*paint) (GstQtGLVideoSink *sink, gpointer painter,
qreal x, qreal y, qreal width, qreal height);
};
#endif // GST_QT_VIDEO_SINK_NO_OPENGL
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2012 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/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#include "gstqtglvideosinkbase.h"
#include "painters/openglsurfacepainter.h"
#include "delegates/qtvideosinkdelegate.h"
#include <QCoreApplication>
#define CAPS_FORMATS "{ BGRA, BGRx, ARGB, xRGB, RGB, RGB16, BGR, v308, AYUV, YV12, I420 }"
const char * const GstQtGLVideoSinkBase::s_colorbalance_labels[] = {
"contrast", "brightness", "hue", "saturation"
};
GstQtVideoSinkBaseClass *GstQtGLVideoSinkBase::s_parent_class = nullptr;
//------------------------------
DEFINE_TYPE_WITH_CODE(GstQtGLVideoSinkBase, GST_TYPE_QT_VIDEO_SINK_BASE, init_interfaces)
void GstQtGLVideoSinkBase::init_interfaces(GType type)
{
static const GInterfaceInfo colorbalance_info = {
(GInterfaceInitFunc) &GstQtGLVideoSinkBase::colorbalance_init, nullptr, nullptr
};
g_type_add_interface_static(type, GST_TYPE_COLOR_BALANCE, &colorbalance_info);
}
//------------------------------
void GstQtGLVideoSinkBase::base_init(gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
element_class->padtemplates = nullptr; //get rid of the pad template of the base class
static GstStaticPadTemplate sink_pad_template =
GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (CAPS_FORMATS))
);
gst_element_class_add_pad_template(
element_class, gst_static_pad_template_get(&sink_pad_template));
}
void GstQtGLVideoSinkBase::class_init(gpointer g_class, gpointer class_data)
{
Q_UNUSED(class_data);
s_parent_class = reinterpret_cast<GstQtVideoSinkBaseClass*>(g_type_class_peek_parent(g_class));
GObjectClass *object_class = G_OBJECT_CLASS(g_class);
object_class->finalize = GstQtGLVideoSinkBase::finalize;
object_class->set_property = GstQtGLVideoSinkBase::set_property;
object_class->get_property = GstQtGLVideoSinkBase::get_property;
GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS(g_class);
base_sink_class->start = GstQtGLVideoSinkBase::start;
base_sink_class->set_caps = GstQtGLVideoSinkBase::set_caps;
g_object_class_install_property(object_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(object_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(object_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(object_class, PROP_SATURATION,
g_param_spec_int("saturation", "Saturation", "The saturation of the video",
-100, 100, 0, static_cast<GParamFlags>(G_PARAM_READWRITE)));
}
void GstQtGLVideoSinkBase::init(GTypeInstance *instance, gpointer g_class)
{
Q_UNUSED(g_class);
GstQtGLVideoSinkBase *self = GST_QT_GL_VIDEO_SINK_BASE(instance);
GstColorBalanceChannel *channel;
self->m_channels_list = nullptr;
for (int i=0; i < LABEL_LAST; i++) {
channel = GST_COLOR_BALANCE_CHANNEL(g_object_new(GST_TYPE_COLOR_BALANCE_CHANNEL, nullptr));
channel->label = g_strdup(s_colorbalance_labels[i]);
channel->min_value = -100;
channel->max_value = 100;
self->m_channels_list = g_list_append(self->m_channels_list, channel);
}
}
void GstQtGLVideoSinkBase::finalize(GObject *object)
{
GstQtGLVideoSinkBase *self = GST_QT_GL_VIDEO_SINK_BASE(object);
while (self->m_channels_list) {
GstColorBalanceChannel *channel = GST_COLOR_BALANCE_CHANNEL(self->m_channels_list->data);
g_object_unref(channel);
self->m_channels_list = g_list_next(self->m_channels_list);
}
g_list_free(self->m_channels_list);
G_OBJECT_CLASS(s_parent_class)->finalize(object);
}
//------------------------------
void GstQtGLVideoSinkBase::colorbalance_init(GstColorBalanceInterface *balance_interface, gpointer data)
{
Q_UNUSED(data);
balance_interface->list_channels = GstQtGLVideoSinkBase::colorbalance_list_channels;
balance_interface->set_value = GstQtGLVideoSinkBase::colorbalance_set_value;
balance_interface->get_value = GstQtGLVideoSinkBase::colorbalance_get_value;
balance_interface->get_balance_type = GstQtGLVideoSinkBase::colorbalance_get_balance_type;
}
const GList *GstQtGLVideoSinkBase::colorbalance_list_channels(GstColorBalance *balance)
{
return GST_QT_GL_VIDEO_SINK_BASE(balance)->m_channels_list;
}
void GstQtGLVideoSinkBase::colorbalance_set_value(GstColorBalance *balance,
GstColorBalanceChannel *channel, gint value)
{
GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(balance);
if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_CONTRAST])) {
sink->delegate->setContrast(value);
} else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_BRIGHTNESS])) {
sink->delegate->setBrightness(value);
} else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_HUE])) {
sink->delegate->setHue(value);
} else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_SATURATION])) {
sink->delegate->setSaturation(value);
} else {
GST_WARNING_OBJECT(sink, "Unknown colorbalance channel %s", channel->label);
}
}
gint GstQtGLVideoSinkBase::colorbalance_get_value(GstColorBalance *balance,
GstColorBalanceChannel *channel)
{
GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(balance);
if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_CONTRAST])) {
return sink->delegate->contrast();
} else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_BRIGHTNESS])) {
return sink->delegate->brightness();
} else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_HUE])) {
return sink->delegate->hue();
} else if (!qstrcmp(channel->label, s_colorbalance_labels[LABEL_SATURATION])) {
return sink->delegate->saturation();
} else {
GST_WARNING_OBJECT(sink, "Unknown colorbalance channel %s", channel->label);
}
return 0;
}
GstColorBalanceType GstQtGLVideoSinkBase::colorbalance_get_balance_type(GstColorBalance *balance)
{
Q_UNUSED(balance);
return GST_COLOR_BALANCE_HARDWARE;
}
//------------------------------
void GstQtGLVideoSinkBase::set_property(GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(object);
switch (prop_id) {
case PROP_CONTRAST:
sink->delegate->setContrast(g_value_get_int(value));
break;
case PROP_BRIGHTNESS:
sink->delegate->setBrightness(g_value_get_int(value));
break;
case PROP_HUE:
sink->delegate->setHue(g_value_get_int(value));
break;
case PROP_SATURATION:
sink->delegate->setSaturation(g_value_get_int(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
void GstQtGLVideoSinkBase::get_property(GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(object);
switch (prop_id) {
case PROP_CONTRAST:
g_value_set_int(value, sink->delegate->contrast());
break;
case PROP_BRIGHTNESS:
g_value_set_int(value, sink->delegate->brightness());
break;
case PROP_HUE:
g_value_set_int(value, sink->delegate->hue());
break;
case PROP_SATURATION:
g_value_set_int(value, sink->delegate->saturation());
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
//------------------------------
gboolean GstQtGLVideoSinkBase::start(GstBaseSink *base)
{
GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(base);
//fail on purpose if the user hasn't set a context
if (sink->delegate->supportedPainterTypes() == QtVideoSinkDelegate::Generic) {
GST_WARNING_OBJECT(sink, "Neither GLSL nor ARB Fragment Program are supported "
"for painting. Did you forget to set a gl context?");
return FALSE;
} else {
return TRUE;
}
}
gboolean GstQtGLVideoSinkBase::set_caps(GstBaseSink *base, GstCaps *caps)
{
GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(base);
GST_LOG_OBJECT(sink, "new caps %" GST_PTR_FORMAT, caps);
BufferFormat format = BufferFormat::fromCaps(caps);
if (OpenGLSurfacePainter::supportedPixelFormats().contains(format.videoFormat())) {
QCoreApplication::postEvent(sink->delegate,
new BaseDelegate::BufferFormatEvent(format));
return TRUE;
} else {
return FALSE;
}
}
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2012 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/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include "gstqtvideosinkbase.h"
#include <gst/video/colorbalance.h>
#ifndef GST_QT_VIDEO_SINK_NO_OPENGL
#define GST_TYPE_QT_GL_VIDEO_SINK_BASE \
(GstQtGLVideoSinkBase::get_type())
#define GST_QT_GL_VIDEO_SINK_BASE(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_QT_GL_VIDEO_SINK_BASE, GstQtGLVideoSinkBase))
struct GstQtGLVideoSinkBase
{
public:
GstQtVideoSinkBase parent;
static GType get_type();
private:
enum {
PROP_0,
PROP_CONTRAST,
PROP_BRIGHTNESS,
PROP_HUE,
PROP_SATURATION
};
//index for s_colorbalance_labels
enum {
LABEL_CONTRAST = 0,
LABEL_BRIGHTNESS,
LABEL_HUE,
LABEL_SATURATION,
LABEL_LAST
};
static void init_interfaces(GType type);
static void base_init(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 colorbalance_init(GstColorBalanceInterface *balance_interface, gpointer data);
static const GList *colorbalance_list_channels(GstColorBalance *balance);
static void colorbalance_set_value(GstColorBalance *balance,
GstColorBalanceChannel *channel,
gint value);
static gint colorbalance_get_value(GstColorBalance *balance,
GstColorBalanceChannel *channel);
static GstColorBalanceType colorbalance_get_balance_type(GstColorBalance *balance);
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 gboolean start(GstBaseSink *sink);
static gboolean set_caps(GstBaseSink *sink, GstCaps *caps);
GList *m_channels_list;
static const char * const s_colorbalance_labels[];
static GstQtVideoSinkBaseClass *s_parent_class;
};
struct GstQtGLVideoSinkBaseClass
{
GstQtVideoSinkBaseClass parent_class;
};
#endif // GST_QT_VIDEO_SINK_NO_OPENGL
/*
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/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#include "gstqtquick2videosink.h"
#include "gstqtvideosinkplugin.h"
#include "gstqtvideosinkmarshal.h"
#include "delegates/qtquick2videosinkdelegate.h"
#include <gst/video/colorbalance.h>
#include <cstring>
#include <QCoreApplication>
#define CAPS_FORMATS "{ BGRA, BGRx, ARGB, xRGB, RGB, RGB16, BGR, v308, AYUV, YV12, I420 }"
#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;
};
static void gst_qt_quick2_video_sink_colorbalance_init (GstColorBalanceInterface * iface, gpointer data);
#define parent_class gst_qt_quick2_video_sink_parent_class
G_DEFINE_TYPE_WITH_CODE (GstQtQuick2VideoSink, gst_qt_quick2_video_sink,
GST_TYPE_VIDEO_SINK,
G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
gst_qt_quick2_video_sink_colorbalance_init));
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)
{
self->priv = GST_QT_QUICK2_VIDEO_SINK_GET_PRIVATE (self);
// delegate
self->priv->delegate = new QtQuick2VideoSinkDelegate(GST_ELEMENT(self));
// colorbalance
GstColorBalanceChannel *channel;
self->priv->channels_list = nullptr;
for (int i=0; i < LABEL_LAST; i++) {
channel = GST_COLOR_BALANCE_CHANNEL(g_object_new(GST_TYPE_COLOR_BALANCE_CHANNEL, nullptr));
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 = nullptr;
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);
BufferFormat format = BufferFormat::fromCaps(caps);
//too lazy to do proper checks. if the format is not UNKNOWN, then
//it should conform to the template caps formats, unless gstreamer
//core has a bug.
if (format.videoFormat() != GST_VIDEO_FORMAT_UNKNOWN) {
QCoreApplication::postEvent(self->priv->delegate,
new BaseDelegate::BufferFormatEvent(format));
return TRUE;
} else {
return FALSE;
}
}
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.", buffer);
QCoreApplication::postEvent(self->priv->delegate, new BaseDelegate::BufferEvent(buffer));
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 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 GstColorBalanceType
gst_qt_quick2_video_sink_colorbalance_get_balance_type (GstColorBalance * balance)
{
Q_UNUSED(balance);
return GST_COLOR_BALANCE_HARDWARE;
}
static void
gst_qt_quick2_video_sink_colorbalance_init(GstColorBalanceInterface *iface, gpointer data)
{
Q_UNUSED(data);
iface->list_channels = gst_qt_quick2_video_sink_colorbalance_list_channels;
iface->set_value = gst_qt_quick2_video_sink_colorbalance_set_value;
iface->get_value = gst_qt_quick2_video_sink_colorbalance_get_value;
iface->get_balance_type = gst_qt_quick2_video_sink_colorbalance_get_balance_type;
}
//------------------------------
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
* emitted.
*
* 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),
nullptr, nullptr,
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 emitted 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, nullptr, nullptr,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
g_type_class_add_private (klass, sizeof (GstQtQuick2VideoSinkPrivate));
static GstStaticPadTemplate sink_pad_template =
GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (CAPS_FORMATS))
);
gst_element_class_add_pad_template(
element_class, gst_static_pad_template_get(&sink_pad_template));
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>");
}
/*
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/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#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);
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2012 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/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#include "gstqtvideosink.h"
#include "gstqtvideosinkmarshal.h"
#include "delegates/qtvideosinkdelegate.h"
guint GstQtVideoSink::s_signals[];
DEFINE_TYPE(GstQtVideoSink, GST_TYPE_QT_VIDEO_SINK_BASE)
//------------------------------
void GstQtVideoSink::emit_update(gpointer sink)
{
g_signal_emit(sink, GstQtVideoSink::s_signals[UPDATE_SIGNAL], 0, NULL);
}
//------------------------------
void GstQtVideoSink::base_init(gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
gst_element_class_set_details_simple(element_class, "Qt video sink", "Sink/Video",
"A video sink that can draw on any Qt surface",
"George Kiagiadakis <george.kiagiadakis@collabora.com>");
}
void GstQtVideoSink::class_init(gpointer g_class, gpointer class_data)
{
Q_UNUSED(class_data);
GstQtVideoSinkClass *qt_video_sink_class = reinterpret_cast<GstQtVideoSinkClass*>(g_class);
qt_video_sink_class->paint = GstQtVideoSink::paint;
/**
* 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 emitted.
*
* 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[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),
nullptr, nullptr,
qRealIsDouble() ?
g_cclosure_user_marshal_VOID__POINTER_DOUBLE_DOUBLE_DOUBLE_DOUBLE :
g_cclosure_user_marshal_VOID__POINTER_FLOAT_FLOAT_FLOAT_FLOAT,
G_TYPE_NONE, 5,
G_TYPE_POINTER, G_TYPE_QREAL, G_TYPE_QREAL, G_TYPE_QREAL, G_TYPE_QREAL);
/**
* GstQtVideoSink::update
*
* This signal is emitted 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, nullptr, nullptr,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
void GstQtVideoSink::init(GTypeInstance *instance, gpointer g_class)
{
Q_UNUSED(g_class);
GstQtVideoSinkBase *sinkBase = GST_QT_VIDEO_SINK_BASE(instance);
sinkBase->delegate = new QtVideoSinkDelegate(GST_ELEMENT(sinkBase));
}
//------------------------------
void GstQtVideoSink::paint(GstQtVideoSink *sink, gpointer painter,
qreal x, qreal y, qreal width, qreal height)
{
GST_QT_VIDEO_SINK_BASE(sink)->delegate->paint(static_cast<QPainter*>(painter),
QRectF(x, y, width, height));
}
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2012 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/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#pragma once
#include "gstqtvideosinkbase.h"
#define GST_TYPE_QT_VIDEO_SINK \
(GstQtVideoSink::get_type())
struct GstQtVideoSink
{
public:
GstQtVideoSinkBase parent;
static GType get_type();
static void emit_update(gpointer sink);
private:
enum {
PAINT_SIGNAL,
UPDATE_SIGNAL,
LAST_SIGNAL
};
static void base_init(gpointer g_class);
static void class_init(gpointer g_class, gpointer class_data);
static void init(GTypeInstance *instance, gpointer g_class);
static void paint(GstQtVideoSink *sink, gpointer painter,
qreal x, qreal y, qreal width, qreal height);
static guint s_signals[LAST_SIGNAL];
};
struct GstQtVideoSinkClass
{
GstQtVideoSinkBaseClass parent_class;
/* paint action signal */
void (*paint) (GstQtVideoSink *sink, gpointer painter,
qreal x, qreal y, qreal width, qreal height);
};
/*
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). <qt-info@nokia.com>
Copyright (C) 2011-2012 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/>.
*/
/**
* @file
* @brief Extracted from QtGstreamer to avoid overly complex dependency
* @author Gus Grubba <gus@auterion.com>
*/
#include "gstqtvideosinkbase.h"
#include "delegates/qtvideosinkdelegate.h"
#include "painters/genericsurfacepainter.h"
#include <cstring>
#include <QCoreApplication>
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
# define CAPS_FORMATS "{ ARGB, xRGB, RGB, RGB16 }"
#else
# define CAPS_FORMATS "{ BGRA, BGRx, RGB, RGB16 }"
#endif
GstVideoSinkClass *GstQtVideoSinkBase::s_parent_class = nullptr;
DEFINE_TYPE(GstQtVideoSinkBase, GST_TYPE_VIDEO_SINK)
//------------------------------
void GstQtVideoSinkBase::base_init(gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
static GstStaticPadTemplate sink_pad_template =
GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (CAPS_FORMATS))
);
gst_element_class_add_pad_template(
element_class, gst_static_pad_template_get(&sink_pad_template));
}
void GstQtVideoSinkBase::class_init(gpointer g_class, gpointer class_data)
{
Q_UNUSED(class_data);
s_parent_class = reinterpret_cast<GstVideoSinkClass*>(g_type_class_peek_parent(g_class));
GObjectClass *object_class = G_OBJECT_CLASS(g_class);
object_class->finalize = GstQtVideoSinkBase::finalize;
object_class->set_property = GstQtVideoSinkBase::set_property;
object_class->get_property = GstQtVideoSinkBase::get_property;
GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
element_class->change_state = GstQtVideoSinkBase::change_state;
GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS(g_class);
base_sink_class->set_caps = GstQtVideoSinkBase::set_caps;
GstVideoSinkClass *video_sink_class = GST_VIDEO_SINK_CLASS(g_class);
video_sink_class->show_frame = GstQtVideoSinkBase::show_frame;
/**
* GstQtVideoSinkBase::pixel-aspect-ratio
*
* The pixel aspect ratio of the display device.
**/
g_object_class_install_property(object_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)));
/**
* GstQtVideoSinkBase::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)));
}
void GstQtVideoSinkBase::init(GTypeInstance *instance, gpointer g_class)
{
Q_UNUSED(instance);
Q_UNUSED(g_class);
/* sink->delegate is initialized in the subclasses */
}
void GstQtVideoSinkBase::finalize(GObject *object)
{
GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(object);
delete sink->delegate;
sink->delegate = 0;
}
//------------------------------
void GstQtVideoSinkBase::set_property(GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(object);
switch (prop_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);
sink->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:
sink->delegate->setForceAspectRatio(g_value_get_boolean(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
void GstQtVideoSinkBase::get_property(GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(object);
switch (prop_id) {
case PROP_PIXEL_ASPECT_RATIO:
{
GValue tmp;
Fraction par = sink->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, sink->delegate->forceAspectRatio());
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
//------------------------------
GstStateChangeReturn GstQtVideoSinkBase::change_state(GstElement *element, GstStateChange transition)
{
GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(element);
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
sink->delegate->setActive(true);
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
sink->delegate->setActive(false);
break;
default:
break;
}
return GST_ELEMENT_CLASS(s_parent_class)->change_state(element, transition);
}
//------------------------------
gboolean GstQtVideoSinkBase::set_caps(GstBaseSink *base, GstCaps *caps)
{
GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(base);
GST_LOG_OBJECT(sink, "new caps %" GST_PTR_FORMAT, caps);
BufferFormat format = BufferFormat::fromCaps(caps);
if (GenericSurfacePainter::supportedPixelFormats().contains(format.videoFormat())) {
QCoreApplication::postEvent(sink->delegate,
new BaseDelegate::BufferFormatEvent(format));
return TRUE;
} else {
return FALSE;
}
}
//------------------------------
GstFlowReturn GstQtVideoSinkBase::show_frame(GstVideoSink *video_sink, GstBuffer *buffer)
{
GstQtVideoSinkBase *sink = GST_QT_VIDEO_SINK_BASE(video_sink);
GST_TRACE_OBJECT(sink, "Posting new buffer (%" GST_PTR_FORMAT") for rendering.", buffer);
QCoreApplication::postEvent(sink->delegate, new BaseDelegate::BufferEvent(buffer));
return GST_FLOW_OK;
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment