Support QVideoWindowControl in the QtQuick Video element.

Change-Id: I953899a3ec92856955d36528057b0d45f9c26394
Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
Reviewed-by: Dmytro Poplavskiy <dmytro.poplavskiy@nokia.com>
This commit is contained in:
Thomas McGuire
2012-04-05 15:53:44 +02:00
committed by Qt by Nokia
parent 926049cb15
commit 025f4d2ee1
13 changed files with 1123 additions and 257 deletions

View File

@@ -14,6 +14,9 @@ HEADERS += \
qdeclarativeaudio_p.h \
qdeclarativemediametadata_p.h \
qdeclarativevideooutput_p.h \
qdeclarativevideooutput_backend_p.h \
qdeclarativevideooutput_render_p.h \
qdeclarativevideooutput_window_p.h \
qsgvideonode_i420.h \
qsgvideonode_rgb.h \
qdeclarativeradio_p.h \
@@ -35,6 +38,8 @@ SOURCES += \
multimedia.cpp \
qdeclarativeaudio.cpp \
qdeclarativevideooutput.cpp \
qdeclarativevideooutput_render.cpp \
qdeclarativevideooutput_window.cpp \
qsgvideonode_i420.cpp \
qsgvideonode_rgb.cpp \
qdeclarativeradio.cpp \

View File

@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Copyright (C) 2012 Research In Motion
** Contact: http://www.qt-project.org/
**
** This file is part of the Qt Toolkit.
@@ -40,83 +41,15 @@
****************************************************************************/
#include "qdeclarativevideooutput_p.h"
#include "qsgvideonode_i420.h"
#include "qsgvideonode_rgb.h"
#include <QtQuick/QQuickItem>
#include <QtMultimedia/QAbstractVideoSurface>
#include "qdeclarativevideooutput_render_p.h"
#include "qdeclarativevideooutput_window_p.h"
#include <QtMultimedia/qmediaobject.h>
#include <QtMultimedia/qmediaservice.h>
#include <QtMultimedia/qvideorenderercontrol.h>
#include <QtMultimedia/qvideosurfaceformat.h>
#include <private/qmediapluginloader_p.h>
#include <QtCore/qmetaobject.h>
//#define DEBUG_VIDEOITEM
Q_DECLARE_METATYPE(QAbstractVideoSurface*)
QT_BEGIN_NAMESPACE
Q_GLOBAL_STATIC_WITH_ARGS(QMediaPluginLoader, videoNodeFactoryLoader,
(QSGVideoNodeFactoryInterface_iid, QLatin1String("video/videonode"), Qt::CaseInsensitive))
class QSGVideoItemSurface : public QAbstractVideoSurface
{
public:
QSGVideoItemSurface(QDeclarativeVideoOutput *item, QObject *parent = 0) :
QAbstractVideoSurface(parent),
m_item(item)
{
}
~QSGVideoItemSurface()
{
}
QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
{
QList<QVideoFrame::PixelFormat> formats;
foreach (QSGVideoNodeFactoryInterface* 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);
}
void stop()
{
m_item->stop();
QAbstractVideoSurface::stop();
}
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.
@@ -155,6 +88,17 @@ private:
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
@@ -185,32 +129,13 @@ QDeclarativeVideoOutput::QDeclarativeVideoOutput(QQuickItem *parent) :
m_orientation(0)
{
setFlag(ItemHasContents, true);
m_surface = new QSGVideoItemSurface(this);
connect(m_surface, SIGNAL(surfaceFormatChanged(QVideoSurfaceFormat)),
this, SLOT(_q_updateNativeSize(QVideoSurfaceFormat)), 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);
}
QDeclarativeVideoOutput::~QDeclarativeVideoOutput()
{
if (m_source && m_sourceType == VideoSurfaceSource) {
if (m_source.data()->property("videoSurface").value<QAbstractVideoSurface*>() == m_surface)
m_source.data()->setProperty("videoSurface", QVariant::fromValue<QAbstractVideoSurface*>(0));
}
m_backend.reset();
m_source.clear();
_q_updateMediaObject();
delete m_surface;
}
/*!
@@ -238,12 +163,8 @@ void QDeclarativeVideoOutput::setSource(QObject *source)
if (m_source && m_sourceType == MediaObjectSource)
disconnect(m_source.data(), 0, this, SLOT(_q_updateMediaObject()));
if (m_source && m_sourceType == VideoSurfaceSource) {
if (m_source.data()->property("videoSurface").value<QAbstractVideoSurface*>() == m_surface)
m_source.data()->setProperty("videoSurface", QVariant::fromValue<QAbstractVideoSurface*>(0));
}
m_surface->stop();
if (m_backend)
m_backend->releaseSource();
m_source = source;
@@ -263,7 +184,14 @@ void QDeclarativeVideoOutput::setSource(QObject *source)
}
m_sourceType = MediaObjectSource;
} else if (metaObject->indexOfProperty("videoSurface") != -1) {
m_source.data()->setProperty("videoSurface", QVariant::fromValue<QAbstractVideoSurface*>(m_surface));
// Make sure our backend is a QDeclarativeVideoRendererBackend
m_backend.reset();
createBackend(0);
Q_ASSERT(m_backend && dynamic_cast<QDeclarativeVideoRendererBackend *>(m_backend.data()));
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;
@@ -276,6 +204,29 @@ void QDeclarativeVideoOutput::setSource(QObject *source)
emit sourceChanged();
}
bool QDeclarativeVideoOutput::createBackend(QMediaService *service)
{
bool backendAvailable = false;
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;
@@ -290,66 +241,22 @@ void QDeclarativeVideoOutput::_q_updateMediaObject()
if (m_mediaObject.data() == mediaObject)
return;
if (m_rendererControl) {
m_rendererControl.data()->setSurface(0);
m_service.data()->releaseControl(m_rendererControl.data());
}
if (m_sourceType != VideoSurfaceSource)
m_backend.reset();
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<QVideoRendererControl *>(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);
}
if (createBackend(service)) {
m_service = service;
m_mediaObject = mediaObject;
}
}
}
}
void QDeclarativeVideoOutput::present(const QVideoFrame &frame)
{
m_frameMutex.lock();
m_frame = frame;
m_frameMutex.unlock();
update();
}
void QDeclarativeVideoOutput::stop()
{
present(QVideoFrame());
}
/*
* Helper - returns true if the given orientation has the same aspect as the default (e.g. 180*n)
*/
static inline bool qIsDefaultAspect(int o)
{
return (o % 180) == 0;
}
/*
* Return the orientation normailized to 0-359
*/
static inline int qNormalizedOrientation(int o)
{
// Negative orientations give negative results
int o2 = o % 360;
if (o2 < 0)
o2 += 360;
return o2;
}
/*!
\qmlproperty enumeration QtMultimedia5::VideoOutput::fillMode
@@ -381,9 +288,12 @@ void QDeclarativeVideoOutput::setFillMode(FillMode mode)
emit fillModeChanged(mode);
}
void QDeclarativeVideoOutput::_q_updateNativeSize(const QVideoSurfaceFormat &format)
void QDeclarativeVideoOutput::_q_updateNativeSize()
{
QSize size = format.sizeHint();
if (!m_backend)
return;
QSize size = m_backend->nativeSize();
if (!qIsDefaultAspect(m_orientation)) {
size.transpose();
}
@@ -403,57 +313,35 @@ void QDeclarativeVideoOutput::_q_updateNativeSize(const QVideoSurfaceFormat &for
/* Based on fill mode and our size, figure out the source/dest rects */
void QDeclarativeVideoOutput::_q_updateGeometry()
{
QRectF rect(0, 0, width(), height());
const QRectF rect(0, 0, width(), height());
const QRectF absoluteRect(x(), y(), width(), height());
if (!m_geometryDirty && m_lastSize == rect)
if (!m_geometryDirty && m_lastRect == absoluteRect)
return;
QRectF oldContentRect(m_contentRect);
m_geometryDirty = false;
m_lastSize = rect;
m_lastRect = absoluteRect;
if (m_nativeSize.isEmpty()) {
//this is necessary for item to receive the
//first paint event and configure video surface.
m_renderedRect = rect;
m_contentRect = rect;
m_sourceTextureRect = QRectF(0, 0, 1, 1);
} else if (m_fillMode == Stretch) {
m_renderedRect = rect;
m_contentRect = rect;
m_sourceTextureRect = QRectF(0, 0, 1, 1);
} else if (m_fillMode == PreserveAspectFit) {
QSizeF size = m_nativeSize;
size.scale(rect.size(), Qt::KeepAspectRatio);
m_renderedRect = QRectF(0, 0, size.width(), size.height());
m_renderedRect.moveCenter(rect.center());
m_contentRect = m_renderedRect;
m_sourceTextureRect = QRectF(0, 0, 1, 1);
} else if (m_fillMode == PreserveAspectCrop) {
m_renderedRect = rect;
} else if (m_fillMode == PreserveAspectFit || m_fillMode == PreserveAspectCrop) {
QSizeF scaled = m_nativeSize;
scaled.scale(rect.size(), Qt::KeepAspectRatioByExpanding);
scaled.scale(rect.size(), m_fillMode == PreserveAspectFit ?
Qt::KeepAspectRatio : Qt::KeepAspectRatioByExpanding);
m_contentRect = QRectF(QPointF(), scaled);
m_contentRect.moveCenter(rect.center());
if (qIsDefaultAspect(m_orientation)) {
m_sourceTextureRect = QRectF((-m_contentRect.left()) / m_contentRect.width(),
(-m_contentRect.top()) / m_contentRect.height(),
rect.width() / m_contentRect.width(),
rect.height() / m_contentRect.height());
} else {
m_sourceTextureRect = QRectF((-m_contentRect.top()) / m_contentRect.height(),
(-m_contentRect.left()) / m_contentRect.width(),
rect.height() / m_contentRect.height(),
rect.width() / m_contentRect.width());
}
}
if (m_backend)
m_backend->updateGeometry();
if (m_contentRect != oldContentRect)
emit contentRectChanged();
}
@@ -707,6 +595,11 @@ QRectF QDeclarativeVideoOutput::mapRectToSourceNormalized(const QRectF &rectangl
mapPointToSourceNormalized(rectangle.bottomRight())).normalized();
}
QDeclarativeVideoOutput::SourceType QDeclarativeVideoOutput::sourceType() const
{
return m_sourceType;
}
/*!
\qmlmethod QPointF QtMultimedia5::VideoOutput::mapPointToItem(const QPointF &point) const
@@ -747,44 +640,33 @@ QRectF QDeclarativeVideoOutput::mapRectToItem(const QRectF &rectangle) const
mapPointToItem(rectangle.bottomRight())).normalized();
}
QSGNode *QDeclarativeVideoOutput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
QSGNode *QDeclarativeVideoOutput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
{
QSGVideoNode *videoNode = static_cast<QSGVideoNode *>(oldNode);
QMutexLocker lock(&m_frameMutex);
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 (QSGVideoNodeFactoryInterface* factory, m_videoNodeFactories) {
videoNode = factory->createNode(m_surface->surfaceFormat());
if (videoNode)
break;
}
}
if (videoNode == 0)
return 0;
_q_updateGeometry();
// Negative rotations need lots of %360
videoNode->setTexturedRectGeometry(m_renderedRect, m_sourceTextureRect, qNormalizedOrientation(m_orientation));
videoNode->setCurrentFrame(m_frame);
return videoNode;
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);
// 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

View File

@@ -0,0 +1,108 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Copyright (C) 2012 Research In Motion
** Contact: http://www.qt-project.org/
**
** 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_BACKEND_P_H
#define QDECLARATIVEVIDEOOUTPUT_BACKEND_P_H
#include <QtCore/qpointer.h>
#include <QtCore/qsize.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qsgnode.h>
QT_BEGIN_NAMESPACE
class QAbstractVideoSurface;
class QDeclarativeVideoOutput;
class QMediaService;
class QDeclarativeVideoBackend
{
public:
explicit QDeclarativeVideoBackend(QDeclarativeVideoOutput *parent)
: q(parent)
{}
virtual ~QDeclarativeVideoBackend()
{}
virtual bool init(QMediaService *service) = 0;
virtual void releaseSource() = 0;
virtual void releaseControl() = 0;
virtual void itemChange(QQuickItem::ItemChange change,
const QQuickItem::ItemChangeData &changeData) = 0;
virtual QSize nativeSize() const = 0;
virtual void updateGeometry() = 0;
virtual QSGNode *updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) = 0;
virtual QAbstractVideoSurface *videoSurface() const = 0;
protected:
QDeclarativeVideoOutput *q;
QPointer<QMediaService> m_service;
};
/*
* Helper - returns true if the given orientation has the same aspect as the default (e.g. 180*n)
*/
namespace {
inline bool qIsDefaultAspect(int o)
{
return (o % 180) == 0;
}
/*
* Return the orientation normalized to 0-359
*/
inline int qNormalizedOrientation(int o)
{
// Negative orientations give negative results
int o2 = o % 360;
if (o2 < 0)
o2 += 360;
return o2;
}
}
QT_END_NAMESPACE
#endif

View File

@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Copyright (C) 2012 Research In Motion
** Contact: http://www.qt-project.org/
**
** This file is part of the Qt Toolkit.
@@ -42,29 +43,15 @@
#ifndef QDECLARATIVEVIDEOOUTPUT_P_H
#define QDECLARATIVEVIDEOOUTPUT_P_H
#include <QtCore/QRectF>
#include <QtQuick/QQuickItem>
#include <QtMultimedia/qvideoframe.h>
#include <QtMultimedia/qmediaobject.h>
#include <QtCore/qrect.h>
#include <QtCore/qsharedpointer.h>
#include <QtCore/qmutex.h>
#include <private/qsgvideonode_p.h>
#include "qsgvideonode_i420.h"
#include "qsgvideonode_rgb.h"
#include <QtQuick/qquickitem.h>
QT_BEGIN_NAMESPACE
class QSGVideoItemSurface;
class QVideoRendererControl;
class QMediaObject;
class QMediaService;
class QVideoSurfaceFormat;
class QDeclarativeVideoBackend;
class QDeclarativeVideoOutput : public QQuickItem
{
@@ -109,6 +96,13 @@ public:
Q_INVOKABLE QPointF mapPointToSourceNormalized(const QPointF &point) const;
Q_INVOKABLE QRectF mapRectToSourceNormalized(const QRectF &rectangle) const;
enum SourceType {
NoSource,
MediaObjectSource,
VideoSurfaceSource
};
SourceType sourceType() const;
Q_SIGNALS:
void sourceChanged();
void fillModeChanged(QDeclarativeVideoOutput::FillMode);
@@ -118,48 +112,32 @@ Q_SIGNALS:
protected:
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
void itemChange(ItemChange change, const ItemChangeData &changeData);
void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
private Q_SLOTS:
void _q_updateMediaObject();
void _q_updateNativeSize(const QVideoSurfaceFormat&);
void _q_updateNativeSize();
void _q_updateGeometry();
private:
enum SourceType {
NoSource,
MediaObjectSource,
VideoSurfaceSource
};
void present(const QVideoFrame &frame);
void stop();
friend class QSGVideoItemSurface;
bool createBackend(QMediaService *service);
SourceType m_sourceType;
QWeakPointer<QObject> m_source;
QWeakPointer<QMediaObject> m_mediaObject;
QWeakPointer<QMediaService> m_service;
QWeakPointer<QVideoRendererControl> m_rendererControl;
QList<QSGVideoNodeFactoryInterface*> m_videoNodeFactories;
QSGVideoItemSurface *m_surface;
QVideoFrame m_frame;
FillMode m_fillMode;
QSize m_nativeSize;
QSGVideoNodeFactory_I420 m_i420Factory;
QSGVideoNodeFactory_RGB m_rgbFactory;
bool m_geometryDirty;
QRectF m_lastSize; // Cache of last size to avoid recalculating geometry
QRectF m_renderedRect; // Destination pixel coordinates, clipped
QRectF m_lastRect; // Cache of last rect to avoid recalculating geometry
QRectF m_contentRect; // Destination pixel coordinates, unclipped
QRectF m_sourceTextureRect; // Source texture coordinates
int m_orientation;
QMutex m_frameMutex;
QScopedPointer<QDeclarativeVideoBackend> m_backend;
};
QT_END_NAMESPACE

View File

@@ -0,0 +1,268 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Copyright (C) 2012 Research In Motion
** Contact: http://www.qt-project.org/
**
** 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_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>
QT_BEGIN_NAMESPACE
Q_GLOBAL_STATIC_WITH_ARGS(QMediaPluginLoader, videoNodeFactoryLoader,
(QSGVideoNodeFactoryInterface_iid, QLatin1String("video/videonode"), Qt::CaseInsensitive))
QDeclarativeVideoRendererBackend::QDeclarativeVideoRendererBackend(QDeclarativeVideoOutput *parent)
: QDeclarativeVideoBackend(parent)
{
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);
}
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 rect(0, 0, q->width(), q->height());
if (nativeSize().isEmpty()) {
m_renderedRect = rect;
m_sourceTextureRect = QRectF(0, 0, 1, 1);
} else if (q->fillMode() == QDeclarativeVideoOutput::Stretch) {
m_renderedRect = rect;
m_sourceTextureRect = QRectF(0, 0, 1, 1);
} else if (q->fillMode() == QDeclarativeVideoOutput::PreserveAspectFit) {
m_sourceTextureRect = QRectF(0, 0, 1, 1);
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();
if (qIsDefaultAspect(q->orientation())) {
m_sourceTextureRect = QRectF(-q->contentRect().left() / contentWidth,
-q->contentRect().top() / contentHeight,
rect.width() / contentWidth,
rect.height() / contentHeight);
} else {
m_sourceTextureRect = QRectF(-q->contentRect().top() / contentHeight,
-q->contentRect().left() / contentWidth,
rect.height() / contentHeight,
rect.width() / contentWidth);
}
}
}
QSGNode *QDeclarativeVideoRendererBackend::updatePaintNode(QSGNode *oldNode,
QQuickItem::UpdatePaintNodeData *data)
{
Q_UNUSED(data);
QSGVideoNode *videoNode = static_cast<QSGVideoNode *>(oldNode);
QMutexLocker lock(&m_frameMutex);
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) {
foreach (QSGVideoNodeFactoryInterface* factory, m_videoNodeFactories) {
videoNode = factory->createNode(m_surface->surfaceFormat());
if (videoNode)
break;
}
}
if (!videoNode)
return 0;
// Negative rotations need lots of %360
videoNode->setTexturedRectGeometry(m_renderedRect, m_sourceTextureRect,
qNormalizedOrientation(q->orientation()));
videoNode->setCurrentFrame(m_frame);
return videoNode;
}
QAbstractVideoSurface *QDeclarativeVideoRendererBackend::videoSurface() const
{
return m_surface;
}
void QDeclarativeVideoRendererBackend::present(const QVideoFrame &frame)
{
m_frameMutex.lock();
m_frame = frame;
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;
}
QT_END_NAMESPACE

View File

@@ -0,0 +1,104 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Copyright (C) 2012 Research In Motion
** Contact: http://www.qt-project.org/
**
** 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_RENDER_P_H
#define QDECLARATIVEVIDEOOUTPUT_RENDER_P_H
#include "qdeclarativevideooutput_backend_p.h"
#include "qsgvideonode_i420.h"
#include "qsgvideonode_rgb.h"
#include <QtCore/qmutex.h>
#include <QtMultimedia/qabstractvideosurface.h>
QT_BEGIN_NAMESPACE
class QSGVideoItemSurface;
class QVideoRendererControl;
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;
friend class QSGVideoItemSurface;
void present(const QVideoFrame &frame);
void stop();
private:
QPointer<QVideoRendererControl> m_rendererControl;
QList<QSGVideoNodeFactoryInterface*> m_videoNodeFactories;
QSGVideoItemSurface *m_surface;
QVideoFrame m_frame;
QSGVideoNodeFactory_I420 m_i420Factory;
QSGVideoNodeFactory_RGB m_rgbFactory;
QMutex m_frameMutex;
QRectF m_renderedRect; // Destination pixel coordinates, clipped
QRectF m_sourceTextureRect; // Source texture coordinates
};
class QSGVideoItemSurface : public QAbstractVideoSurface
{
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);
private:
QDeclarativeVideoRendererBackend *m_backend;
};
QT_END_NAMESPACE
#endif

View File

@@ -0,0 +1,135 @@
/****************************************************************************
**
** Copyright (C) 2012 Research In Motion
** Contact: http://www.qt-project.org/
**
** 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_window_p.h"
#include "qdeclarativevideooutput_p.h"
#include <QtQuick/qquickcanvas.h>
#include <QtMultimedia/qmediaservice.h>
#include <QtMultimedia/qvideowindowcontrol.h>
QT_BEGIN_NAMESPACE
QDeclarativeVideoWindowBackend::QDeclarativeVideoWindowBackend(QDeclarativeVideoOutput *parent)
: QDeclarativeVideoBackend(parent)
{
}
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->canvas())
m_videoWindowControl->setWinId(q->canvas()->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 (change == QQuickItem::ItemSceneChange && m_videoWindowControl) {
if (changeData.canvas)
m_videoWindowControl->setWinId(changeData.canvas->winId());
else
m_videoWindowControl->setWinId(0);
}
}
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(canvasRect.toAlignedRect());
}
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;
}
QT_END_NAMESPACE

View File

@@ -0,0 +1,72 @@
/****************************************************************************
**
** Copyright (C) 2012 Research In Motion
** Contact: http://www.qt-project.org/
**
** 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_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;
private:
QPointer<QVideoWindowControl> m_videoWindowControl;
};
QT_END_NAMESPACE
#endif

View File

@@ -112,6 +112,7 @@ Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug, const QAbstractVideoSurface::Error
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QAbstractVideoSurface*)
Q_DECLARE_METATYPE(QAbstractVideoSurface::Error)
QT_END_HEADER