windows-nt/Source/XPSP1/NT/multimedia/directx/dplay/dvoice/dxvutils/innerque.cpp
2020-09-26 16:20:57 +08:00

622 lines
16 KiB
C++

/*==========================================================================
*
* Copyright (C) 1999 Microsoft Corporation. All Rights Reserved.
*
* File: innerque.cpp
* Content:
*
* History:
* Date By Reason
* ==== == ======
* 07/16/99 pnewson Created
* 07/27/99 pnewson Overhauled to support new message numbering method
* 08/03/99 pnewson General clean up
* 08/24/99 rodtoll Fixed for release builds -- removed m_wQueueId from debug block
* 10/28/99 pnewson Bug #113933 debug spew too verbose
* implement inner queue pool code
* 10/29/99 rodtoll Bug #113726 - Integrate Voxware Codecs. Plugged memory leak
* caused as a result of new architecture.
* 01/14/2000 rodtoll Updated to use new Frame SetEqual function
* 01/31/2000 pnewson replace SAssert with DNASSERT
* 06/28/2000 rodtoll Prefix Bug #38022
*
***************************************************************************/
#include "dxvutilspch.h"
#undef DPF_SUBCOMP
#define DPF_SUBCOMP DN_SUBCOMP_VOICE
#define MODULE_ID INNERQUEUE
// the number of slots reseved to account for
// out of order startup frames. For example, if the first
// three frames of a message arrive in the order 3, 2, 1, instead
// of 1, 2, 3, we must reserve 2 slots in front of the "first"
// frame (3) so we'll have a place to put the tardy 1 and 2.
const BYTE c_bNumStartSlots = 2;
#undef DPF_MODNAME
#define DPF_MODNAME "CInnerQueue::CInnerQueue"
CInnerQueue::CInnerQueue(
BYTE bNumSlots,
WORD wFrameSize,
CFramePool* pfpFramePool,
DNCRITICAL_SECTION* pcsQueue,
BYTE bMsgNum,
BYTE bHighWaterMark,
WORD wQueueId
)
: m_bNumSlots(bNumSlots)
, m_eState(CInnerQueue::empty)
, m_bHighWaterMark(bHighWaterMark)
, m_bQueueSize(0)
, m_bHeadSeqNum(0)
, m_fFirstDequeue(true)
//, m_rgeSlotStates(NULL)
, m_rgpfrSlots(NULL)
, m_bFillingDequeueReqs(0)
, m_wMissingFrames(0)
, m_wDuplicateFrames(0)
, m_wOverflowFrames(0)
, m_wLateFrames(0)
, m_wPossibleZeroLengthDequeues(0)
, m_wKnownZeroLengthDequeues(0)
, m_dwMsgLen(0)
, m_wQueueId(wQueueId)
, m_bMsgNum(bMsgNum)
, m_pfpFramePool(pfpFramePool)
, m_pcsQueue(pcsQueue)
, m_fInited(FALSE)
{
#if defined(DPVOICE_QUEUE_DEBUG)
DPFX(DPFPREP, DVF_INFOLEVEL, "** QUEUE ** %i:%i ** CInnerQueue::CInnerQueue() CFramePool: %p", m_wQueueId, m_bMsgNum, m_pfpFramePool);
#endif
// verify that bNumSlots is at least 8, and is a
// power of 2.
DNASSERT(bNumSlots == 0x08 ||
bNumSlots == 0x10 ||
bNumSlots == 0x20 ||
bNumSlots == 0x40 ||
bNumSlots == 0x80);
// Check to make sure the watermark is not larger
// than the number of slots. It should really be
// significantly less than bNumSlots, but oh well.
//
DNASSERT(bHighWaterMark < bNumSlots - c_bNumStartSlots);
}
#undef DPF_MODNAME
#define DPF_MODNAME "CInnerQueue::Init"
HRESULT CInnerQueue::Init()
{
int i;
/*
// allocate the slot state array
m_rgeSlotStates = new ESlotState[m_bNumSlots];
if (m_rgeSlotStates == NULL)
{
goto error;
}
*/
// allocate the slot array
m_rgpfrSlots = new CFrame*[m_bNumSlots];
if (m_rgpfrSlots == NULL)
{
goto error;
}
// Initialize the slot states and the slots
for (i = 0; i < m_bNumSlots; ++i)
{
//m_rgeSlotStates[i] = essEmpty;
m_rgpfrSlots[i] = NULL;
}
m_fInited = TRUE;
return S_OK;
error:
/*
if (m_rgeSlotStates != NULL)
{
delete [] m_rgeSlotStates;
m_rgeSlotStates = NULL;
}
*/
if (m_rgpfrSlots != NULL)
{
delete [] m_rgpfrSlots;
m_rgpfrSlots = NULL;
}
m_fInited = FALSE;
return E_FAIL;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CInnerQueue::~CInnerQueue"
CInnerQueue::~CInnerQueue()
{
if (m_fInited)
{
/*
if (m_rgeSlotStates != NULL)
{
delete [] m_rgeSlotStates;
m_rgeSlotStates = NULL;
}
*/
if (m_rgpfrSlots != NULL)
{
// check to ensure that no frames are in use
for (int i = 0; i < m_bNumSlots; ++i)
{
if( m_rgpfrSlots[i] != NULL )
m_rgpfrSlots[i]->Return();
}
delete [] m_rgpfrSlots;
m_rgpfrSlots = NULL;
}
}
}
#undef DPF_MODNAME
#define DPF_MODNAME "CInnerQueue::Reset"
void CInnerQueue::Reset()
{
if (!m_fInited)
{
return;
}
// loop through and make sure none of the frames are currently locked and clear the slot states
for (int i = 0; i < m_bNumSlots; ++i)
{
if (m_rgpfrSlots[i] != NULL)
{
m_rgpfrSlots[i]->Return();
}
//m_rgeSlotStates[i] = essEmpty;
}
m_eState = CInnerQueue::empty;
m_bQueueSize = 0;
m_bHeadSeqNum = 0;
m_fFirstDequeue = true;
m_bFillingDequeueReqs = 0;
ResetStats();
}
#undef DPF_MODNAME
#define DPF_MODNAME "CInnerQueue::ResetStats"
void CInnerQueue::ResetStats()
{
if (!m_fInited)
{
return;
}
m_wMissingFrames = 0;
m_wDuplicateFrames = 0;
m_wOverflowFrames = 0;
m_wLateFrames = 0;
m_dwMsgLen = 0;
m_wPossibleZeroLengthDequeues = 0;
m_wKnownZeroLengthDequeues = 0;
}
// This function is not inline because it needs the module id, sigh.
#undef DPF_MODNAME
#define DPF_MODNAME "CInnerQueue::SetHighWaterMark"
void CInnerQueue::SetHighWaterMark(BYTE bHighWaterMark)
{
if (!m_fInited)
{
return;
}
DNASSERT(bHighWaterMark < m_bNumSlots);
m_bHighWaterMark = bHighWaterMark;
}
// Note: this class does not have it's own critical
// section. The caller must ensure that enqueue and
// dequeue are not called at the same time. It is
// intended that this class is used only within
// the InputQueue2 class, which does have a critical
// section.
#undef DPF_MODNAME
#define DPF_MODNAME "CInnerQueue::Enqueue"
void CInnerQueue::Enqueue(const CFrame& frFrame)
{
if (!m_fInited)
{
return;
}
#if defined(DPVOICE_QUEUE_DEBUG)
DPFX(DPFPREP, DVF_INFOLEVEL, "** QUEUE ** %i:%i ** CInnerQueue::Enqueue() MsgNum[%i] SeqNum[%i]", m_wQueueId, m_bMsgNum, frFrame.GetMsgNum(), frFrame.GetSeqNum());
#endif
DNASSERT(m_eState != finished);
if (m_eState == empty)
{
// This is the first frame, so set the head of the queue.
// NOTE: It may seem strange to set the head of the
// queue to 2 frames before the first one we receive,
// but this covers the case where the first frame
// we receive is not the first frame of the message.
// By using this logic, if any of the first, second
// or third frames arrive first, we will not chop
// off the start of the message. When the user
// asks for the first dequeue, it will skip the
// empty slots at the head of the queue, assuming
// they haven't been filled in.
m_bHeadSeqNum = (frFrame.GetSeqNum() - c_bNumStartSlots);
#if defined(DPVOICE_QUEUE_DEBUG)
DPFX(DPFPREP, DVF_INFOLEVEL, "** QUEUE ** %i:%i ** CInnerQueue::Enqueue() new message - m_bHeadSeqNum[%i]", m_wQueueId, m_bMsgNum, m_bHeadSeqNum);
DPFX(DPFPREP, DVF_INFOLEVEL, "** QUEUE ** %i:%i ** CInnerQueue::Enqueue() state changed to filling", m_wQueueId, m_bMsgNum);
#endif
m_eState = filling;
}
// Check to see if we should put this frame into the queue.
//
// NOTE: The logic below implicitly checks for queue overflows.
// if the sequence number is out of the allowable range, one
// of two things may have happened.
// 1) queue overflow
// 2) frame arrived too late
//
// First we need to know if we are dealing with a wraparound
// problem.
bool fKeepFrame = false;
if ((BYTE)(m_bHeadSeqNum + m_bNumSlots) < m_bHeadSeqNum)
{
// we've got a wraparound problem, so use this alternate logic
if (frFrame.GetSeqNum() >= m_bHeadSeqNum
|| frFrame.GetSeqNum() < (BYTE)(m_bHeadSeqNum + m_bNumSlots))
{
fKeepFrame = true;
}
}
else
{
// no wraparound problem, so use the straightforward logic
if (frFrame.GetSeqNum() >= m_bHeadSeqNum
&& frFrame.GetSeqNum() < m_bHeadSeqNum + m_bNumSlots)
{
fKeepFrame = true;
}
}
// if we're supposed to keep this frame, copy it into the
// appropriate slot
if (fKeepFrame)
{
BYTE bSlot = frFrame.GetSeqNum() % m_bNumSlots;
// check to see if this slot is full
//if (m_rgeSlotStates[bSlot] == essFull)
if (m_rgpfrSlots[bSlot] != NULL)
{
// This is a duplicate frame, so don't do anything
// with it, but tell the debugger about it, and
// update our stats.
//
// NOTE: We know that this a duplicate frame and
// not a queue overflow because we have already
// checked for queue overflow above.
#if defined(DPVOICE_QUEUE_DEBUG)
DPFX(DPFPREP, DVF_INFOLEVEL, "** QUEUE ** %i:%i ** CInnerQueue::Enqueue() Ignoring duplicate frame, sequence number[%i], slot[%i]",
m_wQueueId, m_bMsgNum, frFrame.GetSeqNum(), bSlot);
#endif
m_wDuplicateFrames++;
}
else
{
#if defined(DPVOICE_QUEUE_DEBUG)
DPFX(DPFPREP, DVF_INFOLEVEL, "** QUEUE ** %i:%i ** CInnerQueue::Enqueue() putting frame in slot[%i]", m_wQueueId, m_bMsgNum, bSlot);
#endif
// if the frame previously occupying this slot has not
// yet been released, this slot will not have a null pointer.
DNASSERT(m_rgpfrSlots[bSlot] == NULL);
// get a frame from the pool
//m_rgpfrSlots[bSlot] = m_pfpFramePool->Get(m_pcsQueue, &m_rgpfrSlots[bSlot]);
m_rgpfrSlots[bSlot] = m_pfpFramePool->Get(m_pcsQueue, NULL);
#if defined(DPVOICE_QUEUE_DEBUG)
DPFX(DPFPREP, DVF_INFOLEVEL, "** QUEUE ** %i:%i ** CInnerQueue::Enqueue() got frame from pool, Addr:%p", m_wQueueId, m_bMsgNum, m_rgpfrSlots[bSlot]);
#endif
/* RMT -- Added new func to copy frames directly.
// the client number is the same
m_rgpfrSlots[bSlot]->SetClientId(frFrame.GetClientId());
// copy the target
m_rgpfrSlots[bSlot]->SetTarget(frFrame.GetTarget());
// No one but this function should be using
// the sequence number, so just zero it out.
m_rgpfrSlots[bSlot]->SetSeqNum(0);
// copy the frame's data, also sets the frame length
m_rgpfrSlots[bSlot]->CopyData(frFrame);
// set the silence flag
m_rgpfrSlots[bSlot]->SetIsSilence(frFrame.GetIsSilence()); */
HRESULT hr;
hr = m_rgpfrSlots[bSlot]->SetEqual(frFrame);
if( FAILED( hr ) )
{
DNASSERT( FALSE );
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to copy frame in innerque" );
}
// this buffer is now full
//m_rgeSlotStates[bSlot] = essFull;
// increment the queue size
++m_bQueueSize;
#if defined(DPVOICE_QUEUE_DEBUG)
DPFX(DPFPREP, DVF_INFOLEVEL, "** QUEUE ** %i:%i ** CInnerQueue::Enqueue() new queue size[%i]", m_wQueueId, m_bMsgNum, m_bQueueSize);
#endif
// if the queue is currently filling, check to see if we've
// passed the high water mark.
if (m_eState == filling && m_bQueueSize > m_bHighWaterMark)
{
#if defined(DPVOICE_QUEUE_DEBUG)
DPFX(DPFPREP, DVF_INFOLEVEL, "** QUEUE ** %i:%i ** CInnerQueue::Enqueue() High Water Mark hit, now in ready state", m_wQueueId, m_bMsgNum);
#endif
m_bFillingDequeueReqs = 0;
m_eState = ready;
}
}
}
else
{
// Make a guess as to what caused this: overflow or late frame
// Sequence numbers are allowed to be in the range 0 to 255.
// if a sequence number is somewhere in the range 127 prior
// to the current queue head (accounting for wraparound) then
// assume it's a late frame. Otherwise, assume it's an overflow frame.
if ((frFrame.GetSeqNum() < m_bHeadSeqNum
&& frFrame.GetSeqNum() > (int)m_bHeadSeqNum - 127)
|| (frFrame.GetSeqNum() > (128 + m_bHeadSeqNum)))
{
#if defined(DPVOICE_QUEUE_DEBUG)
DPFX(DPFPREP, DVF_INFOLEVEL, "** QUEUE ** %i:%i ** CInnerQueue::Enqueue() Late frame, discarded", m_wQueueId, m_bMsgNum);
#endif
m_wLateFrames++;
}
else
{
#if defined(DPVOICE_QUEUE_DEBUG)
DPFX(DPFPREP, DVF_INFOLEVEL, "** QUEUE ** %i:%i ** CInnerQueue::Enqueue() Overflow frame, discarded", m_wQueueId, m_bMsgNum);
#endif
m_wOverflowFrames++;
}
}
return;
}
// Note: this class does not have it's own critical
// section. The caller must ensure that enqueue and
// dequeue are not called at the same time. It is
// intended that this class is used only within
// the InputQueue2 class, which does have a critical
// section.
#undef DPF_MODNAME
#define DPF_MODNAME "CInnerQueue::Dequeue"
CFrame* CInnerQueue::Dequeue()
{
CFrame* pfrReturn;
if (!m_fInited)
{
return NULL;
}
// make sure that we're in the ready state
DNASSERT(m_eState == ready);
// The only class that should be using this one should
// never call dequeue when there's nothing to get, so assert
DNASSERT(m_bQueueSize != 0);
// If we get here, there is at least one frame in the queue, somewhere.
// increment the length of the message
++m_dwMsgLen;
// find the index of the oldest frame, starting with the frame at the
// head of the queue.
BYTE bSlot = m_bHeadSeqNum % m_bNumSlots;
int i = 0;
#if defined(DPVOICE_QUEUE_DEBUG)
DPFX(DPFPREP, DVF_INFOLEVEL, "** QUEUE ** %i:%i ** CInnerQueue::Dequeue() Checking slot[%i]", m_wQueueId, m_bMsgNum, bSlot);
#endif
//while (m_rgeSlotStates[bSlot] != essFull)
while (m_rgpfrSlots[bSlot] == NULL)
{
// if this is the first dequeue, then we want to skip any empty
// slots to find the first frame in the message. Otherwise, this
// is a lost frame, and should be treated accordingly.
if (m_fFirstDequeue == true)
{
// The current slot does not have a frame, try the
// next. Put in a little sanity check for infinite
// looping.
DNASSERT(i++ < m_bNumSlots);
++bSlot;
bSlot %= m_bNumSlots;
#if defined(DPVOICE_QUEUE_DEBUG)
DPFX(DPFPREP, DVF_INFOLEVEL, "** QUEUE ** %i:%i ** CInnerQueue::Dequeue() slot empty, checking slot[%i]", m_wQueueId, m_bMsgNum, bSlot);
#endif
// increment the head sequence number
++m_bHeadSeqNum;
}
else
{
// This is a lost frame
#if defined(DPVOICE_QUEUE_DEBUG)
DPFX(DPFPREP, DVF_INFOLEVEL, "** QUEUE ** %i:%i ** CInnerQueue::Dequeue() Frame Missing", m_wQueueId, m_bMsgNum);
#endif
++m_wMissingFrames;
// this missing frame is part of the message too, so
// increment the total message size
++m_dwMsgLen;
// increment the head sequence number
++m_bHeadSeqNum;
// this is no longer the first dequeue
m_fFirstDequeue = false;
// return a silent frame marked as lost
CFrame* pfr = m_pfpFramePool->Get(m_pcsQueue, NULL);
pfr->SetIsSilence(true);
pfr->SetIsLost(true);
return pfr;
}
}
m_fFirstDequeue = false;
// By now, bSlot points to a valid, useful frame, which
// we should return.
// mark the slot we are about to return as empty
//m_rgeSlotStates[bSlot] = essEmpty;
// decrement the queue size
--m_bQueueSize;
#if defined(DPVOICE_QUEUE_DEBUG)
DPFX(DPFPREP, DVF_INFOLEVEL, "** QUEUE ** %i:%i ** CInnerQueue::Dequeue() Returning frame in slot[%i]; New queue size[%i]", m_wQueueId, m_bMsgNum, bSlot, m_bQueueSize);
#endif
// increment the head sequence number
++m_bHeadSeqNum;
// this is not a lost frame
//m_rgpfrSlots[bSlot]->SetIsLost(false);
pfrReturn = m_rgpfrSlots[bSlot];
pfrReturn->SetIsLost(false);
m_rgpfrSlots[bSlot] = NULL;
//return(m_rgpfrSlots[bSlot]);
return(pfrReturn);
}
#undef DPF_MODNAME
#define DPF_MODNAME "CInnerQueuePool::CInnerQueuePool"
CInnerQueuePool::CInnerQueuePool(
BYTE bNumSlots,
WORD wFrameSize,
CFramePool* pfpFramePool,
DNCRITICAL_SECTION* pcsQueue)
: m_bNumSlots(bNumSlots)
, m_wFrameSize(wFrameSize)
, m_pfpFramePool(pfpFramePool)
, m_pcsQueue(pcsQueue)
, m_fCritSecInited(FALSE)
{
}
#undef DPF_MODNAME
#define DPF_MODNAME "CInnerQueuePool::~CInnerQueuePool"
CInnerQueuePool::~CInnerQueuePool()
{
for (std::vector<CInnerQueue *>::iterator iter1 = m_vpiqPool.begin(); iter1 < m_vpiqPool.end(); ++iter1)
{
delete *iter1;
}
if (m_fCritSecInited)
{
DNDeleteCriticalSection(&m_lock);
}
}
#undef DPF_MODNAME
#define DPF_MODNAME "CInnerQueuePool::Get"
CInnerQueue* CInnerQueuePool::Get(
BYTE bHighWaterMark,
WORD wQueueId,
BYTE bMsgNum
)
{
HRESULT hr;
BFCSingleLock csl(&m_lock);
csl.Lock();
CInnerQueue* piq;
if (m_vpiqPool.empty())
{
// the pool is empty, return a new inner queue
piq = new CInnerQueue(
m_bNumSlots,
m_wFrameSize,
m_pfpFramePool,
m_pcsQueue,
bMsgNum,
bHighWaterMark,
wQueueId);
if( piq == NULL )
return NULL;
hr = piq->Init();
if (FAILED(hr))
{
delete piq;
return NULL;
}
}
else
{
// there are some inner queues in the pool, pop
// the last one off the back of the vector
piq = m_vpiqPool.back();
m_vpiqPool.pop_back();
piq->SetMsgNum(bMsgNum);
piq->SetQueueId(wQueueId);
piq->SetHighWaterMark(bHighWaterMark);
piq->Reset();
}
return piq;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CInnerQueuePool::Return"
void CInnerQueuePool::Return(CInnerQueue* piq)
{
BFCSingleLock csl(&m_lock);
csl.Lock();
// drop this inner queue on the back for reuse
m_vpiqPool.push_back(piq);
}