Leaked source code of windows server 2003
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.

1508 lines
42 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. qccall.cpp
  5. Abstract:
  6. Implementation of CCallQualityControlRelay
  7. Author:
  8. Qianbo Huai (qhuai) 03/10/2000
  9. --*/
  10. #include "stdafx.h"
  11. HRESULT TypeStream (IUnknown *p, LONG *pMediaType, TERMINAL_DIRECTION *pDirection);
  12. class CInnerStreamLock
  13. {
  14. private:
  15. // no refcount
  16. IInnerStreamQualityControl *m_pQC;
  17. public:
  18. CInnerStreamLock(IInnerStreamQualityControl *pQC, BOOL *pfLocked)
  19. :m_pQC(NULL)
  20. {
  21. DWORD dwCount = 0;
  22. *pfLocked = FALSE;
  23. do
  24. {
  25. // try lock
  26. if (S_OK == pQC->TryLockStream())
  27. {
  28. m_pQC = pQC;
  29. *pfLocked = TRUE;
  30. if (dwCount > 0)
  31. {
  32. LOG((MSP_TRACE, "InnerStreamLock: Succeed after %d tries %p", dwCount, pQC));
  33. }
  34. return;
  35. }
  36. // check if stream is accessing QC
  37. if (S_OK == pQC->IsAccessingQC())
  38. {
  39. LOG((MSP_WARN, "InnerStreamLock: Giving up to avoid deadlock %p", pQC));
  40. return;
  41. }
  42. // try again
  43. if (dwCount++ == 10)
  44. {
  45. LOG((MSP_WARN, "InnerStreamLock: Giving up after 10 tries %p", pQC));
  46. return;
  47. }
  48. // sleep 10 ms, default callback threshold is 7000 ms
  49. SleepEx(10, TRUE);
  50. } while (TRUE);
  51. // should never hit this line
  52. return;
  53. }
  54. ~CInnerStreamLock()
  55. {
  56. if (m_pQC != NULL)
  57. {
  58. m_pQC->UnlockStream();
  59. m_pQC = NULL;
  60. }
  61. }
  62. };
  63. /*//////////////////////////////////////////////////////////////////////////////
  64. ////*/
  65. VOID NTAPI WaitOrTimerCallback (
  66. PVOID pCallQCRelay,
  67. BOOLEAN bTimerFired
  68. )
  69. {
  70. ((CCallQualityControlRelay*)pCallQCRelay)->CallbackProc (bTimerFired);
  71. }
  72. /*//////////////////////////////////////////////////////////////////////////////
  73. ////*/
  74. CCallQualityControlRelay::CCallQualityControlRelay ()
  75. :m_fInitiated (FALSE)
  76. ,m_pCall (NULL)
  77. ,m_hWait (NULL)
  78. ,m_hQCEvent (NULL)
  79. ,m_dwControlInterval (QCDEFAULT_QUALITY_CONTROL_INTERVAL)
  80. ,m_fStop (FALSE)
  81. ,m_fStopAck (FALSE)
  82. #ifdef DEBUG_QUALITY_CONTROL
  83. ,m_hQCDbg (NULL)
  84. ,m_fQCDbgTraceCPULoad (FALSE)
  85. ,m_fQCDbgTraceBitrate (FALSE)
  86. #endif // DEBUG_QUALITY_CONTROL
  87. ,m_lConfBitrate (QCDEFAULT_QUALITY_UNSET)
  88. ,m_lPrefMaxCPULoad (QCDEFAULT_MAX_CPU_LOAD)
  89. ,m_lPrefMaxOutputBitrate (QCDEFAULT_QUALITY_UNSET)
  90. {
  91. m_lCPUUpThreshold = m_lPrefMaxCPULoad + (LONG)(100 * QCDEFAULT_UP_THRESHOLD);
  92. if (m_lCPUUpThreshold > 100)
  93. m_lCPUUpThreshold = 100;
  94. m_lCPULowThreshold = m_lPrefMaxCPULoad - (LONG)(100 * QCDEFAULT_LOW_THRESHOLD);
  95. if (m_lCPULowThreshold < 0)
  96. m_lCPULowThreshold = 0;
  97. m_lOutBitUpThreshold = QCDEFAULT_QUALITY_UNSET;
  98. m_lOutBitLowThreshold = QCDEFAULT_QUALITY_UNSET;
  99. }
  100. CCallQualityControlRelay::~CCallQualityControlRelay ()
  101. {
  102. ENTER_FUNCTION ("CCallQualityControlRelay::~CCallQualityControlRelay");
  103. HRESULT hr;
  104. // if not initialized, no resource has been allocated
  105. if (!m_fInitiated) return;
  106. _ASSERT (m_fStopAck);
  107. CloseHandle (m_hQCEvent);
  108. }
  109. /*//////////////////////////////////////////////////////////////////////////////
  110. Description:
  111. create event handle, create main thread, start cpu usage collection
  112. ////*/
  113. HRESULT
  114. CCallQualityControlRelay::Initialize (CIPConfMSPCall *pCall)
  115. {
  116. ENTER_FUNCTION ("CCallQualityControlRelay::Initialize");
  117. CLock lock (m_lock_QualityData);
  118. LOG ((MSP_TRACE, "%s entered. call=%p", __fxName, pCall));
  119. // avoid re-entry
  120. if (m_fInitiated)
  121. {
  122. LOG ((MSP_WARN, "%s is re-entered", __fxName));
  123. return S_OK;
  124. }
  125. // create qc event
  126. m_hQCEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
  127. if (NULL == m_hQCEvent)
  128. {
  129. LOG ((MSP_ERROR, "%s failed to create qc event", __fxName));
  130. return E_FAIL;
  131. }
  132. // keep a refcount on msp call
  133. pCall->MSPCallAddRef ();
  134. m_pCall = pCall;
  135. #ifdef DEBUG_QUALITY_CONTROL
  136. QCDbgInitiate ();
  137. #endif // DEBUG_QUALITY_CONTROL
  138. m_fInitiated = TRUE;
  139. // we want to distribute resources based on default value before graphs are running
  140. CallbackProc (TRUE);
  141. LOG ((MSP_TRACE, "%s returns. call=%p", __fxName, pCall));
  142. return S_OK;
  143. }
  144. /*//////////////////////////////////////////////////////////////////////////////
  145. Decription:
  146. stop main thread, close qc event handle, release stream qc helpers,
  147. stop cpu usage collection
  148. ////*/
  149. HRESULT
  150. CCallQualityControlRelay::Shutdown (VOID)
  151. {
  152. ENTER_FUNCTION ("CCallQualityControlRelay::Shutdown");
  153. // quality data should alway be locked before inner stream qc
  154. CLock lock1 (m_lock_QualityData);
  155. CLock lock2 (m_lock_aInnerStreamQC);
  156. LOG ((MSP_TRACE, "%s entered. call=%p. init=%d. stop=%d",
  157. __fxName, m_pCall, m_fInitiated, m_fStop));
  158. if (!m_fInitiated) return S_OK;
  159. if (m_fStop) return S_OK;
  160. // set stop signal
  161. m_fStop = TRUE;
  162. if (!SetEvent (m_hQCEvent))
  163. LOG ((MSP_ERROR, "%s failed to set event, %d", __fxName, GetLastError ()));
  164. // release stream qc helper
  165. int i;
  166. for (i=0; i<m_aInnerStreamQC.GetSize (); i++)
  167. {
  168. // an false input to unlink inner call qc on stream
  169. // forces the stream to remove its pointer to call but not to call
  170. // deregister again.
  171. m_aInnerStreamQC[i]->UnlinkInnerCallQC (FALSE);
  172. m_aInnerStreamQC[i]->Release ();
  173. }
  174. m_aInnerStreamQC.RemoveAll ();
  175. //StopCPUUsageCollection ();
  176. #ifdef DEBUG_QUALITY_CONTROL
  177. QCDbgShutdown ();
  178. #endif // DEBUG_QUALITY_CONTROL
  179. LOG ((MSP_TRACE, "%s returns. call=%p", __fxName, m_pCall));
  180. return S_OK;
  181. }
  182. /*//////////////////////////////////////////////////////////////////////////////
  183. Description:
  184. store conference-wide bandwidth
  185. ////*/
  186. HRESULT
  187. CCallQualityControlRelay::SetConfBitrate (
  188. LONG lConfBitrate
  189. )
  190. {
  191. ENTER_FUNCTION ("CCallQualityControlRelay::SetConfBitrate");
  192. CLock lock (m_lock_QualityData);
  193. // check if the limit is valid
  194. if (lConfBitrate < QCLIMIT_MIN_CONFBITRATE)
  195. {
  196. return E_INVALIDARG;
  197. }
  198. m_lConfBitrate = lConfBitrate;
  199. return S_OK;
  200. }
  201. /*//////////////////////////////////////////////////////////////////////////////
  202. Description:
  203. return stored conference-wide bandwidth
  204. ////*/
  205. LONG
  206. CCallQualityControlRelay::GetConfBitrate ()
  207. {
  208. CLock lock (m_lock_QualityData);
  209. if (m_lConfBitrate == QCDEFAULT_QUALITY_UNSET)
  210. {
  211. return 0;
  212. }
  213. return m_lConfBitrate;
  214. }
  215. /*//////////////////////////////////////////////////////////////////////////////
  216. Description:
  217. store inner stream QC interface
  218. ////*/
  219. HRESULT
  220. CCallQualityControlRelay::RegisterInnerStreamQC (
  221. IN IInnerStreamQualityControl *pIInnerStreamQC
  222. )
  223. {
  224. ENTER_FUNCTION ("CCallQualityControlRelay::RegisterInnerStreamQC");
  225. // check input pointer
  226. if (IsBadReadPtr (pIInnerStreamQC, sizeof (IInnerStreamQualityControl)))
  227. {
  228. LOG ((MSP_ERROR, "%s got bad read pointer", __fxName));
  229. return E_POINTER;
  230. }
  231. // store the pointer
  232. CLock lock (m_lock_aInnerStreamQC);
  233. if (m_aInnerStreamQC.Find (pIInnerStreamQC) > 0)
  234. {
  235. LOG ((MSP_ERROR, "%s already stored inner stream qc", __fxName));
  236. return E_INVALIDARG;
  237. }
  238. if (!m_aInnerStreamQC.Add (pIInnerStreamQC))
  239. {
  240. LOG ((MSP_ERROR, "%s failed to add inner stream QC", __fxName));
  241. return E_FAIL;
  242. }
  243. pIInnerStreamQC->AddRef ();
  244. return S_OK;
  245. }
  246. /*//////////////////////////////////////////////////////////////////////////////
  247. Description:
  248. remove the inner stream QC
  249. ////*/
  250. HRESULT
  251. CCallQualityControlRelay::DeRegisterInnerStreamQC (
  252. IN IInnerStreamQualityControl *pIInnerStreamQC
  253. )
  254. {
  255. ENTER_FUNCTION ("CCallQualityControlRelay::DeRegisterInnerStreamQC");
  256. // check input pointer
  257. if (IsBadReadPtr (pIInnerStreamQC, sizeof (IInnerStreamQualityControl)))
  258. {
  259. LOG ((MSP_ERROR, "%s got bad read pointer", __fxName));
  260. return E_POINTER;
  261. }
  262. // remove the pointer
  263. CLock lock (m_lock_aInnerStreamQC);
  264. if (!m_aInnerStreamQC.Remove (pIInnerStreamQC))
  265. {
  266. LOG ((MSP_ERROR, "%s failed to remove inner stream QC, %x", __fxName, pIInnerStreamQC));
  267. return E_FAIL;
  268. }
  269. pIInnerStreamQC->Release ();
  270. return S_OK;
  271. }
  272. /*//////////////////////////////////////////////////////////////////////////////
  273. Description:
  274. this method might be supported in the future
  275. ////*/
  276. HRESULT
  277. CCallQualityControlRelay::ProcessQCEvent (
  278. IN QCEvent event,
  279. IN DWORD dwParam
  280. )
  281. {
  282. return E_NOTIMPL;
  283. }
  284. /*//////////////////////////////////////////////////////////////////////////////
  285. Description:
  286. set quality control related properies on a call
  287. ////*/
  288. HRESULT
  289. CCallQualityControlRelay::Set(
  290. IN CallQualityProperty property,
  291. IN LONG lValue,
  292. IN TAPIControlFlags lFlags
  293. )
  294. {
  295. ENTER_FUNCTION ("CCallQualityControlRelay::Set CallQualityProperty");
  296. HRESULT hr;
  297. CLock lock (m_lock_QualityData);
  298. switch (property)
  299. {
  300. case CallQuality_ControlInterval:
  301. // timeout for the thread
  302. if (lValue < QCLIMIT_MIN_QUALITY_CONTROL_INTERVAL ||
  303. lValue > QCLIMIT_MAX_QUALITY_CONTROL_INTERVAL)
  304. {
  305. LOG ((MSP_ERROR, "%s, control interval %d is out of range", __fxName, lValue));
  306. return E_INVALIDARG;
  307. }
  308. m_dwControlInterval = (DWORD)lValue;
  309. break;
  310. case CallQuality_MaxCPULoad:
  311. // perfered maximum cpu load
  312. if ((lValue < QCLIMIT_MIN_CPU_LOAD) ||
  313. (lValue > QCLIMIT_MAX_CPU_LOAD))
  314. {
  315. LOG ((MSP_ERROR, "%s got out-of-limit cpu load. %d", __fxName, lValue));
  316. return E_INVALIDARG;
  317. }
  318. m_lPrefMaxCPULoad = lValue;
  319. m_lCPUUpThreshold = lValue + (LONG)(100 * QCDEFAULT_UP_THRESHOLD);
  320. if (m_lCPUUpThreshold > 100)
  321. m_lCPUUpThreshold = 100;
  322. m_lCPULowThreshold = lValue - (LONG)(100 * QCDEFAULT_LOW_THRESHOLD);
  323. if (m_lCPULowThreshold < 0)
  324. m_lCPULowThreshold = 0;
  325. break;
  326. case CallQuality_MaxOutputBitrate:
  327. // prefered maximum bitrate for the call
  328. if (lValue < QCLIMIT_MIN_BITRATE)
  329. {
  330. LOG ((MSP_ERROR, "%s, bitrate %d is less than min limit", __fxName, lValue));
  331. return E_INVALIDARG;
  332. }
  333. m_lPrefMaxOutputBitrate = lValue;
  334. m_lOutBitUpThreshold = (LONG)(lValue * (1 + QCDEFAULT_UP_THRESHOLD));
  335. m_lOutBitLowThreshold = (LONG)(lValue * (1 - QCDEFAULT_LOW_THRESHOLD));
  336. if (m_lOutBitLowThreshold < QCLIMIT_MIN_BITRATE)
  337. m_lOutBitLowThreshold = QCLIMIT_MIN_BITRATE;
  338. break;
  339. default:
  340. LOG ((MSP_ERROR, "%s got invalid property %d", __fxName, property));
  341. return E_NOTIMPL;
  342. }
  343. return S_OK;
  344. }
  345. /*//////////////////////////////////////////////////////////////////////////////
  346. Description:
  347. retrieve call quality control property
  348. ////*/
  349. HRESULT
  350. CCallQualityControlRelay::Get(
  351. IN CallQualityProperty property,
  352. OUT LONG *plValue,
  353. OUT TAPIControlFlags *plFlags
  354. )
  355. {
  356. ENTER_FUNCTION ("CCallQualityControlRelay::Get QCCall_e");
  357. // check input pointer
  358. if (IsBadWritePtr (plValue, sizeof (LONG)) ||
  359. IsBadWritePtr (plFlags, sizeof (LONG)))
  360. {
  361. LOG ((MSP_ERROR, "%s got bad write pointer", __fxName));
  362. return E_POINTER;
  363. }
  364. CLock lock (m_lock_QualityData);
  365. *plFlags = TAPIControl_Flags_None;
  366. *plValue = QCDEFAULT_QUALITY_UNSET;
  367. HRESULT hr = S_OK;
  368. switch (property)
  369. {
  370. case CallQuality_ControlInterval:
  371. *plValue = (LONG)m_dwControlInterval;
  372. break;
  373. case CallQuality_ConfBitrate:
  374. *plValue = GetConfBitrate ();
  375. break;
  376. case CallQuality_CurrCPULoad:
  377. DWORD dw;
  378. if (!GetCPUUsage (&dw))
  379. {
  380. LOG ((MSP_ERROR, "%s failed to retrieve CPU usage", __fxName));
  381. hr = E_FAIL;
  382. }
  383. *plValue = (LONG)dw;
  384. break;
  385. case CallQuality_CurrInputBitrate:
  386. // !!! BOTH locks are locked
  387. // !!! MUST: QualityData lock first, InnerStreamQC second
  388. m_lock_aInnerStreamQC.Lock ();
  389. if (FAILED (hr = GetCallBitrate (
  390. TAPIMEDIATYPE_AUDIO | TAPIMEDIATYPE_VIDEO, TD_RENDER, plValue)))
  391. LOG ((MSP_ERROR, "%s failed to compute input bitrate, %x", __fxName, hr));
  392. m_lock_aInnerStreamQC.Unlock ();
  393. break;
  394. case CallQuality_CurrOutputBitrate:
  395. // !!! BOTH locks are locked
  396. // !!! MUST: QualityData lock first, InnerStreamQC second
  397. m_lock_aInnerStreamQC.Lock ();
  398. if (FAILED (hr = GetCallBitrate (
  399. TAPIMEDIATYPE_AUDIO | TAPIMEDIATYPE_VIDEO, TD_CAPTURE, plValue)))
  400. LOG ((MSP_ERROR, "%s failed to compute output bitrate, %x", __fxName, hr));
  401. m_lock_aInnerStreamQC.Unlock ();
  402. break;
  403. default:
  404. LOG ((MSP_ERROR, "%s got invalid property %d", __fxName, property));
  405. hr = E_NOTIMPL;
  406. }
  407. return S_OK;
  408. }
  409. HRESULT CCallQualityControlRelay::GetRange (
  410. IN CallQualityProperty Property,
  411. OUT long *plMin,
  412. OUT long *plMax,
  413. OUT long *plSteppingDelta,
  414. OUT long *plDefault,
  415. OUT TAPIControlFlags *plFlags
  416. )
  417. {
  418. // no need to lock
  419. if (IsBadWritePtr (plMin, sizeof (long)) ||
  420. IsBadWritePtr (plMax, sizeof (long)) ||
  421. IsBadWritePtr (plSteppingDelta, sizeof (long)) ||
  422. IsBadWritePtr (plDefault, sizeof (long)) ||
  423. IsBadWritePtr (plFlags, sizeof (TAPIControlFlags)))
  424. {
  425. LOG ((MSP_ERROR, "CCallQualityControlRelay::GetRange bad write pointer"));
  426. return E_POINTER;
  427. }
  428. HRESULT hr;
  429. switch (Property)
  430. {
  431. case CallQuality_ControlInterval:
  432. *plMin = QCLIMIT_MIN_QUALITY_CONTROL_INTERVAL;
  433. *plMax = QCLIMIT_MAX_QUALITY_CONTROL_INTERVAL;
  434. *plSteppingDelta = 1;
  435. *plDefault = QCDEFAULT_QUALITY_CONTROL_INTERVAL;
  436. *plFlags = TAPIControl_Flags_None;
  437. hr = S_OK;
  438. break;
  439. case CallQuality_MaxCPULoad:
  440. *plMin = QCLIMIT_MIN_CPU_LOAD;
  441. *plMax = QCLIMIT_MAX_CPU_LOAD;
  442. *plSteppingDelta = 1;
  443. *plDefault = QCDEFAULT_MAX_CPU_LOAD;
  444. *plFlags = TAPIControl_Flags_None;
  445. hr = S_OK;
  446. break;
  447. default:
  448. hr = E_NOTIMPL;
  449. }
  450. return hr;
  451. }
  452. /*//////////////////////////////////////////////////////////////////////////////
  453. ////*/
  454. VOID
  455. CCallQualityControlRelay::CallbackProc (BOOLEAN bTimerFired)
  456. {
  457. ENTER_FUNCTION ("CCallQualityControlRelay::CallbackProc");
  458. DWORD dwResult;
  459. // always lock quality data first
  460. m_lock_QualityData.Lock ();
  461. m_lock_aInnerStreamQC.Lock ();
  462. // set wait handle to null
  463. if (m_hWait) UnregisterWait (m_hWait);
  464. m_hWait = NULL;
  465. if (m_fStop) {
  466. LOG ((MSP_TRACE, "%s is being stopped. call=%p", __fxName, m_pCall));
  467. m_fStopAck = TRUE;
  468. m_lock_aInnerStreamQC.Unlock ();
  469. m_lock_QualityData.Unlock ();
  470. m_pCall->MSPCallRelease ();
  471. return;
  472. }
  473. if (!bTimerFired)
  474. LOG ((MSP_ERROR, "%s, QC events are not supported", __fxName));
  475. else
  476. ReDistributeResources ();
  477. BOOL fSuccess = RegisterWaitForSingleObject (
  478. &m_hWait,
  479. m_hQCEvent,
  480. WaitOrTimerCallback,
  481. (PVOID) this,
  482. m_dwControlInterval,
  483. WT_EXECUTEONLYONCE
  484. );
  485. if (!fSuccess || NULL == m_hWait)
  486. {
  487. LOG ((MSP_ERROR, "%s failed to register wait, %d", __fxName, GetLastError ()));
  488. LOG ((MSP_TRACE, "%s self-stops. call=%p", __fxName, m_pCall));
  489. m_fStopAck = TRUE;
  490. m_hWait = NULL;
  491. m_lock_aInnerStreamQC.Unlock ();
  492. m_lock_QualityData.Unlock ();
  493. m_pCall->MSPCallRelease ();
  494. return;
  495. }
  496. m_lock_aInnerStreamQC.Unlock ();
  497. m_lock_QualityData.Unlock ();
  498. }
  499. /*//////////////////////////////////////////////////////////////////////////////
  500. ////*/
  501. HRESULT
  502. CCallQualityControlRelay::GetCallBitrate (
  503. LONG MediaType,
  504. TERMINAL_DIRECTION Direction,
  505. LONG *plValue
  506. )
  507. {
  508. ENTER_FUNCTION ("CCallQualityControlRelay::GetCallBitrate");
  509. LONG sum = 0;
  510. LONG bitrate;
  511. TAPIControlFlags flags;
  512. HRESULT hr;
  513. *plValue = 0;
  514. ITStream *pStream;
  515. LONG mediatype;
  516. TERMINAL_DIRECTION direction;
  517. int i;
  518. for (i=0; i<m_aInnerStreamQC.GetSize (); i++)
  519. {
  520. if (FAILED (hr = m_aInnerStreamQC[i]->QueryInterface (
  521. __uuidof (ITStream), (void**)&pStream)))
  522. {
  523. LOG ((MSP_ERROR, "%s failed to get ITStream interface. %x", __fxName, hr));
  524. return hr;
  525. }
  526. hr = pStream->get_Direction (&direction);
  527. if (FAILED (hr))
  528. {
  529. LOG ((MSP_ERROR, "%s failed to get stream direction. %x", __fxName, hr));
  530. pStream->Release ();
  531. return hr;
  532. }
  533. hr = pStream->get_MediaType (&mediatype);
  534. pStream->Release ();
  535. if (FAILED (hr))
  536. {
  537. LOG ((MSP_ERROR, "%s failed to get stream media type. %x", __fxName, hr));
  538. return hr;
  539. }
  540. if (!(MediaType & mediatype) || // skip if mediatype not match
  541. !(direction == TD_BIDIRECTIONAL || Direction == direction))
  542. continue;
  543. // get bitrate from each stream
  544. hr = m_aInnerStreamQC[i]->Get (InnerStreamQuality_CurrBitrate, &bitrate, &flags);
  545. if (E_NOTIMPL == hr)
  546. continue;
  547. if (FAILED (hr))
  548. {
  549. LOG ((MSP_ERROR, "%s failed to get bitrate from stream. %x", __fxName, hr));
  550. return hr;
  551. }
  552. sum += bitrate;
  553. }
  554. *plValue = sum;
  555. return S_OK;
  556. }
  557. /*//////////////////////////////////////////////////////////////////////////////
  558. ////*/
  559. VOID
  560. CCallQualityControlRelay::ReDistributeResources (VOID)
  561. {
  562. #ifdef DEBUG_QUALITY_CONTROL
  563. // read quality settings from registry
  564. QCDbgRead ();
  565. #endif // DEBUG_QUALITY_CONTROL
  566. ReDistributeBandwidth ();
  567. ReDistributeCPU ();
  568. }
  569. /*//////////////////////////////////////////////////////////////////////////////
  570. ////*/
  571. VOID
  572. CCallQualityControlRelay::ReDistributeCPU (VOID)
  573. {
  574. ENTER_FUNCTION ("CCallQualityControlRelay::ReDistributeCPU");
  575. HRESULT hr;
  576. int i, num_manual=0, num_total=m_aInnerStreamQC.GetSize ();
  577. LONG framerate;
  578. TAPIControlFlags flag;
  579. // check each stream, if manual, adjust based on preferred value
  580. for (i=0; i<num_total; i++)
  581. {
  582. BOOL fStreamLocked = FALSE;
  583. CInnerStreamLock lock(m_aInnerStreamQC[i], &fStreamLocked);
  584. if (!fStreamLocked)
  585. {
  586. // abort re-distribute resources
  587. return;
  588. }
  589. if (FAILED (hr = m_aInnerStreamQC[i]->Get (InnerStreamQuality_PrefMinFrameInterval, &framerate, &flag)))
  590. {
  591. LOG ((MSP_ERROR, "%s failed to get pref max frame rate (unset) on stream, %x", __fxName, hr));
  592. continue;
  593. }
  594. if (flag == TAPIControl_Flags_Manual)
  595. {
  596. num_manual ++;
  597. // use preferred value
  598. hr = m_aInnerStreamQC[i]->Set (InnerStreamQuality_AdjMinFrameInterval, framerate, flag);
  599. if (E_NOTIMPL == hr)
  600. continue;
  601. if (FAILED (hr))
  602. {
  603. LOG ((MSP_ERROR, "%s failed to set adj max frame interval. %x", __fxName, hr));
  604. continue;
  605. }
  606. }
  607. }
  608. // if global cpu load out of range, just return
  609. // it should not happen but we have a back door in registry for debugging purpose
  610. // just be careful here
  611. if (QCLIMIT_MIN_CPU_LOAD > m_lPrefMaxCPULoad ||
  612. QCLIMIT_MAX_CPU_LOAD < m_lPrefMaxCPULoad)
  613. return;
  614. // compute current usage
  615. DWORD dw;
  616. if (!GetCPUUsage (&dw))
  617. {
  618. LOG ((MSP_ERROR, "%s failed to get CPU usage", __fxName));
  619. return;
  620. }
  621. LONG usage = (LONG)dw;
  622. // return if within thresholds
  623. if (usage >= m_lCPULowThreshold &&
  624. usage <= m_lCPUUpThreshold)
  625. return;
  626. // percent to be adjusted
  627. FLOAT percent = ((FLOAT)(m_lPrefMaxCPULoad - usage)) / m_lPrefMaxCPULoad;
  628. #ifdef DEBUG_QUALITY_CONTROL
  629. if (m_fQCDbgTraceCPULoad)
  630. LOG ((MSP_TRACE, "QCTrace CPU: overall = %d, target = %d", usage, m_lPrefMaxCPULoad));
  631. #endif //DEBUG_QUALITY_CONTROL
  632. for (i=0; i<num_total; i++)
  633. {
  634. BOOL fStreamLocked = FALSE;
  635. CInnerStreamLock lock(m_aInnerStreamQC[i], &fStreamLocked);
  636. if (!fStreamLocked)
  637. {
  638. // abort re-distribute resources
  639. return;
  640. }
  641. // get flag
  642. if (FAILED (hr = m_aInnerStreamQC[i]->Get (InnerStreamQuality_PrefMinFrameInterval, &framerate, &flag)))
  643. {
  644. LOG ((MSP_ERROR, "%s failed to get pref max frame rate (unset) on stream, %d", __fxName, hr));
  645. continue;
  646. }
  647. // if manual, skip
  648. if (flag == TAPIControl_Flags_Manual)
  649. continue;
  650. // get current frame rate on the stream
  651. if (E_NOTIMPL == (hr = m_aInnerStreamQC[i]->Get (InnerStreamQuality_AvgFrameInterval,
  652. &framerate, &flag)))
  653. continue;
  654. if (FAILED (hr))
  655. {
  656. LOG ((MSP_ERROR, "%s failed to get frame rate on stream, %x", __fxName, hr));
  657. continue;
  658. }
  659. // need to low cpu but interval is already maximum
  660. if (percent <0 && framerate >= QCLIMIT_MAX_FRAME_INTERVAL)
  661. continue;
  662. #ifdef DEBUG_QUALITY_CONTROL
  663. if (m_fQCDbgTraceCPULoad)
  664. {
  665. ITStream *pStream = NULL;
  666. BSTR bstr = NULL;
  667. if (S_OK == m_aInnerStreamQC[i]->QueryInterface (__uuidof (ITStream), (void**)&pStream))
  668. {
  669. pStream->get_Name (&bstr);
  670. pStream->Release ();
  671. }
  672. LOG ((MSP_TRACE, "QCTrace CPU: %ws frameinterval = %d", bstr, framerate));
  673. if (bstr) SysFreeString (bstr);
  674. }
  675. #endif //DEBUG_QUALITY_CONTROL
  676. // heuristic here is to take into consideration of stream not having been adjusted
  677. framerate -= (LONG) (framerate * percent * (1 + num_manual*0.2));
  678. if (framerate > QCLIMIT_MAX_FRAME_INTERVAL)
  679. framerate = QCLIMIT_MAX_FRAME_INTERVAL;
  680. if (framerate < QCLIMIT_MIN_FRAME_INTERVAL)
  681. framerate = QCLIMIT_MIN_FRAME_INTERVAL;
  682. #ifdef DEBUG_QUALITY_CONTROL
  683. if (m_fQCDbgTraceCPULoad)
  684. LOG ((MSP_TRACE, "QCTrace CPU: target frameinterval = %d", framerate));
  685. #endif //DEBUG_QUALITY_CONTROL
  686. // set new value
  687. if (FAILED (hr = m_aInnerStreamQC[i]->Set (InnerStreamQuality_AdjMinFrameInterval,
  688. framerate, flag)))
  689. {
  690. LOG ((MSP_ERROR, "%s failed to set frame interval on stream, %x", __fxName, hr));
  691. }
  692. }
  693. }
  694. /*//////////////////////////////////////////////////////////////////////////////
  695. ////*/
  696. VOID
  697. CCallQualityControlRelay::ReDistributeBandwidth (VOID)
  698. {
  699. ENTER_FUNCTION ("CCallQualityControlRelay::ReDistributeBandwidth");
  700. HRESULT hr;
  701. int i, num_manual=0, num_total=m_aInnerStreamQC.GetSize ();
  702. LONG bitrate;
  703. TAPIControlFlags flag;
  704. LONG mediatype;
  705. TERMINAL_DIRECTION direction;
  706. // video out bitrate based on conference-wide bitrate
  707. LONG vidoutbitrate = GetVideoOutBitrate ();
  708. // check each stream, if manual, adjust based on preferred value
  709. for (i=0; i<num_total; i++)
  710. {
  711. BOOL fStreamLocked = FALSE;
  712. CInnerStreamLock lock(m_aInnerStreamQC[i], &fStreamLocked);
  713. if (!fStreamLocked)
  714. {
  715. // abort re-distribute resources
  716. return;
  717. }
  718. if (FAILED (hr = m_aInnerStreamQC[i]->Get (InnerStreamQuality_PrefMaxBitrate, &bitrate, &flag)))
  719. {
  720. LOG ((MSP_ERROR, "%s failed to get pref max bitrate (unset) on stream, %d", __fxName, hr));
  721. continue;
  722. }
  723. if (flag == TAPIControl_Flags_Manual)
  724. {
  725. num_manual ++;
  726. // check stream type
  727. if (FAILED (::TypeStream (m_aInnerStreamQC[i], &mediatype, &direction)))
  728. {
  729. LOG ((MSP_ERROR, "%s failed to get stream type", __fxName));
  730. continue;
  731. }
  732. // if it is video out stream and conference-wide bitrate is set
  733. // and the limit on video out stream is smaller than preferred value
  734. if ((mediatype & TAPIMEDIATYPE_VIDEO) &&
  735. direction == TD_CAPTURE &&
  736. vidoutbitrate > QCLIMIT_MIN_BITRATE &&
  737. vidoutbitrate < bitrate)
  738. {
  739. bitrate = vidoutbitrate;
  740. }
  741. hr = m_aInnerStreamQC[i]->Set (InnerStreamQuality_AdjMaxBitrate, bitrate, flag);
  742. if (E_NOTIMPL == hr)
  743. continue;
  744. if (FAILED (hr))
  745. {
  746. LOG ((MSP_ERROR, "%s failed to set adj max bitrate. %d", __fxName, hr));
  747. continue;
  748. }
  749. }
  750. }
  751. // return if target is not set
  752. if (m_lPrefMaxOutputBitrate == QCDEFAULT_QUALITY_UNSET &&
  753. vidoutbitrate < QCLIMIT_MIN_CONFBITRATE)
  754. return;
  755. // compute bitrate target based on preferred value and conference-wide limit
  756. LONG usage;
  757. if (S_OK != (hr = GetCallBitrate (
  758. TAPIMEDIATYPE_VIDEO | TAPIMEDIATYPE_AUDIO, TD_CAPTURE, &usage)))
  759. {
  760. LOG ((MSP_ERROR, "%s failed to get bandwidth usage, %x", __fxName, hr));
  761. return;
  762. }
  763. // return if usage is within threshold
  764. FLOAT percent = 0;
  765. if (m_lPrefMaxOutputBitrate != QCDEFAULT_QUALITY_UNSET &&
  766. (usage > m_lOutBitUpThreshold || usage < m_lOutBitLowThreshold))
  767. {
  768. percent = ((FLOAT)(m_lPrefMaxOutputBitrate - usage)) / m_lPrefMaxOutputBitrate;
  769. }
  770. #ifdef DEBUG_QUALITY_CONTROL
  771. if (m_fQCDbgTraceBitrate && m_lPrefMaxOutputBitrate != QCDEFAULT_QUALITY_UNSET)
  772. LOG ((MSP_TRACE, "QCTrace Bitrate: overall = %d, target = %d", usage, m_lPrefMaxOutputBitrate));
  773. #endif //DEBUG_QUALITY_CONTROL
  774. for (i=0; i<num_total; i++)
  775. {
  776. BOOL fStreamLocked = FALSE;
  777. CInnerStreamLock lock(m_aInnerStreamQC[i], &fStreamLocked);
  778. if (!fStreamLocked)
  779. {
  780. // abort re-distribute resources
  781. return;
  782. }
  783. // get flag
  784. if (FAILED (hr = m_aInnerStreamQC[i]->Get (InnerStreamQuality_PrefMaxBitrate, &bitrate, &flag)))
  785. {
  786. LOG ((MSP_ERROR, "%s failed to get pref max bitrate (unset) on stream, %d", __fxName, hr));
  787. continue;
  788. }
  789. if (FAILED (::TypeStream (m_aInnerStreamQC[i], &mediatype, &direction)))
  790. {
  791. LOG ((MSP_ERROR, "%s failed to get stream type", __fxName));
  792. continue;
  793. }
  794. // return if render
  795. if (direction == TD_RENDER)
  796. {
  797. // only count manual for capture or bidirectional
  798. if (flag == TAPIControl_Flags_Manual)
  799. num_manual --;
  800. continue;
  801. }
  802. // if manual, skip
  803. if (flag == TAPIControl_Flags_Manual)
  804. continue;
  805. // we only deal with video capture stream
  806. if (!(TAPIMEDIATYPE_VIDEO & mediatype))
  807. continue;
  808. // get current bit rate on the stream
  809. if (E_NOTIMPL == (hr = m_aInnerStreamQC[i]->Get (InnerStreamQuality_CurrBitrate,
  810. &bitrate, &flag)))
  811. continue;
  812. if (FAILED (hr))
  813. {
  814. LOG ((MSP_ERROR, "%s failed to get bitrate on stream, %x", __fxName, hr));
  815. continue;
  816. }
  817. // need to low bandwidth but bitrate is already minimum
  818. if (percent <0 && bitrate <= QCLIMIT_MIN_BITRATE)
  819. continue;
  820. #ifdef DEBUG_QUALITY_CONTROL
  821. if (m_fQCDbgTraceBitrate)
  822. {
  823. ITStream *pStream = NULL;
  824. BSTR bstr = NULL;
  825. if (S_OK == m_aInnerStreamQC[i]->QueryInterface (__uuidof (ITStream), (void**)&pStream))
  826. {
  827. pStream->get_Name (&bstr);
  828. pStream->Release ();
  829. }
  830. LOG ((MSP_TRACE, "QCTrace Bitrate: %ws bitrate = %d", bstr, bitrate));
  831. if (bstr) SysFreeString (bstr);
  832. }
  833. #endif //DEBUG_QUALITY_CONTROL
  834. //
  835. // we are here because either m_lPrefMaxOutputBitrate is set by app,
  836. // and/or conference-wide bandwidth is specified.
  837. //
  838. if (m_lPrefMaxOutputBitrate != QCDEFAULT_QUALITY_UNSET)
  839. {
  840. // percent makes sense here
  841. // heuristic here is to take into consideration of stream not having been adjusted
  842. bitrate += (LONG) (bitrate * percent * (1 + num_manual*0.3));
  843. if (vidoutbitrate > QCLIMIT_MIN_BITRATE)
  844. if (bitrate > vidoutbitrate)
  845. bitrate = vidoutbitrate;
  846. }
  847. else
  848. {
  849. if (vidoutbitrate > QCLIMIT_MIN_BITRATE)
  850. bitrate = vidoutbitrate;
  851. }
  852. if (bitrate < QCLIMIT_MIN_BITRATE)
  853. bitrate = QCLIMIT_MIN_BITRATE;
  854. if (bitrate < QCLIMIT_MIN_BITRATE*10)
  855. {
  856. // we want very lower bitrate, try to decrease frame rate as well
  857. m_lPrefMaxCPULoad -= 5;
  858. if (m_lPrefMaxCPULoad < QCLIMIT_MIN_CPU_LOAD)
  859. m_lPrefMaxCPULoad = QCLIMIT_MIN_CPU_LOAD;
  860. }
  861. #ifdef DEBUG_QUALITY_CONTROL
  862. if (m_fQCDbgTraceBitrate)
  863. LOG ((MSP_TRACE, "QCTrace Bitrate: target bitrate = %d", bitrate));
  864. #endif //DEBUG_QUALITY_CONTROL
  865. // set new value
  866. if (E_NOTIMPL == (hr = m_aInnerStreamQC[i]->Set (InnerStreamQuality_AdjMaxBitrate,
  867. bitrate, flag)))
  868. continue;
  869. if (FAILED (hr))
  870. {
  871. LOG ((MSP_ERROR, "%s failed to set bitrate on stream, %x", __fxName, hr));
  872. }
  873. }
  874. }
  875. #ifdef DEBUG_QUALITY_CONTROL
  876. /*//////////////////////////////////////////////////////////////////////////////
  877. ////*/
  878. VOID
  879. CCallQualityControlRelay::QCDbgInitiate (VOID)
  880. {
  881. ENTER_FUNCTION ("CCallQualityControlRelay::QCDbgInitiate");
  882. if (ERROR_SUCCESS != RegOpenKeyEx (
  883. HKEY_LOCAL_MACHINE,
  884. _T("SOFTWARE\\Microsoft\\Tracing\\confqc"),
  885. NULL,
  886. KEY_READ,
  887. &m_hQCDbg
  888. ))
  889. {
  890. LOG ((MSP_TRACE, "%s failed to open reg key", __fxName));
  891. m_hQCDbg = NULL;
  892. }
  893. }
  894. /*//////////////////////////////////////////////////////////////////////////////
  895. ////*/
  896. VOID
  897. CCallQualityControlRelay::QCDbgRead (VOID)
  898. {
  899. ENTER_FUNCTION ("CCallQualityControlRelay::QCDbgRead");
  900. m_fQCDbgTraceCPULoad = FALSE;
  901. m_fQCDbgTraceBitrate = FALSE;
  902. if (!m_hQCDbg)
  903. return;
  904. DWORD dwType, dwSize;
  905. LONG lValue;
  906. // if debug is enabled
  907. if (ERROR_SUCCESS == RegQueryValueEx (
  908. m_hQCDbg,
  909. _T("DebugEnabled"),
  910. NULL,
  911. &dwType,
  912. (LPBYTE)&lValue,
  913. &dwSize))
  914. {
  915. if (REG_DWORD != dwType || 1 != lValue)
  916. return;
  917. }
  918. else
  919. {
  920. LOG ((MSP_WARN, "%s failed to query debug flag", __fxName));
  921. return;
  922. }
  923. // if print out trace info
  924. m_fQCDbgTraceCPULoad = FALSE;
  925. if (ERROR_SUCCESS == RegQueryValueEx (
  926. m_hQCDbg,
  927. _T("TraceCPULoad"),
  928. NULL,
  929. &dwType,
  930. (LPBYTE)&lValue,
  931. &dwSize))
  932. {
  933. if (REG_DWORD == dwType && 1 == lValue)
  934. m_fQCDbgTraceCPULoad = TRUE;
  935. }
  936. m_fQCDbgTraceBitrate = FALSE;
  937. if (ERROR_SUCCESS == RegQueryValueEx (
  938. m_hQCDbg,
  939. _T("TraceBitrate"),
  940. NULL,
  941. &dwType,
  942. (LPBYTE)&lValue,
  943. &dwSize))
  944. {
  945. if (REG_DWORD == dwType && 1 == lValue)
  946. m_fQCDbgTraceBitrate = TRUE;
  947. }
  948. // control interval
  949. if (ERROR_SUCCESS == RegQueryValueEx (
  950. m_hQCDbg,
  951. _T("ControlInterval"),
  952. NULL,
  953. &dwType,
  954. (LPBYTE)&lValue,
  955. &dwSize))
  956. {
  957. if (REG_DWORD == dwType && lValue >= QCLIMIT_MIN_QUALITY_CONTROL_INTERVAL)
  958. m_dwControlInterval = (DWORD)lValue;
  959. else
  960. LOG ((MSP_ERROR, "%s: qeury control interval wrong type %d or wrong value %d", __fxName, dwType, lValue));
  961. }
  962. // max cpu load
  963. if (ERROR_SUCCESS == RegQueryValueEx (
  964. m_hQCDbg,
  965. _T("MaxCPULoad"),
  966. NULL,
  967. &dwType,
  968. (LPBYTE)&lValue,
  969. &dwSize))
  970. {
  971. if (REG_DWORD == dwType && QCLIMIT_MIN_CPU_LOAD <= lValue && lValue <= QCLIMIT_MAX_CPU_LOAD)
  972. m_lPrefMaxCPULoad = lValue;
  973. else
  974. LOG ((MSP_ERROR, "%s: qeury max cpu load wrong type %d or wrong value %d", __fxName, dwType, lValue));
  975. // update threshold
  976. m_lCPUUpThreshold = m_lPrefMaxCPULoad + (LONG)(100 * QCDEFAULT_UP_THRESHOLD);
  977. if (m_lCPUUpThreshold > 100)
  978. m_lCPUUpThreshold = 100;
  979. m_lCPULowThreshold = m_lPrefMaxCPULoad - (LONG)(100 * QCDEFAULT_LOW_THRESHOLD);
  980. if (m_lCPULowThreshold < 0)
  981. m_lCPULowThreshold = 0;
  982. }
  983. // max call bitrate
  984. if (ERROR_SUCCESS == RegQueryValueEx (
  985. m_hQCDbg,
  986. _T("MaxOutputBitrate"),
  987. NULL,
  988. &dwType,
  989. (LPBYTE)&lValue,
  990. &dwSize))
  991. {
  992. if (REG_DWORD == dwType && QCLIMIT_MIN_BITRATE <= lValue)
  993. m_lPrefMaxOutputBitrate = lValue;
  994. else
  995. LOG ((MSP_ERROR, "%s: qeury max call bitrate wrong type %d or wrong value %d", __fxName, dwType, lValue));
  996. // update threshold
  997. m_lOutBitUpThreshold = (LONG)(lValue * (1 + QCDEFAULT_UP_THRESHOLD));
  998. m_lOutBitLowThreshold = (LONG)(lValue * (1 - QCDEFAULT_LOW_THRESHOLD));
  999. if (m_lOutBitLowThreshold < QCLIMIT_MIN_BITRATE)
  1000. m_lOutBitLowThreshold = QCLIMIT_MIN_BITRATE;
  1001. }
  1002. }
  1003. /*//////////////////////////////////////////////////////////////////////////////
  1004. ////*/
  1005. VOID
  1006. CCallQualityControlRelay::QCDbgShutdown (VOID)
  1007. {
  1008. if (m_hQCDbg)
  1009. {
  1010. RegCloseKey (m_hQCDbg);
  1011. m_hQCDbg = NULL;
  1012. }
  1013. }
  1014. #endif // DEBUG_QUALITY_CONTROL
  1015. #pragma warning( disable: 4244 )
  1016. BOOL CCallQualityControlRelay::GetCPUUsage(PDWORD pdwOverallCPUUsage) {
  1017. SYSTEM_PERFORMANCE_INFORMATION PerfInfo;
  1018. static BOOL Initialized = FALSE;
  1019. static SYSTEM_PERFORMANCE_INFORMATION PreviousPerfInfo;
  1020. static SYSTEM_BASIC_INFORMATION BasicInfo;
  1021. static FILETIME PreviousFileTime;
  1022. static FILETIME CurrentFileTime;
  1023. LARGE_INTEGER EndTime, BeginTime, ElapsedTime;
  1024. int PercentBusy;
  1025. *pdwOverallCPUUsage = 0;
  1026. //
  1027. NTSTATUS Status =
  1028. NtQuerySystemInformation(
  1029. SystemPerformanceInformation,
  1030. &PerfInfo,
  1031. sizeof(PerfInfo),
  1032. NULL
  1033. );
  1034. if (NT_ERROR(Status))
  1035. return FALSE;
  1036. // first-time query...
  1037. if (!Initialized) {
  1038. // Get basic info (number of CPU)
  1039. Status =
  1040. NtQuerySystemInformation(
  1041. SystemBasicInformation,
  1042. &BasicInfo,
  1043. sizeof(BasicInfo),
  1044. NULL
  1045. );
  1046. if (NT_ERROR(Status))
  1047. return FALSE;
  1048. GetSystemTimeAsFileTime(&PreviousFileTime);
  1049. PreviousPerfInfo = PerfInfo;
  1050. *pdwOverallCPUUsage = 0;
  1051. Initialized = TRUE;
  1052. return TRUE;
  1053. }
  1054. GetSystemTimeAsFileTime(&CurrentFileTime);
  1055. LARGE_INTEGER TimeBetweenQueries;
  1056. //TimeBetweenQueries.QuadPart = (LARGE_INTEGER)CurrentFileTime - (LARGE_INTEGER)PreviousFileTime;
  1057. TimeBetweenQueries.HighPart = CurrentFileTime.dwHighDateTime - PreviousFileTime.dwHighDateTime;
  1058. TimeBetweenQueries.LowPart = CurrentFileTime.dwLowDateTime - PreviousFileTime.dwLowDateTime;
  1059. EndTime = *(PLARGE_INTEGER)&PerfInfo.IdleProcessTime;
  1060. BeginTime = *(PLARGE_INTEGER)&PreviousPerfInfo.IdleProcessTime;
  1061. ElapsedTime.QuadPart = EndTime.QuadPart - BeginTime.QuadPart;
  1062. if (TimeBetweenQueries.QuadPart <= 0)
  1063. {
  1064. PercentBusy = 0;
  1065. LOG ((MSP_WARN, "GetCPUUsage: TimeBetweenQueries.QuadPart, %d", TimeBetweenQueries.QuadPart));
  1066. }
  1067. else
  1068. {
  1069. PercentBusy = (int)
  1070. ( ((TimeBetweenQueries.QuadPart - ElapsedTime.QuadPart) * 100) /
  1071. (BasicInfo.NumberOfProcessors * TimeBetweenQueries.QuadPart)
  1072. );
  1073. }
  1074. if ( PercentBusy > 100 )
  1075. PercentBusy = 100;
  1076. else if ( PercentBusy < 0 )
  1077. PercentBusy = 0;
  1078. PreviousFileTime = CurrentFileTime;
  1079. PreviousPerfInfo = PerfInfo;
  1080. *pdwOverallCPUUsage = (DWORD)PercentBusy;
  1081. return TRUE;
  1082. }
  1083. /*//////////////////////////////////////////////////////////////////////////////
  1084. Description:
  1085. Computes video out bitrate based on conference-wide bandwidth
  1086. ////*/
  1087. LONG CCallQualityControlRelay::GetVideoOutBitrate ()
  1088. {
  1089. //
  1090. // compute
  1091. // number of video in sub streams
  1092. // audio stream bitrate
  1093. //
  1094. HRESULT hr;
  1095. LONG videooutbps = QCDEFAULT_QUALITY_UNSET;
  1096. LONG audiobps = 0;
  1097. LONG bitrate = 0;
  1098. INT numvideoin = 0;
  1099. IEnumStream *pEnum = NULL;
  1100. ITStream *pStream = NULL;
  1101. ITStreamQualityControl *pStreamQC = NULL;
  1102. ULONG fetched = 0;
  1103. CStreamVideoRecv *pVideoRecv = NULL;
  1104. LONG mediatype;
  1105. TERMINAL_DIRECTION direction;
  1106. TAPIControlFlags flag;
  1107. ENTER_FUNCTION ("Relay::GetVideoOutBitrate");
  1108. if (m_lConfBitrate < QCLIMIT_MIN_CONFBITRATE)
  1109. return videooutbps;
  1110. if (FAILED (hr = m_pCall->EnumerateStreams (&pEnum)))
  1111. {
  1112. LOG ((MSP_ERROR, "%s failed to get IEnumStream. %x", __fxName, hr));
  1113. return videooutbps;
  1114. }
  1115. while (S_OK == pEnum->Next (1, &pStream, &fetched))
  1116. {
  1117. // check each stream
  1118. if (FAILED (hr = ::TypeStream (pStream, &mediatype, &direction)))
  1119. {
  1120. LOG ((MSP_ERROR, "%s failed to type stream. %x", __fxName, hr));
  1121. goto Cleanup;
  1122. }
  1123. // if audio out, get bitrate
  1124. if ((mediatype & TAPIMEDIATYPE_AUDIO) &&
  1125. direction == TD_CAPTURE)
  1126. {
  1127. if (FAILED (hr = pStream->QueryInterface (&pStreamQC)))
  1128. {
  1129. LOG ((MSP_ERROR, "%s failed to query stream quality control. %x", __fxName, hr));
  1130. goto Cleanup;
  1131. }
  1132. if (FAILED (hr = pStreamQC->Get (StreamQuality_CurrBitrate, &bitrate, &flag)))
  1133. {
  1134. LOG ((MSP_ERROR, "%s failed to query bitrate. %x", __fxName, hr));
  1135. goto Cleanup;
  1136. }
  1137. pStreamQC->Release ();
  1138. pStreamQC = NULL;
  1139. audiobps += bitrate;
  1140. }
  1141. // we only need video in here
  1142. if (!(mediatype & TAPIMEDIATYPE_VIDEO) || direction != TD_RENDER)
  1143. {
  1144. pStream->Release ();
  1145. pStream = NULL;
  1146. continue;
  1147. }
  1148. pVideoRecv = dynamic_cast<CStreamVideoRecv *>(pStream);
  1149. if (pVideoRecv != NULL)
  1150. numvideoin += pVideoRecv->GetSubStreamCount ();
  1151. pStream->Release ();
  1152. pStream = NULL;
  1153. }
  1154. pEnum->Release ();
  1155. pEnum = NULL;
  1156. // compute
  1157. numvideoin ++; // count self
  1158. // assume on average there are 1.5 persons talking in the conference.
  1159. // we ignore network overhead.
  1160. videooutbps = (LONG)(((FLOAT)m_lConfBitrate - 1.5*audiobps) / numvideoin);
  1161. Return:
  1162. return videooutbps;
  1163. Cleanup:
  1164. if (pEnum) pEnum->Release ();
  1165. if (pStream) pStream->Release ();
  1166. if (pStreamQC) pStreamQC->Release ();
  1167. goto Return;
  1168. }
  1169. HRESULT TypeStream (IUnknown *p, LONG *pMediaType, TERMINAL_DIRECTION *pDirection)
  1170. {
  1171. HRESULT hr;
  1172. // get ITStream interface
  1173. ITStream *pStream = dynamic_cast<ITStream *>(p);
  1174. if (pStream == NULL)
  1175. {
  1176. LOG ((MSP_ERROR, "TypeStream failed to cast ITStream"));
  1177. return E_INVALIDARG;
  1178. }
  1179. // get stream direction
  1180. if (FAILED (hr = pStream->get_Direction (pDirection)))
  1181. {
  1182. LOG ((MSP_ERROR, "TypeStream failed to get stream direction. %x", hr));
  1183. return hr;
  1184. }
  1185. // get stream mediatype
  1186. if (FAILED (hr = pStream->get_MediaType (pMediaType)))
  1187. {
  1188. LOG ((MSP_ERROR, "TypeStream failed to get stream media type. %x", hr));
  1189. return hr;
  1190. }
  1191. return S_OK;
  1192. }