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.

2707 lines
60 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. confaud.cpp
  5. Abstract:
  6. This module contains implementation of the audio send and receive
  7. stream implementations.
  8. Author:
  9. Mu Han (muhan) 15-September-1999
  10. --*/
  11. #include "stdafx.h"
  12. /////////////////////////////////////////////////////////////////////////////
  13. //
  14. // Helper functions
  15. //
  16. /////////////////////////////////////////////////////////////////////////////
  17. HRESULT ConfigureFullduplexControl(
  18. IN IPin * pIPin,
  19. IN IAudioDuplexController *pIAudioDuplexController
  20. )
  21. /*++
  22. Routine Description:
  23. This method sets the AudioDuplexController on the filter.
  24. Arguments:
  25. pIPin - the pin that belongs to the filter.
  26. pIAudioDuplexController - the IAudioDuplexController interface.
  27. Return Value:
  28. HRESULT.
  29. --*/
  30. {
  31. ENTER_FUNCTION("::ConfigureFullduplexControl");
  32. LOG((MSP_TRACE, "%s enters", __fxName));
  33. if (pIAudioDuplexController == NULL)
  34. {
  35. // we don't need to configure anything.
  36. return S_OK;
  37. }
  38. HRESULT hr;
  39. // find the filter behind the pin.
  40. PIN_INFO PinInfo;
  41. if (FAILED(hr = pIPin->QueryPinInfo(&PinInfo)))
  42. {
  43. LOG((MSP_ERROR,
  44. "%s:can't get the capture filter, hr=%x",
  45. __fxName, hr));
  46. return hr;
  47. }
  48. IAudioDeviceConfig *pIAudioDeviceConfig;
  49. // get the IAudioDeviceConfig interface.
  50. hr = PinInfo.pFilter->QueryInterface(&pIAudioDeviceConfig);
  51. PinInfo.pFilter->Release();
  52. if (FAILED(hr))
  53. {
  54. LOG((MSP_WARN,
  55. "%s:query capture filter's pIAudioDeviceConfig failed, hr=%x",
  56. __fxName, hr));
  57. return hr;
  58. }
  59. // tell the filter about the full-duplex controller.
  60. hr = pIAudioDeviceConfig->SetDuplexController(pIAudioDuplexController);
  61. pIAudioDeviceConfig->Release();
  62. if (FAILED(hr))
  63. {
  64. LOG((MSP_ERROR,
  65. "%s:set IAudioDuplexController failed, hr=%x",
  66. __fxName, hr));
  67. return hr;
  68. }
  69. return hr;
  70. }
  71. /////////////////////////////////////////////////////////////////////////////
  72. //
  73. // CStreamAudioRecv
  74. //
  75. /////////////////////////////////////////////////////////////////////////////
  76. CStreamAudioRecv::CStreamAudioRecv()
  77. : CIPConfMSPStream(),
  78. m_pIAudioDuplexController(NULL)
  79. {
  80. m_szName = L"AudioRecv";
  81. }
  82. CStreamAudioRecv::~CStreamAudioRecv()
  83. {
  84. if (m_pIAudioDuplexController)
  85. {
  86. m_pIAudioDuplexController->Release();
  87. }
  88. }
  89. // this method is called by the call object at init time.
  90. void CStreamAudioRecv::SetFullDuplexController(
  91. IN IAudioDuplexController *pIAudioDuplexController
  92. )
  93. {
  94. _ASSERT(pIAudioDuplexController);
  95. _ASSERT(m_pIAudioDuplexController == NULL);
  96. pIAudioDuplexController->AddRef();
  97. m_pIAudioDuplexController = pIAudioDuplexController;
  98. }
  99. STDMETHODIMP CStreamAudioRecv::GetRange(
  100. IN AudioSettingsProperty Property,
  101. OUT long *plMin,
  102. OUT long *plMax,
  103. OUT long *plSteppingDelta,
  104. OUT long *plDefault,
  105. OUT TAPIControlFlags *plFlags
  106. )
  107. /*++
  108. Routine Description:
  109. Get the range for a audio setting property. Delegated to the renderfilter.
  110. Arguments:
  111. Return Value:
  112. HRESULT.
  113. --*/
  114. {
  115. ENTER_FUNCTION("CStreamAudioRecv::GetRange(AudioSettings)");
  116. if (IsBadWritePtr (plMin, sizeof (long)) ||
  117. IsBadWritePtr (plMax, sizeof (long)) ||
  118. IsBadWritePtr (plSteppingDelta, sizeof (long)) ||
  119. IsBadWritePtr (plDefault, sizeof (long)) ||
  120. IsBadWritePtr (plFlags, sizeof (TAPIControlFlags)))
  121. {
  122. LOG ((MSP_ERROR, "%s: bad write pointer", __fxName));
  123. return E_POINTER;
  124. }
  125. HRESULT hr;
  126. switch (Property)
  127. {
  128. case AudioSettings_SignalLevel:
  129. break;
  130. case AudioSettings_SilenceThreshold:
  131. break;
  132. case AudioSettings_Volume:
  133. break;
  134. case AudioSettings_Balance:
  135. break;
  136. case AudioSettings_Loudness:
  137. break;
  138. case AudioSettings_Treble:
  139. break;
  140. case AudioSettings_Bass:
  141. break;
  142. case AudioSettings_Mono:
  143. break;
  144. default:
  145. hr = E_INVALIDARG;
  146. }
  147. return E_NOTIMPL;
  148. }
  149. STDMETHODIMP CStreamAudioRecv::Get(
  150. IN AudioSettingsProperty Property,
  151. OUT long *plValue,
  152. OUT TAPIControlFlags *plFlags
  153. )
  154. /*++
  155. Routine Description:
  156. Get the value for a audio setting property. Delegated to the renderfilter.
  157. Arguments:
  158. Return Value:
  159. HRESULT.
  160. --*/
  161. {
  162. ENTER_FUNCTION("CStreamAudioRecv::Get(AudioSettings)");
  163. if (IsBadWritePtr (plValue, sizeof(long)) ||
  164. IsBadWritePtr (plFlags, sizeof(TAPIControlFlags)))
  165. {
  166. LOG ((MSP_ERROR, "%s received bad pointer.", __fxName));
  167. return E_POINTER;
  168. }
  169. return E_NOTIMPL;
  170. }
  171. STDMETHODIMP CStreamAudioRecv::Set(
  172. IN AudioSettingsProperty Property,
  173. IN long lValue,
  174. IN TAPIControlFlags lFlags
  175. )
  176. /*++
  177. Routine Description:
  178. Set the value for a audio setting property. Delegated to the renderfilter.
  179. Arguments:
  180. Return Value:
  181. HRESULT.
  182. --*/
  183. {
  184. ENTER_FUNCTION("CStreamAudioRecv::Set(AudioSettings)");
  185. return E_NOTIMPL;
  186. }
  187. //
  188. // ITStreamQualityControl methods
  189. //
  190. STDMETHODIMP CStreamAudioRecv::Set (
  191. IN StreamQualityProperty Property,
  192. IN long lValue,
  193. IN TAPIControlFlags lFlags
  194. )
  195. {
  196. return E_NOTIMPL;
  197. }
  198. HRESULT CStreamAudioRecv::ConfigureRTPFormats(
  199. IN IBaseFilter * pIRTPFilter,
  200. IN IStreamConfig * pIStreamConfig
  201. )
  202. /*++
  203. Routine Description:
  204. Configure the RTP filter with RTP<-->AM media type mappings.
  205. Arguments:
  206. pIRTPFilter - The source RTP Filter.
  207. pIStreamConfig - The stream config interface that has the media info.
  208. Return Value:
  209. HRESULT.
  210. --*/
  211. {
  212. ENTER_FUNCTION("AudioRecv::ConfigureRTPFormats");
  213. LOG((MSP_TRACE, "%s enters", __fxName));
  214. HRESULT hr;
  215. CComPtr<IRtpMediaControl> pIRtpMediaControl;
  216. hr = pIRTPFilter->QueryInterface(&pIRtpMediaControl);
  217. if (FAILED(hr))
  218. {
  219. LOG((MSP_ERROR, "%s adding source filter. %x", __fxName, hr));
  220. return hr;
  221. }
  222. // find the number of capabilities supported.
  223. DWORD dwCount;
  224. hr = pIStreamConfig->GetNumberOfCapabilities(&dwCount);
  225. if (FAILED(hr))
  226. {
  227. LOG((MSP_ERROR, "%s GetNumberOfCapabilities. %x", __fxName, hr));
  228. return hr;
  229. }
  230. BOOL bFound = FALSE;
  231. for (DWORD dw = 0; dw < dwCount; dw ++)
  232. {
  233. // TODO, a new interface is needed to resolve RTP to MediaType.
  234. AM_MEDIA_TYPE *pMediaType;
  235. DWORD dwPayloadType;
  236. hr = pIStreamConfig->GetStreamCaps(
  237. dw, &pMediaType, NULL, &dwPayloadType
  238. );
  239. if (FAILED(hr))
  240. {
  241. LOG((MSP_ERROR, "%s GetStreamCaps. %x", __fxName, hr));
  242. return hr;
  243. }
  244. for (DWORD dw2 = 0; dw2 < m_Settings.dwNumPayloadTypes; dw2 ++)
  245. {
  246. if (dwPayloadType == m_Settings.PayloadTypes[dw2])
  247. {
  248. hr = pIRtpMediaControl->SetFormatMapping(
  249. dwPayloadType,
  250. FindSampleRate(pMediaType),
  251. pMediaType
  252. );
  253. if (FAILED(hr))
  254. {
  255. MSPDeleteMediaType(pMediaType);
  256. LOG((MSP_ERROR, "%s SetFormatMapping. %x", __fxName, hr));
  257. return hr;
  258. }
  259. else
  260. {
  261. LOG((MSP_INFO, "%s Configured payload:%d", __fxName, dwPayloadType));
  262. }
  263. }
  264. }
  265. MSPDeleteMediaType(pMediaType);
  266. }
  267. return S_OK;
  268. }
  269. HRESULT CStreamAudioRecv::ConnectTerminal(
  270. IN ITTerminal * pITTerminal
  271. )
  272. /*++
  273. Routine Description:
  274. connect the mixer to the audio render terminal.
  275. Arguments:
  276. pITTerminal - The terminal to be connected.
  277. Return Value:
  278. HRESULT.
  279. --*/
  280. {
  281. LOG((MSP_TRACE, "AudioRecv.ConnectTerminal, pITTerminal %p", pITTerminal));
  282. HRESULT hr;
  283. // get the terminal control interface.
  284. CComQIPtr<ITTerminalControl, &__uuidof(ITTerminalControl)>
  285. pTerminal(pITTerminal);
  286. if (pTerminal == NULL)
  287. {
  288. LOG((MSP_ERROR, "can't get Terminal Control interface"));
  289. SendStreamEvent(
  290. CALL_TERMINAL_FAIL,
  291. CALL_CAUSE_BAD_DEVICE,
  292. E_NOINTERFACE,
  293. pITTerminal
  294. );
  295. return E_NOINTERFACE;
  296. }
  297. const DWORD MAXPINS = 8;
  298. DWORD dwNumPins = MAXPINS;
  299. IPin * Pins[MAXPINS];
  300. // Get the pins.
  301. hr = pTerminal->ConnectTerminal(
  302. m_pIGraphBuilder, TD_RENDER, &dwNumPins, Pins
  303. );
  304. if (FAILED(hr))
  305. {
  306. LOG((MSP_ERROR, "can't connect to terminal, %x", hr));
  307. SendStreamEvent(
  308. CALL_TERMINAL_FAIL,
  309. CALL_CAUSE_BAD_DEVICE,
  310. hr,
  311. pITTerminal
  312. );
  313. return hr;
  314. }
  315. // the pin count should never be 0.
  316. if (dwNumPins == 0)
  317. {
  318. LOG((MSP_ERROR, "terminal has no pins."));
  319. SendStreamEvent(
  320. CALL_TERMINAL_FAIL,
  321. CALL_CAUSE_BAD_DEVICE,
  322. hr,
  323. pITTerminal
  324. );
  325. pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
  326. return E_UNEXPECTED;
  327. }
  328. if (IsBadReadPtr (Pins, dwNumPins * sizeof (IPin*)))
  329. {
  330. LOG((MSP_ERROR, "terminal returned bad pin array"));
  331. SendStreamEvent(CALL_TERMINAL_FAIL, CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
  332. pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
  333. return E_POINTER;
  334. }
  335. for (DWORD i = 0; i < dwNumPins; i++)
  336. {
  337. if (IsBadReadPtr (Pins[i], sizeof (IPin)))
  338. {
  339. LOG((MSP_ERROR, "terminal returned bad pin. # %d", i));
  340. SendStreamEvent(CALL_TERMINAL_FAIL, CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
  341. pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
  342. return E_POINTER;
  343. }
  344. }
  345. // create filters and connect to the audio render terminal.
  346. hr = SetUpInternalFilters(Pins, dwNumPins);
  347. // release the refcounts on the pins.
  348. for (DWORD i = 0; i < dwNumPins; i ++)
  349. {
  350. Pins[i]->Release();
  351. }
  352. if (FAILED(hr))
  353. {
  354. LOG((MSP_ERROR, "Set up internal filter failed, %x", hr));
  355. pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
  356. CleanUpFilters();
  357. return hr;
  358. }
  359. //
  360. // Now we are actually connected. Update our state and perform postconnection
  361. // (ignore postconnection error code).
  362. //
  363. pTerminal->CompleteConnectTerminal();
  364. return hr;
  365. }
  366. HRESULT CStreamAudioRecv::DisconnectTerminal(
  367. IN ITTerminal * pITTerminal
  368. )
  369. /*++
  370. Routine Description:
  371. Disconnect a terminal. It will remove its filters from the graph and
  372. also release its references to the graph.
  373. If it is the capture terminal being disconnected, all the pins that the
  374. stream cached need to be released too.
  375. Arguments:
  376. pITTerminal - the terminal.
  377. Return Value:
  378. HRESULT.
  379. --*/
  380. {
  381. ENTER_FUNCTION("CStreamAudioRecv::DisconnectTerminal");
  382. LOG((MSP_TRACE, "%s enters, pITTerminal:%p", __fxName, pITTerminal));
  383. HRESULT hr = CIPConfMSPStream::DisconnectTerminal(pITTerminal);
  384. CleanUpFilters();
  385. return hr;
  386. }
  387. HRESULT CStreamAudioRecv::AddOneMixChannel(
  388. IN IBaseFilter* pSourceFilter,
  389. IN IPin *pPin,
  390. IN DWORD dwChannelNumber
  391. )
  392. {
  393. ENTER_FUNCTION("AudioRecv::AddDecoder");
  394. LOG((MSP_TRACE, "%s enters", __fxName));
  395. CComPtr<IBaseFilter> pDecoderFilter;
  396. HRESULT hr;
  397. if (FAILED(hr = ::AddFilter(
  398. m_pIGraphBuilder,
  399. __uuidof(TAPIAudioDecoder),
  400. L"Decoder",
  401. &pDecoderFilter
  402. )))
  403. {
  404. LOG((MSP_ERROR, "%s add Codec filter. %x", __fxName, hr));
  405. return hr;
  406. }
  407. #ifdef DYNGRAPH
  408. CComPtr <IGraphConfig> pIGraphConfig;
  409. hr = m_pIGraphBuilder->QueryInterface(
  410. __uuidof(IGraphConfig),
  411. (void**)&pIGraphConfig
  412. );
  413. if (FAILED(hr))
  414. {
  415. LOG((MSP_ERROR, "%s query IGraphConfig failed. hr=%x", __fxName, hr));
  416. return hr;
  417. }
  418. // this tell the graph that the filter can be removed during reconnect.
  419. hr = pIGraphConfig->SetFilterFlags(pDecoderFilter, AM_FILTER_FLAGS_REMOVABLE);
  420. if (FAILED(hr))
  421. {
  422. LOG((MSP_ERROR, "%s set filter flag failed. hr=%x", __fxName, hr));
  423. return hr;
  424. }
  425. /* // if there is a plugin codec,this method can be use to add it.
  426. hr = pIGraphConfig->AddFilterToCache(pDecoderFilter);
  427. if (FAILED(hr))
  428. {
  429. LOG((MSP_ERROR, "%s, AddFilterToCache failed", __fxName));
  430. return hr;
  431. }
  432. */
  433. #endif
  434. if (dwChannelNumber == 0)
  435. {
  436. // configure the formats for the RTP filter.
  437. CComPtr<IPin> pIPinInput;
  438. if (FAILED(hr = ::FindPin(pDecoderFilter, &pIPinInput, PINDIR_INPUT, TRUE)))
  439. {
  440. LOG((MSP_ERROR,
  441. "find input pin on pCodecFilter failed. hr=%x", hr));
  442. return hr;
  443. }
  444. CComPtr<IStreamConfig> pIStreamConfig;
  445. hr = pIPinInput->QueryInterface(&pIStreamConfig);
  446. if (FAILED(hr))
  447. {
  448. LOG((MSP_ERROR, "%s, query IStreamConfig failed", __fxName));
  449. return hr;
  450. }
  451. // configure the format info on the RTP filter
  452. if (FAILED(hr = ConfigureRTPFormats(pSourceFilter, pIStreamConfig)))
  453. {
  454. LOG((MSP_ERROR, "%s configure RTP formats. %x", __fxName, hr));
  455. return hr;
  456. }
  457. // give the render filter the full-duplex controller.
  458. ::ConfigureFullduplexControl(pPin, m_pIAudioDuplexController);
  459. }
  460. if (FAILED(hr = ::ConnectFilters(
  461. m_pIGraphBuilder,
  462. pSourceFilter,
  463. pDecoderFilter
  464. )))
  465. {
  466. LOG((MSP_ERROR, "%s connect source and decoder filter. %x", __fxName, hr));
  467. return hr;
  468. }
  469. if (FAILED(hr = ::ConnectFilters(
  470. m_pIGraphBuilder,
  471. pDecoderFilter,
  472. pPin
  473. )))
  474. {
  475. LOG((MSP_ERROR, "%s connect decoder filter and pin. %x", __fxName, hr));
  476. return hr;
  477. }
  478. return hr;
  479. }
  480. HRESULT CStreamAudioRecv::SetUpInternalFilters(
  481. IN IPin **ppPins,
  482. IN DWORD dwNumPins
  483. )
  484. /*++
  485. Routine Description:
  486. set up the filters used in the stream.
  487. RTP->Demux->RPH(->DECODER)->Mixer
  488. Arguments:
  489. ppPin - the input pins of the audio render terminal.
  490. dwNumPins - the number of pins in the array.
  491. Return Value:
  492. HRESULT.
  493. --*/
  494. {
  495. ENTER_FUNCTION("AudioRecv::SetUpInternalFilters");
  496. LOG((MSP_TRACE, "%s enters", __fxName));
  497. CComPtr<IBaseFilter> pSourceFilter;
  498. HRESULT hr;
  499. DWORD dw;
  500. if (m_pIRTPSession == NULL)
  501. {
  502. // create and add the source fitler.
  503. if (FAILED(hr = ::AddFilter(
  504. m_pIGraphBuilder,
  505. __uuidof(MSRTPSourceFilter),
  506. L"RtpSource",
  507. &pSourceFilter)))
  508. {
  509. LOG((MSP_ERROR, "%s adding source filter. %x", __fxName, hr));
  510. return hr;
  511. }
  512. // configure the address info on the RTP filter.
  513. if (FAILED(hr = ConfigureRTPFilter(pSourceFilter)))
  514. {
  515. LOG((MSP_ERROR, "%s configure RTP source filter. %x", __fxName, hr));
  516. return hr;
  517. }
  518. }
  519. else
  520. {
  521. if (FAILED (hr = m_pIRTPSession->QueryInterface (&pSourceFilter)))
  522. {
  523. LOG ((MSP_ERROR, "%s failed to get filter from rtp session. %x", __fxName, hr));
  524. return hr;
  525. }
  526. if (FAILED (hr = m_pIGraphBuilder->AddFilter ((IBaseFilter *)pSourceFilter, L"RtpSource")))
  527. {
  528. LOG ((MSP_ERROR, "%s failed to add filter to graph. %x", __fxName, hr));
  529. return hr;
  530. }
  531. }
  532. // get the Demux interface pointer.
  533. CComPtr<IRtpDemux> pIRtpDemux;
  534. hr = pSourceFilter->QueryInterface(&pIRtpDemux);
  535. if (FAILED(hr))
  536. {
  537. LOG((MSP_ERROR, "%s query IRtpDemux failed. %x", __fxName, hr));
  538. return hr;
  539. }
  540. // set the number of output pins we need.
  541. hr = pIRtpDemux->SetPinCount(MAX_MIX_CHANNELS, RTPDMXMODE_AUTO);
  542. if (FAILED(hr))
  543. {
  544. LOG((MSP_ERROR, "%s SetPinCount failed. %x", __fxName, hr));
  545. return hr;
  546. }
  547. // if the render handles multichannel, use it.
  548. if (dwNumPins > 1)
  549. {
  550. for (dw = 0; dw < min(dwNumPins, MAX_MIX_CHANNELS); dw ++)
  551. {
  552. hr = AddOneMixChannel(pSourceFilter, ppPins[dw], dw);
  553. if (FAILED(hr))
  554. {
  555. break;
  556. }
  557. }
  558. return hr;
  559. }
  560. //if the render filter can't handle multichannel, insert a mixer;
  561. CComPtr<IBaseFilter> pMixer;
  562. if (FAILED(hr = ::AddFilter(
  563. m_pIGraphBuilder,
  564. __uuidof(TAPIAudioMixer),
  565. L"AudioMixer",
  566. &pMixer)))
  567. {
  568. LOG((MSP_ERROR, "%s, adding audio Mixer filter. %x", __fxName, hr));
  569. return hr;
  570. }
  571. // Get the enumerator of pins on the mixer filter.
  572. CComPtr<IEnumPins> pIEnumPins;
  573. if (FAILED(hr = pMixer->EnumPins(&pIEnumPins)))
  574. {
  575. LOG((MSP_ERROR, "%s, enum pins on the Mixer filter. %x", __fxName, hr));
  576. return hr;
  577. }
  578. DWORD dwFetched;
  579. IPin * MixerPins[MAX_MIX_CHANNELS];
  580. hr = pIEnumPins->Next(MAX_MIX_CHANNELS, MixerPins, &dwFetched);
  581. if (FAILED(hr) || dwFetched == 0)
  582. {
  583. LOG((MSP_ERROR, "%s, find pin on filter. %x", __fxName, hr));
  584. return E_FAIL;
  585. }
  586. // add the decoding channels.
  587. for (dw = 0; dw < dwFetched; dw ++)
  588. {
  589. hr = AddOneMixChannel(pSourceFilter, MixerPins[dw], dw);
  590. if (FAILED(hr))
  591. {
  592. break;
  593. }
  594. }
  595. // release the refcounts on the pins.
  596. for (dw = 0; dw < dwFetched; dw ++)
  597. {
  598. MixerPins[dw]->Release();
  599. }
  600. if (FAILED(hr))
  601. {
  602. LOG((MSP_ERROR, "%s, add mix channel failed %x", __fxName, hr));
  603. return hr;
  604. }
  605. if (FAILED(hr = ::ConnectFilters(
  606. m_pIGraphBuilder,
  607. pMixer,
  608. ppPins[0]
  609. )))
  610. {
  611. LOG((MSP_ERROR, "%s connect mixer filter and pin. %x", __fxName, hr));
  612. return hr;
  613. }
  614. return S_OK;
  615. }
  616. HRESULT CStreamAudioRecv::SetUpFilters()
  617. /*++
  618. Routine Description:
  619. Insert filters into the graph and connect to the terminals.
  620. Arguments:
  621. Return Value:
  622. HRESULT.
  623. --*/
  624. {
  625. LOG((MSP_TRACE, "AudioRecv SetupFilters entered."));
  626. HRESULT hr;
  627. // we only support one terminal for this stream.
  628. if (m_Terminals.GetSize() != 1)
  629. {
  630. return E_UNEXPECTED;
  631. }
  632. // Connect the mixer to the terminal.
  633. if (FAILED(hr = ConnectTerminal(
  634. m_Terminals[0]
  635. )))
  636. {
  637. LOG((MSP_ERROR, "connect the mixer filter to terminal. %x", hr));
  638. return hr;
  639. }
  640. return hr;
  641. }
  642. HRESULT CStreamAudioRecv::ProcessTalkingEvent(
  643. IN DWORD dwSSRC
  644. )
  645. /*++
  646. Routine Description:
  647. a SSRC is active, file a participant active event.
  648. Arguments:
  649. dwSSRC - the SSRC of the participant.
  650. Return Value:
  651. S_OK,
  652. E_UNEXPECTED
  653. --*/
  654. {
  655. LOG((MSP_TRACE, "%ls Processes pin mapped event, pIPin: %p", m_szName, dwSSRC));
  656. CLock lock(m_lock);
  657. ITParticipant * pITParticipant = NULL;
  658. // find the SSRC in our participant list.
  659. for (int i = 0; i < m_Participants.GetSize(); i ++)
  660. {
  661. if (((CParticipant *)m_Participants[i])->
  662. HasSSRC((ITStream *)this, dwSSRC))
  663. {
  664. pITParticipant = m_Participants[i];
  665. }
  666. }
  667. // if the participant is not there yet, put the event in a queue and it
  668. // will be fired when we have the CName fo the participant.
  669. if (!pITParticipant)
  670. {
  671. LOG((MSP_INFO, "can't find a participant that has SSRC %x", dwSSRC));
  672. m_PendingSSRCs.Add(dwSSRC);
  673. LOG((MSP_INFO, "added the event to pending list, new list size:%d",
  674. m_PendingSSRCs.GetSize()));
  675. return S_OK;
  676. }
  677. ((CIPConfMSPCall *)m_pMSPCall)->SendParticipantEvent(
  678. PE_PARTICIPANT_ACTIVE,
  679. pITParticipant
  680. );
  681. return S_OK;
  682. }
  683. HRESULT CStreamAudioRecv::NewParticipantPostProcess(
  684. IN DWORD dwSSRC,
  685. IN ITParticipant *pITParticipant
  686. )
  687. /*++
  688. Routine Description:
  689. A mapped event happended when we didn't have the participant's name so
  690. it was queued in a list. Now that we have a new participant, let's check
  691. if this is the same participant. If it is, we complete the mapped event
  692. by sending the app an notification.
  693. Arguments:
  694. dwSSRC - the SSRC of the participant.
  695. pITParticipant - the participant object.
  696. Return Value:
  697. S_OK,
  698. E_UNEXPECTED
  699. --*/
  700. {
  701. LOG((MSP_TRACE, "%ls Check pending mapped event, dwSSRC: %x", m_szName, dwSSRC));
  702. // look at the pending SSRC list and find out if this report
  703. // fits in the list.
  704. int i = m_PendingSSRCs.Find(dwSSRC);
  705. if (i < 0)
  706. {
  707. // the SSRC is not in the list of pending PinMappedEvents.
  708. LOG((MSP_TRACE, "the SSRC %x is not in the pending list", dwSSRC));
  709. return S_OK;
  710. }
  711. // get rid of the peding SSRC.
  712. m_PendingSSRCs.RemoveAt(i);
  713. // complete the event.
  714. ((CIPConfMSPCall *)m_pMSPCall)->SendParticipantEvent(
  715. PE_PARTICIPANT_ACTIVE,
  716. pITParticipant
  717. );
  718. return S_OK;
  719. }
  720. HRESULT CStreamAudioRecv::ProcessWasTalkingEvent(
  721. IN DWORD dwSSRC
  722. )
  723. /*++
  724. Routine Description:
  725. A SSRC just got unmapped by the demux. Notify the app that a participant
  726. becomes inactive.
  727. Arguments:
  728. dwSSRC - the SSRC of the participant.
  729. Return Value:
  730. S_OK,
  731. E_UNEXPECTED
  732. --*/
  733. {
  734. LOG((MSP_TRACE, "%ls Processes SSRC unmapped event, pIPin: %p", m_szName, dwSSRC));
  735. CLock lock(m_lock);
  736. // look at the pending SSRC list and find out if it is in the pending list.
  737. int i = m_PendingSSRCs.Find(dwSSRC);
  738. // if the SSRC is in the pending list, just remove it.
  739. if (i >= 0)
  740. {
  741. m_PendingSSRCs.RemoveAt(i);
  742. return S_OK;
  743. }
  744. ITParticipant *pITParticipant = NULL;
  745. // find the SSRC in our participant list.
  746. for (i = 0; i < m_Participants.GetSize(); i ++)
  747. {
  748. if (((CParticipant *)m_Participants[i])->
  749. HasSSRC((ITStream *)this, dwSSRC))
  750. {
  751. pITParticipant = m_Participants[i];
  752. }
  753. }
  754. if (pITParticipant)
  755. {
  756. // fire an event to tell the app that the participant is inactive.
  757. ((CIPConfMSPCall *)m_pMSPCall)->SendParticipantEvent(
  758. PE_PARTICIPANT_INACTIVE,
  759. pITParticipant
  760. );
  761. }
  762. return S_OK;
  763. }
  764. HRESULT CStreamAudioRecv::ProcessParticipantLeave(
  765. IN DWORD dwSSRC
  766. )
  767. /*++
  768. Routine Description:
  769. When participant left the session, remove the stream from the participant
  770. object's list of streams. If all streams are removed, remove the
  771. participant from the call object's list too.
  772. Arguments:
  773. dwSSRC - the SSRC of the participant left.
  774. Return Value:
  775. HRESULT.
  776. --*/
  777. {
  778. LOG((MSP_TRACE, "%ls ProcessParticipantLeave, SSRC: %x", m_szName, dwSSRC));
  779. CLock lock(m_lock);
  780. // look at the pending SSRC list and find out if it is in the pending list.
  781. int i = m_PendingSSRCs.Find(dwSSRC);
  782. // if the SSRC is in the pending list, remove it.
  783. if (i >= 0)
  784. {
  785. m_PendingSSRCs.RemoveAt(i);
  786. }
  787. CParticipant *pParticipant;
  788. BOOL fLast = FALSE;
  789. HRESULT hr = E_FAIL;
  790. // first try to find the SSRC in our participant list.
  791. for (i = 0; i < m_Participants.GetSize(); i ++)
  792. {
  793. pParticipant = (CParticipant *)m_Participants[i];
  794. hr = pParticipant->RemoveStream(
  795. (ITStream *)this,
  796. dwSSRC,
  797. &fLast
  798. );
  799. if (SUCCEEDED(hr))
  800. {
  801. break;
  802. }
  803. }
  804. // if the participant is not found
  805. if (FAILED(hr))
  806. {
  807. LOG((MSP_WARN, "%ws, can't find the SSRC %x", m_szName, dwSSRC));
  808. return hr;
  809. }
  810. ITParticipant *pITParticipant = m_Participants[i];
  811. // fire an event to tell the app that the participant is in active.
  812. ((CIPConfMSPCall *)m_pMSPCall)->SendParticipantEvent(
  813. PE_PARTICIPANT_INACTIVE,
  814. pITParticipant
  815. );
  816. m_Participants.RemoveAt(i);
  817. // if this stream is the last stream that the participant is on,
  818. // tell the call object to remove it from its list.
  819. if (fLast)
  820. {
  821. ((CIPConfMSPCall *)m_pMSPCall)->ParticipantLeft(pITParticipant);
  822. }
  823. pITParticipant->Release();
  824. return S_OK;
  825. }
  826. HRESULT CStreamAudioRecv::ShutDown()
  827. /*++
  828. Routine Description:
  829. Shut down the stream. Release our members and then calls the base class's
  830. ShutDown method.
  831. Arguments:
  832. Return Value:
  833. S_OK
  834. --*/
  835. {
  836. CLock lock(m_lock);
  837. // if there are terminals
  838. BOOL fHasTerminal = FALSE;
  839. if (m_Terminals.GetSize() > 0)
  840. {
  841. fHasTerminal = TRUE;
  842. }
  843. // if graph is running
  844. HRESULT hr;
  845. OAFilterState FilterState = State_Stopped;
  846. if (m_pIMediaControl)
  847. {
  848. if (FAILED (hr = m_pIMediaControl->GetState(0, &FilterState)))
  849. {
  850. LOG ((MSP_ERROR, "CStreamAudioRecv::ShutDown failed to query filter state. %d", hr));
  851. FilterState = State_Stopped;
  852. }
  853. }
  854. // fire event
  855. if (fHasTerminal && FilterState == State_Running)
  856. {
  857. SendStreamEvent(CALL_STREAM_INACTIVE, CALL_CAUSE_LOCAL_REQUEST, 0, NULL);
  858. }
  859. return CIPConfMSPStream::ShutDown();
  860. }
  861. /////////////////////////////////////////////////////////////////////////////
  862. //
  863. // CStreamAudioSend
  864. //
  865. /////////////////////////////////////////////////////////////////////////////
  866. CStreamAudioSend::CStreamAudioSend()
  867. : CIPConfMSPStream(),
  868. m_pIStreamConfig(NULL),
  869. m_pAudioInputMixer(NULL),
  870. m_pSilenceControl(NULL),
  871. m_pAudioDeviceControl(NULL),
  872. m_pCaptureBitrateControl(NULL),
  873. m_pEncoder(NULL),
  874. m_pIAudioDuplexController(NULL),
  875. m_lAutomaticGainControl(DEFUAT_AGC_STATUS),
  876. m_lAcousticEchoCancellation(DEFUAT_AEC_STATUS)
  877. {
  878. m_szName = L"AudioSend";
  879. }
  880. CStreamAudioSend::~CStreamAudioSend()
  881. {
  882. CleanupCachedInterface();
  883. }
  884. // this method is called by the call object at init time.
  885. void CStreamAudioSend::SetFullDuplexController(
  886. IN IAudioDuplexController *pIAudioDuplexController
  887. )
  888. {
  889. _ASSERT(pIAudioDuplexController);
  890. _ASSERT(m_pIAudioDuplexController == NULL);
  891. pIAudioDuplexController->AddRef();
  892. m_pIAudioDuplexController = pIAudioDuplexController;
  893. }
  894. void CStreamAudioSend::CleanupCachedInterface()
  895. {
  896. if (m_pIStreamConfig)
  897. {
  898. m_pIStreamConfig->Release();
  899. m_pIStreamConfig = NULL;
  900. }
  901. if (m_pSilenceControl)
  902. {
  903. m_pSilenceControl->Release();
  904. m_pSilenceControl = NULL;
  905. }
  906. if (m_pCaptureBitrateControl)
  907. {
  908. m_pCaptureBitrateControl->Release();
  909. m_pCaptureBitrateControl = NULL;
  910. }
  911. if (m_pAudioInputMixer)
  912. {
  913. m_pAudioInputMixer->Release();
  914. m_pAudioInputMixer = NULL;
  915. }
  916. if (m_pAudioDeviceControl)
  917. {
  918. m_pAudioDeviceControl->Release();
  919. m_pAudioDeviceControl = NULL;
  920. }
  921. if (m_pEncoder)
  922. {
  923. m_pEncoder->Release();
  924. m_pEncoder = NULL;
  925. }
  926. if (m_pIAudioDuplexController)
  927. {
  928. m_pIAudioDuplexController->Release();
  929. m_pIAudioDuplexController = NULL;
  930. }
  931. }
  932. HRESULT CStreamAudioSend::ShutDown()
  933. /*++
  934. Routine Description:
  935. Shut down the stream. Release our members and then calls the base class's
  936. ShutDown method.
  937. Arguments:
  938. Return Value:
  939. S_OK
  940. --*/
  941. {
  942. CLock lock(m_lock);
  943. // if there are terminals
  944. BOOL fHasTerminal = FALSE;
  945. if (m_Terminals.GetSize() > 0)
  946. {
  947. fHasTerminal = TRUE;
  948. }
  949. // if graph is running
  950. HRESULT hr;
  951. OAFilterState FilterState = State_Stopped;
  952. if (m_pIMediaControl)
  953. {
  954. if (FAILED (hr = m_pIMediaControl->GetState(0, &FilterState)))
  955. {
  956. LOG ((MSP_ERROR, "CStreamAudioSend::ShutDown failed to query filter state. %d", hr));
  957. FilterState = State_Stopped;
  958. }
  959. }
  960. CleanupCachedInterface();
  961. // fire event
  962. if (fHasTerminal && FilterState == State_Running)
  963. {
  964. SendStreamEvent(CALL_STREAM_INACTIVE, CALL_CAUSE_LOCAL_REQUEST, 0, NULL);
  965. }
  966. return CIPConfMSPStream::ShutDown();
  967. }
  968. HRESULT CStreamAudioSend::CacheAdditionalInterfaces(
  969. IN IPin * pIPin
  970. )
  971. {
  972. ENTER_FUNCTION("CStreamAudioSend::CacheAdditionalInterfaces");
  973. LOG((MSP_TRACE, "%s enters", __fxName));
  974. HRESULT hr;
  975. _ASSERT(m_pIStreamConfig == NULL);
  976. hr = pIPin->QueryInterface(&m_pIStreamConfig);
  977. if (FAILED(hr))
  978. {
  979. LOG((MSP_WARN, "%s, query IStreamConfig failed", __fxName));
  980. // this is a required interface.
  981. return hr;
  982. }
  983. // get the SilenceControl interface from the pin.
  984. _ASSERT(m_pSilenceControl == NULL);
  985. hr = pIPin->QueryInterface(&m_pSilenceControl);
  986. if (FAILED(hr))
  987. {
  988. LOG((MSP_WARN,
  989. "%s:query capture pin's ISilenceControl failed, hr=%x",
  990. __fxName, hr));
  991. // this is a required interface.
  992. return hr;
  993. }
  994. // get the BitrateControl interface.
  995. _ASSERT(m_pCaptureBitrateControl == NULL);
  996. hr = pIPin->QueryInterface(&m_pCaptureBitrateControl);
  997. if (FAILED(hr))
  998. {
  999. LOG((MSP_WARN,
  1000. "%s:query capture pin's BitrateControl failed, hr=%x",
  1001. __fxName, hr));
  1002. }
  1003. // find the filter behind the pin.
  1004. PIN_INFO PinInfo;
  1005. if (SUCCEEDED(hr = pIPin->QueryPinInfo(&PinInfo)))
  1006. {
  1007. // get the AudioInputMixer interface.
  1008. _ASSERT(m_pAudioInputMixer == NULL);
  1009. hr = PinInfo.pFilter->QueryInterface(&m_pAudioInputMixer);
  1010. if (FAILED(hr))
  1011. {
  1012. LOG((MSP_WARN,
  1013. "%s:query capture filter's IAMAudioInputMixer failed, hr=%x",
  1014. __fxName, hr));
  1015. }
  1016. // get the AudioDeviceControl interface.
  1017. _ASSERT(m_pAudioDeviceControl == NULL);
  1018. hr = PinInfo.pFilter->QueryInterface(&m_pAudioDeviceControl);
  1019. PinInfo.pFilter->Release();
  1020. if (FAILED(hr))
  1021. {
  1022. LOG((MSP_WARN,
  1023. "%s:query capture filter's AudioDeviceControl failed, hr=%x",
  1024. __fxName, hr));
  1025. }
  1026. else
  1027. {
  1028. hr = m_pAudioDeviceControl->Set(
  1029. AudioDevice_AutomaticGainControl,
  1030. m_lAutomaticGainControl,
  1031. TAPIControl_Flags_None
  1032. );
  1033. if (FAILED(hr))
  1034. {
  1035. LOG((MSP_WARN,
  1036. "%s:set AGC failed, hr=%x",
  1037. __fxName, hr));
  1038. }
  1039. hr = m_pAudioDeviceControl->Set(
  1040. AudioDevice_AcousticEchoCancellation,
  1041. m_lAcousticEchoCancellation,
  1042. TAPIControl_Flags_None
  1043. );
  1044. if (FAILED(hr))
  1045. {
  1046. LOG((MSP_WARN,
  1047. "%s:set AEC failed, hr=%x",
  1048. __fxName, hr));
  1049. }
  1050. }
  1051. }
  1052. else
  1053. {
  1054. LOG((MSP_ERROR,
  1055. "%s:can't get the capture filter, hr=%x",
  1056. __fxName, hr));
  1057. }
  1058. return S_OK;
  1059. }
  1060. HRESULT CStreamAudioSend::GetAudioCapturePin(
  1061. IN ITTerminalControl * pTerminal,
  1062. OUT IPin ** ppIPin
  1063. )
  1064. /*++
  1065. Routine Description:
  1066. This function gets a output pin from the capture terminal.
  1067. Arguments:
  1068. pTerminal - An audio capture terminal.
  1069. ppIPin - the address to hold the returned pointer to IPin interface.
  1070. Return Value:
  1071. HRESULT
  1072. --*/
  1073. {
  1074. LOG((MSP_TRACE, "AudioSend configure audio capture terminal."));
  1075. DWORD dwNumPins = 1;
  1076. IPin * Pins[1];
  1077. // Get the pins from the terminal
  1078. HRESULT hr = pTerminal->ConnectTerminal(
  1079. m_pIGraphBuilder, TD_CAPTURE, &dwNumPins, Pins
  1080. );
  1081. if (FAILED(hr))
  1082. {
  1083. LOG((MSP_ERROR, "can't connect to terminal, %x", hr));
  1084. return hr;
  1085. }
  1086. // This stream needs only one pin from the terminal.
  1087. _ASSERT(dwNumPins == 1);
  1088. if (IsBadReadPtr (Pins, dwNumPins * sizeof (IPin*)))
  1089. {
  1090. LOG((MSP_ERROR, "terminal returned bad pin array"));
  1091. return E_POINTER;
  1092. }
  1093. for (DWORD i = 0; i < dwNumPins; i++)
  1094. {
  1095. if (IsBadReadPtr (Pins[i], sizeof (IPin)))
  1096. {
  1097. LOG((MSP_ERROR, "terminal returned bad pin. # %d", i));
  1098. return E_POINTER;
  1099. }
  1100. }
  1101. // this pin carries a refcount
  1102. *ppIPin = Pins[0];
  1103. return hr;
  1104. }
  1105. HRESULT CStreamAudioSend::ConnectTerminal(
  1106. IN ITTerminal * pITTerminal
  1107. )
  1108. /*++
  1109. Routine Description:
  1110. connect the audio capture terminal to the stream.
  1111. Arguments:
  1112. pITTerminal - The terminal to be connected.
  1113. Return Value:
  1114. HRESULT.
  1115. --*/
  1116. {
  1117. ENTER_FUNCTION("AudioSend::ConnectTerminal");
  1118. LOG((MSP_TRACE, "%s enters, pITTerminal:%p", __fxName, pITTerminal));
  1119. CComQIPtr<ITTerminalControl, &__uuidof(ITTerminalControl)>
  1120. pTerminal(pITTerminal);
  1121. if (pTerminal == NULL)
  1122. {
  1123. LOG((MSP_ERROR, "%s, can't get Terminal Control interface", __fxName));
  1124. SendStreamEvent(
  1125. CALL_TERMINAL_FAIL,
  1126. CALL_CAUSE_BAD_DEVICE,
  1127. E_NOINTERFACE,
  1128. pITTerminal
  1129. );
  1130. return E_NOINTERFACE;
  1131. }
  1132. // find the output pin of the terminal.
  1133. CComPtr<IPin> pCaptureOutputPin;
  1134. HRESULT hr = GetAudioCapturePin(pTerminal, &pCaptureOutputPin);
  1135. if (FAILED(hr))
  1136. {
  1137. LOG((MSP_ERROR, "%s, configure audio capture termianl failed. %x", __fxName, hr));
  1138. SendStreamEvent(
  1139. CALL_TERMINAL_FAIL,
  1140. CALL_CAUSE_BAD_DEVICE,
  1141. hr,
  1142. pITTerminal
  1143. );
  1144. return hr;
  1145. }
  1146. CComPtr<IPin> PinToUse;
  1147. hr = CacheAdditionalInterfaces(pCaptureOutputPin);
  1148. if (SUCCEEDED(hr))
  1149. {
  1150. PinToUse = pCaptureOutputPin;
  1151. // give the filter the full-duplex controller.
  1152. ::ConfigureFullduplexControl(pCaptureOutputPin, m_pIAudioDuplexController);
  1153. }
  1154. else if (hr == E_NOINTERFACE)
  1155. {
  1156. // the capture filter doesn't support the needed interfaces.
  1157. // we need to add our encoder here.
  1158. if (FAILED(hr = ::AddFilter(
  1159. m_pIGraphBuilder,
  1160. __uuidof(TAPIAudioEncoder),
  1161. L"AudioEncoder",
  1162. &m_pEncoder)))
  1163. {
  1164. LOG((MSP_ERROR, "%s, adding Encoder filter. %x", __fxName, hr));
  1165. goto cleanup;
  1166. }
  1167. // This is a hack for legacy terminals. We have to tell the terminal what
  1168. // format to use
  1169. const WORD wBitsPerSample = 16; // 16 bits samples.
  1170. const DWORD dwSampleRate = 8000; // 8KHz.
  1171. hr = ::SetAudioFormat(
  1172. pCaptureOutputPin,
  1173. wBitsPerSample,
  1174. dwSampleRate
  1175. );
  1176. if (FAILED(hr))
  1177. {
  1178. LOG((MSP_WARN, "%s, can't set format. %x", __fxName, hr));
  1179. }
  1180. // This is a hack for legacy terminals. We have to tell the terminal what
  1181. // buffer size to allocate.
  1182. const DWORD dwNumBuffers = 4; // 4 buffers in the allocator.
  1183. const DWORD dwBufferSize = 480; // 30ms samples in each buffer.
  1184. hr = ::SetAudioBufferSize(pCaptureOutputPin, dwNumBuffers, dwBufferSize);
  1185. if (FAILED(hr))
  1186. {
  1187. LOG((MSP_WARN,
  1188. "%s, can't suggest allocator properties. %x", __fxName, hr));
  1189. }
  1190. if (FAILED(hr = ::ConnectFilters(
  1191. m_pIGraphBuilder,
  1192. (IPin *)pCaptureOutputPin,
  1193. (IBaseFilter *)m_pEncoder
  1194. )))
  1195. {
  1196. LOG((MSP_ERROR,
  1197. "%s, connect audio capture filter and encoder filter. %x",
  1198. __fxName, hr));
  1199. goto cleanup;
  1200. }
  1201. CComPtr<IPin> pEncoderOutputPin;
  1202. if (FAILED(hr = ::FindPin(
  1203. m_pEncoder, &pEncoderOutputPin, PINDIR_OUTPUT, TRUE)))
  1204. {
  1205. LOG((MSP_ERROR,
  1206. "%s, find input pin on pCodecFilter failed. hr=%x",
  1207. __fxName, hr));
  1208. goto cleanup;
  1209. }
  1210. PinToUse = pEncoderOutputPin;
  1211. hr = CacheAdditionalInterfaces(pEncoderOutputPin);
  1212. _ASSERT(SUCCEEDED(hr));
  1213. }
  1214. else
  1215. {
  1216. LOG((MSP_ERROR, "%s, can't add codec to table. %x", __fxName, hr));
  1217. goto cleanup;
  1218. }
  1219. // Create other filters to be use in the stream.
  1220. hr = CreateSendFilters(PinToUse);
  1221. if (FAILED(hr))
  1222. {
  1223. LOG((MSP_ERROR, "Create audio send filters failed. %x", hr));
  1224. goto cleanup;
  1225. }
  1226. //
  1227. // Now we are actually connected. Update our state and perform postconnection
  1228. // (ignore postconnection error code).
  1229. //
  1230. pTerminal->CompleteConnectTerminal();
  1231. return hr;
  1232. cleanup:
  1233. CleanupCachedInterface();
  1234. // clean up internal filters as well.
  1235. CleanUpFilters();
  1236. SendStreamEvent(CALL_TERMINAL_FAIL, CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
  1237. pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
  1238. if (m_pEncoder)
  1239. {
  1240. m_pIGraphBuilder->RemoveFilter(m_pEncoder);
  1241. m_pEncoder->Release();
  1242. m_pEncoder = NULL;
  1243. }
  1244. return hr;
  1245. }
  1246. HRESULT CStreamAudioSend::DisconnectTerminal(
  1247. IN ITTerminal * pITTerminal
  1248. )
  1249. /*++
  1250. Routine Description:
  1251. Disconnect a terminal. It will remove its filters from the graph and
  1252. also release its references to the graph.
  1253. If it is the capture terminal being disconnected, all the pins that the
  1254. stream cached need to be released too.
  1255. Arguments:
  1256. pITTerminal - the terminal.
  1257. Return Value:
  1258. HRESULT.
  1259. --*/
  1260. {
  1261. ENTER_FUNCTION("CStreamAudioSend::DisconnectTerminal");
  1262. LOG((MSP_TRACE, "%s enters, pITTerminal:%p", __fxName, pITTerminal));
  1263. HRESULT hr = CIPConfMSPStream::DisconnectTerminal(pITTerminal);
  1264. // release all the capture pins we cached.
  1265. CleanupCachedInterface();
  1266. CleanUpFilters();
  1267. if (m_pEncoder)
  1268. {
  1269. m_pIGraphBuilder->RemoveFilter(m_pEncoder);
  1270. m_pEncoder->Release();
  1271. m_pEncoder = NULL;
  1272. }
  1273. return hr;
  1274. }
  1275. HRESULT CStreamAudioSend::SetUpFilters()
  1276. /*++
  1277. Routine Description:
  1278. Insert filters into the graph and connect to the terminals.
  1279. Arguments:
  1280. Return Value:
  1281. HRESULT.
  1282. --*/
  1283. {
  1284. LOG((MSP_TRACE, "AudioSend SetUpFilters"));
  1285. // only support one terminal for this stream.
  1286. if (m_Terminals.GetSize() != 1)
  1287. {
  1288. return E_UNEXPECTED;
  1289. }
  1290. HRESULT hr;
  1291. // Connect the terminal to the rest of the stream.
  1292. if (FAILED(hr = ConnectTerminal(
  1293. m_Terminals[0]
  1294. )))
  1295. {
  1296. LOG((MSP_ERROR, "connect the terminal to the filters. %x", hr));
  1297. return hr;
  1298. }
  1299. return hr;
  1300. }
  1301. HRESULT ConfigurePacketSize(
  1302. IN const AM_MEDIA_TYPE *pMediaType,
  1303. IN DWORD dwMSPerPacket
  1304. )
  1305. {
  1306. WAVEFORMATEX *pWaveFormatEx = (WAVEFORMATEX *) pMediaType->pbFormat;
  1307. ASSERT(pWaveFormatEx != NULL);
  1308. switch (pWaveFormatEx->wFormatTag)
  1309. {
  1310. case WAVE_FORMAT_ALAW:
  1311. case WAVE_FORMAT_MULAW:
  1312. _ASSERT(pMediaType->cbFormat >= sizeof(WAVEFORMATEX_RTPG711));
  1313. ((WAVEFORMATEX_RTPG711 *)pWaveFormatEx)->wPacketDuration = (WORD)dwMSPerPacket;
  1314. break;
  1315. case WAVE_FORMAT_DVI_ADPCM:
  1316. _ASSERT(pMediaType->cbFormat >= sizeof(WAVEFORMATEX_RTPDVI4));
  1317. ((WAVEFORMATEX_RTPDVI4 *)pWaveFormatEx)->wPacketDuration = (WORD)dwMSPerPacket;
  1318. break;
  1319. case WAVE_FORMAT_GSM610:
  1320. _ASSERT(pMediaType->cbFormat >= sizeof(WAVEFORMATEX_RTPGSM));
  1321. ((WAVEFORMATEX_RTPGSM *)pWaveFormatEx)->wPacketDuration = (WORD)dwMSPerPacket;
  1322. break;
  1323. }
  1324. return S_OK;
  1325. }
  1326. HRESULT CStreamAudioSend::ConfigureRTPFormats(
  1327. IN IBaseFilter * pIRTPFilter,
  1328. IN IStreamConfig * pIStreamConfig
  1329. )
  1330. /*++
  1331. Routine Description:
  1332. Configure the RTP filter with RTP<-->AM media type mappings.
  1333. Arguments:
  1334. pIRTPFilter - The source RTP Filter.
  1335. pIStreamConfig - The stream config interface that has the media info.
  1336. Return Value:
  1337. HRESULT.
  1338. --*/
  1339. {
  1340. ENTER_FUNCTION("AudioSend::ConfigureRTPFormats");
  1341. LOG((MSP_TRACE, "%s enters", __fxName));
  1342. HRESULT hr;
  1343. CComPtr<IRtpMediaControl> pIRtpMediaControl;
  1344. hr = pIRTPFilter->QueryInterface(&pIRtpMediaControl);
  1345. if (FAILED(hr))
  1346. {
  1347. LOG((MSP_ERROR, "%s adding source filter. %x", __fxName, hr));
  1348. return hr;
  1349. }
  1350. // find the number of capabilities supported.
  1351. DWORD dwCount;
  1352. hr = pIStreamConfig->GetNumberOfCapabilities(&dwCount);
  1353. if (FAILED(hr))
  1354. {
  1355. LOG((MSP_ERROR, "%s GetNumberOfCapabilities. %x", __fxName, hr));
  1356. return hr;
  1357. }
  1358. BOOL bFound = FALSE;
  1359. for (DWORD dw = 0; dw < dwCount; dw ++)
  1360. {
  1361. AM_MEDIA_TYPE *pMediaType;
  1362. DWORD dwPayloadType;
  1363. hr = pIStreamConfig->GetStreamCaps(
  1364. dw, &pMediaType, NULL, &dwPayloadType
  1365. );
  1366. if (FAILED(hr))
  1367. {
  1368. LOG((MSP_ERROR, "%s GetStreamCaps. %x", __fxName, hr));
  1369. return hr;
  1370. }
  1371. for (DWORD dw2 = 0; dw2 < m_Settings.dwNumPayloadTypes; dw2 ++)
  1372. {
  1373. if (dwPayloadType == m_Settings.PayloadTypes[dw2])
  1374. {
  1375. if (dw2 == 0)
  1376. {
  1377. // tell the encoder to use this format.
  1378. // TODO, cache all the allowed mediatypes in the conference for
  1379. // future enumerations. It would be nice that we can get the SDP blob
  1380. // when the call object is created.
  1381. if (m_Settings.dwMSPerPacket)
  1382. {
  1383. hr = ConfigurePacketSize(pMediaType, m_Settings.dwMSPerPacket);
  1384. if (FAILED(hr))
  1385. {
  1386. MSPDeleteMediaType(pMediaType);
  1387. LOG((MSP_ERROR, "%s ConfigurePacketSize. hr=%x", __fxName, hr));
  1388. return hr;
  1389. }
  1390. }
  1391. }
  1392. hr = pIRtpMediaControl->SetFormatMapping(
  1393. dwPayloadType,
  1394. FindSampleRate(pMediaType),
  1395. pMediaType
  1396. );
  1397. if (FAILED(hr))
  1398. {
  1399. MSPDeleteMediaType(pMediaType);
  1400. LOG((MSP_ERROR, "%s SetFormatMapping. %x", __fxName, hr));
  1401. return hr;
  1402. }
  1403. else
  1404. {
  1405. LOG((MSP_INFO, "%s Configured payload:%d", __fxName, dwPayloadType));
  1406. }
  1407. if (dw2 == 0)
  1408. {
  1409. hr = pIStreamConfig->SetFormat(dwPayloadType, pMediaType);
  1410. if (FAILED(hr))
  1411. {
  1412. MSPDeleteMediaType(pMediaType);
  1413. LOG((MSP_ERROR, "%s SetFormat. %x", __fxName, hr));
  1414. return hr;
  1415. }
  1416. }
  1417. }
  1418. }
  1419. MSPDeleteMediaType(pMediaType);
  1420. }
  1421. return S_OK;
  1422. }
  1423. HRESULT CStreamAudioSend::CreateSendFilters(
  1424. IN IPin *pPin
  1425. )
  1426. /*++
  1427. Routine Description:
  1428. Insert filters into the graph and connect to the capture pin.
  1429. Capturepin->RTPRender
  1430. Arguments:
  1431. pPin - The output pin on the capture filter.
  1432. Return Value:
  1433. HRESULT.
  1434. --*/
  1435. {
  1436. ENTER_FUNCTION("CStreamAudioSend::CreateSendFilters");
  1437. LOG((MSP_TRACE, "%s enters", __fxName));
  1438. HRESULT hr;
  1439. // Create the RTP render filter and add it into the graph.
  1440. CComPtr<IBaseFilter> pRenderFilter;
  1441. if (m_pIRTPSession == NULL)
  1442. {
  1443. if (FAILED(hr = ::AddFilter(
  1444. m_pIGraphBuilder,
  1445. __uuidof(MSRTPRenderFilter),
  1446. L"RtpRender",
  1447. &pRenderFilter)))
  1448. {
  1449. LOG((MSP_ERROR, "%s, adding render filter. %x", __fxName, hr));
  1450. return hr;
  1451. }
  1452. // Set the address for the render fitler.
  1453. if (FAILED(hr = ConfigureRTPFilter(pRenderFilter)))
  1454. {
  1455. LOG((MSP_ERROR, "%s, set destination address. %x", __fxName, hr));
  1456. return hr;
  1457. }
  1458. }
  1459. else
  1460. {
  1461. if (FAILED (hr = m_pIRTPSession->QueryInterface (&pRenderFilter)))
  1462. {
  1463. LOG ((MSP_ERROR, "%s failed to get filter from rtp session. %x", __fxName, hr));
  1464. return hr;
  1465. }
  1466. if (FAILED (hr = m_pIGraphBuilder->AddFilter ((IBaseFilter *)pRenderFilter, L"RtpRender")))
  1467. {
  1468. LOG ((MSP_ERROR, "%s failed to add filter to graph. %x", __fxName, hr));
  1469. return hr;
  1470. }
  1471. }
  1472. _ASSERT(m_pIStreamConfig != NULL);
  1473. // configure the format info on the RTP filter
  1474. if (FAILED(hr = ConfigureRTPFormats(pRenderFilter, m_pIStreamConfig)))
  1475. {
  1476. LOG((MSP_ERROR, "%s, configure RTP formats. %x", __fxName, hr));
  1477. return hr;
  1478. }
  1479. // Connect the capture filter with the RTP Render filter.
  1480. if (FAILED(hr = ::ConnectFilters(
  1481. m_pIGraphBuilder,
  1482. (IPin *)pPin,
  1483. (IBaseFilter *)pRenderFilter
  1484. )))
  1485. {
  1486. LOG((MSP_ERROR,
  1487. "%s, connect audio capture filter and RTP Render filter. %x",
  1488. __fxName, hr));
  1489. return hr;
  1490. }
  1491. return S_OK;
  1492. }
  1493. HRESULT CStreamAudioSend::ProcessGraphEvent(
  1494. IN long lEventCode,
  1495. IN LONG_PTR lParam1,
  1496. IN LONG_PTR lParam2
  1497. )
  1498. {
  1499. LOG((MSP_TRACE, "%ws ProcessGraphEvent %d", m_szName, lEventCode));
  1500. switch (lEventCode)
  1501. {
  1502. case VAD_EVENTBASE + VAD_SILENCE:
  1503. m_lock.Lock ();
  1504. ((CIPConfMSPCall *)m_pMSPCall)->SendParticipantEvent (PE_LOCAL_SILENT, NULL);
  1505. m_lock.Unlock ();
  1506. break;
  1507. case VAD_EVENTBASE + VAD_TALKING:
  1508. m_lock.Lock ();
  1509. ((CIPConfMSPCall *)m_pMSPCall)->SendParticipantEvent (PE_LOCAL_TALKING, NULL);
  1510. m_lock.Unlock ();
  1511. break;
  1512. default:
  1513. return CIPConfMSPStream::ProcessGraphEvent(
  1514. lEventCode, lParam1, lParam2
  1515. );
  1516. }
  1517. return S_OK;
  1518. }
  1519. STDMETHODIMP CStreamAudioSend::GetRange(
  1520. IN AudioDeviceProperty Property,
  1521. OUT long *plMin,
  1522. OUT long *plMax,
  1523. OUT long *plSteppingDelta,
  1524. OUT long *plDefault,
  1525. OUT TAPIControlFlags *plFlags
  1526. )
  1527. /*++
  1528. Routine Description:
  1529. Get the range for a audio setting property. Delegated to the capture filter.
  1530. Arguments:
  1531. Return Value:
  1532. HRESULT.
  1533. --*/
  1534. {
  1535. ENTER_FUNCTION("CStreamAudioSend::GetRange(AudioDeviceProperty)");
  1536. if (IsBadWritePtr(plMin, sizeof(long)) ||
  1537. IsBadWritePtr(plMax, sizeof(long)) ||
  1538. IsBadWritePtr(plSteppingDelta, sizeof(long)) ||
  1539. IsBadWritePtr(plDefault, sizeof(long)) ||
  1540. IsBadWritePtr(plFlags, sizeof(long)))
  1541. {
  1542. LOG((MSP_ERROR, "%s, bad pointer", __fxName));
  1543. return E_POINTER;
  1544. }
  1545. HRESULT hr = E_NOTIMPL;
  1546. switch (Property)
  1547. {
  1548. case AudioDevice_DuplexMode:
  1549. break;
  1550. case AudioDevice_AutomaticGainControl:
  1551. *plMin = 0;
  1552. *plMax = 1;
  1553. *plSteppingDelta = 1;
  1554. *plDefault = DEFUAT_AGC_STATUS;
  1555. *plFlags = TAPIControl_Flags_Auto;
  1556. hr = S_OK;
  1557. break;
  1558. case AudioDevice_AcousticEchoCancellation:
  1559. *plMin = 0;
  1560. *plMax = 1;
  1561. *plSteppingDelta = 1;
  1562. *plDefault = DEFUAT_AEC_STATUS;
  1563. *plFlags = TAPIControl_Flags_Auto;
  1564. hr = S_OK;
  1565. break;
  1566. default:
  1567. hr = E_INVALIDARG;
  1568. }
  1569. return hr;
  1570. }
  1571. STDMETHODIMP CStreamAudioSend::Get(
  1572. IN AudioDeviceProperty Property,
  1573. OUT long *plValue,
  1574. OUT TAPIControlFlags *plFlags
  1575. )
  1576. /*++
  1577. Routine Description:
  1578. Get the value for a audio setting property. Delegated to the capture filter.
  1579. Arguments:
  1580. Return Value:
  1581. HRESULT.
  1582. --*/
  1583. {
  1584. ENTER_FUNCTION("CStreamAudioSend::Get(AudioDeviceProperty)");
  1585. if (IsBadWritePtr(plValue, sizeof(long)) ||
  1586. IsBadWritePtr(plFlags, sizeof(long)))
  1587. {
  1588. LOG((MSP_ERROR, "%s, bad pointer", __fxName));
  1589. return E_POINTER;
  1590. }
  1591. CLock lock(m_lock);
  1592. HRESULT hr = E_NOTIMPL;
  1593. switch (Property)
  1594. {
  1595. case AudioDevice_DuplexMode:
  1596. break;
  1597. case AudioDevice_AutomaticGainControl:
  1598. *plValue = m_lAutomaticGainControl;
  1599. *plFlags = TAPIControl_Flags_Auto;
  1600. hr = S_OK;
  1601. break;
  1602. case AudioDevice_AcousticEchoCancellation:
  1603. *plValue = m_lAcousticEchoCancellation;
  1604. *plFlags = TAPIControl_Flags_Auto;
  1605. hr = S_OK;
  1606. break;
  1607. default:
  1608. hr = E_INVALIDARG;
  1609. }
  1610. return hr;
  1611. }
  1612. STDMETHODIMP CStreamAudioSend::Set(
  1613. IN AudioDeviceProperty Property,
  1614. IN long lValue,
  1615. IN TAPIControlFlags lFlags
  1616. )
  1617. /*++
  1618. Routine Description:
  1619. Set the value for a audio setting property. Delegated to the capture filter.
  1620. Arguments:
  1621. Return Value:
  1622. HRESULT.
  1623. --*/
  1624. {
  1625. ENTER_FUNCTION("CStreamAudioSend::Set(AudioDeviceProperty)");
  1626. CLock lock(m_lock);
  1627. HRESULT hr;
  1628. switch (Property)
  1629. {
  1630. case AudioDevice_DuplexMode:
  1631. return E_NOTIMPL;
  1632. case AudioDevice_AutomaticGainControl:
  1633. if (lValue !=0 && lValue != 1)
  1634. {
  1635. return E_INVALIDARG;
  1636. }
  1637. // check if we have the interface to delegate to.
  1638. if (m_pAudioDeviceControl)
  1639. {
  1640. // set the value on the filter.
  1641. hr = m_pAudioDeviceControl->Set(Property, lValue, lFlags);
  1642. if (FAILED(hr))
  1643. {
  1644. return hr;
  1645. }
  1646. }
  1647. m_lAutomaticGainControl = lValue;
  1648. return S_OK;
  1649. case AudioDevice_AcousticEchoCancellation:
  1650. if (lValue !=0 && lValue != 1)
  1651. {
  1652. return E_INVALIDARG;
  1653. }
  1654. // check if we have the interface to delegate to.
  1655. if (m_pAudioDeviceControl)
  1656. {
  1657. // set the value on the filter.
  1658. hr = m_pAudioDeviceControl->Set(Property, lValue, lFlags);
  1659. if (FAILED(hr))
  1660. {
  1661. return hr;
  1662. }
  1663. }
  1664. m_lAcousticEchoCancellation = lValue;
  1665. return S_OK;
  1666. }
  1667. return E_INVALIDARG;
  1668. }
  1669. STDMETHODIMP CStreamAudioSend::GetRange(
  1670. IN AudioSettingsProperty Property,
  1671. OUT long *plMin,
  1672. OUT long *plMax,
  1673. OUT long *plSteppingDelta,
  1674. OUT long *plDefault,
  1675. OUT TAPIControlFlags *plFlags
  1676. )
  1677. /*++
  1678. Routine Description:
  1679. Get the range for a audio setting property. Delegated to the capture filter.
  1680. Arguments:
  1681. Return Value:
  1682. HRESULT.
  1683. --*/
  1684. {
  1685. ENTER_FUNCTION("CStreamAudioSend::GetRange(AudioSettings)");
  1686. if (IsBadWritePtr(plMin, sizeof(long)) ||
  1687. IsBadWritePtr(plMax, sizeof(long)) ||
  1688. IsBadWritePtr(plSteppingDelta, sizeof(long)) ||
  1689. IsBadWritePtr(plDefault, sizeof(long)) ||
  1690. IsBadWritePtr(plFlags, sizeof(long)))
  1691. {
  1692. LOG((MSP_ERROR, "%s, bad pointer", __fxName));
  1693. return E_POINTER;
  1694. }
  1695. CLock lock(m_lock);
  1696. HRESULT hr = E_NOINTERFACE;
  1697. switch (Property)
  1698. {
  1699. case AudioSettings_SignalLevel:
  1700. // check if we have the interface to delegate to.
  1701. if (m_pSilenceControl)
  1702. {
  1703. // get the range from the filter.
  1704. hr = m_pSilenceControl->GetAudioLevelRange(plMin, plMax, plSteppingDelta);
  1705. if (SUCCEEDED(hr))
  1706. {
  1707. *plDefault = *plMin;
  1708. *plFlags = TAPIControl_Flags_None;
  1709. }
  1710. }
  1711. break;
  1712. case AudioSettings_SilenceThreshold:
  1713. // check if we have the interface to delegate to.
  1714. if (m_pSilenceControl)
  1715. {
  1716. // get the range from the filter.
  1717. hr = m_pSilenceControl->GetSilenceLevelRange(
  1718. plMin,
  1719. plMax,
  1720. plSteppingDelta,
  1721. plDefault,
  1722. plFlags
  1723. );
  1724. }
  1725. break;
  1726. case AudioSettings_Volume:
  1727. *plMin = MIN_VOLUME;
  1728. *plMax = MAX_VOLUME;
  1729. *plSteppingDelta = 1;
  1730. *plDefault = *plMin;
  1731. *plFlags = TAPIControl_Flags_Manual;
  1732. hr = S_OK;
  1733. break;
  1734. case AudioSettings_Balance:
  1735. hr = E_NOTIMPL;
  1736. break;
  1737. case AudioSettings_Loudness:
  1738. hr = E_NOTIMPL;
  1739. break;
  1740. case AudioSettings_Treble:
  1741. hr = E_NOTIMPL;
  1742. break;
  1743. case AudioSettings_Bass:
  1744. hr = E_NOTIMPL;
  1745. break;
  1746. case AudioSettings_Mono:
  1747. *plMin = 1;
  1748. *plMax = 1;
  1749. *plSteppingDelta = 1;
  1750. *plDefault = 1;
  1751. *plFlags = TAPIControl_Flags_Manual;
  1752. hr = S_OK;
  1753. break;
  1754. default:
  1755. hr = E_INVALIDARG;
  1756. }
  1757. return hr;
  1758. }
  1759. STDMETHODIMP CStreamAudioSend::Get(
  1760. IN AudioSettingsProperty Property,
  1761. OUT long *plValue,
  1762. OUT TAPIControlFlags *plFlags
  1763. )
  1764. /*++
  1765. Routine Description:
  1766. Get the value for a audio setting property. Delegated to the capture filter.
  1767. Arguments:
  1768. Return Value:
  1769. HRESULT.
  1770. --*/
  1771. {
  1772. ENTER_FUNCTION("CStreamAudioSend::Get(AudioSettings)");
  1773. if (IsBadWritePtr(plValue, sizeof(long)) ||
  1774. IsBadWritePtr(plFlags, sizeof(long)))
  1775. {
  1776. LOG((MSP_ERROR, "%s, bad pointer", __fxName));
  1777. return E_POINTER;
  1778. }
  1779. CLock lock(m_lock);
  1780. HRESULT hr = E_NOINTERFACE;
  1781. switch (Property)
  1782. {
  1783. case AudioSettings_SignalLevel:
  1784. // check if we have the interface to delegate to.
  1785. if (m_pSilenceControl)
  1786. {
  1787. // get the level from the filter.
  1788. hr = m_pSilenceControl->GetAudioLevel(plValue);
  1789. if (SUCCEEDED(hr))
  1790. {
  1791. *plFlags = TAPIControl_Flags_None;
  1792. }
  1793. }
  1794. break;
  1795. case AudioSettings_SilenceThreshold:
  1796. // check if we have the interface to delegate to.
  1797. if (m_pSilenceControl)
  1798. {
  1799. // get the level from the filter.
  1800. hr = m_pSilenceControl->GetSilenceLevel(
  1801. plValue,
  1802. plFlags
  1803. );
  1804. }
  1805. break;
  1806. case AudioSettings_Volume:
  1807. if (m_pAudioInputMixer)
  1808. {
  1809. double dVolume;
  1810. hr = m_pAudioInputMixer->get_MixLevel(&dVolume);
  1811. if (SUCCEEDED(hr))
  1812. {
  1813. // Convert the volume from the range 0 - 1 to the API's range.
  1814. *plValue = MIN_VOLUME + (long) (( MAX_VOLUME - MIN_VOLUME ) * dVolume);
  1815. *plFlags = TAPIControl_Flags_Manual;
  1816. }
  1817. }
  1818. break;
  1819. case AudioSettings_Balance:
  1820. hr = E_NOTIMPL;
  1821. break;
  1822. case AudioSettings_Loudness:
  1823. hr = E_NOTIMPL;
  1824. break;
  1825. case AudioSettings_Treble:
  1826. hr = E_NOTIMPL;
  1827. break;
  1828. case AudioSettings_Bass:
  1829. hr = E_NOTIMPL;
  1830. break;
  1831. case AudioSettings_Mono:
  1832. // we only support MONO for now.
  1833. *plValue = 1;
  1834. *plFlags = TAPIControl_Flags_Manual;
  1835. hr = S_OK;
  1836. break;
  1837. default:
  1838. hr = E_INVALIDARG;
  1839. }
  1840. return hr;
  1841. }
  1842. STDMETHODIMP CStreamAudioSend::Set(
  1843. IN AudioSettingsProperty Property,
  1844. IN long lValue,
  1845. IN TAPIControlFlags lFlags
  1846. )
  1847. /*++
  1848. Routine Description:
  1849. Set the value for a audio setting property. Delegated to the capture filter.
  1850. Arguments:
  1851. Return Value:
  1852. HRESULT.
  1853. --*/
  1854. {
  1855. ENTER_FUNCTION("CStreamAudioSend::Set(AudioSettings)");
  1856. CLock lock(m_lock);
  1857. HRESULT hr = E_NOINTERFACE;
  1858. switch (Property)
  1859. {
  1860. case AudioSettings_SignalLevel:
  1861. // this is a read only property.
  1862. hr = E_FAIL;
  1863. break;
  1864. case AudioSettings_SilenceThreshold:
  1865. // check if we have the interface to delegate to.
  1866. if (m_pSilenceControl)
  1867. {
  1868. // get the range from the filter.
  1869. hr = m_pSilenceControl->SetSilenceLevel(
  1870. lValue,
  1871. lFlags
  1872. );
  1873. }
  1874. break;
  1875. case AudioSettings_Volume:
  1876. if (m_pAudioInputMixer)
  1877. {
  1878. // Convert to the range 0 to 1.
  1879. double dVolume = (lValue - MIN_VOLUME )
  1880. / ((double)(MAX_VOLUME - MIN_VOLUME));
  1881. hr = m_pAudioInputMixer->put_MixLevel(dVolume);
  1882. }
  1883. break;
  1884. case AudioSettings_Balance:
  1885. hr = E_NOTIMPL;
  1886. break;
  1887. case AudioSettings_Loudness:
  1888. hr = E_NOTIMPL;
  1889. break;
  1890. case AudioSettings_Treble:
  1891. hr = E_NOTIMPL;
  1892. break;
  1893. case AudioSettings_Bass:
  1894. hr = E_NOTIMPL;
  1895. break;
  1896. case AudioSettings_Mono:
  1897. // we only support MONO for now.
  1898. if (lValue == 1)
  1899. {
  1900. hr = S_OK;
  1901. }
  1902. else
  1903. {
  1904. hr = E_FAIL;
  1905. }
  1906. break;
  1907. default:
  1908. hr = E_INVALIDARG;
  1909. }
  1910. return hr;
  1911. }
  1912. //
  1913. // ITStreamQualityControl methods
  1914. //
  1915. STDMETHODIMP CStreamAudioSend::Set (
  1916. IN StreamQualityProperty Property,
  1917. IN long lValue,
  1918. IN TAPIControlFlags lFlags
  1919. )
  1920. {
  1921. return E_NOTIMPL;
  1922. }
  1923. //
  1924. // IInnerStreamQualityControl methods.
  1925. //
  1926. STDMETHODIMP CStreamAudioSend::GetRange(
  1927. IN InnerStreamQualityProperty property,
  1928. OUT LONG *plMin,
  1929. OUT LONG *plMax,
  1930. OUT LONG *plSteppingDelta,
  1931. OUT LONG *plDefault,
  1932. OUT TAPIControlFlags *plFlags
  1933. )
  1934. /*++
  1935. Routine Description:
  1936. Get the range for a quality control property. Delegated to capture filter
  1937. for now.
  1938. Arguments:
  1939. Return Value:
  1940. HRESULT.
  1941. --*/
  1942. {
  1943. ENTER_FUNCTION("CStreamAudioSend::GetRange (InnerStreamQualityControl)");
  1944. HRESULT hr;
  1945. static BOOL fReported = FALSE;
  1946. CLock lock(m_lock);
  1947. switch (property)
  1948. {
  1949. case InnerStreamQuality_MaxBitrate:
  1950. if (m_pCaptureBitrateControl == NULL)
  1951. {
  1952. if (!fReported)
  1953. {
  1954. LOG((MSP_WARN, "%s, m_pCaptureBitrateControl is NULL", __fxName));
  1955. fReported = TRUE;
  1956. }
  1957. hr = E_NOTIMPL;
  1958. }
  1959. else
  1960. {
  1961. hr = m_pCaptureBitrateControl->GetRange(
  1962. BitrateControl_Maximum, plMin, plMax, plSteppingDelta, plDefault, plFlags, LAYERID
  1963. );
  1964. }
  1965. break;
  1966. case InnerStreamQuality_CurrBitrate:
  1967. if (m_pCaptureBitrateControl == NULL)
  1968. {
  1969. if (!fReported)
  1970. {
  1971. LOG((MSP_WARN, "%s, m_pCaptureBitrateControl is NULL", __fxName));
  1972. fReported = TRUE;
  1973. }
  1974. hr = E_NOTIMPL;
  1975. }
  1976. else
  1977. {
  1978. hr = m_pCaptureBitrateControl->GetRange(
  1979. BitrateControl_Current, plMin, plMax, plSteppingDelta, plDefault, plFlags, LAYERID
  1980. );
  1981. }
  1982. break;
  1983. default:
  1984. hr = CIPConfMSPStream::GetRange (property, plMin, plMax, plSteppingDelta, plDefault, plFlags);
  1985. break;
  1986. }
  1987. return hr;
  1988. }
  1989. STDMETHODIMP CStreamAudioSend::Get(
  1990. IN InnerStreamQualityProperty property,
  1991. OUT LONG *plValue,
  1992. OUT TAPIControlFlags *plFlags
  1993. )
  1994. /*++
  1995. Routine Description:
  1996. Get the value for a quality control property. Delegated to the quality
  1997. controller.
  1998. Arguments:
  1999. Return Value:
  2000. HRESULT.
  2001. --*/
  2002. {
  2003. ENTER_FUNCTION("CStreamAudioSend::Get(QualityControl)");
  2004. HRESULT hr;
  2005. static BOOL fReported = FALSE;
  2006. CLock lock(m_lock);
  2007. switch (property)
  2008. {
  2009. case InnerStreamQuality_MaxBitrate:
  2010. if( m_pCaptureBitrateControl == NULL )
  2011. {
  2012. if (!fReported)
  2013. {
  2014. LOG((MSP_WARN, "%s, m_pCaptureBitrateControl is NULL", __fxName));
  2015. fReported = TRUE;
  2016. }
  2017. hr = E_NOTIMPL;
  2018. }
  2019. else
  2020. {
  2021. hr = m_pCaptureBitrateControl->Get(BitrateControl_Maximum, plValue, plFlags, LAYERID);
  2022. }
  2023. break;
  2024. case InnerStreamQuality_CurrBitrate:
  2025. if (m_pCaptureBitrateControl == NULL)
  2026. {
  2027. if (!fReported)
  2028. {
  2029. LOG((MSP_WARN, "%s, m_pCaptureBitrateControl is NULL", __fxName));
  2030. fReported = TRUE;
  2031. }
  2032. hr = E_NOTIMPL;
  2033. }
  2034. else
  2035. {
  2036. hr = m_pCaptureBitrateControl->Get(BitrateControl_Current, plValue, plFlags, LAYERID);
  2037. }
  2038. break;
  2039. default:
  2040. hr = CIPConfMSPStream::Get (property, plValue, plFlags);
  2041. break;
  2042. }
  2043. return hr;
  2044. }