diff options
author | Michael Goddard <michael.goddard@nokia.com> | 2011-06-29 13:38:46 +1000 |
---|---|---|
committer | Michael Goddard <michael.goddard@nokia.com> | 2011-06-29 13:38:46 +1000 |
commit | 2a34e88c1e1ced28e75c487cd13402e1c9cf9fa3 (patch) | |
tree | e6c1b770c5c47212792a1f9344fa034ea3e54c44 /src/plugins/directshow/player |
Initial copy of QtMultimediaKit.
Comes from original repo, with SHA1:
2c82d5611655e5967f5c5095af50c0991c4378b2
Diffstat (limited to 'src/plugins/directshow/player')
32 files changed, 7557 insertions, 0 deletions
diff --git a/src/plugins/directshow/player/directshowaudioendpointcontrol.cpp b/src/plugins/directshow/player/directshowaudioendpointcontrol.cpp new file mode 100644 index 000000000..01c879732 --- /dev/null +++ b/src/plugins/directshow/player/directshowaudioendpointcontrol.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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" + +DirectShowAudioEndpointControl::DirectShowAudioEndpointControl( + DirectShowPlayerService *service, QObject *parent) + : QAudioEndpointSelector(parent) + , m_service(service) + , m_bindContext(0) + , m_deviceEnumerator(0) +{ + if (CreateBindCtx(0, &m_bindContext) == S_OK) { + m_deviceEnumerator = com_new<ICreateDevEnum>(CLSID_SystemDeviceEnum, IID_ICreateDevEnum); + + 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 +{ +#ifdef __IPropertyBag_INTERFACE_DEFINED__ + 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; +#else + return name.section(QLatin1Char('\\'), -1); +#endif +} + +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, + IID_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(); + } +} diff --git a/src/plugins/directshow/player/directshowaudioendpointcontrol.h b/src/plugins/directshow/player/directshowaudioendpointcontrol.h new file mode 100644 index 000000000..62a0bd47f --- /dev/null +++ b/src/plugins/directshow/player/directshowaudioendpointcontrol.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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 "qaudioendpointselector.h" + +#include <dshow.h> + +class DirectShowPlayerService; + +QT_USE_NAMESPACE + +class DirectShowAudioEndpointControl : public QAudioEndpointSelector +{ + 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; +}; + +#endif + diff --git a/src/plugins/directshow/player/directshoweventloop.cpp b/src/plugins/directshow/player/directshoweventloop.cpp new file mode 100644 index 000000000..f863aa835 --- /dev/null +++ b/src/plugins/directshow/player/directshoweventloop.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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> + +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) + : QObject(parent) + , m_postsHead(0) + , m_postsTail(0) + , m_eventHandle(::CreateEvent(0, 0, 0, 0)) + , m_waitHandle(::CreateEvent(0, 0, 0, 0)) +{ +} + +DirectShowEventLoop::~DirectShowEventLoop() +{ + ::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; + + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); + ::SetEvent(m_eventHandle); +} + +void DirectShowEventLoop::customEvent(QEvent *event) +{ + if (event->type() == QEvent::User) { + processEvents(); + } else { + QObject::customEvent(event); + } +} + +void DirectShowEventLoop::processEvents() +{ + QMutexLocker locker(&m_mutex); + + ::ResetEvent(m_eventHandle); + + while(m_postsHead) { + 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(); + } +} diff --git a/src/plugins/directshow/player/directshoweventloop.h b/src/plugins/directshow/player/directshoweventloop.h new file mode 100644 index 000000000..6f1a51cd5 --- /dev/null +++ b/src/plugins/directshow/player/directshoweventloop.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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/qobject.h> +#include <QtCore/qwaitcondition.h> + +#include <windows.h> + +class DirectShowPostedEvent; + +class DirectShowEventLoop : public QObject +{ + Q_OBJECT +public: + DirectShowEventLoop(QObject *parent = 0); + ~DirectShowEventLoop(); + + void wait(QMutex *mutex); + void wake(); + + void postEvent(QObject *object, QEvent *event); + +protected: + void customEvent(QEvent *event); + +private: + void processEvents(); + + DirectShowPostedEvent *m_postsHead; + DirectShowPostedEvent *m_postsTail; + HANDLE m_eventHandle; + HANDLE m_waitHandle; + QMutex m_mutex; +}; + +#endif diff --git a/src/plugins/directshow/player/directshowglobal.h b/src/plugins/directshow/player/directshowglobal.h new file mode 100644 index 000000000..d14c117ec --- /dev/null +++ b/src/plugins/directshow/player/directshowglobal.h @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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 <QtCore/qglobal.h> + +#include <dshow.h> + +template <typename T> T *com_cast(IUnknown *unknown, const IID &iid) +{ + T *iface = 0; + return unknown && unknown->QueryInterface(iid, reinterpret_cast<void **>(&iface)) == S_OK + ? iface + : 0; +} + +template <typename T> T *com_new(const IID &clsid, const IID &iid) +{ + T *object = 0; + return CoCreateInstance( + clsid, + NULL, + CLSCTX_INPROC_SERVER, + iid, + reinterpret_cast<void **>(&object)) == S_OK + ? object + : 0; +} + +#ifndef __IFilterGraph2_INTERFACE_DEFINED__ +#define __IFilterGraph2_INTERFACE_DEFINED__ +#define INTERFACE IFilterGraph2 +DECLARE_INTERFACE_(IFilterGraph2 ,IGraphBuilder) +{ + STDMETHOD(AddSourceFilterForMoniker)(THIS_ IMoniker *, IBindCtx *, LPCWSTR,IBaseFilter **) PURE; + STDMETHOD(ReconnectEx)(THIS_ IPin *, const AM_MEDIA_TYPE *) PURE; + STDMETHOD(RenderEx)(IPin *, DWORD, DWORD *) PURE; +}; +#undef INTERFACE +#endif + +#ifndef __IAMFilterMiscFlags_INTERFACE_DEFINED__ +#define __IAMFilterMiscFlags_INTERFACE_DEFINED__ +#define INTERFACE IAMFilterMiscFlags +DECLARE_INTERFACE_(IAMFilterMiscFlags ,IUnknown) +{ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + STDMETHOD_(ULONG,GetMiscFlags)(THIS) PURE; +}; +#undef INTERFACE +#endif + +#ifndef __IFileSourceFilter_INTERFACE_DEFINED__ +#define __IFileSourceFilter_INTERFACE_DEFINED__ +#define INTERFACE IFileSourceFilter +DECLARE_INTERFACE_(IFileSourceFilter ,IUnknown) +{ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + STDMETHOD(Load)(THIS_ LPCOLESTR, const AM_MEDIA_TYPE *) PURE; + STDMETHOD(GetCurFile)(THIS_ LPOLESTR *ppszFileName, AM_MEDIA_TYPE *) PURE; +}; +#undef INTERFACE +#endif + +#ifndef __IAMOpenProgress_INTERFACE_DEFINED__ +#define __IAMOpenProgress_INTERFACE_DEFINED__ +#define INTERFACE IAMOpenProgress +DECLARE_INTERFACE_(IAMOpenProgress ,IUnknown) +{ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + STDMETHOD(QueryProgress)(THIS_ LONGLONG *, LONGLONG *) PURE; + STDMETHOD(AbortOperation)(THIS) PURE; +}; +#undef INTERFACE +#endif + +#ifndef __IFilterChain_INTERFACE_DEFINED__ +#define __IFilterChain_INTERFACE_DEFINED__ +#define INTERFACE IFilterChain +DECLARE_INTERFACE_(IFilterChain ,IUnknown) +{ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + STDMETHOD(StartChain)(IBaseFilter *, IBaseFilter *) PURE; + STDMETHOD(PauseChain)(IBaseFilter *, IBaseFilter *) PURE; + STDMETHOD(StopChain)(IBaseFilter *, IBaseFilter *) PURE; + STDMETHOD(RemoveChain)(IBaseFilter *, IBaseFilter *) PURE; +}; +#undef INTERFACE +#endif + +#endif diff --git a/src/plugins/directshow/player/directshowioreader.cpp b/src/plugins/directshow/player/directshowioreader.cpp new file mode 100644 index 000000000..5442f30d9 --- /dev/null +++ b/src/plugins/directshow/player/directshowioreader.cpp @@ -0,0 +1,496 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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> + +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, IID_IMemAllocator); + + 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; + } +} diff --git a/src/plugins/directshow/player/directshowioreader.h b/src/plugins/directshow/player/directshowioreader.h new file mode 100644 index 000000000..ca398f12e --- /dev/null +++ b/src/plugins/directshow/player/directshowioreader.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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_NAMESPACE +class QIODevice; +QT_END_NAMESPACE + +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; +}; + +#endif diff --git a/src/plugins/directshow/player/directshowiosource.cpp b/src/plugins/directshow/player/directshowiosource.cpp new file mode 100644 index 000000000..c8f3e6a97 --- /dev/null +++ b/src/plugins/directshow/player/directshowiosource.cpp @@ -0,0 +1,639 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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> + +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) +{ + // 2dd74950-a890-11d1-abe8-00a0c905f375 + static const GUID iid_IAmFilterMiscFlags = { + 0x2dd74950, 0xa890, 0x11d1, {0xab, 0xe8, 0x00, 0xa0, 0xc9, 0x05, 0xf3, 0x75}}; + + 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, IID_IMemInputPin)) { + if ((m_allocator = com_new<IMemAllocator>(CLSID_MemoryAllocator, IID_IMemAllocator))) { + 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; + } +} + +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/directshow/player/directshowiosource.h b/src/plugins/directshow/player/directshowiosource.h new file mode 100644 index 000000000..3019bdbe9 --- /dev/null +++ b/src/plugins/directshow/player/directshowiosource.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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 "directshowglobal.h" +#include "directshowioreader.h" +#include "directshowmediatype.h" +#include "directshowmediatypelist.h" + +#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; +}; + +#endif diff --git a/src/plugins/directshow/player/directshowmediatype.cpp b/src/plugins/directshow/player/directshowmediatype.cpp new file mode 100644 index 000000000..667d9491e --- /dev/null +++ b/src/plugins/directshow/player/directshowmediatype.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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" + +namespace +{ + struct TypeLookup + { + QVideoFrame::PixelFormat pixelFormat; + GUID mediaType; + }; + + static const TypeLookup qt_typeLookup[] = + { + { QVideoFrame::Format_RGB32, /*MEDIASUBTYPE_RGB32*/ {0xe436eb7e, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}} }, + { QVideoFrame::Format_BGR24, /*MEDIASUBTYPE_RGB24*/ {0xe436eb7d, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}} }, + { QVideoFrame::Format_RGB565, /*MEDIASUBTYPE_RGB565*/ {0xe436eb7b, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}} }, + { QVideoFrame::Format_RGB555, /*MEDIASUBTYPE_RGB555*/ {0xe436eb7c, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}} }, + { QVideoFrame::Format_AYUV444, /*MEDIASUBTYPE_AYUV*/ {0x56555941, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, + { QVideoFrame::Format_YUYV, /*MEDIASUBTYPE_YUY2*/ {0x32595559, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, + { QVideoFrame::Format_UYVY, /*MEDIASUBTYPE_UYVY*/ {0x59565955, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, + { QVideoFrame::Format_IMC1, /*MEDIASUBTYPE_IMC1*/ {0x31434D49, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, + { QVideoFrame::Format_IMC2, /*MEDIASUBTYPE_IMC2*/ {0x32434D49, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, + { QVideoFrame::Format_IMC3, /*MEDIASUBTYPE_IMC3*/ {0x33434D49, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, + { QVideoFrame::Format_IMC4, /*MEDIASUBTYPE_IMC4*/ {0x34434D49, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, + { QVideoFrame::Format_YV12, /*MEDIASUBTYPE_YV12*/ {0x32315659, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, + { QVideoFrame::Format_NV12, /*MEDIASUBTYPE_NV12*/ {0x3231564E, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, + { QVideoFrame::Format_YUV420P, /*MEDIASUBTYPE_IYUV*/ {0x56555949, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} } + }; +} + +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) +{ + // MEDIASUBTYPE_None; + static const GUID none = { + 0xe436eb8e, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70} }; + + 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 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; + } +} diff --git a/src/plugins/directshow/player/directshowmediatype.h b/src/plugins/directshow/player/directshowmediatype.h new file mode 100644 index 000000000..7d37a5c29 --- /dev/null +++ b/src/plugins/directshow/player/directshowmediatype.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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 <qvideosurfaceformat.h> + +#include <dshow.h> +#include <dvdmedia.h> + +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); +}; + +#endif diff --git a/src/plugins/directshow/player/directshowmediatypelist.cpp b/src/plugins/directshow/player/directshowmediatypelist.cpp new file mode 100644 index 000000000..3060975fc --- /dev/null +++ b/src/plugins/directshow/player/directshowmediatypelist.cpp @@ -0,0 +1,226 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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" + + +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; + } +} + diff --git a/src/plugins/directshow/player/directshowmediatypelist.h b/src/plugins/directshow/player/directshowmediatypelist.h new file mode 100644 index 000000000..1b33606cb --- /dev/null +++ b/src/plugins/directshow/player/directshowmediatypelist.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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> + +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; +}; + +#endif diff --git a/src/plugins/directshow/player/directshowmetadatacontrol.cpp b/src/plugins/directshow/player/directshowmetadatacontrol.cpp new file mode 100644 index 000000000..40c3920b4 --- /dev/null +++ b/src/plugins/directshow/player/directshowmetadatacontrol.cpp @@ -0,0 +1,352 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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> + +#ifndef QT_NO_WMSDK +namespace +{ + struct QWMMetaDataKeyLookup + { + QtMultimediaKit::MetaData key; + const wchar_t *token; + }; +} + +static const QWMMetaDataKeyLookup qt_wmMetaDataKeys[] = +{ + { QtMultimediaKit::Title, L"Title" }, + { QtMultimediaKit::SubTitle, L"WM/SubTitle" }, + { QtMultimediaKit::Author, L"Author" }, + { QtMultimediaKit::Comment, L"Comment" }, + { QtMultimediaKit::Description, L"Description" }, + { QtMultimediaKit::Category, L"WM/Category" }, + { QtMultimediaKit::Genre, L"WM/Genre" }, + //{ QtMultimediaKit::Date, 0 }, + { QtMultimediaKit::Year, L"WM/Year" }, + { QtMultimediaKit::UserRating, L"UserRating" }, + //{ QtMultimediaKit::MetaDatawords, 0 }, + { QtMultimediaKit::Language, L"Language" }, + { QtMultimediaKit::Publisher, L"WM/Publisher" }, + { QtMultimediaKit::Copyright, L"Copyright" }, + { QtMultimediaKit::ParentalRating, L"ParentalRating" }, + { QtMultimediaKit::RatingOrganisation, L"RatingOrganisation" }, + + // Media + { QtMultimediaKit::Size, L"FileSize" }, + { QtMultimediaKit::MediaType, L"MediaType" }, + { QtMultimediaKit::Duration, L"Duration" }, + + // Audio + { QtMultimediaKit::AudioBitRate, L"AudioBitRate" }, + { QtMultimediaKit::AudioCodec, L"AudioCodec" }, + { QtMultimediaKit::ChannelCount, L"ChannelCount" }, + { QtMultimediaKit::SampleRate, L"Frequency" }, + + // Music + { QtMultimediaKit::AlbumTitle, L"WM/AlbumTitle" }, + { QtMultimediaKit::AlbumArtist, L"WM/AlbumArtist" }, + { QtMultimediaKit::ContributingArtist, L"Author" }, + { QtMultimediaKit::Composer, L"WM/Composer" }, + { QtMultimediaKit::Conductor, L"WM/Conductor" }, + { QtMultimediaKit::Lyrics, L"WM/Lyrics" }, + { QtMultimediaKit::Mood, L"WM/Mood" }, + { QtMultimediaKit::TrackNumber, L"WM/TrackNumber" }, + //{ QtMultimediaKit::TrackCount, 0 }, + //{ QtMultimediaKit::CoverArtUriSmall, 0 }, + //{ QtMultimediaKit::CoverArtUriLarge, 0 }, + + // Image/Video + //{ QtMultimediaKit::Resolution, 0 }, + //{ QtMultimediaKit::PixelAspectRatio, 0 }, + + // Video + //{ QtMultimediaKit::FrameRate, 0 }, + { QtMultimediaKit::VideoBitRate, L"VideoBitRate" }, + { QtMultimediaKit::VideoCodec, L"VideoCodec" }, + + //{ QtMultimediaKit::PosterUri, 0 }, + + // Movie + { QtMultimediaKit::ChapterNumber, L"ChapterNumber" }, + { QtMultimediaKit::Director, L"WM/Director" }, + { QtMultimediaKit::LeadPerformer, L"LeadPerformer" }, + { QtMultimediaKit::Writer, L"WM/Writer" }, + + // Photos + { QtMultimediaKit::CameraManufacturer, L"CameraManufacturer" }, + { QtMultimediaKit::CameraModel, L"CameraModel" }, + { QtMultimediaKit::Event, L"Event" }, + { QtMultimediaKit::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) + : QMetaDataReaderControl(parent) + , m_content(0) +#ifndef QT_NO_WMSDK + , m_headerInfo(0) +#endif +{ +} + +DirectShowMetaDataControl::~DirectShowMetaDataControl() +{ +} + +bool DirectShowMetaDataControl::isMetaDataAvailable() const +{ +#ifndef QT_NO_WMSDK + return m_content || m_headerInfo; +#else + return m_content; +#endif +} + +QVariant DirectShowMetaDataControl::metaData(QtMultimediaKit::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 QtMultimediaKit::Author: + m_content->get_AuthorName(&string); + break; + case QtMultimediaKit::Title: + m_content->get_Title(&string); + break; + case QtMultimediaKit::ParentalRating: + m_content->get_Rating(&string); + break; + case QtMultimediaKit::Description: + m_content->get_Description(&string); + break; + case QtMultimediaKit::Copyright: + m_content->get_Copyright(&string); + break; + default: + break; + } + + if (string) { + value = QString::fromUtf16(reinterpret_cast<ushort *>(string), ::SysStringLen(string)); + + ::SysFreeString(string); + } + } + return value; +} + +QList<QtMultimediaKit::MetaData> DirectShowMetaDataControl::availableMetaData() const +{ + return QList<QtMultimediaKit::MetaData>(); +} + +QVariant DirectShowMetaDataControl::extendedMetaData(const QString &) const +{ + return 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, IID_IWMHeaderInfo); +#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 { + QMetaDataReaderControl::customEvent(event); + } +} diff --git a/src/plugins/directshow/player/directshowmetadatacontrol.h b/src/plugins/directshow/player/directshowmetadatacontrol.h new file mode 100644 index 000000000..6826d783d --- /dev/null +++ b/src/plugins/directshow/player/directshowmetadatacontrol.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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 <qmetadatareadercontrol.h> + +#include "directshowglobal.h" + +#include <qnetwork.h> + +#ifndef QT_NO_WMSDK +#include <wmsdk.h> +#endif + +#include <QtCore/qcoreevent.h> + +class DirectShowPlayerService; + +QT_USE_NAMESPACE + +class DirectShowMetaDataControl : public QMetaDataReaderControl +{ + Q_OBJECT +public: + DirectShowMetaDataControl(QObject *parent = 0); + ~DirectShowMetaDataControl(); + + bool isMetaDataAvailable() const; + + QVariant metaData(QtMultimediaKit::MetaData key) const; + QList<QtMultimediaKit::MetaData> availableMetaData() const; + + QVariant extendedMetaData(const QString &key) const; + 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 +}; + +#endif diff --git a/src/plugins/directshow/player/directshowpinenum.cpp b/src/plugins/directshow/player/directshowpinenum.cpp new file mode 100644 index 000000000..28093eaee --- /dev/null +++ b/src/plugins/directshow/player/directshowpinenum.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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" + + +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; + } +} diff --git a/src/plugins/directshow/player/directshowpinenum.h b/src/plugins/directshow/player/directshowpinenum.h new file mode 100644 index 000000000..9fba3e84e --- /dev/null +++ b/src/plugins/directshow/player/directshowpinenum.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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> + +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; +}; + +#endif diff --git a/src/plugins/directshow/player/directshowplayercontrol.cpp b/src/plugins/directshow/player/directshowplayercontrol.cpp new file mode 100644 index 000000000..9035a8b21 --- /dev/null +++ b/src/plugins/directshow/player/directshowplayercontrol.cpp @@ -0,0 +1,405 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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> + +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::NoMedia) + , 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); + + emit mediaChanged(m_media); + emitPropertyChanges(); +} + +void DirectShowPlayerControl::play() +{ + if (m_status == QMediaPlayer::NoMedia) + return; + if (m_status == QMediaPlayer::InvalidMedia) { + setMedia(m_media, m_stream); + if (m_error != QMediaPlayer::NoError) + return; + } + m_service->play(); + emit stateChanged(m_state = QMediaPlayer::PlayingState); +} + +void DirectShowPlayerControl::pause() +{ + if (m_status == QMediaPlayer::NoMedia) + return; + if (m_status == QMediaPlayer::InvalidMedia) { + setMedia(m_media, m_stream); + if (m_error != QMediaPlayer::NoError) + return; + } + 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, IID_IBasicAudio); +} + +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); + } +} diff --git a/src/plugins/directshow/player/directshowplayercontrol.h b/src/plugins/directshow/player/directshowplayercontrol.h new file mode 100644 index 000000000..2c1cd6ad7 --- /dev/null +++ b/src/plugins/directshow/player/directshowplayercontrol.h @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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 "qmediacontent.h" +#include "qmediaplayercontrol.h" + +#include <QtCore/qcoreevent.h> + +#include "directshowplayerservice.h" + +QT_USE_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; + +}; + +#endif diff --git a/src/plugins/directshow/player/directshowplayerservice.cpp b/src/plugins/directshow/player/directshowplayerservice.cpp new file mode 100644 index 000000000..ac93f592c --- /dev/null +++ b/src/plugins/directshow/player/directshowplayerservice.cpp @@ -0,0 +1,1408 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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 "directshowvideorenderercontrol.h" +#ifndef Q_WS_SIMULATOR +#include "vmr9videowindowcontrol.h" +#endif + +#include "qmediacontent.h" + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qthread.h> +#include <QtCore/qvarlengtharray.h> + +Q_GLOBAL_STATIC(DirectShowEventLoop, qt_directShowEventLoop) + + +// QMediaPlayer uses millisecond time units, direct show uses 100 nanosecond units. +static const int qt_directShowTimeScale = 10000; + +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_metaDataControl(0) + , m_videoRendererControl(0) +#ifndef Q_WS_SIMULATOR + , m_videoWindowControl(0) +#endif + , m_audioEndpointControl(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) +{ + CoInitialize(NULL); + m_playerControl = new DirectShowPlayerControl(this); + m_metaDataControl = new DirectShowMetaDataControl(this); + m_audioEndpointControl = new DirectShowAudioEndpointControl(this); + + m_taskThread = new DirectShowPlayerServiceThread(this); + m_taskThread->start(); +} + +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_videoRendererControl; +#ifndef Q_WS_SIMULATOR + delete m_videoWindowControl; +#endif + + ::CloseHandle(m_taskHandle); + CoUninitialize(); +} + +QMediaControl *DirectShowPlayerService::requestControl(const char *name) +{ + if (qstrcmp(name, QMediaPlayerControl_iid) == 0) { + return m_playerControl; + } else if (qstrcmp(name, QAudioEndpointSelector_iid) == 0) { + return m_audioEndpointControl; + } else if (qstrcmp(name, QMetaDataReaderControl_iid) == 0) { + return m_metaDataControl; + } else if (qstrcmp(name, QVideoRendererControl_iid) == 0) { +#ifndef Q_WS_SIMULATOR + if (!m_videoRendererControl && !m_videoWindowControl) { +#else + if (!m_videoRendererControl) { +#endif + m_videoRendererControl = new DirectShowVideoRendererControl(m_loop); + + connect(m_videoRendererControl, SIGNAL(filterChanged()), + this, SLOT(videoOutputChanged())); + + return m_videoRendererControl; + } +#ifndef Q_WS_SIMULATOR + } else if (qstrcmp(name, QVideoWindowControl_iid) == 0) { + if (!m_videoRendererControl && !m_videoWindowControl) { + m_videoWindowControl = new Vmr9VideoWindowControl; + + setVideoOutput(m_videoWindowControl->filter()); + + return m_videoWindowControl; + } +#endif + } + return 0; +} + +void DirectShowPlayerService::releaseControl(QMediaControl *control) +{ + if (!control) { + qWarning("QMediaService::releaseControl():" + " Attempted release of null control"); + } else if (control == m_videoRendererControl) { + setVideoOutput(0); + + delete m_videoRendererControl; + + m_videoRendererControl = 0; +#ifndef Q_WS_SIMULATOR + } else if (control == m_videoWindowControl) { + setVideoOutput(0); + + delete m_videoWindowControl; + + m_videoWindowControl = 0; +#endif + } +} + +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 { + // {36b73882-c2c8-11cf-8b46-00805f6cef60} + static const GUID iid_IFilterGraph2 = { + 0x36b73882, 0xc2c8, 0x11cf, {0x8b, 0x46, 0x00, 0x80, 0x5f, 0x6c, 0xef, 0x60} }; + m_graphStatus = Loading; + + m_graph = com_new<IFilterGraph2>(CLSID_FilterGraph, iid_IFilterGraph2); + + 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; + + if (url.scheme() == QLatin1String("http") || url.scheme() == QLatin1String("https")) { + static const GUID clsid_WMAsfReader = { + 0x187463a0, 0x5bb7, 0x11d3, {0xac, 0xbe, 0x00, 0x80, 0xc7, 0x5e, 0x24, 0x6e} }; + + // {56a868a6-0ad4-11ce-b03a-0020af0ba770} + static const GUID iid_IFileSourceFilter = { + 0x56a868a6, 0x0ad4, 0x11ce, {0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70} }; + + if (IFileSourceFilter *fileSource = com_new<IFileSourceFilter>( + clsid_WMAsfReader, iid_IFileSourceFilter)) { + locker->unlock(); + hr = fileSource->Load(reinterpret_cast<const OLECHAR *>(url.toString().utf16()), 0); + + if (SUCCEEDED(hr)) { + source = com_cast<IBaseFilter>(fileSource, IID_IBaseFilter); + + if (!SUCCEEDED(hr = m_graph->AddFilter(source, L"Source")) && source) { + source->Release(); + source = 0; + } + } + fileSource->Release(); + locker->relock(); + } + } else if (url.scheme() == QLatin1String("qrc")) { + DirectShowRcSource *rcSource = new DirectShowRcSource(m_loop); + + locker->unlock(); + if (rcSource->open(url) && SUCCEEDED(hr = m_graph->AddFilter(rcSource, L"Source"))) + source = rcSource; + else + rcSource->Release(); + locker->relock(); + } + + if (!SUCCEEDED(hr)) { + locker->unlock(); + hr = m_graph->AddSourceFilter( + reinterpret_cast<const OLECHAR *>(url.toString().utf16()), L"Source", &source); + locker->relock(); + } + + 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(); + break; + default: + m_error = QMediaPlayer::ResourceError; + m_errorString = QString(); + qWarning("DirectShowPlayerService::doSetUrlSource: Unresolved error code %x", uint(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, IID_IMediaControl)) { + 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*/ 1, 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(); + break; + default: + m_error = QMediaPlayer::ResourceError; + m_errorString = QString(); + qWarning("DirectShowPlayerService::doRender: Unresolved error code %x", + uint(renderHr)); + } + } + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(Error))); + } + + m_executedTasks |= Render; + } + + m_loop->wake(); +} + +void DirectShowPlayerService::doFinalizeLoad(QMutexLocker *locker) +{ + if (m_graphStatus != Loaded) { + if (IMediaEvent *event = com_cast<IMediaEvent>(m_graph, IID_IMediaEvent)) { + event->GetEventHandle(reinterpret_cast<OAEVENT *>(&m_eventHandle)); + event->Release(); + } + if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph, IID_IMediaSeeking)) { + LONGLONG duration = 0; + seeking->GetDuration(&duration); + m_duration = duration / qt_directShowTimeScale; + + 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) { + // {8E1C39A1-DE53-11cf-AA63-0080C744528D} + static const GUID iid_IAMOpenProgress = { + 0x8E1C39A1, 0xDE53, 0x11cf, {0xAA, 0x63, 0x00, 0x80, 0xC7, 0x44, 0x52, 0x8D} }; + + if (IAMOpenProgress *progress = com_cast<IAMOpenProgress>( + m_graph, iid_IAMOpenProgress)) { + 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, IID_IMediaControl)) { + 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); + } + + updateStatus(); +} + +void DirectShowPlayerService::doPlay(QMutexLocker *locker) +{ + if (IMediaControl *control = com_cast<IMediaControl>(m_graph, IID_IMediaControl)) { + 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", uint(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); + } + + updateStatus(); +} + +void DirectShowPlayerService::doPause(QMutexLocker *locker) +{ + if (IMediaControl *control = com_cast<IMediaControl>(m_graph, IID_IMediaControl)) { + locker->unlock(); + HRESULT hr = control->Pause(); + locker->relock(); + + control->Release(); + + if (SUCCEEDED(hr)) { + if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph, IID_IMediaSeeking)) { + LONGLONG position = 0; + + seeking->GetCurrentPosition(&position); + seeking->Release(); + + m_position = position / qt_directShowTimeScale; + } 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", uint(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); + } + + updateStatus(); +} + +void DirectShowPlayerService::doStop(QMutexLocker *locker) +{ + if (m_executedTasks & (Play | Pause)) { + if (IMediaControl *control = com_cast<IMediaControl>(m_graph, IID_IMediaControl)) { + control->Stop(); + control->Release(); + } + + m_position = 0; + m_pendingTasks |= Seek; + + 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, IID_IMediaSeeking)) { + // 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 / qt_directShowTimeScale; + + LONGLONG minimum = 0; + LONGLONG maximum = 0; + m_playbackRange = SUCCEEDED(seeking->GetAvailable(&minimum, &maximum)) + ? QMediaTimeRange(minimum / qt_directShowTimeScale, maximum / qt_directShowTimeScale) + : 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 || (m_pendingTasks & Seek)) { + return m_position; + } else if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph, IID_IMediaSeeking)) { + LONGLONG position = 0; + + seeking->GetCurrentPosition(&position); + seeking->Release(); + + const_cast<qint64 &>(m_position) = position / qt_directShowTimeScale; + + 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 || (m_pendingTasks & Seek)) { + return m_playbackRange; + } else if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph, IID_IMediaSeeking)) { + 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, IID_IMediaSeeking)) { + LONGLONG seekPosition = LONGLONG(m_position) * qt_directShowTimeScale; + + // 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 / qt_directShowTimeScale; + + LONGLONG minimum = 0; + LONGLONG maximum = 0; + m_playbackRange = SUCCEEDED(seeking->GetAvailable(&minimum, &maximum)) + ? QMediaTimeRange( + minimum / qt_directShowTimeScale, maximum / qt_directShowTimeScale) + : QMediaTimeRange(); + + locker->unlock(); + seeking->SetPositions( + &seekPosition, AM_SEEKING_AbsolutePositioning, 0, AM_SEEKING_NoPositioning); + locker->relock(); + + seeking->GetCurrentPosition(¤tPosition); + m_position = currentPosition / qt_directShowTimeScale; + + 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, IID_IWMReaderAdvanced2)) { + 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, IID_IMediaControl)) { + control->Stop(); + control->Release(); + } + + IBaseFilter *decoder = getConnected(m_audioOutput, PINDIR_INPUT); + if (!decoder) { + decoder = m_audioOutput; + decoder->AddRef(); + } + + // {DCFBDCF6-0DC2-45f5-9AB2-7C330EA09C29} + static const GUID iid_IFilterChain = { + 0xDCFBDCF6, 0x0DC2, 0x45f5, {0x9A, 0xB2, 0x7C, 0x33, 0x0E, 0xA0, 0x9C, 0x29} }; + + if (IFilterChain *chain = com_cast<IFilterChain>(m_graph, iid_IFilterChain)) { + 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, IID_IMediaControl)) { + 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(); + } + + // {DCFBDCF6-0DC2-45f5-9AB2-7C330EA09C29} + static const GUID iid_IFilterChain = { + 0xDCFBDCF6, 0x0DC2, 0x45f5, {0x9A, 0xB2, 0x7C, 0x33, 0x0E, 0xA0, 0x9C, 0x29} }; + + if (IFilterChain *chain = com_cast<IFilterChain>(m_graph, iid_IFilterChain)) { + 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); + + if (m_playerControl->mediaStatus() == QMediaPlayer::EndOfMedia) + m_playerControl->updateStatus(QMediaPlayer::LoadedMedia); + m_playerControl->updatePosition(m_position); + } else { + QMediaService::customEvent(event); + } +} + +void DirectShowPlayerService::videoOutputChanged() +{ + setVideoOutput(m_videoRendererControl->filter()); +} + +void DirectShowPlayerService::graphEvent(QMutexLocker *locker) +{ + if (IMediaEvent *event = com_cast<IMediaEvent>(m_graph, IID_IMediaEvent)) { + 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, IID_IMediaSeeking)) { + LONGLONG position = 0; + + seeking->GetCurrentPosition(&position); + seeking->Release(); + + m_position = position / qt_directShowTimeScale; + } + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(EndOfMedia))); + break; + case EC_LENGTH_CHANGED: + if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph, IID_IMediaSeeking)) { + LONGLONG duration = 0; + seeking->GetDuration(&duration); + m_duration = duration / qt_directShowTimeScale; + + 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); + //if the graph is released, we should not process other operations later + if (m_pendingTasks & Shutdown) { + m_pendingTasks = 0; + return; + } + m_pendingTasks = 0; + } 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; + } +} diff --git a/src/plugins/directshow/player/directshowplayerservice.h b/src/plugins/directshow/player/directshowplayerservice.h new file mode 100644 index 000000000..cc0cac1cf --- /dev/null +++ b/src/plugins/directshow/player/directshowplayerservice.h @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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 "qmediaplayer.h" +#include "qmediaresource.h" +#include "qmediaservice.h" +#include "qmediatimerange.h" + +#include "directshoweventloop.h" +#include "directshowglobal.h" + +#include <QtCore/qcoreevent.h> +#include <QtCore/qmutex.h> +#include <QtCore/qurl.h> +#include <QtCore/qwaitcondition.h> + +class DirectShowAudioEndpointControl; +class DirectShowMetaDataControl; +class DirectShowPlayerControl; +class DirectShowVideoRendererControl; +#ifndef Q_WS_SIMULATOR +class Vmr9VideoWindowControl; +#endif + +QT_BEGIN_NAMESPACE +class QMediaContent; +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +class DirectShowPlayerService : public QMediaService +{ + Q_OBJECT +public: + enum StreamType + { + AudioStream = 0x01, + VideoStream = 0x02 + }; + + DirectShowPlayerService(QObject *parent = 0); + ~DirectShowPlayerService(); + + QMediaControl* requestControl(const char *name); + void releaseControl(QMediaControl *control); + + 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; + DirectShowVideoRendererControl *m_videoRendererControl; +#ifndef Q_WS_SIMULATOR + Vmr9VideoWindowControl *m_videoWindowControl; +#endif + 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; +}; + + +#endif diff --git a/src/plugins/directshow/player/directshowsamplescheduler.cpp b/src/plugins/directshow/player/directshowsamplescheduler.cpp new file mode 100644 index 000000000..48b7899c6 --- /dev/null +++ b/src/plugins/directshow/player/directshowsamplescheduler.cpp @@ -0,0 +1,437 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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/qcoreapplication.h> +#include <QtCore/qcoreevent.h> + +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) + : QObject(parent) + , m_pin(pin) + , m_clock(0) + , m_allocator(0) + , m_head(0) + , m_tail(0) + , m_maximumSamples(1) + , m_state(Stopped) + , m_startTime(0) + , m_timeoutEvent(::CreateEvent(0, 0, 0, 0)) + , m_flushEvent(::CreateEvent(0, 0, 0, 0)) +{ + m_semaphore.release(m_maximumSamples); +} + +DirectShowSampleScheduler::~DirectShowSampleScheduler() +{ + ::CloseHandle(m_timeoutEvent); + ::CloseHandle(m_flushEvent); + + 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. + QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); + } else { + locker.unlock(); + HANDLE handles[] = { m_flushEvent, m_timeoutEvent }; + DWORD result = ::WaitForMultipleObjects(2, handles, FALSE, INFINITE); + locker.relock(); + + if (result == WAIT_OBJECT_0 + 1) + QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); + } + } else if (m_tail == m_head) { + // If this is the first frame make it available. + QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); + + if (m_state == Paused) { + ::ResetEvent(m_timeoutEvent); + + locker.unlock(); + HANDLE handles[] = { m_flushEvent, m_timeoutEvent }; + ::WaitForMultipleObjects(2, handles, FALSE, INFINITE); + locker.relock(); + } + } + + 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); + } + + if (!(m_state & Flushing)) + ::ResetEvent(m_flushEvent); + + if (!m_head) + ::SetEvent(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); + + if (!(m_state & Flushing)) + ::ResetEvent(m_flushEvent); +} + +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; + + ::SetEvent(m_flushEvent); +} + +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; + + ::SetEvent(m_flushEvent); + } else { + m_state &= ~Flushing; + + if (m_state != Stopped) + ::ResetEvent(m_flushEvent); + } + } +} + +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(); + + *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::UpdateRequest) { + emit sampleReady(); + + return true; + } else { + return QObject::event(event); + } +} diff --git a/src/plugins/directshow/player/directshowsamplescheduler.h b/src/plugins/directshow/player/directshowsamplescheduler.h new file mode 100644 index 000000000..bea833ef9 --- /dev/null +++ b/src/plugins/directshow/player/directshowsamplescheduler.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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 <dshow.h> + +class DirectShowTimedSample; + +class DirectShowSampleScheduler : public QObject, 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; + HANDLE m_flushEvent; + QSemaphore m_semaphore; + QMutex m_mutex; +}; + +#endif diff --git a/src/plugins/directshow/player/directshowvideorenderercontrol.cpp b/src/plugins/directshow/player/directshowvideorenderercontrol.cpp new file mode 100644 index 000000000..429d5e1ec --- /dev/null +++ b/src/plugins/directshow/player/directshowvideorenderercontrol.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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" + +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; +} diff --git a/src/plugins/directshow/player/directshowvideorenderercontrol.h b/src/plugins/directshow/player/directshowvideorenderercontrol.h new file mode 100644 index 000000000..5057a94e0 --- /dev/null +++ b/src/plugins/directshow/player/directshowvideorenderercontrol.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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 "qvideorenderercontrol.h" + +#include <dshow.h> + +class DirectShowEventLoop; +class VideoSurfaceFilter; + +QT_USE_NAMESPACE + +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; +}; + +#endif diff --git a/src/plugins/directshow/player/mediasamplevideobuffer.cpp b/src/plugins/directshow/player/mediasamplevideobuffer.cpp new file mode 100644 index 000000000..a2d1d7cee --- /dev/null +++ b/src/plugins/directshow/player/mediasamplevideobuffer.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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" + +MediaSampleVideoBuffer::MediaSampleVideoBuffer(IMediaSample *sample, int bytesPerLine) + : QAbstractVideoBuffer(NoHandle) + , m_sample(sample) + , m_bytesPerLine(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; +} diff --git a/src/plugins/directshow/player/mediasamplevideobuffer.h b/src/plugins/directshow/player/mediasamplevideobuffer.h new file mode 100644 index 000000000..0d44e7a1f --- /dev/null +++ b/src/plugins/directshow/player/mediasamplevideobuffer.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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 <qabstractvideobuffer.h> + +#include <dshow.h> + +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; +}; + + +#endif diff --git a/src/plugins/directshow/player/player.pri b/src/plugins/directshow/player/player.pri new file mode 100644 index 000000000..a058b0659 --- /dev/null +++ b/src/plugins/directshow/player/player.pri @@ -0,0 +1,47 @@ +INCLUDEPATH += $$PWD + +DEFINES += QMEDIA_DIRECTSHOW_PLAYER + +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/directshowvideorenderercontrol.h \ + $$PWD/mediasamplevideobuffer.h \ + $$PWD/videosurfacefilter.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/directshowvideorenderercontrol.cpp \ + $$PWD/mediasamplevideobuffer.cpp \ + $$PWD/videosurfacefilter.cpp + +!simulator { +HEADERS += \ + $$PWD/vmr9videowindowcontrol.h + +SOURCES += \ + $$PWD/vmr9videowindowcontrol.cpp +} + +LIBS += -lstrmiids -ldmoguids -luuid -lmsdmo -lole32 -loleaut32 -lgdi32 + diff --git a/src/plugins/directshow/player/videosurfacefilter.cpp b/src/plugins/directshow/player/videosurfacefilter.cpp new file mode 100644 index 000000000..a6a3c1b4f --- /dev/null +++ b/src/plugins/directshow/player/videosurfacefilter.cpp @@ -0,0 +1,631 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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 <qabstractvideosurface.h> + +#include <initguid.h> + +// { 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) +{ + // 2dd74950-a890-11d1-abe8-00a0c905f375 + static const GUID iid_IAmFilterMiscFlags = { + 0x2dd74950, 0xa890, 0x11d1, {0xab, 0xe8, 0x00, 0xa0, 0xc9, 0x05, 0xf3, 0x75} }; + + 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->isFormatSupported(m_surfaceFormat)) { + return VFW_E_TYPE_NOT_ACCEPTED; + } + 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, IID_IMediaEventSink)) { + 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); + + // MEDIASUBTYPE_None; + static const GUID none = { + 0xe436eb8e, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70} }; + + 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 != 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, IID_IMediaEventSink)) { + sink->Notify( + EC_COMPLETE, + S_OK, + reinterpret_cast<LONG_PTR>(static_cast<IBaseFilter *>(this))); + sink->Release(); + } + } + } +} + diff --git a/src/plugins/directshow/player/videosurfacefilter.h b/src/plugins/directshow/player/videosurfacefilter.h new file mode 100644 index 000000000..a58971630 --- /dev/null +++ b/src/plugins/directshow/player/videosurfacefilter.h @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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 "directshowglobal.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_NAMESPACE +class QAbstractVideoSurface; +QT_END_NAMESPACE + +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; +}; + +#endif diff --git a/src/plugins/directshow/player/vmr9videowindowcontrol.cpp b/src/plugins/directshow/player/vmr9videowindowcontrol.cpp new file mode 100644 index 000000000..5e729844b --- /dev/null +++ b/src/plugins/directshow/player/vmr9videowindowcontrol.cpp @@ -0,0 +1,329 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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" + +Vmr9VideoWindowControl::Vmr9VideoWindowControl(QObject *parent) + : QVideoWindowControl(parent) + , m_filter(com_new<IBaseFilter>(CLSID_VideoMixingRenderer9, IID_IBaseFilter)) + , m_windowId(0) + , m_dirtyValues(0) + , m_aspectRatioMode(Qt::KeepAspectRatio) + , m_brightness(0) + , m_contrast(0) + , m_hue(0) + , m_saturation(0) + , m_fullScreen(false) +{ + if (IVMRFilterConfig9 *config = com_cast<IVMRFilterConfig9>(m_filter, IID_IVMRFilterConfig9)) { + 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 (QWidget *widget = QWidget::find(m_windowId)) { + const QColor color = widget->palette().color(QPalette::Window); + + m_windowColor = RGB(color.red(), color.green(), color.blue()); + } + + if (IVMRWindowlessControl9 *control = com_cast<IVMRWindowlessControl9>( + m_filter, IID_IVMRWindowlessControl9)) { + control->SetVideoClippingWindow(m_windowId); + control->SetBorderColor(m_windowColor); + control->Release(); + } +} + +QRect Vmr9VideoWindowControl::displayRect() const +{ + return m_displayRect; +} + +void Vmr9VideoWindowControl::setDisplayRect(const QRect &rect) +{ + m_displayRect = rect; + + if (IVMRWindowlessControl9 *control = com_cast<IVMRWindowlessControl9>( + m_filter, IID_IVMRWindowlessControl9)) { + RECT sourceRect = { 0, 0, 0, 0 }; + RECT displayRect = { rect.left(), rect.top(), rect.right() + 1, rect.bottom() + 1 }; + + control->GetNativeVideoSize(&sourceRect.right, &sourceRect.bottom, 0, 0); + + if (m_aspectRatioMode == Qt::KeepAspectRatioByExpanding) { + QSize clippedSize = rect.size(); + clippedSize.scale(sourceRect.right, sourceRect.bottom, Qt::KeepAspectRatio); + + sourceRect.left = (sourceRect.right - clippedSize.width()) / 2; + sourceRect.top = (sourceRect.bottom - clippedSize.height()) / 2; + sourceRect.right = sourceRect.left + clippedSize.width(); + sourceRect.bottom = sourceRect.top + clippedSize.height(); + } + + 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() +{ + PAINTSTRUCT paint; + + if (HDC dc = ::BeginPaint(m_windowId, &paint)) { + HRESULT hr = E_FAIL; + + if (IVMRWindowlessControl9 *control = com_cast<IVMRWindowlessControl9>( + m_filter, IID_IVMRWindowlessControl9)) { + hr = control->RepaintVideo(m_windowId, dc); + control->Release(); + } + + if (!SUCCEEDED(hr)) { + HPEN pen = ::CreatePen(PS_SOLID, 1, m_windowColor); + HBRUSH brush = ::CreateSolidBrush(m_windowColor); + ::SelectObject(dc, pen); + ::SelectObject(dc, brush); + + ::Rectangle( + dc, + m_displayRect.left(), + m_displayRect.top(), + m_displayRect.right() + 1, + m_displayRect.bottom() + 1); + + ::DeleteObject(pen); + ::DeleteObject(brush); + } + ::EndPaint(m_windowId, &paint); + } +} + +QSize Vmr9VideoWindowControl::nativeSize() const +{ + QSize size; + + if (IVMRWindowlessControl9 *control = com_cast<IVMRWindowlessControl9>( + m_filter, IID_IVMRWindowlessControl9)) { + LONG width; + LONG height; + + if (control->GetNativeVideoSize(&width, &height, 0, 0) == S_OK) + size = QSize(width, height); + control->Release(); + } + return size; +} + +Qt::AspectRatioMode Vmr9VideoWindowControl::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void Vmr9VideoWindowControl::setAspectRatioMode(Qt::AspectRatioMode mode) +{ + m_aspectRatioMode = mode; + + if (IVMRWindowlessControl9 *control = com_cast<IVMRWindowlessControl9>( + m_filter, IID_IVMRWindowlessControl9)) { + switch (mode) { + case Qt::IgnoreAspectRatio: + control->SetAspectRatioMode(VMR9ARMode_None); + break; + case Qt::KeepAspectRatio: + control->SetAspectRatioMode(VMR9ARMode_LetterBox); + break; + case Qt::KeepAspectRatioByExpanding: + control->SetAspectRatioMode(VMR9ARMode_LetterBox); + setDisplayRect(m_displayRect); + 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, IID_IVMRMixerControl9)) { + 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; +} diff --git a/src/plugins/directshow/player/vmr9videowindowcontrol.h b/src/plugins/directshow/player/vmr9videowindowcontrol.h new file mode 100644 index 000000000..b4e39a7fd --- /dev/null +++ b/src/plugins/directshow/player/vmr9videowindowcontrol.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $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 "qvideowindowcontrol.h" + +#include <dshow.h> +#include <d3d9.h> +#include <vmr9.h> + +QT_USE_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; + + Qt::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(Qt::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; + COLORREF m_windowColor; + DWORD m_dirtyValues; + Qt::AspectRatioMode m_aspectRatioMode; + QRect m_displayRect; + int m_brightness; + int m_contrast; + int m_hue; + int m_saturation; + bool m_fullScreen; +}; + +#endif |