/* 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 "qmeminputpin.h"
#include "qbasefilter.h"
#include "compointer.h"
#include
QT_BEGIN_NAMESPACE
namespace Phonon
{
namespace DS9
{
QMemInputPin::QMemInputPin(QBaseFilter *parent, const QVector &mt, bool transform) :
QPin(parent, PINDIR_INPUT, mt), m_shouldDuplicateSamples(true), m_transform(transform)
{
}
QMemInputPin::~QMemInputPin()
{
}
STDMETHODIMP QMemInputPin::QueryInterface(REFIID iid, void **out)
{
if (!out) {
return E_POINTER;
}
if (iid == IID_IMemInputPin) {
*out = static_cast(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);
for(int i = 0; i < m_outputs.count(); ++i) {
IPin *conn = m_outputs.at(i)->connected();
if (conn) {
conn->EndOfStream();
}
}
return S_OK;
}
STDMETHODIMP QMemInputPin::BeginFlush()
{
//pass downstream
for(int i = 0; i < m_outputs.count(); ++i) {
IPin *conn = m_outputs.at(i)->connected();
if (conn) {
conn->BeginFlush();
}
}
QWriteLocker locker(&m_lock);
m_flushing = true;
return S_OK;
}
STDMETHODIMP QMemInputPin::EndFlush()
{
//pass downstream
for(int i = 0; i < m_outputs.count(); ++i) {
IPin *conn = m_outputs.at(i)->connected();
if (conn) {
conn->EndFlush();
}
}
QWriteLocker locker(&m_lock);
m_flushing = false;
return S_OK;
}
STDMETHODIMP QMemInputPin::NewSegment(REFERENCE_TIME start, REFERENCE_TIME stop, double rate)
{
for(int i = 0; i < m_outputs.count(); ++i) {
m_outputs.at(i)->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) {
//we tell the output pins that they should connect with this type
for(int i = 0; i < m_outputs.count(); ++i) {
hr = m_outputs.at(i)->setAcceptedMediaType(connectedType());
if (FAILED(hr)) {
break;
}
}
}
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;
}
{
QWriteLocker locker(&m_lock);
m_shouldDuplicateSamples = m_transform && readonly;
}
setMemoryAllocator(alloc);
for(int i = 0; i < m_outputs.count(); ++i) {
IPin *pin = m_outputs.at(i)->connected();
if (pin) {
ComPointer input(pin, 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;
}
}
for (int i = 0; i < m_outputs.count(); ++i) {
QPin *current = m_outputs.at(i);
IMediaSample *outSample = m_shouldDuplicateSamples ?
duplicateSampleForOutput(sample, current->memoryAllocator())
: sample;
if (m_shouldDuplicateSamples) {
m_parent->processSample(outSample);
}
IPin *pin = current->connected();
if (pin) {
ComPointer input(pin, 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 they can block
for(int i = 0; i < m_outputs.count(); ++i) {
IPin *input = m_outputs.at(i)->connected();
if (input) {
ComPointer meminput(input, IID_IMemInputPin);
if (meminput && meminput->ReceiveCanBlock() != S_FALSE) {
return S_OK;
}
}
}
return S_FALSE;
}
//addition
//this should be used by the filter to tell its input pins to which output they should route the samples
void QMemInputPin::addOutput(QPin *output)
{
QWriteLocker locker(&m_lock);
m_outputs += output;
}
void QMemInputPin::removeOutput(QPin *output)
{
QWriteLocker locker(&m_lock);
m_outputs.removeOne(output);
}
QList QMemInputPin::outputs() const
{
QReadLocker locker(&m_lock);
return m_outputs;
}
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