From abee3a6548e84a1df91590c26718d43bb846dc0b Mon Sep 17 00:00:00 2001 From: Dmytro Poplavskiy Date: Mon, 8 Aug 2011 15:57:32 +1000 Subject: Initial implementation of QML2 VideoOutput element Change-Id: I5ed00433fe5e993086ae1698b7344c8d60a5f0f6 Reviewed-on: http://codereview.qt.nokia.com/2727 Reviewed-by: Qt Sanity Bot Reviewed-by: Gunnar Sletta Reviewed-by: Jonas Rabbe Reviewed-by: Michael Goddard --- src/imports/multimedia/multimedia.cpp | 5 +- src/imports/multimedia/multimedia.pro | 9 + src/imports/multimedia/qdeclarativeaudio.cpp | 1 + src/imports/multimedia/qdeclarativeaudio_p.h | 5 + src/imports/multimedia/qdeclarativevideooutput.cpp | 368 +++++++++++++++++++++ src/imports/multimedia/qdeclarativevideooutput_p.h | 115 +++++++ src/imports/multimedia/qsgvideonode.cpp | 67 ++++ src/imports/multimedia/qsgvideonode_i420.cpp | 295 +++++++++++++++++ src/imports/multimedia/qsgvideonode_i420.h | 79 +++++ src/imports/multimedia/qsgvideonode_p.h | 72 ++++ src/imports/multimedia/qsgvideonode_rgb32.cpp | 142 ++++++++ src/imports/multimedia/qsgvideonode_rgb32.h | 71 ++++ 12 files changed, 1227 insertions(+), 2 deletions(-) create mode 100644 src/imports/multimedia/qdeclarativevideooutput.cpp create mode 100644 src/imports/multimedia/qdeclarativevideooutput_p.h create mode 100644 src/imports/multimedia/qsgvideonode.cpp create mode 100644 src/imports/multimedia/qsgvideonode_i420.cpp create mode 100644 src/imports/multimedia/qsgvideonode_i420.h create mode 100644 src/imports/multimedia/qsgvideonode_p.h create mode 100644 src/imports/multimedia/qsgvideonode_rgb32.cpp create mode 100644 src/imports/multimedia/qsgvideonode_rgb32.h (limited to 'src') diff --git a/src/imports/multimedia/multimedia.cpp b/src/imports/multimedia/multimedia.cpp index b274830fe..18684d578 100644 --- a/src/imports/multimedia/multimedia.cpp +++ b/src/imports/multimedia/multimedia.cpp @@ -47,8 +47,8 @@ #include "qdeclarativemediametadata_p.h" #include "qdeclarativeaudio_p.h" +#include "qdeclarativevideooutput_p.h" #if 0 -#include "qdeclarativevideo_p.h" #include "qdeclarativecamera_p.h" #include "qdeclarativecamerapreviewprovider_p.h" #endif @@ -67,9 +67,10 @@ public: qmlRegisterType(uri, 4, 0, "SoundEffect"); qmlRegisterType(uri, 4, 0, "Audio"); + qmlRegisterType(uri, 4, 0, "MediaPlayer"); + qmlRegisterType(uri, 4, 0, "VideoOutput"); /* Disabled until ported to scenegraph */ #if 0 - qmlRegisterType(uri, 4, 0, "Video"); qmlRegisterType(uri, 4, 0, "Camera"); #endif qmlRegisterType(); diff --git a/src/imports/multimedia/multimedia.pro b/src/imports/multimedia/multimedia.pro index 845fe9b96..1a2e757a9 100644 --- a/src/imports/multimedia/multimedia.pro +++ b/src/imports/multimedia/multimedia.pro @@ -12,11 +12,20 @@ HEADERS += \ qdeclarativeaudio_p.h \ qdeclarativemediabase_p.h \ qdeclarativemediametadata_p.h \ + qdeclarativevideooutput_p.h \ + qsgvideonode_p.h \ + qsgvideonode_i420.h \ + qsgvideonode_rgb32.h \ + SOURCES += \ multimedia.cpp \ qdeclarativeaudio.cpp \ qdeclarativemediabase.cpp \ + qdeclarativevideooutput.cpp \ + qsgvideonode.cpp \ + qsgvideonode_i420.cpp \ + qsgvideonode_rgb32.cpp \ disabled { HEADERS += \ diff --git a/src/imports/multimedia/qdeclarativeaudio.cpp b/src/imports/multimedia/qdeclarativeaudio.cpp index c9801d857..c15d31235 100644 --- a/src/imports/multimedia/qdeclarativeaudio.cpp +++ b/src/imports/multimedia/qdeclarativeaudio.cpp @@ -305,6 +305,7 @@ QDeclarativeAudio::Error QDeclarativeAudio::error() const void QDeclarativeAudio::classBegin() { setObject(this); + emit mediaObjectChanged(); } void QDeclarativeAudio::componentComplete() diff --git a/src/imports/multimedia/qdeclarativeaudio_p.h b/src/imports/multimedia/qdeclarativeaudio_p.h index dd24a0744..7c50bf936 100644 --- a/src/imports/multimedia/qdeclarativeaudio_p.h +++ b/src/imports/multimedia/qdeclarativeaudio_p.h @@ -84,6 +84,7 @@ class QDeclarativeAudio : public QObject, public QDeclarativeMediaBase, public Q Q_PROPERTY(Error error READ error NOTIFY errorChanged) Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged) Q_PROPERTY(QDeclarativeMediaMetaData *metaData READ metaData CONSTANT) + Q_PROPERTY(QObject *mediaObject READ mediaObject NOTIFY mediaObjectChanged SCRIPTABLE false DESIGNABLE false) Q_ENUMS(Status) Q_ENUMS(Error) Q_ENUMS(Loop) @@ -126,6 +127,8 @@ public: void classBegin(); void componentComplete(); + QObject *mediaObject() { return m_mediaObject; } + public Q_SLOTS: void play(); void pause(); @@ -159,6 +162,8 @@ Q_SIGNALS: void errorChanged(); void error(QDeclarativeAudio::Error error, const QString &errorString); + void mediaObjectChanged(); + private Q_SLOTS: void _q_error(int, const QString &); diff --git a/src/imports/multimedia/qdeclarativevideooutput.cpp b/src/imports/multimedia/qdeclarativevideooutput.cpp new file mode 100644 index 000000000..e715c55fa --- /dev/null +++ b/src/imports/multimedia/qdeclarativevideooutput.cpp @@ -0,0 +1,368 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qdeclarativevideooutput_p.h" + +#include "qsgvideonode_p.h" +#include "qsgvideonode_i420.h" +#include "qsgvideonode_rgb32.h" + +#include + +#include +#include +#include +#include + + +#include + +//#define DEBUG_VIDEOITEM + +class QSGVideoItemSurface : public QAbstractVideoSurface +{ +public: + QSGVideoItemSurface(QDeclarativeVideoOutput *item, QObject *parent = 0) : + QAbstractVideoSurface(parent), + m_item(item) + { + } + + ~QSGVideoItemSurface() + { + } + + QList supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const + { + QList formats; + + foreach (QSGVideoNodeFactory* factory, m_item->m_videoNodeFactories) { + formats.append(factory->supportedPixelFormats(handleType)); + } + + return formats; + } + + bool start(const QVideoSurfaceFormat &format) + { +#ifdef DEBUG_VIDEOITEM + qDebug() << Q_FUNC_INFO << format; +#endif + + if (!supportedPixelFormats(format.handleType()).contains(format.pixelFormat())) + return false; + + return QAbstractVideoSurface::start(format); + } + + virtual bool present(const QVideoFrame &frame) + { + if (!frame.isValid()) { + qWarning() << Q_FUNC_INFO << "I'm getting bad frames here..."; + return false; + } + m_item->present(frame); + return true; + } + +private: + QDeclarativeVideoOutput *m_item; +}; + +/*! + \qmlclass VideoOutput QDeclarativeVideoOutput + \brief The VideoOutput element allows you to render video or camera viewfinder. + + \ingroup qml-multimedia + + This element is part of the \bold{QtMultimediaKit 4.0} module. + + \qml + import QtQuick 2.0 + import Qt.multimediakit 4.0 + + Rectangle { + width: 800 + height: 600 + color: "black" + + MediaPlayer { + id: player + source: "file://video.webm" + playing: true + } + + VideoOutput { + id: videoOutput + source: player + anchors.fill: parent + } + } + + \endqml + + The VideoOutput item supports untransformed, stretched, and uniformly scaled video presentation. + For a description of stretched uniformly scaled presentation, see the \l fillMode property + description. + + \sa MediaPlayer, Camera +*/ + +/*! + \internal + \class QDeclarativeVideoOutput + \brief The QDeclarativeVideoOutput class provides a video output item. +*/ + +QDeclarativeVideoOutput::QDeclarativeVideoOutput(QSGItem *parent) : + QSGItem(parent), + m_fillMode(PreserveAspectFit) +{ + setFlag(ItemHasContents, true); + m_surface = new QSGVideoItemSurface(this); + connect(m_surface, SIGNAL(surfaceFormatChanged(QVideoSurfaceFormat)), + this, SLOT(_q_updateNativeSize(QVideoSurfaceFormat)), Qt::QueuedConnection); + + m_videoNodeFactories.append(new QSGVideoNodeFactory_I420); +#ifndef QT_OPENGL_ES + m_videoNodeFactories.append(new QSGVideoNodeFactory_RGB32); +#endif +} + +QDeclarativeVideoOutput::~QDeclarativeVideoOutput() +{ + m_source.clear(); + _q_updateMediaObject(); + delete m_surface; + qDeleteAll(m_videoNodeFactories); +} + +/*! + \qmlproperty variant VideoOutput::source + + This property holds the source item providing the video frames like MediaPlayer or Camera. +*/ + +void QDeclarativeVideoOutput::setSource(QObject *source) +{ +#ifdef DEBUG_VIDEOITEM + qDebug() << Q_FUNC_INFO << source; +#endif + + if (source == m_source.data()) + return; + + if (m_source) + disconnect(0, m_source.data(), SLOT(_q_updateMediaObject())); + + m_source = source; + + if (m_source) { + const QMetaObject *metaObject = m_source.data()->metaObject(); + const QMetaProperty mediaObjectProperty = metaObject->property( + metaObject->indexOfProperty("mediaObject")); + + if (mediaObjectProperty.hasNotifySignal()) { + QMetaMethod method = mediaObjectProperty.notifySignal(); + QMetaObject::connect(m_source.data(), method.methodIndex(), + this, this->metaObject()->indexOfSlot("updateMediaObject()"), + Qt::DirectConnection, 0); + + } + } + + _q_updateMediaObject(); + emit sourceChanged(); +} + +void QDeclarativeVideoOutput::_q_updateMediaObject() +{ + QMediaObject *mediaObject = 0; + + if (m_source) + mediaObject = qobject_cast(m_source.data()->property("mediaObject").value()); + +#ifdef DEBUG_VIDEOITEM + qDebug() << Q_FUNC_INFO << mediaObject; +#endif + + if (m_mediaObject.data() == mediaObject) + return; + + if (m_rendererControl) { + m_rendererControl.data()->setSurface(0); + m_service.data()->releaseControl(m_rendererControl.data()); + } + + m_mediaObject = mediaObject; + m_mediaObject.clear(); + m_service.clear(); + m_rendererControl.clear(); + + if (mediaObject) { + if (QMediaService *service = mediaObject->service()) { + if (QMediaControl *control = service->requestControl(QVideoRendererControl_iid)) { + if ((m_rendererControl = qobject_cast(control))) { + m_service = service; + m_mediaObject = mediaObject; + m_rendererControl.data()->setSurface(m_surface); + } else { + qWarning() << Q_FUNC_INFO << "Media service has no renderer control available"; + service->releaseControl(control); + } + } + } + } +} + +void QDeclarativeVideoOutput::present(const QVideoFrame &frame) +{ + m_frame = frame; + update(); +} + +/*! + \qmlproperty enumeration VideoOutput::fillMode + + Set this property to define how the video is scaled to fit the target area. + + \list + \o Stretch - the video is scaled to fit. + \o PreserveAspectFit - the video is scaled uniformly to fit without cropping + \o PreserveAspectCrop - the video is scaled uniformly to fill, cropping if necessary + \endlist + + The default fill mode is PreserveAspectFit. +*/ + +QDeclarativeVideoOutput::FillMode QDeclarativeVideoOutput::fillMode() const +{ + return m_fillMode; +} + +void QDeclarativeVideoOutput::setFillMode(FillMode mode) +{ + if (mode == m_fillMode) + return; + + m_fillMode = mode; + update(); + + emit fillModeChanged(mode); +} + +void QDeclarativeVideoOutput::_q_updateNativeSize(const QVideoSurfaceFormat &format) +{ + const QSize &size = format.sizeHint(); + if (m_nativeSize != size) { + m_nativeSize = size; + + setImplicitWidth(size.width()); + setImplicitHeight(size.height()); + } +} + +void QDeclarativeVideoOutput::_q_updateGeometry() +{ + QRectF rect(0, 0, width(), height()); + + if (m_nativeSize.isEmpty()) { + //this is necessary for item to receive the + //first paint event and configure video surface. + m_boundingRect = rect; + m_sourceRect = QRectF(0, 0, 1, 1); + } else if (m_fillMode == Stretch) { + m_boundingRect = rect; + m_sourceRect = QRectF(0, 0, 1, 1); + } else if (m_fillMode == PreserveAspectFit) { + QSizeF size = m_nativeSize; + size.scale(rect.size(), Qt::KeepAspectRatio); + + m_boundingRect = QRectF(0, 0, size.width(), size.height()); + m_boundingRect.moveCenter(rect.center()); + + m_sourceRect = QRectF(0, 0, 1, 1); + } else if (m_fillMode == PreserveAspectCrop) { + m_boundingRect = rect; + + QSizeF size = rect.size(); + size.scale(m_nativeSize, Qt::KeepAspectRatio); + + m_sourceRect = QRectF( + 0, 0, size.width() / m_nativeSize.width(), size.height() / m_nativeSize.height()); + m_sourceRect.moveCenter(QPointF(0.5, 0.5)); + } +} + +QSGNode *QDeclarativeVideoOutput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + QSGVideoNode *videoNode = static_cast(oldNode); + + if (videoNode && videoNode->pixelFormat() != m_frame.pixelFormat()) { +#ifdef DEBUG_VIDEOITEM + qDebug() << "updatePaintNode: deleting old video node because frame format changed..."; +#endif + delete videoNode; + videoNode = 0; + } + + if (!m_frame.isValid()) { +#ifdef DEBUG_VIDEOITEM + qDebug() << "updatePaintNode: no frames yet... aborting..."; +#endif + return 0; + } + + if (videoNode == 0) { + foreach (QSGVideoNodeFactory* factory, m_videoNodeFactories) { + videoNode = factory->createNode(m_surface->surfaceFormat()); + if (videoNode) + break; + } + } + + if (videoNode == 0) + return 0; + + _q_updateGeometry(); + videoNode->setTexturedRectGeometry(m_boundingRect, m_sourceRect); + videoNode->setCurrentFrame(m_frame); + return videoNode; +} diff --git a/src/imports/multimedia/qdeclarativevideooutput_p.h b/src/imports/multimedia/qdeclarativevideooutput_p.h new file mode 100644 index 000000000..3fbeb0fb7 --- /dev/null +++ b/src/imports/multimedia/qdeclarativevideooutput_p.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEVIDEOOUTPUT_P_H +#define QDECLARATIVEVIDEOOUTPUT_P_H + +#include + +#include +#include + +#include + +#include "qsgvideonode_p.h" + +class QSGVideoItemSurface; +class QVideoRendererControl; +class QMediaService; +class QVideoSurfaceFormat; + +class QDeclarativeVideoOutput : public QSGItem +{ + Q_OBJECT + Q_DISABLE_COPY(QDeclarativeVideoOutput) + Q_PROPERTY(QObject* source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged) + Q_ENUMS(FillMode) + +public: + enum FillMode + { + Stretch = Qt::IgnoreAspectRatio, + PreserveAspectFit = Qt::KeepAspectRatio, + PreserveAspectCrop = Qt::KeepAspectRatioByExpanding + }; + + QDeclarativeVideoOutput(QSGItem *parent = 0); + ~QDeclarativeVideoOutput(); + + QObject *source() const { return m_source.data(); } + void setSource(QObject *source); + + FillMode fillMode() const; + void setFillMode(FillMode mode); + +Q_SIGNALS: + void sourceChanged(); + void fillModeChanged(QDeclarativeVideoOutput::FillMode); + +protected: + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + +private Q_SLOTS: + void _q_updateMediaObject(); + void _q_updateNativeSize(const QVideoSurfaceFormat&); + void _q_updateGeometry(); + +private: + void present(const QVideoFrame &frame); + + friend class QSGVideoItemSurface; + + QWeakPointer m_source; + QWeakPointer m_mediaObject; + QWeakPointer m_service; + QWeakPointer m_rendererControl; + + QList m_videoNodeFactories; + QSGVideoItemSurface *m_surface; + QVideoFrame m_frame; + FillMode m_fillMode; + QSize m_nativeSize; + QRectF m_boundingRect; + QRectF m_sourceRect; +}; + +#endif // QDECLARATIVEVIDEOOUTPUT_H diff --git a/src/imports/multimedia/qsgvideonode.cpp b/src/imports/multimedia/qsgvideonode.cpp new file mode 100644 index 000000000..712caff65 --- /dev/null +++ b/src/imports/multimedia/qsgvideonode.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgvideonode_p.h" + +QSGVideoNode::QSGVideoNode() +{ +} + +void QSGVideoNode::setTexturedRectGeometry(const QRectF &rect, const QRectF &textureRect) +{ + if (rect == m_rect && textureRect == m_textureRect) + return; + + m_rect = rect; + m_textureRect = textureRect; + + QSGGeometry *g = geometry(); + + if (g == 0) { + g = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4); + QSGGeometry::updateTexturedRectGeometry(g, rect, textureRect); + setGeometry(g); + } else { + QSGGeometry::updateTexturedRectGeometry(g, rect, textureRect); + } + + markDirty(DirtyGeometry); +} diff --git a/src/imports/multimedia/qsgvideonode_i420.cpp b/src/imports/multimedia/qsgvideonode_i420.cpp new file mode 100644 index 000000000..0342de565 --- /dev/null +++ b/src/imports/multimedia/qsgvideonode_i420.cpp @@ -0,0 +1,295 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qsgvideonode_i420.h" +#include +#include + +#include + +QList QSGVideoNodeFactory_I420::supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType) const +{ + QList formats; + + if (handleType == QAbstractVideoBuffer::NoHandle) + formats << QVideoFrame::Format_YUV420P << QVideoFrame::Format_YV12; + + return formats; +} + +QSGVideoNode *QSGVideoNodeFactory_I420::createNode(const QVideoSurfaceFormat &format) +{ + if (supportedPixelFormats(format.handleType()).contains(format.pixelFormat())) + return new QSGVideoNode_I420(format); + + return 0; +} + + +class QSGVideoMaterialShader_YUV420 : public QSGMaterialShader +{ +public: + void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial); + + virtual char const *const *attributeNames() const { + static const char *names[] = { + "qt_VertexPosition", + "qt_VertexTexCoord", + 0 + }; + return names; + } + +protected: + + virtual const char *vertexShader() const { + const char *shader = + "uniform highp mat4 qt_Matrix; \n" + "attribute highp vec4 qt_VertexPosition; \n" + "attribute highp vec2 qt_VertexTexCoord; \n" + "varying highp vec2 qt_TexCoord; \n" + "void main() { \n" + " qt_TexCoord = qt_VertexTexCoord; \n" + " gl_Position = qt_Matrix * qt_VertexPosition; \n" + "}"; + return shader; + } + + virtual const char *fragmentShader() const { + static const char *shader = + "uniform sampler2D yTexture;" + "uniform sampler2D uTexture;" + "uniform sampler2D vTexture;" + "uniform mediump mat4 colorMatrix;" + "uniform lowp float opacity;" + "" + "varying highp vec2 qt_TexCoord;" + "" + "void main()" + "{" + " mediump float Y = texture2D(yTexture, qt_TexCoord).r;" + " mediump float U = texture2D(uTexture, qt_TexCoord).r;" + " mediump float V = texture2D(vTexture, qt_TexCoord).r;" + " mediump vec4 color = vec4(Y, U, V, 1.);" + " gl_FragColor = colorMatrix * color * opacity;" + "}"; + return shader; + } + + virtual void initialize() { + m_id_matrix = program()->uniformLocation("qt_Matrix"); + 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"); + } + + int m_id_matrix; + int m_id_yTexture; + int m_id_uTexture; + int m_id_vTexture; + int m_id_colorMatrix; + int m_id_opacity; +}; + + +class QSGVideoMaterial_YUV420 : public QSGMaterial +{ +public: + QSGVideoMaterial_YUV420(const QVideoSurfaceFormat &format) + { + switch (format.yCbCrColorSpace()) { + case QVideoSurfaceFormat::YCbCr_JPEG: + colorMatrix = QMatrix4x4( + 1.0, 0.000, 1.402, -0.701, + 1.0, -0.344, -0.714, 0.529, + 1.0, 1.772, 0.000, -0.886, + 0.0, 0.000, 0.000, 1.0000); + break; + case QVideoSurfaceFormat::YCbCr_BT709: + case QVideoSurfaceFormat::YCbCr_xvYCC709: + 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; + default: //BT 601: + 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); + } + + setFlag(Blending, false); + } + + virtual QSGMaterialType *type() const { + static QSGMaterialType theType; + return &theType; + } + + virtual QSGMaterialShader *createShader() const { + return new QSGVideoMaterialShader_YUV420; + } + + virtual int compare(const QSGMaterial *other) const { + const QSGVideoMaterial_YUV420 *m = static_cast(other); + int d = idY - m->idY; + if (d) + return d; + else if ((d = idU - m->idU) != 0) + return d; + else + return idV - m->idV; + } + + void updateBlending() { + setFlag(Blending, qFuzzyCompare(opacity, 1.0) ? false : true); + } + + GLuint idY; + GLuint idU; + GLuint idV; + qreal opacity; + QMatrix4x4 colorMatrix; +}; + + +QSGVideoNode_I420::QSGVideoNode_I420(const QVideoSurfaceFormat &format) : + m_width(0), + m_height(0), + m_format(format) +{ + m_material = new QSGVideoMaterial_YUV420(format); + setMaterial(m_material); + m_material->opacity = 1; +} + +QSGVideoNode_I420::~QSGVideoNode_I420() +{ + if (m_width != 0 && m_height != 0) + glDeleteTextures(3, m_id); +} + +void QSGVideoNode_I420::setCurrentFrame(const QVideoFrame &frame) +{ + m_frame = frame; + + m_frame.map(QAbstractVideoBuffer::ReadOnly); + + int fw = frame.width(); + int fh = frame.height(); + + // Frame has changed size, recreate textures... + if (fw != m_width || fh != m_height) { + if (m_width != 0 && m_height != 0) + glDeleteTextures(3, m_id); + glGenTextures(3, m_id); + m_width = fw; + m_height = fh; + + m_material->idY = m_id[0]; + m_material->idU = m_id[1]; + m_material->idV = m_id[2]; + } + + const uchar *bits = frame.bits(); + int bpl = frame.bytesPerLine(); + int bpl2 = (bpl / 2 + 3) & ~3; + int offsetU = bpl * fh; + int offsetV = bpl * fh + bpl2 * fh / 2; + + if (m_frame.pixelFormat() == QVideoFrame::Format_YV12) + qSwap(offsetU, offsetV); + + bindTexture(m_id[0], GL_TEXTURE0, fw, fh, bits); + bindTexture(m_id[1], GL_TEXTURE1, fw/2, fh / 2, bits + offsetU); + bindTexture(m_id[2], GL_TEXTURE2, fw/2, fh / 2, bits + offsetV); + + m_frame.unmap(); + + markDirty(DirtyMaterial); +} + +void QSGVideoNode_I420::bindTexture(int id, int unit, int w, int h, const uchar *bits) +{ + QGLFunctions *functions = QGLContext::currentContext()->functions(); + functions->glActiveTexture(unit); + glBindTexture(GL_TEXTURE_2D, id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, bits); + 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); +} + + +void QSGVideoMaterialShader_YUV420::updateState(const RenderState &state, + QSGMaterial *newMaterial, + QSGMaterial *oldMaterial) +{ + Q_UNUSED(oldMaterial); + + QGLFunctions *functions = state.context()->functions(); + QSGVideoMaterial_YUV420 *mat = static_cast(newMaterial); + program()->setUniformValue(m_id_yTexture, 0); + program()->setUniformValue(m_id_uTexture, 1); + program()->setUniformValue(m_id_vTexture, 2); + + functions->glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mat->idY); + functions->glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, mat->idU); + functions->glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, mat->idV); + + program()->setUniformValue(m_id_colorMatrix, mat->colorMatrix); + if (state.isOpacityDirty()) { + mat->opacity = state.opacity(); + program()->setUniformValue(m_id_opacity, GLfloat(mat->opacity)); + } + + if (state.isMatrixDirty()) + program()->setUniformValue(m_id_matrix, state.combinedMatrix()); +} diff --git a/src/imports/multimedia/qsgvideonode_i420.h b/src/imports/multimedia/qsgvideonode_i420.h new file mode 100644 index 000000000..516f70cc5 --- /dev/null +++ b/src/imports/multimedia/qsgvideonode_i420.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGVIDEONODE_I420_H +#define QSGVIDEONODE_I420_H + +#include "qsgvideonode_p.h" +#include + +class QSGVideoMaterial_YUV420; +class QSGVideoNode_I420 : public QSGVideoNode +{ +public: + QSGVideoNode_I420(const QVideoSurfaceFormat &format); + ~QSGVideoNode_I420(); + + virtual QVideoFrame::PixelFormat pixelFormat() const { + return m_format.pixelFormat(); + } + void setCurrentFrame(const QVideoFrame &frame); + +private: + void bindTexture(int id, int unit, int w, int h, const uchar *bits); + + int m_width; + int m_height; + GLuint m_id[3]; + + QVideoSurfaceFormat m_format; + QSGVideoMaterial_YUV420 *m_material; + QVideoFrame m_frame; +}; + +class QSGVideoNodeFactory_I420 : public QSGVideoNodeFactory { +public: + QList supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const; + QSGVideoNode *createNode(const QVideoSurfaceFormat &format); +}; + + +#endif // QSGVIDEONODE_I420_H diff --git a/src/imports/multimedia/qsgvideonode_p.h b/src/imports/multimedia/qsgvideonode_p.h new file mode 100644 index 000000000..49d5a5c76 --- /dev/null +++ b/src/imports/multimedia/qsgvideonode_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGVIDEONODE_P_H +#define QSGVIDEONODE_P_H + +#include + +#include +#include +#include + +class QSGVideoNode : public QSGGeometryNode +{ +public: + QSGVideoNode(); + + virtual void setCurrentFrame(const QVideoFrame &frame) = 0; + virtual QVideoFrame::PixelFormat pixelFormat() const = 0; + + void setTexturedRectGeometry(const QRectF &boundingRect, const QRectF &textureRect); + +private: + QRectF m_rect; + QRectF m_textureRect; +}; + +class QSGVideoNodeFactory { +public: + virtual QList supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const = 0; + virtual QSGVideoNode *createNode(const QVideoSurfaceFormat &format) = 0; +}; + +#endif // QSGVIDEONODE_H diff --git a/src/imports/multimedia/qsgvideonode_rgb32.cpp b/src/imports/multimedia/qsgvideonode_rgb32.cpp new file mode 100644 index 000000000..1c6738d28 --- /dev/null +++ b/src/imports/multimedia/qsgvideonode_rgb32.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qsgvideonode_rgb32.h" + +#include + +QList QSGVideoNodeFactory_RGB32::supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType) const +{ + QList formats; + + if (handleType == QAbstractVideoBuffer::NoHandle) + formats.append(QVideoFrame::Format_RGB32); + + return formats; +} + +QSGVideoNode *QSGVideoNodeFactory_RGB32::createNode(const QVideoSurfaceFormat &format) +{ + if (supportedPixelFormats(format.handleType()).contains(format.pixelFormat())) + return new QSGVideoNode_RGB32(); + + return 0; +} + + +class QSGVideoTexture_RGB32 : public QSGTexture +{ +public: + QSGVideoTexture_RGB32(); + + int textureId() const { return m_textureId; } + QSize textureSize() const { return m_size; } + bool hasAlphaChannel() const { return false; } + bool hasMipmaps() const { return false; } + + void setCurrentFrame(const QVideoFrame &frame) { m_frame = frame; } + + //QRectF textureSubRect() const; + + void bind(); + +private: + QVideoFrame m_frame; + GLuint m_textureId; + QSize m_size; +}; + +QSGVideoTexture_RGB32::QSGVideoTexture_RGB32() + : QSGTexture() + , m_textureId(0) +{ +} + +void QSGVideoTexture_RGB32::bind() +{ + if (m_frame.isValid()) { + if (m_size != m_frame.size()) { + if (m_textureId) + glDeleteTextures(1, &m_textureId); + glGenTextures(1, &m_textureId); + m_size = m_frame.size(); + } + + if (m_frame.map(QAbstractVideoBuffer::ReadOnly)) { + QGLFunctions *functions = QGLContext::currentContext()->functions(); + const uchar *bits = m_frame.bits(); + functions->glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_textureId); +#ifdef QT_OPENGL_ES + qWarning() << "RGB video doesn't work on GL ES\n"; +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + m_size.width(), m_size.height(), + 0, GL_BGRA, GL_UNSIGNED_BYTE, bits); +#endif + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + m_frame.unmap(); + } + m_frame = QVideoFrame(); + updateBindOptions(true); + } else { + glBindTexture(GL_TEXTURE_2D, m_textureId); + updateBindOptions(false); + } +} + +QSGVideoNode_RGB32::QSGVideoNode_RGB32() +{ + setMaterial(&m_material); + m_texture = new QSGVideoTexture_RGB32(); + + m_material.setTexture(m_texture); + m_material.setFiltering(QSGTexture::Linear); +} + + +void QSGVideoNode_RGB32::setCurrentFrame(const QVideoFrame &frame) +{ + m_texture->setCurrentFrame(frame); + markDirty(DirtyMaterial); +} diff --git a/src/imports/multimedia/qsgvideonode_rgb32.h b/src/imports/multimedia/qsgvideonode_rgb32.h new file mode 100644 index 000000000..b53f38a37 --- /dev/null +++ b/src/imports/multimedia/qsgvideonode_rgb32.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGVIDEONODE_RGB32_H +#define QSGVIDEONODE_RGB32_H + +#include "qsgvideonode_p.h" +#include + +class QSGVideoTexture_RGB32; + +class QSGVideoNode_RGB32 : public QSGVideoNode +{ +public: + QSGVideoNode_RGB32(); + + void setCurrentFrame(const QVideoFrame &frame); + + QVideoFrame::PixelFormat pixelFormat() const { return QVideoFrame::Format_RGB32; } + +private: + QSGTextureMaterial m_material; + QSGVideoTexture_RGB32 *m_texture; +}; + +class QSGVideoNodeFactory_RGB32 : public QSGVideoNodeFactory { +public: + QList supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const; + QSGVideoNode *createNode(const QVideoSurfaceFormat &format); +}; + + +#endif // QSGVIDEONODE_RGB32_H -- cgit v1.2.3