Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

955 lines
36 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 1999 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: inqueue2.cpp
  6. * Content:
  7. *
  8. * History:
  9. * Date By Reason
  10. * ==== == ======
  11. * 07/16/99 pnewson Created
  12. * 07/27/99 pnewson Overhauled to support new message numbering method
  13. * 08/03/99 pnewson General clean up
  14. * 08/24/99 rodtoll Fixed for release builds -- removed m_wQueueId from debug block
  15. * 10/28/99 pnewson Bug #113933 debug spew too verbose
  16. * 01/31/2000 pnewson replace SAssert with DNASSERT
  17. * 02/17/2000 rodtoll Bug #133691 - Choppy audio - queue was not adapting
  18. * 07/09/2000 rodtoll Added signature bytes
  19. * 08/28/2000 masonb Voice Merge: Change #if DEBUG to #ifdef DEBUG
  20. * 09/13/2000 rodtoll Bug #44519 - Fix for fix.
  21. * 10/24/2000 rodtoll Bug #47645 - DPVOICE: Memory corruption - quality array end being overwritten
  22. *
  23. ***************************************************************************/
  24. #include "dxvutilspch.h"
  25. #undef DPF_SUBCOMP
  26. #define DPF_SUBCOMP DN_SUBCOMP_VOICE
  27. #define MODULE_ID INPUTQUEUE2
  28. const int c_iHighestQualitySliderValue = 31;
  29. const int c_iHighestRecentBiasSliderValue = 31;
  30. const double c_dHighestPossibleQuality = 0.001;
  31. const double c_dLowestPossibleQuality = 0.05;
  32. const double c_dHighestPossibleAggr = 5000.0;
  33. const double c_dLowestPossibleAggr = 120000.0;
  34. const double c_dMaxDistanceFromOpt = 100.0;
  35. const double c_dQualityTimeFactor = 1000.0; // in ms
  36. const double c_dQualityFactor = 2.0;
  37. const int c_iFinishedQueueLifetime = 2000; // in ms
  38. #undef DPF_MODNAME
  39. #define DPF_MODNAME "CInputQueue2::CInputQueue2"
  40. CInputQueue2::CInputQueue2( )
  41. : m_dwSignature(VSIG_INPUTQUEUE2)
  42. , m_fFirstDequeue(TRUE)
  43. , m_fFirstEnqueue(TRUE)
  44. , m_bCurMsgNum(0)
  45. , m_vdQualityRatings(0)
  46. , m_vdFactoredOptQuals(0)
  47. , m_bCurHighWaterMark(0)
  48. , m_bMaxHighWaterMark(0)
  49. , m_bInitHighWaterMark(0)
  50. , m_wQueueId(0)
  51. , m_dwTotalFrames(0)
  52. , m_dwTotalMessages(0)
  53. , m_dwTotalBadMessages(0)
  54. , m_dwDiscardedFrames(0)
  55. , m_dwDuplicateFrames(0)
  56. , m_dwLostFrames(0)
  57. , m_dwLateFrames(0)
  58. , m_dwOverflowFrames(0)
  59. , m_wMSPerFrame(0)
  60. , m_pFramePool(NULL)
  61. {
  62. }
  63. HRESULT CInputQueue2::Initialize( PQUEUE_PARAMS pParams )
  64. {
  65. m_fFirstDequeue = TRUE;
  66. m_fFirstEnqueue = TRUE;
  67. m_bCurMsgNum = 0;
  68. m_vdQualityRatings.resize(pParams->bMaxHighWaterMark);
  69. m_vdFactoredOptQuals.resize(pParams->bMaxHighWaterMark);
  70. m_bCurHighWaterMark = pParams->bInitHighWaterMark;
  71. m_bMaxHighWaterMark = pParams->bMaxHighWaterMark;
  72. m_bInitHighWaterMark = pParams->bInitHighWaterMark;
  73. m_wQueueId = pParams->wQueueId;
  74. m_dwTotalFrames = 0;
  75. m_dwTotalMessages = 0;
  76. m_dwTotalBadMessages = 0;
  77. m_dwDiscardedFrames = 0;
  78. m_dwDuplicateFrames = 0;
  79. m_dwLostFrames = 0;
  80. m_dwLateFrames = 0;
  81. m_dwOverflowFrames = 0;
  82. m_wMSPerFrame = pParams->wMSPerFrame;
  83. m_pFramePool = pParams->pFramePool;
  84. if (!DNInitializeCriticalSection(&m_csQueue))
  85. {
  86. return DVERR_OUTOFMEMORY;
  87. }
  88. #if defined(DPVOICE_QUEUE_DEBUG)
  89. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::CInputQueue2() bInnerQueueSize: %i"), m_wQueueId, bInnerQueueSize);
  90. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::CInputQueue2() bMaxHighWaterMark: %i"), m_wQueueId, bMaxHighWaterMark);
  91. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::CInputQueue2() bInitHighWaterMark: %i"), m_wQueueId, bInitHighWaterMark);
  92. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::CInputQueue2() pFramePool: %p"), m_wQueueId, m_pFramePool);
  93. #endif
  94. //// TODO(pnewson, "use one inner queuepool for all queues")
  95. m_pInnerQueuePool =
  96. new CInnerQueuePool(
  97. pParams->bInnerQueueSize,
  98. pParams->wFrameSize,
  99. m_pFramePool,
  100. &m_csQueue);
  101. if( m_pInnerQueuePool == NULL )
  102. {
  103. DPFX(DPFPREP, 0, "Error allocating innerqueue pool!" );
  104. DNDeleteCriticalSection(&m_csQueue);
  105. return DVERR_OUTOFMEMORY;
  106. }
  107. if (!m_pInnerQueuePool->Init())
  108. {
  109. delete m_pInnerQueuePool;
  110. DNDeleteCriticalSection(&m_csQueue);
  111. return DVERR_OUTOFMEMORY;
  112. }
  113. // see header for explanation
  114. // since this is the first time, init the
  115. // member variables, before we call the set
  116. // functions. Weird, but it makes the debug
  117. // messages cleaner. It doesn't acutally
  118. // fix a real problem.
  119. #ifdef DEBUG
  120. m_iQuality = pParams->iQuality;
  121. m_iHops = pParams->iHops;
  122. m_iAggr = pParams->iAggr;
  123. #endif
  124. SetQuality(pParams->iQuality, pParams->iHops);
  125. SetAggr(pParams->iAggr);
  126. // set the queue to an empty state
  127. Reset();
  128. return DV_OK;
  129. }
  130. void CInputQueue2::GetStatistics( PQUEUE_STATISTICS pQueueStats )
  131. {
  132. pQueueStats->dwTotalFrames = GetTotalFrames();
  133. pQueueStats->dwTotalMessages = GetTotalMessages();
  134. pQueueStats->dwTotalBadMessages = GetTotalBadMessages();
  135. pQueueStats->dwDiscardedFrames = GetDiscardedFrames();
  136. pQueueStats->dwDuplicateFrames = GetDuplicateFrames();
  137. pQueueStats->dwLostFrames = GetLostFrames();
  138. pQueueStats->dwLateFrames = GetLateFrames();
  139. pQueueStats->dwOverflowFrames = GetOverflowFrames();
  140. }
  141. void CInputQueue2::DeInitialize()
  142. {
  143. // delete anything remaining in the inner queue list
  144. for (std::list<CInnerQueue*>::iterator iter = m_lpiqInnerQueues.begin(); iter != m_lpiqInnerQueues.end(); ++iter)
  145. {
  146. delete *iter;
  147. }
  148. m_lpiqInnerQueues.clear();
  149. if( m_pInnerQueuePool )
  150. {
  151. delete m_pInnerQueuePool;
  152. m_pInnerQueuePool = NULL;
  153. }
  154. DNDeleteCriticalSection(&m_csQueue);
  155. }
  156. // The destructor. Release all the resources we acquired in the
  157. // constructor
  158. #undef DPF_MODNAME
  159. #define DPF_MODNAME "CInputQueue2::~CInputQueue2"
  160. CInputQueue2::~CInputQueue2()
  161. {
  162. DeInitialize();
  163. m_dwSignature = VSIG_INPUTQUEUE2_FREE;
  164. }
  165. // This function clears all the input buffers and
  166. // resets the other class information to an initial
  167. // state. The queue should not be in use when this
  168. // function is called. i.e. there should not be any
  169. // locked frames.
  170. #undef DPF_MODNAME
  171. #define DPF_MODNAME "CInputQueue2::Reset"
  172. void CInputQueue2::Reset()
  173. {
  174. // make sure no one is using the queue right now
  175. BFCSingleLock csl(&m_csQueue);
  176. csl.Lock();
  177. #if defined(DPVOICE_QUEUE_DEBUG)
  178. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Reset()"), m_wQueueId);
  179. #endif
  180. // loop through and return all inner queues to the pool
  181. for (std::list<CInnerQueue*>::iterator iter = m_lpiqInnerQueues.begin(); iter != m_lpiqInnerQueues.end(); ++iter)
  182. {
  183. m_pInnerQueuePool->Return(*iter);
  184. }
  185. // the next frame will be the first one we accept
  186. m_fFirstEnqueue = TRUE;
  187. // we have not yet received a dequeue request
  188. m_fFirstDequeue = TRUE;
  189. // we don't yet know the first message number, so just use zero
  190. m_bCurMsgNum = 0;
  191. // we should reset back to zero for the current high water mark
  192. m_bCurHighWaterMark = m_bInitHighWaterMark;
  193. // reset the track record on the various high water marks
  194. for (int i = 0; i < m_bMaxHighWaterMark; ++i)
  195. {
  196. m_vdQualityRatings[i] = m_vdFactoredOptQuals[i];
  197. }
  198. // reset all the other stats too
  199. m_dwDiscardedFrames = 0;
  200. m_dwDuplicateFrames = 0;
  201. m_dwLateFrames = 0;
  202. m_dwLostFrames = 0;
  203. m_dwOverflowFrames = 0;
  204. m_dwQueueErrors = 0;
  205. m_dwTotalBadMessages = 0;
  206. m_dwTotalFrames = 0;
  207. m_dwTotalMessages = 0;
  208. }
  209. // Call this function to add a frame to the queue. I
  210. // considered returning a reference to a frame which
  211. // the caller could then stuff, but because the frames
  212. // will not always arrive in order, that would mean I would have
  213. // to copy the frame sometimes anyway. So, for simplicity, the
  214. // caller has allocated a frame, which it passes a reference
  215. // to, and this function will copy that frame into the
  216. // appropriate place in the queue, according to its
  217. // sequence number.
  218. #undef DPF_MODNAME
  219. #define DPF_MODNAME "CInputQueue2::Enqueue"
  220. void CInputQueue2::Enqueue(const CFrame& fr)
  221. {
  222. // start the critical section
  223. BFCSingleLock csl(&m_csQueue);
  224. csl.Lock();
  225. #if defined(DPVOICE_QUEUE_DEBUG)
  226. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** ******************************************"), m_wQueueId);
  227. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Enqueue() MsgNum[%i] SeqNum[%i]"), m_wQueueId, fr.GetMsgNum(), fr.GetSeqNum());
  228. #endif
  229. // Only add the frame if a dequeue has been
  230. // requested. This allows the producer and
  231. // consumer threads to sync up during their
  232. // startup, or after a reset.
  233. if (m_fFirstDequeue == TRUE)
  234. {
  235. #if defined(DPVOICE_QUEUE_DEBUG)
  236. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Enqueue() First Dequeue Not Yet Received - Frame Discarded"), m_wQueueId);
  237. #endif
  238. return;
  239. }
  240. // check to see if this is the first enqueue request
  241. // we've accepted.
  242. if (m_fFirstEnqueue == TRUE)
  243. {
  244. #if defined(DPVOICE_QUEUE_DEBUG)
  245. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Enqueue() First Enqueue"), m_wQueueId);
  246. #endif
  247. // clear the first frame flag
  248. m_fFirstEnqueue = FALSE;
  249. // Since this is the first frame we are accepting,
  250. // we can just get a new inner queue without
  251. // worry that one already exists for this message.
  252. // Note that there should not be any queues already!
  253. DNASSERT(m_lpiqInnerQueues.size() == 0);
  254. #if defined(DPVOICE_QUEUE_DEBUG)
  255. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Enqueue() Creating Inner queue for MsgNum %i"), m_wQueueId, fr.GetMsgNum());
  256. #endif
  257. m_lpiqInnerQueues.push_back(m_pInnerQueuePool->Get(m_bCurHighWaterMark, m_wQueueId, fr.GetMsgNum()));
  258. // stuff the frame into the inner queue
  259. (*m_lpiqInnerQueues.begin())->Enqueue(fr);
  260. }
  261. else
  262. {
  263. // see if we already have a queue started for this message number
  264. #if defined(DPVOICE_QUEUE_DEBUG)
  265. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Enqueue() Checking for an inner queue to put this frame into"), m_wQueueId);
  266. #endif
  267. bool fDone = false;
  268. for (std::list<CInnerQueue*>::iterator iter = m_lpiqInnerQueues.begin(); iter != m_lpiqInnerQueues.end(); ++iter)
  269. {
  270. #if defined(DPVOICE_QUEUE_DEBUG)
  271. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Enqueue() found inner queue for msg number %i"), m_wQueueId, (*iter)->GetMsgNum());
  272. #endif
  273. if ((*iter)->GetMsgNum() == fr.GetMsgNum())
  274. {
  275. #if defined(DPVOICE_QUEUE_DEBUG)
  276. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Enqueue() this is the one, queue size: %i"), m_wQueueId, (*iter)->GetSize());
  277. #endif
  278. // we have found the queue for this frame
  279. switch ((*iter)->GetState())
  280. {
  281. case CInnerQueue::empty:
  282. // we should not get here, since this state is
  283. // only valid for the first frame of a message,
  284. // which is added to the queue below, not in this
  285. // case statement.
  286. DNASSERT(false);
  287. break;
  288. case CInnerQueue::filling:
  289. // check to see if the queue was empty
  290. #if defined(DPVOICE_QUEUE_DEBUG)
  291. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Enqueue() inner queue in filling state"), m_wQueueId);
  292. #endif
  293. if ((*iter)->GetSize() == 0)
  294. {
  295. // the queue was empty. If we have been
  296. // trying to dequeue from it, we now know
  297. // that the message was not done, so those
  298. // dequeues were breaks in the speech.
  299. // update the stats accordingly
  300. #if defined(DPVOICE_QUEUE_DEBUG)
  301. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Enqueue() - converting possible zero length dequeues to known in MsgNum[%i]"), m_wQueueId, fr.GetMsgNum());
  302. #endif
  303. (*iter)->AddToKnownZeroLengthDequeues(
  304. (*iter)->GetPossibleZeroLengthDequeues());
  305. }
  306. // NOTE: falling through!!!
  307. case CInnerQueue::ready:
  308. #if defined(DPVOICE_QUEUE_DEBUG)
  309. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Enqueue() inner queue in ready state (unless the previous message said filling)"), m_wQueueId);
  310. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Enqueue() calling InnerQueue->Enqueue MsgNum[%i]"), m_wQueueId, fr.GetMsgNum());
  311. #endif
  312. (*iter)->Enqueue(fr);
  313. break;
  314. case CInnerQueue::finished:
  315. // do nothing, just discard the frame
  316. #if defined(DPVOICE_QUEUE_DEBUG)
  317. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Enqueue() not calling InnerQueue->Enqueue - MsgNum[%i] in finished state, discarding frame"), m_wQueueId, fr.GetMsgNum());
  318. #endif
  319. break;
  320. }
  321. // done, get out.
  322. return;
  323. }
  324. }
  325. // If we get here, there is not already a queue active for this
  326. // message number, so create one and stuff it with the frame and add
  327. // it to the list.
  328. //// TODO(pnewson, "Use the message number to insert the new inner queue in the right place")
  329. #if defined(DPVOICE_QUEUE_DEBUG)
  330. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Enqueue() Creating Inner queue for MsgNum %i"), m_wQueueId, fr.GetMsgNum());
  331. #endif
  332. CInnerQueue* piq = m_pInnerQueuePool->Get(m_bCurHighWaterMark, m_wQueueId, fr.GetMsgNum());
  333. #if defined(DPVOICE_QUEUE_DEBUG)
  334. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Enqueue() calling InnerQueue->Enqueue MsgNum[%i]"), m_wQueueId, fr.GetMsgNum());
  335. #endif
  336. piq->Enqueue(fr);
  337. m_lpiqInnerQueues.push_back(piq);
  338. }
  339. }
  340. // This function retrieves the next frame from the head of
  341. // the queue. For speed, it does not copy the data out of the
  342. // buffer, but instead returns a pointer to the actual
  343. // frame from the queue. When the caller is finished with
  344. // the CFrame object, it should call Return() on it. This will
  345. // return the frame to the frame pool, and update the queue's
  346. // internal pointers to show that the queue slot is now free.
  347. // If the caller doesn't call Return() in time, when the queue
  348. // attempts to reuse the slot, it will DNASSERT(). The caller
  349. // should always Return frame before it attempts to dequeue
  350. // another one.
  351. #undef DPF_MODNAME
  352. #define DPF_MODNAME "CInputQueue2::Dequeue"
  353. CFrame* CInputQueue2::Dequeue()
  354. {
  355. // start the critical section
  356. BFCSingleLock csl(&m_csQueue);
  357. csl.Lock();
  358. #if defined(DPVOICE_QUEUE_DEBUG)
  359. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** ******************************************"), m_wQueueId);
  360. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Dequeue()"), m_wQueueId);
  361. #endif
  362. CFrame* pfrReturn = 0;
  363. if (m_fFirstDequeue == TRUE)
  364. {
  365. #if defined(DPVOICE_QUEUE_DEBUG)
  366. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Dequeue() First Dequeue"), m_wQueueId);
  367. #endif
  368. // trigger the interlock, so we're free to start enqueueing
  369. m_fFirstDequeue = FALSE;
  370. // since we're not allowed to enqueue until after the
  371. // first dequeue, there will be no inner queues.
  372. // So return a silent frame
  373. pfrReturn = m_pFramePool->Get(&m_csQueue, NULL);
  374. pfrReturn->SetIsSilence(TRUE);
  375. pfrReturn->SetIsLost(false);
  376. return pfrReturn;
  377. }
  378. else
  379. {
  380. pfrReturn = 0;
  381. int iDeadTime;
  382. // cycle through the list of active inner queues
  383. std::list<CInnerQueue*>::iterator iter = m_lpiqInnerQueues.begin();
  384. while (iter != m_lpiqInnerQueues.end())
  385. {
  386. std::list<CInnerQueue*>::iterator cur = iter;
  387. std::list<CInnerQueue*>::iterator next = ++iter;
  388. switch ((*cur)->GetState())
  389. {
  390. case CInnerQueue::finished:
  391. // We keep these old, dead inner queues around for a while
  392. // so that any late straggling frames that were part of this
  393. // message get discarded. We know when to kill them off for
  394. // good because we keep incrementing the possible zero length
  395. // dequeue count. If this finished queue is stale enough,
  396. // return it to the pool.
  397. #if defined(DPVOICE_QUEUE_DEBUG)
  398. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Dequeue() current queue in finished state"), m_wQueueId);
  399. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Dequeue() PossibleZeroLengthDequeues: %i"), m_wQueueId, (*cur)->GetPossibleZeroLengthDequeues());
  400. #endif
  401. (*cur)->IncPossibleZeroLengthDequeues();
  402. iDeadTime = (*cur)->GetPossibleZeroLengthDequeues() * m_wMSPerFrame;
  403. if (iDeadTime > c_iFinishedQueueLifetime)
  404. {
  405. // this queue has been dead long enough, kill it off
  406. #if defined(DPVOICE_QUEUE_DEBUG)
  407. DPFX(DPFPREP, DVF_INFOLEVEL, "***** RETURNING INNER QUEUE TO POOL *****");
  408. #endif
  409. m_pInnerQueuePool->Return(*cur);
  410. m_lpiqInnerQueues.erase(cur);
  411. }
  412. break;
  413. case CInnerQueue::filling:
  414. #if defined(DPVOICE_QUEUE_DEBUG)
  415. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Dequeue() current queue in filling state"), m_wQueueId);
  416. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Dequeue() queue size: %i"), m_wQueueId, (*cur)->GetSize());
  417. #endif
  418. if ((*cur)->GetSize() > 0)
  419. {
  420. // If there is a message after this one, then release this
  421. // message for playback.
  422. // OR
  423. // If we have been spinning trying to release this message
  424. // for a while, then just let it go... The message may be
  425. // too short to trip the high water mark.
  426. #if defined(DPVOICE_QUEUE_DEBUG)
  427. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Dequeue() filling dequeue reqs: %i"), m_wQueueId, (*cur)->GetFillingDequeueReqs());
  428. #endif
  429. if (next != m_lpiqInnerQueues.end()
  430. || (*cur)->GetFillingDequeueReqs() > (*cur)->GetHighWaterMark())
  431. {
  432. // set the state to ready, and dequeue
  433. #if defined(DPVOICE_QUEUE_DEBUG)
  434. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Dequeue() setting state to ready and dequeing"), m_wQueueId);
  435. #endif
  436. (*cur)->SetState(CInnerQueue::ready);
  437. return (*cur)->Dequeue();
  438. }
  439. }
  440. else
  441. {
  442. // there is nothing in this queue
  443. // check to see if another message has begun to arrive
  444. if (next != m_lpiqInnerQueues.end())
  445. {
  446. // there is another message coming in after this
  447. // one, so flip this queue to the finished state
  448. #if defined(DPVOICE_QUEUE_DEBUG)
  449. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Dequeue() new message arriving, setting state to finished"), m_wQueueId);
  450. #endif
  451. (*cur)->SetState(CInnerQueue::finished);
  452. // harvest the stats from this message, now that it
  453. // is done
  454. HarvestStats(*cur);
  455. // Go to the next iteration of this loop, which will
  456. // either dequeue a frame from the next message, or
  457. // return an empty frame
  458. break;
  459. }
  460. }
  461. // if we get here, there is something in this queue, but we are
  462. // not yet ready to release it yet.
  463. // we should return an extra frame and remember
  464. // that we've been here...
  465. #if defined(DPVOICE_QUEUE_DEBUG)
  466. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Dequeue() not ready to release message, returning empty frame"), m_wQueueId);
  467. #endif
  468. (*cur)->IncFillingDequeueReqs();
  469. pfrReturn = m_pFramePool->Get(&m_csQueue, NULL);
  470. pfrReturn->SetIsSilence(TRUE);
  471. pfrReturn->SetIsLost(false);
  472. return pfrReturn;
  473. case CInnerQueue::ready:
  474. #if defined(DPVOICE_QUEUE_DEBUG)
  475. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Dequeue() Queue Size: %i"), m_wQueueId, (*cur)->GetSize());
  476. #endif
  477. // check to see if this ready queue is empty
  478. if ((*cur)->GetSize() == 0)
  479. {
  480. // increment the possible zero length dequeue count
  481. (*cur)->IncPossibleZeroLengthDequeues();
  482. // check to see if another message has begun to arrive
  483. if (next != m_lpiqInnerQueues.end())
  484. {
  485. #if defined(DPVOICE_QUEUE_DEBUG)
  486. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Dequeue() queue is empty, new message arriving, setting state to finished"), m_wQueueId);
  487. #endif
  488. // there is another message coming in after this
  489. // one, so flip this queue to the finished state
  490. (*cur)->SetState(CInnerQueue::finished);
  491. // harvest the stats from this message, now that it
  492. // is done
  493. HarvestStats(*cur);
  494. // Go to the next iteration of this loop, which will
  495. // either dequeue a frame from the next message, or
  496. // return an empty frame
  497. break;
  498. }
  499. // there is nothing in this queue, and there are no more
  500. // messages arriving after this one, so boot this inner
  501. // queue into the filling state so if this is just a long
  502. // pause in the message, it will fill to the high water mark
  503. // again before it begins to play again.
  504. #if defined(DPVOICE_QUEUE_DEBUG)
  505. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Dequeue() queue is empty, setting state to filling, returning empty frame"), m_wQueueId);
  506. #endif
  507. (*cur)->SetState(CInnerQueue::filling);
  508. // return an extra frame
  509. pfrReturn = m_pFramePool->Get(&m_csQueue, NULL);
  510. pfrReturn->SetIsSilence(TRUE);
  511. pfrReturn->SetIsLost(false);
  512. return pfrReturn;
  513. }
  514. else
  515. {
  516. // there's something to return, so do it
  517. return (*cur)->Dequeue();
  518. }
  519. }
  520. }
  521. #if defined(DPVOICE_QUEUE_DEBUG)
  522. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::Dequeue() nothing available in inner queues, returning empty frame"), m_wQueueId);
  523. #endif
  524. // if we get here, there was nothing suitable in the queues
  525. // (if there were any queues) so return an extra frame
  526. pfrReturn = m_pFramePool->Get(&m_csQueue, NULL);
  527. pfrReturn->SetIsSilence(TRUE);
  528. pfrReturn->SetIsLost(false);
  529. return pfrReturn;
  530. }
  531. }
  532. // This function should be called each time a queue is moved to the finished
  533. // state. That's when we have officially declared that the message is finished,
  534. // so it's a good time to see how we handled it. This will also reset the
  535. // stats so the next message starts fresh.
  536. #undef DPF_MODNAME
  537. #define DPF_MODNAME "CInputQueue2::HarvestStats"
  538. void CInputQueue2::HarvestStats(CInnerQueue* piq)
  539. {
  540. m_dwDuplicateFrames += piq->GetDuplicateFrames();
  541. // now that the message is actually complete, we're in a better
  542. // position to decide accurately how many frames were late
  543. // vs. how many were actually lost. When something isn't
  544. // there when it's needed, we increment the missing frames
  545. // count. If it subsequently arrives, it's counted as late.
  546. // Therefore the true count of lost frames is the difference
  547. // between the missing and late counts. Ditto if a frame,
  548. // overflows. We discard it so it's not there when we need it,
  549. // it will then get reported as missing. So subtract that too.
  550. m_dwLostFrames += piq->GetMissingFrames()
  551. - piq->GetLateFrames() - piq->GetOverflowFrames();
  552. m_dwLateFrames += piq->GetLateFrames();
  553. m_dwOverflowFrames += piq->GetOverflowFrames();
  554. // What to do with the zero length dequeue stat? From a
  555. // certain point of view, only one frame was late. From
  556. // another point of view, all the subsequent frames were
  557. // late. Hmmm.... Lets take the middle road and say that
  558. // for each failed zero length dequeue we'll count it as
  559. // equivalent to a late frame
  560. m_dwLateFrames += piq->GetKnownZeroLengthDequeues();
  561. m_dwTotalFrames += piq->GetMsgLen();
  562. m_dwTotalMessages++;
  563. #if defined(DPVOICE_QUEUE_DEBUG)
  564. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::HarvestStats() DuplicateFrames:%i; MissingFrames:%i; LateFrames:%i; OverflowFrames:%i; KnownZeroLengthDequeues:%i; MsgLen:%i;"),
  565. m_wQueueId, piq->GetDuplicateFrames(), piq->GetMissingFrames(), piq->GetLateFrames(), piq->GetOverflowFrames(), piq->GetKnownZeroLengthDequeues(), piq->GetMsgLen());
  566. #endif
  567. // Build a carefully formatted debug string that will give me some data,
  568. // but not give away all of our wonderful queue secrets.
  569. // dump the string to the debug log
  570. // Note!!! If you change the format of this debug string,
  571. // please roll the version number, i.e. HVT1A -> HVT2A so
  572. // we can decode any logs!
  573. DPFX(DPFPREP, DVF_INFOLEVEL, _T("HVT1A:%i:%i:%i:%i:%i:%i"),
  574. m_wQueueId,
  575. m_bCurHighWaterMark,
  576. (int)(m_vdQualityRatings[m_bCurHighWaterMark] * 10000),
  577. piq->GetMsgLen(),
  578. piq->GetKnownZeroLengthDequeues() + piq->GetLateFrames(),
  579. piq->GetMissingFrames());
  580. // The idea:
  581. // The quality quotient is a number between 0 and 1 that indicates
  582. // the quality of the current connection. 0 means perfect - no bad
  583. // stuff, ever. 1 means awful - 100% bad stuff. The number
  584. // is really a weighted average of the number of good frames vs the number
  585. // of bad frames, biased towards the recent frames. The message
  586. // we just received makes up 'm_wFrameStrength * m_wMsgLen' percent of the
  587. // total value. The past history is deweighted by (1 - m_wFrameStrength*m_wMsgLen)
  588. // so the significance of a message decays as time goes by
  589. // and according to the size of the message (number of frames).
  590. //
  591. // Another idea:
  592. // Keep a vector that tracks how good the quality is at each
  593. // high water mark.
  594. // That way, when we want to make a jump up or down in the
  595. // water mark, we can consider it carefully first. This
  596. // gives the adaptive algorithm some memory of what life
  597. // was like at each queue level.
  598. // We choose an optimum level, like .02, that we are
  599. // shooting for. We keep searching the space of high water
  600. // marks until we find the one that's closest to the
  601. // optimum. This optimum is configurable.
  602. //
  603. // The initial quality of each of the high water marks
  604. // is set to the optimum. This quality will then
  605. // vary as that high water mark gains experience.
  606. // If it dips below a certain threshold, then
  607. // we'll jump to the next level up. If that one
  608. // is too good, it will go above a threshold, at
  609. // which point we can consider going back down.
  610. //
  611. // The problem with this is the sudden things that games
  612. // dish out (like when Q2 starts up) when they don't
  613. // lets us have the CPU for a while. These could
  614. // unduly punish a particular high water mark,
  615. // there's really not much we can do about it.
  616. // Oh, well. We'll give it a shot.
  617. // Adjust the quality rating of this watermark.
  618. // The algorithm is requires the results from the last
  619. // message, contained in the inner queue, along with the
  620. // current quality rating.
  621. DNASSERT( m_bCurHighWaterMark < m_bMaxHighWaterMark );
  622. m_vdQualityRatings[m_bCurHighWaterMark] =
  623. AdjustQuality(piq, m_vdQualityRatings[m_bCurHighWaterMark]);
  624. // see if this put us above the highest acceptable quality rating
  625. // we asserted that m_dOptimumQuality != 0.0 in SetQuality, so we
  626. // don't need to check for division by zero
  627. if (m_vdQualityRatings[m_bCurHighWaterMark] / m_vdFactoredOptQuals[m_bCurHighWaterMark] > m_dQualityThreshold)
  628. {
  629. // Check to see which is closer
  630. // the the optimum quality - the current high water
  631. // mark or the one above it. Only do this test if
  632. // we're not already at the maximum high water mark
  633. if (m_bCurHighWaterMark < (m_bMaxHighWaterMark-1) )
  634. {
  635. // To check the "distance" from the optimum, use the
  636. // inverse of the qualities. That normalizes it to
  637. // our perception of quality
  638. // calculate how far the current high water mark
  639. // is from optimum
  640. double dCurDistFromOpt = m_vdQualityRatings[m_bCurHighWaterMark] / m_vdFactoredOptQuals[m_bCurHighWaterMark];
  641. if (dCurDistFromOpt < 1)
  642. {
  643. // quality ratings can never go to zero (enforced in
  644. // AdjustQuality() so this division will never by by zero
  645. dCurDistFromOpt = 1.0 / dCurDistFromOpt;
  646. }
  647. // calculate how far the next high water mark is from
  648. // optimum
  649. //
  650. // quality ratings can never go to zero (enforced in
  651. // AdjustQuality() so this division will never by by zero
  652. double dNextDistFromOpt = m_vdFactoredOptQuals[m_bCurHighWaterMark + 1] / m_vdQualityRatings[m_bCurHighWaterMark + 1];
  653. if (dNextDistFromOpt < 1)
  654. {
  655. dNextDistFromOpt = 1.0 / dNextDistFromOpt;
  656. }
  657. // if the next high water mark is closer to the
  658. // optimum than this one, switch to it.
  659. if (dNextDistFromOpt < dCurDistFromOpt)
  660. {
  661. #if defined(DPVOICE_QUEUE_DEBUG)
  662. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::HarvestStats() Raising High Water Mark to %i"), m_wQueueId, m_bCurHighWaterMark + 1);
  663. #endif
  664. SetNewHighWaterMark(m_bCurHighWaterMark + 1);
  665. }
  666. }
  667. }
  668. // see if this put us below the low threshold
  669. //
  670. // quality ratings can never go to zero (enforced in
  671. // AdjustQuality() so this division will never by by zero
  672. if (m_vdFactoredOptQuals[m_bCurHighWaterMark] / m_vdQualityRatings[m_bCurHighWaterMark] > m_dQualityThreshold)
  673. {
  674. // The quality has moved below the high quality threshold
  675. // Check to see what is closer to the optimum quality -
  676. // the current high water mark or the one below this one.
  677. // Only do this test if we're not already at a zero
  678. // high water mark.
  679. if (m_bCurHighWaterMark > 0)
  680. {
  681. // To check the "distance" from the optimum, use the
  682. // inverse of the qualities. That normalizes it to
  683. // our perception of quality
  684. // calculate how far the current high water mark
  685. // is from optimum
  686. double dCurDistFromOpt = m_vdQualityRatings[m_bCurHighWaterMark] / m_vdFactoredOptQuals[m_bCurHighWaterMark];
  687. if (dCurDistFromOpt < 1)
  688. {
  689. dCurDistFromOpt = 1.0 / dCurDistFromOpt;
  690. }
  691. // calculate how far the previous (lower) high water mark is from
  692. // optimum
  693. double dPrevDistFromOpt = m_vdFactoredOptQuals[m_bCurHighWaterMark - 1] / m_vdQualityRatings[m_bCurHighWaterMark - 1];
  694. if (dPrevDistFromOpt < 1)
  695. {
  696. dPrevDistFromOpt = 1.0 / dPrevDistFromOpt;
  697. }
  698. // if the prev high water mark is closer to the
  699. // optimum than this one, switch to it.
  700. if (dPrevDistFromOpt < dCurDistFromOpt)
  701. {
  702. #if defined(DPVOICE_QUEUE_DEBUG)
  703. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::HarvestStats() Lowering High Water Mark to %i"), m_wQueueId, m_bCurHighWaterMark - 1);
  704. #endif
  705. SetNewHighWaterMark(m_bCurHighWaterMark - 1);
  706. }
  707. }
  708. }
  709. // clear the stats on the inner queue
  710. piq->ResetStats();
  711. }
  712. #undef DPF_MODNAME
  713. #define DPF_MODNAME "CInputQueue2::AdjustQuality"
  714. double CInputQueue2::AdjustQuality(CInnerQueue* piq, double dCurrentQuality)
  715. {
  716. // if the message is zero length, no adjustment is made
  717. // to the queue...
  718. if (piq->GetMsgLen() == 0)
  719. {
  720. return dCurrentQuality;
  721. }
  722. // The longer a message, the stronger its effect on the
  723. // current quality rating.
  724. double dWeighting = min(piq->GetMsgLen() * m_dFrameStrength, 1.0);
  725. // The message quality is the quotient of bad
  726. // stuff that happened (zero length dequeues
  727. // and late frames) to the total number of
  728. // frames in the message. Note that we do not
  729. // count lost frames against the message since
  730. // moving to a higher water mark wouldn't help.
  731. // Note that we impose a "worst case" of 1.0
  732. double dMsgQuality = min(((double)(piq->GetKnownZeroLengthDequeues() + piq->GetLateFrames()) / (double)piq->GetMsgLen()), 1.0);
  733. #if defined(DPVOICE_QUEUE_DEBUG)
  734. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::AdjustQuality() dWeighting: %g; dMsgQuality: %g; dCurrentQuality %g;"),
  735. m_wQueueId, dWeighting, dMsgQuality, dCurrentQuality);
  736. #endif
  737. // The new quality rating is a combination of the
  738. // current quality rating, and the quality of the
  739. // most recent message, weighted by the message length.
  740. double dNewQuality = (dCurrentQuality * (1.0 - dWeighting)) + (dMsgQuality * dWeighting);
  741. // We don't want to allow extremes of quality, or else they will set up
  742. // barriers in the queue statistics that can never be overcome (especially
  743. // a "perfect" quality of zero). So we check here to make sure that the
  744. // new quality is within reason.
  745. double dCurDistFromOpt = dNewQuality / m_dOptimumQuality;
  746. if (dCurDistFromOpt < 1.0 / c_dMaxDistanceFromOpt)
  747. {
  748. dNewQuality = m_dOptimumQuality / c_dMaxDistanceFromOpt;
  749. }
  750. else if (dCurDistFromOpt > c_dMaxDistanceFromOpt)
  751. {
  752. dNewQuality = m_dOptimumQuality * c_dMaxDistanceFromOpt;
  753. }
  754. return dNewQuality;
  755. }
  756. #undef DPF_MODNAME
  757. #define DPF_MODNAME "CInputQueue2::SetNewHighWaterMark"
  758. void CInputQueue2::SetNewHighWaterMark(BYTE bNewHighWaterMark)
  759. {
  760. DNASSERT( bNewHighWaterMark < m_bMaxHighWaterMark );
  761. if( bNewHighWaterMark >= m_bMaxHighWaterMark )
  762. {
  763. DNASSERT( FALSE );
  764. return;
  765. }
  766. m_bCurHighWaterMark = bNewHighWaterMark;
  767. for (std::list<CInnerQueue*>::iterator iter = m_lpiqInnerQueues.begin(); iter != m_lpiqInnerQueues.end(); iter++)
  768. {
  769. (*iter)->SetHighWaterMark(bNewHighWaterMark);
  770. }
  771. return;
  772. }
  773. #undef DPF_MODNAME
  774. #define DPF_MODNAME "CInputQueue2::SetQuality"
  775. void CInputQueue2::SetQuality(int iQuality, int iHops)
  776. {
  777. m_iQuality = iQuality;
  778. m_iHops = iHops;
  779. double dQualityRatio = c_dHighestPossibleQuality / c_dLowestPossibleQuality;
  780. double dInputRatio = (double) iQuality / (double) c_iHighestQualitySliderValue;
  781. m_dOptimumQuality = pow(dQualityRatio, dInputRatio) * c_dLowestPossibleQuality;
  782. #if defined(DPVOICE_QUEUE_DEBUG)
  783. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::SetQuality(%i, %i): m_dOptimumQuality: %f" ), m_wQueueId, iQuality, iHops, m_dOptimumQuality);
  784. #endif
  785. // The quality that the user has requested should be considered
  786. // over the number of hops involved. At the end of each hop is
  787. // a queue who's watermark will be set according to this rating,
  788. // To get an end to end quality that reflects the user's choice,
  789. // this queue's quality rating must be better if it is not the
  790. // only queue in the path. The total number of on time packets is
  791. // the product (as in multiple) of on time packets for each hop.
  792. // Therefore we need to take the Nth root of 1-m_dOptimumQuality
  793. // where N is the number of hops, and subtract that value from 1
  794. // to get the appropriate quality rating for this queue. (get that?)
  795. if (m_iHops > 1)
  796. {
  797. m_dOptimumQuality = (1 - pow((1.0 - m_dOptimumQuality), 1.0 / (double)m_iHops));
  798. }
  799. // the optimum quality should never be zero, or completely perfect,
  800. // or the algorithm will not work.
  801. DNASSERT(m_dOptimumQuality != 0.0);
  802. // update the vector of factored qualities
  803. // We don't just use the raw optimum as provided by the
  804. // caller. We "factor" it such that as the high watermark
  805. // gets larger (and the latency therefore longer) we are
  806. // willing to accept a lower quality.
  807. for (int i = 0; i < m_bMaxHighWaterMark; ++i)
  808. {
  809. m_vdFactoredOptQuals[i] = m_dOptimumQuality *
  810. pow(c_dQualityFactor, (double)(i * m_wMSPerFrame) / c_dQualityTimeFactor);
  811. }
  812. // Build a carefully formatted debug string that will give me some data,
  813. // but not give away all of our wonderful queue secrets.
  814. // dump the string to the debug log
  815. // Note!!! If you change the format of this debug string,
  816. // please roll the version number, i.e. HVT1B -> HVT2B so
  817. // we can decode any logs!
  818. DPFX(DPFPREP, DVF_INFOLEVEL, _T("HVT1B:%i:%i:%i:%i"), m_wQueueId, m_iQuality, m_iHops, m_iAggr);
  819. }
  820. #undef DPF_MODNAME
  821. #define DPF_MODNAME "CInputQueue2::SetAggr"
  822. void CInputQueue2::SetAggr(int iAggr)
  823. {
  824. m_iAggr = iAggr;
  825. double dAggrRatio = c_dHighestPossibleAggr / c_dLowestPossibleAggr;
  826. double dInputRatio = (double) iAggr / (double) c_iHighestQualitySliderValue;
  827. double dAggr = pow(dAggrRatio, dInputRatio) * c_dLowestPossibleAggr;
  828. // The aggressiveness is the number of milliseconds of "memory" the queue
  829. // has for each watermark. To find the frame strength, divide the
  830. // number of milliseconds per frame by the "memory".
  831. m_dFrameStrength = (double)m_wMSPerFrame / dAggr;
  832. // We are using a fixed 1% threshold now - the aggressiveness is now set
  833. // through the frame strength. This low threshold will make the queue
  834. // adapt very quickly at first, while it is learning something about
  835. // the various watermarks, but will settle down more after that.
  836. m_dQualityThreshold = 1.01;
  837. #if defined (DPVOICE_QUEUE_DEBUG)
  838. DPFX(DPFPREP, DVF_INFOLEVEL, _T("** QUEUE ** %i ** CInputQueue2::SetAggr(%i): dAggr: %f, m_dFrameStrength: %f, m_dQualityThreshold %f"), m_wQueueId, m_iAggr, dAggr, m_dFrameStrength, m_dQualityThreshold);
  839. #endif
  840. // Build a carefully formatted debug string that will give me some data,
  841. // but not give away all of our wonderful queue secrets.
  842. // dump the string to the debug log
  843. // Note!!! If you change the format of this debug string,
  844. // please roll the version number, i.e. HVT1C -> HVT2C so
  845. // we can decode any logs!
  846. DPFX(DPFPREP, DVF_INFOLEVEL, _T("HVT1C:%i:%i:%i:%i"), m_wQueueId, m_iQuality, m_iHops, m_iAggr);
  847. }