blob: 93c9763e4eb13040f7387525558b75c836a8c797 [file]
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#include <strmif.h>
#include "cmemallocator.hpp"
#include <vfwmsgs.h>
#include <cassert>
#include <new>
HRESULT CMemAllocator::CreateInstance(
ISampleFactory* pFactory,
IMemAllocator** ppResult)
{
if (pFactory == 0)
return E_INVALIDARG;
if (ppResult == 0)
return E_POINTER;
IMemAllocator*& pResult = *ppResult;
pResult = 0;
CMemAllocator* const p = new (std::nothrow) CMemAllocator(pFactory);
if (p == 0)
return E_OUTOFMEMORY;
pResult = p;
return S_OK;
}
CMemAllocator::CMemAllocator(ISampleFactory* pFactory) :
m_pSampleFactory(pFactory),
m_cRef(1),
m_cActive(-1), //means "properties not set"
m_bCommitted(false)
{
m_hCond = CreateEvent(0, 0, 0, 0);
assert(m_hCond); //TODO
const HRESULT hr = CLockable::Init();
hr;
assert(SUCCEEDED(hr)); //TODO
}
CMemAllocator::~CMemAllocator()
{
assert(m_cActive <= 0);
assert(m_samples.empty());
assert(!m_bCommitted);
const BOOL b = CloseHandle(m_hCond);
b;
assert(b);
m_pSampleFactory->Destroy(this);
}
HRESULT CMemAllocator::QueryInterface(const IID& iid, void** ppv)
{
if (ppv == 0)
return E_POINTER;
IUnknown*& pUnk = reinterpret_cast<IUnknown*&>(*ppv);
if (iid == __uuidof(IUnknown))
pUnk = this;
else if (iid == __uuidof(IMemAllocator))
pUnk = static_cast<IMemAllocator*>(this);
else
{
pUnk = 0;
return E_NOINTERFACE;
}
pUnk->AddRef();
return S_OK;
}
ULONG CMemAllocator::AddRef()
{
return InterlockedIncrement((LONG*)&m_cRef);
}
ULONG CMemAllocator::Release()
{
if (LONG n = InterlockedDecrement((LONG*)&m_cRef))
return n;
delete this;
return 0;
}
ULONG CMemAllocator::GetCount() const
{
return m_cRef;
}
HRESULT CMemAllocator::SetProperties(
ALLOCATOR_PROPERTIES* pPreferred,
ALLOCATOR_PROPERTIES* pActual)
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
if (m_bCommitted)
return VFW_E_ALREADY_COMMITTED;
if (m_cActive > 0)
return VFW_E_BUFFERS_OUTSTANDING;
if (pPreferred == 0) //weird
{
m_props.cBuffers = 0;
m_props.cbBuffer = 0;
m_props.cbAlign = 1;
m_props.cbPrefix = 0;
}
else
{
m_props = *pPreferred;
if (m_props.cBuffers < 0)
m_props.cBuffers = 0;
if (m_props.cbBuffer < 0)
m_props.cbBuffer = 0;
if (m_props.cbAlign <= 0)
m_props.cbAlign = 1;
if (m_props.cbPrefix < 0)
m_props.cbPrefix = 0;
}
if (pActual)
*pActual = m_props;
m_cActive = 0; //means "properties have been set"
return S_OK;
}
HRESULT CMemAllocator::GetProperties(ALLOCATOR_PROPERTIES* p)
{
if (p == 0)
return E_POINTER;
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
if (m_cActive < 0)
return VFW_E_SIZENOTSET;
*p = m_props;
return S_OK;
}
HRESULT CMemAllocator::Commit()
{
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
if (m_bCommitted)
return S_OK;
if (m_cActive > 0)
return VFW_E_BUFFERS_OUTSTANDING;
if (m_cActive < 0)
return VFW_E_SIZENOTSET;
assert(m_samples.empty());
for (long i = 0; i < m_props.cBuffers; ++i)
{
hr = CreateSample();
if (FAILED(hr))
return hr;
}
m_bCommitted = true;
return S_OK;
}
HRESULT CMemAllocator::Decommit()
{
//NOTE: If there are any threads waiting on
//GetBuffer, then that op is supposed to
//finish, returning an error.
//
//Further calls to GetBuffer should fail,
//until Commit() is called.
//
//This op does *not* affect outstanding
//buffers, so it's perfectly legal for
//m_active to have a non-zero value.
//
//The purpose of this op is to prevent a
//filter from getting any more samples.
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
m_bCommitted = false;
while (!m_samples.empty())
{
IMemSample* const sample = m_samples.front();
assert(sample);
m_samples.pop_front();
hr = m_pSampleFactory->DestroySample(sample);
assert(SUCCEEDED(hr));
}
const BOOL b = SetEvent(m_hCond);
b;
assert(b);
return S_OK;
}
HRESULT CMemAllocator::GetBuffer(
IMediaSample** pp,
REFERENCE_TIME*,
REFERENCE_TIME*,
DWORD flags)
{
if (pp == 0)
return E_POINTER;
Lock lock;
if (flags & AM_GBF_NOWAIT)
{
const HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
if (!m_bCommitted)
return VFW_E_NOT_COMMITTED;
assert(m_cActive >= 0);
if (m_cActive >= m_props.cBuffers) //no samples available
return VFW_E_TIMEOUT;
}
else
{
for (;;)
{
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
if (!m_bCommitted)
return VFW_E_NOT_COMMITTED;
assert(m_cActive >= 0);
if (m_cActive < m_props.cBuffers)
break;
lock.Release();
DWORD index;
hr = CoWaitForMultipleHandles(
0, //wait all
INFINITE,
1,
&m_hCond,
&index);
assert(hr == S_OK);
assert(index == 0);
}
}
IMediaSample*& p = *pp;
p = GetSample();
AddRef(); //the contribution of this (active) sample
return S_OK;
}
HRESULT CMemAllocator::ReleaseBuffer(IMediaSample* p)
{
if (p == 0) //should only be called by sample, in its Release method
return E_INVALIDARG;
//TODO: use custom interface for this
//assert(p->m_cRef == 0);
//assert(p->m_pAllocator == this);
Lock lock;
HRESULT hr = lock.Seize(this);
if (FAILED(hr))
return hr;
assert(m_cActive > 0);
--m_cActive;
IMemSample* pSample;
hr = p->QueryInterface(&pSample);
assert(SUCCEEDED(hr));
assert(pSample);
hr = m_pSampleFactory->FinalizeSample(pSample);
assert(SUCCEEDED(hr));
if (m_bCommitted)
m_samples.push_back(pSample);
else
{
hr = m_pSampleFactory->DestroySample(pSample);
assert(SUCCEEDED(hr));
}
const BOOL b = SetEvent(m_hCond);
b;
assert(b);
//Release lock now, in case this sample is holding
//the last reference to this allocator.
lock.Release();
//Now it's safe to release the allocator, after
//releasing the lock; if this causes the allocator
//to be destroyed, then the lock won't attempt to
//manipulate the allocator during its own destruction
//(another thing to try would be to add a ref to the
//allocator in the lock's ctor, and then release the
//ref in its dtor. This would prevent the destruction
//of the allocator until the lock itself is destroyed).
Release(); //the contribution of this sample
return S_OK;
}
HRESULT CMemAllocator::CreateSample()
{
IMemSample* pSample;
const HRESULT hr = m_pSampleFactory->CreateSample(this, pSample);
if (FAILED(hr))
return hr;
assert(pSample);
assert(pSample->GetCount() == 0);
m_samples.push_back(pSample);
return S_OK;
}
IMediaSample* CMemAllocator::GetSample()
{
assert(m_cActive >= 0);
assert(m_cActive < m_props.cBuffers);
assert(!m_samples.empty());
IMemSample* const p = m_samples.front();
assert(p);
m_samples.pop_front();
HRESULT hr = m_pSampleFactory->InitializeSample(p);
assert(SUCCEEDED(hr));
assert(p->GetCount() == 0);
++m_cActive;
IMediaSample* pSample;
hr = p->QueryInterface(&pSample);
assert(SUCCEEDED(hr));
assert(pSample);
assert(p->GetCount() == 1);
return pSample;
}