reactos/dll/directx/ksproxy/allocator.cpp

625 lines
14 KiB
C++

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS WDM Streaming ActiveMovie Proxy
* FILE: dll/directx/ksproxy/allocator.cpp
* PURPOSE: IKsAllocator interface
*
* PROGRAMMERS: Johannes Anderwald (johannes.anderwald@reactos.org)
*/
#include "precomp.h"
const GUID IID_IKsAllocatorEx = {0x091bb63a, 0x603f, 0x11d1, {0xb0, 0x67, 0x00, 0xa0, 0xc9, 0x06, 0x28, 0x02}};
const GUID IID_IKsAllocator = {0x8da64899, 0xc0d9, 0x11d0, {0x84, 0x13, 0x00, 0x00, 0xf8, 0x22, 0xfe, 0x8a}};
class CKsAllocator : public IKsAllocatorEx,
public IMemAllocatorCallbackTemp
{
public:
typedef std::stack<IMediaSample *>MediaSampleStack;
typedef std::list<IMediaSample *>MediaSampleList;
STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
STDMETHODIMP_(ULONG) AddRef()
{
InterlockedIncrement(&m_Ref);
return m_Ref;
}
STDMETHODIMP_(ULONG) Release()
{
InterlockedDecrement(&m_Ref);
if (!m_Ref)
{
delete this;
return 0;
}
return m_Ref;
}
//IKsAllocator
HANDLE STDMETHODCALLTYPE KsGetAllocatorHandle();
KSALLOCATORMODE STDMETHODCALLTYPE KsGetAllocatorMode();
HRESULT STDMETHODCALLTYPE KsGetAllocatorStatus(PKSSTREAMALLOCATOR_STATUS AllocatorStatus);
VOID STDMETHODCALLTYPE KsSetAllocatorMode(KSALLOCATORMODE Mode);
//IKsAllocatorEx
PALLOCATOR_PROPERTIES_EX STDMETHODCALLTYPE KsGetProperties();
VOID STDMETHODCALLTYPE KsSetProperties(PALLOCATOR_PROPERTIES_EX Properties);
VOID STDMETHODCALLTYPE KsSetAllocatorHandle(HANDLE AllocatorHandle);
HANDLE STDMETHODCALLTYPE KsCreateAllocatorAndGetHandle(IKsPin* KsPin);
//IMemAllocator
HRESULT STDMETHODCALLTYPE SetProperties(ALLOCATOR_PROPERTIES *pRequest, ALLOCATOR_PROPERTIES *pActual);
HRESULT STDMETHODCALLTYPE GetProperties(ALLOCATOR_PROPERTIES *pProps);
HRESULT STDMETHODCALLTYPE Commit();
HRESULT STDMETHODCALLTYPE Decommit();
HRESULT STDMETHODCALLTYPE GetBuffer(IMediaSample **ppBuffer, REFERENCE_TIME *pStartTime, REFERENCE_TIME *pEndTime, DWORD dwFlags);
HRESULT STDMETHODCALLTYPE ReleaseBuffer(IMediaSample *pBuffer);
//IMemAllocatorCallbackTemp
HRESULT STDMETHODCALLTYPE SetNotify(IMemAllocatorNotifyCallbackTemp *pNotify);
HRESULT STDMETHODCALLTYPE GetFreeCount(LONG *plBuffersFree);
CKsAllocator();
virtual ~CKsAllocator(){}
VOID STDMETHODCALLTYPE FreeMediaSamples();
protected:
LONG m_Ref;
HANDLE m_hAllocator;
KSALLOCATORMODE m_Mode;
ALLOCATOR_PROPERTIES_EX m_Properties;
IMemAllocatorNotifyCallbackTemp *m_Notify;
ULONG m_Allocated;
LONG m_cbBuffer;
LONG m_cBuffers;
LONG m_cbAlign;
LONG m_cbPrefix;
BOOL m_Committed;
CRITICAL_SECTION m_CriticalSection;
MediaSampleStack m_FreeList;
MediaSampleList m_UsedList;
LPVOID m_Buffer;
BOOL m_FreeSamples;
};
HRESULT
STDMETHODCALLTYPE
CKsAllocator::QueryInterface(
IN REFIID refiid,
OUT PVOID* Output)
{
if (IsEqualGUID(refiid, IID_IUnknown) ||
IsEqualGUID(refiid, IID_IKsAllocator) ||
IsEqualGUID(refiid, IID_IKsAllocatorEx))
{
*Output = PVOID(this);
reinterpret_cast<IUnknown*>(*Output)->AddRef();
return NOERROR;
}
if (IsEqualGUID(refiid, IID_IMemAllocator) ||
IsEqualGUID(refiid, IID_IMemAllocatorCallbackTemp))
{
*Output = (IMemAllocatorCallbackTemp*)(this);
reinterpret_cast<IMemAllocatorCallbackTemp*>(*Output)->AddRef();
return NOERROR;
}
return E_NOINTERFACE;
}
CKsAllocator::CKsAllocator() : m_Ref(0),
m_hAllocator(0),
m_Mode(KsAllocatorMode_User),
m_Notify(0),
m_Allocated(0),
m_cbBuffer(0),
m_cBuffers(0),
m_cbAlign(0),
m_cbPrefix(0),
m_Committed(FALSE),
m_FreeList(),
m_UsedList(),
m_Buffer(0),
m_FreeSamples(FALSE)
{
InitializeCriticalSection(&m_CriticalSection);
}
//-------------------------------------------------------------------
// IMemAllocator
//
HRESULT
STDMETHODCALLTYPE
CKsAllocator::SetProperties(
ALLOCATOR_PROPERTIES *pRequest,
ALLOCATOR_PROPERTIES *pActual)
{
SYSTEM_INFO SystemInfo;
EnterCriticalSection(&m_CriticalSection);
#ifdef KSPROXY_TRACE
OutputDebugStringW(L"CKsAllocator::SetProperties\n");
#endif
if (!pRequest || !pActual)
return E_POINTER;
// zero output properties
ZeroMemory(pActual, sizeof(ALLOCATOR_PROPERTIES));
// get system info
GetSystemInfo(&SystemInfo);
if (!pRequest->cbAlign || (pRequest->cbAlign - 1) & SystemInfo.dwAllocationGranularity)
{
// bad alignment
LeaveCriticalSection(&m_CriticalSection);
return VFW_E_BADALIGN;
}
if (m_Mode == KsAllocatorMode_Kernel)
{
// u can't change a kernel allocator
LeaveCriticalSection(&m_CriticalSection);
return VFW_E_ALREADY_COMMITTED;
}
if (m_Committed)
{
// need to decommit first
LeaveCriticalSection(&m_CriticalSection);
return VFW_E_ALREADY_COMMITTED;
}
if (m_Allocated != m_FreeList.size())
{
// outstanding buffers
LeaveCriticalSection(&m_CriticalSection);
return VFW_E_BUFFERS_OUTSTANDING;
}
pActual->cbAlign = m_cbAlign = pRequest->cbAlign;
pActual->cbBuffer = m_cbBuffer = pRequest->cbBuffer;
pActual->cbPrefix = m_cbPrefix = pRequest->cbPrefix;
pActual->cBuffers = m_cBuffers = pRequest->cBuffers;
LeaveCriticalSection(&m_CriticalSection);
return NOERROR;
}
HRESULT
STDMETHODCALLTYPE
CKsAllocator::GetProperties(
ALLOCATOR_PROPERTIES *pProps)
{
if (!pProps)
return E_POINTER;
pProps->cbBuffer = m_cbBuffer;
pProps->cBuffers = m_cBuffers;
pProps->cbAlign = m_cbAlign;
pProps->cbPrefix = m_cbPrefix;
return NOERROR;
}
HRESULT
STDMETHODCALLTYPE
CKsAllocator::Commit()
{
LONG Index;
PUCHAR CurrentBuffer;
IMediaSample * Sample;
HRESULT hr;
//TODO integer overflow checks
EnterCriticalSection(&m_CriticalSection);
#ifdef KSPROXY_TRACE
OutputDebugStringW(L"CKsAllocator::Commit\n");
#endif
if (m_Mode == KsAllocatorMode_Kernel)
{
/* no-op for kernel allocator */
LeaveCriticalSection(&m_CriticalSection);
return NOERROR;
}
if (m_Committed)
{
// already committed
LeaveCriticalSection(&m_CriticalSection);
return NOERROR;
}
if (m_cbBuffer < 0 || m_cBuffers < 0 || m_cbPrefix < 0)
{
// invalid parameter
LeaveCriticalSection(&m_CriticalSection);
return E_OUTOFMEMORY;
}
LONG Size = m_cbBuffer + m_cbPrefix;
if (m_cbAlign > 1)
{
//check alignment
LONG Mod = Size % m_cbAlign;
if (Mod)
{
// calculate aligned size
Size += m_cbAlign - Mod;
}
}
LONG TotalSize = Size * m_cBuffers;
assert(TotalSize);
assert(m_cBuffers);
assert(Size);
// now allocate buffer
m_Buffer = VirtualAlloc(NULL, TotalSize, MEM_COMMIT, PAGE_READWRITE);
if (!m_Buffer)
{
LeaveCriticalSection(&m_CriticalSection);
return E_OUTOFMEMORY;
}
ZeroMemory(m_Buffer, TotalSize);
CurrentBuffer = (PUCHAR)m_Buffer;
for (Index = 0; Index < m_cBuffers; Index++)
{
// construct media sample
hr = CMediaSample_Constructor((IMemAllocator*)this, CurrentBuffer + m_cbPrefix, m_cbBuffer, IID_IMediaSample, (void**)&Sample);
if (FAILED(hr))
{
LeaveCriticalSection(&m_CriticalSection);
return E_OUTOFMEMORY;
}
// add to free list
m_FreeList.push(Sample);
m_Allocated++;
//next sample buffer
CurrentBuffer += Size;
}
// we are now committed
m_Committed = true;
LeaveCriticalSection(&m_CriticalSection);
return S_OK;
}
HRESULT
STDMETHODCALLTYPE
CKsAllocator::Decommit()
{
EnterCriticalSection(&m_CriticalSection);
#ifdef KSPROXY_TRACE
OutputDebugStringW(L"CKsAllocator::Decommit\n");
#endif
if (m_Mode == KsAllocatorMode_Kernel)
{
/* no-op for kernel allocator */
LeaveCriticalSection(&m_CriticalSection);
return NOERROR;
}
m_Committed = false;
if (m_Allocated != m_FreeList.size())
{
// outstanding buffers
m_FreeSamples = true;
LeaveCriticalSection(&m_CriticalSection);
return NOERROR;
}
else
{
// no outstanding buffers
// free to free them
FreeMediaSamples();
}
LeaveCriticalSection(&m_CriticalSection);
return NOERROR;
}
HRESULT
STDMETHODCALLTYPE
CKsAllocator::GetBuffer(
IMediaSample **ppBuffer,
REFERENCE_TIME *pStartTime,
REFERENCE_TIME *pEndTime,
DWORD dwFlags)
{
IMediaSample * Sample = NULL;
if (!m_Committed)
return VFW_E_NOT_COMMITTED;
do
{
EnterCriticalSection(&m_CriticalSection);
if (!m_FreeList.empty())
{
OutputDebugStringW(L"CKsAllocator::GetBuffer HACK\n");
Sample = m_FreeList.top();
m_FreeList.pop();
}
LeaveCriticalSection(&m_CriticalSection);
if (dwFlags & AM_GBF_NOWAIT)
{
// never wait untill a buffer becomes available
break;
}
}
while(Sample == NULL);
if (!Sample)
{
// no sample acquired
//HACKKKKKKK
Sample = m_UsedList.back();
m_UsedList.pop_back();
if (!Sample)
return VFW_E_TIMEOUT;
}
// store result
*ppBuffer = Sample;
// store sample in used list
m_UsedList.push_front(Sample);
// done
return NOERROR;
}
HRESULT
STDMETHODCALLTYPE
CKsAllocator::ReleaseBuffer(
IMediaSample *pBuffer)
{
EnterCriticalSection(&m_CriticalSection);
#ifdef KSPROXY_TRACE
OutputDebugStringW(L"CKsAllocator::ReleaseBuffer\n");
#endif
// media sample always 1 ref count in free list
pBuffer->AddRef();
// add the sample to the free list
m_FreeList.push(pBuffer);
if (m_FreeSamples)
{
// pending de-commit
if (m_FreeList.size () == m_Allocated)
{
FreeMediaSamples();
}
}
if (m_Notify)
{
//notify caller of an available buffer
m_Notify->NotifyRelease();
}
LeaveCriticalSection(&m_CriticalSection);
return S_OK;
}
//-------------------------------------------------------------------
// IMemAllocatorCallbackTemp
//
HRESULT
STDMETHODCALLTYPE
CKsAllocator::SetNotify(
IMemAllocatorNotifyCallbackTemp *pNotify)
{
EnterCriticalSection(&m_CriticalSection);
#ifdef KSPROXY_TRACE
OutputDebugStringW(L"CKsAllocator::SetNotify\n");
#endif
if (pNotify)
pNotify->AddRef();
if (m_Notify)
m_Notify->Release();
m_Notify = pNotify;
LeaveCriticalSection(&m_CriticalSection);
return NOERROR;
}
HRESULT
STDMETHODCALLTYPE
CKsAllocator::GetFreeCount(
LONG *plBuffersFree)
{
*plBuffersFree = m_Allocated - m_FreeList.size();
return S_OK;
}
//-------------------------------------------------------------------
// IKsAllocator
//
HANDLE
STDMETHODCALLTYPE
CKsAllocator::KsGetAllocatorHandle()
{
return m_hAllocator;
}
KSALLOCATORMODE
STDMETHODCALLTYPE
CKsAllocator::KsGetAllocatorMode()
{
return m_Mode;
}
HRESULT
STDMETHODCALLTYPE
CKsAllocator::KsGetAllocatorStatus(
PKSSTREAMALLOCATOR_STATUS AllocatorStatus)
{
return NOERROR;
}
VOID
STDMETHODCALLTYPE
CKsAllocator::KsSetAllocatorMode(
KSALLOCATORMODE Mode)
{
m_Mode = Mode;
}
//-------------------------------------------------------------------
// IKsAllocatorEx
//
PALLOCATOR_PROPERTIES_EX
STDMETHODCALLTYPE
CKsAllocator::KsGetProperties()
{
return &m_Properties;
}
VOID
STDMETHODCALLTYPE
CKsAllocator::KsSetProperties(
PALLOCATOR_PROPERTIES_EX Properties)
{
CopyMemory(&m_Properties, Properties, sizeof(ALLOCATOR_PROPERTIES_EX));
}
VOID
STDMETHODCALLTYPE
CKsAllocator::KsSetAllocatorHandle(
HANDLE AllocatorHandle)
{
m_hAllocator = AllocatorHandle;
}
HANDLE
STDMETHODCALLTYPE
CKsAllocator::KsCreateAllocatorAndGetHandle(
IKsPin* KsPin)
{
HRESULT hr;
IKsObject * pObject;
KSALLOCATOR_FRAMING AllocatorFraming;
HANDLE hPin;
#ifdef KSPROXY_TRACE
OutputDebugStringW(L"CKsAllocator::KsCreateAllocatorAndGetHandle\n");
#endif
if (m_hAllocator)
{
CloseHandle(m_hAllocator);
m_hAllocator = NULL;
}
// get pin IKsObject interface
hr = KsPin->QueryInterface(IID_IKsObject, (void**)&pObject);
if (FAILED(hr))
return NULL;
// get pin handle
hPin = pObject->KsGetObjectHandle();
//release IKsObject interface
pObject->Release();
if (!hPin || hPin == INVALID_HANDLE_VALUE)
return NULL;
//setup allocator framing
AllocatorFraming.Frames = m_Properties.cBuffers;
AllocatorFraming.FrameSize = m_Properties.cbBuffer;
AllocatorFraming.FileAlignment = (m_Properties.cbAlign -1);
AllocatorFraming.OptionsFlags = KSALLOCATOR_OPTIONF_SYSTEM_MEMORY;
AllocatorFraming.PoolType = (m_Properties.LogicalMemoryType == KS_MemoryTypeKernelPaged);
DWORD dwError = KsCreateAllocator(hPin, &AllocatorFraming, &m_hAllocator);
if (dwError)
return NULL;
return m_hAllocator;
}
//-------------------------------------------------------------------
VOID
STDMETHODCALLTYPE
CKsAllocator::FreeMediaSamples()
{
ULONG Index;
for(Index = 0; Index < m_FreeList.size(); Index++)
{
IMediaSample * Sample = m_FreeList.top();
m_FreeList.pop();
Sample->Release();
}
m_FreeSamples = false;
m_Allocated = 0;
if (m_Buffer)
{
// release buffer
VirtualFree(m_Buffer, 0, MEM_RELEASE);
m_Buffer = NULL;
}
}
HRESULT
WINAPI
CKsAllocator_Constructor(
IUnknown * pUnkOuter,
REFIID riid,
LPVOID * ppv)
{
#ifdef KSPROXY_TRACE
OutputDebugStringW(L"CKsAllocator_Constructor\n");
#endif
CKsAllocator * handler = new CKsAllocator();
if (!handler)
return E_OUTOFMEMORY;
if (FAILED(handler->QueryInterface(riid, ppv)))
{
/* not supported */
delete handler;
return E_NOINTERFACE;
}
return NOERROR;
}