mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 04:11:30 +00:00
626 lines
14 KiB
C++
626 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;
|
|
}
|