/* This file is part of the KDE project. Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 or 3 of the License. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "qbasefilter.h" #include "qpin.h" #include "compointer.h" #include QT_BEGIN_NAMESPACE namespace Phonon { namespace DS9 { static const AM_MEDIA_TYPE defaultMediaType = { MEDIATYPE_NULL, MEDIASUBTYPE_NULL, TRUE, FALSE, 1, GUID_NULL, 0, 0, 0}; class QEnumMediaTypes : public IEnumMediaTypes { public: QEnumMediaTypes(QPin *pin) : m_refCount(1), m_pin(pin), m_index(0) { m_pin->AddRef(); } ~QEnumMediaTypes() { m_pin->Release(); } STDMETHODIMP QueryInterface(const IID &iid,void **out) { if (!out) { return E_POINTER; } HRESULT hr = S_OK; if (iid == IID_IEnumMediaTypes) { *out = static_cast(this); } else if (iid == IID_IUnknown) { *out = static_cast(this); } else { *out = 0; hr = E_NOINTERFACE; } if (hr == S_OK) { AddRef(); } return hr; } STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&m_refCount); } STDMETHODIMP_(ULONG) Release() { ULONG refCount = InterlockedDecrement(&m_refCount); if (refCount == 0) { delete this; } return refCount; } STDMETHODIMP Next(ULONG count, AM_MEDIA_TYPE **out, ULONG *fetched) { QMutexLocker locker(&m_mutex); if (!out) { return E_POINTER; } if (!fetched && count > 1) { return E_INVALIDARG; } int nbFetched = 0; while (nbFetched < int(count) && m_index < m_pin->mediaTypes().count()) { //the caller will deallocate the memory *out = static_cast(::CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))); const AM_MEDIA_TYPE original = m_pin->mediaTypes().at(m_index); **out = QPin::copyMediaType(original); nbFetched++; m_index++; out++; } if (fetched) { *fetched = nbFetched; } return nbFetched == count ? S_OK : S_FALSE; } STDMETHODIMP Skip(ULONG count) { QMutexLocker locker(&m_mutex); m_index = qMin(m_index + int(count), m_pin->mediaTypes().count()); return (m_index == m_pin->mediaTypes().count()) ? S_FALSE : S_OK; } STDMETHODIMP Reset() { QMutexLocker locker(&m_mutex); m_index = 0; return S_OK; } STDMETHODIMP Clone(IEnumMediaTypes **out) { QMutexLocker locker(&m_mutex); if (!out) { return E_POINTER; } *out = new QEnumMediaTypes(m_pin); (*out)->Skip(m_index); return S_OK; } private: LONG m_refCount; QPin *m_pin; int m_index; QMutex m_mutex; }; QPin::QPin(QBaseFilter *parent, PIN_DIRECTION dir, const QVector &mt) : m_memAlloc(0), m_parent(parent), m_refCount(1), m_connected(0), m_direction(dir), m_mediaTypes(mt), m_connectedType(defaultMediaType), m_flushing(false) { Q_ASSERT(m_parent); m_parent->addPin(this); } QPin::~QPin() { m_parent->removePin(this); setMemoryAllocator(0); freeMediaType(m_connectedType); } //reimplementation from IUnknown STDMETHODIMP QPin::QueryInterface(REFIID iid, void**out) { if (!out) { return E_POINTER; } HRESULT hr = S_OK; if (iid == IID_IPin) { *out = static_cast(this); } else if (iid == IID_IUnknown) { *out = static_cast(this); } else if (m_direction == PINDIR_OUTPUT && (iid == IID_IMediaSeeking || iid == IID_IMediaPosition)) { return m_parent->QueryInterface(iid, out); } else { *out = 0; hr = E_NOINTERFACE; } if (hr == S_OK) { AddRef(); } return hr; } STDMETHODIMP_(ULONG) QPin::AddRef() { return InterlockedIncrement(&m_refCount); } STDMETHODIMP_(ULONG) QPin::Release() { ULONG refCount = InterlockedDecrement(&m_refCount); if (refCount == 0) { delete this; } return refCount; } //this is called on the input pins STDMETHODIMP QPin::ReceiveConnection(IPin *pin, const AM_MEDIA_TYPE *type) { if (!pin ||!type) { return E_POINTER; } if (connected()) { return VFW_E_ALREADY_CONNECTED; } if (filterState() != State_Stopped) { return VFW_E_NOT_STOPPED; } if (QueryAccept(type) != S_OK) { return VFW_E_TYPE_NOT_ACCEPTED; } setConnected(pin); setConnectedType(*type); return S_OK; } //this is called on the output pins STDMETHODIMP QPin::Connect(IPin *pin, const AM_MEDIA_TYPE *type) { if (!pin) { return E_POINTER; } if (connected()) { return VFW_E_ALREADY_CONNECTED; } if (filterState() != State_Stopped) { return VFW_E_NOT_STOPPED; } HRESULT hr = S_OK; setConnected(pin); if (!type) { //let(s first try the output pin's mediaTypes if (checkOutputMediaTypesConnection(pin) != S_OK && checkOwnMediaTypesConnection(pin) != S_OK) { hr = VFW_E_NO_ACCEPTABLE_TYPES; } } else if (QueryAccept(type) == S_OK) { setConnectedType(*type); hr = pin->ReceiveConnection(this, type); } else { hr = VFW_E_TYPE_NOT_ACCEPTED; } if (FAILED(hr)) { setConnected(0); setConnectedType(defaultMediaType); } else { ComPointer input(pin, IID_IMemInputPin); if (input) { ComPointer alloc; input->GetAllocator(alloc.pparam()); if (alloc) { //be default we take the allocator from the input pin //we have no reason to force using our own setMemoryAllocator(alloc); } } if (memoryAllocator() == 0) { ALLOCATOR_PROPERTIES prop; if (input && input->GetAllocatorRequirements(&prop) == S_OK) { createDefaultMemoryAllocator(&prop); } else { createDefaultMemoryAllocator(); } } Q_ASSERT(memoryAllocator() != 0); if (input) { input->NotifyAllocator(memoryAllocator(), TRUE); //TRUE is arbitrarily chosen here } } return hr; } STDMETHODIMP QPin::Disconnect() { if (!connected()) { return S_FALSE; } if (filterState() != State_Stopped) { return VFW_E_NOT_STOPPED; } setConnected(0); setConnectedType(defaultMediaType); if (m_direction == PINDIR_INPUT) { setMemoryAllocator(0); } return S_OK; } STDMETHODIMP QPin::ConnectedTo(IPin **other) { if (!other) { return E_POINTER; } *other = connected(true); if (!(*other)) { return VFW_E_NOT_CONNECTED; } return S_OK; } STDMETHODIMP QPin::ConnectionMediaType(AM_MEDIA_TYPE *type) { QReadLocker locker(&m_lock); if (!type) { return E_POINTER; } *type = copyMediaType(m_connectedType); if (!m_connected) { return VFW_E_NOT_CONNECTED; } else { return S_OK; } } STDMETHODIMP QPin::QueryPinInfo(PIN_INFO *info) { QReadLocker locker(&m_lock); if (!info) { return E_POINTER; } info->dir = m_direction; info->pFilter = m_parent; m_parent->AddRef(); qMemCopy(info->achName, m_name.utf16(), qMin(MAX_FILTER_NAME, m_name.length()+1) *2); return S_OK; } STDMETHODIMP QPin::QueryDirection(PIN_DIRECTION *dir) { QReadLocker locker(&m_lock); if (!dir) { return E_POINTER; } *dir = m_direction; return S_OK; } STDMETHODIMP QPin::QueryId(LPWSTR *id) { QReadLocker locker(&m_lock); if (!id) { return E_POINTER; } int nbBytes = (m_name.length()+1)*2; *id = static_cast(::CoTaskMemAlloc(nbBytes)); qMemCopy(*id, m_name.utf16(), nbBytes); return S_OK; } STDMETHODIMP QPin::QueryAccept(const AM_MEDIA_TYPE *type) { QReadLocker locker(&m_lock); if (!type) { return E_POINTER; } for (int i = 0; i < m_mediaTypes.count(); ++i) { const AM_MEDIA_TYPE ¤t = m_mediaTypes.at(i); if ( (type->majortype == current.majortype) && (current.subtype == MEDIASUBTYPE_NULL || type->subtype == current.subtype) && (type->majortype == MEDIATYPE_Stream || type->formattype != GUID_NULL || current.formattype != GUID_NULL) && (current.formattype == GUID_NULL || type->formattype == current.formattype) ) { return S_OK; } } return S_FALSE; } STDMETHODIMP QPin::EnumMediaTypes(IEnumMediaTypes **emt) { if (!emt) { return E_POINTER; } *emt = new QEnumMediaTypes(this); return S_OK; } STDMETHODIMP QPin::EndOfStream() { return E_UNEXPECTED; } STDMETHODIMP QPin::BeginFlush() { return E_UNEXPECTED; } STDMETHODIMP QPin::EndFlush() { return E_UNEXPECTED; } STDMETHODIMP QPin::NewSegment(REFERENCE_TIME start, REFERENCE_TIME stop, double rate) { QReadLocker locker(&m_lock); if (m_direction == PINDIR_OUTPUT && m_connected) { //we deliver this downstream m_connected->NewSegment(start, stop, rate); } return S_OK; } STDMETHODIMP QPin::QueryInternalConnections(IPin **, ULONG*) { //this is not implemented on purpose (the input pins are connected to all the output pins) return E_NOTIMPL; } HRESULT QPin::checkOutputMediaTypesConnection(IPin *pin) { IEnumMediaTypes *emt = 0; HRESULT hr = pin->EnumMediaTypes(&emt); if (hr != S_OK) { return hr; } AM_MEDIA_TYPE *type = 0; while (emt->Next(1, &type, 0) == S_OK) { if (QueryAccept(type) == S_OK) { setConnectedType(*type); if (pin->ReceiveConnection(this, type) == S_OK) { freeMediaType(type); return S_OK; } else { setConnectedType(defaultMediaType); freeMediaType(type); } } } //we didn't find a suitable type return S_FALSE; } HRESULT QPin::checkOwnMediaTypesConnection(IPin *pin) { for(int i = 0; i < m_mediaTypes.count(); ++i) { const AM_MEDIA_TYPE ¤t = m_mediaTypes.at(i); setConnectedType(current); HRESULT hr = pin->ReceiveConnection(this, ¤t); if (hr == S_OK) { return S_OK; } } //we didn't find a suitable type return S_FALSE; } void QPin::freeMediaType(const AM_MEDIA_TYPE &type) { if (type.cbFormat) { ::CoTaskMemFree(type.pbFormat); } if (type.pUnk) { type.pUnk->Release(); } } void QPin::freeMediaType(AM_MEDIA_TYPE *type) { freeMediaType(*type); ::CoTaskMemFree(type); } //addition PIN_DIRECTION QPin::direction() const { return m_direction; } void QPin::setConnectedType(const AM_MEDIA_TYPE &type) { QWriteLocker locker(&m_lock); //1st we free memory freeMediaType(m_connectedType); m_connectedType = copyMediaType(type); } const AM_MEDIA_TYPE &QPin::connectedType() const { QReadLocker locker(&m_lock); return m_connectedType; } void QPin::setConnected(IPin *pin) { QWriteLocker locker(&m_lock); if (pin) { pin->AddRef(); } if (m_connected) { m_connected->Release(); } m_connected = pin; } IPin *QPin::connected(bool addref) const { QReadLocker locker(&m_lock); if (addref && m_connected) { m_connected->AddRef(); } return m_connected; } bool QPin::isFlushing() const { QReadLocker locker(&m_lock); return m_flushing; } FILTER_STATE QPin::filterState() const { QReadLocker locker(&m_lock); FILTER_STATE fstate = State_Stopped; m_parent->GetState(0, &fstate); return fstate; } QVector QPin::mediaTypes() const { QReadLocker locker(&m_lock); return m_mediaTypes; } HRESULT QPin::setAcceptedMediaType(const AM_MEDIA_TYPE &mt) { const QVector oldMediaTypes = m_mediaTypes; m_mediaTypes = QVector() << mt; HRESULT hr = S_OK; IPin *conn = connected(); if (conn) { //try to reconnect to redefine the media type conn->Disconnect(); Disconnect(); hr = Connect(conn, 0); if (FAILED(hr)) { m_mediaTypes = oldMediaTypes; Connect(conn, 0); //just redo the connection with the old media types } } return hr; } void QPin::createDefaultMemoryAllocator(ALLOCATOR_PROPERTIES *prop) { ComPointer alloc(CLSID_MemoryAllocator, IID_IMemAllocator); if (prop) { alloc->SetProperties(prop, 0); } setMemoryAllocator(alloc); } void QPin::setMemoryAllocator(IMemAllocator *alloc) { QWriteLocker locker(&m_lock); if (alloc) { alloc->AddRef(); } if (m_memAlloc) { m_memAlloc->Release(); } m_memAlloc = alloc; } IMemAllocator *QPin::memoryAllocator(bool addref) const { QReadLocker locker(&m_lock); if (addref && m_memAlloc) { m_memAlloc->AddRef(); } return m_memAlloc; } AM_MEDIA_TYPE QPin::copyMediaType(const AM_MEDIA_TYPE &type) { AM_MEDIA_TYPE ret = type; //make a deep copy here if (ret.cbFormat == 0 || ret.pbFormat == 0) { ret.cbFormat = 0; ret.pbFormat = 0; ret.formattype = GUID_NULL; } else { ret.pbFormat = reinterpret_cast(::CoTaskMemAlloc(type.cbFormat)); qMemCopy(ret.pbFormat, type.pbFormat, type.cbFormat); } if (type.pUnk) { type.pUnk->AddRef(); } return ret; } } } QT_END_NAMESPACE