/* 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 <http://www.gnu.org/licenses/>. | |
*/ | |
#include "qmeminputpin.h" | |
#include "qbasefilter.h" | |
#include "compointer.h" | |
#include <QtCore/QDebug> | |
QT_BEGIN_NAMESPACE | |
namespace Phonon | |
{ | |
namespace DS9 | |
{ | |
QMemInputPin::QMemInputPin(QBaseFilter *parent, const QVector<AM_MEDIA_TYPE> &mt, bool transform, QPin *output) : | |
QPin(parent, PINDIR_INPUT, mt), m_shouldDuplicateSamples(true), m_transform(transform), m_output(output) | |
{ | |
} | |
QMemInputPin::~QMemInputPin() | |
{ | |
} | |
STDMETHODIMP QMemInputPin::QueryInterface(REFIID iid, void **out) | |
{ | |
if (!out) { | |
return E_POINTER; | |
} | |
if (iid == IID_IMemInputPin) { | |
*out = static_cast<IMemInputPin*>(this); | |
AddRef(); | |
return S_OK; | |
} else { | |
return QPin::QueryInterface(iid, out); | |
} | |
} | |
STDMETHODIMP_(ULONG) QMemInputPin::AddRef() | |
{ | |
return QPin::AddRef(); | |
} | |
STDMETHODIMP_(ULONG) QMemInputPin::Release() | |
{ | |
return QPin::Release(); | |
} | |
STDMETHODIMP QMemInputPin::EndOfStream() | |
{ | |
//this allows to serialize with Receive calls | |
QMutexLocker locker(&m_mutexReceive); | |
IPin *conn = m_output ? m_output->connected() : 0; | |
if (conn) { | |
conn->EndOfStream(); | |
} | |
return S_OK; | |
} | |
STDMETHODIMP QMemInputPin::BeginFlush() | |
{ | |
//pass downstream | |
IPin *conn = m_output ? m_output->connected() : 0; | |
if (conn) { | |
conn->BeginFlush(); | |
} | |
QMutexLocker locker(&m_mutex); | |
m_flushing = true; | |
return S_OK; | |
} | |
STDMETHODIMP QMemInputPin::EndFlush() | |
{ | |
//pass downstream | |
IPin *conn = m_output ? m_output->connected() : 0; | |
if (conn) { | |
conn->EndFlush(); | |
} | |
QMutexLocker locker(&m_mutex); | |
m_flushing = false; | |
return S_OK; | |
} | |
STDMETHODIMP QMemInputPin::NewSegment(REFERENCE_TIME start, REFERENCE_TIME stop, double rate) | |
{ | |
if (m_output) | |
m_output->NewSegment(start, stop, rate); | |
return S_OK; | |
} | |
//reimplementation to set the type for the output pin | |
//no need to make a deep copy here | |
STDMETHODIMP QMemInputPin::ReceiveConnection(IPin *pin ,const AM_MEDIA_TYPE *mt) | |
{ | |
HRESULT hr = QPin::ReceiveConnection(pin, mt); | |
if (hr == S_OK && | |
mt->majortype != MEDIATYPE_NULL && | |
mt->subtype != MEDIASUBTYPE_NULL && | |
mt->formattype != GUID_NULL && m_output) { | |
//we tell the output pin that it should connect with this type | |
hr = m_output->setAcceptedMediaType(connectedType()); | |
} | |
return hr; | |
} | |
STDMETHODIMP QMemInputPin::GetAllocator(IMemAllocator **alloc) | |
{ | |
if (!alloc) { | |
return E_POINTER; | |
} | |
*alloc = memoryAllocator(true); | |
if (*alloc) { | |
return S_OK; | |
} | |
return VFW_E_NO_ALLOCATOR; | |
} | |
STDMETHODIMP QMemInputPin::NotifyAllocator(IMemAllocator *alloc, BOOL readonly) | |
{ | |
if (!alloc) { | |
return E_POINTER; | |
} | |
{ | |
QMutexLocker locker(&m_mutex); | |
m_shouldDuplicateSamples = m_transform && readonly; | |
} | |
setMemoryAllocator(alloc); | |
if (m_output) { | |
ComPointer<IMemInputPin> input(m_output, IID_IMemInputPin); | |
input->NotifyAllocator(alloc, m_shouldDuplicateSamples); | |
} | |
return S_OK; | |
} | |
STDMETHODIMP QMemInputPin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *prop) | |
{ | |
if (!prop) { | |
return E_POINTER; | |
} | |
//we have no particular requirements | |
return E_NOTIMPL; | |
} | |
STDMETHODIMP QMemInputPin::Receive(IMediaSample *sample) | |
{ | |
QMutexLocker locker(&m_mutexReceive); | |
if (!sample) { | |
return E_POINTER; | |
} | |
if (filterState() == State_Stopped) { | |
return VFW_E_WRONG_STATE; | |
} | |
if (isFlushing()) { | |
return S_FALSE; //we are still flushing | |
} | |
if (!m_shouldDuplicateSamples) { | |
//we do it just once | |
HRESULT hr = m_parent->processSample(sample); | |
if (!SUCCEEDED(hr)) { | |
return hr; | |
} | |
} | |
if (m_output) { | |
IMediaSample *outSample = m_shouldDuplicateSamples ? | |
duplicateSampleForOutput(sample, m_output->memoryAllocator()) | |
: sample; | |
if (m_shouldDuplicateSamples) { | |
m_parent->processSample(outSample); | |
} | |
ComPointer<IMemInputPin> input(m_output->connected(), IID_IMemInputPin); | |
if (input) { | |
input->Receive(outSample); | |
} | |
if (m_shouldDuplicateSamples) { | |
outSample->Release(); | |
} | |
} | |
return S_OK; | |
} | |
STDMETHODIMP QMemInputPin::ReceiveMultiple(IMediaSample **samples,long count,long *nbDone) | |
{ | |
//no need to lock here: there is no access to member data | |
if (!samples || !nbDone) { | |
return E_POINTER; | |
} | |
*nbDone = 0; //initialization | |
while( *nbDone != count) { | |
HRESULT hr = Receive(samples[*nbDone]); | |
if (FAILED(hr)) { | |
return hr; | |
} | |
(*nbDone)++; | |
} | |
return S_OK; | |
} | |
STDMETHODIMP QMemInputPin::ReceiveCanBlock() | |
{ | |
//we test the output to see if it can block | |
if (m_output) { | |
ComPointer<IMemInputPin> meminput(m_output->connected(), IID_IMemInputPin); | |
if (meminput && meminput->ReceiveCanBlock() != S_FALSE) { | |
return S_OK; | |
} | |
} | |
return S_FALSE; | |
} | |
ALLOCATOR_PROPERTIES QMemInputPin::getDefaultAllocatorProperties() const | |
{ | |
//those values reduce buffering a lot (good for the volume effect) | |
ALLOCATOR_PROPERTIES prop = {4096, 1, 1, 0}; | |
return prop; | |
} | |
IMediaSample *QMemInputPin::duplicateSampleForOutput(IMediaSample *sample, IMemAllocator *alloc) | |
{ | |
LONG length = sample->GetActualDataLength(); | |
HRESULT hr = alloc->Commit(); | |
if (hr == HRESULT(VFW_E_SIZENOTSET)) { | |
ALLOCATOR_PROPERTIES prop = getDefaultAllocatorProperties(); | |
prop.cbBuffer = qMax(prop.cbBuffer, length); | |
ALLOCATOR_PROPERTIES actual; | |
//we just try to set the properties... | |
alloc->SetProperties(&prop, &actual); | |
hr = alloc->Commit(); | |
} | |
Q_ASSERT(SUCCEEDED(hr)); | |
IMediaSample *out; | |
hr = alloc->GetBuffer(&out, 0, 0, AM_GBF_NOTASYNCPOINT); | |
Q_ASSERT(SUCCEEDED(hr)); | |
//let's copy the sample | |
{ | |
REFERENCE_TIME start, end; | |
sample->GetTime(&start, &end); | |
out->SetTime(&start, &end); | |
} | |
hr = out->SetActualDataLength(length); | |
Q_ASSERT(SUCCEEDED(hr)); | |
hr = out->SetDiscontinuity(sample->IsDiscontinuity()); | |
Q_ASSERT(SUCCEEDED(hr)); | |
{ | |
LONGLONG start, end; | |
hr = sample->GetMediaTime(&start, &end); | |
if (hr != HRESULT(VFW_E_MEDIA_TIME_NOT_SET)) { | |
hr = out->SetMediaTime(&start, &end); | |
Q_ASSERT(SUCCEEDED(hr)); | |
} | |
} | |
AM_MEDIA_TYPE *type = 0; | |
hr = sample->GetMediaType(&type); | |
Q_ASSERT(SUCCEEDED(hr)); | |
hr = out->SetMediaType(type); | |
Q_ASSERT(SUCCEEDED(hr)); | |
hr = out->SetPreroll(sample->IsPreroll()); | |
Q_ASSERT(SUCCEEDED(hr)); | |
hr = out->SetSyncPoint(sample->IsSyncPoint()); | |
Q_ASSERT(SUCCEEDED(hr)); | |
BYTE *dest = 0, *src = 0; | |
hr = out->GetPointer(&dest); | |
Q_ASSERT(SUCCEEDED(hr)); | |
hr = sample->GetPointer(&src); | |
Q_ASSERT(SUCCEEDED(hr)); | |
qMemCopy(dest, src, sample->GetActualDataLength()); | |
return out; | |
} | |
} | |
} | |
QT_END_NAMESPACE |