diff options
Diffstat (limited to 'src/plugins/mediaservices')
100 files changed, 18550 insertions, 0 deletions
diff --git a/src/plugins/mediaservices/directshow/directshow.pro b/src/plugins/mediaservices/directshow/directshow.pro new file mode 100644 index 0000000..a3080d3 --- /dev/null +++ b/src/plugins/mediaservices/directshow/directshow.pro @@ -0,0 +1,14 @@ +TARGET = dsengine +include(../../qpluginbase.pri) + +QT += multimedia + +HEADERS += dsserviceplugin.h +SOURCES += dsserviceplugin.cpp + +include(mediaplayer/mediaplayer.pri) + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/mediaservices +target.path = $$[QT_INSTALL_PLUGINS]/plugins/mediaservices +INSTALLS += target + diff --git a/src/plugins/mediaservices/directshow/dsserviceplugin.cpp b/src/plugins/mediaservices/directshow/dsserviceplugin.cpp new file mode 100644 index 0000000..f3ab889 --- /dev/null +++ b/src/plugins/mediaservices/directshow/dsserviceplugin.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> +#include <QtCore/qdebug.h> +#include <QtCore/QFile> + +#include "dsserviceplugin.h" + +#ifdef QMEDIA_DIRECTSHOW_CAMERA +#include "dscameraservice.h" +#endif + +#ifdef QMEDIA_DIRECTSHOW_PLAYER +#include "directshowplayerservice.h" +#endif + +#include <qmediaserviceprovider.h> + + +#ifdef QMEDIA_DIRECTSHOW_CAMERA +#ifndef _STRSAFE_H_INCLUDED_ +#include <tchar.h> +#endif +#include <dshow.h> +#include <objbase.h> +#include <initguid.h> +#pragma comment(lib, "strmiids.lib") +#pragma comment(lib, "ole32.lib") +#include <windows.h> +#endif + + +QT_BEGIN_NAMESPACE + +QStringList DSServicePlugin::keys() const +{ + return QStringList() +#ifdef QMEDIA_DIRECTSHOW_CAMERA + << QLatin1String(Q_MEDIASERVICE_CAMERA) +#endif +#ifdef QMEDIA_DIRECTSHOW_PLAYER + << QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER) +#endif + ; +} + +QMediaService* DSServicePlugin::create(QString const& key) +{ +#ifdef QMEDIA_DIRECTSHOW_CAMERA + if (key == QLatin1String(Q_MEDIASERVICE_CAMERA)) + return new DSCameraService; +#endif +#ifdef QMEDIA_DIRECTSHOW_PLAYER + if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) + return new DirectShowPlayerService; +#endif + + qDebug() << "unsupported key:" << key; + return 0; +} + +void DSServicePlugin::release(QMediaService *service) +{ + delete service; +} + +QList<QByteArray> DSServicePlugin::devices(const QByteArray &service) const +{ +#ifdef QMEDIA_DIRECTSHOW_CAMERA + if (service == Q_MEDIASERVICE_CAMERA) { + if (m_cameraDevices.isEmpty()) + updateDevices(); + + return m_cameraDevices; + } +#endif + + return QList<QByteArray>(); +} + +QString DSServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device) +{ +#ifdef QMEDIA_DIRECTSHOW_CAMERA + if (service == Q_MEDIASERVICE_CAMERA) { + if (m_cameraDevices.isEmpty()) + updateDevices(); + + for (int i=0; i<m_cameraDevices.count(); i++) + if (m_cameraDevices[i] == device) + return m_cameraDescriptions[i]; + } +#endif + return QString(); +} + +#ifdef QMEDIA_DIRECTSHOW_CAMERA +void DSServicePlugin::updateDevices() const +{ + m_cameraDevices.clear(); + m_cameraDescriptions.clear(); + + CoInitialize(NULL); + ICreateDevEnum* pDevEnum = NULL; + IEnumMoniker* pEnum = NULL; + // Create the System device enumerator + HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, + CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, + reinterpret_cast<void**>(&pDevEnum)); + if(SUCCEEDED(hr)) { + // Create the enumerator for the video capture category + hr = pDevEnum->CreateClassEnumerator( + CLSID_VideoInputDeviceCategory, &pEnum, 0); + pEnum->Reset(); + // go through and find all video capture devices + IMoniker* pMoniker = NULL; + while(pEnum->Next(1, &pMoniker, NULL) == S_OK) { + IPropertyBag *pPropBag; + hr = pMoniker->BindToStorage(0,0,IID_IPropertyBag, + (void**)(&pPropBag)); + if(FAILED(hr)) { + pMoniker->Release(); + continue; // skip this one + } + // Find the description + WCHAR str[120]; + VARIANT varName; + varName.vt = VT_BSTR; + hr = pPropBag->Read(L"FriendlyName", &varName, 0); + if(SUCCEEDED(hr)) { + StringCchCopyW(str,sizeof(str)/sizeof(str[0]),varName.bstrVal); + QString temp(QString::fromUtf16((unsigned short*)str)); + m_cameraDevices.append(QString("ds:%1").arg(temp).toLocal8Bit().constData()); + hr = pPropBag->Read(L"Description", &varName, 0); + StringCchCopyW(str,sizeof(str)/sizeof(str[0]),varName.bstrVal); + QString temp2(QString::fromUtf16((unsigned short*)str)); + m_cameraDescriptions.append(temp2); + } + pPropBag->Release(); + pMoniker->Release(); + } + } + CoUninitialize(); +} +#endif + +QT_END_NAMESPACE + +Q_EXPORT_PLUGIN2(dsengine, DSServicePlugin); + diff --git a/src/plugins/mediaservices/directshow/dsserviceplugin.h b/src/plugins/mediaservices/directshow/dsserviceplugin.h new file mode 100644 index 0000000..3c6f1b8 --- /dev/null +++ b/src/plugins/mediaservices/directshow/dsserviceplugin.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DSSERVICEPLUGIN_H +#define DSSERVICEPLUGIN_H + +#include <qmediaserviceproviderplugin.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class DSServicePlugin : public QMediaServiceProviderPlugin, public QMediaServiceSupportedDevicesInterface +{ + Q_OBJECT + Q_INTERFACES(QMediaServiceSupportedDevicesInterface) +public: + QStringList keys() const; + QMediaService* create(QString const& key); + void release(QMediaService *service); + + QList<QByteArray> devices(const QByteArray &service) const; + QString deviceDescription(const QByteArray &service, const QByteArray &device); + +private: +#ifdef QMEDIA_DIRECTSHOW_CAMERA + void updateDevices() const; + + mutable QList<QByteArray> m_cameraDevices; + mutable QStringList m_cameraDescriptions; +#endif +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // DSSERVICEPLUGIN_H diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowaudioendpointcontrol.cpp b/src/plugins/mediaservices/directshow/mediaplayer/directshowaudioendpointcontrol.cpp new file mode 100644 index 0000000..570b44a --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowaudioendpointcontrol.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowaudioendpointcontrol.h" + +#include "directshowglobal.h" +#include "directshowplayerservice.h" + + +QT_BEGIN_NAMESPACE + +DirectShowAudioEndpointControl::DirectShowAudioEndpointControl( + DirectShowPlayerService *service, QObject *parent) + : QMediaControl(parent) + , m_service(service) + , m_bindContext(0) + , m_deviceEnumerator(0) +{ + if (CreateBindCtx(0, &m_bindContext) == S_OK) { + m_deviceEnumerator = com_new<ICreateDevEnum>(CLSID_SystemDeviceEnum); + + updateEndpoints(); + + setActiveEndpoint(m_defaultEndpoint); + } +} + +DirectShowAudioEndpointControl::~DirectShowAudioEndpointControl() +{ + foreach (IMoniker *moniker, m_devices) + moniker->Release(); + + if (m_bindContext) + m_bindContext->Release(); + + if (m_deviceEnumerator) + m_deviceEnumerator->Release(); +} + +QList<QString> DirectShowAudioEndpointControl::availableEndpoints() const +{ + return m_devices.keys(); +} + +QString DirectShowAudioEndpointControl::endpointDescription(const QString &name) const +{ + QString description; + + if (IMoniker *moniker = m_devices.value(name, 0)) { + IPropertyBag *propertyBag = 0; + if (SUCCEEDED(moniker->BindToStorage( + 0, 0, IID_IPropertyBag, reinterpret_cast<void **>(&propertyBag)))) { + VARIANT name; + VariantInit(&name); + if (SUCCEEDED(propertyBag->Read(L"FriendlyName", &name, 0))) + description = QString::fromWCharArray(name.bstrVal); + VariantClear(&name); + propertyBag->Release(); + } + } + return description;; +} + +QString DirectShowAudioEndpointControl::defaultEndpoint() const +{ + return m_defaultEndpoint; +} + +QString DirectShowAudioEndpointControl::activeEndpoint() const +{ + return m_activeEndpoint; +} + +void DirectShowAudioEndpointControl::setActiveEndpoint(const QString &name) +{ + if (m_activeEndpoint == name) + return; + + if (IMoniker *moniker = m_devices.value(name, 0)) { + IBaseFilter *filter = 0; + + if (moniker->BindToObject( + m_bindContext, + 0, + __uuidof(IBaseFilter), + reinterpret_cast<void **>(&filter)) == S_OK) { + m_service->setAudioOutput(filter); + + filter->Release(); + } + } +} + +void DirectShowAudioEndpointControl::updateEndpoints() +{ + IMalloc *oleMalloc = 0; + if (m_deviceEnumerator && CoGetMalloc(1, &oleMalloc) == S_OK) { + IEnumMoniker *monikers = 0; + + if (m_deviceEnumerator->CreateClassEnumerator( + CLSID_AudioRendererCategory, &monikers, 0) == S_OK) { + for (IMoniker *moniker = 0; monikers->Next(1, &moniker, 0) == S_OK; moniker->Release()) { + OLECHAR *string = 0; + if (moniker->GetDisplayName(m_bindContext, 0, &string) == S_OK) { + QString deviceId = QString::fromWCharArray(string); + oleMalloc->Free(string); + + moniker->AddRef(); + m_devices.insert(deviceId, moniker); + + if (m_defaultEndpoint.isEmpty() + || deviceId.endsWith(QLatin1String("Default DirectSound Device"))) { + m_defaultEndpoint = deviceId; + } + } + } + monikers->Release(); + } + oleMalloc->Release(); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowaudioendpointcontrol.h b/src/plugins/mediaservices/directshow/mediaplayer/directshowaudioendpointcontrol.h new file mode 100644 index 0000000..2faac13 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowaudioendpointcontrol.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWAUDIOENDPOINTCONTROL_H +#define DIRECTSHOWAUDIOENDPOINTCONTROL_H + +#include <QtMultimedia/qmediacontrol.h> + +#include <dshow.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class DirectShowPlayerService; + +class DirectShowAudioEndpointControl : public QMediaControl +{ + Q_OBJECT +public: + DirectShowAudioEndpointControl(DirectShowPlayerService *service, QObject *parent = 0); + ~DirectShowAudioEndpointControl(); + + QList<QString> availableEndpoints() const; + + QString endpointDescription(const QString &name) const; + + QString defaultEndpoint() const; + QString activeEndpoint() const; + + void setActiveEndpoint(const QString& name); + +private: + void updateEndpoints(); + + DirectShowPlayerService *m_service; + IBindCtx *m_bindContext; + ICreateDevEnum *m_deviceEnumerator; + + QMap<QString, IMoniker *> m_devices; + QString m_defaultEndpoint; + QString m_activeEndpoint; +}; + +#define QAudioEndpointSelector_iid "com.nokia.Qt.QAudioEndpointSelector/1.0" + +class Duck +{ + uint quack; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif + diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshoweventloop.cpp b/src/plugins/mediaservices/directshow/mediaplayer/directshoweventloop.cpp new file mode 100644 index 0000000..07541c2 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshoweventloop.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <directshoweventloop.h> + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qcoreevent.h> + + +QT_BEGIN_NAMESPACE + + +class DirectShowPostedEvent +{ +public: + DirectShowPostedEvent(QObject *receiver, QEvent *event) + : receiver(receiver) + , event(event) + , next(0) + { + } + + ~DirectShowPostedEvent() + { + delete event; + } + + QObject *receiver; + QEvent *event; + DirectShowPostedEvent *next; +}; + +DirectShowEventLoop::DirectShowEventLoop(QObject *parent) + : QWinEventNotifier(parent) + , m_postsHead(0) + , m_postsTail(0) + , m_eventHandle(::CreateEvent(0, 0, 0, 0)) + , m_waitHandle(::CreateEvent(0, 0, 0, 0)) +{ + setHandle(m_eventHandle); + setEnabled(true); +} + +DirectShowEventLoop::~DirectShowEventLoop() +{ + setEnabled(false); + + ::CloseHandle(m_eventHandle); + ::CloseHandle(m_waitHandle); + + for (DirectShowPostedEvent *post = m_postsHead; post; post = m_postsHead) { + m_postsHead = m_postsHead->next; + + delete post; + } +} + +void DirectShowEventLoop::wait(QMutex *mutex) +{ + ::ResetEvent(m_waitHandle); + + mutex->unlock(); + + HANDLE handles[] = { m_eventHandle, m_waitHandle }; + while (::WaitForMultipleObjects(2, handles, false, INFINITE) == WAIT_OBJECT_0) + processEvents(); + + mutex->lock(); +} + +void DirectShowEventLoop::wake() +{ + ::SetEvent(m_waitHandle); +} + +void DirectShowEventLoop::postEvent(QObject *receiver, QEvent *event) +{ + QMutexLocker locker(&m_mutex); + + DirectShowPostedEvent *post = new DirectShowPostedEvent(receiver, event); + + if (m_postsTail) + m_postsTail->next = post; + else + m_postsHead = post; + + m_postsTail = post; + + ::SetEvent(m_eventHandle); +} + +bool DirectShowEventLoop::event(QEvent *event) +{ + if (event->type() == QEvent::WinEventAct) { + processEvents(); + + return true; + } else { + return QWinEventNotifier::event(event); + } +} + +void DirectShowEventLoop::processEvents() +{ + QMutexLocker locker(&m_mutex); + + while(m_postsHead) { + ::ResetEvent(m_eventHandle); + + DirectShowPostedEvent *post = m_postsHead; + m_postsHead = m_postsHead->next; + + if (!m_postsHead) + m_postsTail = 0; + + locker.unlock(); + QCoreApplication::sendEvent(post->receiver, post->event); + delete post; + locker.relock(); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshoweventloop.h b/src/plugins/mediaservices/directshow/mediaplayer/directshoweventloop.h new file mode 100644 index 0000000..f46e65a --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshoweventloop.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWEVENTLOOP_H +#define DIRECTSHOWEVENTLOOP_H + +#include <QtCore/qmutex.h> +#include <QtCore/private/qwineventnotifier_p.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class DirectShowPostedEvent; + +class DirectShowEventLoop : public QWinEventNotifier +{ + Q_OBJECT +public: + DirectShowEventLoop(QObject *parent = 0); + ~DirectShowEventLoop(); + + void wait(QMutex *mutex); + void wake(); + + void postEvent(QObject *object, QEvent *event); + + bool event(QEvent *event); + +private: + void processEvents(); + + DirectShowPostedEvent *m_postsHead; + DirectShowPostedEvent *m_postsTail; + HANDLE m_eventHandle; + HANDLE m_waitHandle; + QMutex m_mutex; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowglobal.h b/src/plugins/mediaservices/directshow/mediaplayer/directshowglobal.h new file mode 100644 index 0000000..1c9fe54 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowglobal.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWGLOBAL_H +#define DIRECTSHOWGLOBAL_H + +#include <dshow.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +template <typename T> T *com_cast(IUnknown *unknown) +{ + T *iface = 0; + return unknown && unknown->QueryInterface( + __uuidof(T), reinterpret_cast<void **>(&iface)) == S_OK + ? iface + : 0; +} + +template <typename T> T *com_new(const IID &clsid) +{ + T *object = 0; + return CoCreateInstance( + clsid, + NULL, + CLSCTX_INPROC_SERVER, + __uuidof(T), + reinterpret_cast<void **>(&object)) == S_OK + ? object + : 0; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowioreader.cpp b/src/plugins/mediaservices/directshow/mediaplayer/directshowioreader.cpp new file mode 100644 index 0000000..54446b8 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowioreader.cpp @@ -0,0 +1,501 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowioreader.h" + +#include "directshoweventloop.h" +#include "directshowglobal.h" +#include "directshowiosource.h" + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qcoreevent.h> +#include <QtCore/qiodevice.h> +#include <QtCore/qthread.h> + + +QT_BEGIN_NAMESPACE + +class DirectShowSampleRequest +{ +public: + DirectShowSampleRequest( + IMediaSample *sample, DWORD_PTR userData, LONGLONG position, LONG length, BYTE *buffer) + : next(0) + , sample(sample) + , userData(userData) + , position(position) + , length(length) + , buffer(buffer) + , result(S_FALSE) + { + } + + DirectShowSampleRequest *remove() { DirectShowSampleRequest *n = next; delete this; return n; } + + DirectShowSampleRequest *next; + IMediaSample *sample; + DWORD_PTR userData; + LONGLONG position; + LONG length; + BYTE *buffer; + HRESULT result; +}; + +DirectShowIOReader::DirectShowIOReader( + QIODevice *device, DirectShowIOSource *source, DirectShowEventLoop *loop) + : m_source(source) + , m_device(device) + , m_loop(loop) + , m_pendingHead(0) + , m_pendingTail(0) + , m_readyHead(0) + , m_readyTail(0) + , m_synchronousPosition(0) + , m_synchronousLength(0) + , m_synchronousBytesRead(0) + , m_synchronousBuffer(0) + , m_synchronousResult(S_OK) + , m_totalLength(0) + , m_availableLength(0) + , m_flushing(false) +{ + moveToThread(device->thread()); + + connect(device, SIGNAL(readyRead()), this, SLOT(readyRead())); +} + +DirectShowIOReader::~DirectShowIOReader() +{ + flushRequests(); +} + +HRESULT DirectShowIOReader::QueryInterface(REFIID riid, void **ppvObject) +{ + return m_source->QueryInterface(riid, ppvObject); +} + +ULONG DirectShowIOReader::AddRef() +{ + return m_source->AddRef(); +} + +ULONG DirectShowIOReader::Release() +{ + return m_source->Release(); +} + +// IAsyncReader +HRESULT DirectShowIOReader::RequestAllocator( + IMemAllocator *pPreferred, ALLOCATOR_PROPERTIES *pProps, IMemAllocator **ppActual) +{ + if (!ppActual || !pProps) { + return E_POINTER; + } else { + ALLOCATOR_PROPERTIES actualProperties; + + if (pProps->cbAlign == 0) + pProps->cbAlign = 1; + + if (pPreferred && pPreferred->SetProperties(pProps, &actualProperties) == S_OK) { + pPreferred->AddRef(); + + *ppActual = pPreferred; + + m_source->setAllocator(*ppActual); + + return S_OK; + } else { + *ppActual = com_new<IMemAllocator>(CLSID_MemoryAllocator); + + if (*ppActual) { + if ((*ppActual)->SetProperties(pProps, &actualProperties) != S_OK) { + (*ppActual)->Release(); + } else { + m_source->setAllocator(*ppActual); + + return S_OK; + } + } + } + ppActual = 0; + + return E_FAIL; + } +} + +HRESULT DirectShowIOReader::Request(IMediaSample *pSample, DWORD_PTR dwUser) +{ + QMutexLocker locker(&m_mutex); + + if (!pSample) { + return E_POINTER; + } else if (m_flushing) { + return VFW_E_WRONG_STATE; + } else { + REFERENCE_TIME startTime = 0; + REFERENCE_TIME endTime = 0; + BYTE *buffer; + + if (pSample->GetTime(&startTime, &endTime) != S_OK + || pSample->GetPointer(&buffer) != S_OK) { + return VFW_E_SAMPLE_TIME_NOT_SET; + } else { + LONGLONG position = startTime / 10000000; + LONG length = (endTime - startTime) / 10000000; + + DirectShowSampleRequest *request = new DirectShowSampleRequest( + pSample, dwUser, position, length, buffer); + + if (m_pendingTail) { + m_pendingTail->next = request; + } else { + m_pendingHead = request; + + m_loop->postEvent(this, new QEvent(QEvent::User)); + } + m_pendingTail = request; + + return S_OK; + } + } +} + +HRESULT DirectShowIOReader::WaitForNext( + DWORD dwTimeout, IMediaSample **ppSample, DWORD_PTR *pdwUser) +{ + if (!ppSample || !pdwUser) + return E_POINTER; + + QMutexLocker locker(&m_mutex); + + do { + if (m_readyHead) { + DirectShowSampleRequest *request = m_readyHead; + + *ppSample = request->sample; + *pdwUser = request->userData; + + HRESULT hr = request->result; + + m_readyHead = request->next; + + if (!m_readyHead) + m_readyTail = 0; + + delete request; + + return hr; + } else if (m_flushing) { + *ppSample = 0; + *pdwUser = 0; + + return VFW_E_WRONG_STATE; + } + } while (m_wait.wait(&m_mutex, dwTimeout)); + + *ppSample = 0; + *pdwUser = 0; + + return VFW_E_TIMEOUT; +} + +HRESULT DirectShowIOReader::SyncReadAligned(IMediaSample *pSample) +{ + if (!pSample) { + return E_POINTER; + } else { + REFERENCE_TIME startTime = 0; + REFERENCE_TIME endTime = 0; + BYTE *buffer; + + if (pSample->GetTime(&startTime, &endTime) != S_OK + || pSample->GetPointer(&buffer) != S_OK) { + return VFW_E_SAMPLE_TIME_NOT_SET; + } else { + LONGLONG position = startTime / 10000000; + LONG length = (endTime - startTime) / 10000000; + + QMutexLocker locker(&m_mutex); + + if (thread() == QThread::currentThread()) { + qint64 bytesRead = 0; + + HRESULT hr = blockingRead(position, length, buffer, &bytesRead); + + if (SUCCEEDED(hr)) + pSample->SetActualDataLength(bytesRead); + + return hr; + } else { + m_synchronousPosition = position; + m_synchronousLength = length; + m_synchronousBuffer = buffer; + + m_loop->postEvent(this, new QEvent(QEvent::User)); + + m_wait.wait(&m_mutex); + + m_synchronousBuffer = 0; + + if (SUCCEEDED(m_synchronousResult)) + pSample->SetActualDataLength(m_synchronousBytesRead); + + return m_synchronousResult; + } + } + } +} + +HRESULT DirectShowIOReader::SyncRead(LONGLONG llPosition, LONG lLength, BYTE *pBuffer) +{ + if (!pBuffer) { + return E_POINTER; + } else { + if (thread() == QThread::currentThread()) { + qint64 bytesRead; + + return blockingRead(llPosition, lLength, pBuffer, &bytesRead); + } else { + QMutexLocker locker(&m_mutex); + + m_synchronousPosition = llPosition; + m_synchronousLength = lLength; + m_synchronousBuffer = pBuffer; + + m_loop->postEvent(this, new QEvent(QEvent::User)); + + m_wait.wait(&m_mutex); + + m_synchronousBuffer = 0; + + return m_synchronousResult; + } + } +} + +HRESULT DirectShowIOReader::Length(LONGLONG *pTotal, LONGLONG *pAvailable) +{ + if (!pTotal || !pAvailable) { + return E_POINTER; + } else { + QMutexLocker locker(&m_mutex); + + *pTotal = m_totalLength; + *pAvailable = m_availableLength; + + return S_OK; + } +} + + +HRESULT DirectShowIOReader::BeginFlush() +{ + QMutexLocker locker(&m_mutex); + + if (m_flushing) + return S_FALSE; + + m_flushing = true; + + flushRequests(); + + m_wait.wakeAll(); + + return S_OK; +} + +HRESULT DirectShowIOReader::EndFlush() +{ + QMutexLocker locker(&m_mutex); + + if (!m_flushing) + return S_FALSE; + + m_flushing = false; + + return S_OK; +} + +void DirectShowIOReader::customEvent(QEvent *event) +{ + if (event->type() == QEvent::User) { + readyRead(); + } else { + QObject::customEvent(event); + } +} + +void DirectShowIOReader::readyRead() +{ + QMutexLocker locker(&m_mutex); + + m_availableLength = m_device->bytesAvailable() + m_device->pos(); + m_totalLength = m_device->size(); + + if (m_synchronousBuffer) { + if (nonBlockingRead( + m_synchronousPosition, + m_synchronousLength, + m_synchronousBuffer, + &m_synchronousBytesRead, + &m_synchronousResult)) { + m_wait.wakeAll(); + } + } else { + qint64 bytesRead = 0; + + while (m_pendingHead && nonBlockingRead( + m_pendingHead->position, + m_pendingHead->length, + m_pendingHead->buffer, + &bytesRead, + &m_pendingHead->result)) { + m_pendingHead->sample->SetActualDataLength(bytesRead); + + if (m_readyTail) + m_readyTail->next = m_pendingHead; + m_readyTail = m_pendingHead; + + m_pendingHead = m_pendingHead->next; + + m_readyTail->next = 0; + + if (!m_pendingHead) + m_pendingTail = 0; + + if (!m_readyHead) + m_readyHead = m_readyTail; + + m_wait.wakeAll(); + } + } +} + +HRESULT DirectShowIOReader::blockingRead( + LONGLONG position, LONG length, BYTE *buffer, qint64 *bytesRead) +{ + *bytesRead = 0; + + if (qint64(position) > m_device->size()) + return S_FALSE; + + const qint64 maxSize = qMin<qint64>(m_device->size(), position + length); + + while (m_device->bytesAvailable() + m_device->pos() < maxSize) { + if (!m_device->waitForReadyRead(-1)) + return S_FALSE; + } + + if (m_device->pos() != position && !m_device->seek(position)) + return S_FALSE; + + const qint64 maxBytes = qMin<qint64>(length, m_device->bytesAvailable()); + + *bytesRead = m_device->read(reinterpret_cast<char *>(buffer), maxBytes); + + if (*bytesRead != length) { + qMemSet(buffer + *bytesRead, 0, length - *bytesRead); + + return S_FALSE; + } else { + return S_OK; + } +} + +bool DirectShowIOReader::nonBlockingRead( + LONGLONG position, LONG length, BYTE *buffer, qint64 *bytesRead, HRESULT *result) +{ + const qint64 maxSize = qMin<qint64>(m_device->size(), position + length); + + if (position > m_device->size()) { + *bytesRead = 0; + *result = S_FALSE; + + return true; + } else if (m_device->bytesAvailable() + m_device->pos() >= maxSize) { + if (m_device->pos() != position && !m_device->seek(position)) { + *bytesRead = 0; + *result = S_FALSE; + + return true; + } else { + const qint64 maxBytes = qMin<qint64>(length, m_device->bytesAvailable()); + + *bytesRead = m_device->read(reinterpret_cast<char *>(buffer), maxBytes); + + if (*bytesRead != length) { + qMemSet(buffer + *bytesRead, 0, length - *bytesRead); + + *result = S_FALSE; + } else { + *result = S_OK; + } + + return true; + } + } else { + return false; + } +} + +void DirectShowIOReader::flushRequests() +{ + while (m_pendingHead) { + m_pendingHead->result = VFW_E_WRONG_STATE; + + if (m_readyTail) + m_readyTail->next = m_pendingHead; + + m_readyTail = m_pendingHead; + + m_pendingHead = m_pendingHead->next; + + m_readyTail->next = 0; + + if (!m_pendingHead) + m_pendingTail = 0; + + if (!m_readyHead) + m_readyHead = m_readyTail; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowioreader.h b/src/plugins/mediaservices/directshow/mediaplayer/directshowioreader.h new file mode 100644 index 0000000..8cbc2f1 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowioreader.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWIOREADER_H +#define DIRECTSHOWIOREADER_H + +#include <QtCore/qmutex.h> +#include <QtCore/qobject.h> +#include <QtCore/qwaitcondition.h> + +#include <dshow.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QIODevice; + +class DirectShowEventLoop; +class DirectShowIOSource; +class DirectShowSampleRequest; + +class DirectShowIOReader : public QObject, public IAsyncReader +{ + Q_OBJECT +public: + DirectShowIOReader(QIODevice *device, DirectShowIOSource *source, DirectShowEventLoop *loop); + ~DirectShowIOReader(); + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // IAsyncReader + HRESULT STDMETHODCALLTYPE RequestAllocator( + IMemAllocator *pPreferred, ALLOCATOR_PROPERTIES *pProps, IMemAllocator **ppActual); + + HRESULT STDMETHODCALLTYPE Request(IMediaSample *pSample, DWORD_PTR dwUser); + + HRESULT STDMETHODCALLTYPE WaitForNext( + DWORD dwTimeout, IMediaSample **ppSample, DWORD_PTR *pdwUser); + + HRESULT STDMETHODCALLTYPE SyncReadAligned(IMediaSample *pSample); + + HRESULT STDMETHODCALLTYPE SyncRead(LONGLONG llPosition, LONG lLength, BYTE *pBuffer); + + HRESULT STDMETHODCALLTYPE Length(LONGLONG *pTotal, LONGLONG *pAvailable); + + HRESULT STDMETHODCALLTYPE BeginFlush(); + HRESULT STDMETHODCALLTYPE EndFlush(); + +protected: + void customEvent(QEvent *event); + +private Q_SLOTS: + void readyRead(); + +private: + HRESULT blockingRead(LONGLONG position, LONG length, BYTE *buffer, qint64 *bytesRead); + bool nonBlockingRead( + LONGLONG position, LONG length, BYTE *buffer, qint64 *bytesRead, HRESULT *result); + void flushRequests(); + + DirectShowIOSource *m_source; + QIODevice *m_device; + DirectShowEventLoop *m_loop; + DirectShowSampleRequest *m_pendingHead; + DirectShowSampleRequest *m_pendingTail; + DirectShowSampleRequest *m_readyHead; + DirectShowSampleRequest *m_readyTail; + LONGLONG m_synchronousPosition; + LONG m_synchronousLength; + qint64 m_synchronousBytesRead; + BYTE *m_synchronousBuffer; + HRESULT m_synchronousResult; + LONGLONG m_totalLength; + LONGLONG m_availableLength; + bool m_flushing; + QMutex m_mutex; + QWaitCondition m_wait; +}; + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowiosource.cpp b/src/plugins/mediaservices/directshow/mediaplayer/directshowiosource.cpp new file mode 100644 index 0000000..c2fb0d4 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowiosource.cpp @@ -0,0 +1,639 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowiosource.h" + +#include "directshowglobal.h" +#include "directshowmediatype.h" +#include "directshowpinenum.h" + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qurl.h> + +QT_BEGIN_NAMESPACE + +static const GUID directshow_subtypes[] = +{ + MEDIASUBTYPE_Avi, + MEDIASUBTYPE_WAVE, + MEDIASUBTYPE_NULL +}; + +DirectShowIOSource::DirectShowIOSource(DirectShowEventLoop *loop) + : m_ref(1) + , m_state(State_Stopped) + , m_reader(0) + , m_loop(loop) + , m_graph(0) + , m_clock(0) + , m_allocator(0) + , m_peerPin(0) + , m_pinId(QLatin1String("Data")) +{ + QVector<AM_MEDIA_TYPE> mediaTypes; + + AM_MEDIA_TYPE type = + { + MEDIATYPE_Stream, // majortype + MEDIASUBTYPE_NULL, // subtype + TRUE, // bFixedSizeSamples + FALSE, // bTemporalCompression + 1, // lSampleSize + GUID_NULL, // formattype + 0, // pUnk + 0, // cbFormat + 0, // pbFormat + }; + + static const int count = sizeof(directshow_subtypes) / sizeof(GUID); + + for (int i = 0; i < count; ++i) { + type.subtype = directshow_subtypes[i]; + mediaTypes.append(type); + } + + setMediaTypes(mediaTypes); +} + +DirectShowIOSource::~DirectShowIOSource() +{ + Q_ASSERT(m_ref == 0); + + delete m_reader; +} + +void DirectShowIOSource::setDevice(QIODevice *device) +{ + Q_ASSERT(!m_reader); + + m_reader = new DirectShowIOReader(device, this, m_loop); +} + +void DirectShowIOSource::setAllocator(IMemAllocator *allocator) +{ + if (m_allocator) + m_allocator->Release(); + + m_allocator = allocator; + + if (m_allocator) + m_allocator->AddRef(); +} + +// IUnknown +HRESULT DirectShowIOSource::QueryInterface(REFIID riid, void **ppvObject) +{ + if (!ppvObject) { + return E_POINTER; + } else if (riid == IID_IUnknown + || riid == IID_IPersist + || riid == IID_IMediaFilter + || riid == IID_IBaseFilter) { + *ppvObject = static_cast<IBaseFilter *>(this); + } else if (riid == IID_IAMFilterMiscFlags) { + *ppvObject = static_cast<IAMFilterMiscFlags *>(this); + } else if (riid == IID_IPin) { + *ppvObject = static_cast<IPin *>(this); + } else if (riid == IID_IAsyncReader) { + *ppvObject = static_cast<IAsyncReader *>(m_reader); + } else { + *ppvObject = 0; + + return E_NOINTERFACE; + } + + AddRef(); + + return S_OK; +} + +ULONG DirectShowIOSource::AddRef() +{ + return InterlockedIncrement(&m_ref); +} + +ULONG DirectShowIOSource::Release() +{ + ULONG ref = InterlockedDecrement(&m_ref); + + if (ref == 0) { + delete this; + } + + return ref; +} + +// IPersist +HRESULT DirectShowIOSource::GetClassID(CLSID *pClassID) +{ + *pClassID = CLSID_NULL; + + return S_OK; +} + +// IMediaFilter +HRESULT DirectShowIOSource::Run(REFERENCE_TIME tStart) +{ + QMutexLocker locker(&m_mutex); + + m_state = State_Running; + + return S_OK; +} + +HRESULT DirectShowIOSource::Pause() +{ + QMutexLocker locker(&m_mutex); + + m_state = State_Paused; + + return S_OK; +} + +HRESULT DirectShowIOSource::Stop() +{ + QMutexLocker locker(&m_mutex); + + m_state = State_Stopped; + + return S_OK; +} + +HRESULT DirectShowIOSource::GetState(DWORD dwMilliSecsTimeout, FILTER_STATE *pState) +{ + Q_UNUSED(dwMilliSecsTimeout); + + if (!pState) { + return E_POINTER; + } else { + QMutexLocker locker(&m_mutex); + + *pState = m_state; + + return S_OK; + } +} + +HRESULT DirectShowIOSource::SetSyncSource(IReferenceClock *pClock) +{ + QMutexLocker locker(&m_mutex); + + if (m_clock) + m_clock->Release(); + + m_clock = pClock; + + if (m_clock) + m_clock->AddRef(); + + return S_OK; +} + +HRESULT DirectShowIOSource::GetSyncSource(IReferenceClock **ppClock) +{ + if (!ppClock) { + return E_POINTER; + } else { + if (!m_clock) { + *ppClock = 0; + + return S_FALSE; + } else { + m_clock->AddRef(); + + *ppClock = m_clock; + + return S_OK; + } + } +} + +// IBaseFilter +HRESULT DirectShowIOSource::EnumPins(IEnumPins **ppEnum) +{ + if (!ppEnum) { + return E_POINTER; + } else { + *ppEnum = new DirectShowPinEnum(QList<IPin *>() << this); + + return S_OK; + } +} + +HRESULT DirectShowIOSource::FindPin(LPCWSTR Id, IPin **ppPin) +{ + if (!ppPin || !Id) { + return E_POINTER; + } else { + QMutexLocker locker(&m_mutex); + if (QString::fromWCharArray(Id) == m_pinId) { + AddRef(); + + *ppPin = this; + + return S_OK; + } else { + *ppPin = 0; + + return VFW_E_NOT_FOUND; + } + } +} + +HRESULT DirectShowIOSource::JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName) +{ + QMutexLocker locker(&m_mutex); + + m_graph = pGraph; + m_filterName = QString::fromWCharArray(pName); + + return S_OK; +} + +HRESULT DirectShowIOSource::QueryFilterInfo(FILTER_INFO *pInfo) +{ + if (!pInfo) { + return E_POINTER; + } else { + QString name = m_filterName; + + if (name.length() >= MAX_FILTER_NAME) + name.truncate(MAX_FILTER_NAME - 1); + + int length = name.toWCharArray(pInfo->achName); + pInfo->achName[length] = '\0'; + + if (m_graph) + m_graph->AddRef(); + + pInfo->pGraph = m_graph; + + return S_OK; + } +} + +HRESULT DirectShowIOSource::QueryVendorInfo(LPWSTR *pVendorInfo) +{ + Q_UNUSED(pVendorInfo); + + return E_NOTIMPL; +} + +// IAMFilterMiscFlags +ULONG DirectShowIOSource::GetMiscFlags() +{ + return AM_FILTER_MISC_FLAGS_IS_SOURCE; +} + +// IPin +HRESULT DirectShowIOSource::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt) +{ + QMutexLocker locker(&m_mutex); + if (!pReceivePin) { + return E_POINTER; + } else if (m_state != State_Stopped) { + return VFW_E_NOT_STOPPED; + } else if (m_peerPin) { + return VFW_E_ALREADY_CONNECTED; + } else { + HRESULT hr = VFW_E_TYPE_NOT_ACCEPTED; + + m_peerPin = pReceivePin; + m_peerPin->AddRef(); + + if (!pmt) { + IEnumMediaTypes *mediaTypes = 0; + if (pReceivePin->EnumMediaTypes(&mediaTypes) == S_OK) { + for (AM_MEDIA_TYPE *type = 0; + mediaTypes->Next(1, &type, 0) == S_OK; + DirectShowMediaType::deleteType(type)) { + switch (tryConnect(pReceivePin, type)) { + case S_OK: + DirectShowMediaType::freeData(type); + mediaTypes->Release(); + return S_OK; + case VFW_E_NO_TRANSPORT: + hr = VFW_E_NO_TRANSPORT; + break; + default: + break; + } + } + mediaTypes->Release(); + } + AM_MEDIA_TYPE type = + { + MEDIATYPE_Stream, // majortype + MEDIASUBTYPE_NULL, // subtype + TRUE, // bFixedSizeSamples + FALSE, // bTemporalCompression + 1, // lSampleSize + GUID_NULL, // formattype + 0, // pUnk + 0, // cbFormat + 0, // pbFormat + }; + + static const int count = sizeof(directshow_subtypes) / sizeof(GUID); + + for (int i = 0; i < count; ++i) { + type.subtype = directshow_subtypes[i]; + + switch (tryConnect(pReceivePin, &type)) { + case S_OK: + return S_OK; + case VFW_E_NO_TRANSPORT: + hr = VFW_E_NO_TRANSPORT; + break; + default: + break; + } + } + } else if (pmt->majortype == MEDIATYPE_Stream && (hr = tryConnect(pReceivePin, pmt))) { + return S_OK; + } + + m_peerPin->Release(); + m_peerPin = 0; + + m_mediaType.clear(); + + return hr; + } +} + +HRESULT DirectShowIOSource::tryConnect(IPin *pin, const AM_MEDIA_TYPE *type) +{ + m_mediaType = *type; + + HRESULT hr = pin->ReceiveConnection(this, type); + + if (!SUCCEEDED(hr)) { + if (m_allocator) { + m_allocator->Release(); + m_allocator = 0; + } + } else if (!m_allocator) { + hr = VFW_E_NO_TRANSPORT; + + if (IMemInputPin *memPin = com_cast<IMemInputPin>(pin)) { + if ((m_allocator = com_new<IMemAllocator>(CLSID_MemoryAllocator))) { + ALLOCATOR_PROPERTIES properties; + if (memPin->GetAllocatorRequirements(&properties) == S_OK + || m_allocator->GetProperties(&properties) == S_OK) { + if (properties.cbAlign == 0) + properties.cbAlign = 1; + + ALLOCATOR_PROPERTIES actualProperties; + if (SUCCEEDED(hr = m_allocator->SetProperties(&properties, &actualProperties))) + hr = memPin->NotifyAllocator(m_allocator, TRUE); + } + if (!SUCCEEDED(hr)) { + m_allocator->Release(); + m_allocator = 0; + } + } + memPin->Release(); + } + if (!SUCCEEDED(hr)) + pin->Disconnect(); + } + return hr; +} + +HRESULT DirectShowIOSource::ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt) +{ + Q_UNUSED(pConnector); + Q_UNUSED(pmt); + // Output pin. + return E_NOTIMPL; +} + +HRESULT DirectShowIOSource::Disconnect() +{ + if (!m_peerPin) { + return S_FALSE; + } else if (m_state != State_Stopped) { + return VFW_E_NOT_STOPPED; + } else { + HRESULT hr = m_peerPin->Disconnect(); + + if (!SUCCEEDED(hr)) + return hr; + + if (m_allocator) { + m_allocator->Release(); + m_allocator = 0; + } + + m_peerPin->Release(); + m_peerPin = 0; + + m_mediaType.clear(); + + return S_OK; + } +} + +HRESULT DirectShowIOSource::ConnectedTo(IPin **ppPin) +{ + if (!ppPin) { + return E_POINTER; + } else { + QMutexLocker locker(&m_mutex); + + if (!m_peerPin) { + *ppPin = 0; + + return VFW_E_NOT_CONNECTED; + } else { + m_peerPin->AddRef(); + + *ppPin = m_peerPin; + + return S_OK; + } + } +} + +HRESULT DirectShowIOSource::ConnectionMediaType(AM_MEDIA_TYPE *pmt) +{ + if (!pmt) { + return E_POINTER; + } else { + QMutexLocker locker(&m_mutex); + + if (!m_peerPin) { + pmt = 0; + + return VFW_E_NOT_CONNECTED; + } else { + DirectShowMediaType::copy(pmt, m_mediaType); + + return S_OK; + } + } +} + +HRESULT DirectShowIOSource::QueryPinInfo(PIN_INFO *pInfo) +{ + if (!pInfo) { + return E_POINTER; + } else { + AddRef(); + + pInfo->pFilter = this; + pInfo->dir = PINDIR_OUTPUT; + + const int bytes = qMin(MAX_FILTER_NAME, (m_pinId.length() + 1) * 2); + + qMemCopy(pInfo->achName, m_pinId.utf16(), bytes); + + return S_OK; + } +} + +HRESULT DirectShowIOSource::QueryId(LPWSTR *Id) +{ + if (!Id) { + return E_POINTER; + } else { + const int bytes = (m_pinId.length() + 1) * 2; + + *Id = static_cast<LPWSTR>(::CoTaskMemAlloc(bytes)); + + qMemCopy(*Id, m_pinId.utf16(), bytes); + + return S_OK; + } +} + +HRESULT DirectShowIOSource::QueryAccept(const AM_MEDIA_TYPE *pmt) +{ + if (!pmt) { + return E_POINTER; + } else if (pmt->majortype == MEDIATYPE_Stream) { + return S_OK; + } else { + return S_FALSE; + } +} + +HRESULT DirectShowIOSource::EnumMediaTypes(IEnumMediaTypes **ppEnum) +{ + if (!ppEnum) { + return E_POINTER; + } else { + *ppEnum = createMediaTypeEnum(); + + return S_OK; + } +} + +HRESULT DirectShowIOSource::QueryInternalConnections(IPin **apPin, ULONG *nPin) +{ + Q_UNUSED(apPin); + Q_UNUSED(nPin); + + return E_NOTIMPL; +} + +HRESULT DirectShowIOSource::EndOfStream() +{ + return S_OK; +} + +HRESULT DirectShowIOSource::BeginFlush() +{ + return m_reader->BeginFlush(); +} + +HRESULT DirectShowIOSource::EndFlush() +{ + return m_reader->EndFlush(); +} + +HRESULT DirectShowIOSource::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) +{ + Q_UNUSED(tStart); + Q_UNUSED(tStop); + Q_UNUSED(dRate); + + return S_OK; +} + +HRESULT DirectShowIOSource::QueryDirection(PIN_DIRECTION *pPinDir) +{ + if (!pPinDir) { + return E_POINTER; + } else { + *pPinDir = PINDIR_OUTPUT; + + return S_OK; + } +} + +QT_END_NAMESPACE + +DirectShowRcSource::DirectShowRcSource(DirectShowEventLoop *loop) + : DirectShowIOSource(loop) +{ +} + +bool DirectShowRcSource::open(const QUrl &url) +{ + m_file.moveToThread(QCoreApplication::instance()->thread()); + + m_file.setFileName(QLatin1Char(':') + url.path()); + + qDebug("qrc file %s", qPrintable(m_file.fileName())); + + if (m_file.open(QIODevice::ReadOnly)) { + qDebug("Size %d", m_file.size()); + qDebug("Sequential %d", int(m_file.isSequential())); + + setDevice(&m_file); + + return true; + } else { + return false; + } +} diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowiosource.h b/src/plugins/mediaservices/directshow/mediaplayer/directshowiosource.h new file mode 100644 index 0000000..b626473 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowiosource.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWIOSOURCE_H +#define DIRECTSHOWIOSOURCE_H + +#include "directshowioreader.h" +#include "directshowmediatype.h" +#include "directshowmediatypelist.h" + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +#include <QtCore/qfile.h> + +class DirectShowIOSource + : public DirectShowMediaTypeList + , public IBaseFilter + , public IAMFilterMiscFlags + , public IPin +{ +public: + DirectShowIOSource(DirectShowEventLoop *loop); + ~DirectShowIOSource(); + + void setDevice(QIODevice *device); + void setAllocator(IMemAllocator *allocator); + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // IPersist + HRESULT STDMETHODCALLTYPE GetClassID(CLSID *pClassID); + + // IMediaFilter + HRESULT STDMETHODCALLTYPE Run(REFERENCE_TIME tStart); + HRESULT STDMETHODCALLTYPE Pause(); + HRESULT STDMETHODCALLTYPE Stop(); + + HRESULT STDMETHODCALLTYPE GetState(DWORD dwMilliSecsTimeout, FILTER_STATE *pState); + + HRESULT STDMETHODCALLTYPE SetSyncSource(IReferenceClock *pClock); + HRESULT STDMETHODCALLTYPE GetSyncSource(IReferenceClock **ppClock); + + // IBaseFilter + HRESULT STDMETHODCALLTYPE EnumPins(IEnumPins **ppEnum); + HRESULT STDMETHODCALLTYPE FindPin(LPCWSTR Id, IPin **ppPin); + + HRESULT STDMETHODCALLTYPE JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName); + + HRESULT STDMETHODCALLTYPE QueryFilterInfo(FILTER_INFO *pInfo); + HRESULT STDMETHODCALLTYPE QueryVendorInfo(LPWSTR *pVendorInfo); + + // IAMFilterMiscFlags + ULONG STDMETHODCALLTYPE GetMiscFlags(); + + // IPin + HRESULT STDMETHODCALLTYPE Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt); + HRESULT STDMETHODCALLTYPE ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt); + HRESULT STDMETHODCALLTYPE Disconnect(); + HRESULT STDMETHODCALLTYPE ConnectedTo(IPin **ppPin); + + HRESULT STDMETHODCALLTYPE ConnectionMediaType(AM_MEDIA_TYPE *pmt); + + HRESULT STDMETHODCALLTYPE QueryPinInfo(PIN_INFO *pInfo); + HRESULT STDMETHODCALLTYPE QueryId(LPWSTR *Id); + + HRESULT STDMETHODCALLTYPE QueryAccept(const AM_MEDIA_TYPE *pmt); + + HRESULT STDMETHODCALLTYPE EnumMediaTypes(IEnumMediaTypes **ppEnum); + + HRESULT STDMETHODCALLTYPE QueryInternalConnections(IPin **apPin, ULONG *nPin); + + HRESULT STDMETHODCALLTYPE EndOfStream(); + + HRESULT STDMETHODCALLTYPE BeginFlush(); + HRESULT STDMETHODCALLTYPE EndFlush(); + + HRESULT STDMETHODCALLTYPE NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate); + + HRESULT STDMETHODCALLTYPE QueryDirection(PIN_DIRECTION *pPinDir); + +private: + HRESULT tryConnect(IPin *pin, const AM_MEDIA_TYPE *type); + + volatile LONG m_ref; + FILTER_STATE m_state; + DirectShowIOReader *m_reader; + DirectShowEventLoop *m_loop; + IFilterGraph *m_graph; + IReferenceClock *m_clock; + IMemAllocator *m_allocator; + IPin *m_peerPin; + DirectShowMediaType m_mediaType; + QString m_filterName; + const QString m_pinId; + QMutex m_mutex; +}; + +class DirectShowRcSource : public DirectShowIOSource +{ +public: + DirectShowRcSource(DirectShowEventLoop *loop); + + bool open(const QUrl &url); + +private: + QFile m_file; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowmediatype.cpp b/src/plugins/mediaservices/directshow/mediaplayer/directshowmediatype.cpp new file mode 100644 index 0000000..f719b29 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowmediatype.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowmediatype.h" + + +QT_BEGIN_NAMESPACE + +namespace +{ + struct TypeLookup + { + QVideoFrame::PixelFormat pixelFormat; + GUID mediaType; + }; + + static const TypeLookup qt_typeLookup[] = + { + { QVideoFrame::Format_RGB32, MEDIASUBTYPE_RGB32 }, + { QVideoFrame::Format_BGR24, MEDIASUBTYPE_RGB24 }, + { QVideoFrame::Format_RGB565, MEDIASUBTYPE_RGB565 }, + { QVideoFrame::Format_RGB555, MEDIASUBTYPE_RGB555 }, + { QVideoFrame::Format_AYUV444, MEDIASUBTYPE_AYUV }, + { QVideoFrame::Format_YUYV, MEDIASUBTYPE_YUY2 }, + { QVideoFrame::Format_UYVY, MEDIASUBTYPE_UYVY }, + { QVideoFrame::Format_IMC1, MEDIASUBTYPE_IMC1 }, + { QVideoFrame::Format_IMC2, MEDIASUBTYPE_IMC2 }, + { QVideoFrame::Format_IMC3, MEDIASUBTYPE_IMC3 }, + { QVideoFrame::Format_IMC4, MEDIASUBTYPE_IMC4 }, + { QVideoFrame::Format_YV12, MEDIASUBTYPE_YV12 }, + { QVideoFrame::Format_NV12, MEDIASUBTYPE_NV12 }, + { QVideoFrame::Format_YUV420P, MEDIASUBTYPE_IYUV } + }; +} + +void DirectShowMediaType::copy(AM_MEDIA_TYPE *target, const AM_MEDIA_TYPE &source) +{ + *target = source; + + if (source.cbFormat > 0) { + target->pbFormat = reinterpret_cast<PBYTE>(CoTaskMemAlloc(source.cbFormat)); + memcpy(target->pbFormat, source.pbFormat, source.cbFormat); + } + if (target->pUnk) + target->pUnk->AddRef(); +} + +void DirectShowMediaType::deleteType(AM_MEDIA_TYPE *type) +{ + freeData(type); + + CoTaskMemFree(type); +} + +void DirectShowMediaType::freeData(AM_MEDIA_TYPE *type) +{ + if (type->cbFormat > 0) + CoTaskMemFree(type->pbFormat); + + if (type->pUnk) + type->pUnk->Release(); +} + + +GUID DirectShowMediaType::convertPixelFormat(QVideoFrame::PixelFormat format) +{ + const int count = sizeof(qt_typeLookup) / sizeof(TypeLookup); + + for (int i = 0; i < count; ++i) + if (qt_typeLookup[i].pixelFormat == format) + return qt_typeLookup[i].mediaType; + return MEDIASUBTYPE_None; +} + +QVideoSurfaceFormat DirectShowMediaType::formatFromType(const AM_MEDIA_TYPE &type) +{ + const int count = sizeof(qt_typeLookup) / sizeof(TypeLookup); + + for (int i = 0; i < count; ++i) { + if (IsEqualGUID(qt_typeLookup[i].mediaType, type.subtype) && type.cbFormat > 0) { + if (IsEqualGUID(type.formattype, FORMAT_VideoInfo)) { + VIDEOINFOHEADER *header = reinterpret_cast<VIDEOINFOHEADER *>(type.pbFormat); + + QVideoSurfaceFormat format( + QSize(header->bmiHeader.biWidth, qAbs(header->bmiHeader.biHeight)), + qt_typeLookup[i].pixelFormat); + + if (header->AvgTimePerFrame > 0) + format.setFrameRate(10000 /header->AvgTimePerFrame); + + format.setScanLineDirection(header->bmiHeader.biHeight < 0 + ? QVideoSurfaceFormat::TopToBottom + : QVideoSurfaceFormat::BottomToTop); + + return format; + } else if (IsEqualGUID(type.formattype, FORMAT_VideoInfo2)) { + VIDEOINFOHEADER2 *header = reinterpret_cast<VIDEOINFOHEADER2 *>(type.pbFormat); + + QVideoSurfaceFormat format( + QSize(header->bmiHeader.biWidth, qAbs(header->bmiHeader.biHeight)), + qt_typeLookup[i].pixelFormat); + + if (header->AvgTimePerFrame > 0) + format.setFrameRate(10000 / header->AvgTimePerFrame); + + format.setScanLineDirection(header->bmiHeader.biHeight < 0 + ? QVideoSurfaceFormat::TopToBottom + : QVideoSurfaceFormat::BottomToTop); + + return format; + } + } + } + return QVideoSurfaceFormat(); +} + +int DirectShowMediaType::bytesPerLine(const QVideoSurfaceFormat &format) +{ + switch (format.pixelFormat()) { + // 32 bpp packed formats. + case QVideoFrame::Format_RGB32: + case QVideoFrame::Format_AYUV444: + return format.frameWidth() * 4; + // 24 bpp packed formats. + case QVideoFrame::Format_RGB24: + return format.frameWidth() * 3 + 3 - format.frameWidth() % 4; + // 16 bpp packed formats. + case QVideoFrame::Format_RGB565: + case QVideoFrame::Format_RGB555: + case QVideoFrame::Format_YUYV: + case QVideoFrame::Format_UYVY: + return format.frameWidth() * 2 + 3 - format.frameWidth() % 4; + // Planar formats. + case QVideoFrame::Format_IMC1: + case QVideoFrame::Format_IMC2: + case QVideoFrame::Format_IMC3: + case QVideoFrame::Format_IMC4: + case QVideoFrame::Format_YV12: + case QVideoFrame::Format_NV12: + case QVideoFrame::Format_YUV420P: + return format.frameWidth() + 3 - format.frameWidth() % 4; + default: + return 0; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowmediatype.h b/src/plugins/mediaservices/directshow/mediaplayer/directshowmediatype.h new file mode 100644 index 0000000..3cc7307 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowmediatype.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWMEDIATYPE_H +#define DIRECTSHOWMEDIATYPE_H + +#include <QtMultimedia/qvideosurfaceformat.h> + +#include <dshow.h> +#include <dvdmedia.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class DirectShowMediaType : public AM_MEDIA_TYPE +{ +public: + DirectShowMediaType() { memset(this, 0, sizeof(DirectShowMediaType)); } + DirectShowMediaType(const AM_MEDIA_TYPE &type) { copy(this, type); } + DirectShowMediaType(const DirectShowMediaType &other) { copy(this, other); } + DirectShowMediaType &operator =(const AM_MEDIA_TYPE &type) { + freeData(this); copy(this, type); return *this; } + DirectShowMediaType &operator =(const DirectShowMediaType &other) { + freeData(this); copy(this, other); return *this; } + ~DirectShowMediaType() { freeData(this); } + + void clear() { freeData(this); memset(this, 0, sizeof(DirectShowMediaType)); } + + static void copy(AM_MEDIA_TYPE *target, const AM_MEDIA_TYPE &source); + static void freeData(AM_MEDIA_TYPE *type); + static void deleteType(AM_MEDIA_TYPE *type); + + static GUID convertPixelFormat(QVideoFrame::PixelFormat format); + static QVideoSurfaceFormat formatFromType(const AM_MEDIA_TYPE &type); + + static int bytesPerLine(const QVideoSurfaceFormat &format); +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowmediatypelist.cpp b/src/plugins/mediaservices/directshow/mediaplayer/directshowmediatypelist.cpp new file mode 100644 index 0000000..f67794a --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowmediatypelist.cpp @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowmediatypelist.h" + +#include "directshowmediatype.h" +#include "videosurfacefilter.h" + + +QT_BEGIN_NAMESPACE + +class DirectShowMediaTypeEnum : public IEnumMediaTypes +{ +public: + DirectShowMediaTypeEnum(DirectShowMediaTypeList *list, int token, int index = 0); + ~DirectShowMediaTypeEnum(); + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // IEnumMediaTypes + HRESULT STDMETHODCALLTYPE Next( + ULONG cMediaTypes, AM_MEDIA_TYPE **ppMediaTypes, ULONG *pcFetched); + HRESULT STDMETHODCALLTYPE Skip(ULONG cMediaTypes); + HRESULT STDMETHODCALLTYPE Reset(); + + HRESULT STDMETHODCALLTYPE Clone(IEnumMediaTypes **ppEnum); + +private: + LONG m_ref; + DirectShowMediaTypeList *m_list; + int m_mediaTypeToken; + int m_index; +}; + + +DirectShowMediaTypeEnum::DirectShowMediaTypeEnum( + DirectShowMediaTypeList *list, int token, int index) + : m_ref(1) + , m_list(list) + , m_mediaTypeToken(token) + , m_index(index) +{ + m_list->AddRef(); +} + +DirectShowMediaTypeEnum::~DirectShowMediaTypeEnum() +{ + m_list->Release(); +} + +HRESULT DirectShowMediaTypeEnum::QueryInterface(REFIID riid, void **ppvObject) +{ + if (!ppvObject) { + return E_POINTER; + } else if (riid == IID_IUnknown + || riid == IID_IEnumMediaTypes) { + *ppvObject = static_cast<IEnumMediaTypes *>(this); + } else { + *ppvObject = 0; + + return E_NOINTERFACE; + } + + AddRef(); + + return S_OK; +} + +ULONG DirectShowMediaTypeEnum::AddRef() +{ + return InterlockedIncrement(&m_ref); +} + +ULONG DirectShowMediaTypeEnum::Release() +{ + ULONG ref = InterlockedDecrement(&m_ref); + + if (ref == 0) { + delete this; + } + + return ref; +} + +HRESULT DirectShowMediaTypeEnum::Next( + ULONG cMediaTypes, AM_MEDIA_TYPE **ppMediaTypes, ULONG *pcFetched) +{ + return m_list->nextMediaType(m_mediaTypeToken, &m_index, cMediaTypes, ppMediaTypes, pcFetched); +} + +HRESULT DirectShowMediaTypeEnum::Skip(ULONG cMediaTypes) +{ + return m_list->skipMediaType(m_mediaTypeToken, &m_index, cMediaTypes); +} + +HRESULT DirectShowMediaTypeEnum::Reset() +{ + m_mediaTypeToken = m_list->currentMediaTypeToken(); + m_index = 0; + + return S_OK; +} + +HRESULT DirectShowMediaTypeEnum::Clone(IEnumMediaTypes **ppEnum) +{ + return m_list->cloneMediaType(m_mediaTypeToken, m_index, ppEnum); +} + + +DirectShowMediaTypeList::DirectShowMediaTypeList() + : m_mediaTypeToken(0) +{ +} + +IEnumMediaTypes *DirectShowMediaTypeList::createMediaTypeEnum() +{ + return new DirectShowMediaTypeEnum(this, m_mediaTypeToken, 0); +} + + +void DirectShowMediaTypeList::setMediaTypes(const QVector<AM_MEDIA_TYPE> &types) +{ + ++m_mediaTypeToken; + + m_mediaTypes = types; +} + + +int DirectShowMediaTypeList::currentMediaTypeToken() +{ + return m_mediaTypeToken; +} + +HRESULT DirectShowMediaTypeList::nextMediaType( + int token, int *index, ULONG count, AM_MEDIA_TYPE **types, ULONG *fetchedCount) +{ + if (!types || (count != 1 && !fetchedCount)) { + return E_POINTER; + } else if (m_mediaTypeToken != token) { + return VFW_E_ENUM_OUT_OF_SYNC; + } else { + int boundedCount = qBound<int>(0, count, m_mediaTypes.count() - *index); + + for (int i = 0; i < boundedCount; ++i, ++(*index)) { + types[i] = reinterpret_cast<AM_MEDIA_TYPE *>(CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))); + + if (types[i]) { + DirectShowMediaType::copy(types[i], m_mediaTypes.at(*index)); + } else { + for (--i; i >= 0; --i) + CoTaskMemFree(types[i]); + + if (fetchedCount) + *fetchedCount = 0; + + return E_OUTOFMEMORY; + } + } + if (fetchedCount) + *fetchedCount = boundedCount; + + return boundedCount == count ? S_OK : S_FALSE; + } +} + +HRESULT DirectShowMediaTypeList::skipMediaType(int token, int *index, ULONG count) +{ + if (m_mediaTypeToken != token) { + return VFW_E_ENUM_OUT_OF_SYNC; + } else { + *index = qMin<int>(*index + count, m_mediaTypes.size()); + + return *index < m_mediaTypes.size() ? S_OK : S_FALSE; + } +} + +HRESULT DirectShowMediaTypeList::cloneMediaType(int token, int index, IEnumMediaTypes **enumeration) +{ + if (m_mediaTypeToken != token) { + return VFW_E_ENUM_OUT_OF_SYNC; + } else { + *enumeration = new DirectShowMediaTypeEnum(this, token, index); + + return S_OK; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowmediatypelist.h b/src/plugins/mediaservices/directshow/mediaplayer/directshowmediatypelist.h new file mode 100644 index 0000000..b49f24c --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowmediatypelist.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWMEDIATYPELIST_H +#define DIRECTSHOWMEDIATYPELIST_H + +#include <QtCore/qvector.h> + +#include <dshow.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class DirectShowMediaTypeList : public IUnknown +{ +public: + DirectShowMediaTypeList(); + + IEnumMediaTypes *createMediaTypeEnum(); + + void setMediaTypes(const QVector<AM_MEDIA_TYPE> &types); + + virtual int currentMediaTypeToken(); + virtual HRESULT nextMediaType( + int token, int *index, ULONG count, AM_MEDIA_TYPE **types, ULONG *fetchedCount); + virtual HRESULT skipMediaType(int token, int *index, ULONG count); + virtual HRESULT cloneMediaType(int token, int index, IEnumMediaTypes **enumeration); + +private: + int m_mediaTypeToken; + QVector<AM_MEDIA_TYPE> m_mediaTypes; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowmetadatacontrol.cpp b/src/plugins/mediaservices/directshow/mediaplayer/directshowmetadatacontrol.cpp new file mode 100644 index 0000000..7b2552f --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowmetadatacontrol.cpp @@ -0,0 +1,370 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <dshow.h> +#include <initguid.h> +#include <qnetwork.h> + +#include "directshowmetadatacontrol.h" + +#include "directshowplayerservice.h" + +#include <QtCore/qcoreapplication.h> + + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_WMSDK +namespace +{ + struct QWMMetaDataKeyLookup + { + QtMultimedia::MetaData key; + const wchar_t *token; + }; +} + +static const QWMMetaDataKeyLookup qt_wmMetaDataKeys[] = +{ + { QtMultimedia::Title, L"Title" }, + { QtMultimedia::SubTitle, L"WM/SubTitle" }, + { QtMultimedia::Author, L"Author" }, + { QtMultimedia::Comment, L"Comment" }, + { QtMultimedia::Description, L"Description" }, + { QtMultimedia::Category, L"WM/Category" }, + { QtMultimedia::Genre, L"WM/Genre" }, + //{ QtMultimedia::Date, 0 }, + { QtMultimedia::Year, L"WM/Year" }, + { QtMultimedia::UserRating, L"UserRating" }, + //{ QtMultimedia::MetaDatawords, 0 }, + { QtMultimedia::Language, L"Language" }, + { QtMultimedia::Publisher, L"WM/Publisher" }, + { QtMultimedia::Copyright, L"Copyright" }, + { QtMultimedia::ParentalRating, L"ParentalRating" }, + { QtMultimedia::RatingOrganisation, L"RatingOrganisation" }, + + // Media + { QtMultimedia::Size, L"FileSize" }, + { QtMultimedia::MediaType, L"MediaType" }, + { QtMultimedia::Duration, L"Duration" }, + + // Audio + { QtMultimedia::AudioBitRate, L"AudioBitRate" }, + { QtMultimedia::AudioCodec, L"AudioCodec" }, + { QtMultimedia::ChannelCount, L"ChannelCount" }, + { QtMultimedia::SampleRate, L"Frequency" }, + + // Music + { QtMultimedia::AlbumTitle, L"WM/AlbumTitle" }, + { QtMultimedia::AlbumArtist, L"WM/AlbumArtist" }, + { QtMultimedia::ContributingArtist, L"Author" }, + { QtMultimedia::Composer, L"WM/Composer" }, + { QtMultimedia::Conductor, L"WM/Conductor" }, + { QtMultimedia::Lyrics, L"WM/Lyrics" }, + { QtMultimedia::Mood, L"WM/Mood" }, + { QtMultimedia::TrackNumber, L"WM/TrackNumber" }, + //{ QtMultimedia::TrackCount, 0 }, + //{ QtMultimedia::CoverArtUriSmall, 0 }, + //{ QtMultimedia::CoverArtUriLarge, 0 }, + + // Image/Video + //{ QtMultimedia::Resolution, 0 }, + //{ QtMultimedia::PixelAspectRatio, 0 }, + + // Video + //{ QtMultimedia::FrameRate, 0 }, + { QtMultimedia::VideoBitRate, L"VideoBitRate" }, + { QtMultimedia::VideoCodec, L"VideoCodec" }, + + //{ QtMultimedia::PosterUri, 0 }, + + // Movie + { QtMultimedia::ChapterNumber, L"ChapterNumber" }, + { QtMultimedia::Director, L"WM/Director" }, + { QtMultimedia::LeadPerformer, L"LeadPerformer" }, + { QtMultimedia::Writer, L"WM/Writer" }, + + // Photos + { QtMultimedia::CameraManufacturer, L"CameraManufacturer" }, + { QtMultimedia::CameraModel, L"CameraModel" }, + { QtMultimedia::Event, L"Event" }, + { QtMultimedia::Subject, L"Subject" } +}; + +static QVariant getValue(IWMHeaderInfo *header, const wchar_t *key) +{ + WORD streamNumber = 0; + WMT_ATTR_DATATYPE type = WMT_TYPE_DWORD; + WORD size = 0; + + if (header->GetAttributeByName(&streamNumber, key, &type, 0, &size) == S_OK) { + switch (type) { + case WMT_TYPE_DWORD: + if (size == sizeof(DWORD)) { + DWORD word; + if (header->GetAttributeByName( + &streamNumber, + key, + &type, + reinterpret_cast<BYTE *>(&word), + &size) == S_OK) { + return int(word); + } + } + break; + case WMT_TYPE_STRING: + { + QString string; + string.resize(size / 2 - 1); + + if (header->GetAttributeByName( + &streamNumber, + key, + &type, + reinterpret_cast<BYTE *>(const_cast<ushort *>(string.utf16())), + &size) == S_OK) { + return string; + } + } + break; + case WMT_TYPE_BINARY: + { + QByteArray bytes; + bytes.resize(size); + if (header->GetAttributeByName( + &streamNumber, + key, + &type, + reinterpret_cast<BYTE *>(bytes.data()), + &size) == S_OK) { + return bytes; + } + } + break; + case WMT_TYPE_BOOL: + if (size == sizeof(DWORD)) { + DWORD word; + if (header->GetAttributeByName( + &streamNumber, + key, + &type, + reinterpret_cast<BYTE *>(&word), + &size) == S_OK) { + return bool(word); + } + } + break; + case WMT_TYPE_QWORD: + if (size == sizeof(QWORD)) { + QWORD word; + if (header->GetAttributeByName( + &streamNumber, + key, + &type, + reinterpret_cast<BYTE *>(&word), + &size) == S_OK) { + return qint64(word); + } + } + break; + case WMT_TYPE_WORD: + if (size == sizeof(WORD)){ + WORD word; + if (header->GetAttributeByName( + &streamNumber, + key, + &type, + reinterpret_cast<BYTE *>(&word), + &size) == S_OK) { + return short(word); + } + } + break; + case WMT_TYPE_GUID: + if (size == 16) { + } + break; + default: + break; + } + } + return QVariant(); +} +#endif + +DirectShowMetaDataControl::DirectShowMetaDataControl(QObject *parent) + : QMetaDataControl(parent) + , m_content(0) +#ifndef QT_NO_WMSDK + , m_headerInfo(0) +#endif +{ +} + +DirectShowMetaDataControl::~DirectShowMetaDataControl() +{ +} + +bool DirectShowMetaDataControl::isWritable() const +{ + return false; +} + +bool DirectShowMetaDataControl::isMetaDataAvailable() const +{ +#ifndef QT_NO_WMSDK + return m_content || m_headerInfo; +#else + return m_content; +#endif +} + +QVariant DirectShowMetaDataControl::metaData(QtMultimedia::MetaData key) const +{ + QVariant value; + +#ifndef QT_NO_WMSDK + if (m_headerInfo) { + static const int count = sizeof(qt_wmMetaDataKeys) / sizeof(QWMMetaDataKeyLookup); + for (int i = 0; i < count; ++i) { + if (qt_wmMetaDataKeys[i].key == key) { + value = getValue(m_headerInfo, qt_wmMetaDataKeys[i].token); + break; + } + } + } else if (m_content) { +#else + if (m_content) { +#endif + BSTR string = 0; + + switch (key) { + case QtMultimedia::Author: + m_content->get_AuthorName(&string); + break; + case QtMultimedia::Title: + m_content->get_Title(&string); + break; + case QtMultimedia::ParentalRating: + m_content->get_Rating(&string); + break; + case QtMultimedia::Description: + m_content->get_Description(&string); + break; + case QtMultimedia::Copyright: + m_content->get_Copyright(&string); + break; + default: + break; + } + + if (string) { + value = QString::fromUtf16(string, ::SysStringLen(string)); + + ::SysFreeString(string); + } + } + return value; +} + +void DirectShowMetaDataControl::setMetaData(QtMultimedia::MetaData, const QVariant &) +{ +} + +QList<QtMultimedia::MetaData> DirectShowMetaDataControl::availableMetaData() const +{ + return QList<QtMultimedia::MetaData>(); +} + +QVariant DirectShowMetaDataControl::extendedMetaData(const QString &) const +{ + return QVariant(); +} + +void DirectShowMetaDataControl::setExtendedMetaData(const QString &, const QVariant &) +{ +} + +QStringList DirectShowMetaDataControl::availableExtendedMetaData() const +{ + return QStringList(); +} + +void DirectShowMetaDataControl::updateGraph(IFilterGraph2 *graph, IBaseFilter *source) +{ + if (m_content) + m_content->Release(); + + if (!graph || graph->QueryInterface( + IID_IAMMediaContent, reinterpret_cast<void **>(&m_content)) != S_OK) { + m_content = 0; + } + +#ifdef QT_NO_WMSDK + Q_UNUSED(source); +#else + if (m_headerInfo) + m_headerInfo->Release(); + + m_headerInfo = com_cast<IWMHeaderInfo>(source); +#endif + // DirectShowMediaPlayerService holds a lock at this point so defer emitting signals to a later + // time. + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(MetaDataChanged))); +} + +void DirectShowMetaDataControl::customEvent(QEvent *event) +{ + if (event->type() == QEvent::Type(MetaDataChanged)) { + event->accept(); + + emit metaDataChanged(); +#ifndef QT_NO_WMSDK + emit metaDataAvailableChanged(m_content || m_headerInfo); +#else + emit metaDataAvailableChanged(m_content); +#endif + } else { + QMetaDataControl::customEvent(event); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowmetadatacontrol.h b/src/plugins/mediaservices/directshow/mediaplayer/directshowmetadatacontrol.h new file mode 100644 index 0000000..966f9b8 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowmetadatacontrol.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWMETADATACONTROL_H +#define DIRECTSHOWMETADATACONTROL_H + +#include <QtMultimedia/qmetadatacontrol.h> + +#include <dshow.h> +#include <qnetwork.h> + +#ifndef QT_NO_WMSDK +#include <wmsdk.h> +#endif + +#include <QtCore/qcoreevent.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class DirectShowPlayerService; + + +class DirectShowMetaDataControl : public QMetaDataControl +{ + Q_OBJECT +public: + DirectShowMetaDataControl(QObject *parent = 0); + ~DirectShowMetaDataControl(); + + bool isWritable() const; + bool isMetaDataAvailable() const; + + QVariant metaData(QtMultimedia::MetaData key) const; + void setMetaData(QtMultimedia::MetaData key, const QVariant &value); + QList<QtMultimedia::MetaData> availableMetaData() const; + + QVariant extendedMetaData(const QString &key) const; + void setExtendedMetaData(const QString &key, const QVariant &value); + QStringList availableExtendedMetaData() const; + + void updateGraph(IFilterGraph2 *graph, IBaseFilter *source); + +protected: + void customEvent(QEvent *event); + +private: + enum Event + { + MetaDataChanged = QEvent::User + }; + + IAMMediaContent *m_content; +#ifndef QT_NO_WMSDK + IWMHeaderInfo *m_headerInfo; +#endif +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowpinenum.cpp b/src/plugins/mediaservices/directshow/mediaplayer/directshowpinenum.cpp new file mode 100644 index 0000000..19f65da --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowpinenum.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowpinenum.h" + +#include <QtMultimedia> + + +QT_BEGIN_NAMESPACE + +DirectShowPinEnum::DirectShowPinEnum(const QList<IPin *> &pins) + : m_ref(1) + , m_pins(pins) + , m_index(0) +{ + foreach (IPin *pin, m_pins) + pin->AddRef(); +} + +DirectShowPinEnum::~DirectShowPinEnum() +{ + foreach (IPin *pin, m_pins) + pin->Release(); +} + +HRESULT DirectShowPinEnum::QueryInterface(REFIID riid, void **ppvObject) +{ + if (riid == IID_IUnknown + || riid == IID_IEnumPins) { + AddRef(); + + *ppvObject = static_cast<IEnumPins *>(this); + + return S_OK; + } else { + *ppvObject = 0; + + return E_NOINTERFACE; + } +} + +ULONG DirectShowPinEnum::AddRef() +{ + return InterlockedIncrement(&m_ref); +} + +ULONG DirectShowPinEnum::Release() +{ + ULONG ref = InterlockedDecrement(&m_ref); + + if (ref == 0) { + delete this; + } + + return ref; +} + +HRESULT DirectShowPinEnum::Next(ULONG cPins, IPin **ppPins, ULONG *pcFetched) +{ + if (ppPins && (pcFetched || cPins == 1)) { + ULONG count = qBound<ULONG>(0, cPins, m_pins.count() - m_index); + + for (ULONG i = 0; i < count; ++i, ++m_index) { + ppPins[i] = m_pins.at(m_index); + ppPins[i]->AddRef(); + } + + if (pcFetched) + *pcFetched = count; + + return count == cPins ? S_OK : S_FALSE; + } else { + return E_POINTER; + } +} + +HRESULT DirectShowPinEnum::Skip(ULONG cPins) +{ + m_index = qMin(int(m_index + cPins), m_pins.count()); + + return m_index < m_pins.count() ? S_OK : S_FALSE; +} + +HRESULT DirectShowPinEnum::Reset() +{ + m_index = 0; + + return S_OK; +} + +HRESULT DirectShowPinEnum::Clone(IEnumPins **ppEnum) +{ + if (ppEnum) { + *ppEnum = new DirectShowPinEnum(m_pins); + + return S_OK; + } else { + return E_POINTER; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowpinenum.h b/src/plugins/mediaservices/directshow/mediaplayer/directshowpinenum.h new file mode 100644 index 0000000..40d99ea --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowpinenum.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWPINENUM_H +#define DIRECTSHOWPINENUM_H + +#include <QtCore/qlist.h> + +#include <dshow.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class DirectShowPinEnum : public IEnumPins +{ +public: + DirectShowPinEnum(const QList<IPin *> &pins); + ~DirectShowPinEnum(); + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // IEnumPins + HRESULT STDMETHODCALLTYPE Next(ULONG cPins, IPin **ppPins, ULONG *pcFetched); + HRESULT STDMETHODCALLTYPE Skip(ULONG cPins); + HRESULT STDMETHODCALLTYPE Reset(); + HRESULT STDMETHODCALLTYPE Clone(IEnumPins **ppEnum); + +private: + LONG m_ref; + QList<IPin *> m_pins; + int m_index; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowplayercontrol.cpp b/src/plugins/mediaservices/directshow/mediaplayer/directshowplayercontrol.cpp new file mode 100644 index 0000000..b024557 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowplayercontrol.cpp @@ -0,0 +1,395 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowplayercontrol.h" + +#include "directshowplayerservice.h" + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qmath.h> + + +QT_BEGIN_NAMESPACE + +static int volumeToDecibels(int volume) +{ + if (volume == 0) { + return -10000; + } else if (volume == 100) { + return 0; +#ifdef QT_USE_MATH_H_FLOATS + } else if (sizeof(qreal) == sizeof(float)) { + return qRound(::log10f(float(volume) / 100) * 5000); +#endif + } else { + return qRound(::log10(qreal(volume) / 100) * 5000); + } +} + +static int decibelsToVolume(int dB) +{ + if (dB == -10000) { + return 0; + } else if (dB == 0) { + return 100; + } else { + return qRound(100 * qPow(10, qreal(dB) / 5000)); + } +} + +DirectShowPlayerControl::DirectShowPlayerControl(DirectShowPlayerService *service, QObject *parent) + : QMediaPlayerControl(parent) + , m_service(service) + , m_audio(0) + , m_updateProperties(0) + , m_state(QMediaPlayer::StoppedState) + , m_status(QMediaPlayer::UnknownMediaStatus) + , m_error(QMediaPlayer::NoError) + , m_streamTypes(0) + , m_muteVolume(-1) + , m_position(0) + , m_duration(0) + , m_playbackRate(0) + , m_seekable(false) +{ +} + +DirectShowPlayerControl::~DirectShowPlayerControl() +{ + if (m_audio) + m_audio->Release(); +} + +QMediaPlayer::State DirectShowPlayerControl::state() const +{ + return m_state; +} + +QMediaPlayer::MediaStatus DirectShowPlayerControl::mediaStatus() const +{ + return m_status; +} + +qint64 DirectShowPlayerControl::duration() const +{ + return m_duration; +} + +qint64 DirectShowPlayerControl::position() const +{ + return const_cast<qint64 &>(m_position) = m_service->position(); +} + +void DirectShowPlayerControl::setPosition(qint64 position) +{ + m_service->seek(position); +} + +int DirectShowPlayerControl::volume() const +{ + if (m_muteVolume >= 0) { + return m_muteVolume; + } else if (m_audio) { + long dB = 0; + + m_audio->get_Volume(&dB); + + return decibelsToVolume(dB); + } else { + return 0; + } +} + +void DirectShowPlayerControl::setVolume(int volume) +{ + int boundedVolume = qBound(0, volume, 100); + + if (m_muteVolume >= 0) { + m_muteVolume = boundedVolume; + + emit volumeChanged(m_muteVolume); + } else if (m_audio) { + m_audio->put_Volume(volumeToDecibels(volume)); + + emit volumeChanged(boundedVolume); + } +} + +bool DirectShowPlayerControl::isMuted() const +{ + return m_muteVolume >= 0; +} + +void DirectShowPlayerControl::setMuted(bool muted) +{ + if (muted && m_muteVolume < 0) { + if (m_audio) { + long dB = 0; + + m_audio->get_Volume(&dB); + + m_muteVolume = decibelsToVolume(dB); + + m_audio->put_Volume(-10000); + } else { + m_muteVolume = 0; + } + + emit mutedChanged(muted); + } else if (!muted && m_muteVolume >= 0) { + if (m_audio) { + m_audio->put_Volume(volumeToDecibels(m_muteVolume)); + } + m_muteVolume = -1; + + emit mutedChanged(muted); + } +} + +int DirectShowPlayerControl::bufferStatus() const +{ + return m_service->bufferStatus(); +} + +bool DirectShowPlayerControl::isAudioAvailable() const +{ + return m_streamTypes & DirectShowPlayerService::AudioStream; +} + +bool DirectShowPlayerControl::isVideoAvailable() const +{ + return m_streamTypes & DirectShowPlayerService::VideoStream; +} + +bool DirectShowPlayerControl::isSeekable() const +{ + return m_seekable; +} + +QMediaTimeRange DirectShowPlayerControl::availablePlaybackRanges() const +{ + return m_service->availablePlaybackRanges(); +} + +qreal DirectShowPlayerControl::playbackRate() const +{ + return m_playbackRate; +} + +void DirectShowPlayerControl::setPlaybackRate(qreal rate) +{ + if (m_playbackRate != rate) { + m_service->setRate(rate); + + emit playbackRateChanged(m_playbackRate = rate); + } +} + +QMediaContent DirectShowPlayerControl::media() const +{ + return m_media; +} + +const QIODevice *DirectShowPlayerControl::mediaStream() const +{ + return m_stream; +} + +void DirectShowPlayerControl::setMedia(const QMediaContent &media, QIODevice *stream) +{ + m_media = media; + m_stream = stream; + + m_updateProperties &= PlaybackRateProperty; + + m_service->load(media, stream); + + emitPropertyChanges(); +} + +void DirectShowPlayerControl::play() +{ + m_service->play(); + emit stateChanged(m_state = QMediaPlayer::PlayingState); +} + +void DirectShowPlayerControl::pause() +{ + m_service->pause(); + emit stateChanged(m_state = QMediaPlayer::PausedState); +} + +void DirectShowPlayerControl::stop() +{ + m_service->stop(); + emit stateChanged(m_state = QMediaPlayer::StoppedState); +} + +void DirectShowPlayerControl::customEvent(QEvent *event) +{ + if (event->type() == QEvent::Type(PropertiesChanged)) { + emitPropertyChanges(); + + event->accept(); + } else { + QMediaPlayerControl::customEvent(event); + } +} + +void DirectShowPlayerControl::emitPropertyChanges() +{ + int properties = m_updateProperties; + m_updateProperties = 0; + + if ((properties & ErrorProperty) && m_error != QMediaPlayer::NoError) + emit error(m_error, m_errorString); + + if (properties & PlaybackRateProperty) + emit playbackRateChanged(m_playbackRate); + + if (properties & StreamTypesProperty) { + emit audioAvailableChanged(m_streamTypes & DirectShowPlayerService::AudioStream); + emit videoAvailableChanged(m_streamTypes & DirectShowPlayerService::VideoStream); + } + + if (properties & PositionProperty) + emit positionChanged(m_position); + + if (properties & DurationProperty) + emit durationChanged(m_duration); + + if (properties & SeekableProperty) + emit seekableChanged(m_seekable); + + if (properties & StatusProperty) + emit mediaStatusChanged(m_status); + + if (properties & StateProperty) + emit stateChanged(m_state); +} + +void DirectShowPlayerControl::scheduleUpdate(int properties) +{ + if (m_updateProperties == 0) + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(PropertiesChanged))); + + m_updateProperties |= properties; +} + +void DirectShowPlayerControl::updateState(QMediaPlayer::State state) +{ + if (m_state != state) { + m_state = state; + + scheduleUpdate(StateProperty); + } +} + +void DirectShowPlayerControl::updateStatus(QMediaPlayer::MediaStatus status) +{ + if (m_status != status) { + m_status = status; + + scheduleUpdate(StatusProperty); + } +} + +void DirectShowPlayerControl::updateMediaInfo(qint64 duration, int streamTypes, bool seekable) +{ + int properties = 0; + + if (m_duration != duration) { + m_duration = duration; + + properties |= DurationProperty; + } + if (m_streamTypes != streamTypes) { + m_streamTypes = streamTypes; + + properties |= StreamTypesProperty; + } + + if (m_seekable != seekable) { + m_seekable = seekable; + + properties |= SeekableProperty; + } + + if (properties != 0) + scheduleUpdate(properties); +} + +void DirectShowPlayerControl::updatePlaybackRate(qreal rate) +{ + if (m_playbackRate != rate) { + m_playbackRate = rate; + + scheduleUpdate(PlaybackRateProperty); + } +} + +void DirectShowPlayerControl::updateAudioOutput(IBaseFilter *filter) +{ + if (m_audio) + m_audio->Release(); + + m_audio = com_cast<IBasicAudio>(filter); +} + +void DirectShowPlayerControl::updateError(QMediaPlayer::Error error, const QString &errorString) +{ + m_error = error; + m_errorString = errorString; + + if (m_error != QMediaPlayer::NoError) + scheduleUpdate(ErrorProperty); +} + +void DirectShowPlayerControl::updatePosition(qint64 position) +{ + if (m_position != position) { + m_position = position; + + scheduleUpdate(PositionProperty); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowplayercontrol.h b/src/plugins/mediaservices/directshow/mediaplayer/directshowplayercontrol.h new file mode 100644 index 0000000..dd25d30 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowplayercontrol.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWPLAYERCONTROL_H +#define DIRECTSHOWPLAYERCONTROL_H + +#include <QtMultimedia/qmediacontent.h> +#include <QtMultimedia/qmediaplayercontrol.h> + +#include <QtCore/qcoreevent.h> + +#include "directshowplayerservice.h" + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class DirectShowPlayerControl : public QMediaPlayerControl +{ + Q_OBJECT +public: + DirectShowPlayerControl(DirectShowPlayerService *service, QObject *parent = 0); + ~DirectShowPlayerControl(); + + QMediaPlayer::State state() const; + + QMediaPlayer::MediaStatus mediaStatus() const; + + qint64 duration() const; + + qint64 position() const; + void setPosition(qint64 position); + + int volume() const; + void setVolume(int volume); + + bool isMuted() const; + void setMuted(bool muted); + + int bufferStatus() const; + + bool isAudioAvailable() const; + bool isVideoAvailable() const; + + bool isSeekable() const; + + QMediaTimeRange availablePlaybackRanges() const; + + qreal playbackRate() const; + void setPlaybackRate(qreal rate); + + QMediaContent media() const; + const QIODevice *mediaStream() const; + void setMedia(const QMediaContent &media, QIODevice *stream); + + void play(); + void pause(); + void stop(); + + void updateState(QMediaPlayer::State state); + void updateStatus(QMediaPlayer::MediaStatus status); + void updateMediaInfo(qint64 duration, int streamTypes, bool seekable); + void updatePlaybackRate(qreal rate); + void updateAudioOutput(IBaseFilter *filter); + void updateError(QMediaPlayer::Error error, const QString &errorString); + void updatePosition(qint64 position); + +protected: + void customEvent(QEvent *event); + +private: + enum Properties + { + StateProperty = 0x01, + StatusProperty = 0x02, + StreamTypesProperty = 0x04, + DurationProperty = 0x08, + PlaybackRateProperty = 0x10, + SeekableProperty = 0x20, + ErrorProperty = 0x40, + PositionProperty = 0x80 + }; + + enum Event + { + PropertiesChanged = QEvent::User + }; + + void scheduleUpdate(int properties); + void emitPropertyChanges(); + + DirectShowPlayerService *m_service; + IBasicAudio *m_audio; + QIODevice *m_stream; + int m_updateProperties; + QMediaPlayer::State m_state; + QMediaPlayer::MediaStatus m_status; + QMediaPlayer::Error m_error; + int m_streamTypes; + int m_muteVolume; + qint64 m_position; + qint64 m_duration; + qreal m_playbackRate; + bool m_seekable; + QMediaContent m_media; + QString m_errorString; + +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowplayerservice.cpp b/src/plugins/mediaservices/directshow/mediaplayer/directshowplayerservice.cpp new file mode 100644 index 0000000..57f4bec --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowplayerservice.cpp @@ -0,0 +1,1352 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowplayerservice.h" + +#include "directshowaudioendpointcontrol.h" +#include "directshowiosource.h" +#include "directshowmetadatacontrol.h" +#include "directshowplayercontrol.h" +#include "directshowvideooutputcontrol.h" +#include "directshowvideorenderercontrol.h" +#include "vmr9videowindowcontrol.h" + +#include <QtMultimedia/qmediacontent.h> + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qthread.h> +#include <QtCore/qvarlengtharray.h> + +#include <uuids.h> + +Q_GLOBAL_STATIC(DirectShowEventLoop, qt_directShowEventLoop) + +QT_BEGIN_NAMESPACE + +class DirectShowPlayerServiceThread : public QThread +{ +public: + DirectShowPlayerServiceThread(DirectShowPlayerService *service) + : m_service(service) + { + } + +protected: + void run() { m_service->run(); } + +private: + DirectShowPlayerService *m_service; +}; + +DirectShowPlayerService::DirectShowPlayerService(QObject *parent) + : QMediaService(parent) + , m_playerControl(0) + , m_audioEndpointControl(0) + , m_metaDataControl(0) + , m_videoOutputControl(0) + , m_videoRendererControl(0) + , m_videoWindowControl(0) + , m_taskThread(0) + , m_loop(qt_directShowEventLoop()) + , m_pendingTasks(0) + , m_executingTask(0) + , m_executedTasks(0) + , m_taskHandle(::CreateEvent(0, 0, 0, 0)) + , m_eventHandle(0) + , m_graphStatus(NoMedia) + , m_stream(0) + , m_graph(0) + , m_source(0) + , m_audioOutput(0) + , m_videoOutput(0) + , m_rate(1.0) + , m_position(0) + , m_duration(0) + , m_buffering(false) + , m_seekable(false) + , m_atEnd(false) +{ + m_playerControl = new DirectShowPlayerControl(this); + m_metaDataControl = new DirectShowMetaDataControl(this); + m_videoOutputControl = new DirectShowVideoOutputControl; + m_audioEndpointControl = new DirectShowAudioEndpointControl(this); + m_videoRendererControl = new DirectShowVideoRendererControl(m_loop); + m_videoWindowControl = new Vmr9VideoWindowControl; + + m_taskThread = new DirectShowPlayerServiceThread(this); + m_taskThread->start(); + + connect(m_videoOutputControl, SIGNAL(outputChanged()), this, SLOT(videoOutputChanged())); + connect(m_videoRendererControl, SIGNAL(filterChanged()), this, SLOT(videoOutputChanged())); +} + +DirectShowPlayerService::~DirectShowPlayerService() +{ + { + QMutexLocker locker(&m_mutex); + + releaseGraph(); + + m_pendingTasks = Shutdown; + ::SetEvent(m_taskHandle); + } + + m_taskThread->wait(); + delete m_taskThread; + + if (m_audioOutput) { + m_audioOutput->Release(); + m_audioOutput = 0; + } + + if (m_videoOutput) { + m_videoOutput->Release(); + m_videoOutput = 0; + } + + delete m_playerControl; + delete m_audioEndpointControl; + delete m_metaDataControl; + delete m_videoOutputControl; + delete m_videoRendererControl; + delete m_videoWindowControl; + + ::CloseHandle(m_taskHandle); +} + +QMediaControl *DirectShowPlayerService::control(const char *name) const +{ + if (qstrcmp(name, QMediaPlayerControl_iid) == 0) + return m_playerControl; + else if (qstrcmp(name, QAudioEndpointSelector_iid) == 0) + return m_audioEndpointControl; + else if (qstrcmp(name, QMetaDataControl_iid) == 0) + return m_metaDataControl; + else if (qstrcmp(name, QVideoOutputControl_iid) == 0) + return m_videoOutputControl; + else if (qstrcmp(name, QVideoRendererControl_iid) == 0) + return m_videoRendererControl; + else if (qstrcmp(name, QVideoWindowControl_iid) == 0) + return m_videoWindowControl; + else + return 0; +} + +void DirectShowPlayerService::load(const QMediaContent &media, QIODevice *stream) +{ + QMutexLocker locker(&m_mutex); + + m_pendingTasks = 0; + + if (m_graph) + releaseGraph(); + + m_resources = media.resources(); + m_stream = stream; + m_error = QMediaPlayer::NoError; + m_errorString = QString(); + m_position = 0; + m_duration = 0; + m_streamTypes = 0; + m_executedTasks = 0; + m_buffering = false; + m_seekable = false; + m_atEnd = false; + m_metaDataControl->updateGraph(0, 0); + + if (m_resources.isEmpty() && !stream) { + m_pendingTasks = 0; + m_graphStatus = NoMedia; + + m_url.clear(); + } else if (stream && (!stream->isReadable() || stream->isSequential())) { + m_pendingTasks = 0; + m_graphStatus = InvalidMedia; + m_error = QMediaPlayer::ResourceError; + } else { + m_graphStatus = Loading; + + m_graph = com_new<IFilterGraph2>(CLSID_FilterGraph); + + if (stream) + m_pendingTasks = SetStreamSource; + else + m_pendingTasks = SetUrlSource; + + ::SetEvent(m_taskHandle); + } + + m_playerControl->updateError(m_error, m_errorString); + m_playerControl->updateMediaInfo(m_duration, m_streamTypes, m_seekable); + m_playerControl->updateState(QMediaPlayer::StoppedState); + m_playerControl->updatePosition(m_position); + updateStatus(); +} + +void DirectShowPlayerService::doSetUrlSource(QMutexLocker *locker) +{ + IBaseFilter *source = 0; + + QMediaResource resource = m_resources.takeFirst(); + QUrl url = resource.url(); + + HRESULT hr = E_FAIL; + +#ifndef QT_NO_WMSDK + if (url.scheme() == QLatin1String("http") || url.scheme() == QLatin1String("https")) { + if (IFileSourceFilter *fileSource = com_new<IFileSourceFilter>(CLSID_WMAsfReader)) { + locker->unlock(); + hr = fileSource->Load(url.toString().utf16(), 0); + locker->relock(); + + if (SUCCEEDED(hr)) { + source = com_cast<IBaseFilter>(fileSource); + + if (!SUCCEEDED(hr = m_graph->AddFilter(source, L"Source")) && source) { + source->Release(); + source = 0; + } + } + + fileSource->Release(); + } + } else if (url.scheme() == QLatin1String("qrc")) { + DirectShowRcSource *rcSource = new DirectShowRcSource(m_loop); + + if (rcSource->open(url) && SUCCEEDED(hr = m_graph->AddFilter(rcSource, L"Source"))) + source = rcSource; + else + rcSource->Release(); + } + + if (!SUCCEEDED(hr)) { +#endif + locker->unlock(); + hr = m_graph->AddSourceFilter(url.toString().utf16(), L"Source", &source); + locker->relock(); +#ifndef QT_NO_WMSDK + } +#endif + + if (SUCCEEDED(hr)) { + m_executedTasks = SetSource; + m_pendingTasks |= Render; + + if (m_audioOutput) + m_pendingTasks |= SetAudioOutput; + if (m_videoOutput) + m_pendingTasks |= SetVideoOutput; + + if (m_rate != 1.0) + m_pendingTasks |= SetRate; + + m_source = source; + } else if (!m_resources.isEmpty()) { + m_pendingTasks |= SetUrlSource; + } else { + m_pendingTasks = 0; + m_graphStatus = InvalidMedia; + + switch (hr) { + case VFW_E_UNKNOWN_FILE_TYPE: + m_error = QMediaPlayer::FormatError; + m_errorString = QString(); + break; + case E_OUTOFMEMORY: + case VFW_E_CANNOT_LOAD_SOURCE_FILTER: + case VFW_E_NOT_FOUND: + m_error = QMediaPlayer::ResourceError; + m_errorString = QString(); + default: + m_error = QMediaPlayer::ResourceError; + m_errorString = QString(); + qWarning("DirectShowPlayerService::doSetUrlSource: Unresolved error code %x", hr); + break; + } + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(Error))); + } +} + +void DirectShowPlayerService::doSetStreamSource(QMutexLocker *locker) +{ + DirectShowIOSource *source = new DirectShowIOSource(m_loop); + source->setDevice(m_stream); + + if (SUCCEEDED(m_graph->AddFilter(source, L"Source"))) { + m_executedTasks = SetSource; + m_pendingTasks |= Render; + + if (m_audioOutput) + m_pendingTasks |= SetAudioOutput; + if (m_videoOutput) + m_pendingTasks |= SetVideoOutput; + + if (m_rate != 1.0) + m_pendingTasks |= SetRate; + + m_source = source; + } else { + source->Release(); + + m_pendingTasks = 0; + m_graphStatus = InvalidMedia; + + m_error = QMediaPlayer::ResourceError; + m_errorString = QString(); + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(Error))); + } +} + +void DirectShowPlayerService::doRender(QMutexLocker *locker) +{ + m_pendingTasks |= m_executedTasks & (Play | Pause); + + if (IMediaControl *control = com_cast<IMediaControl>(m_graph)) { + control->Stop(); + control->Release(); + } + + if (m_pendingTasks & SetAudioOutput) { + m_graph->AddFilter(m_audioOutput, L"AudioOutput"); + + m_pendingTasks ^= SetAudioOutput; + m_executedTasks |= SetAudioOutput; + } + if (m_pendingTasks & SetVideoOutput) { + m_graph->AddFilter(m_videoOutput, L"VideoOutput"); + + m_pendingTasks ^= SetVideoOutput; + m_executedTasks |= SetVideoOutput; + } + + IFilterGraph2 *graph = m_graph; + graph->AddRef(); + + QVarLengthArray<IBaseFilter *, 16> filters; + m_source->AddRef(); + filters.append(m_source); + + bool rendered = false; + + HRESULT renderHr = S_OK; + + while (!filters.isEmpty()) { + IEnumPins *pins = 0; + IBaseFilter *filter = filters[filters.size() - 1]; + filters.removeLast(); + + if (!(m_pendingTasks & ReleaseFilters) && SUCCEEDED(filter->EnumPins(&pins))) { + int outputs = 0; + for (IPin *pin = 0; pins->Next(1, &pin, 0) == S_OK; pin->Release()) { + PIN_DIRECTION direction; + if (pin->QueryDirection(&direction) == S_OK && direction == PINDIR_OUTPUT) { + ++outputs; + + IPin *peer = 0; + if (pin->ConnectedTo(&peer) == S_OK) { + PIN_INFO peerInfo; + if (SUCCEEDED(peer->QueryPinInfo(&peerInfo))) + filters.append(peerInfo.pFilter); + peer->Release(); + } else { + locker->unlock(); + HRESULT hr; + if (SUCCEEDED(hr = graph->RenderEx( + pin, AM_RENDEREX_RENDERTOEXISTINGRENDERERS, 0))) { + rendered = true; + } else if (renderHr == S_OK || renderHr == VFW_E_NO_DECOMPRESSOR){ + renderHr = hr; + } + locker->relock(); + } + } + } + + pins->Release(); + + if (outputs == 0) + rendered = true; + } + filter->Release(); + } + + if (m_audioOutput && !isConnected(m_audioOutput, PINDIR_INPUT)) { + graph->RemoveFilter(m_audioOutput); + + m_executedTasks &= ~SetAudioOutput; + } + + if (m_videoOutput && !isConnected(m_videoOutput, PINDIR_INPUT)) { + graph->RemoveFilter(m_videoOutput); + + m_executedTasks &= ~SetVideoOutput; + } + + graph->Release(); + + if (!(m_pendingTasks & ReleaseFilters)) { + if (rendered) { + if (!(m_executedTasks & FinalizeLoad)) + m_pendingTasks |= FinalizeLoad; + } else { + m_pendingTasks = 0; + + m_graphStatus = InvalidMedia; + + if (!m_audioOutput && !m_videoOutput) { + m_error = QMediaPlayer::ResourceError; + m_errorString = QString(); + } else { + switch (renderHr) { + case VFW_E_UNSUPPORTED_AUDIO: + case VFW_E_UNSUPPORTED_VIDEO: + case VFW_E_UNSUPPORTED_STREAM: + m_error = QMediaPlayer::FormatError; + m_errorString = QString(); + default: + m_error = QMediaPlayer::ResourceError; + m_errorString = QString(); + qWarning("DirectShowPlayerService::doRender: Unresolved error code %x", + renderHr); + } + } + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(Error))); + } + + m_executedTasks |= Render; + } +} + +void DirectShowPlayerService::doFinalizeLoad(QMutexLocker *locker) +{ + if (m_graphStatus != Loaded) { + if (IMediaEvent *event = com_cast<IMediaEvent>(m_graph)) { + event->GetEventHandle(reinterpret_cast<OAEVENT *>(&m_eventHandle)); + event->Release(); + } + if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph)) { + LONGLONG duration = 0; + seeking->GetDuration(&duration); + m_duration = duration / 10; + + DWORD capabilities = 0; + seeking->GetCapabilities(&capabilities); + m_seekable = capabilities & AM_SEEKING_CanSeekAbsolute; + + seeking->Release(); + } + } + + if ((m_executedTasks & SetOutputs) == SetOutputs) { + m_streamTypes = AudioStream | VideoStream; + } else { + m_streamTypes = findStreamTypes(m_source); + } + + m_executedTasks |= FinalizeLoad; + + m_graphStatus = Loaded; + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(FinalizedLoad))); +} + +void DirectShowPlayerService::releaseGraph() +{ + if (m_graph) { + if (m_executingTask != 0) { + if (IAMOpenProgress *progress = com_cast<IAMOpenProgress>(m_graph)) { + progress->AbortOperation(); + progress->Release(); + } + m_graph->Abort(); + } + + m_pendingTasks = ReleaseGraph; + + ::SetEvent(m_taskHandle); + + m_loop->wait(&m_mutex); + } +} + +void DirectShowPlayerService::doReleaseGraph(QMutexLocker *locker) +{ + Q_UNUSED(locker); + + if (IMediaControl *control = com_cast<IMediaControl>(m_graph)) { + control->Stop(); + control->Release(); + } + + if (m_source) { + m_source->Release(); + m_source = 0; + } + + m_eventHandle = 0; + + m_graph->Release(); + m_graph = 0; + + m_loop->wake(); +} + +int DirectShowPlayerService::findStreamTypes(IBaseFilter *source) const +{ + QVarLengthArray<IBaseFilter *, 16> filters; + source->AddRef(); + filters.append(source); + + int streamTypes = 0; + + while (!filters.isEmpty()) { + IEnumPins *pins = 0; + IBaseFilter *filter = filters[filters.size() - 1]; + filters.removeLast(); + + if (SUCCEEDED(filter->EnumPins(&pins))) { + for (IPin *pin = 0; pins->Next(1, &pin, 0) == S_OK; pin->Release()) { + PIN_DIRECTION direction; + if (pin->QueryDirection(&direction) == S_OK && direction == PINDIR_OUTPUT) { + AM_MEDIA_TYPE connectionType; + if (SUCCEEDED(pin->ConnectionMediaType(&connectionType))) { + IPin *peer = 0; + + if (connectionType.majortype == MEDIATYPE_Audio) { + streamTypes |= AudioStream; + } else if (connectionType.majortype == MEDIATYPE_Video) { + streamTypes |= VideoStream; + } else if (SUCCEEDED(pin->ConnectedTo(&peer))) { + PIN_INFO peerInfo; + if (SUCCEEDED(peer->QueryPinInfo(&peerInfo))) + filters.append(peerInfo.pFilter); + peer->Release(); + } + } else { + streamTypes |= findStreamType(pin); + } + } + } + } + filter->Release(); + } + return streamTypes; +} + +int DirectShowPlayerService::findStreamType(IPin *pin) const +{ + IEnumMediaTypes *types; + + if (SUCCEEDED(pin->EnumMediaTypes(&types))) { + bool video = false; + bool audio = false; + bool other = false; + + for (AM_MEDIA_TYPE *type = 0; + types->Next(1, &type, 0) == S_OK; + DirectShowMediaType::deleteType(type)) { + if (type->majortype == MEDIATYPE_Audio) + audio = true; + else if (type->majortype == MEDIATYPE_Video) + video = true; + else + other = true; + } + types->Release(); + + if (other) + return 0; + else if (audio && !video) + return AudioStream; + else if (!audio && video) + return VideoStream; + else + return 0; + } else { + return 0; + } +} + +void DirectShowPlayerService::play() +{ + QMutexLocker locker(&m_mutex); + + m_pendingTasks &= ~Pause; + m_pendingTasks |= Play; + + if (m_executedTasks & Render) { + if (m_executedTasks & Stop) { + m_atEnd = false; + m_position = 0; + m_pendingTasks |= Seek; + m_executedTasks ^= Stop; + } + + ::SetEvent(m_taskHandle); + } +} + +void DirectShowPlayerService::doPlay(QMutexLocker *locker) +{ + if (IMediaControl *control = com_cast<IMediaControl>(m_graph)) { + locker->unlock(); + HRESULT hr = control->Run(); + locker->relock(); + + control->Release(); + + if (SUCCEEDED(hr)) { + m_executedTasks |= Play; + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StatusChange))); + } else { + m_error = QMediaPlayer::ResourceError; + m_errorString = QString(); + qWarning("DirectShowPlayerService::doPlay: Unresolved error code %x", hr); + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(Error))); + } + } +} + +void DirectShowPlayerService::pause() +{ + QMutexLocker locker(&m_mutex); + + m_pendingTasks &= ~Play; + m_pendingTasks |= Pause; + + if (m_executedTasks & Render) { + if (m_executedTasks & Stop) { + m_atEnd = false; + m_position = 0; + m_pendingTasks |= Seek; + m_executedTasks ^= Stop; + } + + ::SetEvent(m_taskHandle); + } +} + +void DirectShowPlayerService::doPause(QMutexLocker *locker) +{ + if (IMediaControl *control = com_cast<IMediaControl>(m_graph)) { + locker->unlock(); + HRESULT hr = control->Pause(); + locker->relock(); + + control->Release(); + + if (SUCCEEDED(hr)) { + if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph)) { + LONGLONG position = 0; + + seeking->GetCurrentPosition(&position); + seeking->Release(); + + m_position = position / 10; + } else { + m_position = 0; + } + + m_executedTasks |= Pause; + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StatusChange))); + } else { + m_error = QMediaPlayer::ResourceError; + m_errorString = QString(); + qWarning("DirectShowPlayerService::doPause: Unresolved error code %x", hr); + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(Error))); + } + } +} + +void DirectShowPlayerService::stop() +{ + QMutexLocker locker(&m_mutex); + + m_pendingTasks &= ~(Play | Pause | Seek); + + if ((m_executingTask | m_executedTasks) & (Play | Pause | Seek)) { + m_pendingTasks |= Stop; + + ::SetEvent(m_taskHandle); + + m_loop->wait(&m_mutex); + } + +} + +void DirectShowPlayerService::doStop(QMutexLocker *locker) +{ + if (m_executedTasks & (Play | Pause)) { + if (IMediaControl *control = com_cast<IMediaControl>(m_graph)) { + control->Stop(); + control->Release(); + } + + if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph)) { + LONGLONG position = 0; + + seeking->GetCurrentPosition(&position); + seeking->Release(); + + m_position = position / 10; + } else { + m_position = 0; + } + + m_executedTasks &= ~(Play | Pause); + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StatusChange))); + } + + m_executedTasks |= Stop; + + m_loop->wake(); +} + +void DirectShowPlayerService::setRate(qreal rate) +{ + QMutexLocker locker(&m_mutex); + + m_rate = rate; + + m_pendingTasks |= SetRate; + + if (m_executedTasks & FinalizeLoad) + ::SetEvent(m_taskHandle); +} + +void DirectShowPlayerService::doSetRate(QMutexLocker *locker) +{ + if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph)) { + // Cache current values as we can't query IMediaSeeking during a seek due to the + // possibility of a deadlock when flushing the VideoSurfaceFilter. + LONGLONG currentPosition = 0; + seeking->GetCurrentPosition(¤tPosition); + m_position = currentPosition / 10; + + LONGLONG minimum = 0; + LONGLONG maximum = 0; + m_playbackRange = SUCCEEDED(seeking->GetAvailable(&minimum, &maximum)) + ? QMediaTimeRange(minimum / 10, maximum / 10) + : QMediaTimeRange(); + + locker->unlock(); + HRESULT hr = seeking->SetRate(m_rate); + locker->relock(); + + if (!SUCCEEDED(hr)) { + double rate = 0.0; + m_rate = seeking->GetRate(&rate) + ? rate + : 1.0; + } + + seeking->Release(); + } else if (m_rate != 1.0) { + m_rate = 1.0; + } + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(RateChange))); +} + +qint64 DirectShowPlayerService::position() const +{ + QMutexLocker locker(const_cast<QMutex *>(&m_mutex)); + + if (m_graphStatus == Loaded) { + if (m_executingTask == Seek || m_executingTask == SetRate) { + return m_position; + } else if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph)) { + LONGLONG position = 0; + + seeking->GetCurrentPosition(&position); + seeking->Release(); + + const_cast<qint64 &>(m_position) = position / 10; + + return m_position; + } + } + return 0; +} + +QMediaTimeRange DirectShowPlayerService::availablePlaybackRanges() const +{ + QMutexLocker locker(const_cast<QMutex *>(&m_mutex)); + + if (m_graphStatus == Loaded) { + if (m_executingTask == Seek || m_executingTask == SetRate) { + return m_playbackRange; + } else if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph)) { + LONGLONG minimum = 0; + LONGLONG maximum = 0; + + HRESULT hr = seeking->GetAvailable(&minimum, &maximum); + seeking->Release(); + + if (SUCCEEDED(hr)) + return QMediaTimeRange(minimum, maximum); + } + } + return QMediaTimeRange(); +} + +void DirectShowPlayerService::seek(qint64 position) +{ + QMutexLocker locker(&m_mutex); + + m_position = position; + + m_pendingTasks |= Seek; + + if (m_executedTasks & FinalizeLoad) + ::SetEvent(m_taskHandle); +} + +void DirectShowPlayerService::doSeek(QMutexLocker *locker) +{ + if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph)) { + LONGLONG seekPosition = LONGLONG(m_position) * 10; + + // Cache current values as we can't query IMediaSeeking during a seek due to the + // possibility of a deadlock when flushing the VideoSurfaceFilter. + LONGLONG currentPosition = 0; + seeking->GetCurrentPosition(¤tPosition); + m_position = currentPosition / 10; + + LONGLONG minimum = 0; + LONGLONG maximum = 0; + m_playbackRange = SUCCEEDED(seeking->GetAvailable(&minimum, &maximum)) + ? QMediaTimeRange(minimum / 10, maximum / 10) + : QMediaTimeRange(); + + locker->unlock(); + seeking->SetPositions( + &seekPosition, AM_SEEKING_AbsolutePositioning, 0, AM_SEEKING_NoPositioning); + locker->relock(); + + seeking->GetCurrentPosition(¤tPosition); + m_position = currentPosition / 10; + + seeking->Release(); + } else { + m_position = 0; + } + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(PositionChange))); +} + +int DirectShowPlayerService::bufferStatus() const +{ +#ifndef QT_NO_WMSDK + QMutexLocker locker(const_cast<QMutex *>(&m_mutex)); + + if (IWMReaderAdvanced2 *reader = com_cast<IWMReaderAdvanced2>(m_source)) { + DWORD percentage = 0; + + reader->GetBufferProgress(&percentage, 0); + reader->Release(); + + return percentage; + } else { + return 0; + } +#else + return 0; +#endif +} + +void DirectShowPlayerService::setAudioOutput(IBaseFilter *filter) +{ + QMutexLocker locker(&m_mutex); + + if (m_graph) { + if (m_audioOutput) { + if (m_executedTasks & SetAudioOutput) { + m_pendingTasks |= ReleaseAudioOutput; + + ::SetEvent(m_taskHandle); + + m_loop->wait(&m_mutex); + } + m_audioOutput->Release(); + } + + m_audioOutput = filter; + + if (m_audioOutput) { + m_audioOutput->AddRef(); + + m_pendingTasks |= SetAudioOutput; + + if (m_executedTasks & SetSource) { + m_pendingTasks |= Render; + + ::SetEvent(m_taskHandle); + } + } else { + m_pendingTasks &= ~ SetAudioOutput; + } + } else { + if (m_audioOutput) + m_audioOutput->Release(); + + m_audioOutput = filter; + + if (m_audioOutput) + m_audioOutput->AddRef(); + } + + m_playerControl->updateAudioOutput(m_audioOutput); +} + +void DirectShowPlayerService::doReleaseAudioOutput(QMutexLocker *locker) +{ + m_pendingTasks |= m_executedTasks & (Play | Pause); + + if (IMediaControl *control = com_cast<IMediaControl>(m_graph)) { + control->Stop(); + control->Release(); + } + + IBaseFilter *decoder = getConnected(m_audioOutput, PINDIR_INPUT); + if (!decoder) { + decoder = m_audioOutput; + decoder->AddRef(); + } + + if (IFilterChain *chain = com_cast<IFilterChain>(m_graph)) { + chain->RemoveChain(decoder, m_audioOutput); + chain->Release(); + } else { + m_graph->RemoveFilter(m_audioOutput); + } + + decoder->Release(); + + m_executedTasks &= ~SetAudioOutput; + + m_loop->wake(); +} + +void DirectShowPlayerService::setVideoOutput(IBaseFilter *filter) +{ + QMutexLocker locker(&m_mutex); + + if (m_graph) { + if (m_videoOutput) { + if (m_executedTasks & SetVideoOutput) { + m_pendingTasks |= ReleaseVideoOutput; + + ::SetEvent(m_taskHandle); + + m_loop->wait(&m_mutex); + } + m_videoOutput->Release(); + } + + m_videoOutput = filter; + + if (m_videoOutput) { + m_videoOutput->AddRef(); + + m_pendingTasks |= SetVideoOutput; + + if (m_executedTasks & SetSource) { + m_pendingTasks |= Render; + + ::SetEvent(m_taskHandle); + } + } + } else { + if (m_videoOutput) + m_videoOutput->Release(); + + m_videoOutput = filter; + + if (m_videoOutput) + m_videoOutput->AddRef(); + } +} + +void DirectShowPlayerService::doReleaseVideoOutput(QMutexLocker *locker) +{ + m_pendingTasks |= m_executedTasks & (Play | Pause); + + if (IMediaControl *control = com_cast<IMediaControl>(m_graph)) { + control->Stop(); + control->Release(); + } + + IBaseFilter *intermediate = 0; + if (!SUCCEEDED(m_graph->FindFilterByName(L"Color Space Converter", &intermediate))) { + intermediate = m_videoOutput; + intermediate->AddRef(); + } + + IBaseFilter *decoder = getConnected(intermediate, PINDIR_INPUT); + if (!decoder) { + decoder = intermediate; + decoder->AddRef(); + } + + if (IFilterChain *chain = com_cast<IFilterChain>(m_graph)) { + chain->RemoveChain(decoder, m_videoOutput); + chain->Release(); + } else { + m_graph->RemoveFilter(m_videoOutput); + } + + intermediate->Release(); + decoder->Release(); + + m_executedTasks &= ~SetVideoOutput; + + m_loop->wake(); +} + +void DirectShowPlayerService::customEvent(QEvent *event) +{ + if (event->type() == QEvent::Type(FinalizedLoad)) { + QMutexLocker locker(&m_mutex); + + m_playerControl->updateMediaInfo(m_duration, m_streamTypes, m_seekable); + m_metaDataControl->updateGraph(m_graph, m_source); + + updateStatus(); + } else if (event->type() == QEvent::Type(Error)) { + QMutexLocker locker(&m_mutex); + + if (m_error != QMediaPlayer::NoError) { + m_playerControl->updateError(m_error, m_errorString); + m_playerControl->updateMediaInfo(m_duration, m_streamTypes, m_seekable); + m_playerControl->updateState(QMediaPlayer::StoppedState); + updateStatus(); + } + } else if (event->type() == QEvent::Type(RateChange)) { + QMutexLocker locker(&m_mutex); + + m_playerControl->updatePlaybackRate(m_rate); + } else if (event->type() == QEvent::Type(StatusChange)) { + QMutexLocker locker(&m_mutex); + + updateStatus(); + m_playerControl->updatePosition(m_position); + } else if (event->type() == QEvent::Type(DurationChange)) { + QMutexLocker locker(&m_mutex); + + m_playerControl->updateMediaInfo(m_duration, m_streamTypes, m_seekable); + } else if (event->type() == QEvent::Type(EndOfMedia)) { + QMutexLocker locker(&m_mutex); + + if (m_atEnd) { + m_playerControl->updateState(QMediaPlayer::StoppedState); + m_playerControl->updateStatus(QMediaPlayer::EndOfMedia); + m_playerControl->updatePosition(m_position); + } + } else if (event->type() == QEvent::Type(PositionChange)) { + QMutexLocker locker(&m_mutex); + + m_playerControl->updatePosition(m_position); + } else { + QMediaService::customEvent(event); + } +} + +void DirectShowPlayerService::videoOutputChanged() +{ + IBaseFilter *videoOutput = 0; + + switch (m_videoOutputControl->output()) { + case QVideoOutputControl::RendererOutput: + videoOutput = m_videoRendererControl->filter(); + break; + case QVideoOutputControl::WindowOutput: + videoOutput = m_videoWindowControl->filter(); + break; + default: + break; + } + + setVideoOutput(videoOutput); +} + +void DirectShowPlayerService::graphEvent(QMutexLocker *locker) +{ + if (IMediaEvent *event = com_cast<IMediaEvent>(m_graph)) { + long eventCode; + LONG_PTR param1; + LONG_PTR param2; + + while (event->GetEvent(&eventCode, ¶m1, ¶m2, 0) == S_OK) { + switch (eventCode) { + case EC_BUFFERING_DATA: + m_buffering = param1; + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StatusChange))); + break; + case EC_COMPLETE: + m_executedTasks &= ~(Play | Pause); + m_executedTasks |= Stop; + + m_buffering = false; + m_atEnd = true; + + if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph)) { + LONGLONG position = 0; + + seeking->GetCurrentPosition(&position); + seeking->Release(); + + m_position = position / 10; + } + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(EndOfMedia))); + break; + case EC_LENGTH_CHANGED: + if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph)) { + LONGLONG duration = 0; + seeking->GetDuration(&duration); + m_duration = duration / 10; + + DWORD capabilities = 0; + seeking->GetCapabilities(&capabilities); + m_seekable = capabilities & AM_SEEKING_CanSeekAbsolute; + + seeking->Release(); + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(DurationChange))); + } + break; + default: + break; + } + + event->FreeEventParams(eventCode, param1, param2); + } + event->Release(); + } +} + +void DirectShowPlayerService::updateStatus() +{ + switch (m_graphStatus) { + case NoMedia: + m_playerControl->updateStatus(QMediaPlayer::NoMedia); + break; + case Loading: + m_playerControl->updateStatus(QMediaPlayer::LoadingMedia); + break; + case Loaded: + if ((m_pendingTasks | m_executingTask | m_executedTasks) & (Play | Pause)) { + if (m_buffering) + m_playerControl->updateStatus(QMediaPlayer::BufferingMedia); + else + m_playerControl->updateStatus(QMediaPlayer::BufferedMedia); + } else { + m_playerControl->updateStatus(QMediaPlayer::LoadedMedia); + } + break; + case InvalidMedia: + m_playerControl->updateStatus(QMediaPlayer::InvalidMedia); + break; + default: + m_playerControl->updateStatus(QMediaPlayer::UnknownMediaStatus); + } +} + +bool DirectShowPlayerService::isConnected(IBaseFilter *filter, PIN_DIRECTION direction) const +{ + bool connected = false; + + IEnumPins *pins = 0; + + if (SUCCEEDED(filter->EnumPins(&pins))) { + for (IPin *pin = 0; pins->Next(1, &pin, 0) == S_OK; pin->Release()) { + PIN_DIRECTION dir; + if (SUCCEEDED(pin->QueryDirection(&dir)) && dir == direction) { + IPin *peer = 0; + if (SUCCEEDED(pin->ConnectedTo(&peer))) { + connected = true; + + peer->Release(); + } + } + } + pins->Release(); + } + return connected; +} + +IBaseFilter *DirectShowPlayerService::getConnected( + IBaseFilter *filter, PIN_DIRECTION direction) const +{ + IBaseFilter *connected = 0; + + IEnumPins *pins = 0; + + if (SUCCEEDED(filter->EnumPins(&pins))) { + for (IPin *pin = 0; pins->Next(1, &pin, 0) == S_OK; pin->Release()) { + PIN_DIRECTION dir; + if (SUCCEEDED(pin->QueryDirection(&dir)) && dir == direction) { + IPin *peer = 0; + if (SUCCEEDED(pin->ConnectedTo(&peer))) { + PIN_INFO info; + + if (SUCCEEDED(peer->QueryPinInfo(&info))) { + if (connected) { + qWarning("DirectShowPlayerService::getConnected: " + "Multiple connected filters"); + connected->Release(); + } + connected = info.pFilter; + } + peer->Release(); + } + } + } + pins->Release(); + } + return connected; +} + +void DirectShowPlayerService::run() +{ + QMutexLocker locker(&m_mutex); + + for (;;) { + ::ResetEvent(m_taskHandle); + + while (m_pendingTasks == 0) { + DWORD result = 0; + + locker.unlock(); + if (m_eventHandle) { + HANDLE handles[] = { m_taskHandle, m_eventHandle }; + + result = ::WaitForMultipleObjects(2, handles, false, INFINITE); + } else { + result = ::WaitForSingleObject(m_taskHandle, INFINITE); + } + locker.relock(); + + if (result == WAIT_OBJECT_0 + 1) { + graphEvent(&locker); + } + } + + if (m_pendingTasks & ReleaseGraph) { + m_pendingTasks ^= ReleaseGraph; + m_executingTask = ReleaseGraph; + + doReleaseGraph(&locker); + } else if (m_pendingTasks & Shutdown) { + return; + } else if (m_pendingTasks & ReleaseAudioOutput) { + m_pendingTasks ^= ReleaseAudioOutput; + m_executingTask = ReleaseAudioOutput; + + doReleaseAudioOutput(&locker); + } else if (m_pendingTasks & ReleaseVideoOutput) { + m_pendingTasks ^= ReleaseVideoOutput; + m_executingTask = ReleaseVideoOutput; + + doReleaseVideoOutput(&locker); + } else if (m_pendingTasks & SetUrlSource) { + m_pendingTasks ^= SetUrlSource; + m_executingTask = SetUrlSource; + + doSetUrlSource(&locker); + } else if (m_pendingTasks & SetStreamSource) { + m_pendingTasks ^= SetStreamSource; + m_executingTask = SetStreamSource; + + doSetStreamSource(&locker); + } else if (m_pendingTasks & Render) { + m_pendingTasks ^= Render; + m_executingTask = Render; + + doRender(&locker); + } else if (!(m_executedTasks & Render)) { + m_pendingTasks &= ~(FinalizeLoad | SetRate | Stop | Pause | Seek | Play); + } else if (m_pendingTasks & FinalizeLoad) { + m_pendingTasks ^= FinalizeLoad; + m_executingTask = FinalizeLoad; + + doFinalizeLoad(&locker); + } else if (m_pendingTasks & Stop) { + m_pendingTasks ^= Stop; + m_executingTask = Stop; + + doStop(&locker); + } else if (m_pendingTasks & SetRate) { + m_pendingTasks ^= SetRate; + m_executingTask = SetRate; + + doSetRate(&locker); + } else if (m_pendingTasks & Pause) { + m_pendingTasks ^= Pause; + m_executingTask = Pause; + + doPause(&locker); + } else if (m_pendingTasks & Seek) { + m_pendingTasks ^= Seek; + m_executingTask = Seek; + + doSeek(&locker); + } else if (m_pendingTasks & Play) { + m_pendingTasks ^= Play; + m_executingTask = Play; + + doPlay(&locker); + } + m_executingTask = 0; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowplayerservice.h b/src/plugins/mediaservices/directshow/mediaplayer/directshowplayerservice.h new file mode 100644 index 0000000..a5da9a4 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowplayerservice.h @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWPLAYERSERVICE_H +#define DIRECTSHOWPLAYERSERVICE_H + +#include <QtMultimedia/qmediaplayer.h> +#include <QtMultimedia/qmediaresource.h> +#include <QtMultimedia/qmediaservice.h> +#include <QtMultimedia/qmediatimerange.h> + +#include "directshoweventloop.h" +#include "directshowglobal.h" + +#include <QtCore/qcoreevent.h> +#include <QtCore/qmutex.h> +#include <QtCore/qurl.h> +#include <QtCore/qwaitcondition.h> + +#include <QtCore/private/qwineventnotifier_p.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class DirectShowAudioEndpointControl; +class DirectShowMetaDataControl; +class DirectShowPlayerControl; +class DirectShowVideoOutputControl; +class DirectShowVideoRendererControl; +class Vmr9VideoWindowControl; + +class DirectShowPlayerService : public QMediaService +{ + Q_OBJECT +public: + enum StreamType + { + AudioStream = 0x01, + VideoStream = 0x02 + }; + + DirectShowPlayerService(QObject *parent = 0); + ~DirectShowPlayerService(); + + QMediaControl* control(const char *name) const; + + void load(const QMediaContent &media, QIODevice *stream); + void play(); + void pause(); + void stop(); + + qint64 position() const; + QMediaTimeRange availablePlaybackRanges() const; + + void seek(qint64 position); + void setRate(qreal rate); + + int bufferStatus() const; + + void setAudioOutput(IBaseFilter *filter); + void setVideoOutput(IBaseFilter *filter); + +protected: + void customEvent(QEvent *event); + +private Q_SLOTS: + void videoOutputChanged(); + +private: + void releaseGraph(); + void updateStatus(); + + int findStreamTypes(IBaseFilter *source) const; + int findStreamType(IPin *pin) const; + + bool isConnected(IBaseFilter *filter, PIN_DIRECTION direction) const; + IBaseFilter *getConnected(IBaseFilter *filter, PIN_DIRECTION direction) const; + + void run(); + + void doSetUrlSource(QMutexLocker *locker); + void doSetStreamSource(QMutexLocker *locker); + void doRender(QMutexLocker *locker); + void doFinalizeLoad(QMutexLocker *locker); + void doSetRate(QMutexLocker *locker); + void doSeek(QMutexLocker *locker); + void doPlay(QMutexLocker *locker); + void doPause(QMutexLocker *locker); + void doStop(QMutexLocker *locker); + void doReleaseAudioOutput(QMutexLocker *locker); + void doReleaseVideoOutput(QMutexLocker *locker); + void doReleaseGraph(QMutexLocker *locker); + + void graphEvent(QMutexLocker *locker); + + enum Task + { + Shutdown = 0x0001, + SetUrlSource = 0x0002, + SetStreamSource = 0x0004, + SetSource = SetUrlSource | SetStreamSource, + SetAudioOutput = 0x0008, + SetVideoOutput = 0x0010, + SetOutputs = SetAudioOutput | SetVideoOutput, + Render = 0x0020, + FinalizeLoad = 0x0040, + SetRate = 0x0080, + Seek = 0x0100, + Play = 0x0200, + Pause = 0x0400, + Stop = 0x0800, + ReleaseGraph = 0x1000, + ReleaseAudioOutput = 0x2000, + ReleaseVideoOutput = 0x4000, + ReleaseFilters = ReleaseGraph | ReleaseAudioOutput | ReleaseVideoOutput + }; + + enum Event + { + FinalizedLoad = QEvent::User, + Error, + RateChange, + Started, + Paused, + DurationChange, + StatusChange, + EndOfMedia, + PositionChange + }; + + enum GraphStatus + { + NoMedia, + Loading, + Loaded, + InvalidMedia + }; + + DirectShowPlayerControl *m_playerControl; + DirectShowMetaDataControl *m_metaDataControl; + DirectShowVideoOutputControl *m_videoOutputControl; + DirectShowVideoRendererControl *m_videoRendererControl; + Vmr9VideoWindowControl *m_videoWindowControl; + DirectShowAudioEndpointControl *m_audioEndpointControl; + + QThread *m_taskThread; + DirectShowEventLoop *m_loop; + int m_pendingTasks; + int m_executingTask; + int m_executedTasks; + HANDLE m_taskHandle; + HANDLE m_eventHandle; + GraphStatus m_graphStatus; + QMediaPlayer::Error m_error; + QIODevice *m_stream; + IFilterGraph2 *m_graph; + IBaseFilter *m_source; + IBaseFilter *m_audioOutput; + IBaseFilter *m_videoOutput; + int m_streamTypes; + qreal m_rate; + qint64 m_position; + qint64 m_duration; + bool m_buffering; + bool m_seekable; + bool m_atEnd; + QMediaTimeRange m_playbackRange; + QUrl m_url; + QMediaResourceList m_resources; + QString m_errorString; + QMutex m_mutex; + + friend class DirectShowPlayerServiceThread; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowsamplescheduler.cpp b/src/plugins/mediaservices/directshow/mediaplayer/directshowsamplescheduler.cpp new file mode 100644 index 0000000..733080e --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowsamplescheduler.cpp @@ -0,0 +1,414 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowsamplescheduler.h" + +#include <QtCore/qcoreevent.h> + + +QT_BEGIN_NAMESPACE + +class DirectShowTimedSample +{ +public: + DirectShowTimedSample(IMediaSample *sample) + : m_next(0) + , m_sample(sample) + , m_cookie(0) + , m_lastSample(false) + { + m_sample->AddRef(); + } + + ~DirectShowTimedSample() + { + m_sample->Release(); + } + + IMediaSample *sample() const { return m_sample; } + + DirectShowTimedSample *nextSample() const { return m_next; } + void setNextSample(DirectShowTimedSample *sample) { Q_ASSERT(!m_next); m_next = sample; } + + DirectShowTimedSample *remove() { + DirectShowTimedSample *next = m_next; delete this; return next; } + + bool schedule(IReferenceClock *clock, REFERENCE_TIME startTime, HANDLE handle); + void unschedule(IReferenceClock *clock); + + bool isReady(IReferenceClock *clock) const; + + bool isLast() const { return m_lastSample; } + void setLast() { m_lastSample = true; } + +private: + DirectShowTimedSample *m_next; + IMediaSample *m_sample; + DWORD_PTR m_cookie; + bool m_lastSample; +}; + +bool DirectShowTimedSample::schedule( + IReferenceClock *clock, REFERENCE_TIME startTime, HANDLE handle) +{ + REFERENCE_TIME sampleStartTime; + REFERENCE_TIME sampleEndTime; + if (m_sample->GetTime(&sampleStartTime, &sampleEndTime) == S_OK) { + if (clock->AdviseTime( + startTime, sampleStartTime, reinterpret_cast<HEVENT>(handle), &m_cookie) == S_OK) { + return true; + } + } + return false; +} + +void DirectShowTimedSample::unschedule(IReferenceClock *clock) +{ + clock->Unadvise(m_cookie); +} + +bool DirectShowTimedSample::isReady(IReferenceClock *clock) const +{ + REFERENCE_TIME sampleStartTime; + REFERENCE_TIME sampleEndTime; + REFERENCE_TIME currentTime; + if (m_sample->GetTime(&sampleStartTime, &sampleEndTime) == S_OK) { + if (clock->GetTime(¤tTime) == S_OK) + return currentTime >= sampleStartTime; + } + return true; +} + +DirectShowSampleScheduler::DirectShowSampleScheduler(IUnknown *pin, QObject *parent) + : QWinEventNotifier(parent) + , m_pin(pin) + , m_clock(0) + , m_allocator(0) + , m_head(0) + , m_tail(0) + , m_maximumSamples(2) + , m_state(Stopped) + , m_startTime(0) + , m_timeoutEvent(::CreateEvent(0, 0, 0, 0)) +{ + m_semaphore.release(m_maximumSamples); + + setHandle(m_timeoutEvent); + setEnabled(true); +} + +DirectShowSampleScheduler::~DirectShowSampleScheduler() +{ + setEnabled(false); + + ::CloseHandle(m_timeoutEvent); + + Q_ASSERT(!m_clock); + Q_ASSERT(!m_allocator); +} + +HRESULT DirectShowSampleScheduler::QueryInterface(REFIID riid, void **ppvObject) +{ + return m_pin->QueryInterface(riid, ppvObject); +} + +ULONG DirectShowSampleScheduler::AddRef() +{ + return m_pin->AddRef(); +} + +ULONG DirectShowSampleScheduler::Release() +{ + return m_pin->Release(); +} + +// IMemInputPin +HRESULT DirectShowSampleScheduler::GetAllocator(IMemAllocator **ppAllocator) +{ + if (!ppAllocator) { + return E_POINTER; + } else { + QMutexLocker locker(&m_mutex); + + if (!m_allocator) { + return VFW_E_NO_ALLOCATOR; + } else { + *ppAllocator = m_allocator; + + return S_OK; + } + } +} + +HRESULT DirectShowSampleScheduler::NotifyAllocator(IMemAllocator *pAllocator, BOOL bReadOnly) +{ + Q_UNUSED(bReadOnly); + + HRESULT hr; + ALLOCATOR_PROPERTIES properties; + + if (!pAllocator) { + if (m_allocator) + m_allocator->Release(); + + m_allocator = 0; + + return S_OK; + } else if ((hr = pAllocator->GetProperties(&properties)) != S_OK) { + return hr; + } else { + if (properties.cBuffers == 1) { + ALLOCATOR_PROPERTIES actual; + + properties.cBuffers = 2; + if ((hr = pAllocator->SetProperties(&properties, &actual)) != S_OK) + return hr; + } + + QMutexLocker locker(&m_mutex); + + if (m_allocator) + m_allocator->Release(); + + m_allocator = pAllocator; + m_allocator->AddRef(); + + return S_OK; + } +} + +HRESULT DirectShowSampleScheduler::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps) +{ + if (!pProps) + return E_POINTER; + + pProps->cBuffers = 2; + + return S_OK; +} + +HRESULT DirectShowSampleScheduler::Receive(IMediaSample *pSample) +{ + if (!pSample) + return E_POINTER; + + m_semaphore.acquire(1); + + QMutexLocker locker(&m_mutex); + + if (m_state & Flushing) { + m_semaphore.release(1); + + return S_FALSE; + } else if (m_state == Stopped) { + m_semaphore.release(); + + return VFW_E_WRONG_STATE; + } else { + DirectShowTimedSample *timedSample = new DirectShowTimedSample(pSample); + + if (m_tail) + m_tail->setNextSample(timedSample); + else + m_head = timedSample; + + m_tail = timedSample; + + if (m_state == Running) { + if (!timedSample->schedule(m_clock, m_startTime, m_timeoutEvent)) { + // Timing information is unavailable, so schedule frames immediately. + QMetaObject::invokeMethod(this, "timerActivated", Qt::QueuedConnection); + } + } else if (m_tail == m_head) { + // If this is the first frame make is available. + QMetaObject::invokeMethod(this, "timerActivated", Qt::QueuedConnection); + } + + return S_OK; + } +} + +HRESULT DirectShowSampleScheduler::ReceiveMultiple( + IMediaSample **pSamples, long nSamples, long *nSamplesProcessed) +{ + if (!pSamples || !nSamplesProcessed) + return E_POINTER; + + for (*nSamplesProcessed = 0; *nSamplesProcessed < nSamples; ++(*nSamplesProcessed)) { + HRESULT hr = Receive(pSamples[*nSamplesProcessed]); + + if (hr != S_OK) + return hr; + } + return S_OK; +} + +HRESULT DirectShowSampleScheduler::ReceiveCanBlock() +{ + return S_OK; +} + +void DirectShowSampleScheduler::run(REFERENCE_TIME startTime) +{ + QMutexLocker locker(&m_mutex); + + m_state = (m_state & Flushing) | Running; + m_startTime = startTime; + + for (DirectShowTimedSample *sample = m_head; sample; sample = sample->nextSample()) { + sample->schedule(m_clock, m_startTime, m_timeoutEvent); + } +} + +void DirectShowSampleScheduler::pause() +{ + QMutexLocker locker(&m_mutex); + + m_state = (m_state & Flushing) | Paused; + + for (DirectShowTimedSample *sample = m_head; sample; sample = sample->nextSample()) + sample->unschedule(m_clock); +} + +void DirectShowSampleScheduler::stop() +{ + QMutexLocker locker(&m_mutex); + + m_state = m_state & Flushing; + + for (DirectShowTimedSample *sample = m_head; sample; sample = sample->remove()) { + sample->unschedule(m_clock); + + m_semaphore.release(1); + } + + m_head = 0; + m_tail = 0; +} + +void DirectShowSampleScheduler::setFlushing(bool flushing) +{ + QMutexLocker locker(&m_mutex); + + const bool isFlushing = m_state & Flushing; + + if (isFlushing != flushing) { + if (flushing) { + m_state |= Flushing; + + for (DirectShowTimedSample *sample = m_head; sample; sample = sample->remove()) { + sample->unschedule(m_clock); + + m_semaphore.release(1); + } + m_head = 0; + m_tail = 0; + } else { + m_state &= ~Flushing; + } + } +} + +void DirectShowSampleScheduler::setClock(IReferenceClock *clock) +{ + QMutexLocker locker(&m_mutex); + + if (m_clock) + m_clock->Release(); + + m_clock = clock; + + if (m_clock) + m_clock->AddRef(); +} + +IMediaSample *DirectShowSampleScheduler::takeSample(bool *eos) +{ + QMutexLocker locker(&m_mutex); + + if (m_head && m_head->isReady(m_clock)) { + IMediaSample *sample = m_head->sample(); + sample->AddRef(); + + if (m_state == Running) { + *eos = m_head->isLast(); + + m_head = m_head->remove(); + + if (!m_head) + m_tail = 0; + + m_semaphore.release(1); + } + + return sample; + } else { + return 0; + } +} + +bool DirectShowSampleScheduler::scheduleEndOfStream() +{ + QMutexLocker locker(&m_mutex); + + if (m_tail) { + m_tail->setLast(); + + return true; + } else { + return false; + } +} + +bool DirectShowSampleScheduler::event(QEvent *event) +{ + if (event->type() == QEvent::WinEventAct) { + QObject::event(event); + + emit sampleReady(); + + return true; + } else { + return QWinEventNotifier::event(event); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowsamplescheduler.h b/src/plugins/mediaservices/directshow/mediaplayer/directshowsamplescheduler.h new file mode 100644 index 0000000..007fa99 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowsamplescheduler.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWSAMPLESCHEDULER_H +#define DIRECTSHOWSAMPLESCHEDULER_H + +#include <QtCore/qmutex.h> +#include <QtCore/qobject.h> +#include <QtCore/qsemaphore.h> + +#include <QtCore/private/qwineventnotifier_p.h> + +#include <dshow.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class DirectShowTimedSample; + +class DirectShowSampleScheduler : public QWinEventNotifier, public IMemInputPin +{ + Q_OBJECT +public: + + enum State + { + Stopped = 0x00, + Running = 0x01, + Paused = 0x02, + RunMask = 0x03, + Flushing = 0x04 + }; + + DirectShowSampleScheduler(IUnknown *pin, QObject *parent = 0); + ~DirectShowSampleScheduler(); + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // IMemInputPin + HRESULT STDMETHODCALLTYPE GetAllocator(IMemAllocator **ppAllocator); + HRESULT STDMETHODCALLTYPE NotifyAllocator(IMemAllocator *pAllocator, BOOL bReadOnly); + HRESULT STDMETHODCALLTYPE GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps); + + HRESULT STDMETHODCALLTYPE Receive(IMediaSample *pSample); + HRESULT STDMETHODCALLTYPE ReceiveMultiple(IMediaSample **pSamples, long nSamples, long *nSamplesProcessed); + HRESULT STDMETHODCALLTYPE ReceiveCanBlock(); + + void run(REFERENCE_TIME startTime); + void pause(); + void stop(); + void setFlushing(bool flushing); + + IReferenceClock *clock() const { return m_clock; } + void setClock(IReferenceClock *clock); + + bool schedule(IMediaSample *sample); + bool scheduleEndOfStream(); + + IMediaSample *takeSample(bool *eos); + + bool event(QEvent *event); + +Q_SIGNALS: + void sampleReady(); + +private: + IUnknown *m_pin; + IReferenceClock *m_clock; + IMemAllocator *m_allocator; + DirectShowTimedSample *m_head; + DirectShowTimedSample *m_tail; + int m_maximumSamples; + int m_state; + REFERENCE_TIME m_startTime; + HANDLE m_timeoutEvent; + QSemaphore m_semaphore; + QMutex m_mutex; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowvideooutputcontrol.cpp b/src/plugins/mediaservices/directshow/mediaplayer/directshowvideooutputcontrol.cpp new file mode 100644 index 0000000..ee2bea8 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowvideooutputcontrol.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowvideooutputcontrol.h" + + +QT_BEGIN_NAMESPACE + +DirectShowVideoOutputControl::DirectShowVideoOutputControl(QObject *parent) + : QVideoOutputControl(parent) + , m_output(NoOutput) +{ + +} + +DirectShowVideoOutputControl::~DirectShowVideoOutputControl() +{ +} + +QList<QVideoOutputControl::Output> DirectShowVideoOutputControl::availableOutputs() const +{ + return QList<Output>() + << RendererOutput + << WindowOutput; +} + + +QVideoOutputControl::Output DirectShowVideoOutputControl::output() const +{ + return m_output; +} + +void DirectShowVideoOutputControl::setOutput(Output output) +{ + if (output != m_output) { + switch (output) { + case NoOutput: + case RendererOutput: + case WindowOutput: + m_output = output; + emit outputChanged(); + break; + default: + break; + } + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowvideooutputcontrol.h b/src/plugins/mediaservices/directshow/mediaplayer/directshowvideooutputcontrol.h new file mode 100644 index 0000000..acb2937 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowvideooutputcontrol.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWVIDEOUTPUTCONTROL_H +#define DIRECTSHOWVIDEOOUPUTCONTROL_H + +#include <QtMultimedia/qvideooutputcontrol.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class DirectShowVideoOutputControl : public QVideoOutputControl +{ + Q_OBJECT +public: + DirectShowVideoOutputControl(QObject *parent = 0); + ~DirectShowVideoOutputControl(); + + QList<Output> availableOutputs() const; + + Output output() const; + void setOutput(Output output); + +Q_SIGNALS: + void outputChanged(); + +private: + Output m_output; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowvideorenderercontrol.cpp b/src/plugins/mediaservices/directshow/mediaplayer/directshowvideorenderercontrol.cpp new file mode 100644 index 0000000..f27cb10 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowvideorenderercontrol.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowvideorenderercontrol.h" + +#include "videosurfacefilter.h" + + +QT_BEGIN_NAMESPACE + + +DirectShowVideoRendererControl::DirectShowVideoRendererControl(DirectShowEventLoop *loop, QObject *parent) + : QVideoRendererControl(parent) + , m_loop(loop) + , m_surface(0) + , m_filter(0) +{ +} + +DirectShowVideoRendererControl::~DirectShowVideoRendererControl() +{ + delete m_filter; +} + +QAbstractVideoSurface *DirectShowVideoRendererControl::surface() const +{ + return m_surface; +} + +void DirectShowVideoRendererControl::setSurface(QAbstractVideoSurface *surface) +{ + if (surface != m_surface) { + m_surface = surface; + + VideoSurfaceFilter *existingFilter = m_filter; + + if (surface) { + m_filter = new VideoSurfaceFilter(surface, m_loop); + } else { + m_filter = 0; + } + + emit filterChanged(); + + delete existingFilter; + } +} + +IBaseFilter *DirectShowVideoRendererControl::filter() +{ + return m_filter; +} + + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/directshow/mediaplayer/directshowvideorenderercontrol.h b/src/plugins/mediaservices/directshow/mediaplayer/directshowvideorenderercontrol.h new file mode 100644 index 0000000..6b4f4a2 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/directshowvideorenderercontrol.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWVIDEORENDERERCONTROL_H +#define DIRECTSHOWVIDEORENDERERCONTROL_H + +#include <QtMultimedia/qvideorenderercontrol.h> + +#include <dshow.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class DirectShowEventLoop; +class VideoSurfaceFilter; + +class DirectShowVideoRendererControl : public QVideoRendererControl +{ + Q_OBJECT +public: + DirectShowVideoRendererControl(DirectShowEventLoop *loop, QObject *parent = 0); + ~DirectShowVideoRendererControl(); + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + + IBaseFilter *filter(); + +Q_SIGNALS: + void filterChanged(); + +private: + DirectShowEventLoop *m_loop; + QAbstractVideoSurface *m_surface; + VideoSurfaceFilter *m_filter; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/directshow/mediaplayer/mediaplayer.pri b/src/plugins/mediaservices/directshow/mediaplayer/mediaplayer.pri new file mode 100644 index 0000000..a7adb38 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/mediaplayer.pri @@ -0,0 +1,45 @@ +INCLUDEPATH += $$PWD + +DEFINES += QMEDIA_DIRECTSHOW_PLAYER + +win32-g++: DEFINES += QT_NO_WMSDK + +HEADERS += \ + $$PWD/directshowaudioendpointcontrol.h \ + $$PWD/directshoweventloop.h \ + $$PWD/directshowglobal.h \ + $$PWD/directshowioreader.h \ + $$PWD/directshowiosource.h \ + $$PWD/directshowmediatype.h \ + $$PWD/directshowmediatypelist.h \ + $$PWD/directshowmetadatacontrol.h \ + $$PWD/directshowpinenum.h \ + $$PWD/directshowplayercontrol.h \ + $$PWD/directshowplayerservice.h \ + $$PWD/directshowsamplescheduler.h \ + $$PWD/directshowvideooutputcontrol.h \ + $$PWD/directshowvideorenderercontrol.h \ + $$PWD/mediasamplevideobuffer.h \ + $$PWD/videosurfacefilter.h \ + $$PWD/vmr9videowindowcontrol.h + +SOURCES += \ + $$PWD/directshowaudioendpointcontrol.cpp \ + $$PWD/directshoweventloop.cpp \ + $$PWD/directshowioreader.cpp \ + $$PWD/directshowiosource.cpp \ + $$PWD/directshowmediatype.cpp \ + $$PWD/directshowmediatypelist.cpp \ + $$PWD/directshowmetadatacontrol.cpp \ + $$PWD/directshowpinenum.cpp \ + $$PWD/directshowplayercontrol.cpp \ + $$PWD/directshowplayerservice.cpp \ + $$PWD/directshowsamplescheduler.cpp \ + $$PWD/directshowvideooutputcontrol.cpp \ + $$PWD/directshowvideorenderercontrol.cpp \ + $$PWD/mediasamplevideobuffer.cpp \ + $$PWD/videosurfacefilter.cpp \ + $$PWD/vmr9videowindowcontrol.cpp + +LIBS += -lstrmiids -ldmoguids -luuid -lmsdmo -lole32 -loleaut32 + diff --git a/src/plugins/mediaservices/directshow/mediaplayer/mediasamplevideobuffer.cpp b/src/plugins/mediaservices/directshow/mediaplayer/mediasamplevideobuffer.cpp new file mode 100644 index 0000000..7eff226 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/mediasamplevideobuffer.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mediasamplevideobuffer.h" + + +QT_BEGIN_NAMESPACE + +MediaSampleVideoBuffer::MediaSampleVideoBuffer(IMediaSample *sample, int bytesPerLine) + : QAbstractVideoBuffer(NoHandle) + , m_sample(sample) + , m_bytesPerLine(m_bytesPerLine) + , m_mapMode(NotMapped) +{ + m_sample->AddRef(); +} + +MediaSampleVideoBuffer::~MediaSampleVideoBuffer() +{ + m_sample->Release(); +} + +uchar *MediaSampleVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine) +{ + if (m_mapMode == NotMapped && mode != NotMapped) { + if (numBytes) + *numBytes = m_sample->GetActualDataLength(); + + if (bytesPerLine) + *bytesPerLine = m_bytesPerLine; + + BYTE *bytes = 0; + + if (m_sample->GetPointer(&bytes) == S_OK) { + m_mapMode = mode; + + return reinterpret_cast<uchar *>(bytes); + } + } + return 0; +} + +void MediaSampleVideoBuffer::unmap() +{ + m_mapMode = NotMapped; +} + +QAbstractVideoBuffer::MapMode MediaSampleVideoBuffer::mapMode() const +{ + return m_mapMode; +} + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/directshow/mediaplayer/mediasamplevideobuffer.h b/src/plugins/mediaservices/directshow/mediaplayer/mediasamplevideobuffer.h new file mode 100644 index 0000000..06dc31c --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/mediasamplevideobuffer.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MEDIASAMPLEVIDEOBUFFER_H +#define MEDIASAMPLEVIDEOBUFFER_H + +#include <QtMultimedia/qabstractvideobuffer.h> + +#include <dshow.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class MediaSampleVideoBuffer : public QAbstractVideoBuffer +{ +public: + MediaSampleVideoBuffer(IMediaSample *sample, int bytesPerLine); + ~MediaSampleVideoBuffer(); + + IMediaSample *sample() { return m_sample; } + + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine); + void unmap(); + + MapMode mapMode() const; + +private: + IMediaSample *m_sample; + int m_bytesPerLine; + MapMode m_mapMode; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/directshow/mediaplayer/videosurfacefilter.cpp b/src/plugins/mediaservices/directshow/mediaplayer/videosurfacefilter.cpp new file mode 100644 index 0000000..7b4aad5 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/videosurfacefilter.cpp @@ -0,0 +1,625 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "videosurfacefilter.h" + +#include "directshoweventloop.h" +#include "directshowglobal.h" +#include "directshowpinenum.h" +#include "mediasamplevideobuffer.h" + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qcoreevent.h> +#include <QtCore/qthread.h> +#include <QtMultimedia/qabstractvideosurface.h> + +#include <initguid.h> + + +QT_BEGIN_NAMESPACE + +// { e23cad72-153d-406c-bf3f-4c4b523d96f2 } +DEFINE_GUID(CLSID_VideoSurfaceFilter, +0xe23cad72, 0x153d, 0x406c, 0xbf, 0x3f, 0x4c, 0x4b, 0x52, 0x3d, 0x96, 0xf2); + +VideoSurfaceFilter::VideoSurfaceFilter( + QAbstractVideoSurface *surface, DirectShowEventLoop *loop, QObject *parent) + : QObject(parent) + , m_ref(1) + , m_state(State_Stopped) + , m_surface(surface) + , m_loop(loop) + , m_graph(0) + , m_peerPin(0) + , m_bytesPerLine(0) + , m_startResult(S_OK) + , m_pinId(QString::fromLatin1("reference")) + , m_sampleScheduler(static_cast<IPin *>(this)) +{ + connect(surface, SIGNAL(supportedFormatsChanged()), this, SLOT(supportedFormatsChanged())); + connect(&m_sampleScheduler, SIGNAL(sampleReady()), this, SLOT(sampleReady())); +} + +VideoSurfaceFilter::~VideoSurfaceFilter() +{ + Q_ASSERT(m_ref == 1); +} + +HRESULT VideoSurfaceFilter::QueryInterface(REFIID riid, void **ppvObject) +{ + if (!ppvObject) { + return E_POINTER; + } else if (riid == IID_IUnknown + || riid == IID_IPersist + || riid == IID_IMediaFilter + || riid == IID_IBaseFilter) { + *ppvObject = static_cast<IBaseFilter *>(this); + } else if (riid == IID_IAMFilterMiscFlags) { + *ppvObject = static_cast<IAMFilterMiscFlags *>(this); + } else if (riid == IID_IPin) { + *ppvObject = static_cast<IPin *>(this); + } else if (riid == IID_IMemInputPin) { + *ppvObject = static_cast<IMemInputPin *>(&m_sampleScheduler); + } else { + *ppvObject = 0; + + return E_NOINTERFACE; + } + + AddRef(); + + return S_OK; +} + +ULONG VideoSurfaceFilter::AddRef() +{ + return InterlockedIncrement(&m_ref); +} + +ULONG VideoSurfaceFilter::Release() +{ + ULONG ref = InterlockedDecrement(&m_ref); + + Q_ASSERT(ref != 0); + + return ref; +} + +HRESULT VideoSurfaceFilter::GetClassID(CLSID *pClassID) +{ + *pClassID = CLSID_VideoSurfaceFilter; + + return S_OK; +} + +HRESULT VideoSurfaceFilter::Run(REFERENCE_TIME tStart) +{ + m_state = State_Running; + + m_sampleScheduler.run(tStart); + + return S_OK; +} + +HRESULT VideoSurfaceFilter::Pause() +{ + m_state = State_Paused; + + m_sampleScheduler.pause(); + + return S_OK; +} + +HRESULT VideoSurfaceFilter::Stop() +{ + m_state = State_Stopped; + + m_sampleScheduler.stop(); + + return S_OK; +} + +HRESULT VideoSurfaceFilter::GetState(DWORD dwMilliSecsTimeout, FILTER_STATE *pState) +{ + if (!pState) + return E_POINTER; + + *pState = m_state; + + return S_OK; +} + +HRESULT VideoSurfaceFilter::SetSyncSource(IReferenceClock *pClock) +{ + + m_sampleScheduler.setClock(pClock); + + return S_OK; +} + +HRESULT VideoSurfaceFilter::GetSyncSource(IReferenceClock **ppClock) +{ + if (!ppClock) { + return E_POINTER; + } else { + *ppClock = m_sampleScheduler.clock(); + + if (*ppClock) { + (*ppClock)->AddRef(); + + return S_OK; + } else { + return S_FALSE; + } + } +} + +HRESULT VideoSurfaceFilter::EnumPins(IEnumPins **ppEnum) +{ + if (ppEnum) { + *ppEnum = new DirectShowPinEnum(QList<IPin *>() << this); + + return S_OK; + } else { + return E_POINTER; + } +} + +HRESULT VideoSurfaceFilter::FindPin(LPCWSTR pId, IPin **ppPin) +{ + if (!ppPin || !pId) { + return E_POINTER; + } else if (QString::fromWCharArray(pId) == m_pinId) { + AddRef(); + + *ppPin = this; + + return S_OK; + } else { + return VFW_E_NOT_FOUND; + } +} + +HRESULT VideoSurfaceFilter::JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName) +{ + m_graph = pGraph; + m_name = QString::fromWCharArray(pName); + + return S_OK; +} + +HRESULT VideoSurfaceFilter::QueryFilterInfo(FILTER_INFO *pInfo) +{ + if (pInfo) { + QString name = m_name; + + if (name.length() >= MAX_FILTER_NAME) + name.truncate(MAX_FILTER_NAME - 1); + + int length = name.toWCharArray(pInfo->achName); + pInfo->achName[length] = '\0'; + + if (m_graph) + m_graph->AddRef(); + + pInfo->pGraph = m_graph; + + return S_OK; + } else { + return E_POINTER; + } +} + +HRESULT VideoSurfaceFilter::QueryVendorInfo(LPWSTR *pVendorInfo) +{ + Q_UNUSED(pVendorInfo); + + return E_NOTIMPL; +} + +ULONG VideoSurfaceFilter::GetMiscFlags() +{ + return AM_FILTER_MISC_FLAGS_IS_RENDERER; +} + + +HRESULT VideoSurfaceFilter::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt) +{ + // This is an input pin, you shouldn't be calling Connect on it. + return E_POINTER; +} + +HRESULT VideoSurfaceFilter::ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt) +{ + if (!pConnector) { + return E_POINTER; + } else if (!pmt) { + return E_POINTER; + } else { + HRESULT hr; + QMutexLocker locker(&m_mutex); + + if (m_peerPin) { + hr = VFW_E_ALREADY_CONNECTED; + } else if (pmt->majortype != MEDIATYPE_Video) { + hr = VFW_E_TYPE_NOT_ACCEPTED; + } else { + m_surfaceFormat = DirectShowMediaType::formatFromType(*pmt); + m_bytesPerLine = DirectShowMediaType::bytesPerLine(m_surfaceFormat); + + if (thread() == QThread::currentThread()) { + hr = start(); + } else { + m_loop->postEvent(this, new QEvent(QEvent::Type(StartSurface))); + + m_wait.wait(&m_mutex); + + hr = m_startResult; + } + } + if (hr == S_OK) { + m_peerPin = pConnector; + m_peerPin->AddRef(); + + DirectShowMediaType::copy(&m_mediaType, *pmt); + } + return hr; + } +} + +HRESULT VideoSurfaceFilter::start() +{ + if (!m_surface->start(m_surfaceFormat)) { + return VFW_E_TYPE_NOT_ACCEPTED; + } else { + return S_OK; + } +} + +HRESULT VideoSurfaceFilter::Disconnect() +{ + QMutexLocker locker(&m_mutex); + + if (!m_peerPin) + return S_FALSE; + + if (thread() == QThread::currentThread()) { + stop(); + } else { + m_loop->postEvent(this, new QEvent(QEvent::Type(StopSurface))); + + m_wait.wait(&m_mutex); + } + + m_mediaType.clear(); + + m_sampleScheduler.NotifyAllocator(0, FALSE); + + m_peerPin->Release(); + m_peerPin = 0; + + return S_OK; +} + +void VideoSurfaceFilter::stop() +{ + m_surface->stop(); +} + +HRESULT VideoSurfaceFilter::ConnectedTo(IPin **ppPin) +{ + if (!ppPin) { + return E_POINTER; + } else { + QMutexLocker locker(&m_mutex); + + if (!m_peerPin) { + return VFW_E_NOT_CONNECTED; + } else { + m_peerPin->AddRef(); + + *ppPin = m_peerPin; + + return S_OK; + } + } +} + +HRESULT VideoSurfaceFilter::ConnectionMediaType(AM_MEDIA_TYPE *pmt) +{ + if (!pmt) { + return E_POINTER; + } else { + QMutexLocker locker(&m_mutex); + + if (!m_peerPin) { + return VFW_E_NOT_CONNECTED; + } else { + DirectShowMediaType::copy(pmt, m_mediaType); + + return S_OK; + } + } +} + +HRESULT VideoSurfaceFilter::QueryPinInfo(PIN_INFO *pInfo) +{ + if (!pInfo) { + return E_POINTER; + } else { + AddRef(); + + pInfo->pFilter = this; + pInfo->dir = PINDIR_INPUT; + + const int bytes = qMin(MAX_FILTER_NAME, (m_pinId.length() + 1) * 2); + + qMemCopy(pInfo->achName, m_pinId.utf16(), bytes); + + return S_OK; + } +} + +HRESULT VideoSurfaceFilter::QueryId(LPWSTR *Id) +{ + if (!Id) { + return E_POINTER; + } else { + const int bytes = (m_pinId.length() + 1) * 2; + + *Id = static_cast<LPWSTR>(::CoTaskMemAlloc(bytes)); + + qMemCopy(*Id, m_pinId.utf16(), bytes); + + return S_OK; + } +} + +HRESULT VideoSurfaceFilter::QueryAccept(const AM_MEDIA_TYPE *pmt) +{ + return !m_surface->isFormatSupported(DirectShowMediaType::formatFromType(*pmt)) + ? S_OK + : S_FALSE; +} + +HRESULT VideoSurfaceFilter::EnumMediaTypes(IEnumMediaTypes **ppEnum) +{ + if (!ppEnum) { + return E_POINTER; + } else { + QMutexLocker locker(&m_mutex); + + *ppEnum = createMediaTypeEnum(); + + return S_OK; + } +} + +HRESULT VideoSurfaceFilter::QueryInternalConnections(IPin **apPin, ULONG *nPin) +{ + Q_UNUSED(apPin); + Q_UNUSED(nPin); + + return E_NOTIMPL; +} + +HRESULT VideoSurfaceFilter::EndOfStream() +{ + QMutexLocker locker(&m_mutex); + + if (!m_sampleScheduler.scheduleEndOfStream()) { + if (IMediaEventSink *sink = com_cast<IMediaEventSink>(m_graph)) { + sink->Notify( + EC_COMPLETE, + S_OK, + reinterpret_cast<LONG_PTR>(static_cast<IBaseFilter *>(this))); + sink->Release(); + } + } + + return S_OK; +} + +HRESULT VideoSurfaceFilter::BeginFlush() +{ + QMutexLocker locker(&m_mutex); + + m_sampleScheduler.setFlushing(true); + + if (thread() == QThread::currentThread()) { + flush(); + } else { + m_loop->postEvent(this, new QEvent(QEvent::Type(FlushSurface))); + + m_wait.wait(&m_mutex); + } + + return S_OK; +} + +HRESULT VideoSurfaceFilter::EndFlush() +{ + QMutexLocker locker(&m_mutex); + + m_sampleScheduler.setFlushing(false); + + return S_OK; +} + +void VideoSurfaceFilter::flush() +{ + m_surface->present(QVideoFrame()); + + m_wait.wakeAll(); +} + +HRESULT VideoSurfaceFilter::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) +{ + Q_UNUSED(tStart); + Q_UNUSED(tStop); + Q_UNUSED(dRate); + + return S_OK; +} + +HRESULT VideoSurfaceFilter::QueryDirection(PIN_DIRECTION *pPinDir) +{ + if (!pPinDir) { + return E_POINTER; + } else { + *pPinDir = PINDIR_INPUT; + + return S_OK; + } +} + +int VideoSurfaceFilter::currentMediaTypeToken() +{ + QMutexLocker locker(&m_mutex); + + return DirectShowMediaTypeList::currentMediaTypeToken(); +} + +HRESULT VideoSurfaceFilter::nextMediaType( + int token, int *index, ULONG count, AM_MEDIA_TYPE **types, ULONG *fetchedCount) +{ + QMutexLocker locker(&m_mutex); + + return DirectShowMediaTypeList::nextMediaType(token, index, count, types, fetchedCount); + +} + +HRESULT VideoSurfaceFilter::skipMediaType(int token, int *index, ULONG count) +{ + QMutexLocker locker(&m_mutex); + + return DirectShowMediaTypeList::skipMediaType(token, index, count); +} + +HRESULT VideoSurfaceFilter::cloneMediaType(int token, int index, IEnumMediaTypes **enumeration) +{ + QMutexLocker locker(&m_mutex); + + return DirectShowMediaTypeList::cloneMediaType(token, index, enumeration); +} + +void VideoSurfaceFilter::customEvent(QEvent *event) +{ + if (event->type() == StartSurface) { + QMutexLocker locker(&m_mutex); + + m_startResult = start(); + + m_wait.wakeAll(); + } else if (event->type() == StopSurface) { + QMutexLocker locker(&m_mutex); + + stop(); + + m_wait.wakeAll(); + } else if (event->type() == FlushSurface) { + QMutexLocker locker(&m_mutex); + + flush(); + + m_wait.wakeAll(); + } else { + QObject::customEvent(event); + } +} + +void VideoSurfaceFilter::supportedFormatsChanged() +{ + QMutexLocker locker(&m_mutex); + + QList<QVideoFrame::PixelFormat> formats = m_surface->supportedPixelFormats(); + + QVector<AM_MEDIA_TYPE> mediaTypes; + mediaTypes.reserve(formats.count()); + + AM_MEDIA_TYPE type; + type.majortype = MEDIATYPE_Video; + type.bFixedSizeSamples = TRUE; + type.bTemporalCompression = FALSE; + type.lSampleSize = 0; + type.formattype = GUID_NULL; + type.pUnk = 0; + type.cbFormat = 0; + type.pbFormat = 0; + + foreach (QVideoFrame::PixelFormat format, formats) { + type.subtype = DirectShowMediaType::convertPixelFormat(format); + + if (type.subtype != MEDIASUBTYPE_None) + mediaTypes.append(type); + } + + setMediaTypes(mediaTypes); +} + +void VideoSurfaceFilter::sampleReady() +{ + bool eos = false; + + IMediaSample *sample = m_sampleScheduler.takeSample(&eos); + + if (sample) { + m_surface->present(QVideoFrame( + new MediaSampleVideoBuffer(sample, m_bytesPerLine), + m_surfaceFormat.frameSize(), + m_surfaceFormat.pixelFormat())); + + sample->Release(); + + if (eos) { + if (IMediaEventSink *sink = com_cast<IMediaEventSink>(m_graph)) { + sink->Notify( + EC_COMPLETE, + S_OK, + reinterpret_cast<LONG_PTR>(static_cast<IBaseFilter *>(this))); + sink->Release(); + } + } + } +} + +QT_END_NAMESPACE + diff --git a/src/plugins/mediaservices/directshow/mediaplayer/videosurfacefilter.h b/src/plugins/mediaservices/directshow/mediaplayer/videosurfacefilter.h new file mode 100644 index 0000000..8f3a101 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/videosurfacefilter.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef VIDEOSURFACEFILTER_H +#define VIDEOSURFACEFILTER_H + +#include "directshowmediatypelist.h" +#include "directshowsamplescheduler.h" +#include "directshowmediatype.h" + +#include <QtCore/qbasictimer.h> +#include <QtCore/qcoreevent.h> +#include <QtCore/qmutex.h> +#include <QtCore/qsemaphore.h> +#include <QtCore/qstring.h> +#include <QtCore/qwaitcondition.h> + +#include <dshow.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QAbstractVideoSurface; + +class DirectShowEventLoop; + +class VideoSurfaceFilter + : public QObject + , public DirectShowMediaTypeList + , public IBaseFilter + , public IAMFilterMiscFlags + , public IPin +{ + Q_OBJECT +public: + VideoSurfaceFilter( + QAbstractVideoSurface *surface, DirectShowEventLoop *loop, QObject *parent = 0); + ~VideoSurfaceFilter(); + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // IPersist + HRESULT STDMETHODCALLTYPE GetClassID(CLSID *pClassID); + + // IMediaFilter + HRESULT STDMETHODCALLTYPE Run(REFERENCE_TIME tStart); + HRESULT STDMETHODCALLTYPE Pause(); + HRESULT STDMETHODCALLTYPE Stop(); + + HRESULT STDMETHODCALLTYPE GetState(DWORD dwMilliSecsTimeout, FILTER_STATE *pState); + + HRESULT STDMETHODCALLTYPE SetSyncSource(IReferenceClock *pClock); + HRESULT STDMETHODCALLTYPE GetSyncSource(IReferenceClock **ppClock); + + // IBaseFilter + HRESULT STDMETHODCALLTYPE EnumPins(IEnumPins **ppEnum); + HRESULT STDMETHODCALLTYPE FindPin(LPCWSTR Id, IPin **ppPin); + + HRESULT STDMETHODCALLTYPE JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName); + + HRESULT STDMETHODCALLTYPE QueryFilterInfo(FILTER_INFO *pInfo); + HRESULT STDMETHODCALLTYPE QueryVendorInfo(LPWSTR *pVendorInfo); + + // IAMFilterMiscFlags + ULONG STDMETHODCALLTYPE GetMiscFlags(); + + // IPin + HRESULT STDMETHODCALLTYPE Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt); + HRESULT STDMETHODCALLTYPE ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt); + HRESULT STDMETHODCALLTYPE Disconnect(); + HRESULT STDMETHODCALLTYPE ConnectedTo(IPin **ppPin); + + HRESULT STDMETHODCALLTYPE ConnectionMediaType(AM_MEDIA_TYPE *pmt); + + HRESULT STDMETHODCALLTYPE QueryPinInfo(PIN_INFO *pInfo); + HRESULT STDMETHODCALLTYPE QueryId(LPWSTR *Id); + + HRESULT STDMETHODCALLTYPE QueryAccept(const AM_MEDIA_TYPE *pmt); + + HRESULT STDMETHODCALLTYPE EnumMediaTypes(IEnumMediaTypes **ppEnum); + + HRESULT STDMETHODCALLTYPE QueryInternalConnections(IPin **apPin, ULONG *nPin); + + HRESULT STDMETHODCALLTYPE EndOfStream(); + + HRESULT STDMETHODCALLTYPE BeginFlush(); + HRESULT STDMETHODCALLTYPE EndFlush(); + + HRESULT STDMETHODCALLTYPE NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate); + + HRESULT STDMETHODCALLTYPE QueryDirection(PIN_DIRECTION *pPinDir); + + int currentMediaTypeToken(); + HRESULT nextMediaType( + int token, int *index, ULONG count, AM_MEDIA_TYPE **types, ULONG *fetchedCount); + HRESULT skipMediaType(int token, int *index, ULONG count); + HRESULT cloneMediaType(int token, int index, IEnumMediaTypes **enumeration); + +protected: + void customEvent(QEvent *event); + +private Q_SLOTS: + void supportedFormatsChanged(); + void sampleReady(); + +private: + HRESULT start(); + void stop(); + void flush(); + + enum + { + StartSurface = QEvent::User, + StopSurface, + FlushSurface + }; + + LONG m_ref; + FILTER_STATE m_state; + QAbstractVideoSurface *m_surface; + DirectShowEventLoop *m_loop; + IFilterGraph *m_graph; + IPin *m_peerPin; + int m_bytesPerLine; + HRESULT m_startResult; + QString m_name; + QString m_pinId; + DirectShowMediaType m_mediaType; + QVideoSurfaceFormat m_surfaceFormat; + QMutex m_mutex; + QWaitCondition m_wait; + DirectShowSampleScheduler m_sampleScheduler; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/directshow/mediaplayer/vmr9videowindowcontrol.cpp b/src/plugins/mediaservices/directshow/mediaplayer/vmr9videowindowcontrol.cpp new file mode 100644 index 0000000..a564e14 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/vmr9videowindowcontrol.cpp @@ -0,0 +1,308 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "vmr9videowindowcontrol.h" + +#include "directshowglobal.h" + + +QT_BEGIN_NAMESPACE + +Vmr9VideoWindowControl::Vmr9VideoWindowControl(QObject *parent) + : QVideoWindowControl(parent) + , m_filter(com_new<IBaseFilter>(CLSID_VideoMixingRenderer9)) + , m_windowId(0) + , m_dirtyValues(0) + , m_brightness(0) + , m_contrast(0) + , m_hue(0) + , m_saturation(0) + , m_fullScreen(false) +{ + if (IVMRFilterConfig9 *config = com_cast<IVMRFilterConfig9>(m_filter)) { + config->SetRenderingMode(VMR9Mode_Windowless); + config->SetNumberOfStreams(1); + config->Release(); + } +} + +Vmr9VideoWindowControl::~Vmr9VideoWindowControl() +{ + if (m_filter) + m_filter->Release(); +} + + +WId Vmr9VideoWindowControl::winId() const +{ + return m_windowId; + +} + +void Vmr9VideoWindowControl::setWinId(WId id) +{ + m_windowId = id; + + if (IVMRWindowlessControl9 *control = com_cast<IVMRWindowlessControl9>(m_filter)) { + control->SetVideoClippingWindow(m_windowId); + control->Release(); + } +} + +QRect Vmr9VideoWindowControl::displayRect() const +{ + QRect rect; + + if (IVMRWindowlessControl9 *control = com_cast<IVMRWindowlessControl9>(m_filter)) { + RECT sourceRect; + RECT displayRect; + + if (control->GetVideoPosition(&sourceRect, &displayRect) == S_OK) { + rect = QRect( + displayRect.left, + displayRect.bottom, + displayRect.right - displayRect.left, + displayRect.bottom - displayRect.top); + } + control->Release(); + } + return rect; +} + +void Vmr9VideoWindowControl::setDisplayRect(const QRect &rect) +{ + if (IVMRWindowlessControl9 *control = com_cast<IVMRWindowlessControl9>(m_filter)) { + RECT sourceRect = { 0, 0, 0, 0 }; + RECT displayRect = { rect.left(), rect.top(), rect.right(), rect.bottom() }; + + control->GetNativeVideoSize(&sourceRect.right, &sourceRect.bottom, 0, 0); + control->SetVideoPosition(&sourceRect, &displayRect); + control->Release(); + } +} + +bool Vmr9VideoWindowControl::isFullScreen() const +{ + return m_fullScreen; +} + +void Vmr9VideoWindowControl::setFullScreen(bool fullScreen) +{ + emit fullScreenChanged(m_fullScreen = fullScreen); +} + +void Vmr9VideoWindowControl::repaint() +{ + + if (QWidget *widget = QWidget::find(m_windowId)) { + HDC dc = widget->getDC(); + if (IVMRWindowlessControl9 *control = com_cast<IVMRWindowlessControl9>(m_filter)) { + control->RepaintVideo(m_windowId, dc); + control->Release(); + } + widget->releaseDC(dc); + } +} + +QSize Vmr9VideoWindowControl::nativeSize() const +{ + QSize size; + + if (IVMRWindowlessControl9 *control = com_cast<IVMRWindowlessControl9>(m_filter)) { + LONG width; + LONG height; + + if (control->GetNativeVideoSize(&width, &height, 0, 0) == S_OK) + size = QSize(width, height); + control->Release(); + } + return size; +} + +QVideoWidget::AspectRatioMode Vmr9VideoWindowControl::aspectRatioMode() const +{ + QVideoWidget::AspectRatioMode mode = QVideoWidget::KeepAspectRatio; + + if (IVMRWindowlessControl9 *control = com_cast<IVMRWindowlessControl9>(m_filter)) { + DWORD arMode; + + if (control->GetAspectRatioMode(&arMode) == S_OK && arMode == VMR9ARMode_None) + mode = QVideoWidget::IgnoreAspectRatio; + control->Release(); + } + return mode; +} + +void Vmr9VideoWindowControl::setAspectRatioMode(QVideoWidget::AspectRatioMode mode) +{ + if (IVMRWindowlessControl9 *control = com_cast<IVMRWindowlessControl9>(m_filter)) { + switch (mode) { + case QVideoWidget::IgnoreAspectRatio: + control->SetAspectRatioMode(VMR9ARMode_None); + break; + case QVideoWidget::KeepAspectRatio: + control->SetAspectRatioMode(VMR9ARMode_LetterBox); + break; + default: + break; + } + control->Release(); + } +} + +int Vmr9VideoWindowControl::brightness() const +{ + return m_brightness; +} + +void Vmr9VideoWindowControl::setBrightness(int brightness) +{ + m_brightness = brightness; + + m_dirtyValues |= ProcAmpControl9_Brightness; + + setProcAmpValues(); + + emit brightnessChanged(brightness); +} + +int Vmr9VideoWindowControl::contrast() const +{ + return m_contrast; +} + +void Vmr9VideoWindowControl::setContrast(int contrast) +{ + m_contrast = contrast; + + m_dirtyValues |= ProcAmpControl9_Contrast; + + setProcAmpValues(); + + emit contrastChanged(contrast); +} + +int Vmr9VideoWindowControl::hue() const +{ + return m_hue; +} + +void Vmr9VideoWindowControl::setHue(int hue) +{ + m_hue = hue; + + m_dirtyValues |= ProcAmpControl9_Hue; + + setProcAmpValues(); + + emit hueChanged(hue); +} + +int Vmr9VideoWindowControl::saturation() const +{ + return m_saturation; +} + +void Vmr9VideoWindowControl::setSaturation(int saturation) +{ + m_saturation = saturation; + + m_dirtyValues |= ProcAmpControl9_Saturation; + + setProcAmpValues(); + + emit saturationChanged(saturation); +} + +void Vmr9VideoWindowControl::setProcAmpValues() +{ + if (IVMRMixerControl9 *control = com_cast<IVMRMixerControl9>(m_filter)) { + VMR9ProcAmpControl procAmp; + procAmp.dwSize = sizeof(VMR9ProcAmpControl); + procAmp.dwFlags = m_dirtyValues; + + if (m_dirtyValues & ProcAmpControl9_Brightness) { + procAmp.Brightness = scaleProcAmpValue( + control, ProcAmpControl9_Brightness, m_brightness); + } + if (m_dirtyValues & ProcAmpControl9_Contrast) { + procAmp.Contrast = scaleProcAmpValue( + control, ProcAmpControl9_Contrast, m_contrast); + } + if (m_dirtyValues & ProcAmpControl9_Hue) { + procAmp.Hue = scaleProcAmpValue( + control, ProcAmpControl9_Hue, m_hue); + } + if (m_dirtyValues & ProcAmpControl9_Saturation) { + procAmp.Saturation = scaleProcAmpValue( + control, ProcAmpControl9_Saturation, m_saturation); + } + + if (SUCCEEDED(control->SetProcAmpControl(0, &procAmp))) { + m_dirtyValues = 0; + } + + control->Release(); + } +} + +float Vmr9VideoWindowControl::scaleProcAmpValue( + IVMRMixerControl9 *control, VMR9ProcAmpControlFlags property, int value) const +{ + float scaledValue = 0.0; + + VMR9ProcAmpControlRange range; + range.dwSize = sizeof(VMR9ProcAmpControlRange); + range.dwProperty = property; + + if (SUCCEEDED(control->GetProcAmpControlRange(0, &range))) { + scaledValue = range.DefaultValue; + if (value > 0) + scaledValue += float(value) * (range.MaxValue - range.DefaultValue) / 100; + else if (value < 0) + scaledValue -= float(value) * (range.MinValue - range.DefaultValue) / 100; + } + + return scaledValue; +} + +QT_END_NAMESPACE + diff --git a/src/plugins/mediaservices/directshow/mediaplayer/vmr9videowindowcontrol.h b/src/plugins/mediaservices/directshow/mediaplayer/vmr9videowindowcontrol.h new file mode 100644 index 0000000..a0aca95 --- /dev/null +++ b/src/plugins/mediaservices/directshow/mediaplayer/vmr9videowindowcontrol.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef VMR9VIDEOWINDOWCONTROL_H +#define VMR9VIDEOWINDOWCONTROL_H + +#include <QtMultimedia/qvideowindowcontrol.h> + +#include <dshow.h> +#include <d3d9.h> +#include <vmr9.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class Vmr9VideoWindowControl : public QVideoWindowControl +{ + Q_OBJECT +public: + Vmr9VideoWindowControl(QObject *parent = 0); + ~Vmr9VideoWindowControl(); + + IBaseFilter *filter() const { return m_filter; } + + WId winId() const; + void setWinId(WId id); + + QRect displayRect() const; + void setDisplayRect(const QRect &rect); + + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + void repaint(); + + QSize nativeSize() const; + + QVideoWidget::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(QVideoWidget::AspectRatioMode mode); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + +private: + void setProcAmpValues(); + float scaleProcAmpValue( + IVMRMixerControl9 *control, VMR9ProcAmpControlFlags property, int value) const; + + IBaseFilter *m_filter; + WId m_windowId; + DWORD m_dirtyValues; + int m_brightness; + int m_contrast; + int m_hue; + int m_saturation; + bool m_fullScreen; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/gstreamer.pro b/src/plugins/mediaservices/gstreamer/gstreamer.pro new file mode 100644 index 0000000..db0ee4e --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/gstreamer.pro @@ -0,0 +1,58 @@ +TARGET = gstengine +include(../../qpluginbase.pri) + +QT += multimedia + +unix:contains(QT_CONFIG, alsa) { + DEFINES += HAVE_ALSA + LIBS += -lasound +} + +LIBS += -lXv + +CONFIG += link_pkgconfig + +PKGCONFIG += \ + gstreamer-0.10 \ + gstreamer-base-0.10 \ + gstreamer-interfaces-0.10 \ + gstreamer-audio-0.10 \ + gstreamer-video-0.10 + +# Input +HEADERS += \ + qgstreamermessage.h \ + qgstreamerbushelper.h \ + qgstreamervideooutputcontrol.h \ + qgstreamervideorendererinterface.h \ + qgstreamervideowidget.h \ + qgstreamerserviceplugin.h \ + qgstreamervideoinputdevicecontrol.h \ + qgstreamervideooverlay.h \ + qgstreamervideorenderer.h \ + qgstvideobuffer.h \ + qvideosurfacegstsink.h \ + qx11videosurface.h \ + qgstxvimagebuffer.h + + +SOURCES += \ + qgstreamermessage.cpp \ + qgstreamerbushelper.cpp \ + qgstreamervideooutputcontrol.cpp \ + qgstreamervideorendererinterface.cpp \ + qgstreamervideowidget.cpp \ + qgstreamerserviceplugin.cpp \ + qgstreamervideoinputdevicecontrol.cpp \ + qgstreamervideooverlay.cpp \ + qgstreamervideorenderer.cpp \ + qgstvideobuffer.cpp \ + qvideosurfacegstsink.cpp \ + qx11videosurface.cpp \ + qgstxvimagebuffer.cpp + +include(mediaplayer/mediaplayer.pri) + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/mediaservices +target.path = $$[QT_INSTALL_PLUGINS]/plugins/mediaservices +INSTALLS += target diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/mediaplayer.pri b/src/plugins/mediaservices/gstreamer/mediaplayer/mediaplayer.pri new file mode 100644 index 0000000..19ff034 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/mediaplayer.pri @@ -0,0 +1,17 @@ +INCLUDEPATH += $$PWD + +DEFINES += QMEDIA_GSTREAMER_PLAYER + +HEADERS += \ + $$PWD/qgstreamerplayercontrol.h \ + $$PWD/qgstreamerplayerservice.h \ + $$PWD/qgstreamerplayersession.h \ + $$PWD/qgstreamermetadataprovider.h + +SOURCES += \ + $$PWD/qgstreamerplayercontrol.cpp \ + $$PWD/qgstreamerplayerservice.cpp \ + $$PWD/qgstreamerplayersession.cpp \ + $$PWD/qgstreamermetadataprovider.cpp + + diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp new file mode 100644 index 0000000..49eeb23 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamermetadataprovider.h" +#include "qgstreamerplayersession.h" +#include <QDebug> + +#include <gst/gstversion.h> + +struct QGstreamerMetaDataKeyLookup +{ + QtMultimedia::MetaData key; + const char *token; +}; + +static const QGstreamerMetaDataKeyLookup qt_gstreamerMetaDataKeys[] = +{ + { QtMultimedia::Title, GST_TAG_TITLE }, + //{ QtMultimedia::SubTitle, 0 }, + //{ QtMultimedia::Author, 0 }, + { QtMultimedia::Comment, GST_TAG_COMMENT }, + { QtMultimedia::Description, GST_TAG_DESCRIPTION }, + //{ QtMultimedia::Category, 0 }, + { QtMultimedia::Genre, GST_TAG_GENRE }, + { QtMultimedia::Year, "year" }, + //{ QtMultimedia::UserRating, 0 }, + + { QtMultimedia::Language, GST_TAG_LANGUAGE_CODE }, + + { QtMultimedia::Publisher, GST_TAG_ORGANIZATION }, + { QtMultimedia::Copyright, GST_TAG_COPYRIGHT }, + //{ QtMultimedia::ParentalRating, 0 }, + //{ QtMultimedia::RatingOrganisation, 0 }, + + // Media + //{ QtMultimedia::Size, 0 }, + //{ QtMultimedia::MediaType, 0 }, + { QtMultimedia::Duration, GST_TAG_DURATION }, + + // Audio + { QtMultimedia::AudioBitRate, GST_TAG_BITRATE }, + { QtMultimedia::AudioCodec, GST_TAG_AUDIO_CODEC }, + //{ QtMultimedia::ChannelCount, 0 }, + //{ QtMultimedia::Frequency, 0 }, + + // Music + { QtMultimedia::AlbumTitle, GST_TAG_ALBUM }, + { QtMultimedia::AlbumArtist, GST_TAG_ARTIST}, + { QtMultimedia::ContributingArtist, GST_TAG_PERFORMER }, +#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 19) + { QtMultimedia::Composer, GST_TAG_COMPOSER }, +#endif + //{ QtMultimedia::Conductor, 0 }, + //{ QtMultimedia::Lyrics, 0 }, + //{ QtMultimedia::Mood, 0 }, + { QtMultimedia::TrackNumber, GST_TAG_TRACK_NUMBER }, + + //{ QtMultimedia::CoverArtUrlSmall, 0 }, + //{ QtMultimedia::CoverArtUrlLarge, 0 }, + + // Image/Video + //{ QtMultimedia::Resolution, 0 }, + //{ QtMultimedia::PixelAspectRatio, 0 }, + + // Video + //{ QtMultimedia::VideoFrameRate, 0 }, + //{ QtMultimedia::VideoBitRate, 0 }, + { QtMultimedia::VideoCodec, GST_TAG_VIDEO_CODEC }, + + //{ QtMultimedia::PosterUrl, 0 }, + + // Movie + //{ QtMultimedia::ChapterNumber, 0 }, + //{ QtMultimedia::Director, 0 }, + { QtMultimedia::LeadPerformer, GST_TAG_PERFORMER }, + //{ QtMultimedia::Writer, 0 }, + + // Photos + //{ QtMultimedia::CameraManufacturer, 0 }, + //{ QtMultimedia::CameraModel, 0 }, + //{ QtMultimedia::Event, 0 }, + //{ QtMultimedia::Subject, 0 } +}; + +QGstreamerMetaDataProvider::QGstreamerMetaDataProvider(QGstreamerPlayerSession *session, QObject *parent) + :QMetaDataControl(parent), m_session(session) +{ + connect(m_session, SIGNAL(tagsChanged()), SLOT(updateTags())); +} + +QGstreamerMetaDataProvider::~QGstreamerMetaDataProvider() +{ +} + +bool QGstreamerMetaDataProvider::isMetaDataAvailable() const +{ + return !m_session->tags().isEmpty(); +} + +bool QGstreamerMetaDataProvider::isWritable() const +{ + return false; +} + +QVariant QGstreamerMetaDataProvider::metaData(QtMultimedia::MetaData key) const +{ + static const int count = sizeof(qt_gstreamerMetaDataKeys) / sizeof(QGstreamerMetaDataKeyLookup); + + for (int i = 0; i < count; ++i) { + if (qt_gstreamerMetaDataKeys[i].key == key) { + return m_session->tags().value(QByteArray(qt_gstreamerMetaDataKeys[i].token)); + } + } + return QVariant(); +} + +void QGstreamerMetaDataProvider::setMetaData(QtMultimedia::MetaData key, QVariant const &value) +{ + Q_UNUSED(key); + Q_UNUSED(value); +} + +QList<QtMultimedia::MetaData> QGstreamerMetaDataProvider::availableMetaData() const +{ + static QMap<QByteArray, QtMultimedia::MetaData> keysMap; + if (keysMap.isEmpty()) { + const int count = sizeof(qt_gstreamerMetaDataKeys) / sizeof(QGstreamerMetaDataKeyLookup); + for (int i = 0; i < count; ++i) { + keysMap[QByteArray(qt_gstreamerMetaDataKeys[i].token)] = qt_gstreamerMetaDataKeys[i].key; + } + } + + QList<QtMultimedia::MetaData> res; + foreach (const QByteArray &key, m_session->tags().keys()) { + QtMultimedia::MetaData tag = keysMap.value(key, QtMultimedia::MetaData(-1)); + if (tag != -1) + res.append(tag); + } + + return res; +} + +QVariant QGstreamerMetaDataProvider::extendedMetaData(const QString &key) const +{ + return m_session->tags().value(key.toLatin1()); +} + +void QGstreamerMetaDataProvider::setExtendedMetaData(const QString &key, QVariant const &value) +{ + Q_UNUSED(key); + Q_UNUSED(value); +} + +QStringList QGstreamerMetaDataProvider::availableExtendedMetaData() const +{ + QStringList res; + foreach (const QByteArray &key, m_session->tags().keys()) + res.append(QString(key)); + + return res; +} + +void QGstreamerMetaDataProvider::updateTags() +{ + emit metaDataChanged(); +} diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamermetadataprovider.h b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamermetadataprovider.h new file mode 100644 index 0000000..267c2d7 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamermetadataprovider.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERMETADATAPROVIDER_H +#define QGSTREAMERMETADATAPROVIDER_H + +#include <QtMultimedia/qmetadatacontrol.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerPlayerSession; + +class QGstreamerMetaDataProvider : public QMetaDataControl +{ + Q_OBJECT +public: + QGstreamerMetaDataProvider( QGstreamerPlayerSession *session, QObject *parent ); + virtual ~QGstreamerMetaDataProvider(); + + bool isMetaDataAvailable() const; + bool isWritable() const; + + QVariant metaData(QtMultimedia::MetaData key) const; + void setMetaData(QtMultimedia::MetaData key, const QVariant &value); + QList<QtMultimedia::MetaData> availableMetaData() const; + + QVariant extendedMetaData(const QString &key) const ; + void setExtendedMetaData(const QString &key, const QVariant &value); + QStringList availableExtendedMetaData() const; + +private slots: + void updateTags(); + +private: + QGstreamerPlayerSession *m_session; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGSTREAMERMETADATAPROVIDER_H diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp new file mode 100644 index 0000000..d417266 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp @@ -0,0 +1,343 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamerplayercontrol.h" +#include "qgstreamerplayersession.h" + +#include <qmediaplaylistnavigator.h> + +#include <QtCore/qdir.h> +#include <QtCore/qsocketnotifier.h> +#include <QtCore/qurl.h> +#include <QtCore/qdebug.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *session, QObject *parent) + : QMediaPlayerControl(parent) + , m_session(session) + , m_stream(0) + , m_fifoNotifier(0) + , m_fifoCanWrite(false) + , m_bufferSize(0) + , m_bufferOffset(0) +{ + m_fifoFd[0] = -1; + m_fifoFd[1] = -1; + + connect(m_session, SIGNAL(positionChanged(qint64)), + this, SIGNAL(positionChanged(qint64))); + connect(m_session, SIGNAL(durationChanged(qint64)), + this, SIGNAL(durationChanged(qint64))); + connect(m_session, SIGNAL(mutedStateChanged(bool)), + this, SIGNAL(mutedChanged(bool))); + connect(m_session, SIGNAL(volumeChanged(int)), + this, SIGNAL(volumeChanged(int))); + connect(m_session, SIGNAL(stateChanged(QMediaPlayer::State)), + this, SIGNAL(stateChanged(QMediaPlayer::State))); + connect(m_session, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), + this, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); + connect(m_session,SIGNAL(bufferingProgressChanged(int)), + this, SIGNAL(bufferStatusChanged(int))); + connect(m_session, SIGNAL(audioAvailableChanged(bool)), + this, SIGNAL(audioAvailableChanged(bool))); + connect(m_session, SIGNAL(videoAvailableChanged(bool)), + this, SIGNAL(videoAvailableChanged(bool))); + connect(m_session, SIGNAL(seekableChanged(bool)), + this, SIGNAL(seekableChanged(bool))); + connect(m_session, SIGNAL(error(int,QString)), + this, SIGNAL(error(int,QString))); +} + +QGstreamerPlayerControl::~QGstreamerPlayerControl() +{ + if (m_fifoFd[0] >= 0) { + ::close(m_fifoFd[0]); + ::close(m_fifoFd[1]); + m_fifoFd[0] = -1; + m_fifoFd[1] = -1; + } +} + +qint64 QGstreamerPlayerControl::position() const +{ + return m_session->position(); +} + +qint64 QGstreamerPlayerControl::duration() const +{ + return m_session->duration(); +} + +QMediaPlayer::State QGstreamerPlayerControl::state() const +{ + return m_session->state(); +} + +QMediaPlayer::MediaStatus QGstreamerPlayerControl::mediaStatus() const +{ + return m_session->mediaStatus(); +} + +int QGstreamerPlayerControl::bufferStatus() const +{ + return 100; +} + +int QGstreamerPlayerControl::volume() const +{ + return m_session->volume(); +} + +bool QGstreamerPlayerControl::isMuted() const +{ + return m_session->isMuted(); +} + +bool QGstreamerPlayerControl::isSeekable() const +{ + return m_session->isSeekable(); +} + +QMediaTimeRange QGstreamerPlayerControl::availablePlaybackRanges() const +{ + QMediaTimeRange ranges; + + if (m_session->isSeekable()) + ranges.addInterval(0, m_session->duration()); + + return ranges; +} + +qreal QGstreamerPlayerControl::playbackRate() const +{ + return m_session->playbackRate(); +} + +void QGstreamerPlayerControl::setPlaybackRate(qreal rate) +{ + m_session->setPlaybackRate(rate); +} + +void QGstreamerPlayerControl::setPosition(qint64 pos) +{ + m_session->seek(pos); +} + +void QGstreamerPlayerControl::play() +{ + m_session->play(); + + if (m_fifoFd[1] >= 0) { + m_fifoCanWrite = true; + + writeFifo(); + } +} + +void QGstreamerPlayerControl::pause() +{ + m_session->pause(); +} + +void QGstreamerPlayerControl::stop() +{ + m_session->stop(); +} + +void QGstreamerPlayerControl::setVolume(int volume) +{ + m_session->setVolume(volume); +} + +void QGstreamerPlayerControl::setMuted(bool muted) +{ + m_session->setMuted(muted); +} + +QMediaContent QGstreamerPlayerControl::media() const +{ + return m_currentResource; +} + +const QIODevice *QGstreamerPlayerControl::mediaStream() const +{ + return m_stream; +} + +void QGstreamerPlayerControl::setMedia(const QMediaContent &content, QIODevice *stream) +{ + m_session->stop(); + + if (m_stream) { + closeFifo(); + + disconnect(m_stream, SIGNAL(readyRead()), this, SLOT(writeFifo())); + m_stream = 0; + } + + m_currentResource = content; + m_stream = stream; + + QUrl url; + + if (m_stream) { + if (m_stream->isReadable() && openFifo()) { + url = QUrl(QString(QLatin1String("fd://%1")).arg(m_fifoFd[0])); + } + } else if (!content.isNull()) { + url = content.canonicalUrl(); + } + + m_session->load(url); + + emit mediaChanged(m_currentResource); +} + +void QGstreamerPlayerControl::setVideoOutput(QObject *output) +{ + m_session->setVideoRenderer(output); +} + +bool QGstreamerPlayerControl::isAudioAvailable() const +{ + return m_session->isAudioAvailable(); +} + +bool QGstreamerPlayerControl::isVideoAvailable() const +{ + return m_session->isVideoAvailable(); +} + +void QGstreamerPlayerControl::writeFifo() +{ + if (m_fifoCanWrite) { + qint64 bytesToRead = qMin<qint64>( + m_stream->bytesAvailable(), PIPE_BUF - m_bufferSize); + + if (bytesToRead > 0) { + int bytesRead = m_stream->read(&m_buffer[m_bufferOffset + m_bufferSize], bytesToRead); + + if (bytesRead > 0) + m_bufferSize += bytesRead; + } + + if (m_bufferSize > 0) { + int bytesWritten = ::write(m_fifoFd[1], &m_buffer[m_bufferOffset], size_t(m_bufferSize)); + + if (bytesWritten > 0) { + m_bufferOffset += bytesWritten; + m_bufferSize -= bytesWritten; + + if (m_bufferSize == 0) + m_bufferOffset = 0; + } else if (errno == EAGAIN) { + m_fifoCanWrite = false; + } else { + closeFifo(); + } + } + } + + m_fifoNotifier->setEnabled(m_stream->bytesAvailable() > 0); +} + +void QGstreamerPlayerControl::fifoReadyWrite(int socket) +{ + if (socket == m_fifoFd[1]) { + m_fifoCanWrite = true; + + writeFifo(); + } +} + +bool QGstreamerPlayerControl::openFifo() +{ + Q_ASSERT(m_fifoFd[0] < 0); + Q_ASSERT(m_fifoFd[1] < 0); + + if (::pipe(m_fifoFd) == 0) { + int flags = ::fcntl(m_fifoFd[1], F_GETFD); + + if (::fcntl(m_fifoFd[1], F_SETFD, flags | O_NONBLOCK) >= 0) { + m_fifoNotifier = new QSocketNotifier(m_fifoFd[1], QSocketNotifier::Write); + + connect(m_fifoNotifier, SIGNAL(activated(int)), this, SLOT(fifoReadyWrite(int))); + + return true; + } else { + qWarning("Failed to make pipe non blocking %d", errno); + + ::close(m_fifoFd[0]); + ::close(m_fifoFd[1]); + + m_fifoFd[0] = -1; + m_fifoFd[1] = -1; + + return false; + } + } else { + qWarning("Failed to create pipe %d", errno); + + return false; + } +} + +void QGstreamerPlayerControl::closeFifo() +{ + if (m_fifoFd[0] >= 0) { + delete m_fifoNotifier; + m_fifoNotifier = 0; + + ::close(m_fifoFd[0]); + ::close(m_fifoFd[1]); + m_fifoFd[0] = -1; + m_fifoFd[1] = -1; + + m_fifoCanWrite = false; + + m_bufferSize = 0; + m_bufferOffset = 0; + } +} diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayercontrol.h b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayercontrol.h new file mode 100644 index 0000000..ae0f8b6 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayercontrol.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERPLAYERCONTROL_H +#define QGSTREAMERPLAYERCONTROL_H + +#include <QtCore/qobject.h> + +#include <QtMultimedia/qmediaplayercontrol.h> +#include <QtMultimedia/qmediaplayer.h> + +#include <limits.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QMediaPlaylist; +class QGstreamerPlayerSession; +class QGstreamerPlayerService; +class QMediaPlaylistNavigator; +class QSocketNotifier; + +class QGstreamerPlayerControl : public QMediaPlayerControl +{ + Q_OBJECT + +public: + QGstreamerPlayerControl(QGstreamerPlayerSession *session, QObject *parent = 0); + ~QGstreamerPlayerControl(); + + QMediaPlayer::State state() const; + QMediaPlayer::MediaStatus mediaStatus() const; + + qint64 position() const; + qint64 duration() const; + + int bufferStatus() const; + + int volume() const; + bool isMuted() const; + + bool isAudioAvailable() const; + bool isVideoAvailable() const; + void setVideoOutput(QObject *output); + + bool isSeekable() const; + QMediaTimeRange availablePlaybackRanges() const; + + qreal playbackRate() const; + void setPlaybackRate(qreal rate); + + QMediaContent media() const; + const QIODevice *mediaStream() const; + void setMedia(const QMediaContent&, QIODevice *); + +public Q_SLOTS: + void setPosition(qint64 pos); + + void play(); + void pause(); + void stop(); + + void setVolume(int volume); + void setMuted(bool muted); + +private Q_SLOTS: + void writeFifo(); + void fifoReadyWrite(int socket); + +private: + bool openFifo(); + void closeFifo(); + + QGstreamerPlayerSession *m_session; + QMediaContent m_currentResource; + QIODevice *m_stream; + QSocketNotifier *m_fifoNotifier; + int m_fifoFd[2]; + bool m_fifoCanWrite; + int m_bufferSize; + int m_bufferOffset; + char m_buffer[PIPE_BUF]; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayerservice.cpp b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayerservice.cpp new file mode 100644 index 0000000..d5d7bd0 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayerservice.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qvariant.h> +#include <QtCore/qdebug.h> +#include <QtGui/qwidget.h> + +#include "qgstreamerplayerservice.h" +#include "qgstreamerplayercontrol.h" +#include "qgstreamerplayersession.h" +#include "qgstreamermetadataprovider.h" +#include "qgstreamervideooutputcontrol.h" + +#include "qgstreamervideooverlay.h" +#include "qgstreamervideorenderer.h" + +#include "qgstreamervideowidget.h" +//#include "qgstreamerstreamscontrol.h" + +#include <qmediaplaylistnavigator.h> +#include <qmediaplaylist.h> + + +QT_BEGIN_NAMESPACE + + +QGstreamerPlayerService::QGstreamerPlayerService(QObject *parent): + QMediaService(parent) +{ + m_session = new QGstreamerPlayerSession(this); + m_control = new QGstreamerPlayerControl(m_session, this); + m_metaData = new QGstreamerMetaDataProvider(m_session, this); + m_videoOutput = new QGstreamerVideoOutputControl(this); +// m_streamsControl = new QGstreamerStreamsControl(m_session,this); + + connect(m_videoOutput, SIGNAL(outputChanged(QVideoOutputControl::Output)), + this, SLOT(videoOutputChanged(QVideoOutputControl::Output))); + m_videoRenderer = new QGstreamerVideoRenderer(this); + m_videoWindow = new QGstreamerVideoOverlay(this); + m_videoWidget = new QGstreamerVideoWidgetControl(this); + + m_videoOutput->setAvailableOutputs(QList<QVideoOutputControl::Output>() + << QVideoOutputControl::RendererOutput + << QVideoOutputControl::WindowOutput + << QVideoOutputControl::WidgetOutput); +} + +QGstreamerPlayerService::~QGstreamerPlayerService() +{ +} + +QMediaControl *QGstreamerPlayerService::control(const char *name) const +{ + if (qstrcmp(name,QMediaPlayerControl_iid) == 0) + return m_control; + + if (qstrcmp(name,QMetaDataControl_iid) == 0) + return m_metaData; + +// if (qstrcmp(name,QMediaStreamsControl_iid) == 0) +// return m_streamsControl; + + if (qstrcmp(name, QVideoOutputControl_iid) == 0) + return m_videoOutput; + + if (qstrcmp(name, QVideoWidgetControl_iid) == 0) + return m_videoWidget; + + if (qstrcmp(name, QVideoRendererControl_iid) == 0) + return m_videoRenderer; + + if (qstrcmp(name, QVideoWindowControl_iid) == 0) + return m_videoWindow; + + return 0; +} + +void QGstreamerPlayerService::videoOutputChanged(QVideoOutputControl::Output output) +{ + switch (output) { + case QVideoOutputControl::NoOutput: + m_control->setVideoOutput(0); + break; + case QVideoOutputControl::RendererOutput: + m_control->setVideoOutput(m_videoRenderer); + break; + case QVideoOutputControl::WindowOutput: + m_control->setVideoOutput(m_videoWindow); + break; + case QVideoOutputControl::WidgetOutput: + m_control->setVideoOutput(m_videoWidget); + break; + default: + qWarning("Invalid video output selection"); + break; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayerservice.h b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayerservice.h new file mode 100644 index 0000000..f60c72e --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayerservice.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERPLAYERSERVICE_H +#define QGSTREAMERPLAYERSERVICE_H + +#include <QtCore/qobject.h> +#include <QtCore/qiodevice.h> + +#include <QtMultimedia/qmediaservice.h> + +#include "qgstreamervideooutputcontrol.h" + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QMediaMetaData; +class QMediaPlayerControl; +class QMediaPlaylist; +class QMediaPlaylistNavigator; +class QGstreamerMetaData; +class QGstreamerPlayerControl; +class QGstreamerPlayerSession; +class QGstreamerMetaDataProvider; +class QGstreamerStreamsControl; +class QGstreamerVideoRenderer; +class QGstreamerVideoOverlay; +class QGstreamerVideoWidgetControl; + + +class QGstreamerPlayerService : public QMediaService +{ + Q_OBJECT +public: + QGstreamerPlayerService(QObject *parent = 0); + ~QGstreamerPlayerService(); + + //void setVideoOutput(QObject *output); + + QMediaControl *control(const char *name) const; + +private slots: + void videoOutputChanged(QVideoOutputControl::Output output); + +private: + QGstreamerPlayerControl *m_control; + QGstreamerPlayerSession *m_session; + QGstreamerMetaDataProvider *m_metaData; + QGstreamerVideoOutputControl *m_videoOutput; + QGstreamerStreamsControl *m_streamsControl; + + QGstreamerVideoRenderer *m_videoRenderer; + QGstreamerVideoOverlay *m_videoWindow; + QGstreamerVideoWidgetControl *m_videoWidget; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayersession.cpp b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayersession.cpp new file mode 100644 index 0000000..2e5d10f --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayersession.cpp @@ -0,0 +1,712 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamerplayersession.h" +#include "qgstreamerbushelper.h" + +#include "qgstreamervideorendererinterface.h" + +#include <gst/gstvalue.h> + +#include <QtCore/qdatetime.h> +#include <QtCore/qdebug.h> + +//#define USE_PLAYBIN2 + +QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) + :QObject(parent), + m_state(QMediaPlayer::StoppedState), + m_mediaStatus(QMediaPlayer::UnknownMediaStatus), + m_busHelper(0), + m_playbin(0), + m_nullVideoOutput(0), + m_bus(0), + m_renderer(0), + m_volume(100), + m_playbackRate(1.0), + m_muted(false), + m_audioAvailable(false), + m_videoAvailable(false), + m_seekable(false), + m_lastPosition(0), + m_duration(-1) +{ + static bool initialized = false; + if (!initialized) { + initialized = true; + gst_init(NULL, NULL); + } + +#ifdef USE_PLAYBIN2 + m_playbin = gst_element_factory_make("playbin2", NULL); +#else + m_playbin = gst_element_factory_make("playbin", NULL); +#endif + + if (m_playbin != 0) { + // Sort out messages + m_bus = gst_element_get_bus(m_playbin); + m_busHelper = new QGstreamerBusHelper(m_bus, this); + connect(m_busHelper, SIGNAL(message(QGstreamerMessage)), SLOT(busMessage(QGstreamerMessage))); + m_busHelper->installSyncEventFilter(this); + + m_nullVideoOutput = gst_element_factory_make("fakesink", NULL); + g_object_set(G_OBJECT(m_playbin), "video-sink", m_nullVideoOutput, NULL); + + // Initial volume + double volume = 1.0; + g_object_get(G_OBJECT(m_playbin), "volume", &volume, NULL); + m_volume = int(volume*100); + } +} + +QGstreamerPlayerSession::~QGstreamerPlayerSession() +{ + if (m_playbin) { + stop(); + + delete m_busHelper; + gst_object_unref(GST_OBJECT(m_bus)); + gst_object_unref(GST_OBJECT(m_playbin)); + } +} + +void QGstreamerPlayerSession::load(const QUrl &url) +{ + m_url = url; + if (m_playbin) { + m_tags.clear(); + emit tagsChanged(); + + g_object_set(G_OBJECT(m_playbin), "uri", m_url.toEncoded().constData(), NULL); + +// if (!m_streamTypes.isEmpty()) { +// m_streamProperties.clear(); +// m_streamTypes.clear(); +// +// emit streamsChanged(); +// } + } +} + +qint64 QGstreamerPlayerSession::duration() const +{ + return m_duration; +} + +qint64 QGstreamerPlayerSession::position() const +{ + GstFormat format = GST_FORMAT_TIME; + gint64 position = 0; + + if ( m_playbin && gst_element_query_position(m_playbin, &format, &position)) + return position / 1000000; + else + return 0; +} + +qreal QGstreamerPlayerSession::playbackRate() const +{ + return m_playbackRate; +} + +void QGstreamerPlayerSession::setPlaybackRate(qreal rate) +{ + if (!qFuzzyCompare(m_playbackRate, rate)) { + m_playbackRate = rate; + if (m_playbin) { + gst_element_seek(m_playbin, rate, GST_FORMAT_TIME, + GstSeekFlags(GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SEGMENT), + GST_SEEK_TYPE_NONE,0, + GST_SEEK_TYPE_NONE,0 ); + } + } +} + + +//int QGstreamerPlayerSession::activeStream(QMediaStreamsControl::StreamType streamType) const +//{ +// int streamNumber = -1; +// if (m_playbin) { +// switch (streamType) { +// case QMediaStreamsControl::AudioStream: +// g_object_set(G_OBJECT(m_playbin), "current-audio", streamNumber, NULL); +// break; +// case QMediaStreamsControl::VideoStream: +// g_object_set(G_OBJECT(m_playbin), "current-video", streamNumber, NULL); +// break; +// case QMediaStreamsControl::SubPictureStream: +// g_object_set(G_OBJECT(m_playbin), "current-text", streamNumber, NULL); +// break; +// default: +// break; +// } +// } +// +//#ifdef USE_PLAYBIN2 +// streamNumber += m_playbin2StreamOffset.value(streamType,0); +//#endif +// +// return streamNumber; +//} + +//void QGstreamerPlayerSession::setActiveStream(QMediaStreamsControl::StreamType streamType, int streamNumber) +//{ +//#ifdef USE_PLAYBIN2 +// streamNumber -= m_playbin2StreamOffset.value(streamType,0); +//#endif +// +// if (m_playbin) { +// switch (streamType) { +// case QMediaStreamsControl::AudioStream: +// g_object_get(G_OBJECT(m_playbin), "current-audio", &streamNumber, NULL); +// break; +// case QMediaStreamsControl::VideoStream: +// g_object_get(G_OBJECT(m_playbin), "current-video", &streamNumber, NULL); +// break; +// case QMediaStreamsControl::SubPictureStream: +// g_object_get(G_OBJECT(m_playbin), "current-text", &streamNumber, NULL); +// break; +// default: +// break; +// } +// } +//} + + +bool QGstreamerPlayerSession::isBuffering() const +{ + return false; +} + +int QGstreamerPlayerSession::bufferingProgress() const +{ + return 0; +} + +int QGstreamerPlayerSession::volume() const +{ + return m_volume; +} + +bool QGstreamerPlayerSession::isMuted() const +{ + return m_muted; +} + +bool QGstreamerPlayerSession::isAudioAvailable() const +{ + return m_audioAvailable; +} + +void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput) +{ + m_renderer = qobject_cast<QGstreamerVideoRendererInterface*>(videoOutput); + if (m_renderer) + g_object_set(G_OBJECT(m_playbin), "video-sink", m_renderer->videoSink(), NULL); + else + g_object_set(G_OBJECT(m_playbin), "video-sink", m_nullVideoOutput, NULL); +} + +bool QGstreamerPlayerSession::isVideoAvailable() const +{ + return m_videoAvailable; +} + +bool QGstreamerPlayerSession::isSeekable() const +{ + return m_seekable; +} + +void QGstreamerPlayerSession::play() +{ + if (m_playbin) { + if (gst_element_set_state(m_playbin, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { + qWarning() << "GStreamer; Unable to play -" << m_url.toString(); + m_state = QMediaPlayer::StoppedState; + m_mediaStatus = QMediaPlayer::InvalidMedia; + + emit stateChanged(m_state); + emit mediaStatusChanged(m_mediaStatus); + + emit error(int(QMediaPlayer::ResourceError), tr("Unable to play %1").arg(m_url.path())); + } + } +} + +void QGstreamerPlayerSession::pause() +{ + if (m_playbin) + gst_element_set_state(m_playbin, GST_STATE_PAUSED); +} + +void QGstreamerPlayerSession::stop() +{ + if (m_playbin) { + gst_element_set_state(m_playbin, GST_STATE_NULL); + + //we have to do it here, since gstreamer will not emit bus messages any more + if (m_state != QMediaPlayer::StoppedState) + emit stateChanged(m_state = QMediaPlayer::StoppedState); + } +} + +void QGstreamerPlayerSession::seek(qint64 ms) +{ + if (m_playbin && m_state != QMediaPlayer::StoppedState) { + gint64 position = (gint64)ms * 1000000; + gst_element_seek_simple(m_playbin, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, position); + } +} + +void QGstreamerPlayerSession::setVolume(int volume) +{ + m_volume = volume; + emit volumeChanged(m_volume); + + if (!m_muted && m_playbin) + g_object_set(G_OBJECT(m_playbin), "volume", m_volume/100.0, NULL); + +} + +void QGstreamerPlayerSession::setMuted(bool muted) +{ + m_muted = muted; + + g_object_set(G_OBJECT(m_playbin), "volume", (m_muted ? 0 : m_volume/100.0), NULL); + + emit mutedStateChanged(m_muted); +} + +static void addTagToMap(const GstTagList *list, + const gchar *tag, + gpointer user_data) +{ + QMap<QByteArray, QVariant> *map = reinterpret_cast<QMap<QByteArray, QVariant>* >(user_data); + + GValue val; + val.g_type = 0; + gst_tag_list_copy_value(&val,list,tag); + + switch( G_VALUE_TYPE(&val) ) { + case G_TYPE_STRING: + { + const gchar *str_value = g_value_get_string(&val); + map->insert(QByteArray(tag), QString::fromUtf8(str_value)); + break; + } + case G_TYPE_INT: + map->insert(QByteArray(tag), g_value_get_int(&val)); + break; + case G_TYPE_UINT: + map->insert(QByteArray(tag), g_value_get_uint(&val)); + break; + case G_TYPE_LONG: + map->insert(QByteArray(tag), qint64(g_value_get_long(&val))); + break; + case G_TYPE_BOOLEAN: + map->insert(QByteArray(tag), g_value_get_boolean(&val)); + break; + case G_TYPE_CHAR: + map->insert(QByteArray(tag), g_value_get_char(&val)); + break; + case G_TYPE_DOUBLE: + map->insert(QByteArray(tag), g_value_get_double(&val)); + break; + default: + // GST_TYPE_DATE is a function, not a constant, so pull it out of the switch + if (G_VALUE_TYPE(&val) == GST_TYPE_DATE) { + const GDate *date = gst_value_get_date(&val); + if (g_date_valid(date)) { + int year = g_date_get_year(date); + int month = g_date_get_month(date); + int day = g_date_get_day(date); + map->insert(QByteArray(tag), QDate(year,month,day)); + if (!map->contains("year")) + map->insert("year", year); + } + } else if (G_VALUE_TYPE(&val) == GST_TYPE_FRACTION) { + int nom = gst_value_get_fraction_numerator(&val); + int denom = gst_value_get_fraction_denominator(&val); + + if (denom > 0) { + map->insert(QByteArray(tag), double(nom)/denom); + } + } + break; + } + + g_value_unset(&val); +} + +void QGstreamerPlayerSession::setSeekable(bool seekable) +{ + if (seekable != m_seekable) { + m_seekable = seekable; + emit seekableChanged(m_seekable); + } +} + +void QGstreamerPlayerSession::setMediaStatus(QMediaPlayer::MediaStatus status) +{ + if (m_mediaStatus != status) { + m_mediaStatus = status; + emit mediaStatusChanged(status); + } +} + +bool QGstreamerPlayerSession::processSyncMessage(const QGstreamerMessage &message) +{ + GstMessage* gm = message.rawMessage(); + + if (gm && + GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT && + gst_structure_has_name(gm->structure, "prepare-xwindow-id")) + { + if (m_renderer) + m_renderer->precessNewStream(); + return true; + } + + return false; +} + +void QGstreamerPlayerSession::busMessage(const QGstreamerMessage &message) +{ + GstMessage* gm = message.rawMessage(); + + if (gm == 0) { + // Null message, query current position + quint32 newPos = position(); + + if (newPos/1000 != m_lastPosition) { + m_lastPosition = newPos/1000; + emit positionChanged(newPos); + } + + } else { + //tag message comes from elements inside playbin, not from playbin itself + if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_TAG) { + //qDebug() << "tag message"; + GstTagList *tag_list; + gst_message_parse_tag(gm, &tag_list); + gst_tag_list_foreach(tag_list, addTagToMap, &m_tags); + + //qDebug() << m_tags; + + emit tagsChanged(); + } + + if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_playbin)) { + switch (GST_MESSAGE_TYPE(gm)) { + case GST_MESSAGE_DURATION: + break; + + case GST_MESSAGE_STATE_CHANGED: + { + GstState oldState; + GstState newState; + GstState pending; + + gst_message_parse_state_changed(gm, &oldState, &newState, &pending); + + /*QStringList states; + states << "GST_STATE_VOID_PENDING" << "GST_STATE_NULL" << "GST_STATE_READY" << "GST_STATE_PAUSED" << "GST_STATE_PLAYING"; + + qDebug() << QString("state changed: old: %1 new: %2 pending: %3") \ + .arg(states[oldState]) \ + .arg(states[newState]) \ + .arg(states[pending]);*/ + + switch (newState) { + case GST_STATE_VOID_PENDING: + case GST_STATE_NULL: + setMediaStatus(QMediaPlayer::UnknownMediaStatus); + setSeekable(false); + if (m_state != QMediaPlayer::StoppedState) + emit stateChanged(m_state = QMediaPlayer::StoppedState); + break; + case GST_STATE_READY: + setMediaStatus(QMediaPlayer::LoadedMedia); + setSeekable(false); + if (m_state != QMediaPlayer::StoppedState) + emit stateChanged(m_state = QMediaPlayer::StoppedState); + break; + case GST_STATE_PAUSED: + //don't emit state changes for intermediate states + if (m_state != QMediaPlayer::PausedState && pending == GST_STATE_VOID_PENDING) + emit stateChanged(m_state = QMediaPlayer::PausedState); + + setMediaStatus(QMediaPlayer::LoadedMedia); + + //check for seekable + if (oldState == GST_STATE_READY) { + /* + //gst_element_seek_simple doesn't work reliably here, have to find a better solution + + GstFormat format = GST_FORMAT_TIME; + gint64 position = 0; + bool seekable = false; + if (gst_element_query_position(m_playbin, &format, &position)) { + seekable = gst_element_seek_simple(m_playbin, format, GST_SEEK_FLAG_NONE, position); + } + + setSeekable(seekable); + */ + + setSeekable(true); + + if (!qFuzzyCompare(m_playbackRate, qreal(1.0))) + setPlaybackRate(m_playbackRate); + + if (m_renderer) + m_renderer->precessNewStream(); + + } + + + break; + case GST_STATE_PLAYING: + if (oldState == GST_STATE_PAUSED) + getStreamsInfo(); + + if (m_state != QMediaPlayer::PlayingState) + emit stateChanged(m_state = QMediaPlayer::PlayingState); + break; + } + } + break; + + case GST_MESSAGE_EOS: + if (m_state != QMediaPlayer::StoppedState) + emit stateChanged(m_state = QMediaPlayer::StoppedState); + + setMediaStatus(QMediaPlayer::EndOfMedia); + + emit playbackFinished(); + break; + + case GST_MESSAGE_TAG: + case GST_MESSAGE_STREAM_STATUS: + case GST_MESSAGE_UNKNOWN: + break; + case GST_MESSAGE_ERROR: + { + GError *err; + gchar *debug; + gst_message_parse_error (gm, &err, &debug); + emit error(int(QMediaPlayer::ResourceError), QString::fromUtf8(err->message)); + qWarning() << "Error:" << QString::fromUtf8(err->message); + g_error_free (err); + g_free (debug); + } + break; + case GST_MESSAGE_WARNING: + case GST_MESSAGE_INFO: + break; + case GST_MESSAGE_BUFFERING: + setMediaStatus(QMediaPlayer::BufferingMedia); + break; + case GST_MESSAGE_STATE_DIRTY: + case GST_MESSAGE_STEP_DONE: + case GST_MESSAGE_CLOCK_PROVIDE: + case GST_MESSAGE_CLOCK_LOST: + case GST_MESSAGE_NEW_CLOCK: + case GST_MESSAGE_STRUCTURE_CHANGE: + case GST_MESSAGE_APPLICATION: + case GST_MESSAGE_ELEMENT: + case GST_MESSAGE_SEGMENT_START: + case GST_MESSAGE_SEGMENT_DONE: + case GST_MESSAGE_LATENCY: +#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 13) + case GST_MESSAGE_ASYNC_START: + case GST_MESSAGE_ASYNC_DONE: +#if GST_VERSION_MICRO >= 23 + case GST_MESSAGE_REQUEST_STATE: +#endif +#endif + case GST_MESSAGE_ANY: + break; + } + } + } +} + +void QGstreamerPlayerSession::getStreamsInfo() +{ + GstFormat format = GST_FORMAT_TIME; + gint64 duration = 0; + + if (gst_element_query_duration(m_playbin, &format, &duration)) { + int newDuration = duration / 1000000; + if (m_duration != newDuration) { + m_duration = newDuration; + emit durationChanged(m_duration); + } + } + + //check if video is available: + bool haveAudio = false; + bool haveVideo = false; +// m_streamProperties.clear(); +// m_streamTypes.clear(); + +#ifdef USE_PLAYBIN2 + gint audioStreamsCount = 0; + gint videoStreamsCount = 0; + gint textStreamsCount = 0; + + g_object_get(G_OBJECT(m_playbin), "n-audio", &audioStreamsCount, NULL); + g_object_get(G_OBJECT(m_playbin), "n-video", &videoStreamsCount, NULL); + g_object_get(G_OBJECT(m_playbin), "n-text", &textStreamsCount, NULL); + + haveAudio = audioStreamsCount > 0; + haveVideo = videoStreamsCount > 0; + + m_playbin2StreamOffset[QMediaStreamsControl::AudioStream] = 0; + m_playbin2StreamOffset[QMediaStreamsControl::VideoStream] = audioStreamsCount; + m_playbin2StreamOffset[QMediaStreamsControl::SubPictureStream] = audioStreamsCount+videoStreamsCount; + + for (int i=0; i<audioStreamsCount; i++) + m_streamTypes.append(QMediaStreamsControl::AudioStream); + + for (int i=0; i<videoStreamsCount; i++) + m_streamTypes.append(QMediaStreamsControl::VideoStream); + + for (int i=0; i<textStreamsCount; i++) + m_streamTypes.append(QMediaStreamsControl::SubPictureStream); + + for (int i=0; i<m_streamTypes.count(); i++) { + QMediaStreamsControl::StreamType streamType = m_streamTypes[i]; + QMap<QtMultimedia::MetaData, QVariant> streamProperties; + + int streamIndex = i - m_playbin2StreamOffset[streamType]; + + GstTagList *tags = 0; + switch (streamType) { + case QMediaStreamsControl::AudioStream: + g_signal_emit_by_name(G_OBJECT(m_playbin), "get-audio-tags", streamIndex, &tags); + break; + case QMediaStreamsControl::VideoStream: + g_signal_emit_by_name(G_OBJECT(m_playbin), "get-video-tags", streamIndex, &tags); + break; + case QMediaStreamsControl::SubPictureStream: + g_signal_emit_by_name(G_OBJECT(m_playbin), "get-text-tags", streamIndex, &tags); + break; + default: + break; + } + + if (tags && gst_is_tag_list(tags)) { + gchar *languageCode = 0; + if (gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &languageCode)) + streamProperties[QtMultimedia::Language] = QString::fromUtf8(languageCode); + + //qDebug() << "language for setream" << i << QString::fromUtf8(languageCode); + g_free (languageCode); + } + + m_streamProperties.append(streamProperties); + } + +#else + enum { + GST_STREAM_TYPE_UNKNOWN, + GST_STREAM_TYPE_AUDIO, + GST_STREAM_TYPE_VIDEO, + GST_STREAM_TYPE_TEXT, + GST_STREAM_TYPE_SUBPICTURE, + GST_STREAM_TYPE_ELEMENT + }; + + GList* streamInfo; + g_object_get(G_OBJECT(m_playbin), "stream-info", &streamInfo, NULL); + + for (; streamInfo != 0; streamInfo = g_list_next(streamInfo)) { + gint type; + gchar *languageCode = 0; + + GObject* obj = G_OBJECT(streamInfo->data); + + g_object_get(obj, "type", &type, NULL); + g_object_get(obj, "language-code", &languageCode, NULL); + + if (type == GST_STREAM_TYPE_VIDEO) + haveVideo = true; + else if (type == GST_STREAM_TYPE_AUDIO) + haveAudio = true; + +// QMediaStreamsControl::StreamType streamType = QMediaStreamsControl::UnknownStream; +// +// switch (type) { +// case GST_STREAM_TYPE_VIDEO: +// streamType = QMediaStreamsControl::VideoStream; +// break; +// case GST_STREAM_TYPE_AUDIO: +// streamType = QMediaStreamsControl::AudioStream; +// break; +// case GST_STREAM_TYPE_SUBPICTURE: +// streamType = QMediaStreamsControl::SubPictureStream; +// break; +// default: +// streamType = QMediaStreamsControl::UnknownStream; +// break; +// } +// +// QMap<QtMultimedia::MetaData, QVariant> streamProperties; +// streamProperties[QtMultimedia::Language] = QString::fromUtf8(languageCode); +// +// m_streamProperties.append(streamProperties); +// m_streamTypes.append(streamType); + } +#endif + + if (haveAudio != m_audioAvailable) { + m_audioAvailable = haveAudio; + emit audioAvailableChanged(m_audioAvailable); + } + if (haveVideo != m_videoAvailable) { + m_videoAvailable = haveVideo; + emit videoAvailableChanged(m_videoAvailable); + } + + emit streamsChanged(); +} diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayersession.h b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayersession.h new file mode 100644 index 0000000..d650ec7 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayersession.h @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERPLAYERSESSION_H +#define QGSTREAMERPLAYERSESSION_H + +#include <QObject> +#include <QUrl> +#include "qgstreamerplayercontrol.h" +#include "qgstreamerbushelper.h" +#include <QtMultimedia/qmediaplayer.h> +//#include <qmediastreamscontrol.h> + +#include <gst/gst.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerBusHelper; +class QGstreamerMessage; +class QGstreamerVideoRendererInterface; + +class QGstreamerPlayerSession : public QObject, public QGstreamerSyncEventFilter +{ +Q_OBJECT + +public: + QGstreamerPlayerSession(QObject *parent); + virtual ~QGstreamerPlayerSession(); + + QUrl url() const; + + QMediaPlayer::State state() const { return m_state; } + QMediaPlayer::MediaStatus mediaStatus() const { return m_mediaStatus; } + + qint64 duration() const; + qint64 position() const; + + bool isBuffering() const; + + int bufferingProgress() const; + + int volume() const; + bool isMuted() const; + + void setVideoRenderer(QObject *renderer); + bool isAudioAvailable() const; + bool isVideoAvailable() const; + + bool isSeekable() const; + + qreal playbackRate() const; + void setPlaybackRate(qreal rate); + + QMap<QByteArray ,QVariant> tags() const { return m_tags; } + QMap<QtMultimedia::MetaData,QVariant> streamProperties(int streamNumber) const { return m_streamProperties[streamNumber]; } +// int streamCount() const { return m_streamProperties.count(); } +// QMediaStreamsControl::StreamType streamType(int streamNumber) { return m_streamTypes.value(streamNumber, QMediaStreamsControl::UnknownStream); } +// +// int activeStream(QMediaStreamsControl::StreamType streamType) const; +// void setActiveStream(QMediaStreamsControl::StreamType streamType, int streamNumber); + + bool processSyncMessage(const QGstreamerMessage &message); + +public slots: + void load(const QUrl &url); + + void play(); + void pause(); + void stop(); + + void seek(qint64 pos); + + void setVolume(int volume); + void setMuted(bool muted); + +signals: + void durationChanged(qint64 duration); + void positionChanged(qint64 position); + void stateChanged(QMediaPlayer::State state); + void mediaStatusChanged(QMediaPlayer::MediaStatus mediaStatus); + void volumeChanged(int volume); + void mutedStateChanged(bool muted); + void audioAvailableChanged(bool audioAvailable); + void videoAvailableChanged(bool videoAvailable); + void bufferingChanged(bool buffering); + void bufferingProgressChanged(int percentFilled); + void playbackFinished(); + void tagsChanged(); + void streamsChanged(); + void seekableChanged(bool); + void error(int error, const QString &errorString); + +private slots: + void busMessage(const QGstreamerMessage &message); + void getStreamsInfo(); + void setSeekable(bool); + +private: + void setMediaStatus(QMediaPlayer::MediaStatus); + + QUrl m_url; + QMediaPlayer::State m_state; + QMediaPlayer::MediaStatus m_mediaStatus; + QGstreamerBusHelper* m_busHelper; + GstElement* m_playbin; + GstElement* m_nullVideoOutput; + GstBus* m_bus; + QGstreamerVideoRendererInterface *m_renderer; + + QMap<QByteArray, QVariant> m_tags; + QList< QMap<QtMultimedia::MetaData,QVariant> > m_streamProperties; +// QList<QMediaStreamsControl::StreamType> m_streamTypes; +// QMap<QMediaStreamsControl::StreamType, int> m_playbin2StreamOffset; + + + int m_volume; + qreal m_playbackRate; + bool m_muted; + bool m_audioAvailable; + bool m_videoAvailable; + bool m_seekable; + + qint64 m_lastPosition; + qint64 m_duration; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGSTREAMERPLAYERSESSION_H diff --git a/src/plugins/mediaservices/gstreamer/qgstreamerbushelper.cpp b/src/plugins/mediaservices/gstreamer/qgstreamerbushelper.cpp new file mode 100644 index 0000000..59ae5be --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamerbushelper.cpp @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QMap> +#include <QTimer> +#include <QMutex> + +#include "qgstreamerbushelper.h" + + +#ifndef QT_NO_GLIB +class QGstreamerBusHelperPrivate : public QObject +{ + Q_OBJECT + +public: + void addWatch(GstBus* bus, QGstreamerBusHelper* helper) + { + setParent(helper); + m_tag = gst_bus_add_watch_full(bus, 0, busCallback, this, NULL); + m_helper = helper; + filter = 0; + } + + void removeWatch(QGstreamerBusHelper* helper) + { + Q_UNUSED(helper); + g_source_remove(m_tag); + } + + static QGstreamerBusHelperPrivate* instance() + { + return new QGstreamerBusHelperPrivate; + } + +private: + void processMessage(GstBus* bus, GstMessage* message) + { + Q_UNUSED(bus); + emit m_helper->message(message); + } + + static gboolean busCallback(GstBus *bus, GstMessage *message, gpointer data) + { + reinterpret_cast<QGstreamerBusHelperPrivate*>(data)->processMessage(bus, message); + return TRUE; + } + + guint m_tag; + QGstreamerBusHelper* m_helper; + +public: + GstBus* bus; + QGstreamerSyncEventFilter *filter; + QMutex filterMutex; +}; + +#else + +class QGstreamerBusHelperPrivate : public QObject +{ + Q_OBJECT + typedef QMap<QGstreamerBusHelper*, GstBus*> HelperMap; + +public: + void addWatch(GstBus* bus, QGstreamerBusHelper* helper) + { + m_helperMap.insert(helper, bus); + + if (m_helperMap.size() == 1) + m_intervalTimer->start(); + } + + void removeWatch(QGstreamerBusHelper* helper) + { + m_helperMap.remove(helper); + + if (m_helperMap.size() == 0) + m_intervalTimer->stop(); + } + + static QGstreamerBusHelperPrivate* instance() + { + static QGstreamerBusHelperPrivate self; + + return &self; + } + +private slots: + void interval() + { + for (HelperMap::iterator it = m_helperMap.begin(); it != m_helperMap.end(); ++it) { + GstMessage* message; + + while ((message = gst_bus_poll(it.value(), GST_MESSAGE_ANY, 0)) != 0) { + emit it.key()->message(message); + gst_message_unref(message); + } + + emit it.key()->message(QGstreamerMessage()); + } + } + +private: + QGstreamerBusHelperPrivate() + { + m_intervalTimer = new QTimer(this); + m_intervalTimer->setInterval(250); + + connect(m_intervalTimer, SIGNAL(timeout()), SLOT(interval())); + } + + HelperMap m_helperMap; + QTimer* m_intervalTimer; + +public: + GstBus* bus; + QGstreamerSyncEventFilter *filter; + QMutex filterMutex; +}; +#endif + + +static GstBusSyncReply syncGstBusFilter(GstBus* bus, GstMessage* message, QGstreamerBusHelperPrivate *d) +{ + Q_UNUSED(bus); + QMutexLocker lock(&d->filterMutex); + + bool res = false; + + if (d->filter) + res = d->filter->processSyncMessage(QGstreamerMessage(message)); + + return res ? GST_BUS_DROP : GST_BUS_PASS; +} + + +/*! + \class QGstreamerBusHelper + \internal +*/ + +QGstreamerBusHelper::QGstreamerBusHelper(GstBus* bus, QObject* parent): + QObject(parent), + d(QGstreamerBusHelperPrivate::instance()) +{ + d->bus = bus; + d->addWatch(bus, this); + + gst_bus_set_sync_handler(bus, (GstBusSyncHandler)syncGstBusFilter, d); +} + +QGstreamerBusHelper::~QGstreamerBusHelper() +{ + d->removeWatch(this); + gst_bus_set_sync_handler(d->bus,0,0); +} + +void QGstreamerBusHelper::installSyncEventFilter(QGstreamerSyncEventFilter *filter) +{ + QMutexLocker lock(&d->filterMutex); + d->filter = filter; +} + +#include "qgstreamerbushelper.moc" diff --git a/src/plugins/mediaservices/gstreamer/qgstreamerbushelper.h b/src/plugins/mediaservices/gstreamer/qgstreamerbushelper.h new file mode 100644 index 0000000..8600015 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamerbushelper.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERBUSHELPER_H +#define QGSTREAMERBUSHELPER_H + +#include <QObject> + +#include <qgstreamermessage.h> +#include <gst/gst.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerSyncEventFilter { +public: + //returns true if message was processed and should be dropped, false otherwise + virtual bool processSyncMessage(const QGstreamerMessage &message) = 0; +}; + +class QGstreamerBusHelperPrivate; + +class QGstreamerBusHelper : public QObject +{ + Q_OBJECT + friend class QGstreamerBusHelperPrivate; + +public: + QGstreamerBusHelper(GstBus* bus, QObject* parent = 0); + ~QGstreamerBusHelper(); + + void installSyncEventFilter(QGstreamerSyncEventFilter *filter); + +signals: + void message(QGstreamerMessage const& message); + + +private: + QGstreamerBusHelperPrivate* d; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/qgstreamermessage.cpp b/src/plugins/mediaservices/gstreamer/qgstreamermessage.cpp new file mode 100644 index 0000000..0a689d9 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamermessage.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <gst/gst.h> + +#include "qgstreamermessage.h" + + +static int wuchi = qRegisterMetaType<QGstreamerMessage>(); + + +/*! + \class QGstreamerMessage + \internal +*/ + +QGstreamerMessage::QGstreamerMessage(): + m_message(0) +{ +} + +QGstreamerMessage::QGstreamerMessage(GstMessage* message): + m_message(message) +{ + gst_message_ref(m_message); +} + +QGstreamerMessage::QGstreamerMessage(QGstreamerMessage const& m): + m_message(m.m_message) +{ + gst_message_ref(m_message); +} + + +QGstreamerMessage::~QGstreamerMessage() +{ + if (m_message != 0) + gst_message_unref(m_message); +} + +GstMessage* QGstreamerMessage::rawMessage() const +{ + return m_message; +} + +QGstreamerMessage& QGstreamerMessage::operator=(QGstreamerMessage const& rhs) +{ + if (m_message != 0) + gst_message_unref(m_message); + + if ((m_message = rhs.m_message) != 0) + gst_message_ref(m_message); + + return *this; +} diff --git a/src/plugins/mediaservices/gstreamer/qgstreamermessage.h b/src/plugins/mediaservices/gstreamer/qgstreamermessage.h new file mode 100644 index 0000000..4680903 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamermessage.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERMESSAGE_H +#define QGSTREAMERMESSAGE_H + +#include <QMetaType> + +#include <gst/gst.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerMessage +{ +public: + QGstreamerMessage(); + QGstreamerMessage(GstMessage* message); + QGstreamerMessage(QGstreamerMessage const& m); + ~QGstreamerMessage(); + + GstMessage* rawMessage() const; + + QGstreamerMessage& operator=(QGstreamerMessage const& rhs); + +private: + GstMessage* m_message; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGstreamerMessage); + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/qgstreamerserviceplugin.cpp b/src/plugins/mediaservices/gstreamer/qgstreamerserviceplugin.cpp new file mode 100644 index 0000000..9e5f425 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamerserviceplugin.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> +#include <QtCore/qdebug.h> +#include <QtGui/QIcon> +#include <QtCore/QDir> +#include <QtCore/QDebug> + +#include "qgstreamerserviceplugin.h" + +#ifdef QMEDIA_GSTREAMER_PLAYER +#include "qgstreamerplayerservice.h" +#endif +#ifdef QMEDIA_GSTREAMER_CAPTURE +#include "qgstreamercaptureservice.h" +#endif + +#include <QtMultimedia/qmediaserviceprovider.h> + +#include <linux/types.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <linux/videodev2.h> + + +QT_BEGIN_NAMESPACE + + +QStringList QGstreamerServicePlugin::keys() const +{ + return QStringList() +#ifdef QMEDIA_GSTREAMER_PLAYER + << QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER) +#endif +#ifdef QMEDIA_GSTREAMER_CAPTURE + << QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE) + << QLatin1String(Q_MEDIASERVICE_CAMERA) +#endif + ; +} + +QMediaService* QGstreamerServicePlugin::create(const QString &key) +{ +#ifdef QMEDIA_GSTREAMER_PLAYER + if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) + return new QGstreamerPlayerService; +#endif +#ifdef QMEDIA_GSTREAMER_CAPTURE + if (key == QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE)) + return new QGstreamerCaptureService(key); + + if (key == QLatin1String(Q_MEDIASERVICE_CAMERA)) + return new QGstreamerCaptureService(key); +#endif + + qDebug() << "unsupported key:" << key; + return 0; +} + +void QGstreamerServicePlugin::release(QMediaService *service) +{ + delete service; +} + +QList<QByteArray> QGstreamerServicePlugin::devices(const QByteArray &service) const +{ + if (service == Q_MEDIASERVICE_CAMERA) { + if (m_cameraDevices.isEmpty()) + updateDevices(); + + return m_cameraDevices; + } + + return QList<QByteArray>(); +} + +QString QGstreamerServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device) +{ + if (service == Q_MEDIASERVICE_CAMERA) { + if (m_cameraDevices.isEmpty()) + updateDevices(); + + for (int i=0; i<m_cameraDevices.count(); i++) + if (m_cameraDevices[i] == device) + return m_cameraDescriptions[i]; + } + + return QString(); +} + +void QGstreamerServicePlugin::updateDevices() const +{ + m_cameraDevices.clear(); + m_cameraDescriptions.clear(); + + QDir devDir("/dev"); + devDir.setFilter(QDir::System); + + QFileInfoList entries = devDir.entryInfoList(QStringList() << "video*"); + + foreach( const QFileInfo &entryInfo, entries ) { + qDebug() << "Try" << entryInfo.filePath(); + + int fd = ::open(entryInfo.filePath().toLatin1().constData(), O_RDWR ); + if (fd == -1) + continue; + + bool isCamera = false; + + v4l2_input input; + memset(&input, 0, sizeof(input)); + for (; ::ioctl(fd, VIDIOC_ENUMINPUT, &input) >= 0; ++input.index) { + if(input.type == V4L2_INPUT_TYPE_CAMERA || input.type == 0) { + isCamera = ::ioctl(fd, VIDIOC_S_INPUT, input.index) != 0; + break; + } + } + + if (isCamera) { + // find out its driver "name" + QString name; + struct v4l2_capability vcap; + memset(&vcap, 0, sizeof(struct v4l2_capability)); + + if (ioctl(fd, VIDIOC_QUERYCAP, &vcap) != 0) + name = entryInfo.fileName(); + else + name = QString((const char*)vcap.card); + qDebug() << "found camera: " << name; + + m_cameraDevices.append(entryInfo.filePath().toLocal8Bit()); + m_cameraDescriptions.append(name); + } + ::close(fd); + } +} + +Q_EXPORT_PLUGIN2(gstengine, QGstreamerServicePlugin); + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/gstreamer/qgstreamerserviceplugin.h b/src/plugins/mediaservices/gstreamer/qgstreamerserviceplugin.h new file mode 100644 index 0000000..d6d6899 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamerserviceplugin.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QGSTREAMERSERVICEPLUGIN_H +#define QGSTREAMERSERVICEPLUGIN_H + +#include <QtMultimedia/qmediaserviceproviderplugin.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerServicePlugin : public QMediaServiceProviderPlugin, public QMediaServiceSupportedDevicesInterface +{ + Q_OBJECT + Q_INTERFACES(QMediaServiceSupportedDevicesInterface) +public: + QStringList keys() const; + QMediaService* create(QString const& key); + void release(QMediaService *service); + + QList<QByteArray> devices(const QByteArray &service) const; + QString deviceDescription(const QByteArray &service, const QByteArray &device); + +private: + void updateDevices() const; + + mutable QList<QByteArray> m_cameraDevices; + mutable QStringList m_cameraDescriptions; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGSTREAMERSERVICEPLUGIN_H diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideoinputdevicecontrol.cpp b/src/plugins/mediaservices/gstreamer/qgstreamervideoinputdevicecontrol.cpp new file mode 100644 index 0000000..ac9a1e3 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideoinputdevicecontrol.cpp @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideoinputdevicecontrol.h" + +#include <QtGui/QIcon> +#include <QtCore/QDir> +#include <QtCore/QDebug> + +#include <linux/types.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <linux/videodev2.h> + +QGstreamerVideoInputDeviceControl::QGstreamerVideoInputDeviceControl(QObject *parent) + :QVideoDeviceControl(parent), m_selectedDevice(0) +{ + update(); +} + +QGstreamerVideoInputDeviceControl::~QGstreamerVideoInputDeviceControl() +{ +} + +int QGstreamerVideoInputDeviceControl::deviceCount() const +{ + return m_names.size(); +} + +QString QGstreamerVideoInputDeviceControl::deviceName(int index) const +{ + return m_names[index]; +} + +QString QGstreamerVideoInputDeviceControl::deviceDescription(int index) const +{ + return m_descriptions[index]; +} + +QIcon QGstreamerVideoInputDeviceControl::deviceIcon(int index) const +{ + Q_UNUSED(index); + return QIcon(); +} + +int QGstreamerVideoInputDeviceControl::defaultDevice() const +{ + return 0; +} + +int QGstreamerVideoInputDeviceControl::selectedDevice() const +{ + return m_selectedDevice; +} + + +void QGstreamerVideoInputDeviceControl::setSelectedDevice(int index) +{ + if (index != m_selectedDevice) { + m_selectedDevice = index; + emit selectedDeviceChanged(index); + emit selectedDeviceChanged(deviceName(index)); + } +} + + +void QGstreamerVideoInputDeviceControl::update() +{ + m_names.clear(); + m_descriptions.clear(); + + QDir devDir("/dev"); + devDir.setFilter(QDir::System); + + QFileInfoList entries = devDir.entryInfoList(QStringList() << "video*"); + + foreach( const QFileInfo &entryInfo, entries ) { + qDebug() << "Try" << entryInfo.filePath(); + + int fd = ::open(entryInfo.filePath().toLatin1().constData(), O_RDWR ); + if (fd == -1) + continue; + + bool isCamera = false; + + v4l2_input input; + memset(&input, 0, sizeof(input)); + for (; ::ioctl(fd, VIDIOC_ENUMINPUT, &input) >= 0; ++input.index) { + if(input.type == V4L2_INPUT_TYPE_CAMERA || input.type == 0) { + isCamera = ::ioctl(fd, VIDIOC_S_INPUT, input.index) != 0; + break; + } + } + + if (isCamera) { + // find out its driver "name" + QString name; + struct v4l2_capability vcap; + memset(&vcap, 0, sizeof(struct v4l2_capability)); + + if (ioctl(fd, VIDIOC_QUERYCAP, &vcap) != 0) + name = entryInfo.fileName(); + else + name = QString((const char*)vcap.card); + qDebug() << "found camera: " << name; + + m_names.append(entryInfo.filePath()); + m_descriptions.append(name); + } + ::close(fd); + } +} diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideoinputdevicecontrol.h b/src/plugins/mediaservices/gstreamer/qgstreamervideoinputdevicecontrol.h new file mode 100644 index 0000000..6762bab --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideoinputdevicecontrol.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOINPUTDEVICECONTROL_H +#define QGSTREAMERVIDEOINPUTDEVICECONTROL_H + +#include <QtMultimedia/qvideodevicecontrol.h> +#include <QtCore/qstringlist.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QGstreamerVideoInputDeviceControl : public QVideoDeviceControl +{ +Q_OBJECT +public: + QGstreamerVideoInputDeviceControl(QObject *parent); + ~QGstreamerVideoInputDeviceControl(); + + int deviceCount() const; + + QString deviceName(int index) const; + QString deviceDescription(int index) const; + QIcon deviceIcon(int index) const; + + int defaultDevice() const; + int selectedDevice() const; + +public Q_SLOTS: + void setSelectedDevice(int index); + +private: + void update(); + + int m_selectedDevice; + QStringList m_names; + QStringList m_descriptions; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGSTREAMERAUDIOINPUTDEVICECONTROL_H diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideooutputcontrol.cpp b/src/plugins/mediaservices/gstreamer/qgstreamervideooutputcontrol.cpp new file mode 100644 index 0000000..decf524 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideooutputcontrol.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideooutputcontrol.h" + +QGstreamerVideoOutputControl::QGstreamerVideoOutputControl(QObject *parent) + : QVideoOutputControl(parent) + , m_output(NoOutput) +{ +} + +QList<QVideoOutputControl::Output> QGstreamerVideoOutputControl::availableOutputs() const +{ + return m_outputs; +} + +void QGstreamerVideoOutputControl::setAvailableOutputs(const QList<Output> &outputs) +{ + emit availableOutputsChanged(m_outputs = outputs); +} + +QVideoOutputControl::Output QGstreamerVideoOutputControl::output() const +{ + return m_output; +} + +void QGstreamerVideoOutputControl::setOutput(Output output) +{ + if (!m_outputs.contains(output)) + output = NoOutput; + + if (m_output != output) + emit outputChanged(m_output = output); +} diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideooutputcontrol.h b/src/plugins/mediaservices/gstreamer/qgstreamervideooutputcontrol.h new file mode 100644 index 0000000..7685239 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideooutputcontrol.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOOUTPUTCONTROL_H +#define QGSTREAMERVIDEOOUTPUTCONTROL_H + +#include <QtMultimedia/qvideooutputcontrol.h> + +#include <gst/gst.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerVideoRendererInterface +{ +public: + virtual ~QGstreamerVideoRendererInterface(); + virtual GstElement *videoSink() = 0; + virtual void precessNewStream() {} +}; + +class QGstreamerVideoOutputControl : public QVideoOutputControl +{ + Q_OBJECT +public: + QGstreamerVideoOutputControl(QObject *parent = 0); + + QList<Output> availableOutputs() const; + void setAvailableOutputs(const QList<Output> &outputs); + + Output output() const; + void setOutput(Output output); + +Q_SIGNALS: + void outputChanged(QVideoOutputControl::Output output); + +private: + QList<Output> m_outputs; + Output m_output; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideooverlay.cpp b/src/plugins/mediaservices/gstreamer/qgstreamervideooverlay.cpp new file mode 100644 index 0000000..6c6c802 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideooverlay.cpp @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideooverlay.h" +#include "qvideosurfacegstsink.h" + +#include <QtMultimedia/qvideosurfaceformat.h> + +#include "qx11videosurface.h" + +QGstreamerVideoOverlay::QGstreamerVideoOverlay(QObject *parent) + : QVideoWindowControl(parent) + , m_surface(new QX11VideoSurface) + , m_videoSink(reinterpret_cast<GstElement*>(QVideoSurfaceGstSink::createSink(m_surface))) + , m_aspectRatioMode(QVideoWidget::KeepAspectRatio) + , m_fullScreen(false) +{ + if (m_videoSink) { + gst_object_ref(GST_OBJECT(m_videoSink)); //Take ownership + gst_object_sink(GST_OBJECT(m_videoSink)); + } + + connect(m_surface, SIGNAL(surfaceFormatChanged(QVideoSurfaceFormat)), + this, SLOT(surfaceFormatChanged())); +} + +QGstreamerVideoOverlay::~QGstreamerVideoOverlay() +{ + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); + + delete m_surface; +} + +WId QGstreamerVideoOverlay::winId() const +{ + return m_surface->winId(); +} + +void QGstreamerVideoOverlay::setWinId(WId id) +{ + m_surface->setWinId(id); +} + +QRect QGstreamerVideoOverlay::displayRect() const +{ + return m_displayRect; +} + +void QGstreamerVideoOverlay::setDisplayRect(const QRect &rect) +{ + m_displayRect = rect; + + setScaledDisplayRect(); +} + +QVideoWidget::AspectRatioMode QGstreamerVideoOverlay::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void QGstreamerVideoOverlay::setAspectRatioMode(QVideoWidget::AspectRatioMode mode) +{ + m_aspectRatioMode = mode; + + setScaledDisplayRect(); +} + +void QGstreamerVideoOverlay::repaint() +{ +} + +int QGstreamerVideoOverlay::brightness() const +{ + return m_surface->brightness(); +} + +void QGstreamerVideoOverlay::setBrightness(int brightness) +{ + m_surface->setBrightness(brightness); + + emit brightnessChanged(m_surface->brightness()); +} + +int QGstreamerVideoOverlay::contrast() const +{ + return m_surface->contrast(); +} + +void QGstreamerVideoOverlay::setContrast(int contrast) +{ + m_surface->setContrast(contrast); + + emit contrastChanged(m_surface->contrast()); +} + +int QGstreamerVideoOverlay::hue() const +{ + return m_surface->hue(); +} + +void QGstreamerVideoOverlay::setHue(int hue) +{ + m_surface->setHue(hue); + + emit hueChanged(m_surface->hue()); +} + +int QGstreamerVideoOverlay::saturation() const +{ + return m_surface->saturation(); +} + +void QGstreamerVideoOverlay::setSaturation(int saturation) +{ + m_surface->setSaturation(saturation); + + emit saturationChanged(m_surface->saturation()); +} + +bool QGstreamerVideoOverlay::isFullScreen() const +{ + return m_fullScreen; +} + +void QGstreamerVideoOverlay::setFullScreen(bool fullScreen) +{ + emit fullScreenChanged(m_fullScreen = fullScreen); +} + +QSize QGstreamerVideoOverlay::nativeSize() const +{ + return m_surface->surfaceFormat().sizeHint(); +} + +QAbstractVideoSurface *QGstreamerVideoOverlay::surface() const +{ + return m_surface; +} + +GstElement *QGstreamerVideoOverlay::videoSink() +{ + return m_videoSink; +} + +void QGstreamerVideoOverlay::surfaceFormatChanged() +{ + setScaledDisplayRect(); + + emit nativeSizeChanged(); +} + +void QGstreamerVideoOverlay::setScaledDisplayRect() +{ + switch (m_aspectRatioMode) { + case QVideoWidget::KeepAspectRatio: + { + QSize size = m_surface->surfaceFormat().viewport().size(); + + size.scale(m_displayRect.size(), Qt::KeepAspectRatio); + + QRect rect(QPoint(0, 0), size); + rect.moveCenter(m_displayRect.center()); + + m_surface->setDisplayRect(rect); + } + break; + case QVideoWidget::IgnoreAspectRatio: + m_surface->setDisplayRect(m_displayRect); + break; + }; +} diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideooverlay.h b/src/plugins/mediaservices/gstreamer/qgstreamervideooverlay.h new file mode 100644 index 0000000..9566949 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideooverlay.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOOVERLAY_H +#define QGSTREAMERVIDEOOVERLAY_H + +#include <QtMultimedia/qvideowindowcontrol.h> + +#include "qgstreamervideorendererinterface.h" + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QAbstractVideoSurface; +class QX11VideoSurface; + +class QGstreamerVideoOverlay : public QVideoWindowControl, public QGstreamerVideoRendererInterface +{ + Q_OBJECT + Q_INTERFACES(QGstreamerVideoRendererInterface) +public: + QGstreamerVideoOverlay(QObject *parent = 0); + ~QGstreamerVideoOverlay(); + + WId winId() const; + void setWinId(WId id); + + QRect displayRect() const; + void setDisplayRect(const QRect &rect); + + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + QSize nativeSize() const; + + QVideoWidget::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(QVideoWidget::AspectRatioMode mode); + + void repaint(); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + + QAbstractVideoSurface *surface() const; + + GstElement *videoSink(); + +private slots: + void surfaceFormatChanged(); + +private: + void setScaledDisplayRect(); + + QX11VideoSurface *m_surface; + GstElement *m_videoSink; + QVideoWidget::AspectRatioMode m_aspectRatioMode; + QRect m_displayRect; + bool m_fullScreen; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideorenderer.cpp b/src/plugins/mediaservices/gstreamer/qgstreamervideorenderer.cpp new file mode 100644 index 0000000..25a53cf --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideorenderer.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideorenderer.h" +#include "qvideosurfacegstsink.h" + +#include <QEvent> +#include <QApplication> + +#include <gst/gst.h> + +QGstreamerVideoRenderer::QGstreamerVideoRenderer(QObject *parent) + :QVideoRendererControl(parent),m_videoSink(0) +{ +} + +QGstreamerVideoRenderer::~QGstreamerVideoRenderer() +{ + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); +} + +GstElement *QGstreamerVideoRenderer::videoSink() +{ + if (!m_videoSink) { + m_videoSink = reinterpret_cast<GstElement*>(QVideoSurfaceGstSink::createSink(m_surface)); + gst_object_ref(GST_OBJECT(m_videoSink)); //Take ownership + gst_object_sink(GST_OBJECT(m_videoSink)); + } + + return m_videoSink; +} + + +QAbstractVideoSurface *QGstreamerVideoRenderer::surface() const +{ + return m_surface; +} + +void QGstreamerVideoRenderer::setSurface(QAbstractVideoSurface *surface) +{ + m_surface = surface; +} + diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideorenderer.h b/src/plugins/mediaservices/gstreamer/qgstreamervideorenderer.h new file mode 100644 index 0000000..ba3f806 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideorenderer.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEORENDERER_H +#define QGSTREAMERVIDEORENDERER_H + +#include <QtMultimedia/qvideorenderercontrol.h> +#include "qvideosurfacegstsink.h" + +#include "qgstreamervideorendererinterface.h" + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerVideoRenderer : public QVideoRendererControl, public QGstreamerVideoRendererInterface +{ + Q_OBJECT + Q_INTERFACES(QGstreamerVideoRendererInterface) +public: + QGstreamerVideoRenderer(QObject *parent = 0); + virtual ~QGstreamerVideoRenderer(); + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + + GstElement *videoSink(); + void precessNewStream() {} + +private: + GstElement *m_videoSink; + QAbstractVideoSurface *m_surface; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGSTREAMERVIDEORENDRER_H diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideorendererinterface.cpp b/src/plugins/mediaservices/gstreamer/qgstreamervideorendererinterface.cpp new file mode 100644 index 0000000..0a1c20d --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideorendererinterface.cpp @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideorendererinterface.h" + +QGstreamerVideoRendererInterface::~QGstreamerVideoRendererInterface() +{ +} diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideorendererinterface.h b/src/plugins/mediaservices/gstreamer/qgstreamervideorendererinterface.h new file mode 100644 index 0000000..39deee8 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideorendererinterface.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOOUTPUTCONTROL_H +#define QGSTREAMERVIDEOOUTPUTCONTROL_H + +#include <gst/gst.h> + +#include <QtCore/qobject.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerVideoRendererInterface +{ +public: + virtual ~QGstreamerVideoRendererInterface(); + virtual GstElement *videoSink() = 0; + virtual void precessNewStream() {} +}; + +QT_END_NAMESPACE + +#define QGstreamerVideoRendererInterface_iid "com.nokia.Qt.QGstreamerVideoRendererInterface/1.0" +Q_DECLARE_INTERFACE(QGstreamerVideoRendererInterface, QGstreamerVideoRendererInterface_iid) + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideowidget.cpp b/src/plugins/mediaservices/gstreamer/qgstreamervideowidget.cpp new file mode 100644 index 0000000..1d8d43d --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideowidget.cpp @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideowidget.h" + +#include <QtCore/qcoreevent.h> +#include <QtCore/qdebug.h> +#include <QtGui/qapplication.h> +#include <QtGui/qpainter.h> + +#include <X11/Xlib.h> +#include <gst/gst.h> +#include <gst/interfaces/xoverlay.h> +#include <gst/interfaces/propertyprobe.h> + +class QGstreamerVideoWidget : public QWidget +{ +public: + QGstreamerVideoWidget(QWidget *parent = 0) + :QWidget(parent) + { + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + QPalette palette; + palette.setColor(QPalette::Background, Qt::black); + setPalette(palette); + } + + virtual ~QGstreamerVideoWidget() {} + + QSize sizeHint() const + { + return m_nativeSize; + } + + void setNativeSize( const QSize &size) + { + if (size != m_nativeSize) { + m_nativeSize = size; + if (size.isEmpty()) + setMinimumSize(0,0); + else + setMinimumSize(160,120); + + updateGeometry(); + } + } + +protected: + void paintEvent(QPaintEvent *) + { + QPainter painter(this); + painter.fillRect(rect(), palette().background()); + } + + QSize m_nativeSize; +}; + +QGstreamerVideoWidgetControl::QGstreamerVideoWidgetControl(QObject *parent) + : QVideoWidgetControl(parent) + , m_videoSink(0) + , m_widget(new QGstreamerVideoWidget) + , m_fullScreen(false) +{ + m_widget->installEventFilter(this); + m_windowId = m_widget->winId(); + + m_videoSink = gst_element_factory_make ("xvimagesink", NULL); + if (m_videoSink) { + // Check if the xv sink is usable + if (gst_element_set_state(m_videoSink, GST_STATE_READY) != GST_STATE_CHANGE_SUCCESS) { + gst_object_unref(GST_OBJECT(m_videoSink)); + m_videoSink = 0; + } else { + gst_element_set_state(m_videoSink, GST_STATE_NULL); + + g_object_set(G_OBJECT(m_videoSink), "force-aspect-ratio", 1, (const char*)NULL); + } + } + + if (!m_videoSink) + m_videoSink = gst_element_factory_make ("ximagesink", NULL); + + gst_object_ref (GST_OBJECT (m_videoSink)); //Take ownership + gst_object_sink (GST_OBJECT (m_videoSink)); +} + +QGstreamerVideoWidgetControl::~QGstreamerVideoWidgetControl() +{ + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); + + delete m_widget; +} + +GstElement *QGstreamerVideoWidgetControl::videoSink() +{ + return m_videoSink; +} + +bool QGstreamerVideoWidgetControl::eventFilter(QObject *object, QEvent *e) +{ + if (object == m_widget) { + if (e->type() == QEvent::ParentChange || e->type() == QEvent::Show) { + WId newWId = m_widget->winId(); + if (newWId != m_windowId) { + m_windowId = newWId; + // Even if we have created a winId at this point, other X applications + // need to be aware of it. + QApplication::syncX(); + setOverlay(); + } + } + + if (e->type() == QEvent::Show) { + // Setting these values ensures smooth resizing since it + // will prevent the system from clearing the background + m_widget->setAttribute(Qt::WA_NoSystemBackground, true); + m_widget->setAttribute(Qt::WA_PaintOnScreen, true); + } else if (e->type() == QEvent::Resize) { + // This is a workaround for missing background repaints + // when reducing window size + windowExposed(); + } + } + + return false; +} + +void QGstreamerVideoWidgetControl::precessNewStream() +{ + setOverlay(); + QMetaObject::invokeMethod(this, "updateNativeVideoSize", Qt::QueuedConnection); +} + +void QGstreamerVideoWidgetControl::setOverlay() +{ + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { + gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_videoSink), m_windowId); + } +} + +void QGstreamerVideoWidgetControl::updateNativeVideoSize() +{ + if (m_videoSink) { + //find video native size to update video widget size hint + GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); + GstCaps *caps = gst_pad_get_negotiated_caps(pad); + + if (caps) { + GstStructure *str; + gint width, height; + + if ((str = gst_caps_get_structure (caps, 0))) { + if (gst_structure_get_int (str, "width", &width) && gst_structure_get_int (str, "height", &height)) { + gint aspectNum = 0; + gint aspectDenum = 0; + if (gst_structure_get_fraction(str, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { + if (aspectDenum > 0) + width = width*aspectNum/aspectDenum; + } + m_widget->setNativeSize(QSize(width, height)); + } + } + gst_caps_unref(caps); + } + } else { + m_widget->setNativeSize(QSize()); + } +} + + +void QGstreamerVideoWidgetControl::windowExposed() +{ + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) + gst_x_overlay_expose(GST_X_OVERLAY(m_videoSink)); +} + +QWidget *QGstreamerVideoWidgetControl::videoWidget() +{ + return m_widget; +} + +QVideoWidget::AspectRatioMode QGstreamerVideoWidgetControl::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void QGstreamerVideoWidgetControl::setAspectRatioMode(QVideoWidget::AspectRatioMode mode) +{ + if (m_videoSink) { + g_object_set(G_OBJECT(m_videoSink), + "force-aspect-ratio", + (mode == QVideoWidget::KeepAspectRatio), + (const char*)NULL); + } + + m_aspectRatioMode = mode; +} + +bool QGstreamerVideoWidgetControl::isFullScreen() const +{ + return m_fullScreen; +} + +void QGstreamerVideoWidgetControl::setFullScreen(bool fullScreen) +{ + emit fullScreenChanged(m_fullScreen = fullScreen); +} + +int QGstreamerVideoWidgetControl::brightness() const +{ + int brightness = 0; + + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "brightness")) + g_object_get(G_OBJECT(m_videoSink), "brightness", &brightness, NULL); + + return brightness / 10; +} + +void QGstreamerVideoWidgetControl::setBrightness(int brightness) +{ + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "brightness")) { + g_object_set(G_OBJECT(m_videoSink), "brightness", brightness * 10, NULL); + + emit brightnessChanged(brightness); + } +} + +int QGstreamerVideoWidgetControl::contrast() const +{ + int contrast = 0; + + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "contrast")) + g_object_get(G_OBJECT(m_videoSink), "contrast", &contrast, NULL); + + return contrast / 10; +} + +void QGstreamerVideoWidgetControl::setContrast(int contrast) +{ + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "contrast")) { + g_object_set(G_OBJECT(m_videoSink), "contrast", contrast * 10, NULL); + + emit contrastChanged(contrast); + } +} + +int QGstreamerVideoWidgetControl::hue() const +{ + int hue = 0; + + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "hue")) + g_object_get(G_OBJECT(m_videoSink), "hue", &hue, NULL); + + return hue / 10; +} + +void QGstreamerVideoWidgetControl::setHue(int hue) +{ + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "hue")) { + g_object_set(G_OBJECT(m_videoSink), "hue", hue * 10, NULL); + + emit hueChanged(hue); + } +} + +int QGstreamerVideoWidgetControl::saturation() const +{ + int saturation = 0; + + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "saturation")) + g_object_get(G_OBJECT(m_videoSink), "saturation", &saturation, NULL); + + return saturation / 10; +} + +void QGstreamerVideoWidgetControl::setSaturation(int saturation) +{ + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "saturation")) { + g_object_set(G_OBJECT(m_videoSink), "saturation", saturation * 10, NULL); + + emit saturationChanged(saturation); + } +} diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideowidget.h b/src/plugins/mediaservices/gstreamer/qgstreamervideowidget.h new file mode 100644 index 0000000..7b966b7 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideowidget.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOWIDGET_H +#define QGSTREAMERVIDEOWIDGET_H + +#include <QtMultimedia/qvideowidgetcontrol.h> + +#include "qgstreamervideorendererinterface.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerVideoWidget; + +class QGstreamerVideoWidgetControl + : public QVideoWidgetControl + , public QGstreamerVideoRendererInterface +{ + Q_OBJECT + Q_INTERFACES(QGstreamerVideoRendererInterface) +public: + QGstreamerVideoWidgetControl(QObject *parent = 0); + virtual ~QGstreamerVideoWidgetControl(); + + GstElement *videoSink(); + void precessNewStream(); + + QWidget *videoWidget(); + + QVideoWidget::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(QVideoWidget::AspectRatioMode mode); + + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + + void setOverlay(); + + bool eventFilter(QObject *object, QEvent *event); + +public slots: + void updateNativeVideoSize(); + +private: + void windowExposed(); + + GstElement *m_videoSink; + QGstreamerVideoWidget *m_widget; + WId m_windowId; + QVideoWidget::AspectRatioMode m_aspectRatioMode; + bool m_fullScreen; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGSTREAMERVIDEOWIDGET_H diff --git a/src/plugins/mediaservices/gstreamer/qgstvideobuffer.cpp b/src/plugins/mediaservices/gstreamer/qgstvideobuffer.cpp new file mode 100644 index 0000000..9519db6 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstvideobuffer.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstvideobuffer.h" + + +QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine) + : QAbstractVideoBuffer(NoHandle) + , m_buffer(buffer) + , m_bytesPerLine(bytesPerLine) + , m_mode(NotMapped) +{ + gst_buffer_ref(m_buffer); +} + +QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine, + QGstVideoBuffer::HandleType handleType, + const QVariant &handle) + : QAbstractVideoBuffer(handleType) + , m_buffer(buffer) + , m_bytesPerLine(bytesPerLine) + , m_mode(NotMapped) + , m_handle(handle) +{ + gst_buffer_ref(m_buffer); +} + +QGstVideoBuffer::~QGstVideoBuffer() +{ + gst_buffer_unref(m_buffer); +} + + +QAbstractVideoBuffer::MapMode QGstVideoBuffer::mapMode() const +{ + return m_mode; +} + +uchar *QGstVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine) +{ + if (mode != NotMapped && m_mode == NotMapped) { + if (numBytes) + *numBytes = m_buffer->size; + + if (bytesPerLine) + *bytesPerLine = m_bytesPerLine; + + m_mode = mode; + + return m_buffer->data; + } else { + return 0; + } +} +void QGstVideoBuffer::unmap() +{ + m_mode = NotMapped; +} + diff --git a/src/plugins/mediaservices/gstreamer/qgstvideobuffer.h b/src/plugins/mediaservices/gstreamer/qgstvideobuffer.h new file mode 100644 index 0000000..5133e2e --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstvideobuffer.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTVIDEOBUFFER_H +#define QGSTVIDEOBUFFER_H + +#include <QtMultimedia/QAbstractVideoBuffer> +#include <QtCore/qvariant.h> + +#include <gst/gst.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstVideoBuffer : public QAbstractVideoBuffer +{ +public: + QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine); + QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine, + HandleType handleType, const QVariant &handle); + ~QGstVideoBuffer(); + + MapMode mapMode() const; + + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine); + void unmap(); + + QVariant handle() const { return m_handle; } +private: + GstBuffer *m_buffer; + int m_bytesPerLine; + MapMode m_mode; + QVariant m_handle; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/qgstxvimagebuffer.cpp b/src/plugins/mediaservices/gstreamer/qgstxvimagebuffer.cpp new file mode 100644 index 0000000..0e47c98 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstxvimagebuffer.cpp @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qdebug.h> +#include <QtCore/qthread.h> +#include <QtCore/qvariant.h> +#include <QtGui/qx11info_x11.h> + +#include "qgstxvimagebuffer.h" +#include "qvideosurfacegstsink.h" + +GstBufferClass *QGstXvImageBuffer::parent_class = NULL; + +GType QGstXvImageBuffer::get_type(void) +{ + static GType buffer_type = 0; + + if (buffer_type == 0) { + static const GTypeInfo buffer_info = { + sizeof (GstBufferClass), + NULL, + NULL, + QGstXvImageBuffer::class_init, + NULL, + NULL, + sizeof(QGstXvImageBuffer), + 0, + (GInstanceInitFunc)QGstXvImageBuffer::buffer_init, + NULL + }; + buffer_type = g_type_register_static(GST_TYPE_BUFFER, + "QGstXvImageBuffer", &buffer_info, GTypeFlags(0)); + } + return buffer_type; +} + +void QGstXvImageBuffer::class_init(gpointer g_class, gpointer class_data) +{ + Q_UNUSED(class_data); + GST_MINI_OBJECT_CLASS(g_class)->finalize = + (GstMiniObjectFinalizeFunction)buffer_finalize; + parent_class = (GstBufferClass*)g_type_class_peek_parent(g_class); +} + +void QGstXvImageBuffer::buffer_init(QGstXvImageBuffer *xvImage, gpointer g_class) +{ + Q_UNUSED(g_class); + xvImage->pool = 0; + xvImage->shmInfo.shmaddr = ((char *) -1); + xvImage->shmInfo.shmid = -1; + xvImage->markedForDeletion = false; +} + +void QGstXvImageBuffer::buffer_finalize(QGstXvImageBuffer * xvImage) +{ + if (xvImage->pool) { + if (xvImage->markedForDeletion) + xvImage->pool->destroyBuffer(xvImage); + else + xvImage->pool->recycleBuffer(xvImage); + } +} + + +QGstXvImageBufferPool::QGstXvImageBufferPool(QObject *parent) + :QObject(parent) +{ +} + +QGstXvImageBufferPool::~QGstXvImageBufferPool() +{ +} + +bool QGstXvImageBufferPool::isFormatSupported(const QVideoSurfaceFormat &surfaceFormat) +{ + bool ok = true; + surfaceFormat.property("portId").toULongLong(&ok); + if (!ok) + return false; + + int xvFormatId = surfaceFormat.property("xvFormatId").toInt(&ok); + if (!ok || xvFormatId < 0) + return false; + + int dataSize = surfaceFormat.property("dataSize").toInt(&ok); + if (!ok || dataSize<=0) + return false; + + return true; +} + +QGstXvImageBuffer *QGstXvImageBufferPool::takeBuffer(const QVideoSurfaceFormat &format, GstCaps *caps) +{ + m_poolMutex.lock(); + + m_caps = caps; + if (format != m_format) { + doClear(); + m_format = format; + } + + + if (m_pool.isEmpty()) { + //qDebug() << "QGstXvImageBufferPool::takeBuffer: no buffer available, allocate the new one"; + if (QThread::currentThread() == thread()) { + m_poolMutex.unlock(); + queuedAlloc(); + m_poolMutex.lock(); + } else { + QMetaObject::invokeMethod(this, "queuedAlloc", Qt::QueuedConnection); + m_allocWaitCondition.wait(&m_poolMutex, 300); + } + } + QGstXvImageBuffer *res = 0; + + if (!m_pool.isEmpty()) { + res = m_pool.takeLast(); + } + + m_poolMutex.unlock(); + + return res; +} + +void QGstXvImageBufferPool::queuedAlloc() +{ + QMutexLocker lock(&m_poolMutex); + + Q_ASSERT(QThread::currentThread() == thread()); + + QGstXvImageBuffer *xvBuffer = (QGstXvImageBuffer *)gst_mini_object_new(QGstXvImageBuffer::get_type()); + + quint64 portId = m_format.property("portId").toULongLong(); + int xvFormatId = m_format.property("xvFormatId").toInt(); + + xvBuffer->xvImage = XvShmCreateImage( + QX11Info::display(), + portId, + xvFormatId, + 0, + m_format.frameWidth(), + m_format.frameHeight(), + &xvBuffer->shmInfo + ); + + if (!xvBuffer->xvImage) { + qDebug() << "QGstXvImageBufferPool: XvShmCreateImage failed"; + m_allocWaitCondition.wakeOne(); + return; + } + + xvBuffer->shmInfo.shmid = shmget(IPC_PRIVATE, xvBuffer->xvImage->data_size, IPC_CREAT | 0777); + xvBuffer->shmInfo.shmaddr = xvBuffer->xvImage->data = (char*)shmat(xvBuffer->shmInfo.shmid, 0, 0); + xvBuffer->shmInfo.readOnly = False; + + if (!XShmAttach(QX11Info::display(), &xvBuffer->shmInfo)) { + qDebug() << "QGstXvImageBufferPool: XShmAttach failed"; + m_allocWaitCondition.wakeOne(); + return; + } + + shmctl (xvBuffer->shmInfo.shmid, IPC_RMID, NULL); + + xvBuffer->pool = this; + GST_MINI_OBJECT_CAST(xvBuffer)->flags = 0; + gst_buffer_set_caps(GST_BUFFER_CAST(xvBuffer), m_caps); + GST_BUFFER_DATA(xvBuffer) = (uchar*)xvBuffer->xvImage->data; + GST_BUFFER_SIZE(xvBuffer) = xvBuffer->xvImage->data_size; + + m_allBuffers.append(xvBuffer); + m_pool.append(xvBuffer); + + m_allocWaitCondition.wakeOne(); +} + + +void QGstXvImageBufferPool::clear() +{ + QMutexLocker lock(&m_poolMutex); + doClear(); +} + +void QGstXvImageBufferPool::doClear() +{ + foreach (QGstXvImageBuffer *xvBuffer, m_allBuffers) { + xvBuffer->markedForDeletion = true; + } + m_allBuffers.clear(); + + foreach (QGstXvImageBuffer *xvBuffer, m_pool) { + gst_buffer_unref(GST_BUFFER(xvBuffer)); + } + m_pool.clear(); + + m_format = QVideoSurfaceFormat(); +} + +void QGstXvImageBufferPool::queuedDestroy() +{ + QMutexLocker lock(&m_destroyMutex); + + foreach(XvShmImage xvImage, m_imagesToDestroy) { + if (xvImage.shmInfo.shmaddr != ((void *) -1)) { + XShmDetach(QX11Info::display(), &xvImage.shmInfo); + XSync(QX11Info::display(), false); + + shmdt(xvImage.shmInfo.shmaddr); + } + + if (xvImage.xvImage) + XFree(xvImage.xvImage); + } + + m_imagesToDestroy.clear(); + + XSync(QX11Info::display(), false); +} + +void QGstXvImageBufferPool::recycleBuffer(QGstXvImageBuffer *xvBuffer) +{ + QMutexLocker lock(&m_poolMutex); + gst_buffer_ref(GST_BUFFER_CAST(xvBuffer)); + m_pool.append(xvBuffer); +} + +void QGstXvImageBufferPool::destroyBuffer(QGstXvImageBuffer *xvBuffer) +{ + XvShmImage imageToDestroy; + imageToDestroy.xvImage = xvBuffer->xvImage; + imageToDestroy.shmInfo = xvBuffer->shmInfo; + + m_destroyMutex.lock(); + m_imagesToDestroy.append(imageToDestroy); + m_destroyMutex.unlock(); + + if (m_imagesToDestroy.size() == 1) + QMetaObject::invokeMethod(this, "queuedDestroy", Qt::QueuedConnection); +} diff --git a/src/plugins/mediaservices/gstreamer/qgstxvimagebuffer.h b/src/plugins/mediaservices/gstreamer/qgstxvimagebuffer.h new file mode 100644 index 0000000..beeb01f --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstxvimagebuffer.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTXVIMAGEBUFFER_H +#define QGSTXVIMAGEBUFFER_H + +#include <QtMultimedia/qabstractvideobuffer.h> +#include <QtMultimedia/qvideosurfaceformat.h> +#include <QtCore/qmutex.h> +#include <QtCore/qwaitcondition.h> +#include <QtCore/qqueue.h> + +#include <X11/Xlib.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <X11/extensions/XShm.h> +#include <X11/Xlib.h> +#include <X11/extensions/Xv.h> +#include <X11/extensions/Xvlib.h> + + +#include <gst/gst.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QGstXvImageBufferPool; + +struct QGstXvImageBuffer { + GstBuffer buffer; + QGstXvImageBufferPool *pool; + XvImage *xvImage; + XShmSegmentInfo shmInfo; + bool markedForDeletion; + + static GType get_type(void); + static void class_init(gpointer g_class, gpointer class_data); + static void buffer_init(QGstXvImageBuffer *xvimage, gpointer g_class); + static void buffer_finalize(QGstXvImageBuffer * xvimage); + static GstBufferClass *parent_class; +}; + +const QAbstractVideoBuffer::HandleType XvHandleType = QAbstractVideoBuffer::HandleType(4); + +Q_DECLARE_METATYPE(XvImage*) + + +class QGstXvImageBufferPool : public QObject { +Q_OBJECT +friend class QGstXvImageBuffer; +public: + QGstXvImageBufferPool(QObject *parent = 0); + virtual ~QGstXvImageBufferPool(); + + bool isFormatSupported(const QVideoSurfaceFormat &format); + + QGstXvImageBuffer *takeBuffer(const QVideoSurfaceFormat &format, GstCaps *caps); + void clear(); + +private slots: + void queuedAlloc(); + void queuedDestroy(); + + void doClear(); + + void recycleBuffer(QGstXvImageBuffer *); + void destroyBuffer(QGstXvImageBuffer *); + +private: + struct XvShmImage { + XvImage *xvImage; + XShmSegmentInfo shmInfo; + }; + + QMutex m_poolMutex; + QMutex m_allocMutex; + QWaitCondition m_allocWaitCondition; + QMutex m_destroyMutex; + QVideoSurfaceFormat m_format; + GstCaps *m_caps; + QList<QGstXvImageBuffer*> m_pool; + QList<QGstXvImageBuffer*> m_allBuffers; + QList<XvShmImage> m_imagesToDestroy; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + + +#endif diff --git a/src/plugins/mediaservices/gstreamer/qvideosurfacegstsink.cpp b/src/plugins/mediaservices/gstreamer/qvideosurfacegstsink.cpp new file mode 100644 index 0000000..9e3807b --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qvideosurfacegstsink.cpp @@ -0,0 +1,694 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtMultimedia/QAbstractVideoSurface> +#include <QtMultimedia/QVideoFrame> +#include <QDebug> +#include <QMap> +#include <QDebug> +#include <QThread> +#include <QtGui/qx11info_x11.h> + +#include "qvideosurfacegstsink.h" + +#include "qgstvideobuffer.h" +#include "qgstxvimagebuffer.h" + + +Q_DECLARE_METATYPE(QVideoSurfaceFormat) + +QVideoSurfaceGstDelegate::QVideoSurfaceGstDelegate(QAbstractVideoSurface *surface) + : m_surface(surface) + , m_renderReturn(GST_FLOW_ERROR) + , m_bytesPerLine(0) +{ + m_supportedPixelFormats = m_surface->supportedPixelFormats(); + + connect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(supportedFormatsChanged())); +} + +QList<QVideoFrame::PixelFormat> QVideoSurfaceGstDelegate::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const +{ + QMutexLocker locker(const_cast<QMutex *>(&m_mutex)); + + if (handleType == QAbstractVideoBuffer::NoHandle) + return m_supportedPixelFormats; + else + return m_surface->supportedPixelFormats(handleType); +} + +QVideoSurfaceFormat QVideoSurfaceGstDelegate::surfaceFormat() const +{ + QMutexLocker locker(const_cast<QMutex *>(&m_mutex)); + return m_format; +} + +bool QVideoSurfaceGstDelegate::start(const QVideoSurfaceFormat &format, int bytesPerLine) +{ + QMutexLocker locker(&m_mutex); + + m_format = format; + m_bytesPerLine = bytesPerLine; + + if (QThread::currentThread() == thread()) { + m_started = !m_surface.isNull() ? m_surface->start(m_format) : false; + } else { + QMetaObject::invokeMethod(this, "queuedStart", Qt::QueuedConnection); + + m_setupCondition.wait(&m_mutex); + } + + m_format = m_surface->surfaceFormat(); + + return m_started; +} + +void QVideoSurfaceGstDelegate::stop() +{ + QMutexLocker locker(&m_mutex); + + if (QThread::currentThread() == thread()) { + if (!m_surface.isNull()) + m_surface->stop(); + } else { + QMetaObject::invokeMethod(this, "queuedStop", Qt::QueuedConnection); + + m_setupCondition.wait(&m_mutex); + } + + m_started = false; +} + +bool QVideoSurfaceGstDelegate::isActive() +{ + QMutexLocker locker(&m_mutex); + return m_surface->isActive(); +} + +GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer) +{ + QMutexLocker locker(&m_mutex); + + QGstVideoBuffer *videoBuffer = 0; + + if (G_TYPE_CHECK_INSTANCE_TYPE(buffer, QGstXvImageBuffer::get_type())) { + QGstXvImageBuffer *xvBuffer = reinterpret_cast<QGstXvImageBuffer *>(buffer); + QVariant handle = QVariant::fromValue(xvBuffer->xvImage); + videoBuffer = new QGstVideoBuffer(buffer, m_bytesPerLine, XvHandleType, handle); + } else + videoBuffer = new QGstVideoBuffer(buffer, m_bytesPerLine); + + m_frame = QVideoFrame( + videoBuffer, + m_format.frameSize(), + m_format.pixelFormat()); + + qint64 startTime = GST_BUFFER_TIMESTAMP(buffer); + + if (startTime >= 0) { + m_frame.setStartTime(startTime/G_GINT64_CONSTANT (1000000)); + + qint64 duration = GST_BUFFER_DURATION(buffer); + + if (duration >= 0) + m_frame.setEndTime((startTime + duration)/G_GINT64_CONSTANT (1000000)); + } + + QMetaObject::invokeMethod(this, "queuedRender", Qt::QueuedConnection); + + if (!m_renderCondition.wait(&m_mutex, 300)) { + m_frame = QVideoFrame(); + + return GST_FLOW_OK; + } else { + return m_renderReturn; + } +} + +void QVideoSurfaceGstDelegate::queuedStart() +{ + QMutexLocker locker(&m_mutex); + + m_started = m_surface->start(m_format); + + m_setupCondition.wakeAll(); +} + +void QVideoSurfaceGstDelegate::queuedStop() +{ + QMutexLocker locker(&m_mutex); + + m_surface->stop(); + + m_setupCondition.wakeAll(); +} + +void QVideoSurfaceGstDelegate::queuedRender() +{ + QMutexLocker locker(&m_mutex); + + if (m_surface.isNull()) { + m_renderReturn = GST_FLOW_ERROR; + } else if (m_surface->present(m_frame)) { + m_renderReturn = GST_FLOW_OK; + } else { + switch (m_surface->error()) { + case QAbstractVideoSurface::NoError: + m_renderReturn = GST_FLOW_OK; + break; + case QAbstractVideoSurface::StoppedError: + m_renderReturn = GST_FLOW_NOT_NEGOTIATED; + break; + default: + m_renderReturn = GST_FLOW_ERROR; + break; + } + } + + m_renderCondition.wakeAll(); +} + +void QVideoSurfaceGstDelegate::supportedFormatsChanged() +{ + QMutexLocker locker(&m_mutex); + + m_supportedPixelFormats = m_surface->supportedPixelFormats(); +} + +struct YuvFormat +{ + QVideoFrame::PixelFormat pixelFormat; + guint32 fourcc; + int bitsPerPixel; +}; + +static const YuvFormat qt_yuvColorLookup[] = +{ + { QVideoFrame::Format_YUV420P, GST_MAKE_FOURCC('I','4','2','0'), 8 }, + { QVideoFrame::Format_YV12, GST_MAKE_FOURCC('Y','V','1','2'), 8 }, + { QVideoFrame::Format_UYVY, GST_MAKE_FOURCC('U','Y','V','Y'), 16 }, + { QVideoFrame::Format_YUYV, GST_MAKE_FOURCC('Y','U','Y','2'), 16 }, + { QVideoFrame::Format_NV12, GST_MAKE_FOURCC('N','V','1','2'), 8 }, + { QVideoFrame::Format_NV21, GST_MAKE_FOURCC('N','V','2','1'), 8 }, + { QVideoFrame::Format_AYUV444, GST_MAKE_FOURCC('A','Y','U','V'), 32 } +}; + +static int indexOfYuvColor(QVideoFrame::PixelFormat format) +{ + const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat); + + for (int i = 0; i < count; ++i) + if (qt_yuvColorLookup[i].pixelFormat == format) + return i; + + return -1; +} + +static int indexOfYuvColor(guint32 fourcc) +{ + const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat); + + for (int i = 0; i < count; ++i) + if (qt_yuvColorLookup[i].fourcc == fourcc) + return i; + + return -1; +} + +struct RgbFormat +{ + QVideoFrame::PixelFormat pixelFormat; + int bitsPerPixel; + int depth; + int endianness; + int red; + int green; + int blue; + int alpha; +}; + +static const RgbFormat qt_rgbColorLookup[] = +{ + { QVideoFrame::Format_RGB32 , 32, 24, 4321, 0x0000FF00, 0x00FF0000, 0xFF000000, 0x00000000 }, + { QVideoFrame::Format_RGB32 , 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 }, + { QVideoFrame::Format_BGR32 , 32, 24, 4321, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x00000000 }, + { QVideoFrame::Format_BGR32 , 32, 24, 1234, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 }, + { QVideoFrame::Format_ARGB32, 32, 24, 4321, 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF }, + { QVideoFrame::Format_ARGB32, 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000 }, + { QVideoFrame::Format_RGB24 , 24, 24, 4321, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 }, + { QVideoFrame::Format_BGR24 , 24, 24, 4321, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 }, + { QVideoFrame::Format_RGB565, 16, 16, 1234, 0x0000F800, 0x000007E0, 0x0000001F, 0x00000000 } +}; + +static int indexOfRgbColor( + int bits, int depth, int endianness, int red, int green, int blue, int alpha) +{ + const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat); + + for (int i = 0; i < count; ++i) { + if (qt_rgbColorLookup[i].bitsPerPixel == bits + && qt_rgbColorLookup[i].depth == depth + && qt_rgbColorLookup[i].endianness == endianness + && qt_rgbColorLookup[i].red == red + && qt_rgbColorLookup[i].green == green + && qt_rgbColorLookup[i].blue == blue + && qt_rgbColorLookup[i].alpha == alpha) { + return i; + } + } + return -1; +} + +static GstVideoSinkClass *sink_parent_class; + +#define VO_SINK(s) QVideoSurfaceGstSink *sink(reinterpret_cast<QVideoSurfaceGstSink *>(s)) + +QVideoSurfaceGstSink *QVideoSurfaceGstSink::createSink(QAbstractVideoSurface *surface) +{ + QVideoSurfaceGstSink *sink = reinterpret_cast<QVideoSurfaceGstSink *>( + g_object_new(QVideoSurfaceGstSink::get_type(), 0)); + + sink->delegate = new QVideoSurfaceGstDelegate(surface); + + return sink; +} + +GType QVideoSurfaceGstSink::get_type() +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = + { + sizeof(QVideoSurfaceGstSinkClass), // class_size + base_init, // base_init + NULL, // base_finalize + class_init, // class_init + NULL, // class_finalize + NULL, // class_data + sizeof(QVideoSurfaceGstSink), // instance_size + 0, // n_preallocs + instance_init, // instance_init + 0 // value_table + }; + + type = g_type_register_static( + GST_TYPE_VIDEO_SINK, "QVideoSurfaceGstSink", &info, GTypeFlags(0)); + } + + return type; +} + +void QVideoSurfaceGstSink::class_init(gpointer g_class, gpointer class_data) +{ + Q_UNUSED(class_data); + + sink_parent_class = reinterpret_cast<GstVideoSinkClass *>(g_type_class_peek_parent(g_class)); + + GstBaseSinkClass *base_sink_class = reinterpret_cast<GstBaseSinkClass *>(g_class); + base_sink_class->get_caps = QVideoSurfaceGstSink::get_caps; + base_sink_class->set_caps = QVideoSurfaceGstSink::set_caps; + base_sink_class->buffer_alloc = QVideoSurfaceGstSink::buffer_alloc; + base_sink_class->start = QVideoSurfaceGstSink::start; + base_sink_class->stop = QVideoSurfaceGstSink::stop; + // base_sink_class->unlock = QVideoSurfaceGstSink::unlock; // Not implemented. + // base_sink_class->event = QVideoSurfaceGstSink::event; // Not implemented. + base_sink_class->preroll = QVideoSurfaceGstSink::preroll; + base_sink_class->render = QVideoSurfaceGstSink::render; + + GstElementClass *element_class = reinterpret_cast<GstElementClass *>(g_class); + element_class->change_state = QVideoSurfaceGstSink::change_state; + + GObjectClass *object_class = reinterpret_cast<GObjectClass *>(g_class); + object_class->finalize = QVideoSurfaceGstSink::finalize; +} + +void QVideoSurfaceGstSink::base_init(gpointer g_class) +{ + static GstStaticPadTemplate sink_pad_template = GST_STATIC_PAD_TEMPLATE( + "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS( + "video/x-raw-rgb, " + "framerate = (fraction) [ 0, MAX ], " + "width = (int) [ 1, MAX ], " + "height = (int) [ 1, MAX ]; " + "video/x-raw-yuv, " + "framerate = (fraction) [ 0, MAX ], " + "width = (int) [ 1, MAX ], " + "height = (int) [ 1, MAX ]")); + + gst_element_class_add_pad_template( + GST_ELEMENT_CLASS(g_class), gst_static_pad_template_get(&sink_pad_template)); +} + +void QVideoSurfaceGstSink::instance_init(GTypeInstance *instance, gpointer g_class) +{ + VO_SINK(instance); + + Q_UNUSED(g_class); + + sink->delegate = 0; + sink->pool = new QGstXvImageBufferPool(); + sink->lastRequestedCaps = 0; + sink->lastBufferCaps = 0; + sink->lastSurfaceFormat = new QVideoSurfaceFormat; +} + +void QVideoSurfaceGstSink::finalize(GObject *object) +{ + VO_SINK(object); + delete sink->pool; + sink->pool = 0; + delete sink->lastSurfaceFormat; + sink->lastSurfaceFormat = 0; + + if (sink->lastBufferCaps) + gst_caps_unref(sink->lastBufferCaps); + sink->lastBufferCaps = 0; + + if (sink->lastRequestedCaps) + gst_caps_unref(sink->lastRequestedCaps); + sink->lastRequestedCaps = 0; +} + +GstStateChangeReturn QVideoSurfaceGstSink::change_state( + GstElement *element, GstStateChange transition) +{ + Q_UNUSED(element); + + return GST_ELEMENT_CLASS(sink_parent_class)->change_state( + element, transition); +} + +GstCaps *QVideoSurfaceGstSink::get_caps(GstBaseSink *base) +{ + VO_SINK(base); + + GstCaps *caps = gst_caps_new_empty(); + + foreach (QVideoFrame::PixelFormat format, sink->delegate->supportedPixelFormats()) { + int index = indexOfYuvColor(format); + + if (index != -1) { + gst_caps_append_structure(caps, gst_structure_new( + "video/x-raw-yuv", + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1, + "width" , GST_TYPE_INT_RANGE, 1, INT_MAX, + "height" , GST_TYPE_INT_RANGE, 1, INT_MAX, + "format" , GST_TYPE_FOURCC, qt_yuvColorLookup[index].fourcc, + NULL)); + continue; + } + + const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat); + + for (int i = 0; i < count; ++i) { + if (qt_rgbColorLookup[i].pixelFormat == format) { + GstStructure *structure = gst_structure_new( + "video/x-raw-rgb", + "framerate" , GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1, + "width" , GST_TYPE_INT_RANGE, 1, INT_MAX, + "height" , GST_TYPE_INT_RANGE, 1, INT_MAX, + "bpp" , G_TYPE_INT, qt_rgbColorLookup[i].bitsPerPixel, + "depth" , G_TYPE_INT, qt_rgbColorLookup[i].depth, + "endianness", G_TYPE_INT, qt_rgbColorLookup[i].endianness, + "red_mask" , G_TYPE_INT, qt_rgbColorLookup[i].red, + "green_mask", G_TYPE_INT, qt_rgbColorLookup[i].green, + "blue_mask" , G_TYPE_INT, qt_rgbColorLookup[i].blue, + NULL); + + if (qt_rgbColorLookup[i].alpha != 0) { + gst_structure_set( + structure, "alpha_mask", G_TYPE_INT, qt_rgbColorLookup[i].alpha, NULL); + } + gst_caps_append_structure(caps, structure); + } + } + } + + return caps; +} + +gboolean QVideoSurfaceGstSink::set_caps(GstBaseSink *base, GstCaps *caps) +{ + VO_SINK(base); + + //qDebug() << "set_caps"; + //qDebug() << gst_caps_to_string(caps); + + if (!caps) { + sink->delegate->stop(); + + return TRUE; + } else { + int bytesPerLine = 0; + QVideoSurfaceFormat format = formatForCaps(caps, &bytesPerLine); + + if (sink->delegate->isActive()) { + QVideoSurfaceFormat surfaceFormst = sink->delegate->surfaceFormat(); + + if (format.pixelFormat() == surfaceFormst.pixelFormat() && + format.frameSize() == surfaceFormst.frameSize()) + return TRUE; + else + sink->delegate->stop(); + } + + if (sink->lastRequestedCaps) + gst_caps_unref(sink->lastRequestedCaps); + sink->lastRequestedCaps = 0; + + //qDebug() << "Staring video surface:"; + //qDebug() << format; + //qDebug() << bytesPerLine; + + if (sink->delegate->start(format, bytesPerLine)) + return TRUE; + + } + + return FALSE; +} + +QVideoSurfaceFormat QVideoSurfaceGstSink::formatForCaps(GstCaps *caps, int *bytesPerLine) +{ + const GstStructure *structure = gst_caps_get_structure(caps, 0); + + QVideoFrame::PixelFormat pixelFormat = QVideoFrame::Format_Invalid; + int bitsPerPixel = 0; + + QSize size; + gst_structure_get_int(structure, "width", &size.rwidth()); + gst_structure_get_int(structure, "height", &size.rheight()); + + if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) { + guint32 fourcc = 0; + gst_structure_get_fourcc(structure, "format", &fourcc); + + int index = indexOfYuvColor(fourcc); + if (index != -1) { + pixelFormat = qt_yuvColorLookup[index].pixelFormat; + bitsPerPixel = qt_yuvColorLookup[index].bitsPerPixel; + } + } else if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) { + int depth = 0; + int endianness = 0; + int red = 0; + int green = 0; + int blue = 0; + int alpha = 0; + + gst_structure_get_int(structure, "bpp", &bitsPerPixel); + gst_structure_get_int(structure, "depth", &depth); + gst_structure_get_int(structure, "endianness", &endianness); + gst_structure_get_int(structure, "red_mask", &red); + gst_structure_get_int(structure, "green_mask", &green); + gst_structure_get_int(structure, "blue_mask", &blue); + gst_structure_get_int(structure, "alpha_mask", &alpha); + + int index = indexOfRgbColor(bitsPerPixel, depth, endianness, red, green, blue, alpha); + + if (index != -1) + pixelFormat = qt_rgbColorLookup[index].pixelFormat; + } + + if (pixelFormat != QVideoFrame::Format_Invalid) { + QVideoSurfaceFormat format(size, pixelFormat); + + QPair<int, int> rate; + gst_structure_get_fraction(structure, "framerate", &rate.first, &rate.second); + + if (rate.second) + format.setFrameRate(qreal(rate.first)/rate.second); + + gint aspectNum = 0; + gint aspectDenum = 0; + if (gst_structure_get_fraction( + structure, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { + if (aspectDenum > 0) + format.setPixelAspectRatio(aspectNum, aspectDenum); + } + + if (bytesPerLine) + *bytesPerLine = ((size.width() * bitsPerPixel / 8) + 3) & ~3; + + return format; + } + + return QVideoSurfaceFormat(); +} + + +GstFlowReturn QVideoSurfaceGstSink::buffer_alloc( + GstBaseSink *base, guint64 offset, guint size, GstCaps *caps, GstBuffer **buffer) +{ + VO_SINK(base); + + Q_UNUSED(offset); + Q_UNUSED(size); + + *buffer = 0; + + if (sink->lastRequestedCaps && gst_caps_is_equal(sink->lastRequestedCaps, caps)) { + //qDebug() << "reusing last caps"; + *buffer = GST_BUFFER(sink->pool->takeBuffer(*sink->lastSurfaceFormat, sink->lastBufferCaps)); + return GST_FLOW_OK; + } + + if (sink->delegate->supportedPixelFormats(XvHandleType).isEmpty()) { + //qDebug() << "sink doesn't support Xv buffers, skip buffers allocation"; + return GST_FLOW_OK; + } + + GstCaps *intersection = gst_caps_intersect(get_caps(GST_BASE_SINK(sink)), caps); + + if (gst_caps_is_empty (intersection)) { + gst_caps_unref(intersection); + return GST_FLOW_NOT_NEGOTIATED; + } + + if (sink->delegate->isActive()) { + //if format was changed, restart the surface + QVideoSurfaceFormat format = formatForCaps(intersection); + QVideoSurfaceFormat surfaceFormat = sink->delegate->surfaceFormat(); + + if (format.pixelFormat() != surfaceFormat.pixelFormat() || + format.frameSize() != surfaceFormat.frameSize()) { + //qDebug() << "new format requested, restart video surface"; + sink->delegate->stop(); + } + } + + if (!sink->delegate->isActive()) { + int bytesPerLine = 0; + QVideoSurfaceFormat format = formatForCaps(intersection, &bytesPerLine); + + if (!sink->delegate->start(format, bytesPerLine)) { + qDebug() << "failed to start video surface"; + return GST_FLOW_NOT_NEGOTIATED; + } + } + + QVideoSurfaceFormat surfaceFormat = sink->delegate->surfaceFormat(); + + if (!sink->pool->isFormatSupported(surfaceFormat)) { + //qDebug() << "sink doesn't provide Xv buffer details, skip buffers allocation"; + return GST_FLOW_OK; + } + + if (sink->lastRequestedCaps) + gst_caps_unref(sink->lastRequestedCaps); + sink->lastRequestedCaps = caps; + gst_caps_ref(sink->lastRequestedCaps); + + if (sink->lastBufferCaps) + gst_caps_unref(sink->lastBufferCaps); + sink->lastBufferCaps = intersection; + gst_caps_ref(sink->lastBufferCaps); + + *sink->lastSurfaceFormat = surfaceFormat; + + *buffer = GST_BUFFER(sink->pool->takeBuffer(surfaceFormat, intersection)); + + return GST_FLOW_OK; +} + +gboolean QVideoSurfaceGstSink::start(GstBaseSink *base) +{ + Q_UNUSED(base); + + return TRUE; +} + +gboolean QVideoSurfaceGstSink::stop(GstBaseSink *base) +{ + Q_UNUSED(base); + + return TRUE; +} + +gboolean QVideoSurfaceGstSink::unlock(GstBaseSink *base) +{ + Q_UNUSED(base); + + return TRUE; +} + +gboolean QVideoSurfaceGstSink::event(GstBaseSink *base, GstEvent *event) +{ + Q_UNUSED(base); + Q_UNUSED(event); + + return TRUE; +} + +GstFlowReturn QVideoSurfaceGstSink::preroll(GstBaseSink *base, GstBuffer *buffer) +{ + VO_SINK(base); + + return sink->delegate->render(buffer); +} + +GstFlowReturn QVideoSurfaceGstSink::render(GstBaseSink *base, GstBuffer *buffer) +{ + VO_SINK(base); + return sink->delegate->render(buffer); +} + diff --git a/src/plugins/mediaservices/gstreamer/qvideosurfacegstsink.h b/src/plugins/mediaservices/gstreamer/qvideosurfacegstsink.h new file mode 100644 index 0000000..f59a43c --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qvideosurfacegstsink.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef VIDEOSURFACEGSTSINK_H +#define VIDEOSURFACEGSTSINK_H + +#include <gst/video/gstvideosink.h> + +#include <QtCore/qlist.h> +#include <QtCore/qmutex.h> +#include <QtCore/qpointer.h> +#include <QtCore/qwaitcondition.h> +#include <QtMultimedia/qvideosurfaceformat.h> +#include <QtMultimedia/qvideoframe.h> +#include <QtMultimedia/qabstractvideobuffer.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QAbstractVideoSurface; + +class QGstXvImageBuffer; +class QGstXvImageBufferPool; + + +class QVideoSurfaceGstDelegate : public QObject +{ + Q_OBJECT +public: + QVideoSurfaceGstDelegate(QAbstractVideoSurface *surface); + + QList<QVideoFrame::PixelFormat> supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const; + + QVideoSurfaceFormat surfaceFormat() const; + + + bool start(const QVideoSurfaceFormat &format, int bytesPerLine); + void stop(); + + bool isActive(); + + GstFlowReturn render(GstBuffer *buffer); + +private slots: + void queuedStart(); + void queuedStop(); + void queuedRender(); + + void supportedFormatsChanged(); + +private: + QPointer<QAbstractVideoSurface> m_surface; + QList<QVideoFrame::PixelFormat> m_supportedPixelFormats; + QMutex m_mutex; + QWaitCondition m_setupCondition; + QWaitCondition m_renderCondition; + QVideoSurfaceFormat m_format; + QVideoFrame m_frame; + GstFlowReturn m_renderReturn; + int m_bytesPerLine; + bool m_started; +}; + +class QVideoSurfaceGstSink +{ +public: + GstVideoSink parent; + + static QVideoSurfaceGstSink *createSink(QAbstractVideoSurface *surface); + static QVideoSurfaceFormat formatForCaps(GstCaps *caps, int *bytesPerLine = 0); + +private: + static GType get_type(); + static void class_init(gpointer g_class, gpointer class_data); + static void base_init(gpointer g_class); + static void instance_init(GTypeInstance *instance, gpointer g_class); + + static void finalize(GObject *object); + + static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition); + + static GstCaps *get_caps(GstBaseSink *sink); + static gboolean set_caps(GstBaseSink *sink, GstCaps *caps); + + static GstFlowReturn buffer_alloc( + GstBaseSink *sink, guint64 offset, guint size, GstCaps *caps, GstBuffer **buffer); + + static gboolean start(GstBaseSink *sink); + static gboolean stop(GstBaseSink *sink); + + static gboolean unlock(GstBaseSink *sink); + + static gboolean event(GstBaseSink *sink, GstEvent *event); + static GstFlowReturn preroll(GstBaseSink *sink, GstBuffer *buffer); + static GstFlowReturn render(GstBaseSink *sink, GstBuffer *buffer); + +private: + QVideoSurfaceGstDelegate *delegate; + QGstXvImageBufferPool *pool; + GstCaps *lastRequestedCaps; + GstCaps *lastBufferCaps; + QVideoSurfaceFormat *lastSurfaceFormat; +}; + + +class QVideoSurfaceGstSinkClass +{ +public: + GstVideoSinkClass parent_class; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/qx11videosurface.cpp b/src/plugins/mediaservices/gstreamer/qx11videosurface.cpp new file mode 100644 index 0000000..6e282ff --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qx11videosurface.cpp @@ -0,0 +1,509 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qvariant.h> +#include <QtCore/qdebug.h> +#include <QtGui/qx11info_x11.h> +#include <QtMultimedia/qvideosurfaceformat.h> + +#include "qx11videosurface.h" + +Q_DECLARE_METATYPE(XvImage*); + +static QAbstractVideoBuffer::HandleType XvHandleType = QAbstractVideoBuffer::HandleType(4); + +struct XvFormatRgb +{ + QVideoFrame::PixelFormat pixelFormat; + int bits_per_pixel; + int format; + int num_planes; + + int depth; + unsigned int red_mask; + unsigned int green_mask; + unsigned int blue_mask; + +}; + +bool operator ==(const XvImageFormatValues &format, const XvFormatRgb &rgb) +{ + return format.type == XvRGB + && format.bits_per_pixel == rgb.bits_per_pixel + && format.format == rgb.format + && format.num_planes == rgb.num_planes + && format.depth == rgb.depth + && format.red_mask == rgb.red_mask + && format.blue_mask == rgb.blue_mask; +} + +static const XvFormatRgb qt_xvRgbLookup[] = +{ + { QVideoFrame::Format_ARGB32, 32, XvPacked, 1, 32, 0x00FF0000, 0x0000FF00, 0x000000FF }, + { QVideoFrame::Format_RGB32 , 32, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF }, + { QVideoFrame::Format_RGB24 , 24, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF }, + { QVideoFrame::Format_RGB565, 16, XvPacked, 1, 16, 0x0000F800, 0x000007E0, 0x0000001F }, + { QVideoFrame::Format_BGRA32, 32, XvPacked, 1, 32, 0xFF000000, 0x00FF0000, 0x0000FF00 }, + { QVideoFrame::Format_BGR32 , 32, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF }, + { QVideoFrame::Format_BGR24 , 24, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF }, + { QVideoFrame::Format_BGR565, 16, XvPacked, 1, 16, 0x0000F800, 0x000007E0, 0x0000001F } +}; + +struct XvFormatYuv +{ + QVideoFrame::PixelFormat pixelFormat; + int bits_per_pixel; + int format; + int num_planes; + + unsigned int y_sample_bits; + unsigned int u_sample_bits; + unsigned int v_sample_bits; + unsigned int horz_y_period; + unsigned int horz_u_period; + unsigned int horz_v_period; + unsigned int vert_y_period; + unsigned int vert_u_period; + unsigned int vert_v_period; + char component_order[32]; +}; + +bool operator ==(const XvImageFormatValues &format, const XvFormatYuv &yuv) +{ + return format.type == XvYUV + && format.bits_per_pixel == yuv.bits_per_pixel + && format.format == yuv.format + && format.num_planes == yuv.num_planes + && format.y_sample_bits == yuv.y_sample_bits + && format.u_sample_bits == yuv.u_sample_bits + && format.v_sample_bits == yuv.v_sample_bits + && format.horz_y_period == yuv.horz_y_period + && format.horz_u_period == yuv.horz_u_period + && format.horz_v_period == yuv.horz_v_period + && format.horz_y_period == yuv.vert_y_period + && format.vert_u_period == yuv.vert_u_period + && format.vert_v_period == yuv.vert_v_period + && qstrncmp(format.component_order, yuv.component_order, 32) == 0; +} + +static const XvFormatYuv qt_xvYuvLookup[] = +{ + { QVideoFrame::Format_YUV444 , 24, XvPacked, 1, 8, 8, 8, 1, 1, 1, 1, 1, 1, "YUV" }, + { QVideoFrame::Format_YUV420P, 12, XvPlanar, 3, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YUV" }, + { QVideoFrame::Format_YV12 , 12, XvPlanar, 3, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YVU" }, + { QVideoFrame::Format_UYVY , 16, XvPacked, 1, 8, 8, 8, 1, 2, 2, 1, 1, 1, "UYVY" }, + { QVideoFrame::Format_YUYV , 16, XvPacked, 1, 8, 8, 8, 1, 2, 2, 1, 1, 1, "YUY2" }, + { QVideoFrame::Format_YUYV , 16, XvPacked, 1, 8, 8, 8, 1, 2, 2, 1, 1, 1, "YUYV" }, + { QVideoFrame::Format_NV12 , 12, XvPlanar, 2, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YUV" }, + { QVideoFrame::Format_NV12 , 12, XvPlanar, 2, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YVU" }, + { QVideoFrame::Format_Y8 , 8 , XvPlanar, 1, 8, 0, 0, 1, 0, 0, 1, 0, 0, "Y" } +}; + +QX11VideoSurface::QX11VideoSurface(QObject *parent) + : QAbstractVideoSurface(parent) + , m_winId(0) + , m_portId(0) + , m_gc(0) + , m_image(0) +{ +} + +QX11VideoSurface::~QX11VideoSurface() +{ + if (m_gc) + XFreeGC(QX11Info::display(), m_gc); + + if (m_portId != 0) + XvUngrabPort(QX11Info::display(), m_portId, 0); +} + +WId QX11VideoSurface::winId() const +{ + return m_winId; +} + +void QX11VideoSurface::setWinId(WId id) +{ + if (id == m_winId) + return; + + if (m_image) + XFree(m_image); + + if (m_gc) { + XFreeGC(QX11Info::display(), m_gc); + m_gc = 0; + } + + if (m_portId != 0) + XvUngrabPort(QX11Info::display(), m_portId, 0); + + m_supportedPixelFormats.clear(); + m_formatIds.clear(); + + m_winId = id; + + if (m_winId && findPort()) { + querySupportedFormats(); + + m_gc = XCreateGC(QX11Info::display(), m_winId, 0, 0); + + if (m_image) { + m_image = 0; + + if (!start(surfaceFormat())) + QAbstractVideoSurface::stop(); + } + } else if (m_image) { + m_image = 0; + + QAbstractVideoSurface::stop(); + } + + emit supportedFormatsChanged(); +} + +QRect QX11VideoSurface::displayRect() const +{ + return m_displayRect; +} + +void QX11VideoSurface::setDisplayRect(const QRect &rect) +{ + m_displayRect = rect; +} + +int QX11VideoSurface::brightness() const +{ + return getAttribute("XV_BRIGHTNESS", m_brightnessRange.first, m_brightnessRange.second); +} + +void QX11VideoSurface::setBrightness(int brightness) +{ + setAttribute("XV_BRIGHTNESS", brightness, m_brightnessRange.first, m_brightnessRange.second); +} + +int QX11VideoSurface::contrast() const +{ + return getAttribute("XV_CONTRAST", m_contrastRange.first, m_contrastRange.second); +} + +void QX11VideoSurface::setContrast(int contrast) +{ + setAttribute("XV_CONTRAST", contrast, m_contrastRange.first, m_contrastRange.second); +} + +int QX11VideoSurface::hue() const +{ + return getAttribute("XV_HUE", m_hueRange.first, m_hueRange.second); +} + +void QX11VideoSurface::setHue(int hue) +{ + setAttribute("XV_HUE", hue, m_hueRange.first, m_hueRange.second); +} + +int QX11VideoSurface::saturation() const +{ + return getAttribute("XV_SATURATION", m_saturationRange.first, m_saturationRange.second); +} + +void QX11VideoSurface::setSaturation(int saturation) +{ + setAttribute("XV_SATURATION", saturation, m_saturationRange.first, m_saturationRange.second); +} + +int QX11VideoSurface::getAttribute(const char *attribute, int minimum, int maximum) const +{ + if (m_portId != 0) { + Display *display = QX11Info::display(); + + Atom atom = XInternAtom(display, attribute, True); + + int value = 0; + + XvGetPortAttribute(display, m_portId, atom, &value); + + return redistribute(value, minimum, maximum, -100, 100); + } else { + return 0; + } +} + +void QX11VideoSurface::setAttribute(const char *attribute, int value, int minimum, int maximum) +{ + if (m_portId != 0) { + Display *display = QX11Info::display(); + + Atom atom = XInternAtom(display, attribute, True); + + XvSetPortAttribute( + display, m_portId, atom, redistribute(value, -100, 100, minimum, maximum)); + } +} + +int QX11VideoSurface::redistribute( + int value, int fromLower, int fromUpper, int toLower, int toUpper) +{ + return fromUpper != fromLower + ? ((value - fromLower) * (toUpper - toLower) / (fromUpper - fromLower)) + toLower + : 0; +} + +QList<QVideoFrame::PixelFormat> QX11VideoSurface::supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType) const +{ + return handleType == QAbstractVideoBuffer::NoHandle || handleType == XvHandleType + ? m_supportedPixelFormats + : QList<QVideoFrame::PixelFormat>(); +} + +bool QX11VideoSurface::start(const QVideoSurfaceFormat &format) +{ + if (m_image) + XFree(m_image); + + int xvFormatId = 0; + for (int i = 0; i < m_supportedPixelFormats.count(); ++i) { + if (m_supportedPixelFormats.at(i) == format.pixelFormat()) { + xvFormatId = m_formatIds.at(i); + break; + } + } + + if (xvFormatId == 0) { + setError(UnsupportedFormatError); + } else { + XvImage *image = XvCreateImage( + QX11Info::display(), + m_portId, + xvFormatId, + 0, + format.frameWidth(), + format.frameHeight()); + + if (!image) { + setError(ResourceError); + } else { + m_viewport = format.viewport(); + m_image = image; + + QVideoSurfaceFormat newFormat = format; + newFormat.setProperty("portId", QVariant(quint64(m_portId))); + newFormat.setProperty("xvFormatId", xvFormatId); + newFormat.setProperty("dataSize", image->data_size); + + return QAbstractVideoSurface::start(newFormat); + } + } + + if (m_image) { + m_image = 0; + + QAbstractVideoSurface::stop(); + } + + return false; +} + +void QX11VideoSurface::stop() +{ + if (m_image) { + XFree(m_image); + m_image = 0; + + QAbstractVideoSurface::stop(); + } +} + +bool QX11VideoSurface::present(const QVideoFrame &frame) +{ + if (!m_image) { + setError(StoppedError); + return false; + } else if (m_image->width != frame.width() || m_image->height != frame.height()) { + setError(IncorrectFormatError); + return false; + } else { + QVideoFrame frameCopy(frame); + + if (!frameCopy.map(QAbstractVideoBuffer::ReadOnly)) { + setError(IncorrectFormatError); + return false; + } else { + bool presented = false; + + if (frame.handleType() != XvHandleType && + m_image->data_size > frame.mappedBytes()) { + qWarning("Insufficient frame buffer size"); + setError(IncorrectFormatError); + } else if (frame.handleType() != XvHandleType && + m_image->num_planes > 0 && + m_image->pitches[0] != frame.bytesPerLine()) { + qWarning("Incompatible frame pitches"); + setError(IncorrectFormatError); + } else { + if (frame.handleType() != XvHandleType) { + m_image->data = reinterpret_cast<char *>(frameCopy.bits()); + + //qDebug() << "copy frame"; + XvPutImage( + QX11Info::display(), + m_portId, + m_winId, + m_gc, + m_image, + m_viewport.x(), + m_viewport.y(), + m_viewport.width(), + m_viewport.height(), + m_displayRect.x(), + m_displayRect.y(), + m_displayRect.width(), + m_displayRect.height()); + + m_image->data = 0; + } else { + XvImage *img = frame.handle().value<XvImage*>(); + + //qDebug() << "render directly"; + if (img) + XvShmPutImage( + QX11Info::display(), + m_portId, + m_winId, + m_gc, + img, + m_viewport.x(), + m_viewport.y(), + m_viewport.width(), + m_viewport.height(), + m_displayRect.x(), + m_displayRect.y(), + m_displayRect.width(), + m_displayRect.height(), + false); + } + + presented = true; + } + + frameCopy.unmap(); + + return presented; + } + } +} + +bool QX11VideoSurface::findPort() +{ + unsigned int count = 0; + XvAdaptorInfo *adaptors = 0; + bool portFound = false; + + if (XvQueryAdaptors(QX11Info::display(), m_winId, &count, &adaptors) == Success) { + for (unsigned int i = 0; i < count && !portFound; ++i) { + if (adaptors[i].type & XvImageMask) { + m_portId = adaptors[i].base_id; + + for (unsigned int j = 0; j < adaptors[i].num_ports && !portFound; ++j, ++m_portId) + portFound = XvGrabPort(QX11Info::display(), m_portId, 0) == Success; + } + } + XvFreeAdaptorInfo(adaptors); + } + + return portFound; +} + +void QX11VideoSurface::querySupportedFormats() +{ + int count = 0; + if (XvImageFormatValues *imageFormats = XvListImageFormats( + QX11Info::display(), m_portId, &count)) { + const int rgbCount = sizeof(qt_xvRgbLookup) / sizeof(XvFormatRgb); + const int yuvCount = sizeof(qt_xvYuvLookup) / sizeof(XvFormatYuv); + + for (int i = 0; i < count; ++i) { + switch (imageFormats[i].type) { + case XvRGB: + for (int j = 0; j < rgbCount; ++j) { + if (imageFormats[i] == qt_xvRgbLookup[j]) { + m_supportedPixelFormats.append(qt_xvRgbLookup[j].pixelFormat); + m_formatIds.append(imageFormats[i].id); + break; + } + } + break; + case XvYUV: + for (int j = 0; j < yuvCount; ++j) { + if (imageFormats[i] == qt_xvYuvLookup[j]) { + m_supportedPixelFormats.append(qt_xvYuvLookup[j].pixelFormat); + m_formatIds.append(imageFormats[i].id); + break; + } + } + break; + } + } + XFree(imageFormats); + } + + m_brightnessRange = qMakePair(0, 0); + m_contrastRange = qMakePair(0, 0); + m_hueRange = qMakePair(0, 0); + m_saturationRange = qMakePair(0, 0); + + if (XvAttribute *attributes = XvQueryPortAttributes(QX11Info::display(), m_portId, &count)) { + for (int i = 0; i < count; ++i) { + if (qstrcmp(attributes[i].name, "XV_BRIGHTNESS") == 0) + m_brightnessRange = qMakePair(attributes[i].min_value, attributes[i].max_value); + else if (qstrcmp(attributes[i].name, "XV_CONTRAST") == 0) + m_contrastRange = qMakePair(attributes[i].min_value, attributes[i].max_value); + else if (qstrcmp(attributes[i].name, "XV_HUE") == 0) + m_hueRange = qMakePair(attributes[i].min_value, attributes[i].max_value); + else if (qstrcmp(attributes[i].name, "XV_SATURATION") == 0) + m_saturationRange = qMakePair(attributes[i].min_value, attributes[i].max_value); + } + + XFree(attributes); + } +} diff --git a/src/plugins/mediaservices/gstreamer/qx11videosurface.h b/src/plugins/mediaservices/gstreamer/qx11videosurface.h new file mode 100644 index 0000000..1be963e --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qx11videosurface.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QX11VIDEOSURFACE_H +#define QX11VIDEOSURFACE_H + +#include <QtGui/qwidget.h> +#include <QtMultimedia/qabstractvideosurface.h> + +#include <X11/Xlib.h> +#include <X11/extensions/Xv.h> +#include <X11/extensions/Xvlib.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QX11VideoSurface : public QAbstractVideoSurface +{ + Q_OBJECT +public: + QX11VideoSurface(QObject *parent = 0); + ~QX11VideoSurface(); + + WId winId() const; + void setWinId(WId id); + + QRect displayRect() const; + void setDisplayRect(const QRect &rect); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + + QList<QVideoFrame::PixelFormat> supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const; + + bool start(const QVideoSurfaceFormat &format); + void stop(); + + bool present(const QVideoFrame &frame); + +private: + WId m_winId; + XvPortID m_portId; + GC m_gc; + XvImage *m_image; + QList<QVideoFrame::PixelFormat> m_supportedPixelFormats; + QVector<int> m_formatIds; + QRect m_viewport; + QRect m_displayRect; + QPair<int, int> m_brightnessRange; + QPair<int, int> m_contrastRange; + QPair<int, int> m_hueRange; + QPair<int, int> m_saturationRange; + + bool findPort(); + void querySupportedFormats(); + + int getAttribute(const char *attribute, int minimum, int maximum) const; + void setAttribute(const char *attribute, int value, int minimum, int maximum); + + static int redistribute(int value, int fromLower, int fromUpper, int toLower, int toUpper); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/mediaservices.pro b/src/plugins/mediaservices/mediaservices.pro new file mode 100644 index 0000000..55e6aba --- /dev/null +++ b/src/plugins/mediaservices/mediaservices.pro @@ -0,0 +1,18 @@ +TEMPLATE = subdirs + +win32:!wince: SUBDIRS += directshow + +mac: SUBDIRS += qt7 + +unix:!mac:!symbian { + TMP_GST_LIBS = \ + gstreamer-0.10 >= 0.10.19 \ + gstreamer-base-0.10 >= 0.10.19 \ + gstreamer-interfaces-0.10 >= 0.10.19 \ + gstreamer-audio-0.10 >= 0.10.19 \ + gstreamer-video-0.10 >= 0.10.19 + + system(pkg-config --exists \'$${TMP_GST_LIBS}\' --print-errors): { + SUBDIRS += gstreamer + } +} diff --git a/src/plugins/mediaservices/qt7/mediaplayer/mediaplayer.pri b/src/plugins/mediaservices/qt7/mediaplayer/mediaplayer.pri new file mode 100644 index 0000000..577209e --- /dev/null +++ b/src/plugins/mediaservices/qt7/mediaplayer/mediaplayer.pri @@ -0,0 +1,18 @@ +INCLUDEPATH += $$PWD + +DEFINES += QMEDIA_QT7_PLAYER + +HEADERS += \ + $$PWD/qt7playercontrol.h \ + $$PWD/qt7playermetadata.h \ + $$PWD/qt7playerservice.h \ + $$PWD/qt7playersession.h + +OBJECTIVE_SOURCES += \ + $$PWD/qt7playercontrol.mm \ + $$PWD/qt7playermetadata.mm \ + $$PWD/qt7playerservice.mm \ + $$PWD/qt7playersession.mm + + + diff --git a/src/plugins/mediaservices/qt7/mediaplayer/qt7playercontrol.h b/src/plugins/mediaservices/qt7/mediaplayer/qt7playercontrol.h new file mode 100644 index 0000000..907d13d --- /dev/null +++ b/src/plugins/mediaservices/qt7/mediaplayer/qt7playercontrol.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7PLAYERCONTROL_H +#define QT7PLAYERCONTROL_H + +#include <QtCore/qobject.h> +#include <QtGui/qmacdefines_mac.h> + +#include <QtMultimedia/qmediaplayercontrol.h> +#include <QtMultimedia/qmediaplayer.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QT7PlayerSession; +class QT7PlayerService; +class QMediaPlaylist; +class QMediaPlaylistNavigator; + +class QT7PlayerControl : public QMediaPlayerControl +{ +Q_OBJECT +public: + QT7PlayerControl(QObject *parent = 0); + ~QT7PlayerControl(); + + void setSession(QT7PlayerSession *session); + + QMediaPlayer::State state() const; + QMediaPlayer::MediaStatus mediaStatus() const; + + QMediaContent media() const; + const QIODevice *mediaStream() const; + void setMedia(const QMediaContent &content, QIODevice *stream); + + qint64 position() const; + qint64 duration() const; + + int bufferStatus() const; + + int volume() const; + bool isMuted() const; + + bool isAudioAvailable() const; + bool isVideoAvailable() const; + + bool isSeekable() const; + + QMediaTimeRange availablePlaybackRanges() const; + + qreal playbackRate() const; + void setPlaybackRate(qreal rate); + +public Q_SLOTS: + void setPosition(qint64 pos); + + void play(); + void pause(); + void stop(); + + void setVolume(int volume); + void setMuted(bool muted); + +Q_SIGNALS: + void mediaChanged(const QMediaContent& content); + void durationChanged(qint64 duration); + void positionChanged(qint64 position); + void stateChanged(QMediaPlayer::State newState); + void mediaStatusChanged(QMediaPlayer::MediaStatus status); + void volumeChanged(int volume); + void mutedChanged(bool muted); + void videoAvailableChanged(bool videoAvailable); + void bufferStatusChanged(int percentFilled); + void seekableChanged(bool); + void seekRangeChanged(const QPair<qint64,qint64>&); + void playbackRateChanged(qreal rate); + void error(int error, const QString &errorString); + +private: + QT7PlayerSession *m_session; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/qt7/mediaplayer/qt7playercontrol.mm b/src/plugins/mediaservices/qt7/mediaplayer/qt7playercontrol.mm new file mode 100644 index 0000000..0f4ac41 --- /dev/null +++ b/src/plugins/mediaservices/qt7/mediaplayer/qt7playercontrol.mm @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qt7playercontrol.h" +#include "qt7playersession.h" + +#include <QtMultimedia/qmediaplaylistnavigator.h> + +#include <QtCore/qurl.h> +#include <QtCore/qdebug.h> + + +QT_BEGIN_NAMESPACE + +QT7PlayerControl::QT7PlayerControl(QObject *parent) + : QMediaPlayerControl(parent) +{ +} + +QT7PlayerControl::~QT7PlayerControl() +{ +} + +void QT7PlayerControl::setSession(QT7PlayerSession *session) +{ + m_session = session; + + connect(m_session, SIGNAL(positionChanged(qint64)), this, SIGNAL(positionChanged(qint64))); + connect(m_session, SIGNAL(durationChanged(qint64)), this, SIGNAL(durationChanged(qint64))); + connect(m_session, SIGNAL(stateChanged(QMediaPlayer::State)), + this, SIGNAL(stateChanged(QMediaPlayer::State))); + connect(m_session, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), + this, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); + connect(m_session, SIGNAL(volumeChanged(int)), this, SIGNAL(volumeChanged(int))); + connect(m_session, SIGNAL(mutedChanged(bool)), this, SIGNAL(mutedChanged(bool))); + connect(m_session, SIGNAL(audioAvailableChanged(bool)), this, SIGNAL(audioAvailableChanged(bool))); + connect(m_session, SIGNAL(videoAvailableChanged(bool)), this, SIGNAL(videoAvailableChanged(bool))); + connect(m_session, SIGNAL(error(int,QString)), this, SIGNAL(error(int,QString))); +} + +qint64 QT7PlayerControl::position() const +{ + return m_session->position(); +} + +qint64 QT7PlayerControl::duration() const +{ + return m_session->duration(); +} + +QMediaPlayer::State QT7PlayerControl::state() const +{ + return m_session->state(); +} + +QMediaPlayer::MediaStatus QT7PlayerControl::mediaStatus() const +{ + return m_session->mediaStatus(); +} + +int QT7PlayerControl::bufferStatus() const +{ + return m_session->bufferStatus(); +} + +int QT7PlayerControl::volume() const +{ + return m_session->volume(); +} + +bool QT7PlayerControl::isMuted() const +{ + return m_session->isMuted(); +} + +bool QT7PlayerControl::isSeekable() const +{ + return m_session->isSeekable(); +} + +QMediaTimeRange QT7PlayerControl::availablePlaybackRanges() const +{ + return isSeekable() ? QMediaTimeRange(0, duration()) : QMediaTimeRange(); +} + +qreal QT7PlayerControl::playbackRate() const +{ + return m_session->playbackRate(); +} + +void QT7PlayerControl::setPlaybackRate(qreal rate) +{ + m_session->setPlaybackRate(rate); +} + +void QT7PlayerControl::setPosition(qint64 pos) +{ + m_session->setPosition(pos); +} + +void QT7PlayerControl::play() +{ + m_session->play(); +} + +void QT7PlayerControl::pause() +{ + m_session->pause(); +} + +void QT7PlayerControl::stop() +{ + m_session->stop(); +} + +void QT7PlayerControl::setVolume(int volume) +{ + m_session->setVolume(volume); +} + +void QT7PlayerControl::setMuted(bool muted) +{ + m_session->setMuted(muted); +} + +QMediaContent QT7PlayerControl::media() const +{ + return m_session->media(); +} + +const QIODevice *QT7PlayerControl::mediaStream() const +{ + return m_session->mediaStream(); +} + +void QT7PlayerControl::setMedia(const QMediaContent &content, QIODevice *stream) +{ + m_session->setMedia(content, stream); + + emit mediaChanged(content); +} + +bool QT7PlayerControl::isAudioAvailable() const +{ + return m_session->isAudioAvailable(); +} + +bool QT7PlayerControl::isVideoAvailable() const +{ + return m_session->isVideoAvailable(); +} + +#include "moc_qt7playercontrol.cpp" + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/qt7/mediaplayer/qt7playermetadata.h b/src/plugins/mediaservices/qt7/mediaplayer/qt7playermetadata.h new file mode 100644 index 0000000..f16807a --- /dev/null +++ b/src/plugins/mediaservices/qt7/mediaplayer/qt7playermetadata.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7PLAYERMETADATACONTROL_H +#define QT7PLAYERMETADATACONTROL_H + +#include <QtMultimedia/qmetadatacontrol.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QT7PlayerSession; + +class QT7PlayerMetaDataControl : public QMetaDataControl +{ + Q_OBJECT +public: + QT7PlayerMetaDataControl(QT7PlayerSession *session, QObject *parent); + virtual ~QT7PlayerMetaDataControl(); + + bool isMetaDataAvailable() const; + bool isWritable() const; + + QVariant metaData(QtMultimedia::MetaData key) const; + void setMetaData(QtMultimedia::MetaData key, const QVariant &value); + QList<QtMultimedia::MetaData> availableMetaData() const; + + QVariant extendedMetaData(const QString &key) const ; + void setExtendedMetaData(const QString &key, const QVariant &value); + QStringList availableExtendedMetaData() const; + +private slots: + void updateTags(); + +private: + QT7PlayerSession *m_session; + QMap<QtMultimedia::MetaData, QVariant> m_tags; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/qt7/mediaplayer/qt7playermetadata.mm b/src/plugins/mediaservices/qt7/mediaplayer/qt7playermetadata.mm new file mode 100644 index 0000000..a14981a --- /dev/null +++ b/src/plugins/mediaservices/qt7/mediaplayer/qt7playermetadata.mm @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qt7backend.h" +#include "qt7playermetadata.h" +#include "qt7playersession.h" +#include <QtCore/qvarlengtharray.h> + +#import <QTKit/QTMovie.h> + +#ifdef QUICKTIME_C_API_AVAILABLE + #include <QuickTime/QuickTime.h> + #undef check // avoid name clash; +#endif + +QT_BEGIN_NAMESPACE + +QT7PlayerMetaDataControl::QT7PlayerMetaDataControl(QT7PlayerSession *session, QObject *parent) + :QMetaDataControl(parent), m_session(session) +{ +} + +QT7PlayerMetaDataControl::~QT7PlayerMetaDataControl() +{ +} + +bool QT7PlayerMetaDataControl::isMetaDataAvailable() const +{ + return !m_tags.isEmpty(); +} + +bool QT7PlayerMetaDataControl::isWritable() const +{ + return false; +} + +QVariant QT7PlayerMetaDataControl::metaData(QtMultimedia::MetaData key) const +{ + return m_tags.value(key); +} + +void QT7PlayerMetaDataControl::setMetaData(QtMultimedia::MetaData key, QVariant const &value) +{ + Q_UNUSED(key); + Q_UNUSED(value); +} + +QList<QtMultimedia::MetaData> QT7PlayerMetaDataControl::availableMetaData() const +{ + return m_tags.keys(); +} + +QVariant QT7PlayerMetaDataControl::extendedMetaData(const QString &key) const +{ + Q_UNUSED(key); + return QVariant(); +} + +void QT7PlayerMetaDataControl::setExtendedMetaData(const QString &key, QVariant const &value) +{ + Q_UNUSED(key); + Q_UNUSED(value); +} + +QStringList QT7PlayerMetaDataControl::availableExtendedMetaData() const +{ + return QStringList(); +} + +#ifdef QUICKTIME_C_API_AVAILABLE + +static QString stripCopyRightSymbol(const QString &key) +{ + return key.right(key.length()-1); +} + +static QString convertQuickTimeKeyToUserKey(const QString &key) +{ + if (key == QLatin1String("com.apple.quicktime.displayname")) + return QLatin1String("nam"); + else if (key == QLatin1String("com.apple.quicktime.album")) + return QLatin1String("alb"); + else if (key == QLatin1String("com.apple.quicktime.artist")) + return QLatin1String("ART"); + else + return QLatin1String("???"); +} + +static OSStatus readMetaValue(QTMetaDataRef metaDataRef, QTMetaDataItem item, QTPropertyClass propClass, + QTPropertyID id, QTPropertyValuePtr *value, ByteCount *size) +{ + QTPropertyValueType type; + ByteCount propSize; + UInt32 propFlags; + OSStatus err = QTMetaDataGetItemPropertyInfo(metaDataRef, item, propClass, id, &type, &propSize, &propFlags); + + + *value = malloc(propSize); + + err = QTMetaDataGetItemProperty(metaDataRef, item, propClass, id, propSize, *value, size); + + if (type == 'code' || type == 'itsk' || type == 'itlk') { + // convert from native endian to big endian + OSTypePtr pType = (OSTypePtr)*value; + *pType = EndianU32_NtoB(*pType); + } + + return err; +} + +static UInt32 getMetaType(QTMetaDataRef metaDataRef, QTMetaDataItem item) +{ + QTPropertyValuePtr value = 0; + ByteCount ignore = 0; + OSStatus err = readMetaValue( + metaDataRef, item, kPropertyClass_MetaDataItem, kQTMetaDataItemPropertyID_DataType, &value, &ignore); + + UInt32 type = *((UInt32 *) value); + if (value) + free(value); + return type; +} + +static QString cFStringToQString(CFStringRef str) +{ + if(!str) + return QString(); + CFIndex length = CFStringGetLength(str); + const UniChar *chars = CFStringGetCharactersPtr(str); + if (chars) + return QString(reinterpret_cast<const QChar *>(chars), length); + + QVarLengthArray<UniChar> buffer(length); + CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data()); + return QString(reinterpret_cast<const QChar *>(buffer.constData()), length); +} + + +static QString getMetaValue(QTMetaDataRef metaDataRef, QTMetaDataItem item, SInt32 id) +{ + QTPropertyValuePtr value = 0; + ByteCount size = 0; + OSStatus err = readMetaValue(metaDataRef, item, kPropertyClass_MetaDataItem, id, &value, &size); + + QString string; + UInt32 dataType = getMetaType(metaDataRef, item); + switch (dataType){ + case kQTMetaDataTypeUTF8: + case kQTMetaDataTypeMacEncodedText: + string = cFStringToQString(CFStringCreateWithBytes(0, (UInt8*)value, size, kCFStringEncodingUTF8, false)); + break; + case kQTMetaDataTypeUTF16BE: + string = cFStringToQString(CFStringCreateWithBytes(0, (UInt8*)value, size, kCFStringEncodingUTF16BE, false)); + break; + default: + break; + } + + if (value) + free(value); + return string; +} + + +static void readFormattedData(QTMetaDataRef metaDataRef, OSType format, QMultiMap<QString, QString> &result) +{ + QTMetaDataItem item = kQTMetaDataItemUninitialized; + OSStatus err = QTMetaDataGetNextItem(metaDataRef, format, item, kQTMetaDataKeyFormatWildcard, 0, 0, &item); + while (err == noErr){ + QString key = getMetaValue(metaDataRef, item, kQTMetaDataItemPropertyID_Key); + if (format == kQTMetaDataStorageFormatQuickTime) + key = convertQuickTimeKeyToUserKey(key); + else + key = stripCopyRightSymbol(key); + + if (!result.contains(key)){ + QString val = getMetaValue(metaDataRef, item, kQTMetaDataItemPropertyID_Value); + result.insert(key, val); + } + err = QTMetaDataGetNextItem(metaDataRef, format, item, kQTMetaDataKeyFormatWildcard, 0, 0, &item); + } +} +#endif + + +void QT7PlayerMetaDataControl::updateTags() +{ + bool wasEmpty = m_tags.isEmpty(); + m_tags.clear(); + + QTMovie *movie = (QTMovie*)m_session->movie(); + + if (movie) { + QMultiMap<QString, QString> metaMap; + +#ifdef QUICKTIME_C_API_AVAILABLE + QTMetaDataRef metaDataRef; + OSStatus err = QTCopyMovieMetaData([movie quickTimeMovie], &metaDataRef); + + readFormattedData(metaDataRef, kQTMetaDataStorageFormatUserData, metaMap); + readFormattedData(metaDataRef, kQTMetaDataStorageFormatQuickTime, metaMap); + readFormattedData(metaDataRef, kQTMetaDataStorageFormatiTunes, metaMap); +#else + NSString *name = [movie attributeForKey:@"QTMovieDisplayNameAttribute"]; + metaMap.insert(QLatin1String("nam"), QString::fromUtf8([name UTF8String])); +#endif // QUICKTIME_C_API_AVAILABLE + + m_tags.insert(QtMultimedia::AlbumArtist, metaMap.value(QLatin1String("ART"))); + m_tags.insert(QtMultimedia::AlbumTitle, metaMap.value(QLatin1String("alb"))); + m_tags.insert(QtMultimedia::Title, metaMap.value(QLatin1String("nam"))); + m_tags.insert(QtMultimedia::Date, metaMap.value(QLatin1String("day"))); + m_tags.insert(QtMultimedia::Genre, metaMap.value(QLatin1String("gnre"))); + m_tags.insert(QtMultimedia::TrackNumber, metaMap.value(QLatin1String("trk"))); + m_tags.insert(QtMultimedia::Description, metaMap.value(QLatin1String("des"))); + } + + if (!wasEmpty || !m_tags.isEmpty()) + emit metaDataChanged(); +} + +QT_END_NAMESPACE + +#include "moc_qt7playermetadata.cpp" diff --git a/src/plugins/mediaservices/qt7/mediaplayer/qt7playerservice.h b/src/plugins/mediaservices/qt7/mediaplayer/qt7playerservice.h new file mode 100644 index 0000000..d4b30b8 --- /dev/null +++ b/src/plugins/mediaservices/qt7/mediaplayer/qt7playerservice.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7PLAYERSERVICE_H +#define QT7PLAYERSERVICE_H + +#include <QtCore/qobject.h> +#include <QtMultimedia/qmediaservice.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QMediaMetaData; +class QMediaPlayerControl; +class QMediaPlaylist; +class QMediaPlaylistNavigator; +class QT7PlayerControl; +class QT7PlayerMetaDataControl; +class QT7VideoOutputControl; +class QT7VideoWindowControl; +class QT7VideoWidgetControl; +class QT7VideoRendererControl; +class QT7VideoOutput; +class QT7PlayerSession; + +class QT7PlayerService : public QMediaService +{ +Q_OBJECT +public: + QT7PlayerService(QObject *parent = 0); + ~QT7PlayerService(); + + QMediaControl *control(const char *name) const; + +private slots: + void updateVideoOutput(); + +private: + QT7PlayerSession *m_session; + QT7PlayerControl *m_control; + QT7VideoOutputControl *m_videoOutputControl; + QT7VideoWindowControl *m_videoWidnowControl; + QT7VideoWidgetControl *m_videoWidgetControl; + QT7VideoRendererControl *m_videoRendererControl; + QT7PlayerMetaDataControl *m_playerMetaDataControl; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/mediaservices/qt7/mediaplayer/qt7playerservice.mm b/src/plugins/mediaservices/qt7/mediaplayer/qt7playerservice.mm new file mode 100644 index 0000000..df15fa8 --- /dev/null +++ b/src/plugins/mediaservices/qt7/mediaplayer/qt7playerservice.mm @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qvariant.h> +#include <QtCore/qdebug.h> +#include <QtGui/qwidget.h> + +#include "qt7backend.h" +#include "qt7playerservice.h" +#include "qt7playercontrol.h" +#include "qt7playersession.h" +#include "qt7videooutputcontrol.h" +#include "qt7movieviewoutput.h" +#include "qt7movieviewrenderer.h" +#include "qt7movierenderer.h" +#include "qt7movievideowidget.h" +#include "qt7playermetadata.h" + +#include <QtMultimedia/qmediaplaylistnavigator.h> +#include <QtMultimedia/qmediaplaylist.h> + +QT_BEGIN_NAMESPACE + +QT7PlayerService::QT7PlayerService(QObject *parent): + QMediaService(parent) +{ + m_session = new QT7PlayerSession; + + m_control = new QT7PlayerControl(this); + m_control->setSession(m_session); + + m_playerMetaDataControl = new QT7PlayerMetaDataControl(m_session, this); + connect(m_control, SIGNAL(mediaChanged(QMediaContent)), m_playerMetaDataControl, SLOT(updateTags())); + + m_videoOutputControl = new QT7VideoOutputControl(this); + + m_videoWidnowControl = 0; + m_videoWidgetControl = 0; + m_videoRendererControl = 0; + +#if defined(QT_MAC_USE_COCOA) + m_videoWidnowControl = new QT7MovieViewOutput(this); + m_videoOutputControl->enableOutput(QVideoOutputControl::WindowOutput); + qDebug() << "Using cocoa"; +#endif + +#ifdef QUICKTIME_C_API_AVAILABLE + m_videoRendererControl = new QT7MovieRenderer(this); + m_videoOutputControl->enableOutput(QVideoOutputControl::RendererOutput); + + m_videoWidgetControl = new QT7MovieVideoWidget(this); + m_videoOutputControl->enableOutput(QVideoOutputControl::WidgetOutput); + qDebug() << "QuickTime C API is available"; +#else + m_videoRendererControl = new QT7MovieViewRenderer(this); + m_videoOutputControl->enableOutput(QVideoOutputControl::RendererOutput); + qDebug() << "QuickTime C API is not available"; +#endif + + + connect(m_videoOutputControl, SIGNAL(videoOutputChanged(QVideoOutputControl::Output)), + this, SLOT(updateVideoOutput())); +} + +QT7PlayerService::~QT7PlayerService() +{ +} + +QMediaControl *QT7PlayerService::control(const char *name) const +{ + if (qstrcmp(name, QMediaPlayerControl_iid) == 0) + return m_control; + + if (qstrcmp(name, QVideoOutputControl_iid) == 0) + return m_videoOutputControl; + + if (qstrcmp(name, QVideoWindowControl_iid) == 0) + return m_videoWidnowControl; + + if (qstrcmp(name, QVideoRendererControl_iid) == 0) + return m_videoRendererControl; + + if (qstrcmp(name, QVideoWidgetControl_iid) == 0) + return m_videoWidgetControl; + + if (qstrcmp(name, QMetaDataControl_iid) == 0) + return m_playerMetaDataControl; + + return 0; +} + +void QT7PlayerService::updateVideoOutput() +{ + qDebug() << "QT7PlayerService::updateVideoOutput" << m_videoOutputControl->output(); + + switch (m_videoOutputControl->output()) { + case QVideoOutputControl::WindowOutput: + m_session->setVideoOutput(m_videoWidnowControl); + break; + case QVideoOutputControl::RendererOutput: + m_session->setVideoOutput(m_videoRendererControl); + break; + case QVideoOutputControl::WidgetOutput: + m_session->setVideoOutput(m_videoWidgetControl); + break; + default: + m_session->setVideoOutput(0); + } +} + +QT_END_NAMESPACE + +#include "moc_qt7playerservice.cpp" diff --git a/src/plugins/mediaservices/qt7/mediaplayer/qt7playersession.h b/src/plugins/mediaservices/qt7/mediaplayer/qt7playersession.h new file mode 100644 index 0000000..4742e2e --- /dev/null +++ b/src/plugins/mediaservices/qt7/mediaplayer/qt7playersession.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7PLAYERSESSION_H +#define QT7PLAYERSESSION_H + +#include <QtCore/qobject.h> +#include <QtGui/qmacdefines_mac.h> + +#include <QtMultimedia/qmediaplayercontrol.h> +#include <QtMultimedia/qmediaplayer.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QT7PlayerControl; +class QMediaPlaylist; +class QMediaPlaylistNavigator; +class QT7VideoOutput; +class QT7PlayerSession; +class QT7PlayerService; + +class QT7PlayerSession : public QObject +{ +Q_OBJECT +public: + QT7PlayerSession(QObject *parent = 0); + ~QT7PlayerSession(); + + void *movie() const; + + void setControl(QT7PlayerControl *control); + void setVideoOutput(QT7VideoOutput *output); + + QMediaPlayer::State state() const; + QMediaPlayer::MediaStatus mediaStatus() const; + + QMediaContent media() const; + const QIODevice *mediaStream() const; + void setMedia(const QMediaContent &content, QIODevice *stream); + + qint64 position() const; + qint64 duration() const; + + int bufferStatus() const; + + int volume() const; + bool isMuted() const; + + bool isAudioAvailable() const; + bool isVideoAvailable() const; + + bool isSeekable() const; + + qreal playbackRate() const; + +public slots: + void setPlaybackRate(qreal rate); + + void setPosition(qint64 pos); + + void play(); + void pause(); + void stop(); + + void setVolume(int volume); + void setMuted(bool muted); + + void processEOS(); + void processStateChange(); + void processVolumeChange(); + void processNaturalSizeChange(); + +signals: + void positionChanged(qint64 position); + void durationChanged(qint64 duration); + void stateChanged(QMediaPlayer::State newState); + void mediaStatusChanged(QMediaPlayer::MediaStatus status); + void volumeChanged(int volume); + void mutedChanged(bool muted); + void audioAvailableChanged(bool audioAvailable); + void videoAvailableChanged(bool videoAvailable); + void error(int error, const QString &errorString); + +private: + void *m_QTMovie; + void *m_movieObserver; + + QMediaPlayer::State m_state; + QMediaPlayer::MediaStatus m_mediaStatus; + QIODevice *m_mediaStream; + QMediaContent m_resources; + + QT7VideoOutput *m_videoOutput; + + mutable qint64 m_currentTime; + + bool m_muted; + int m_volume; + qreal m_rate; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/qt7/mediaplayer/qt7playersession.mm b/src/plugins/mediaservices/qt7/mediaplayer/qt7playersession.mm new file mode 100644 index 0000000..5507633 --- /dev/null +++ b/src/plugins/mediaservices/qt7/mediaplayer/qt7playersession.mm @@ -0,0 +1,517 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import <QTKit/QTDataReference.h> +#import <QTKit/QTMovie.h> + +#include "qt7backend.h" + +#include "qt7playersession.h" +#include "qt7playercontrol.h" +#include "qt7videooutputcontrol.h" + +#include <QtMultimedia/qmediaplaylistnavigator.h> + +#include <CoreFoundation/CoreFoundation.h> + +#include <QtCore/qurl.h> +#include <QtCore/qdebug.h> + + +QT_BEGIN_NAMESPACE + +@interface QTMovieObserver : NSObject +{ +@private + QT7PlayerSession *m_session; + QTMovie *m_movie; +} + +- (QTMovieObserver *) initWithPlayerSession:(QT7PlayerSession*)session; +- (void) setMovie:(QTMovie *)movie; +- (void) processEOS:(NSNotification *)notification; +- (void) processStateChange:(NSNotification *)notification; +@end + +@implementation QTMovieObserver + +- (QTMovieObserver *) initWithPlayerSession:(QT7PlayerSession*)session +{ + if (!(self = [super init])) + return nil; + + self->m_session = session; + return self; +} + +- (void) setMovie:(QTMovie *)movie +{ + if (m_movie == movie) + return; + + if (m_movie) { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [m_movie release]; + } + + m_movie = movie; + + if (movie) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(processEOS:) + name:QTMovieDidEndNotification + object:m_movie]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(processStateChange:) + name:QTMovieLoadStateDidChangeNotification + object:m_movie]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(processVolumeChange:) + name:QTMovieVolumeDidChangeNotification + object:m_movie]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(processNaturalSizeChange:) + name: +#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6) + QTMovieNaturalSizeDidChangeNotification +#else + QTMovieEditedNotification +#endif + object:m_movie]; + [movie retain]; + } +} + +- (void) processEOS:(NSNotification *)notification +{ + Q_UNUSED(notification); + m_session->processEOS(); +} + +- (void) processStateChange:(NSNotification *)notification +{ + Q_UNUSED(notification); + m_session->processStateChange(); +} + +- (void) processVolumeChange:(NSNotification *)notification +{ + Q_UNUSED(notification); + m_session->processVolumeChange(); +} + +- (void) processNaturalSizeChange :(NSNotification *)notification +{ + Q_UNUSED(notification); + m_session->processNaturalSizeChange(); +} + +@end + +static CFStringRef qString2CFStringRef(const QString &string) +{ + return CFStringCreateWithCharacters(0, reinterpret_cast<const UniChar *>(string.unicode()), + string.length()); +} + +QT7PlayerSession::QT7PlayerSession(QObject *parent) + : QObject(parent) + , m_QTMovie(0) + , m_state(QMediaPlayer::StoppedState) + , m_mediaStatus(QMediaPlayer::NoMedia) + , m_mediaStream(0) + , m_videoOutput(0) + , m_muted(false) + , m_volume(100) + , m_rate(1.0) +{ + m_movieObserver = [[QTMovieObserver alloc] initWithPlayerSession:this]; +} + +QT7PlayerSession::~QT7PlayerSession() +{ + [(QTMovieObserver*)m_movieObserver setMovie:nil]; + [(QTMovieObserver*)m_movieObserver release]; +} + +void *QT7PlayerSession::movie() const +{ + return m_QTMovie; +} + +void QT7PlayerSession::setVideoOutput(QT7VideoOutput *output) +{ + if (m_videoOutput == output) + return; + + if (m_videoOutput) { + m_videoOutput->setEnabled(false); + m_videoOutput->setMovie(0); + } + + m_videoOutput = output; + + if (m_videoOutput) { + m_videoOutput->setEnabled(m_QTMovie != 0); + m_videoOutput->setMovie(m_QTMovie); + } +} + + +qint64 QT7PlayerSession::position() const +{ + if (!m_QTMovie || m_state == QMediaPlayer::PausedState) + return m_currentTime; + + AutoReleasePool pool; + + QTTime qtTime = [(QTMovie*)m_QTMovie currentTime]; + quint64 t = static_cast<quint64>(float(qtTime.timeValue) / float(qtTime.timeScale) * 1000.0f); + m_currentTime = t; + + return m_currentTime; +} + +qint64 QT7PlayerSession::duration() const +{ + if (!m_QTMovie) + return 0; + + AutoReleasePool pool; + + QTTime qtTime = [(QTMovie*)m_QTMovie duration]; + + return static_cast<quint64>(float(qtTime.timeValue) / float(qtTime.timeScale) * 1000.0f); +} + +QMediaPlayer::State QT7PlayerSession::state() const +{ + return m_state; +} + +QMediaPlayer::MediaStatus QT7PlayerSession::mediaStatus() const +{ + return m_mediaStatus; +} + +int QT7PlayerSession::bufferStatus() const +{ + return 100; +} + +int QT7PlayerSession::volume() const +{ + return m_volume; +} + +bool QT7PlayerSession::isMuted() const +{ + return m_muted; +} + +bool QT7PlayerSession::isSeekable() const +{ + return true; +} + +qreal QT7PlayerSession::playbackRate() const +{ + return m_rate; +} + +void QT7PlayerSession::setPlaybackRate(qreal rate) +{ + if (qFuzzyCompare(m_rate, rate)) + return; + + m_rate = rate; + + if (m_QTMovie && m_state == QMediaPlayer::PlayingState) { + float preferredRate = [[(QTMovie*)m_QTMovie attributeForKey:@"QTMoviePreferredRateAttribute"] floatValue]; + [(QTMovie*)m_QTMovie setRate:preferredRate*m_rate]; + } +} + +void QT7PlayerSession::setPosition(qint64 pos) +{ + if ( !isSeekable() || pos == position()) + return; + + AutoReleasePool pool; + + pos = qMin(pos, duration()); + + QTTime newQTTime = [(QTMovie*)m_QTMovie currentTime]; + newQTTime.timeValue = (pos / 1000.0f) * newQTTime.timeScale; + [(QTMovie*)m_QTMovie setCurrentTime:newQTTime]; +} + +void QT7PlayerSession::play() +{ + float preferredRate = [[(QTMovie*)m_QTMovie attributeForKey:@"QTMoviePreferredRateAttribute"] floatValue]; + [(QTMovie*)m_QTMovie setRate:preferredRate*m_rate]; + + if (m_state != QMediaPlayer::PlayingState) + emit stateChanged(m_state = QMediaPlayer::PlayingState); +} + +void QT7PlayerSession::pause() +{ + m_state = QMediaPlayer::PausedState; + + [(QTMovie*)m_QTMovie setRate:0]; + + emit stateChanged(m_state); +} + +void QT7PlayerSession::stop() +{ + m_state = QMediaPlayer::StoppedState; + + [(QTMovie*)m_QTMovie setRate:0]; + setPosition(0); + + if (m_state == QMediaPlayer::StoppedState) + emit stateChanged(m_state); +} + +void QT7PlayerSession::setVolume(int volume) +{ + if (m_QTMovie) { + m_volume = volume; + [(QTMovie*)m_QTMovie setVolume:(volume/100.0f)]; + } +} + +void QT7PlayerSession::setMuted(bool muted) +{ + if (m_muted != muted) { + m_muted = muted; + + if (m_QTMovie) + [(QTMovie*)m_QTMovie setMuted:m_muted]; + + emit mutedChanged(muted); + } +} + +QMediaContent QT7PlayerSession::media() const +{ + return m_resources; +} + +const QIODevice *QT7PlayerSession::mediaStream() const +{ + return m_mediaStream; +} + +void QT7PlayerSession::setMedia(const QMediaContent &content, QIODevice *stream) +{ + AutoReleasePool pool; + + if (m_QTMovie) { + [(QTMovieObserver*)m_movieObserver setMovie:nil]; + + if (m_videoOutput) { + m_videoOutput->setEnabled(false); + m_videoOutput->setMovie(0); + } + + [(QTMovie*)m_QTMovie release]; + m_QTMovie = 0; + } + + m_resources = content; + m_mediaStream = stream; + m_mediaStatus = QMediaPlayer::NoMedia; + + QUrl url; + + if (!content.isNull()) + url = content.canonicalUrl(); + else + return; + + qDebug() << "Open media" << url; + + NSError *err = 0; + NSString *urlString = (NSString *)qString2CFStringRef(url.toString()); + + NSDictionary *attr = [NSDictionary dictionaryWithObjectsAndKeys: + [NSURL URLWithString:urlString], QTMovieURLAttribute, + [NSNumber numberWithBool:YES], QTMovieOpenAsyncOKAttribute, + [NSNumber numberWithBool:YES], QTMovieIsActiveAttribute, + [NSNumber numberWithBool:YES], QTMovieResolveDataRefsAttribute, + [NSNumber numberWithBool:YES], QTMovieDontInteractWithUserAttribute, + nil]; + + m_QTMovie = [[QTMovie movieWithAttributes:attr error:&err] retain]; + + if (err) { + [(QTMovie*)m_QTMovie release]; + m_QTMovie = 0; + QString description = QString::fromUtf8([[err localizedDescription] UTF8String]); + + emit error(QMediaPlayer::FormatError, description ); + } else { + [(QTMovieObserver*)m_movieObserver setMovie:(QTMovie*)m_QTMovie]; + + if (m_videoOutput) { + m_videoOutput->setMovie(m_QTMovie); + m_videoOutput->setEnabled(true); + } + processStateChange(); + + [(QTMovie*)m_QTMovie setMuted:m_muted]; + setVolume(m_volume); + } +} + +bool QT7PlayerSession::isAudioAvailable() const +{ + if (!m_QTMovie) + return false; + + AutoReleasePool pool; + return [[(QTMovie*)m_QTMovie attributeForKey:@"QTMovieHasAudioAttribute"] boolValue] == YES; +} + +bool QT7PlayerSession::isVideoAvailable() const +{ + if (!m_QTMovie) + return false; + + AutoReleasePool pool; + return [[(QTMovie*)m_QTMovie attributeForKey:@"QTMovieHasVideoAttribute"] boolValue] == YES; +} + +void QT7PlayerSession::processEOS() +{ + m_mediaStatus = QMediaPlayer::EndOfMedia; + emit stateChanged(m_state = QMediaPlayer::StoppedState); + emit mediaStatusChanged(m_mediaStatus); +} + +void QT7PlayerSession::processStateChange() +{ + signed long state = [[(QTMovie*)m_QTMovie attributeForKey:QTMovieLoadStateAttribute] + longValue]; + qDebug() << "Moview load state changed:" << state; + +#ifndef QUICKTIME_C_API_AVAILABLE + enum { + kMovieLoadStateError = -1L, + kMovieLoadStateLoading = 1000, + kMovieLoadStateLoaded = 2000, + kMovieLoadStatePlayable = 10000, + kMovieLoadStatePlaythroughOK = 20000, + kMovieLoadStateComplete = 100000 + }; +#endif + + QMediaPlayer::MediaStatus newStatus = QMediaPlayer::NoMedia; + bool isPlaying = (m_state != QMediaPlayer::StoppedState); + + if (state >= kMovieLoadStateComplete) { + newStatus = isPlaying ? QMediaPlayer::BufferedMedia : QMediaPlayer::LoadedMedia; + } else if (state >= kMovieLoadStatePlayable) + newStatus = isPlaying ? QMediaPlayer::BufferingMedia : QMediaPlayer::LoadingMedia; + else if (state >= kMovieLoadStateLoading) + newStatus = isPlaying ? QMediaPlayer::StalledMedia : QMediaPlayer::LoadingMedia; + + if (state == kMovieLoadStateError) { + newStatus = QMediaPlayer::InvalidMedia; + emit error(QMediaPlayer::FormatError, tr("Playback failed")); + } + + if (newStatus != m_mediaStatus) { + switch (newStatus) { + case QMediaPlayer::BufferedMedia: + case QMediaPlayer::BufferingMedia: + //delayed playback start is necessary for network sources + if (m_state == QMediaPlayer::PlayingState) { + QMetaObject::invokeMethod(this, "play", Qt::QueuedConnection); + } + //fall + case QMediaPlayer::LoadedMedia: + case QMediaPlayer::LoadingMedia: + emit durationChanged(duration()); + emit audioAvailableChanged(isAudioAvailable()); + emit videoAvailableChanged(isVideoAvailable()); + break; + case QMediaPlayer::InvalidMedia: + emit stateChanged(m_state = QMediaPlayer::StoppedState); + default: + break; + } + + emit mediaStatusChanged(m_mediaStatus = newStatus); + } +} + +void QT7PlayerSession::processVolumeChange() +{ + if (!m_QTMovie) + return; + + int newVolume = qRound(100.0f*[((QTMovie*)m_QTMovie) volume]); + + if (newVolume != m_volume) { + emit volumeChanged(m_volume = newVolume); + } +} + +void QT7PlayerSession::processNaturalSizeChange() +{ + if (m_videoOutput) { + NSSize size = [[(QTMovie*)m_QTMovie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue]; + qDebug() << "Native size changed:" << QSize(size.width, size.height); + m_videoOutput->updateNaturalSize(QSize(size.width, size.height)); + } +} + +#include "moc_qt7playersession.cpp" + +QT_END_NAMESPACE + diff --git a/src/plugins/mediaservices/qt7/qcvdisplaylink.h b/src/plugins/mediaservices/qt7/qcvdisplaylink.h new file mode 100644 index 0000000..5cd8f73 --- /dev/null +++ b/src/plugins/mediaservices/qt7/qcvdisplaylink.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCVDISPLAYLINK_H +#define QCVDISPLAYLINK_H + +#include <QtCore/qobject.h> +#include <QtCore/qmutex.h> + +#include <CoreVideo/CVDisplayLink.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QCvDisplayLink : public QObject +{ +Q_OBJECT +public: + QCvDisplayLink(QObject *parent = 0); + virtual ~QCvDisplayLink(); + + bool isValid(); + bool isActive() const; + +public slots: + void start(); + void stop(); + +signals: + void tick(const CVTimeStamp &ts); + +public: + void displayLinkEvent(const CVTimeStamp *); + +protected: + virtual bool event(QEvent *); + +private: + CVDisplayLinkRef m_displayLink; + QMutex m_displayLinkMutex; + bool m_pendingDisplayLinkEvent; + bool m_isActive; + CVTimeStamp m_frameTimeStamp; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif + diff --git a/src/plugins/mediaservices/qt7/qcvdisplaylink.mm b/src/plugins/mediaservices/qt7/qcvdisplaylink.mm new file mode 100644 index 0000000..98ae71d --- /dev/null +++ b/src/plugins/mediaservices/qt7/qcvdisplaylink.mm @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcvdisplaylink.h" + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qdebug.h> + + +QT_USE_NAMESPACE + +static CVReturn CVDisplayLinkCallback(CVDisplayLinkRef displayLink, + const CVTimeStamp *inNow, + const CVTimeStamp *inOutputTime, + CVOptionFlags flagsIn, + CVOptionFlags *flagsOut, + void *displayLinkContext) +{ + Q_UNUSED(displayLink); + Q_UNUSED(inNow); + Q_UNUSED(flagsIn); + Q_UNUSED(flagsOut); + + QCvDisplayLink *link = (QCvDisplayLink *)displayLinkContext; + + link->displayLinkEvent(inOutputTime); + return kCVReturnSuccess; +} + + +QCvDisplayLink::QCvDisplayLink(QObject *parent) + :QObject(parent), + m_pendingDisplayLinkEvent(false), + m_isActive(false) +{ + // create display link for the main display + CVDisplayLinkCreateWithCGDisplay(kCGDirectMainDisplay, &m_displayLink); + if (m_displayLink) { + // set the current display of a display link. + CVDisplayLinkSetCurrentCGDisplay(m_displayLink, kCGDirectMainDisplay); + + // set the renderer output callback function + CVDisplayLinkSetOutputCallback(m_displayLink, &CVDisplayLinkCallback, this); + } +} + +QCvDisplayLink::~QCvDisplayLink() +{ + if (m_displayLink) { + CVDisplayLinkStop(m_displayLink); + CVDisplayLinkRelease(m_displayLink); + m_displayLink = NULL; + } +} + +bool QCvDisplayLink::isValid() +{ + return m_displayLink != 0; +} + +bool QCvDisplayLink::isActive() const +{ + return m_isActive; +} + +void QCvDisplayLink::start() +{ + if (m_displayLink && !m_isActive) { + CVDisplayLinkStart(m_displayLink); + m_isActive = true; + } +} + +void QCvDisplayLink::stop() +{ + if (m_displayLink && m_isActive) { + CVDisplayLinkStop(m_displayLink); + m_isActive = false; + } +} + +void QCvDisplayLink::displayLinkEvent(const CVTimeStamp *ts) +{ + // This function is called from a + // thread != gui thread. So we post the event. + // But we need to make sure that we don't post faster + // than the event loop can eat: + m_displayLinkMutex.lock(); + bool pending = m_pendingDisplayLinkEvent; + m_pendingDisplayLinkEvent = true; + m_frameTimeStamp = *ts; + m_displayLinkMutex.unlock(); + + if (!pending) + qApp->postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority); +} + +bool QCvDisplayLink::event(QEvent *event) +{ + switch (event->type()){ + case QEvent::User: { + m_displayLinkMutex.lock(); + m_pendingDisplayLinkEvent = false; + CVTimeStamp ts = m_frameTimeStamp; + m_displayLinkMutex.unlock(); + + emit tick(ts); + + return false; + } + break; + default: + break; + } + return QObject::event(event); +} + +QT_END_NAMESPACE + +#include "moc_qcvdisplaylink.cpp" + diff --git a/src/plugins/mediaservices/qt7/qt7.pro b/src/plugins/mediaservices/qt7/qt7.pro new file mode 100644 index 0000000..13d14d7 --- /dev/null +++ b/src/plugins/mediaservices/qt7/qt7.pro @@ -0,0 +1,45 @@ +TARGET = qt7 +include(../../qpluginbase.pri) + +QT += opengl multimedia + +LIBS += -framework AppKit -framework AudioUnit \ + -framework AudioToolbox -framework CoreAudio \ + -framework QuartzCore -framework QTKit + +# The Quicktime framework is only awailable for 32-bit builds, so we +# need to check for this before linking against it. +# QMAKE_MAC_XARCH is not awailable on Tiger, but at the same time, +# we never build for 64-bit architechtures on Tiger either: +contains(QMAKE_MAC_XARCH, no) { + LIBS += -framework QuickTime +} else { + LIBS += -Xarch_i386 -framework QuickTime -Xarch_ppc -framework QuickTime +} + +HEADERS += \ + qt7backend.h \ + qt7videooutputcontrol.h \ + qt7movieviewoutput.h \ + qt7movievideowidget.h \ + qt7movieviewrenderer.h \ + qt7serviceplugin.h \ + qt7movierenderer.h \ + qcvdisplaylink.h + +OBJECTIVE_SOURCES += \ + qt7backend.mm \ + qt7serviceplugin.mm \ + qt7movieviewoutput.mm \ + qt7movievideowidget.mm \ + qt7movieviewrenderer.mm \ + qt7movierenderer.mm \ + qt7videooutputcontrol.mm \ + qcvdisplaylink.mm + +include(mediaplayer/mediaplayer.pri) + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/mediaservices +target.path = $$[QT_INSTALL_PLUGINS]/plugins/mediaservices +INSTALLS += target + diff --git a/src/plugins/mediaservices/qt7/qt7backend.h b/src/plugins/mediaservices/qt7/qt7backend.h new file mode 100644 index 0000000..5668965 --- /dev/null +++ b/src/plugins/mediaservices/qt7/qt7backend.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7BACKEND_H +#define QT7BACKEND_H + +#include <QtCore/qstring.h> + +#ifndef Q_WS_MAC64 +#define QUICKTIME_C_API_AVAILABLE +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class AutoReleasePool +{ +private: + void *pool; +public: + AutoReleasePool(); + ~AutoReleasePool(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/qt7/qt7backend.mm b/src/plugins/mediaservices/qt7/qt7backend.mm new file mode 100644 index 0000000..478589b --- /dev/null +++ b/src/plugins/mediaservices/qt7/qt7backend.mm @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qt7backend.h" + +#import <Foundation/NSAutoreleasePool.h> +#include <CoreFoundation/CFBase.h> + + +QT_BEGIN_NAMESPACE + +AutoReleasePool::AutoReleasePool() +{ + pool = (void*)[[NSAutoreleasePool alloc] init]; +} + +AutoReleasePool::~AutoReleasePool() +{ + [(NSAutoreleasePool*)pool release]; +} + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/qt7/qt7movierenderer.h b/src/plugins/mediaservices/qt7/qt7movierenderer.h new file mode 100644 index 0000000..a547329 --- /dev/null +++ b/src/plugins/mediaservices/qt7/qt7movierenderer.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7MOVIERENDERER_H +#define QT7MOVIERENDERER_H + +#include "qt7backend.h" + +#include <QtCore/qobject.h> +#include <QtCore/qmutex.h> + +#include <qvideorenderercontrol.h> +#include <qmediaplayer.h> + +#include <QtGui/qmacdefines_mac.h> +#include "qt7videooutputcontrol.h" + +#include <CoreVideo/CVOpenGLTexture.h> +#include <QuickTime/QuickTime.h> + + +QT_BEGIN_HEADER + +class QGLContext; + +QT_BEGIN_NAMESPACE + +class QCvDisplayLink; +class QT7PlayerSession; +class QT7PlayerService; + +class QT7MovieRenderer : public QT7VideoRendererControl +{ +Q_OBJECT +public: + QT7MovieRenderer(QObject *parent = 0); + virtual ~QT7MovieRenderer(); + + void setEnabled(bool); + void setMovie(void *movie); + void updateNaturalSize(const QSize &newSize); + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + + QSize nativeSize() const; + +private slots: + void updateVideoFrame(const CVTimeStamp &ts); + +private: + void setupVideoOutput(); + bool createPixelBufferVisualContext(); + bool createGLVisualContext(); + + void *m_movie; + + QMutex m_mutex; + + QCvDisplayLink *m_displayLink; +#ifdef QUICKTIME_C_API_AVAILABLE + QTVisualContextRef m_visualContext; + bool m_usingGLContext; + const QGLContext *m_currentGLContext; + QSize m_pixelBufferContextGeometry; +#endif + QAbstractVideoSurface *m_surface; + QSize m_nativeSize; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/qt7/qt7movierenderer.mm b/src/plugins/mediaservices/qt7/qt7movierenderer.mm new file mode 100644 index 0000000..587f3b9 --- /dev/null +++ b/src/plugins/mediaservices/qt7/qt7movierenderer.mm @@ -0,0 +1,454 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import <QTKit/QTKit.h> + +#include "qt7backend.h" + +#include "qt7playercontrol.h" +#include "qt7movierenderer.h" +#include "qt7playersession.h" +#include "qcvdisplaylink.h" +#include <QtCore/qdebug.h> +#include <QtCore/qcoreapplication.h> + +#include <QGLWidget> + +#include <QtMultimedia/qabstractvideobuffer.h> +#include <QtMultimedia/qabstractvideosurface.h> +#include <QtMultimedia/qvideosurfaceformat.h> + +QT_BEGIN_NAMESPACE + +class CVGLTextureVideoBuffer : public QAbstractVideoBuffer +{ +public: + CVGLTextureVideoBuffer(CVOpenGLTextureRef buffer) + : QAbstractVideoBuffer(NoHandle) + , m_buffer(buffer) + , m_mode(NotMapped) + { + CVOpenGLTextureRetain(m_buffer); + } + + virtual ~CVGLTextureVideoBuffer() + { + CVOpenGLTextureRelease(m_buffer); + } + + QVariant handle() const + { + GLuint id = CVOpenGLTextureGetName(m_buffer); + return QVariant(int(id)); + } + + HandleType handleType() const + { + return GLTextureHandle; + } + + MapMode mapMode() const { return m_mode; } + + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) + { + if (numBytes) + *numBytes = 0; + + if (bytesPerLine) + *bytesPerLine = 0; + + m_mode = mode; + return 0; + } + + void unmap() { m_mode = NotMapped; } + +private: + CVOpenGLTextureRef m_buffer; + MapMode m_mode; +}; + + +class CVPixelBufferVideoBuffer : public QAbstractVideoBuffer +{ +public: + CVPixelBufferVideoBuffer(CVPixelBufferRef buffer) + : QAbstractVideoBuffer(NoHandle) + , m_buffer(buffer) + , m_mode(NotMapped) + { + CVPixelBufferRetain(m_buffer); + } + + virtual ~CVPixelBufferVideoBuffer() + { + CVPixelBufferRelease(m_buffer); + } + + MapMode mapMode() const { return m_mode; } + + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) + { + if (mode != NotMapped && m_mode == NotMapped) { + CVPixelBufferLockBaseAddress(m_buffer, 0); + + if (numBytes) + *numBytes = CVPixelBufferGetDataSize(m_buffer); + + if (bytesPerLine) + *bytesPerLine = CVPixelBufferGetBytesPerRow(m_buffer); + + m_mode = mode; + + return (uchar*)CVPixelBufferGetBaseAddress(m_buffer); + } else { + return 0; + } + } + + void unmap() + { + if (m_mode != NotMapped) { + m_mode = NotMapped; + CVPixelBufferUnlockBaseAddress(m_buffer, 0); + } + } + +private: + CVPixelBufferRef m_buffer; + MapMode m_mode; +}; + + + +QT7MovieRenderer::QT7MovieRenderer(QObject *parent) + :QT7VideoRendererControl(parent), + m_movie(0), +#ifdef QUICKTIME_C_API_AVAILABLE + m_visualContext(0), + m_usingGLContext(false), + m_currentGLContext(0), +#endif + m_surface(0) +{ + qDebug() << "QT7MovieRenderer"; + + m_displayLink = new QCvDisplayLink(this); + connect(m_displayLink, SIGNAL(tick(CVTimeStamp)), SLOT(updateVideoFrame(CVTimeStamp))); +} + + +bool QT7MovieRenderer::createGLVisualContext() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + AutoReleasePool pool; + CGLContextObj cglContext = CGLGetCurrentContext(); + NSOpenGLPixelFormat *nsglPixelFormat = [NSOpenGLView defaultPixelFormat]; + CGLPixelFormatObj cglPixelFormat = static_cast<CGLPixelFormatObj>([nsglPixelFormat CGLPixelFormatObj]); + + OSStatus err = QTOpenGLTextureContextCreate(kCFAllocatorDefault, cglContext, + cglPixelFormat, NULL, &m_visualContext); + if (err != noErr) + qWarning() << "Could not create visual context (OpenGL)"; + + return (err == noErr); +#endif // QUICKTIME_C_API_AVAILABLE + + return false; +} + +#ifdef QUICKTIME_C_API_AVAILABLE +static bool DictionarySetValue(CFMutableDictionaryRef dict, CFStringRef key, SInt32 value) +{ + CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); + + if (number) { + CFDictionarySetValue( dict, key, number ); + CFRelease( number ); + return true; + } + return false; +} +#endif // QUICKTIME_C_API_AVAILABLE + +bool QT7MovieRenderer::createPixelBufferVisualContext() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + if (m_visualContext) { + QTVisualContextRelease(m_visualContext); + m_visualContext = 0; + } + + m_pixelBufferContextGeometry = m_nativeSize; + + CFMutableDictionaryRef pixelBufferOptions = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + //DictionarySetValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, k32ARGBPixelFormat ); + DictionarySetValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, k32BGRAPixelFormat ); + DictionarySetValue(pixelBufferOptions, kCVPixelBufferWidthKey, m_nativeSize.width() ); + DictionarySetValue(pixelBufferOptions, kCVPixelBufferHeightKey, m_nativeSize.height() ); + DictionarySetValue(pixelBufferOptions, kCVPixelBufferBytesPerRowAlignmentKey, 16); + //CFDictionarySetValue(pixelBufferOptions, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); + + CFMutableDictionaryRef visualContextOptions = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue(visualContextOptions, kQTVisualContextPixelBufferAttributesKey, pixelBufferOptions); + CFDictionarySetValue(visualContextOptions, kQTVisualContextWorkingColorSpaceKey, CGColorSpaceCreateDeviceRGB()); + + OSStatus err = QTPixelBufferContextCreate(kCFAllocatorDefault, + visualContextOptions, + &m_visualContext); + CFRelease(pixelBufferOptions); + CFRelease(visualContextOptions); + + if (err != noErr) { + qWarning() << "Could not create visual context (PixelBuffer)"; + return false; + } + + return true; +#endif // QUICKTIME_C_API_AVAILABLE + + return false; +} + + +QT7MovieRenderer::~QT7MovieRenderer() +{ + m_displayLink->stop(); +} + +void QT7MovieRenderer::setupVideoOutput() +{ + AutoReleasePool pool; + + qDebug() << "QT7MovieRenderer::setupVideoOutput" << m_movie; + + if (m_movie == 0 || m_surface == 0) { + m_displayLink->stop(); + return; + } + + NSSize size = [[(QTMovie*)m_movie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue]; + m_nativeSize = QSize(size.width, size.height); + +#ifdef QUICKTIME_C_API_AVAILABLE + bool usedGLContext = m_usingGLContext; + + if (!m_nativeSize.isEmpty()) { + + bool glSupported = !m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty(); + + //Try rendering using opengl textures first: + if (glSupported) { + QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32, QAbstractVideoBuffer::GLTextureHandle); + + if (m_surface->isActive()) + m_surface->stop(); + + qDebug() << "Starting the surface with format" << format; + if (!m_surface->start(format)) { + qDebug() << "failed to start video surface" << m_surface->error(); + glSupported = false; + } else { + m_usingGLContext = true; + } + + } + + if (!glSupported) { + m_usingGLContext = false; + QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32); + + if (m_surface->isActive() && m_surface->surfaceFormat() != format) { + qDebug() << "Surface format was changed, stop the surface."; + m_surface->stop(); + } + + if (!m_surface->isActive()) { + qDebug() << "Starting the surface with format" << format; + if (!m_surface->start(format)) + qDebug() << "failed to start video surface" << m_surface->error(); + } + } + } + + + if (m_visualContext) { + //check if the visual context still can be reused + if (usedGLContext != m_usingGLContext || + (m_usingGLContext && (m_currentGLContext != QGLContext::currentContext())) || + (!m_usingGLContext && (m_pixelBufferContextGeometry != m_nativeSize))) { + QTVisualContextRelease(m_visualContext); + m_pixelBufferContextGeometry = QSize(); + m_visualContext = 0; + } + } + + if (!m_nativeSize.isEmpty()) { + if (!m_visualContext) { + if (m_usingGLContext) { + qDebug() << "Building OpenGL visual context" << m_nativeSize; + m_currentGLContext = QGLContext::currentContext(); + if (!createGLVisualContext()) { + qWarning() << "QT7MovieRenderer: failed to create visual context"; + return; + } + } else { + qDebug() << "Building Pixel Buffer visual context" << m_nativeSize; + if (!createPixelBufferVisualContext()) { + qWarning() << "QT7MovieRenderer: failed to create visual context"; + return; + } + } + } + + // targets a Movie to render into a visual context + SetMovieVisualContext([(QTMovie*)m_movie quickTimeMovie], m_visualContext); + + m_displayLink->start(); + } +#endif + +} + +void QT7MovieRenderer::setEnabled(bool) +{ +} + +void QT7MovieRenderer::setMovie(void *movie) +{ + qDebug() << "QT7MovieRenderer::setMovie" << movie; + +#ifdef QUICKTIME_C_API_AVAILABLE + QMutexLocker locker(&m_mutex); + + if (m_movie != movie) { + if (m_movie) { + //ensure the old movie doesn't hold the visual context, otherwise it can't be reused + SetMovieVisualContext([(QTMovie*)m_movie quickTimeMovie], nil); + [(QTMovie*)m_movie release]; + } + + m_movie = movie; + [(QTMovie*)m_movie retain]; + + setupVideoOutput(); + } +#endif +} + +void QT7MovieRenderer::updateNaturalSize(const QSize &newSize) +{ + if (m_nativeSize != newSize) { + m_nativeSize = newSize; + setupVideoOutput(); + } +} + +QAbstractVideoSurface *QT7MovieRenderer::surface() const +{ + return m_surface; +} + +void QT7MovieRenderer::setSurface(QAbstractVideoSurface *surface) +{ + qDebug() << "Set video surface" << surface; + + if (surface == m_surface) + return; + + QMutexLocker locker(&m_mutex); + + if (m_surface && m_surface->isActive()) + m_surface->stop(); + + m_surface = surface; + setupVideoOutput(); +} + + +QSize QT7MovieRenderer::nativeSize() const +{ + return m_nativeSize; +} + +void QT7MovieRenderer::updateVideoFrame(const CVTimeStamp &ts) +{ +#ifdef QUICKTIME_C_API_AVAILABLE + + QMutexLocker locker(&m_mutex); + + if (m_surface && m_surface->isActive() && + m_visualContext && QTVisualContextIsNewImageAvailable(m_visualContext, &ts)) { + + CVImageBufferRef imageBuffer = NULL; + + OSStatus status = QTVisualContextCopyImageForTime(m_visualContext, NULL, &ts, &imageBuffer); + + if (status == noErr && imageBuffer) { + //qDebug() << "render video frame"; + QAbstractVideoBuffer *buffer = 0; + + if (m_usingGLContext) { + buffer = new CVGLTextureVideoBuffer((CVOpenGLTextureRef)imageBuffer); + CVOpenGLTextureRelease((CVOpenGLTextureRef)imageBuffer); + //qDebug() << "render GL video frame" << buffer->handle(); + } else { + buffer = new CVPixelBufferVideoBuffer((CVPixelBufferRef)imageBuffer); + CVPixelBufferRelease((CVPixelBufferRef)imageBuffer); + } + + QVideoFrame frame(buffer, m_nativeSize, QVideoFrame::Format_RGB32); + m_surface->present(frame); + QTVisualContextTask(m_visualContext); + } + } +#else + Q_UNUSED(ts); +#endif +} + +#include "moc_qt7movierenderer.cpp" + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/qt7/qt7movievideowidget.h b/src/plugins/mediaservices/qt7/qt7movievideowidget.h new file mode 100644 index 0000000..3acd373 --- /dev/null +++ b/src/plugins/mediaservices/qt7/qt7movievideowidget.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7MOVIEVIDEOWIDGET_H +#define QT7MOVIEVIDEOWIDGET_H + +#include <QtCore/qobject.h> +#include <QtCore/qmutex.h> + +#include <qvideowindowcontrol.h> +#include <qmediaplayer.h> + +#include <QtGui/qmacdefines_mac.h> +#include "qt7videooutputcontrol.h" + +#include <CoreVideo/CVOpenGLTexture.h> +#include <QuickTime/QuickTime.h> + + +QT_BEGIN_HEADER + +class GLVideoWidget; + +QT_BEGIN_NAMESPACE + +class QCvDisplayLink; +class QT7PlayerSession; +class QT7PlayerService; + +class QT7MovieVideoWidget : public QT7VideoWidgetControl +{ +Q_OBJECT +public: + QT7MovieVideoWidget(QObject *parent = 0); + virtual ~QT7MovieVideoWidget(); + + void setEnabled(bool); + void setMovie(void *movie); + void updateNaturalSize(const QSize &newSize); + + QWidget *videoWidget(); + + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + QSize nativeSize() const; + + QVideoWidget::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(QVideoWidget::AspectRatioMode mode); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + +private slots: + void updateVideoFrame(const CVTimeStamp &ts); + +private: + void setupVideoOutput(); + bool createVisualContext(); + + void updateColors(); + + void *m_movie; + GLVideoWidget *m_videoWidget; + + QCvDisplayLink *m_displayLink; + +#ifdef QUICKTIME_C_API_AVAILABLE + QTVisualContextRef m_visualContext; +#endif + + bool m_fullscreen; + QSize m_nativeSize; + QVideoWidget::AspectRatioMode m_aspectRatioMode; + int m_brightness; + int m_contrast; + int m_hue; + int m_saturation; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/qt7/qt7movievideowidget.mm b/src/plugins/mediaservices/qt7/qt7movievideowidget.mm new file mode 100644 index 0000000..6e74fcd --- /dev/null +++ b/src/plugins/mediaservices/qt7/qt7movievideowidget.mm @@ -0,0 +1,417 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import <QTKit/QTKit.h> + +#include "qt7backend.h" + +#include "qt7playercontrol.h" +#include "qt7movievideowidget.h" +#include "qt7playersession.h" +#include "qcvdisplaylink.h" +#include <QtCore/qdebug.h> +#include <QtCore/qcoreapplication.h> + +#include <QGLWidget> + +#import <QuartzCore/QuartzCore.h> + +#include "math.h" + +QT_BEGIN_NAMESPACE + +class GLVideoWidget : public QGLWidget +{ +public: + + GLVideoWidget(QWidget *parent, const QGLFormat &format) + : QGLWidget(format, parent), + m_texRef(0), + m_nativeSize(640,480), + m_aspectRatioMode(QVideoWidget::KeepAspectRatio) + { + setAutoFillBackground(false); + } + + void initializeGL() + { + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + } + + void resizeGL(int w, int h) + { + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glViewport(0, 0, GLsizei(w), GLsizei(h)); + gluOrtho2D(0, GLsizei(w), 0, GLsizei(h)); + updateGL(); + } + + void paintGL() + { + glClear(GL_COLOR_BUFFER_BIT); + if (!m_texRef) + return; + + glPushMatrix(); + glDisable(GL_CULL_FACE); + GLenum target = CVOpenGLTextureGetTarget(m_texRef); + glEnable(target); + + glBindTexture(target, CVOpenGLTextureGetName(m_texRef)); + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + GLfloat lowerLeft[2], lowerRight[2], upperRight[2], upperLeft[2]; + CVOpenGLTextureGetCleanTexCoords(m_texRef, lowerLeft, lowerRight, upperRight, upperLeft); + + glBegin(GL_QUADS); + QRect rect = displayRect(); + glTexCoord2f(lowerLeft[0], lowerLeft[1]); + glVertex2i(rect.topLeft().x(), rect.topLeft().y()); + glTexCoord2f(lowerRight[0], lowerRight[1]); + glVertex2i(rect.topRight().x() + 1, rect.topRight().y()); + glTexCoord2f(upperRight[0], upperRight[1]); + glVertex2i(rect.bottomRight().x() + 1, rect.bottomRight().y() + 1); + glTexCoord2f(upperLeft[0], upperLeft[1]); + glVertex2i(rect.bottomLeft().x(), rect.bottomLeft().y() + 1); + glEnd(); + glPopMatrix(); + } + + void setCVTexture(CVOpenGLTextureRef texRef) + { + if (m_texRef) + CVOpenGLTextureRelease(m_texRef); + + m_texRef = texRef; + + if (m_texRef) + CVOpenGLTextureRetain(m_texRef); + + if (isVisible()) { + makeCurrent(); + paintGL(); + swapBuffers(); + } + } + + QSize sizeHint() const + { + return m_nativeSize; + } + + void setNativeSize(const QSize &size) + { + m_nativeSize = size; + } + + void setAspectRatioMode(QVideoWidget::AspectRatioMode mode) + { + if (m_aspectRatioMode != mode) { + m_aspectRatioMode = mode; + update(); + } + } + +private: + QRect displayRect() const + { + QRect displayRect = rect(); + + if (m_aspectRatioMode == QVideoWidget::KeepAspectRatio) { + QSize size = m_nativeSize; + size.scale(displayRect.size(), Qt::KeepAspectRatio); + + displayRect = QRect(QPoint(0, 0), size); + displayRect.moveCenter(rect().center()); + } + return displayRect; + } + + CVOpenGLTextureRef m_texRef; + QSize m_nativeSize; + QVideoWidget::AspectRatioMode m_aspectRatioMode; +}; + +QT7MovieVideoWidget::QT7MovieVideoWidget(QObject *parent) + :QT7VideoWidgetControl(parent), + m_movie(0), + m_videoWidget(0), + m_fullscreen(false), + m_aspectRatioMode(QVideoWidget::KeepAspectRatio), + m_brightness(0), + m_contrast(0), + m_hue(0), + m_saturation(0) +{ + qDebug() << "QT7MovieVideoWidget"; + + QGLFormat format = QGLFormat::defaultFormat(); + format.setSwapInterval(1); // Vertical sync (avoid tearing) + m_videoWidget = new GLVideoWidget(0, format); + + m_displayLink = new QCvDisplayLink(this); + + connect(m_displayLink, SIGNAL(tick(CVTimeStamp)), SLOT(updateVideoFrame(CVTimeStamp))); + + if (!createVisualContext()) { + qWarning() << "QT7MovieVideoWidget: failed to create visual context"; + } +} + + +bool QT7MovieVideoWidget::createVisualContext() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + m_videoWidget->makeCurrent(); + + AutoReleasePool pool; + CGLContextObj cglContext = CGLGetCurrentContext(); + NSOpenGLPixelFormat *nsglPixelFormat = [NSOpenGLView defaultPixelFormat]; + CGLPixelFormatObj cglPixelFormat = static_cast<CGLPixelFormatObj>([nsglPixelFormat CGLPixelFormatObj]); + + CFTypeRef keys[] = { kQTVisualContextWorkingColorSpaceKey }; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CFDictionaryRef textureContextAttributes = CFDictionaryCreate(kCFAllocatorDefault, + (const void **)keys, + (const void **)&colorSpace, 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + OSStatus err = QTOpenGLTextureContextCreate(kCFAllocatorDefault, + cglContext, + cglPixelFormat, + textureContextAttributes, + &m_visualContext); + if (err != noErr) + qWarning() << "Could not create visual context (OpenGL)"; + + + return (err == noErr); +#endif // QUICKTIME_C_API_AVAILABLE + + return false; +} + +QT7MovieVideoWidget::~QT7MovieVideoWidget() +{ + m_displayLink->stop(); + [(QTMovie*)m_movie release]; + delete m_videoWidget; +} + +QWidget *QT7MovieVideoWidget::videoWidget() +{ + return m_videoWidget; +} + +void QT7MovieVideoWidget::setupVideoOutput() +{ + AutoReleasePool pool; + + qDebug() << "QT7MovieVideoWidget::setupVideoOutput" << m_movie; + + if (m_movie == 0) { + m_displayLink->stop(); + return; + } + + NSSize size = [[(QTMovie*)m_movie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue]; + m_nativeSize = QSize(size.width, size.height); + m_videoWidget->setNativeSize(m_nativeSize); + +#ifdef QUICKTIME_C_API_AVAILABLE + // targets a Movie to render into a visual context + SetMovieVisualContext([(QTMovie*)m_movie quickTimeMovie], m_visualContext); +#endif + + m_displayLink->start(); +} + +void QT7MovieVideoWidget::setEnabled(bool) +{ +} + +void QT7MovieVideoWidget::setMovie(void *movie) +{ + if (m_movie == movie) + return; + + if (m_movie) { +#ifdef QUICKTIME_C_API_AVAILABLE + SetMovieVisualContext([(QTMovie*)m_movie quickTimeMovie], nil); +#endif + [(QTMovie*)m_movie release]; + } + + m_movie = movie; + [(QTMovie*)m_movie retain]; + + setupVideoOutput(); +} + +void QT7MovieVideoWidget::updateNaturalSize(const QSize &newSize) +{ + if (m_nativeSize != newSize) { + m_nativeSize = newSize; + setupVideoOutput(); + } +} + +bool QT7MovieVideoWidget::isFullScreen() const +{ + return m_fullscreen; +} + +void QT7MovieVideoWidget::setFullScreen(bool fullScreen) +{ + m_fullscreen = fullScreen; +} + +QSize QT7MovieVideoWidget::nativeSize() const +{ + return m_nativeSize; +} + +QVideoWidget::AspectRatioMode QT7MovieVideoWidget::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void QT7MovieVideoWidget::setAspectRatioMode(QVideoWidget::AspectRatioMode mode) +{ + m_aspectRatioMode = mode; + m_videoWidget->setAspectRatioMode(mode); +} + +int QT7MovieVideoWidget::brightness() const +{ + return m_brightness; +} + +void QT7MovieVideoWidget::setBrightness(int brightness) +{ + m_brightness = brightness; + updateColors(); +} + +int QT7MovieVideoWidget::contrast() const +{ + return m_contrast; +} + +void QT7MovieVideoWidget::setContrast(int contrast) +{ + m_contrast = contrast; + updateColors(); +} + +int QT7MovieVideoWidget::hue() const +{ + return m_hue; +} + +void QT7MovieVideoWidget::setHue(int hue) +{ + m_hue = hue; + updateColors(); +} + +int QT7MovieVideoWidget::saturation() const +{ + return m_saturation; +} + +void QT7MovieVideoWidget::setSaturation(int saturation) +{ + m_saturation = saturation; + updateColors(); +} + +void QT7MovieVideoWidget::updateColors() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + if (m_movie) { + QTMovie *movie = (QTMovie*)m_movie; + + Float32 value; + value = m_brightness/100.0; + SetMovieVisualBrightness([movie quickTimeMovie], value, 0); + value = pow(2, m_contrast/50.0); + SetMovieVisualContrast([movie quickTimeMovie], value, 0); + value = m_hue/100.0; + SetMovieVisualHue([movie quickTimeMovie], value, 0); + value = 1.0+m_saturation/100.0; + SetMovieVisualSaturation([movie quickTimeMovie], value, 0); + } +#endif +} + +void QT7MovieVideoWidget::updateVideoFrame(const CVTimeStamp &ts) +{ +#ifdef QUICKTIME_C_API_AVAILABLE + AutoReleasePool pool; + // check for new frame + if (m_visualContext && QTVisualContextIsNewImageAvailable(m_visualContext, &ts)) { + CVOpenGLTextureRef currentFrame = NULL; + + // get a "frame" (image buffer) from the Visual Context, indexed by the provided time + OSStatus status = QTVisualContextCopyImageForTime(m_visualContext, NULL, &ts, ¤tFrame); + + // the above call may produce a null frame so check for this first + // if we have a frame, then draw it + if (status == noErr && currentFrame) { + //qDebug() << "render video frame"; + m_videoWidget->setCVTexture(currentFrame); + CVOpenGLTextureRelease(currentFrame); + } + QTVisualContextTask(m_visualContext); + } +#else + Q_UNUSED(ts); +#endif +} + +#include "moc_qt7movievideowidget.cpp" + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/qt7/qt7movieviewoutput.h b/src/plugins/mediaservices/qt7/qt7movieviewoutput.h new file mode 100644 index 0000000..30eefa7 --- /dev/null +++ b/src/plugins/mediaservices/qt7/qt7movieviewoutput.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7MOVIEVIEWOUTPUT_H +#define QT7MOVIEVIEWOUTPUT_H + +#include <QtCore/qobject.h> + +#include <QtMultimedia/qvideowindowcontrol.h> +#include <QtMultimedia/qmediaplayer.h> + +#include <QtGui/qmacdefines_mac.h> +#include "qt7videooutputcontrol.h" + + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +class QT7PlayerSession; +class QT7PlayerService; + +class QT7MovieViewOutput : public QT7VideoWindowControl +{ +public: + QT7MovieViewOutput(QObject *parent = 0); + ~QT7MovieViewOutput(); + + void setEnabled(bool); + void setMovie(void *movie); + void updateNaturalSize(const QSize &newSize); + + WId winId() const; + void setWinId(WId id); + + QRect displayRect() const; + void setDisplayRect(const QRect &rect); + + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + void repaint(); + + QSize nativeSize() const; + + QVideoWidget::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(QVideoWidget::AspectRatioMode mode); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + +private: + void setupVideoOutput(); + + void *m_movie; + void *m_movieView; + + WId m_winId; + QRect m_displayRect; + bool m_fullscreen; + QSize m_nativeSize; + QVideoWidget::AspectRatioMode m_aspectRatioMode; + int m_brightness; + int m_contrast; + int m_hue; + int m_saturation; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/qt7/qt7movieviewoutput.mm b/src/plugins/mediaservices/qt7/qt7movieviewoutput.mm new file mode 100644 index 0000000..b549487 --- /dev/null +++ b/src/plugins/mediaservices/qt7/qt7movieviewoutput.mm @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import <QTKit/QTKit.h> + +#include "qt7backend.h" + +#include "qt7playercontrol.h" +#include "qt7movieviewoutput.h" +#include "qt7playersession.h" +#include <QtCore/qdebug.h> + + +QT_BEGIN_NAMESPACE + +#define VIDEO_TRANSPARENT(m) -(void)m:(NSEvent *)e{[[self superview] m:e];} + +@interface TransparentQTMovieView : QTMovieView +{ +@private + QRect *m_drawRect; + qreal m_brightness, m_contrast, m_saturation, m_hue; +} + +- (TransparentQTMovieView *) init; +- (void) setDrawRect:(QRect &)rect; +- (void) setContrast:(qreal) contrast; +@end + +@implementation TransparentQTMovieView + +- (TransparentQTMovieView *) init +{ + self = [super initWithFrame:NSZeroRect]; + if (self) { + [self setControllerVisible:NO]; + [self setContrast:1.0]; + [self setDelegate:self]; + } + return self; +} + +- (void) dealloc +{ + [super dealloc]; +} + +- (void) setContrast:(qreal) contrast +{ + m_hue = 0.0; + m_brightness = 0.0; + m_contrast = contrast; + m_saturation = 1.0; +} + + +- (void) setDrawRect:(QRect &)rect +{ + *m_drawRect = rect; + + NSRect nsrect; + nsrect.origin.x = m_drawRect->x(); + nsrect.origin.y = m_drawRect->y(); + nsrect.size.width = m_drawRect->width(); + nsrect.size.height = m_drawRect->height(); + [self setFrame:nsrect]; +} + +- (CIImage *) view:(QTMovieView *)view willDisplayImage:(CIImage *)img +{ + // This method is called from QTMovieView just + // before the image will be drawn. + Q_UNUSED(view); + + if ( !qFuzzyCompare(m_brightness, 0.0) || + !qFuzzyCompare(m_contrast, 1.0) || + !qFuzzyCompare(m_saturation, 1.0)){ + CIFilter *colorFilter = [CIFilter filterWithName:@"CIColorControls"]; + [colorFilter setValue:[NSNumber numberWithFloat:m_brightness] forKey:@"inputBrightness"]; + [colorFilter setValue:[NSNumber numberWithFloat:(m_contrast < 1) ? m_contrast : 1 + ((m_contrast-1)*3)] forKey:@"inputContrast"]; + [colorFilter setValue:[NSNumber numberWithFloat:m_saturation] forKey:@"inputSaturation"]; + [colorFilter setValue:img forKey:@"inputImage"]; + img = [colorFilter valueForKey:@"outputImage"]; + } + + /*if (m_hue){ + CIFilter *colorFilter = [CIFilter filterWithName:@"CIHueAdjust"]; + [colorFilter setValue:[NSNumber numberWithFloat:(m_hue * 3.14)] forKey:@"inputAngle"]; + [colorFilter setValue:img forKey:@"inputImage"]; + img = [colorFilter valueForKey:@"outputImage"]; + }*/ + + return img; +} + + +VIDEO_TRANSPARENT(mouseDown); +VIDEO_TRANSPARENT(mouseDragged); +VIDEO_TRANSPARENT(mouseUp); +VIDEO_TRANSPARENT(mouseMoved); +VIDEO_TRANSPARENT(mouseEntered); +VIDEO_TRANSPARENT(mouseExited); +VIDEO_TRANSPARENT(rightMouseDown); +VIDEO_TRANSPARENT(rightMouseDragged); +VIDEO_TRANSPARENT(rightMouseUp); +VIDEO_TRANSPARENT(otherMouseDown); +VIDEO_TRANSPARENT(otherMouseDragged); +VIDEO_TRANSPARENT(otherMouseUp); +VIDEO_TRANSPARENT(keyDown); +VIDEO_TRANSPARENT(keyUp); +VIDEO_TRANSPARENT(scrollWheel) + +@end + + +QT7MovieViewOutput::QT7MovieViewOutput(QObject *parent) + :QT7VideoWindowControl(parent), + m_movie(0), + m_movieView(0), + m_winId(0), + m_fullscreen(false), + m_aspectRatioMode(QVideoWidget::KeepAspectRatio), + m_brightness(0), + m_contrast(0), + m_hue(0), + m_saturation(0) +{ +} + +QT7MovieViewOutput::~QT7MovieViewOutput() +{ +} + +void QT7MovieViewOutput::setupVideoOutput() +{ + AutoReleasePool pool; + + //qDebug() << "QT7MovieViewOutput::setupVideoOutput" << m_movie << m_winId; + if (m_movie == 0 || m_winId <= 0) + return; + + NSSize size = [[(QTMovie*)m_movie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue]; + m_nativeSize = QSize(size.width, size.height); + + if (!m_movieView) + m_movieView = [[TransparentQTMovieView alloc] init]; + + [(QTMovieView*)m_movieView setControllerVisible:NO]; + [(QTMovieView*)m_movieView setMovie:(QTMovie*)m_movie]; + + [(NSView *)m_winId addSubview:(QTMovieView*)m_movieView]; + + setDisplayRect(m_displayRect); +} + +void QT7MovieViewOutput::setEnabled(bool) +{ +} + +void QT7MovieViewOutput::setMovie(void *movie) +{ + m_movie = movie; + setupVideoOutput(); +} + +void QT7MovieViewOutput::updateNaturalSize(const QSize &newSize) +{ + m_nativeSize = newSize; +} + +WId QT7MovieViewOutput::winId() const +{ + return m_winId; +} + +void QT7MovieViewOutput::setWinId(WId id) +{ + m_winId = id; + setupVideoOutput(); +} + +QRect QT7MovieViewOutput::displayRect() const +{ + return m_displayRect; +} + +void QT7MovieViewOutput::setDisplayRect(const QRect &rect) +{ + m_displayRect = rect; + + if (m_movieView) { + AutoReleasePool pool; + [(QTMovieView*)m_movieView setPreservesAspectRatio:(m_aspectRatioMode == QVideoWidget::KeepAspectRatio ? YES : NO)]; + [(QTMovieView*)m_movieView setFrame:NSMakeRect(m_displayRect.x(), + m_displayRect.y(), + m_displayRect.width(), + m_displayRect.height())]; + } + +} + +bool QT7MovieViewOutput::isFullScreen() const +{ + return m_fullscreen; +} + +void QT7MovieViewOutput::setFullScreen(bool fullScreen) +{ + m_fullscreen = fullScreen; + setDisplayRect(m_displayRect); +} + +void QT7MovieViewOutput::repaint() +{ +} + +QSize QT7MovieViewOutput::nativeSize() const +{ + return m_nativeSize; +} + +QVideoWidget::AspectRatioMode QT7MovieViewOutput::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void QT7MovieViewOutput::setAspectRatioMode(QVideoWidget::AspectRatioMode mode) +{ + m_aspectRatioMode = mode; + setDisplayRect(m_displayRect); +} + +int QT7MovieViewOutput::brightness() const +{ + return m_brightness; +} + +void QT7MovieViewOutput::setBrightness(int brightness) +{ + m_brightness = brightness; +} + +int QT7MovieViewOutput::contrast() const +{ + return m_contrast; +} + +void QT7MovieViewOutput::setContrast(int contrast) +{ + m_contrast = contrast; + [(TransparentQTMovieView*)m_movieView setContrast:(contrast/100.0+1.0)]; +} + +int QT7MovieViewOutput::hue() const +{ + return m_hue; +} + +void QT7MovieViewOutput::setHue(int hue) +{ + m_hue = hue; +} + +int QT7MovieViewOutput::saturation() const +{ + return m_saturation; +} + +void QT7MovieViewOutput::setSaturation(int saturation) +{ + m_saturation = saturation; +} + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/qt7/qt7movieviewrenderer.h b/src/plugins/mediaservices/qt7/qt7movieviewrenderer.h new file mode 100644 index 0000000..336006c --- /dev/null +++ b/src/plugins/mediaservices/qt7/qt7movieviewrenderer.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7MOVIEVIEWRENDERER_H +#define QT7MOVIEVIEWRENDERER_H + +#include <QtCore/qobject.h> +#include <QtCore/qmutex.h> + +#include <QtMultimedia/qvideowindowcontrol.h> +#include <QtMultimedia/qmediaplayer.h> + +#include <QtGui/qmacdefines_mac.h> +#include "qt7videooutputcontrol.h" +#include <QtMultimedia/qvideoframe.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QVideoFrame; +class QT7PlayerSession; +class QT7PlayerService; + +class QT7MovieViewRenderer : public QT7VideoRendererControl +{ +public: + QT7MovieViewRenderer(QObject *parent = 0); + ~QT7MovieViewRenderer(); + + void setEnabled(bool); + void setMovie(void *movie); + void updateNaturalSize(const QSize &newSize); + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + + void renderFrame(const QVideoFrame &); + +protected: + bool event(QEvent *event); + +private: + void setupVideoOutput(); + + void *m_movie; + void *m_movieView; + QSize m_nativeSize; + QAbstractVideoSurface *m_surface; + QVideoFrame m_currentFrame; + QMutex m_mutex; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/qt7/qt7movieviewrenderer.mm b/src/plugins/mediaservices/qt7/qt7movieviewrenderer.mm new file mode 100644 index 0000000..342feb8 --- /dev/null +++ b/src/plugins/mediaservices/qt7/qt7movieviewrenderer.mm @@ -0,0 +1,349 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import <QTKit/QTKit.h> + +#include "qt7backend.h" + +#include "qt7playercontrol.h" +#include "qt7movieviewrenderer.h" +#include "qt7playersession.h" +#include <QtCore/qdebug.h> +#include <QtCore/qcoreevent.h> +#include <QtCore/qcoreapplication.h> + +#include <QtMultimedia/qabstractvideobuffer.h> +#include <QtMultimedia/qabstractvideosurface.h> +#include <QtMultimedia/qvideosurfaceformat.h> + + +QT_BEGIN_NAMESPACE + +class NSBitmapVideoBuffer : public QAbstractVideoBuffer +{ +public: + NSBitmapVideoBuffer(NSBitmapImageRep *buffer) + : QAbstractVideoBuffer(NoHandle) + , m_buffer(buffer) + , m_mode(NotMapped) + { + [m_buffer retain]; + } + + virtual ~NSBitmapVideoBuffer() + { + [m_buffer release]; + } + + MapMode mapMode() const { return m_mode; } + + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) + { + if (mode != NotMapped && m_mode == NotMapped) { + if (numBytes) + *numBytes = [m_buffer bytesPerPlane]; + + if (bytesPerLine) + *bytesPerLine = [m_buffer bytesPerRow]; + + m_mode = mode; + + return [m_buffer bitmapData]; + } else { + return 0; + } + } + + void unmap() { m_mode = NotMapped; } + +private: + NSBitmapImageRep *m_buffer; + MapMode m_mode; +}; + + +#define VIDEO_TRANSPARENT(m) -(void)m:(NSEvent *)e{[[self superview] m:e];} + +@interface HiddenQTMovieView : QTMovieView +{ +@private + QWidget *m_window; + QT7MovieViewRenderer *m_renderer; +} + +- (HiddenQTMovieView *) initWithRenderer:(QT7MovieViewRenderer *)renderer; +- (void) setRenderer:(QT7MovieViewRenderer *)renderer; +- (void) setDrawRect:(const QRect &)rect; +@end + +@implementation HiddenQTMovieView + +- (HiddenQTMovieView *) initWithRenderer:(QT7MovieViewRenderer *)renderer +{ + self = [super initWithFrame:NSZeroRect]; + if (self) { + [self setControllerVisible:NO]; + [self setDelegate:self]; + + self->m_renderer = renderer; + + self->m_window = new QWidget; + self->m_window->setWindowOpacity(0.0); + self->m_window->show(); + self->m_window->hide(); + + [(NSView *)(self->m_window->winId()) addSubview:self]; + [self setDrawRect:QRect(0,0,1,1)]; + } + return self; +} + +- (void) dealloc +{ + [super dealloc]; +} + +- (void) setRenderer:(QT7MovieViewRenderer *)renderer +{ + m_renderer = renderer; +} + +- (void) setDrawRect:(const QRect &)rect +{ + NSRect nsrect; + nsrect.origin.x = rect.x(); + nsrect.origin.y = rect.y(); + nsrect.size.width = rect.width(); + nsrect.size.height = rect.height(); + [self setFrame:nsrect]; +} + +- (CIImage *) view:(QTMovieView *)view willDisplayImage:(CIImage *)img +{ + // This method is called from QTMovieView just + // before the image will be drawn. + Q_UNUSED(view); + if (m_renderer) { + NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithCIImage:img]; + CGRect bounds = [img extent]; + int w = bounds.size.width; + int h = bounds.size.height; + + // Swap red and blue (same as QImage::rgbSwapped, but without copy) + uchar *data = [bitmap bitmapData]; + //qDebug() << data << w << h; + int bytesPerLine = [bitmap bytesPerRow]; + for (int i=0; i<h; ++i) { + quint32 *p = (quint32*)data; + data += bytesPerLine; + quint32 *end = p + w; + while (p < end) { + *p = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00); + p++; + } + } + + QVideoFrame frame( new NSBitmapVideoBuffer(bitmap), QSize(w,h), QVideoFrame::Format_RGB32 ); + + //static int i=0; + //i++; + //QImage img([bitmap bitmapData], w, h, QImage::Format_RGB32); + //img.save(QString("img%1.jpg").arg(i)); + + [bitmap release]; + + if (m_renderer) + m_renderer->renderFrame(frame); + } + + return img; +} + +// Override this method so that the movie doesn't stop if +// the window becomes invisible +- (void)viewWillMoveToWindow:(NSWindow *)newWindow +{ + Q_UNUSED(newWindow); +} + + +VIDEO_TRANSPARENT(mouseDown); +VIDEO_TRANSPARENT(mouseDragged); +VIDEO_TRANSPARENT(mouseUp); +VIDEO_TRANSPARENT(mouseMoved); +VIDEO_TRANSPARENT(mouseEntered); +VIDEO_TRANSPARENT(mouseExited); +VIDEO_TRANSPARENT(rightMouseDown); +VIDEO_TRANSPARENT(rightMouseDragged); +VIDEO_TRANSPARENT(rightMouseUp); +VIDEO_TRANSPARENT(otherMouseDown); +VIDEO_TRANSPARENT(otherMouseDragged); +VIDEO_TRANSPARENT(otherMouseUp); +VIDEO_TRANSPARENT(keyDown); +VIDEO_TRANSPARENT(keyUp); +VIDEO_TRANSPARENT(scrollWheel) + +@end + + +QT7MovieViewRenderer::QT7MovieViewRenderer(QObject *parent) + :QT7VideoRendererControl(parent), + m_movie(0), + m_movieView(0), + m_surface(0) +{ +} + +QT7MovieViewRenderer::~QT7MovieViewRenderer() +{ + [(HiddenQTMovieView*)m_movieView setRenderer:0]; + + QMutexLocker locker(&m_mutex); + m_currentFrame = QVideoFrame(); + [(HiddenQTMovieView*)m_movieView release]; +} + +void QT7MovieViewRenderer::setupVideoOutput() +{ + AutoReleasePool pool; + + qDebug() << "QT7MovieViewRenderer::setupVideoOutput" << m_movie << m_surface; + + HiddenQTMovieView *movieView = (HiddenQTMovieView*)m_movieView; + + if (movieView && !m_movie) { + [movieView setMovie:nil]; + } + + if (m_movie) { + NSSize size = [[(QTMovie*)m_movie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue]; + + m_nativeSize = QSize(size.width, size.height); + + if (!movieView) { + movieView = [[HiddenQTMovieView alloc] initWithRenderer:this]; + m_movieView = movieView; + [movieView setControllerVisible:NO]; + } + + [movieView setMovie:(QTMovie*)m_movie]; + //[movieView setDrawRect:QRect(QPoint(0,0), m_nativeSize)]; + } + + if (m_surface && !m_nativeSize.isEmpty()) { + QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32); + + if (m_surface->isActive() && m_surface->surfaceFormat() != format) { + qDebug() << "Surface format was changed, stop the surface."; + m_surface->stop(); + } + + if (!m_surface->isActive()) { + qDebug() << "Starting the surface with format" << format; + if (!m_surface->start(format)) + qDebug() << "failed to start video surface" << m_surface->error(); + } + } +} + +void QT7MovieViewRenderer::setEnabled(bool) +{ +} + +void QT7MovieViewRenderer::setMovie(void *movie) +{ + if (movie == m_movie) + return; + + QMutexLocker locker(&m_mutex); + m_movie = movie; + setupVideoOutput(); +} + +void QT7MovieViewRenderer::updateNaturalSize(const QSize &newSize) +{ + if (m_nativeSize != newSize) { + m_nativeSize = newSize; + setupVideoOutput(); + } +} + +QAbstractVideoSurface *QT7MovieViewRenderer::surface() const +{ + return m_surface; +} + +void QT7MovieViewRenderer::setSurface(QAbstractVideoSurface *surface) +{ + if (surface == m_surface) + return; + + QMutexLocker locker(&m_mutex); + + if (m_surface && m_surface->isActive()) + m_surface->stop(); + + m_surface = surface; + setupVideoOutput(); +} + +void QT7MovieViewRenderer::renderFrame(const QVideoFrame &frame) +{ + { + QMutexLocker locker(&m_mutex); + m_currentFrame = frame; + } + + qApp->postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority); +} + +bool QT7MovieViewRenderer::event(QEvent *event) +{ + if (event->type() == QEvent::User) { + QMutexLocker locker(&m_mutex); + if (m_surface->isActive()) + m_surface->present(m_currentFrame); + } + + return QT7VideoRendererControl::event(event); +} + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/qt7/qt7serviceplugin.h b/src/plugins/mediaservices/qt7/qt7serviceplugin.h new file mode 100644 index 0000000..c5afda1 --- /dev/null +++ b/src/plugins/mediaservices/qt7/qt7serviceplugin.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QT7SERVICEPLUGIN_H +#define QT7SERVICEPLUGIN_H + +#include <QtMultimedia/qmediaserviceproviderplugin.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QT7ServicePlugin : public QMediaServiceProviderPlugin +{ +public: + QStringList keys() const; + QMediaService* create(QString const& key); + void release(QMediaService *service); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGSTREAMERSERVICEPLUGIN_H diff --git a/src/plugins/mediaservices/qt7/qt7serviceplugin.mm b/src/plugins/mediaservices/qt7/qt7serviceplugin.mm new file mode 100644 index 0000000..c59a453 --- /dev/null +++ b/src/plugins/mediaservices/qt7/qt7serviceplugin.mm @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> +#include <QtCore/qdebug.h> + +#include "qt7serviceplugin.h" +#include "qt7playerservice.h" + +#include <QtMultimedia/qmediaserviceprovider.h> + +QT_BEGIN_NAMESPACE + +QStringList QT7ServicePlugin::keys() const +{ + return QStringList() +#ifdef QMEDIA_QT7_PLAYER + << QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER); +#endif +} + +QMediaService* QT7ServicePlugin::create(QString const& key) +{ +#ifdef QMEDIA_QT7_PLAYER + if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) + return new QT7PlayerService; +#endif + + qWarning() << "Attempt to create unknown service with key" << key; + return 0; +} + +void QT7ServicePlugin::release(QMediaService *service) +{ + delete service; +} + +Q_EXPORT_PLUGIN2(qt7_serviceplugin, QT7ServicePlugin); + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/qt7/qt7videooutputcontrol.h b/src/plugins/mediaservices/qt7/qt7videooutputcontrol.h new file mode 100644 index 0000000..2c60919 --- /dev/null +++ b/src/plugins/mediaservices/qt7/qt7videooutputcontrol.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7VIDEOOUTPUTCONTROL_H +#define QT7VIDEOOUTPUTCONTROL_H + +#include <QtCore/qobject.h> +#include <QtCore/qsize.h> + +#include <QtMultimedia/qvideooutputcontrol.h> +#include <QtMultimedia/qvideowindowcontrol.h> +#include <QtMultimedia/qvideowidgetcontrol.h> +#include <QtMultimedia/qvideorenderercontrol.h> +#include <QtMultimedia/qmediaplayer.h> + +#include <QtGui/qmacdefines_mac.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QMediaPlaylist; +class QMediaPlaylistNavigator; +class QT7PlayerSession; +class QT7PlayerService; + + +class QT7VideoOutput { +public: + virtual ~QT7VideoOutput() {} + virtual void setEnabled(bool enabled) = 0; + virtual void setMovie(void *movie) = 0; + virtual void updateNaturalSize(const QSize &newSize) = 0; +}; + +class QT7VideoWindowControl : public QVideoWindowControl, public QT7VideoOutput +{ +public: + virtual ~QT7VideoWindowControl() {} + +protected: + QT7VideoWindowControl(QObject *parent) + :QVideoWindowControl(parent) + {} +}; + +class QT7VideoRendererControl : public QVideoRendererControl, public QT7VideoOutput +{ +public: + virtual ~QT7VideoRendererControl() {} + +protected: + QT7VideoRendererControl(QObject *parent) + :QVideoRendererControl(parent) + {} +}; + +class QT7VideoWidgetControl : public QVideoWidgetControl, public QT7VideoOutput +{ +public: + virtual ~QT7VideoWidgetControl() {} + +protected: + QT7VideoWidgetControl(QObject *parent) + :QVideoWidgetControl(parent) + {} +}; + +class QT7VideoOutputControl : public QVideoOutputControl +{ +Q_OBJECT +public: + QT7VideoOutputControl(QObject *parent = 0); + ~QT7VideoOutputControl(); + + void setSession(QT7PlayerSession *session); + + QList<Output> availableOutputs() const; + void enableOutput(Output); + + Output output() const; + void setOutput(Output output); + +signals: + void videoOutputChanged(QVideoOutputControl::Output); + +private: + QT7PlayerSession *m_session; + Output m_output; + QList<Output> m_outputs; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/qt7/qt7videooutputcontrol.mm b/src/plugins/mediaservices/qt7/qt7videooutputcontrol.mm new file mode 100644 index 0000000..a468431 --- /dev/null +++ b/src/plugins/mediaservices/qt7/qt7videooutputcontrol.mm @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qt7playercontrol.h" +#include "qt7videooutputcontrol.h" +#include "qt7playersession.h" +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +QT7VideoOutputControl::QT7VideoOutputControl(QObject *parent) + :QVideoOutputControl(parent), + m_session(0), + m_output(QVideoOutputControl::NoOutput) +{ +} + +QT7VideoOutputControl::~QT7VideoOutputControl() +{ +} + +void QT7VideoOutputControl::setSession(QT7PlayerSession *session) +{ + m_session = session; +} + +QList<QVideoOutputControl::Output> QT7VideoOutputControl::availableOutputs() const +{ + return m_outputs; +} + +void QT7VideoOutputControl::enableOutput(QVideoOutputControl::Output output) +{ + if (!m_outputs.contains(output)) + m_outputs.append(output); +} + +QVideoOutputControl::Output QT7VideoOutputControl::output() const +{ + return m_output; +} + +void QT7VideoOutputControl::setOutput(Output output) +{ + if (m_output != output) { + m_output = output; + emit videoOutputChanged(m_output); + } + +} + +QT_END_NAMESPACE + +#include "moc_qt7videooutputcontrol.cpp" + |