diff options
Diffstat (limited to 'src/qtmultimediaquicktools')
-rw-r--r-- | src/qtmultimediaquicktools/qdeclarativevideooutput.cpp | 760 | ||||
-rw-r--r-- | src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp | 351 | ||||
-rw-r--r-- | src/qtmultimediaquicktools/qdeclarativevideooutput_render_p.h | 117 | ||||
-rw-r--r-- | src/qtmultimediaquicktools/qdeclarativevideooutput_window.cpp | 153 | ||||
-rw-r--r-- | src/qtmultimediaquicktools/qdeclarativevideooutput_window_p.h | 74 | ||||
-rw-r--r-- | src/qtmultimediaquicktools/qsgvideonode_i420.cpp | 330 | ||||
-rw-r--r-- | src/qtmultimediaquicktools/qsgvideonode_i420.h | 77 | ||||
-rw-r--r-- | src/qtmultimediaquicktools/qsgvideonode_rgb.cpp | 289 | ||||
-rw-r--r-- | src/qtmultimediaquicktools/qsgvideonode_rgb.h | 77 | ||||
-rw-r--r-- | src/qtmultimediaquicktools/qsgvideonode_texture.cpp | 271 | ||||
-rw-r--r-- | src/qtmultimediaquicktools/qsgvideonode_texture.h | 77 | ||||
-rw-r--r-- | src/qtmultimediaquicktools/qtmultimediaquicktools.pro | 18 |
12 files changed, 2592 insertions, 2 deletions
diff --git a/src/qtmultimediaquicktools/qdeclarativevideooutput.cpp b/src/qtmultimediaquicktools/qdeclarativevideooutput.cpp new file mode 100644 index 000000000..321fd5e81 --- /dev/null +++ b/src/qtmultimediaquicktools/qdeclarativevideooutput.cpp @@ -0,0 +1,760 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2012 Research In Motion +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qdeclarativevideooutput_p.h" + +#include "qdeclarativevideooutput_render_p.h" +#include "qdeclarativevideooutput_window_p.h" +#include <private/qvideooutputorientationhandler_p.h> +#include <QtMultimedia/qmediaobject.h> +#include <QtMultimedia/qmediaservice.h> +#include <private/qmediapluginloader_p.h> + +//#define DEBUG_VIDEOITEM + +QT_BEGIN_NAMESPACE + +/*! + \qmltype VideoOutput + \instantiates QDeclarativeVideoOutput + \brief Render video or camera viewfinder. + + \ingroup multimedia_qml + \ingroup multimedia_video_qml + \inqmlmodule QtMultimedia + + \c VideoOutput is part of the \b{QtMultimedia 5.0} module. + + \qml + import QtQuick 2.0 + import QtMultimedia 5.0 + + Rectangle { + width: 800 + height: 600 + color: "black" + + MediaPlayer { + id: player + source: "file://video.webm" + autoPlay: 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. + + The VideoOutput item works with backends that support either QVideoRendererControl or + QVideoWindowControl. If the backend only supports QVideoWindowControl, the video is rendered + onto an overlay window that is layered on top of the QtQuick window. Due to the nature of the + video overlays, certain features are not available for these kind of backends: + \list + \li Some transformations like rotations + \li Having other QtQuick items on top of the VideoOutput item + \endlist + Most backends however do support QVideoRendererControl and therefore don't have the limitations + listed above. + + \sa MediaPlayer, Camera + + \section1 Screen Saver + + If it is likely that an application will be playing video for an extended + period of time without user interaction it may be necessary to disable + the platform's screen saver. The \l ScreenSaver (from \l QtSystemInfo) + may be used to disable the screensaver in this fashion: + + \qml + import QtSystemInfo 5.0 + + ScreenSaver { screenSaverEnabled: false } + \endqml +*/ + +/*! + \internal + \class QDeclarativeVideoOutput + \brief The QDeclarativeVideoOutput class provides a video output item. +*/ + +QDeclarativeVideoOutput::QDeclarativeVideoOutput(QQuickItem *parent) : + QQuickItem(parent), + m_sourceType(NoSource), + m_fillMode(PreserveAspectFit), + m_geometryDirty(true), + m_orientation(0), + m_autoOrientation(false), + m_screenOrientationHandler(0) +{ + setFlag(ItemHasContents, true); +} + +QDeclarativeVideoOutput::~QDeclarativeVideoOutput() +{ + m_backend.reset(); + m_source.clear(); + _q_updateMediaObject(); +} + +/*! + \qmlproperty variant QtMultimedia::VideoOutput::source + + This property holds the source item providing the video frames like MediaPlayer or Camera. + + If you are extending your own C++ classes to interoperate with VideoOutput, you can + either provide a QObject based class with a \c mediaObject property that exposes a + QMediaObject derived class that has a QVideoRendererControl available, or you can + provide a QObject based class with a writable \c videoSurface property that can + accept a QAbstractVideoSurface based class and can follow the correct protocol to + deliver QVideoFrames to it. +*/ + +void QDeclarativeVideoOutput::setSource(QObject *source) +{ +#ifdef DEBUG_VIDEOITEM + qDebug() << Q_FUNC_INFO << source; +#endif + + if (source == m_source.data()) + return; + + if (m_source && m_sourceType == MediaObjectSource) + disconnect(m_source.data(), 0, this, SLOT(_q_updateMediaObject())); + + if (m_backend) + m_backend->releaseSource(); + + m_source = source; + + if (m_source) { + const QMetaObject *metaObject = m_source.data()->metaObject(); + + int mediaObjectPropertyIndex = metaObject->indexOfProperty("mediaObject"); + if (mediaObjectPropertyIndex != -1) { + const QMetaProperty mediaObjectProperty = metaObject->property(mediaObjectPropertyIndex); + + if (mediaObjectProperty.hasNotifySignal()) { + QMetaMethod method = mediaObjectProperty.notifySignal(); + QMetaObject::connect(m_source.data(), method.methodIndex(), + this, this->metaObject()->indexOfSlot("_q_updateMediaObject()"), + Qt::DirectConnection, 0); + + } + m_sourceType = MediaObjectSource; + } else if (metaObject->indexOfProperty("videoSurface") != -1) { + // Make sure our backend is a QDeclarativeVideoRendererBackend + m_backend.reset(); + createBackend(0); + Q_ASSERT(m_backend); +#ifndef QT_NO_DYNAMIC_CAST + Q_ASSERT(dynamic_cast<QDeclarativeVideoRendererBackend *>(m_backend.data())); +#endif + QAbstractVideoSurface * const surface = m_backend->videoSurface(); + Q_ASSERT(surface); + m_source.data()->setProperty("videoSurface", + QVariant::fromValue<QAbstractVideoSurface*>(surface)); + m_sourceType = VideoSurfaceSource; + } else { + m_sourceType = NoSource; + } + } else { + m_sourceType = NoSource; + } + + _q_updateMediaObject(); + emit sourceChanged(); +} + +Q_GLOBAL_STATIC_WITH_ARGS(QMediaPluginLoader, videoBackendFactoryLoader, + (QDeclarativeVideoBackendFactoryInterface_iid, QLatin1String("video/declarativevideobackend"), Qt::CaseInsensitive)) + +bool QDeclarativeVideoOutput::createBackend(QMediaService *service) +{ + bool backendAvailable = false; + + foreach (QObject *instance, videoBackendFactoryLoader()->instances(QLatin1String("declarativevideobackend"))) { + if (QDeclarativeVideoBackendFactoryInterface *plugin = qobject_cast<QDeclarativeVideoBackendFactoryInterface*>(instance)) { + m_backend.reset(plugin->create(this)); + if (m_backend && m_backend->init(service)) { + backendAvailable = true; + break; + } + } + } + + if (!backendAvailable) { + m_backend.reset(new QDeclarativeVideoRendererBackend(this)); + if (m_backend->init(service)) + backendAvailable = true; + } + + // QDeclarativeVideoWindowBackend only works when there is a service with a QVideoWindowControl. + // Without service, the QDeclarativeVideoRendererBackend should always work. + if (!backendAvailable) { + Q_ASSERT(service); + m_backend.reset(new QDeclarativeVideoWindowBackend(this)); + if (m_backend->init(service)) + backendAvailable = true; + } + + if (!backendAvailable) { + qWarning() << Q_FUNC_INFO << "Media service has neither renderer nor window control available."; + m_backend.reset(); + } + return backendAvailable; +} + +void QDeclarativeVideoOutput::_q_updateMediaObject() +{ + QMediaObject *mediaObject = 0; + + if (m_source) + mediaObject = qobject_cast<QMediaObject*>(m_source.data()->property("mediaObject").value<QObject*>()); + +#ifdef DEBUG_VIDEOITEM + qDebug() << Q_FUNC_INFO << mediaObject; +#endif + + if (m_mediaObject.data() == mediaObject) + return; + + if (m_sourceType != VideoSurfaceSource) + m_backend.reset(); + + m_mediaObject.clear(); + m_service.clear(); + + if (mediaObject) { + if (QMediaService *service = mediaObject->service()) { + if (createBackend(service)) { + m_service = service; + m_mediaObject = mediaObject; + } + } + } +} + +/*! + \qmlproperty enumeration QtMultimedia::VideoOutput::fillMode + + Set this property to define how the video is scaled to fit the target area. + + \list + \li Stretch - the video is scaled to fit. + \li PreserveAspectFit - the video is scaled uniformly to fit without cropping + \li 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; + m_geometryDirty = true; + update(); + + emit fillModeChanged(mode); +} + +void QDeclarativeVideoOutput::_q_updateNativeSize() +{ + if (!m_backend) + return; + + QSize size = m_backend->nativeSize(); + if (!qIsDefaultAspect(m_orientation)) { + size.transpose(); + } + + if (m_nativeSize != size) { + m_nativeSize = size; + + m_geometryDirty = true; + + setImplicitWidth(size.width()); + setImplicitHeight(size.height()); + + emit sourceRectChanged(); + } +} + +/* Based on fill mode and our size, figure out the source/dest rects */ +void QDeclarativeVideoOutput::_q_updateGeometry() +{ + const QRectF rect(0, 0, width(), height()); + const QRectF absoluteRect(x(), y(), width(), height()); + + if (!m_geometryDirty && m_lastRect == absoluteRect) + return; + + QRectF oldContentRect(m_contentRect); + + m_geometryDirty = false; + m_lastRect = absoluteRect; + + if (m_nativeSize.isEmpty()) { + //this is necessary for item to receive the + //first paint event and configure video surface. + m_contentRect = rect; + } else if (m_fillMode == Stretch) { + m_contentRect = rect; + } else if (m_fillMode == PreserveAspectFit || m_fillMode == PreserveAspectCrop) { + QSizeF scaled = m_nativeSize; + scaled.scale(rect.size(), m_fillMode == PreserveAspectFit ? + Qt::KeepAspectRatio : Qt::KeepAspectRatioByExpanding); + + m_contentRect = QRectF(QPointF(), scaled); + m_contentRect.moveCenter(rect.center()); + } + + if (m_backend) + m_backend->updateGeometry(); + + if (m_contentRect != oldContentRect) + emit contentRectChanged(); +} + +void QDeclarativeVideoOutput::_q_screenOrientationChanged(int orientation) +{ + setOrientation(orientation); +} + +/*! + \qmlproperty int QtMultimedia::VideoOutput::orientation + + In some cases the source video stream requires a certain + orientation to be correct. This includes + sources like a camera viewfinder, where the displayed + viewfinder should match reality, no matter what rotation + the rest of the user interface has. + + This property allows you to apply a rotation (in steps + of 90 degrees) to compensate for any user interface + rotation, with positive values in the anti-clockwise direction. + + The orientation change will also affect the mapping + of coordinates from source to viewport. +*/ +int QDeclarativeVideoOutput::orientation() const +{ + return m_orientation; +} + +void QDeclarativeVideoOutput::setOrientation(int orientation) +{ + // Make sure it's a multiple of 90. + if (orientation % 90) + return; + + // If there's no actual change, return + if (m_orientation == orientation) + return; + + // If the new orientation is the same effect + // as the old one, don't update the video node stuff + if ((m_orientation % 360) == (orientation % 360)) { + m_orientation = orientation; + emit orientationChanged(); + return; + } + + m_geometryDirty = true; + + // Otherwise, a new orientation + // See if we need to change aspect ratio orientation too + bool oldAspect = qIsDefaultAspect(m_orientation); + bool newAspect = qIsDefaultAspect(orientation); + + m_orientation = orientation; + + if (oldAspect != newAspect) { + m_nativeSize.transpose(); + + setImplicitWidth(m_nativeSize.width()); + setImplicitHeight(m_nativeSize.height()); + + // Source rectangle does not change for orientation + } + + update(); + emit orientationChanged(); +} + +/*! + \qmlproperty int QtMultimedia::VideoOutput::autoOrientation + + This property allows you to enable and disable auto orientation + of the video stream, so that its orientation always matches + the orientation of the screen. If \c autoOrientation is enabled, + the \c orientation property is overwritten. + + By default \c autoOrientation is disabled. + + \since QtMultimedia 5.2 +*/ +bool QDeclarativeVideoOutput::autoOrientation() const +{ + return m_autoOrientation; +} + +void QDeclarativeVideoOutput::setAutoOrientation(bool autoOrientation) +{ + if (autoOrientation == m_autoOrientation) + return; + + m_autoOrientation = autoOrientation; + if (m_autoOrientation) { + m_screenOrientationHandler = new QVideoOutputOrientationHandler(this); + connect(m_screenOrientationHandler, SIGNAL(orientationChanged(int)), + this, SLOT(_q_screenOrientationChanged(int))); + + _q_screenOrientationChanged(m_screenOrientationHandler->currentOrientation()); + } else { + disconnect(m_screenOrientationHandler, SIGNAL(orientationChanged(int)), + this, SLOT(_q_screenOrientationChanged(int))); + m_screenOrientationHandler->deleteLater(); + m_screenOrientationHandler = 0; + } + + emit autoOrientationChanged(); +} + +/*! + \qmlproperty rectangle QtMultimedia::VideoOutput::contentRect + + This property holds the item coordinates of the area that + would contain video to render. With certain fill modes, + this rectangle will be larger than the visible area of the + \c VideoOutput. + + This property is useful when other coordinates are specified + in terms of the source dimensions - this applied for relative + (normalized) frame coordinates in the range of 0 to 1.0. + + \sa mapRectToItem(), mapPointToItem() + + Areas outside this will be transparent. +*/ +QRectF QDeclarativeVideoOutput::contentRect() const +{ + return m_contentRect; +} + +/*! + \qmlproperty rectangle QtMultimedia::VideoOutput::sourceRect + + This property holds the area of the source video + content that is considered for rendering. The + values are in source pixel coordinates, adjusted for + the source's pixel aspect ratio. + + Note that typically the top left corner of this rectangle + will be \c {0,0} while the width and height will be the + width and height of the input content. Only when the video + source has a viewport set, these values will differ. + + The orientation setting does not affect this rectangle. + + \sa QVideoSurfaceFormat::pixelAspectRatio() + \sa QVideoSurfaceFormat::viewport() +*/ +QRectF QDeclarativeVideoOutput::sourceRect() const +{ + // We might have to transpose back + QSizeF size = m_nativeSize; + if (!qIsDefaultAspect(m_orientation)) { + size.transpose(); + } + + // No backend? Just assume no viewport. + if (!m_nativeSize.isValid() || !m_backend) { + return QRectF(QPointF(), size); + } + + // Take the viewport into account for the top left position. + // m_nativeSize is already adjusted to the viewport, as it originats + // from QVideoSurfaceFormat::sizeHint(), which includes pixel aspect + // ratio and viewport. + const QRectF viewport = m_backend->adjustedViewport(); + Q_ASSERT(viewport.size() == size); + return QRectF(viewport.topLeft(), size); +} + +/*! + \qmlmethod QPointF QtMultimedia::VideoOutput::mapNormalizedPointToItem (const QPointF &point) const + + Given normalized coordinates \a point (that is, each + component in the range of 0 to 1.0), return the mapped point + that it corresponds to (in item coordinates). + This mapping is affected by the orientation. + + Depending on the fill mode, this point may lie outside the rendered + rectangle. + */ +QPointF QDeclarativeVideoOutput::mapNormalizedPointToItem(const QPointF &point) const +{ + qreal dx = point.x(); + qreal dy = point.y(); + + if (qIsDefaultAspect(m_orientation)) { + dx *= m_contentRect.width(); + dy *= m_contentRect.height(); + } else { + dx *= m_contentRect.height(); + dy *= m_contentRect.width(); + } + + switch (qNormalizedOrientation(m_orientation)) { + case 0: + default: + return m_contentRect.topLeft() + QPointF(dx, dy); + case 90: + return m_contentRect.bottomLeft() + QPointF(dy, -dx); + case 180: + return m_contentRect.bottomRight() + QPointF(-dx, -dy); + case 270: + return m_contentRect.topRight() + QPointF(-dy, dx); + } +} + +/*! + \qmlmethod QRectF QtMultimedia::VideoOutput::mapNormalizedRectToItem(const QRectF &rectangle) const + + Given a rectangle \a rectangle in normalized + coordinates (that is, each component in the range of 0 to 1.0), + return the mapped rectangle that it corresponds to (in item coordinates). + This mapping is affected by the orientation. + + Depending on the fill mode, this rectangle may extend outside the rendered + rectangle. + */ +QRectF QDeclarativeVideoOutput::mapNormalizedRectToItem(const QRectF &rectangle) const +{ + return QRectF(mapNormalizedPointToItem(rectangle.topLeft()), + mapNormalizedPointToItem(rectangle.bottomRight())).normalized(); +} + +/*! + \qmlmethod QPointF QtMultimedia::VideoOutput::mapPointToSource(const QPointF &point) const + + Given a point \a point in item coordinates, return the + corresponding point in source coordinates. This mapping is + affected by the orientation. + + If the supplied point lies outside the rendered area, the returned + point will be outside the source rectangle. + */ +QPointF QDeclarativeVideoOutput::mapPointToSource(const QPointF &point) const +{ + QPointF norm = mapPointToSourceNormalized(point); + + if (qIsDefaultAspect(m_orientation)) + return QPointF(norm.x() * m_nativeSize.width(), norm.y() * m_nativeSize.height()); + else + return QPointF(norm.x() * m_nativeSize.height(), norm.y() * m_nativeSize.width()); +} + +/*! + \qmlmethod QRectF QtMultimedia::VideoOutput::mapRectToSource(const QRectF &rectangle) const + + Given a rectangle \a rectangle in item coordinates, return the + corresponding rectangle in source coordinates. This mapping is + affected by the orientation. + + This mapping is affected by the orientation. + + If the supplied point lies outside the rendered area, the returned + point will be outside the source rectangle. + */ +QRectF QDeclarativeVideoOutput::mapRectToSource(const QRectF &rectangle) const +{ + return QRectF(mapPointToSource(rectangle.topLeft()), + mapPointToSource(rectangle.bottomRight())).normalized(); +} + +/*! + \qmlmethod QPointF QtMultimedia::VideoOutput::mapPointToSourceNormalized(const QPointF &point) const + + Given a point \a point in item coordinates, return the + corresponding point in normalized source coordinates. This mapping is + affected by the orientation. + + If the supplied point lies outside the rendered area, the returned + point will be outside the source rectangle. No clamping is performed. + */ +QPointF QDeclarativeVideoOutput::mapPointToSourceNormalized(const QPointF &point) const +{ + if (m_contentRect.isEmpty()) + return QPointF(); + + // Normalize the item source point + qreal nx = (point.x() - m_contentRect.left()) / m_contentRect.width(); + qreal ny = (point.y() - m_contentRect.top()) / m_contentRect.height(); + + const qreal one(1.0f); + + // For now, the origin of the source rectangle is 0,0 + switch (qNormalizedOrientation(m_orientation)) { + case 0: + default: + return QPointF(nx, ny); + case 90: + return QPointF(one - ny, nx); + case 180: + return QPointF(one - nx, one - ny); + case 270: + return QPointF(ny, one - nx); + } +} + +/*! + \qmlmethod QRectF QtMultimedia::VideoOutput::mapRectToSourceNormalized(const QRectF &rectangle) const + + Given a rectangle \a rectangle in item coordinates, return the + corresponding rectangle in normalized source coordinates. This mapping is + affected by the orientation. + + This mapping is affected by the orientation. + + If the supplied point lies outside the rendered area, the returned + point will be outside the source rectangle. No clamping is performed. + */ +QRectF QDeclarativeVideoOutput::mapRectToSourceNormalized(const QRectF &rectangle) const +{ + return QRectF(mapPointToSourceNormalized(rectangle.topLeft()), + mapPointToSourceNormalized(rectangle.bottomRight())).normalized(); +} + +QDeclarativeVideoOutput::SourceType QDeclarativeVideoOutput::sourceType() const +{ + return m_sourceType; +} + +/*! + \qmlmethod QPointF QtMultimedia::VideoOutput::mapPointToItem(const QPointF &point) const + + Given a point \a point in source coordinates, return the + corresponding point in item coordinates. This mapping is + affected by the orientation. + + Depending on the fill mode, this point may lie outside the rendered + rectangle. + */ +QPointF QDeclarativeVideoOutput::mapPointToItem(const QPointF &point) const +{ + if (m_nativeSize.isEmpty()) + return QPointF(); + + // Just normalize and use that function + // m_nativeSize is transposed in some orientations + if (qIsDefaultAspect(m_orientation)) + return mapNormalizedPointToItem(QPointF(point.x() / m_nativeSize.width(), point.y() / m_nativeSize.height())); + else + return mapNormalizedPointToItem(QPointF(point.x() / m_nativeSize.height(), point.y() / m_nativeSize.width())); +} + +/*! + \qmlmethod QRectF QtMultimedia::VideoOutput::mapRectToItem(const QRectF &rectangle) const + + Given a rectangle \a rectangle in source coordinates, return the + corresponding rectangle in item coordinates. This mapping is + affected by the orientation. + + Depending on the fill mode, this rectangle may extend outside the rendered + rectangle. + + */ +QRectF QDeclarativeVideoOutput::mapRectToItem(const QRectF &rectangle) const +{ + return QRectF(mapPointToItem(rectangle.topLeft()), + mapPointToItem(rectangle.bottomRight())).normalized(); +} + +QSGNode *QDeclarativeVideoOutput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) +{ + _q_updateGeometry(); + + if (!m_backend) + return 0; + + return m_backend->updatePaintNode(oldNode, data); +} + +void QDeclarativeVideoOutput::itemChange(QQuickItem::ItemChange change, + const QQuickItem::ItemChangeData &changeData) +{ + if (m_backend) + m_backend->itemChange(change, changeData); +} + +void QDeclarativeVideoOutput::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_UNUSED(newGeometry); + Q_UNUSED(oldGeometry); + + QQuickItem::geometryChanged(newGeometry, oldGeometry); + + // Explicitly listen to geometry changes here. This is needed since changing the position does + // not trigger a call to updatePaintNode(). + // We need to react to position changes though, as the window backened's display rect gets + // changed in that situation. + _q_updateGeometry(); +} + +QT_END_NAMESPACE diff --git a/src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp b/src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp new file mode 100644 index 000000000..cd03cd6b8 --- /dev/null +++ b/src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp @@ -0,0 +1,351 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2012 Research In Motion +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativevideooutput_render_p.h" +#include "qdeclarativevideooutput_p.h" +#include <QtMultimedia/qvideorenderercontrol.h> +#include <QtMultimedia/qmediaservice.h> +#include <private/qmediapluginloader_p.h> +#include <private/qsgvideonode_p.h> + +#include <QtGui/QOpenGLContext> + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC_WITH_ARGS(QMediaPluginLoader, videoNodeFactoryLoader, + (QSGVideoNodeFactoryInterface_iid, QLatin1String("video/videonode"), Qt::CaseInsensitive)) + +QDeclarativeVideoRendererBackend::QDeclarativeVideoRendererBackend(QDeclarativeVideoOutput *parent) + : QDeclarativeVideoBackend(parent), + m_glContext(0), + m_frameChanged(false) +{ + m_surface = new QSGVideoItemSurface(this); + QObject::connect(m_surface, SIGNAL(surfaceFormatChanged(QVideoSurfaceFormat)), + q, SLOT(_q_updateNativeSize()), Qt::QueuedConnection); + + foreach (QObject *instance, videoNodeFactoryLoader()->instances(QSGVideoNodeFactoryPluginKey)) { + QSGVideoNodeFactoryInterface* plugin = qobject_cast<QSGVideoNodeFactoryInterface*>(instance); + if (plugin) + m_videoNodeFactories.append(plugin); + } + + // Append existing node factories as fallback if we have no plugins + m_videoNodeFactories.append(&m_i420Factory); + m_videoNodeFactories.append(&m_rgbFactory); + m_videoNodeFactories.append(&m_textureFactory); +} + +QDeclarativeVideoRendererBackend::~QDeclarativeVideoRendererBackend() +{ + releaseSource(); + releaseControl(); + delete m_surface; +} + +bool QDeclarativeVideoRendererBackend::init(QMediaService *service) +{ + // When there is no service, the source is an object with a "videoSurface" property, which + // doesn't require a QVideoRendererControl and therefore always works + if (!service) + return true; + + if (QMediaControl *control = service->requestControl(QVideoRendererControl_iid)) { + if ((m_rendererControl = qobject_cast<QVideoRendererControl *>(control))) { + m_rendererControl->setSurface(m_surface); + m_service = service; + return true; + } + } + return false; +} + +void QDeclarativeVideoRendererBackend::itemChange(QQuickItem::ItemChange change, + const QQuickItem::ItemChangeData &changeData) +{ + Q_UNUSED(change); + Q_UNUSED(changeData); +} + +void QDeclarativeVideoRendererBackend::releaseSource() +{ + if (q->source() && q->sourceType() == QDeclarativeVideoOutput::VideoSurfaceSource) { + if (q->source()->property("videoSurface").value<QAbstractVideoSurface*>() == m_surface) + q->source()->setProperty("videoSurface", QVariant::fromValue<QAbstractVideoSurface*>(0)); + } + + m_surface->stop(); +} + +void QDeclarativeVideoRendererBackend::releaseControl() +{ + if (m_rendererControl) { + m_rendererControl->setSurface(0); + if (m_service) + m_service->releaseControl(m_rendererControl); + m_rendererControl = 0; + } +} + +QSize QDeclarativeVideoRendererBackend::nativeSize() const +{ + return m_surface->surfaceFormat().sizeHint(); +} + +void QDeclarativeVideoRendererBackend::updateGeometry() +{ + const QRectF viewport = videoSurface()->surfaceFormat().viewport(); + const QSizeF frameSize = videoSurface()->surfaceFormat().frameSize(); + const QRectF normalizedViewport(viewport.x() / frameSize.width(), + viewport.y() / frameSize.height(), + viewport.width() / frameSize.width(), + viewport.height() / frameSize.height()); + const QRectF rect(0, 0, q->width(), q->height()); + if (nativeSize().isEmpty()) { + m_renderedRect = rect; + m_sourceTextureRect = normalizedViewport; + } else if (q->fillMode() == QDeclarativeVideoOutput::Stretch) { + m_renderedRect = rect; + m_sourceTextureRect = normalizedViewport; + } else if (q->fillMode() == QDeclarativeVideoOutput::PreserveAspectFit) { + m_sourceTextureRect = normalizedViewport; + m_renderedRect = q->contentRect(); + } else if (q->fillMode() == QDeclarativeVideoOutput::PreserveAspectCrop) { + m_renderedRect = rect; + const qreal contentHeight = q->contentRect().height(); + const qreal contentWidth = q->contentRect().width(); + + // Calculate the size of the source rectangle without taking the viewport into account + const qreal relativeOffsetLeft = -q->contentRect().left() / contentWidth; + const qreal relativeOffsetTop = -q->contentRect().top() / contentHeight; + const qreal relativeWidth = rect.width() / contentWidth; + const qreal relativeHeight = rect.height() / contentHeight; + + // Now take the viewport size into account + const qreal totalOffsetLeft = normalizedViewport.x() + relativeOffsetLeft * normalizedViewport.width(); + const qreal totalOffsetTop = normalizedViewport.y() + relativeOffsetTop * normalizedViewport.height(); + const qreal totalWidth = normalizedViewport.width() * relativeWidth; + const qreal totalHeight = normalizedViewport.height() * relativeHeight; + + if (qIsDefaultAspect(q->orientation())) { + m_sourceTextureRect = QRectF(totalOffsetLeft, totalOffsetTop, + totalWidth, totalHeight); + } else { + m_sourceTextureRect = QRectF(totalOffsetTop, totalOffsetLeft, + totalHeight, totalWidth); + } + } + + if (videoSurface()->surfaceFormat().scanLineDirection() == QVideoSurfaceFormat::BottomToTop) { + qreal top = m_sourceTextureRect.top(); + m_sourceTextureRect.setTop(m_sourceTextureRect.bottom()); + m_sourceTextureRect.setBottom(top); + } +} + +QSGNode *QDeclarativeVideoRendererBackend::updatePaintNode(QSGNode *oldNode, + QQuickItem::UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + QSGVideoNode *videoNode = static_cast<QSGVideoNode *>(oldNode); + + QMutexLocker lock(&m_frameMutex); + + if (!m_glContext) { + m_glContext = QOpenGLContext::currentContext(); + m_surface->scheduleOpenGLContextUpdate(); + + // Internal mechanism to call back the surface renderer from the QtQuick render thread + QObject *obj = m_surface->property("_q_GLThreadCallback").value<QObject*>(); + if (obj) { + QEvent ev(QEvent::User); + obj->event(&ev); + } + } + + if (m_frameChanged) { + 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 + m_frameChanged = false; + return 0; + } + + if (!videoNode) { + foreach (QSGVideoNodeFactoryInterface* factory, m_videoNodeFactories) { + videoNode = factory->createNode(m_surface->surfaceFormat()); + if (videoNode) + break; + } + } + } + + if (!videoNode) { + m_frameChanged = false; + m_frame = QVideoFrame(); + return 0; + } + + // Negative rotations need lots of %360 + videoNode->setTexturedRectGeometry(m_renderedRect, m_sourceTextureRect, + qNormalizedOrientation(q->orientation())); + if (m_frameChanged) { + videoNode->setCurrentFrame(m_frame); + //don't keep the frame for more than really necessary + m_frameChanged = false; + m_frame = QVideoFrame(); + } + return videoNode; +} + +QAbstractVideoSurface *QDeclarativeVideoRendererBackend::videoSurface() const +{ + return m_surface; +} + +QRectF QDeclarativeVideoRendererBackend::adjustedViewport() const +{ + const QRectF viewport = m_surface->surfaceFormat().viewport(); + const QSize pixelAspectRatio = m_surface->surfaceFormat().pixelAspectRatio(); + + if (pixelAspectRatio.height() != 0) { + const qreal ratio = pixelAspectRatio.width() / pixelAspectRatio.height(); + QRectF result = viewport; + result.setX(result.x() * ratio); + result.setWidth(result.width() * ratio); + return result; + } + + return viewport; +} + +QOpenGLContext *QDeclarativeVideoRendererBackend::glContext() const +{ + return m_glContext; +} + +void QDeclarativeVideoRendererBackend::present(const QVideoFrame &frame) +{ + m_frameMutex.lock(); + m_frame = frame; + m_frameChanged = true; + m_frameMutex.unlock(); + + q->update(); +} + +void QDeclarativeVideoRendererBackend::stop() +{ + present(QVideoFrame()); +} + +QSGVideoItemSurface::QSGVideoItemSurface(QDeclarativeVideoRendererBackend *backend, QObject *parent) + : QAbstractVideoSurface(parent), + m_backend(backend) +{ +} + +QSGVideoItemSurface::~QSGVideoItemSurface() +{ +} + +QList<QVideoFrame::PixelFormat> QSGVideoItemSurface::supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType) const +{ + QList<QVideoFrame::PixelFormat> formats; + + foreach (QSGVideoNodeFactoryInterface* factory, m_backend->m_videoNodeFactories) + formats.append(factory->supportedPixelFormats(handleType)); + + return formats; +} + +bool QSGVideoItemSurface::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); +} + +void QSGVideoItemSurface::stop() +{ + m_backend->stop(); + QAbstractVideoSurface::stop(); +} + +bool QSGVideoItemSurface::present(const QVideoFrame &frame) +{ + if (!frame.isValid()) { + qWarning() << Q_FUNC_INFO << "I'm getting bad frames here..."; + return false; + } + m_backend->present(frame); + return true; +} + +void QSGVideoItemSurface::scheduleOpenGLContextUpdate() +{ + //This method is called from render thread + QMetaObject::invokeMethod(this, "updateOpenGLContext"); +} + +void QSGVideoItemSurface::updateOpenGLContext() +{ + //Set a dynamic property to access the OpenGL context in Qt Quick render thread. + this->setProperty("GLContext", QVariant::fromValue<QObject*>(m_backend->glContext())); +} + +QT_END_NAMESPACE diff --git a/src/qtmultimediaquicktools/qdeclarativevideooutput_render_p.h b/src/qtmultimediaquicktools/qdeclarativevideooutput_render_p.h new file mode 100644 index 000000000..3682c15a6 --- /dev/null +++ b/src/qtmultimediaquicktools/qdeclarativevideooutput_render_p.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2012 Research In Motion +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEVIDEOOUTPUT_RENDER_P_H +#define QDECLARATIVEVIDEOOUTPUT_RENDER_P_H + +#include "qdeclarativevideooutput_backend_p.h" +#include "qsgvideonode_i420.h" +#include "qsgvideonode_rgb.h" +#include "qsgvideonode_texture.h" + +#include <QtCore/qmutex.h> +#include <QtMultimedia/qabstractvideosurface.h> + +QT_BEGIN_NAMESPACE + +class QSGVideoItemSurface; +class QVideoRendererControl; +class QOpenGLContext; + +class QDeclarativeVideoRendererBackend : public QDeclarativeVideoBackend +{ +public: + QDeclarativeVideoRendererBackend(QDeclarativeVideoOutput *parent); + ~QDeclarativeVideoRendererBackend(); + + bool init(QMediaService *service); + void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &changeData); + void releaseSource(); + void releaseControl(); + QSize nativeSize() const; + void updateGeometry(); + QSGNode *updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data); + QAbstractVideoSurface *videoSurface() const; + QRectF adjustedViewport() const Q_DECL_OVERRIDE; + QOpenGLContext *glContext() const; + + friend class QSGVideoItemSurface; + void present(const QVideoFrame &frame); + void stop(); + +private: + QPointer<QVideoRendererControl> m_rendererControl; + QList<QSGVideoNodeFactoryInterface*> m_videoNodeFactories; + QSGVideoItemSurface *m_surface; + QOpenGLContext *m_glContext; + QVideoFrame m_frame; + bool m_frameChanged; + QSGVideoNodeFactory_I420 m_i420Factory; + QSGVideoNodeFactory_RGB m_rgbFactory; + QSGVideoNodeFactory_Texture m_textureFactory; + QMutex m_frameMutex; + QRectF m_renderedRect; // Destination pixel coordinates, clipped + QRectF m_sourceTextureRect; // Source texture coordinates +}; + +class QSGVideoItemSurface : public QAbstractVideoSurface +{ + Q_OBJECT +public: + explicit QSGVideoItemSurface(QDeclarativeVideoRendererBackend *backend, QObject *parent = 0); + ~QSGVideoItemSurface(); + QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const; + bool start(const QVideoSurfaceFormat &format); + void stop(); + bool present(const QVideoFrame &frame); + void scheduleOpenGLContextUpdate(); + +private slots: + void updateOpenGLContext(); + +private: + QDeclarativeVideoRendererBackend *m_backend; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qtmultimediaquicktools/qdeclarativevideooutput_window.cpp b/src/qtmultimediaquicktools/qdeclarativevideooutput_window.cpp new file mode 100644 index 000000000..2da63c107 --- /dev/null +++ b/src/qtmultimediaquicktools/qdeclarativevideooutput_window.cpp @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Research In Motion +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativevideooutput_window_p.h" +#include "qdeclarativevideooutput_p.h" +#include <QtQuick/qquickwindow.h> +#include <QtMultimedia/qmediaservice.h> +#include <QtMultimedia/qvideowindowcontrol.h> + +QT_BEGIN_NAMESPACE + +QDeclarativeVideoWindowBackend::QDeclarativeVideoWindowBackend(QDeclarativeVideoOutput *parent) + : QDeclarativeVideoBackend(parent), + m_visible(true) +{ +} + +QDeclarativeVideoWindowBackend::~QDeclarativeVideoWindowBackend() +{ + releaseSource(); + releaseControl(); +} + +bool QDeclarativeVideoWindowBackend::init(QMediaService *service) +{ + if (QMediaControl *control = service->requestControl(QVideoWindowControl_iid)) { + if ((m_videoWindowControl = qobject_cast<QVideoWindowControl *>(control))) { + if (q->window()) + m_videoWindowControl->setWinId(q->window()->winId()); + m_service = service; + QObject::connect(m_videoWindowControl.data(), SIGNAL(nativeSizeChanged()), + q, SLOT(_q_updateNativeSize())); + return true; + } + } + return false; +} + +void QDeclarativeVideoWindowBackend::itemChange(QQuickItem::ItemChange change, + const QQuickItem::ItemChangeData &changeData) +{ + if (!m_videoWindowControl) + return; + + switch (change) { + case QQuickItem::ItemVisibleHasChanged: + m_visible = changeData.boolValue; + updateGeometry(); + break; + case QQuickItem::ItemSceneChange: + if (changeData.window) + m_videoWindowControl->setWinId(changeData.window->winId()); + else + m_videoWindowControl->setWinId(0); + break; + default: break; + } +} + +void QDeclarativeVideoWindowBackend::releaseSource() +{ +} + +void QDeclarativeVideoWindowBackend::releaseControl() +{ + if (m_videoWindowControl) { + m_videoWindowControl->setWinId(0); + if (m_service) + m_service->releaseControl(m_videoWindowControl); + m_videoWindowControl = 0; + } +} + +QSize QDeclarativeVideoWindowBackend::nativeSize() const +{ + return m_videoWindowControl->nativeSize(); +} + +void QDeclarativeVideoWindowBackend::updateGeometry() +{ + switch (q->fillMode()) { + case QDeclarativeVideoOutput::PreserveAspectFit: + m_videoWindowControl->setAspectRatioMode(Qt::KeepAspectRatio); break; + case QDeclarativeVideoOutput::PreserveAspectCrop: + m_videoWindowControl->setAspectRatioMode(Qt::KeepAspectRatioByExpanding); break; + case QDeclarativeVideoOutput::Stretch: + m_videoWindowControl->setAspectRatioMode(Qt::IgnoreAspectRatio); break; + }; + + const QRectF canvasRect = q->mapRectToScene(QRectF(0, 0, q->width(), q->height())); + m_videoWindowControl->setDisplayRect(m_visible ? canvasRect.toAlignedRect() : QRect()); +} + +QSGNode *QDeclarativeVideoWindowBackend::updatePaintNode(QSGNode *oldNode, + QQuickItem::UpdatePaintNodeData *data) +{ + Q_UNUSED(oldNode); + Q_UNUSED(data); + m_videoWindowControl->repaint(); + return 0; +} + +QAbstractVideoSurface *QDeclarativeVideoWindowBackend::videoSurface() const +{ + return 0; +} + +QRectF QDeclarativeVideoWindowBackend::adjustedViewport() const +{ + // No viewport supported by QVideoWindowControl, so make the viewport the same size + // as the source + return QRectF(QPointF(0, 0), nativeSize()); +} + +QT_END_NAMESPACE diff --git a/src/qtmultimediaquicktools/qdeclarativevideooutput_window_p.h b/src/qtmultimediaquicktools/qdeclarativevideooutput_window_p.h new file mode 100644 index 000000000..eb7b35b85 --- /dev/null +++ b/src/qtmultimediaquicktools/qdeclarativevideooutput_window_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Research In Motion +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEVIDEOOUTPUT_WINDOW_P_H +#define QDECLARATIVEVIDEOOUTPUT_WINDOW_P_H + +#include "qdeclarativevideooutput_backend_p.h" + +QT_BEGIN_NAMESPACE + +class QVideoWindowControl; + +class QDeclarativeVideoWindowBackend : public QDeclarativeVideoBackend +{ +public: + QDeclarativeVideoWindowBackend(QDeclarativeVideoOutput *parent); + ~QDeclarativeVideoWindowBackend(); + + bool init(QMediaService *service); + void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &changeData); + void releaseSource(); + void releaseControl(); + QSize nativeSize() const; + void updateGeometry(); + QSGNode *updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data); + QAbstractVideoSurface *videoSurface() const; + QRectF adjustedViewport() const Q_DECL_OVERRIDE; + +private: + QPointer<QVideoWindowControl> m_videoWindowControl; + bool m_visible; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qtmultimediaquicktools/qsgvideonode_i420.cpp b/src/qtmultimediaquicktools/qsgvideonode_i420.cpp new file mode 100644 index 000000000..d7eb04863 --- /dev/null +++ b/src/qtmultimediaquicktools/qsgvideonode_i420.cpp @@ -0,0 +1,330 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qsgvideonode_i420.h" +#include <QtCore/qmutex.h> +#include <QtQuick/qsgtexturematerial.h> +#include <QtQuick/qsgmaterial.h> +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLFunctions> +#include <QtGui/QOpenGLShaderProgram> + +QT_BEGIN_NAMESPACE + +QList<QVideoFrame::PixelFormat> QSGVideoNodeFactory_I420::supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType) const +{ + QList<QVideoFrame::PixelFormat> 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); + ~QSGVideoMaterial_YUV420(); + + 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<const QSGVideoMaterial_YUV420 *>(other); + int d = m_textureIds[0] - m->m_textureIds[0]; + if (d) + return d; + else if ((d = m_textureIds[1] - m->m_textureIds[1]) != 0) + return d; + else + return m_textureIds[2] - m->m_textureIds[2]; + } + + void updateBlending() { + setFlag(Blending, qFuzzyCompare(m_opacity, qreal(1.0)) ? false : true); + } + + void setCurrentFrame(const QVideoFrame &frame) { + QMutexLocker lock(&m_frameMutex); + m_frame = frame; + } + + void bind(); + void bindTexture(int id, int w, int h, const uchar *bits); + + QVideoSurfaceFormat m_format; + QSize m_textureSize; + + static const uint Num_Texture_IDs = 3; + GLuint m_textureIds[Num_Texture_IDs]; + + qreal m_opacity; + QMatrix4x4 m_colorMatrix; + + QVideoFrame m_frame; + QMutex m_frameMutex; +}; + +QSGVideoMaterial_YUV420::QSGVideoMaterial_YUV420(const QVideoSurfaceFormat &format) : + m_format(format), + m_opacity(1.0) +{ + memset(m_textureIds, 0, sizeof(m_textureIds)); + + switch (format.yCbCrColorSpace()) { + case QVideoSurfaceFormat::YCbCr_JPEG: + m_colorMatrix = QMatrix4x4( + 1.0f, 0.000f, 1.402f, -0.701f, + 1.0f, -0.344f, -0.714f, 0.529f, + 1.0f, 1.772f, 0.000f, -0.886f, + 0.0f, 0.000f, 0.000f, 1.0000f); + break; + case QVideoSurfaceFormat::YCbCr_BT709: + case QVideoSurfaceFormat::YCbCr_xvYCC709: + m_colorMatrix = QMatrix4x4( + 1.164f, 0.000f, 1.793f, -0.5727f, + 1.164f, -0.534f, -0.213f, 0.3007f, + 1.164f, 2.115f, 0.000f, -1.1302f, + 0.0f, 0.000f, 0.000f, 1.0000f); + break; + default: //BT 601: + m_colorMatrix = QMatrix4x4( + 1.164f, 0.000f, 1.596f, -0.8708f, + 1.164f, -0.392f, -0.813f, 0.5296f, + 1.164f, 2.017f, 0.000f, -1.081f, + 0.0f, 0.000f, 0.000f, 1.0000f); + } + + setFlag(Blending, false); +} + +QSGVideoMaterial_YUV420::~QSGVideoMaterial_YUV420() +{ + if (!m_textureSize.isEmpty()) + glDeleteTextures(Num_Texture_IDs, m_textureIds); +} + +void QSGVideoMaterial_YUV420::bind() +{ + QOpenGLFunctions *functions = QOpenGLContext::currentContext()->functions(); + + QMutexLocker lock(&m_frameMutex); + if (m_frame.isValid()) { + if (m_frame.map(QAbstractVideoBuffer::ReadOnly)) { + int fw = m_frame.width(); + int fh = m_frame.height(); + + // Frame has changed size, recreate textures... + if (m_textureSize != m_frame.size()) { + if (!m_textureSize.isEmpty()) + glDeleteTextures(Num_Texture_IDs, m_textureIds); + glGenTextures(Num_Texture_IDs, m_textureIds); + m_textureSize = m_frame.size(); + } + + const uchar *bits = m_frame.bits(); + int bpl = m_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); + + + functions->glActiveTexture(GL_TEXTURE1); + bindTexture(m_textureIds[1], fw/2, fh / 2, bits + offsetU); + functions->glActiveTexture(GL_TEXTURE2); + bindTexture(m_textureIds[2], fw/2, fh / 2, bits + offsetV); + functions->glActiveTexture(GL_TEXTURE0); // Finish with 0 as default texture unit + bindTexture(m_textureIds[0], fw, fh, bits); + + m_frame.unmap(); + } + + m_frame = QVideoFrame(); + } else { + functions->glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, m_textureIds[1]); + functions->glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, m_textureIds[2]); + functions->glActiveTexture(GL_TEXTURE0); // Finish with 0 as default texture unit + glBindTexture(GL_TEXTURE_2D, m_textureIds[0]); + } +} + +void QSGVideoMaterial_YUV420::bindTexture(int id, int w, int h, const uchar *bits) +{ + 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); +} + +QSGVideoNode_I420::QSGVideoNode_I420(const QVideoSurfaceFormat &format) : + m_format(format) +{ + setFlag(QSGNode::OwnsMaterial); + m_material = new QSGVideoMaterial_YUV420(format); + setMaterial(m_material); +} + +QSGVideoNode_I420::~QSGVideoNode_I420() +{ +} + +void QSGVideoNode_I420::setCurrentFrame(const QVideoFrame &frame) +{ + m_material->setCurrentFrame(frame); + markDirty(DirtyMaterial); +} + + +void QSGVideoMaterialShader_YUV420::updateState(const RenderState &state, + QSGMaterial *newMaterial, + QSGMaterial *oldMaterial) +{ + Q_UNUSED(oldMaterial); + + QSGVideoMaterial_YUV420 *mat = static_cast<QSGVideoMaterial_YUV420 *>(newMaterial); + program()->setUniformValue(m_id_yTexture, 0); + program()->setUniformValue(m_id_uTexture, 1); + program()->setUniformValue(m_id_vTexture, 2); + + mat->bind(); + + program()->setUniformValue(m_id_colorMatrix, mat->m_colorMatrix); + if (state.isOpacityDirty()) { + mat->m_opacity = state.opacity(); + program()->setUniformValue(m_id_opacity, GLfloat(mat->m_opacity)); + } + + if (state.isMatrixDirty()) + program()->setUniformValue(m_id_matrix, state.combinedMatrix()); +} + +QT_END_NAMESPACE diff --git a/src/qtmultimediaquicktools/qsgvideonode_i420.h b/src/qtmultimediaquicktools/qsgvideonode_i420.h new file mode 100644 index 000000000..a9def5514 --- /dev/null +++ b/src/qtmultimediaquicktools/qsgvideonode_i420.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGVIDEONODE_I420_H +#define QSGVIDEONODE_I420_H + +#include <private/qsgvideonode_p.h> +#include <QtMultimedia/qvideosurfaceformat.h> + +QT_BEGIN_NAMESPACE + +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); + + QVideoSurfaceFormat m_format; + QSGVideoMaterial_YUV420 *m_material; +}; + +class QSGVideoNodeFactory_I420 : public QSGVideoNodeFactoryInterface { +public: + QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const; + QSGVideoNode *createNode(const QVideoSurfaceFormat &format); +}; + +QT_END_NAMESPACE + +#endif // QSGVIDEONODE_I420_H diff --git a/src/qtmultimediaquicktools/qsgvideonode_rgb.cpp b/src/qtmultimediaquicktools/qsgvideonode_rgb.cpp new file mode 100644 index 000000000..fbe60c9a7 --- /dev/null +++ b/src/qtmultimediaquicktools/qsgvideonode_rgb.cpp @@ -0,0 +1,289 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qsgvideonode_rgb.h" +#include <QtQuick/qsgtexturematerial.h> +#include <QtQuick/qsgmaterial.h> +#include <QtCore/qmutex.h> +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLFunctions> +#include <QtGui/QOpenGLShaderProgram> + +QT_BEGIN_NAMESPACE + +QList<QVideoFrame::PixelFormat> QSGVideoNodeFactory_RGB::supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType) const +{ + QList<QVideoFrame::PixelFormat> pixelFormats; + + if (handleType == QAbstractVideoBuffer::NoHandle) { + pixelFormats.append(QVideoFrame::Format_RGB565); + pixelFormats.append(QVideoFrame::Format_RGB32); + pixelFormats.append(QVideoFrame::Format_ARGB32); + pixelFormats.append(QVideoFrame::Format_BGR32); + pixelFormats.append(QVideoFrame::Format_BGRA32); + } + + return pixelFormats; +} + +QSGVideoNode *QSGVideoNodeFactory_RGB::createNode(const QVideoSurfaceFormat &format) +{ + if (supportedPixelFormats(format.handleType()).contains(format.pixelFormat())) + return new QSGVideoNode_RGB(format); + + return 0; +} + + +class QSGVideoMaterialShader_RGB : public QSGMaterialShader +{ +public: + QSGVideoMaterialShader_RGB(QVideoFrame::PixelFormat pixelFormat) + : QSGMaterialShader(), + m_id_matrix(-1), + m_id_rgbTexture(-1), + m_id_opacity(-1), + m_pixelFormat(pixelFormat) + { + } + + 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 rgbTexture;" + "uniform lowp float opacity;" + "" + "varying highp vec2 qt_TexCoord;" + "" + "void main()" + "{" + " gl_FragColor = texture2D(rgbTexture, qt_TexCoord) * opacity;" + "}"; + + static const char *colorsSwapShader = + "uniform sampler2D rgbTexture;" + "uniform lowp float opacity;" + "" + "varying highp vec2 qt_TexCoord;" + "" + "void main()" + "{" + " gl_FragColor = vec4(texture2D(rgbTexture, qt_TexCoord).bgr, 1.0) * opacity;" + "}"; + + + switch (m_pixelFormat) { + case QVideoFrame::Format_RGB32: + case QVideoFrame::Format_ARGB32: + return colorsSwapShader; + default: + return shader; + } + } + + virtual void initialize() { + m_id_matrix = program()->uniformLocation("qt_Matrix"); + m_id_rgbTexture = program()->uniformLocation("rgbTexture"); + m_id_opacity = program()->uniformLocation("opacity"); + } + + int m_id_matrix; + int m_id_rgbTexture; + int m_id_opacity; + QVideoFrame::PixelFormat m_pixelFormat; +}; + + +class QSGVideoMaterial_RGB : public QSGMaterial +{ +public: + QSGVideoMaterial_RGB(const QVideoSurfaceFormat &format) : + m_format(format), + m_textureId(0), + m_opacity(1.0) + { + setFlag(Blending, false); + } + + ~QSGVideoMaterial_RGB() + { + if (m_textureId) + glDeleteTextures(1, &m_textureId); + } + + virtual QSGMaterialType *type() const { + static QSGMaterialType theType; + return &theType; + } + + virtual QSGMaterialShader *createShader() const { + return new QSGVideoMaterialShader_RGB(m_format.pixelFormat()); + } + + virtual int compare(const QSGMaterial *other) const { + const QSGVideoMaterial_RGB *m = static_cast<const QSGVideoMaterial_RGB *>(other); + return m_textureId - m->m_textureId; + } + + void updateBlending() { + setFlag(Blending, qFuzzyCompare(m_opacity, qreal(1.0)) ? false : true); + } + + void setVideoFrame(const QVideoFrame &frame) { + QMutexLocker lock(&m_frameMutex); + m_frame = frame; + } + + void bind() + { + QOpenGLFunctions *functions = QOpenGLContext::currentContext()->functions(); + + QMutexLocker lock(&m_frameMutex); + if (m_frame.isValid()) { + if (m_frame.map(QAbstractVideoBuffer::ReadOnly)) { + if (m_textureSize != m_frame.size()) { + if (!m_textureSize.isEmpty()) + glDeleteTextures(1, &m_textureId); + glGenTextures(1, &m_textureId); + m_textureSize = m_frame.size(); + } + + GLint dataType = GL_UNSIGNED_BYTE; + GLint dataFormat = GL_RGBA; + + if (m_frame.pixelFormat() == QVideoFrame::Format_RGB565) { + dataType = GL_UNSIGNED_SHORT_5_6_5; + dataFormat = GL_RGB; + } + + functions->glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_textureId); + glTexImage2D(GL_TEXTURE_2D, 0, dataFormat, + m_textureSize.width(), m_textureSize.height(), + 0, dataFormat, dataType, m_frame.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); + + m_frame.unmap(); + } + m_frame = QVideoFrame(); + } else { + functions->glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_textureId); + } + } + + QVideoFrame m_frame; + QMutex m_frameMutex; + QSize m_textureSize; + QVideoSurfaceFormat m_format; + GLuint m_textureId; + qreal m_opacity; +}; + + +QSGVideoNode_RGB::QSGVideoNode_RGB(const QVideoSurfaceFormat &format) : + m_format(format) +{ + setFlag(QSGNode::OwnsMaterial); + m_material = new QSGVideoMaterial_RGB(format); + setMaterial(m_material); +} + +QSGVideoNode_RGB::~QSGVideoNode_RGB() +{ +} + +void QSGVideoNode_RGB::setCurrentFrame(const QVideoFrame &frame) +{ + m_material->setVideoFrame(frame); + markDirty(DirtyMaterial); +} + +void QSGVideoMaterialShader_RGB::updateState(const RenderState &state, + QSGMaterial *newMaterial, + QSGMaterial *oldMaterial) +{ + Q_UNUSED(oldMaterial); + QSGVideoMaterial_RGB *mat = static_cast<QSGVideoMaterial_RGB *>(newMaterial); + program()->setUniformValue(m_id_rgbTexture, 0); + + mat->bind(); + + if (state.isOpacityDirty()) { + mat->m_opacity = state.opacity(); + mat->updateBlending(); + program()->setUniformValue(m_id_opacity, GLfloat(mat->m_opacity)); + } + + if (state.isMatrixDirty()) + program()->setUniformValue(m_id_matrix, state.combinedMatrix()); +} + +QT_END_NAMESPACE diff --git a/src/qtmultimediaquicktools/qsgvideonode_rgb.h b/src/qtmultimediaquicktools/qsgvideonode_rgb.h new file mode 100644 index 000000000..141a00e9f --- /dev/null +++ b/src/qtmultimediaquicktools/qsgvideonode_rgb.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGVIDEONODE_RGB_H +#define QSGVIDEONODE_RGB_H + +#include <private/qsgvideonode_p.h> +#include <QtMultimedia/qvideosurfaceformat.h> + +QT_BEGIN_NAMESPACE + +class QSGVideoMaterial_RGB; + +class QSGVideoNode_RGB : public QSGVideoNode +{ +public: + QSGVideoNode_RGB(const QVideoSurfaceFormat &format); + ~QSGVideoNode_RGB(); + + virtual QVideoFrame::PixelFormat pixelFormat() const { + return m_format.pixelFormat(); + } + void setCurrentFrame(const QVideoFrame &frame); + +private: + QVideoSurfaceFormat m_format; + QSGVideoMaterial_RGB *m_material; + QVideoFrame m_frame; +}; + +class QSGVideoNodeFactory_RGB : public QSGVideoNodeFactoryInterface { +public: + QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const; + QSGVideoNode *createNode(const QVideoSurfaceFormat &format); +}; + +QT_END_NAMESPACE + +#endif // QSGVIDEONODE_RGB_H diff --git a/src/qtmultimediaquicktools/qsgvideonode_texture.cpp b/src/qtmultimediaquicktools/qsgvideonode_texture.cpp new file mode 100644 index 000000000..2320387b1 --- /dev/null +++ b/src/qtmultimediaquicktools/qsgvideonode_texture.cpp @@ -0,0 +1,271 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qsgvideonode_texture.h" +#include <QtQuick/qsgtexturematerial.h> +#include <QtQuick/qsgmaterial.h> +#include <QtCore/qmutex.h> +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLFunctions> +#include <QtGui/QOpenGLShaderProgram> + +QT_BEGIN_NAMESPACE + +QList<QVideoFrame::PixelFormat> QSGVideoNodeFactory_Texture::supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType) const +{ + QList<QVideoFrame::PixelFormat> pixelFormats; + + if (handleType == QAbstractVideoBuffer::GLTextureHandle) { + pixelFormats.append(QVideoFrame::Format_RGB565); + pixelFormats.append(QVideoFrame::Format_RGB32); + pixelFormats.append(QVideoFrame::Format_ARGB32); + pixelFormats.append(QVideoFrame::Format_BGR32); + pixelFormats.append(QVideoFrame::Format_BGRA32); + } + + return pixelFormats; +} + +QSGVideoNode *QSGVideoNodeFactory_Texture::createNode(const QVideoSurfaceFormat &format) +{ + if (supportedPixelFormats(format.handleType()).contains(format.pixelFormat())) + return new QSGVideoNode_Texture(format); + + return 0; +} + + +class QSGVideoMaterialShader_Texture : public QSGMaterialShader +{ +public: + QSGVideoMaterialShader_Texture(QVideoFrame::PixelFormat pixelFormat) + : QSGMaterialShader(), + m_pixelFormat(pixelFormat) + { + } + + 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 rgbTexture;" + "uniform lowp float opacity;" + "" + "varying highp vec2 qt_TexCoord;" + "" + "void main()" + "{" + " gl_FragColor = texture2D(rgbTexture, qt_TexCoord) * opacity;" + "}"; + +#ifndef QT_OPENGL_ES_2_ANGLE + static const char *colorsSwapShader = + "uniform sampler2D rgbTexture;" + "uniform lowp float opacity;" + "" + "varying highp vec2 qt_TexCoord;" + "" + "void main()" + "{" + " gl_FragColor = vec4(texture2D(rgbTexture, qt_TexCoord).bgr, 1.0) * opacity;" + "}"; + + + switch (m_pixelFormat) { + case QVideoFrame::Format_RGB32: + case QVideoFrame::Format_ARGB32: + return colorsSwapShader; + default: + return shader; + } +#else + return shader; +#endif + } + + virtual void initialize() { + m_id_matrix = program()->uniformLocation("qt_Matrix"); + m_id_Texture = program()->uniformLocation("rgbTexture"); + m_id_opacity = program()->uniformLocation("opacity"); + } + + int m_id_matrix; + int m_id_Texture; + int m_id_opacity; + QVideoFrame::PixelFormat m_pixelFormat; +}; + + +class QSGVideoMaterial_Texture : public QSGMaterial +{ +public: + QSGVideoMaterial_Texture(const QVideoSurfaceFormat &format) : + m_format(format), + m_textureId(0), + m_opacity(1.0) + { + setFlag(Blending, false); + } + + ~QSGVideoMaterial_Texture() + { + m_frame = QVideoFrame(); + } + + virtual QSGMaterialType *type() const { + static QSGMaterialType theType; + return &theType; + } + + virtual QSGMaterialShader *createShader() const { + return new QSGVideoMaterialShader_Texture(m_format.pixelFormat()); + } + + virtual int compare(const QSGMaterial *other) const { + const QSGVideoMaterial_Texture *m = static_cast<const QSGVideoMaterial_Texture *>(other); + int diff = m_textureId - m->m_textureId; + if (diff) + return diff; + + diff = m_format.pixelFormat() - m->m_format.pixelFormat(); + if (diff) + return diff; + + return (m_opacity > m->m_opacity) ? 1 : -1; + } + + void updateBlending() { + setFlag(Blending, qFuzzyCompare(m_opacity, qreal(1.0)) ? false : true); + } + + void setVideoFrame(const QVideoFrame &frame) { + QMutexLocker lock(&m_frameMutex); + m_frame = frame; + } + + void bind() + { + QMutexLocker lock(&m_frameMutex); + if (m_frame.isValid()) { + m_textureId = m_frame.handle().toUInt(); + glBindTexture(GL_TEXTURE_2D, m_textureId); + + 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); + } else { + m_textureId = 0; + } + } + + QVideoFrame m_frame; + QMutex m_frameMutex; + QSize m_textureSize; + QVideoSurfaceFormat m_format; + GLuint m_textureId; + qreal m_opacity; +}; + + +QSGVideoNode_Texture::QSGVideoNode_Texture(const QVideoSurfaceFormat &format) : + m_format(format) +{ + setFlag(QSGNode::OwnsMaterial); + m_material = new QSGVideoMaterial_Texture(format); + setMaterial(m_material); +} + +QSGVideoNode_Texture::~QSGVideoNode_Texture() +{ +} + +void QSGVideoNode_Texture::setCurrentFrame(const QVideoFrame &frame) +{ + m_material->setVideoFrame(frame); + markDirty(DirtyMaterial); +} + +void QSGVideoMaterialShader_Texture::updateState(const RenderState &state, + QSGMaterial *newMaterial, + QSGMaterial *oldMaterial) +{ + Q_UNUSED(oldMaterial); + QSGVideoMaterial_Texture *mat = static_cast<QSGVideoMaterial_Texture *>(newMaterial); + program()->setUniformValue(m_id_Texture, 0); + + mat->bind(); + + if (state.isOpacityDirty()) { + mat->m_opacity = state.opacity(); + mat->updateBlending(); + program()->setUniformValue(m_id_opacity, GLfloat(mat->m_opacity)); + } + + if (state.isMatrixDirty()) + program()->setUniformValue(m_id_matrix, state.combinedMatrix()); +} + +QT_END_NAMESPACE diff --git a/src/qtmultimediaquicktools/qsgvideonode_texture.h b/src/qtmultimediaquicktools/qsgvideonode_texture.h new file mode 100644 index 000000000..42dadbcbb --- /dev/null +++ b/src/qtmultimediaquicktools/qsgvideonode_texture.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGVIDEONODE_TEXTURE_H +#define QSGVIDEONODE_TEXTURE_H + +#include <private/qsgvideonode_p.h> +#include <QtMultimedia/qvideosurfaceformat.h> + +QT_BEGIN_NAMESPACE + +class QSGVideoMaterial_Texture; + +class QSGVideoNode_Texture : public QSGVideoNode +{ +public: + QSGVideoNode_Texture(const QVideoSurfaceFormat &format); + ~QSGVideoNode_Texture(); + + virtual QVideoFrame::PixelFormat pixelFormat() const { + return m_format.pixelFormat(); + } + void setCurrentFrame(const QVideoFrame &frame); + +private: + QVideoSurfaceFormat m_format; + QSGVideoMaterial_Texture *m_material; + QVideoFrame m_frame; +}; + +class QSGVideoNodeFactory_Texture : public QSGVideoNodeFactoryInterface { +public: + QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const; + QSGVideoNode *createNode(const QVideoSurfaceFormat &format); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qtmultimediaquicktools/qtmultimediaquicktools.pro b/src/qtmultimediaquicktools/qtmultimediaquicktools.pro index da4d0dc1a..6fd38be86 100644 --- a/src/qtmultimediaquicktools/qtmultimediaquicktools.pro +++ b/src/qtmultimediaquicktools/qtmultimediaquicktools.pro @@ -11,10 +11,24 @@ DEFINES += QT_BUILD_QTMM_QUICK_LIB INCLUDEPATH += ../multimedia/qtmultimediaquicktools_headers/ PRIVATE_HEADERS += \ + ../multimedia/qtmultimediaquicktools_headers/qdeclarativevideooutput_p.h \ + ../multimedia/qtmultimediaquicktools_headers/qdeclarativevideooutput_backend_p.h \ ../multimedia/qtmultimediaquicktools_headers/qsgvideonode_p.h \ ../multimedia/qtmultimediaquicktools_headers/qtmultimediaquickdefs_p.h SOURCES += \ - qsgvideonode_p.cpp + qsgvideonode_p.cpp \ + qdeclarativevideooutput.cpp \ + qdeclarativevideooutput_render.cpp \ + qdeclarativevideooutput_window.cpp \ + qsgvideonode_i420.cpp \ + qsgvideonode_rgb.cpp \ + qsgvideonode_texture.cpp -HEADERS += $$PRIVATE_HEADERS +HEADERS += \ + $$PRIVATE_HEADERS \ + qdeclarativevideooutput_render_p.h \ + qdeclarativevideooutput_window_p.h \ + qsgvideonode_i420.h \ + qsgvideonode_rgb.h \ + qsgvideonode_texture.h |