//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1996 - 1997 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
/* Sequential allocator methods */
#include <streams.h>
#include <alloc.h>
CSequentialAllocator::CSequentialAllocator(
LPUNKNOWN pUnk,
HRESULT *phr
) : CMemAllocator(TEXT("CSequential Allocator"), pUnk, phr),
m_pbNext(m_pBuffer),
m_parSamples(NULL),
m_lValid(0),
m_pbStartValid(NULL)
{
}
CSequentialAllocator::~CSequentialAllocator()
{
if (m_parSamples != NULL) {
delete [] m_parSamples;
}
}
STDMETHODIMP CSequentialAllocator::GetBuffer(
IMediaSample **ppBuffer,
REFERENCE_TIME * pStartTime,
REFERENCE_TIME * pEndTime,
DWORD dwFlags
)
{
/* Like the normal version except we will only allocate the NEXT
buffer
*/
UNREFERENCED_PARAMETER(pStartTime);
UNREFERENCED_PARAMETER(pEndTime);
UNREFERENCED_PARAMETER(dwFlags);
CMediaSample *pSample = NULL;
*ppBuffer = NULL;
for (;;)
{
{ // scope for lock
CAutoLock cObjectLock(this);
/* Check we are committed */
if (!m_bCommitted) {
return VFW_E_NOT_COMMITTED;
}
/* Check if the one we want is there */
CMediaSample *pSearch = m_lFree.Head();
while (pSearch) {
PBYTE pbBuffer;
pSearch->GetPointer(&pbBuffer);
if (pbBuffer == m_pbNext) {
m_lFree.Remove(pSearch);
pSample = pSearch;
ASSERT(m_lSize == pSample->GetSize());
m_pbNext += m_lSize;
if (m_pbNext == m_pBuffer + m_lSize * m_lCount) {
m_pbNext = m_pBuffer;
}
break;
} else {
pSearch = m_lFree.Next(pSearch);
}
}
if (pSample == NULL) {
/* If there were some samples but just not ours someone
else may be waiting
*/
if (m_lFree.GetCount() != 0) {
NotifySample();
}
SetWaiting();
}
}
/* If we didn't get a sample then wait for the list to signal */
if (pSample) {
break;
}
ASSERT(m_hSem != NULL);
DbgLog((LOG_TRACE, 4, TEXT("Waiting - %d buffers available"),
m_lFree.GetCount()));
WaitForSingleObject(m_hSem, INFINITE);
}
#ifdef VFW_S_CANT_CUE
/* Addref the buffer up to one. On release
back to zero instead of being deleted, it will requeue itself by
calling the ReleaseBuffer member function. NOTE the owner of a
media sample must always be derived from CBaseAllocator */
ASSERT(pSample->m_cRef == 0);
pSample->m_cRef = 1;
*ppBuffer = pSample;
#else
/* This QueryInterface should addref the buffer up to one. On release
back to zero instead of being deleted, it will requeue itself by
calling the ReleaseBuffer member function. NOTE the owner of a
media sample must always be derived from CBaseAllocator */
HRESULT hr = pSample->QueryInterface(IID_IMediaSample, (void **)ppBuffer);
/* For each sample outstanding, we need to AddRef ourselves on his behalf
he cannot do it, as there is no correct ordering of his release and his
call to ReleaseBuffer as both could destroy him. We release this count
in ReleaseBuffer, called when the sample's count drops to zero */
AddRef();
#endif
return NOERROR;
}
HRESULT CSequentialAllocator::Alloc()
{
CAutoLock lck(this);
if (m_parSamples != NULL) {
delete [] m_parSamples;
}
m_parSamples = new LPCMEDIASAMPLE[m_lCount];
if (m_parSamples == NULL) {
return E_OUTOFMEMORY;
}
HRESULT hr = CMemAllocator::Alloc();
if (S_OK == hr) {
ASSERT(m_lCount == m_lFree.GetCount());
/* Find the smallest */
CMediaSample *pSample = m_lFree.Head();
m_pBuffer = (PBYTE)(DWORD)-1;
for (; pSample != NULL; pSample = m_lFree.Next(pSample)) {
PBYTE pbTemp;
pSample->GetPointer(&pbTemp);
if (m_pBuffer > pbTemp) {
m_pBuffer = pbTemp;
}
}
pSample = m_lFree.Head();
for ( ;pSample != NULL; pSample = m_lFree.Next(pSample)) {
PBYTE pbTemp;
pSample->GetPointer(&pbTemp);
m_parSamples[BufferIndex(pbTemp)] = pSample;
}
}
m_pbStartValid = m_pBuffer;
m_pbNext = m_pBuffer;
ASSERT(m_lValid == 0);
return hr;
}
/* Get buffer index */
int CSequentialAllocator::BufferIndex(PBYTE pbBuffer)
{
int iPos = (pbBuffer - m_pBuffer) / m_lSize;
ASSERT(iPos < m_lCount);
return iPos;
}
/* Given an address get the IMediaSample pointer -
NB needs optimizing
*/
CMediaSample *CSequentialAllocator::SampleFromBuffer(PBYTE pBuffer)
{
return m_parSamples[BufferIndex(pBuffer)];
}
/* Add a buffer to the valid list */
void CSequentialAllocator::AddBuffer(CMediaSample *pSample)
{
/* Don't get fooled by 0 length buffers ! */
if (pSample->GetActualDataLength() == 0) {
return;
}
pSample->AddRef();
#ifdef DEBUG
PBYTE pbBuffer;
pSample->GetPointer(&pbBuffer);
ASSERT(m_pbStartValid + m_lValid == pbBuffer);
#endif
m_lValid += pSample->GetActualDataLength();
}
/* Step through valid data */
HRESULT CSequentialAllocator::Advance(LONG lAdvance)
{
/* For every sample boundary we step over we should Release()
a buffer
*/
int iStart = BufferIndex(m_pbStartValid);
int iEnd = BufferIndex(m_pbStartValid + lAdvance);
m_lValid -= lAdvance;
m_pbStartValid += lAdvance;
/* If we're at the end and the last buffer wasn't full move on
to the next
*/
if (m_lValid == 0 &&
(m_pbStartValid - m_pBuffer) % m_lSize != 0) {
iEnd++;
m_pbStartValid = m_pBuffer + m_lSize * iEnd;
}
while (iStart != iEnd) {
m_parSamples[iStart]->Release();
iStart++;
}
ASSERT(m_lValid <= (m_lCount * m_lSize) / 2);
/*
If we're already into the last buffer and about to wrap
NOTE m_Valid CAN be > m_lSize - hit it with a huge
PROGRAM_STREAM_DIRECTORY packet (can be almost 64K)
*/
if (m_pbStartValid + m_lValid == m_pBuffer + m_lSize * m_lCount) {
return Wrap();
} else {
return S_OK;
}
}
/* Get the valid part */
PBYTE CSequentialAllocator::GetValid(LONG *plValid)
{
*plValid = m_lValid;
return m_pbStartValid;
}
/* Wrap end to go back to start */
HRESULT CSequentialAllocator::Wrap(void)
{
if (m_lValid != 0) {
/* Make sure the copy will work */
ASSERT(m_lValid <= (m_lSize * m_lCount) / 2);
IMediaSample *pSample;
/* These samples will be AddRef'd already */
int nBuffers = (m_lValid + m_lSize - 1) / m_lSize;
ASSERT(nBuffers <= m_lCount / 2);
for (int i = 0; i < nBuffers; i++) {
HRESULT hr = GetBuffer(&pSample, NULL, NULL, 0);
if (FAILED(hr)) {
return hr;
}
ASSERT(pSample == m_parSamples[i]);
}
/* Now copy the data back to the start */
CopyMemory((PVOID)(m_pBuffer + m_lSize * nBuffers - m_lValid),
(PVOID)m_pbStartValid,
m_lValid);
m_pbStartValid = m_pBuffer + m_lSize * nBuffers - m_lValid;
/* Release the last buffers since we've effectively
transferred the ref count to the first one
*/
for ( ; nBuffers > 0; nBuffers--) {
m_parSamples[m_lCount - nBuffers]->Release();
}
} else {
m_pbStartValid = m_pBuffer;
}
return S_OK;
}
/* Flush the allocator - just discard all the data in it */
void CSequentialAllocator::Flush()
{
Advance(m_lValid);
}