/*========================================================================== * * 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::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); }