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.

1504 lines
35 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. H323aud.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. #include "common.h"
  13. #include <initguid.h>
  14. #include <amrtpnet.h> // rtp guilds
  15. #include <amrtpdmx.h> // demux guild
  16. #include <amrtpuid.h> // AMRTP media types
  17. #include <amrtpss.h> // for silence suppression filter
  18. #include <irtprph.h> // for IRTPRPHFilter
  19. #include <irtpsph.h> // for IRTPSPHFilter
  20. #include <mixflter.h> // audio mixer
  21. #include <g711uids.h> // for G711 codec CLSID
  22. #include <g723uids.h> // for G723 codec CLSID
  23. /////////////////////////////////////////////////////////////////////////////
  24. //
  25. // Private functions
  26. //
  27. /////////////////////////////////////////////////////////////////////////////
  28. DWORD AudioBitRate(
  29. IN DWORD dwPayloadType,
  30. IN AUDIOSETTINGS * pAudioSettings
  31. )
  32. /*++
  33. Routine Description:
  34. Calculate the bit rate based on the audio settings.
  35. Arguments:
  36. dwPayLoadType - the RTP paylaod type.
  37. pAudioSettings - the setting of the audio stream.
  38. Return Value:
  39. >=0 the bit rate needed.
  40. -1 don't know the bit rate.
  41. --*/
  42. {
  43. const DWORD HEADER_OVERHEAD = 28; // in bytes, UDP + IP headers
  44. DWORD dwBitRate = -1;
  45. switch (dwPayloadType)
  46. {
  47. case PAYLOAD_G711U:
  48. case PAYLOAD_G711A:
  49. dwBitRate = (HEADER_OVERHEAD +
  50. G711PacketSize(pAudioSettings->dwMillisecondsPerPacket)) * 8
  51. * 1000 / pAudioSettings->dwMillisecondsPerPacket;
  52. // adjust by three percent to make qos happy.
  53. dwBitRate = dwBitRate * 103 / 100;
  54. break;
  55. case PAYLOAD_G723:
  56. dwBitRate = (HEADER_OVERHEAD +
  57. G723PacketSize(pAudioSettings->dwMillisecondsPerPacket)) * 8
  58. * 1000 / pAudioSettings->dwMillisecondsPerPacket;
  59. // adjust by three percent to make qos happy.
  60. dwBitRate = dwBitRate * 103 / 100;
  61. break;
  62. }
  63. return dwBitRate;
  64. }
  65. /////////////////////////////////////////////////////////////////////////////
  66. //
  67. // CStreamAudioRecv
  68. //
  69. /////////////////////////////////////////////////////////////////////////////
  70. CStreamAudioRecv::CStreamAudioRecv()
  71. : CH323MSPStream()
  72. {
  73. m_szName = L"AudioRecv";
  74. }
  75. HRESULT CStreamAudioRecv::Configure(
  76. IN HANDLE htChannel,
  77. IN STREAMSETTINGS &StreamSettings
  78. )
  79. /*++
  80. Routine Description:
  81. Configure the settings of this stream.
  82. Arguments:
  83. StreamSettings - The setting structure got from the SDP blob.
  84. Return Value:
  85. HRESULT.
  86. --*/
  87. {
  88. LOG((MSP_TRACE, "AudioRecv Configure entered."));
  89. CLock lock(m_lock);
  90. _ASSERTE(m_fIsConfigured == FALSE);
  91. switch (StreamSettings.dwPayloadType)
  92. {
  93. case PAYLOAD_G711U:
  94. // The mixer can convert them, no codec needed.
  95. m_pClsidCodecFilter = &GUID_NULL;
  96. m_pRPHInputMinorType = &MEDIASUBTYPE_RTP_Payload_G711U;
  97. m_pClsidPHFilter = &CLSID_INTEL_RPHAUD;
  98. break;
  99. case PAYLOAD_G711A:
  100. m_pClsidCodecFilter = &CLSID_G711Codec;
  101. m_pRPHInputMinorType = &MEDIASUBTYPE_RTP_Payload_G711A;
  102. m_pClsidPHFilter = &CLSID_INTEL_RPHAUD;
  103. break;
  104. case PAYLOAD_G723:
  105. m_pClsidCodecFilter = &CLSID_IntelG723Codec;
  106. m_pRPHInputMinorType = &MEDIASUBTYPE_RTP_Payload_G723;
  107. m_pClsidPHFilter = &CLSID_INTEL_RPHAUD;
  108. break;
  109. default:
  110. LOG((MSP_ERROR, "unknown payload type, %x", StreamSettings.dwPayloadType));
  111. return E_FAIL;
  112. }
  113. m_Settings = StreamSettings;
  114. m_htChannel = htChannel;
  115. m_fIsConfigured = TRUE;
  116. InternalConfigure();
  117. return S_OK;
  118. }
  119. HRESULT CStreamAudioRecv::ConfigureRTPFilter(
  120. IN IBaseFilter * pIBaseFilter
  121. )
  122. /*++
  123. Routine Description:
  124. Configure the source RTP filter. Including set the address, port, TTL,
  125. QOS, thread priority, clcokrate, etc.
  126. Arguments:
  127. pIBaseFilter - The source RTP Filter.
  128. Return Value:
  129. HRESULT.
  130. --*/
  131. {
  132. LOG((MSP_TRACE, "AudioRecv ConfigureRTPFilter"));
  133. HRESULT hr;
  134. // Get the IRTPStream interface pointer on the filter.
  135. CComQIPtr<IRTPStream, &IID_IRTPStream> pIRTPStream(pIBaseFilter);
  136. if (pIRTPStream == NULL)
  137. {
  138. LOG((MSP_ERROR, "get RTP Stream interface"));
  139. return E_NOINTERFACE;
  140. }
  141. // select the local interface that the RTP should be using.
  142. LOG((MSP_INFO, "set locol Address:%x", m_Settings.dwIPLocal));
  143. // Set the local address and port used in the filter.
  144. if (FAILED(hr = pIRTPStream->SelectLocalIPAddress(
  145. htonl(m_Settings.dwIPLocal)
  146. )))
  147. {
  148. LOG((MSP_ERROR, "set locol Address, hr:%x", hr));
  149. return hr;
  150. }
  151. LOG((MSP_INFO, "set remote Address:%x, port:%d, local port:%d",
  152. m_Settings.dwIPRemote, 0, m_Settings.wRTPPortLocal));
  153. // Set the remote address and port used in the filter.
  154. if (FAILED(hr = pIRTPStream->SetAddress(
  155. htons(m_Settings.wRTPPortLocal), // local port.
  156. 0, // remote port.
  157. htonl(m_Settings.dwIPRemote) // remote address.
  158. )))
  159. {
  160. LOG((MSP_ERROR, "set remote Address, hr:%x", hr));
  161. return hr;
  162. }
  163. // Get the IRTCPStream interface pointer.
  164. CComQIPtr<IRTCPStream,
  165. &IID_IRTCPStream> pIRTCPStream(pIBaseFilter);
  166. if (pIRTCPStream == NULL)
  167. {
  168. LOG((MSP_ERROR, "get RTCP Stream interface"));
  169. return E_NOINTERFACE;
  170. }
  171. LOG((MSP_INFO, "set remote RTCP Address:%x, port:%d, local port:%d",
  172. m_Settings.dwIPRemote, m_Settings.wRTCPPortRemote,
  173. m_Settings.wRTCPPortLocal));
  174. // Set the remote RTCP address and port.
  175. if (FAILED(hr = pIRTCPStream->SetRTCPAddress(
  176. htons(m_Settings.wRTCPPortLocal),
  177. htons(m_Settings.wRTCPPortRemote),
  178. htonl(m_Settings.dwIPRemote)
  179. )))
  180. {
  181. LOG((MSP_ERROR, "set remote RTCP Address, hr:%x", hr));
  182. return hr;
  183. }
  184. // Set the TTL used in the filter.
  185. if (FAILED(hr = pIRTPStream->SetMulticastScope(DEFAULT_TTL)))
  186. {
  187. LOG((MSP_ERROR, "set TTL. %x", hr));
  188. return hr;
  189. }
  190. // Set the priority of the session
  191. if (FAILED(hr = pIRTPStream->SetSessionClassPriority(
  192. RTP_CLASS_AUDIO,
  193. g_dwAudioThreadPriority
  194. )))
  195. {
  196. LOG((MSP_WARN, "set session class and priority. %x", hr));
  197. }
  198. // Set the sample rate of the session
  199. LOG((MSP_INFO, "setting session sample rate to %d", g_dwAudioSampleRate));
  200. if (FAILED(hr = pIRTPStream->SetDataClock(g_dwAudioSampleRate)))
  201. {
  202. LOG((MSP_WARN, "set session sample rate. %x", hr));
  203. }
  204. // Enable the RTCP events
  205. if (FAILED(hr = ::EnableRTCPEvents(pIBaseFilter)))
  206. {
  207. LOG((MSP_WARN, "can not enable RTCP events %x", hr));
  208. }
  209. DWORD dwBitRate = AudioBitRate(
  210. m_Settings.dwPayloadType,
  211. &m_Settings.Audio
  212. );
  213. if (FAILED(hr = ::SetQOSOption(
  214. pIBaseFilter,
  215. m_Settings.dwPayloadType, // payload
  216. dwBitRate,
  217. TRUE
  218. )))
  219. {
  220. LOG((MSP_ERROR, "set QOS option. %x", hr));
  221. return hr;
  222. }
  223. return S_OK;
  224. }
  225. HRESULT CStreamAudioRecv::ConnectTerminal(
  226. IN ITTerminal * pITTerminal
  227. )
  228. /*++
  229. Routine Description:
  230. connect the mixer to the audio render terminal.
  231. Arguments:
  232. pITTerminal - The terminal to be connected.
  233. Return Value:
  234. HRESULT.
  235. --*/
  236. {
  237. LOG((MSP_TRACE, "AudioRecv.ConnectTerminal, pITTerminal %p", pITTerminal));
  238. HRESULT hr;
  239. // if our filters have not been contructed, do it now.
  240. if (m_pEdgeFilter == NULL)
  241. {
  242. hr = SetUpInternalFilters();
  243. if (FAILED(hr))
  244. {
  245. LOG((MSP_ERROR, "Set up internal filter failed, %x", hr));
  246. CleanUpFilters();
  247. return hr;
  248. }
  249. }
  250. // get the terminal control interface.
  251. CComQIPtr<ITTerminalControl, &IID_ITTerminalControl>
  252. pTerminal(pITTerminal);
  253. if (pTerminal == NULL)
  254. {
  255. LOG((MSP_ERROR, "can't get Terminal Control interface"));
  256. SendStreamEvent(CALL_TERMINAL_FAIL,
  257. CALL_CAUSE_BAD_DEVICE, E_NOINTERFACE, pITTerminal);
  258. return E_NOINTERFACE;
  259. }
  260. const DWORD MAXPINS = 8;
  261. DWORD dwNumPins = MAXPINS;
  262. IPin * Pins[MAXPINS];
  263. // Get the pins.
  264. hr = pTerminal->ConnectTerminal(
  265. m_pIGraphBuilder, 0, &dwNumPins, Pins
  266. );
  267. if (FAILED(hr))
  268. {
  269. LOG((MSP_ERROR, "can't connect to terminal, %x", hr));
  270. SendStreamEvent(CALL_TERMINAL_FAIL, CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
  271. return hr;
  272. }
  273. // the pin count should never be 0.
  274. if (dwNumPins == 0)
  275. {
  276. LOG((MSP_ERROR, "terminal has no pins."));
  277. SendStreamEvent(CALL_TERMINAL_FAIL, CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
  278. pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
  279. return E_UNEXPECTED;
  280. }
  281. // Connect the mixer filter to the audio render terminal.
  282. hr = ::ConnectFilters(
  283. m_pIGraphBuilder,
  284. (IBaseFilter *)m_pEdgeFilter,
  285. (IPin *)Pins[0]
  286. );
  287. // release the refcounts on the pins.
  288. for (DWORD i = 0; i < dwNumPins; i ++)
  289. {
  290. Pins[i]->Release();
  291. }
  292. if (FAILED(hr))
  293. {
  294. LOG((MSP_ERROR, "connect to the mixer filter. %x", hr));
  295. pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
  296. return hr;
  297. }
  298. //
  299. // Now we are actually connected. Update our state and perform postconnection
  300. // (ignore postconnection error code).
  301. //
  302. pTerminal->CompleteConnectTerminal();
  303. return hr;
  304. }
  305. HRESULT CStreamAudioRecv::SetUpInternalFilters()
  306. /*++
  307. Routine Description:
  308. set up the filters used in the stream.
  309. RTP->Demux->RPH(->DECODER)->Mixer
  310. Arguments:
  311. Return Value:
  312. HRESULT.
  313. --*/
  314. {
  315. LOG((MSP_TRACE, "AudioRecv.SetUpInternalFilters"));
  316. CComPtr<IBaseFilter> pSourceFilter;
  317. HRESULT hr;
  318. // create and add the source fitler.
  319. if (FAILED(hr = ::AddFilter(
  320. m_pIGraphBuilder,
  321. CLSID_RTPSourceFilter,
  322. L"RtpSource",
  323. &pSourceFilter)))
  324. {
  325. LOG((MSP_ERROR, "adding source filter. %x", hr));
  326. return hr;
  327. }
  328. if (FAILED(hr = ConfigureRTPFilter(pSourceFilter)))
  329. {
  330. LOG((MSP_ERROR, "configure RTP source filter. %x", hr));
  331. return hr;
  332. }
  333. // Create and add the payload handler into the filtergraph.
  334. CComPtr<IBaseFilter> pIRPHFilter;
  335. if (FAILED(hr = ::AddFilter(
  336. m_pIGraphBuilder,
  337. *m_pClsidPHFilter,
  338. L"RPH",
  339. &pIRPHFilter
  340. )))
  341. {
  342. LOG((MSP_ERROR, "add RPH filter. %x", hr));
  343. return hr;
  344. }
  345. // Get the IRTPRPHFilter interface.
  346. CComQIPtr<IRTPRPHFilter, &IID_IRTPRPHFilter>pIRTPRPHFilter(pIRPHFilter);
  347. if (pIRTPRPHFilter == NULL)
  348. {
  349. LOG((MSP_ERROR, "get IRTPRPHFilter interface"));
  350. return hr;
  351. }
  352. DWORD dwBufferSize = 0;
  353. switch (m_Settings.dwPayloadType)
  354. {
  355. case PAYLOAD_G711U:
  356. case PAYLOAD_G711A:
  357. dwBufferSize = G711PacketSize(
  358. m_Settings.Audio.dwMillisecondsPerPacket
  359. );
  360. break;
  361. case PAYLOAD_G723:
  362. dwBufferSize = G723PacketSize(
  363. m_Settings.Audio.dwMillisecondsPerPacket
  364. );
  365. break;
  366. }
  367. // set the media buffer size so that the receive buffers are of the
  368. // right size.
  369. if (FAILED(hr = pIRTPRPHFilter->SetMediaBufferSize(
  370. dwBufferSize
  371. )))
  372. {
  373. LOG((MSP_ERROR, "Set media buffer size. %x", hr));
  374. return hr;
  375. }
  376. LOG((MSP_INFO, "Set RPH media buffer size to %d", dwBufferSize));
  377. if (FAILED(hr = pIRTPRPHFilter->OverridePayloadType(
  378. (BYTE)m_Settings.dwPayloadType
  379. )))
  380. {
  381. LOG((LOG_ERROR, "override payload type. %x", hr));
  382. return FALSE;
  383. }
  384. #ifdef USEDEMUX
  385. // Connect the payload handler to the output pin on the demux.
  386. if (FAILED(hr = ::ConnectFilters(
  387. m_pIGraphBuilder,
  388. (IPin *)pIPinOutput,
  389. (IBaseFilter *)pIRPHFilter
  390. )))
  391. {
  392. LOG((MSP_ERROR, "connect demux and RPH filter. %x", hr));
  393. return hr;
  394. }
  395. #else
  396. // Connect the payload handler to the network filter.
  397. if (FAILED(hr = ::ConnectFilters(
  398. m_pIGraphBuilder,
  399. (IBaseFilter *)pSourceFilter,
  400. (IBaseFilter *)pIRPHFilter
  401. )))
  402. {
  403. LOG((MSP_ERROR, "connect network and RPH filter. %x", hr));
  404. return hr;
  405. }
  406. #endif
  407. CComPtr<IBaseFilter> pIFilter;
  408. // connect the codec filter if it is needed.
  409. if (*m_pClsidCodecFilter != GUID_NULL)
  410. {
  411. if (FAILED(hr = ::AddFilter(
  412. m_pIGraphBuilder,
  413. *m_pClsidCodecFilter,
  414. L"codec",
  415. &pIFilter
  416. )))
  417. {
  418. LOG((MSP_ERROR, "add Codec filter. %x", hr));
  419. return hr;
  420. }
  421. if (*m_pClsidCodecFilter == CLSID_IntelG723Codec)
  422. {
  423. IG723CodecLicense *pCodecLicense;
  424. if (SUCCEEDED(hr = pIFilter->QueryInterface(
  425. IID_IG723CodecLicense,
  426. (void **)&pCodecLicense
  427. )))
  428. {
  429. pCodecLicense->put_LicenseKey(G723KEY_PSword0, G723KEY_PSword1);
  430. pCodecLicense->Release();
  431. }
  432. }
  433. // Connect the decoder and the payload handler.
  434. if (FAILED(hr = ::ConnectFilters(
  435. m_pIGraphBuilder,
  436. (IBaseFilter *)pIRPHFilter,
  437. (IBaseFilter *)pIFilter
  438. )))
  439. {
  440. LOG((MSP_ERROR, "connect RPH filter and codec. %x", hr));
  441. return hr;
  442. }
  443. }
  444. else
  445. {
  446. pIFilter = pIRPHFilter;
  447. }
  448. // Create and add the mixer filter into the filtergraph.
  449. CComPtr<IBaseFilter> pIMixerFilter;
  450. if (FAILED(hr = ::AddFilter(
  451. m_pIGraphBuilder,
  452. CLSID_AudioMixFilter,
  453. L"Mixer",
  454. &pIMixerFilter
  455. )))
  456. {
  457. LOG((MSP_ERROR, "add Mixer filter. %x", hr));
  458. return hr;
  459. }
  460. LOG((MSP_INFO, "Added Mixer filter"));
  461. // Connect the payload handler or the codec filter to the mixer filter.
  462. if (FAILED(hr = ::ConnectFilters(
  463. m_pIGraphBuilder,
  464. (IBaseFilter *)pIFilter,
  465. (IBaseFilter *)pIMixerFilter
  466. )))
  467. {
  468. LOG((MSP_ERROR, "connect to the mixer filter. %x", hr));
  469. return hr;
  470. }
  471. // if every thing went well, keep a reference to the last filter so that
  472. // the change of terminal will not require a recreating of all the filters.
  473. if (SUCCEEDED(hr))
  474. {
  475. m_pEdgeFilter = pIMixerFilter;
  476. m_pEdgeFilter->AddRef();
  477. }
  478. return hr;
  479. }
  480. HRESULT CStreamAudioRecv::SetUpFilters()
  481. /*++
  482. Routine Description:
  483. Insert filters into the graph and connect to the terminals.
  484. Arguments:
  485. Return Value:
  486. HRESULT.
  487. --*/
  488. {
  489. LOG((MSP_TRACE, "AudioRecv SetupFilters entered."));
  490. HRESULT hr;
  491. // we only support one terminal for this stream.
  492. if (m_Terminals.GetSize() != 1)
  493. {
  494. return E_UNEXPECTED;
  495. }
  496. // Connect the mixer to the terminal.
  497. if (FAILED(hr = ConnectTerminal(
  498. m_Terminals[0]
  499. )))
  500. {
  501. LOG((MSP_ERROR, "connect to terminal failed. %x", hr));
  502. return hr;
  503. }
  504. return hr;
  505. }
  506. /////////////////////////////////////////////////////////////////////////////
  507. //
  508. // CStreamAudioSend
  509. //
  510. /////////////////////////////////////////////////////////////////////////////
  511. CStreamAudioSend::CStreamAudioSend()
  512. : CH323MSPStream()
  513. {
  514. m_szName = L"AudioSend";
  515. }
  516. HRESULT CStreamAudioSend::Configure(
  517. IN HANDLE htChannel,
  518. IN STREAMSETTINGS &StreamSettings
  519. )
  520. /*++
  521. Routine Description:
  522. Configure the settings of this stream.
  523. Arguments:
  524. StreamSettings - The setting structure got from the SDP blob.
  525. Return Value:
  526. HRESULT.
  527. --*/
  528. {
  529. LOG((MSP_TRACE, "AudioSend Configure entered."));
  530. CLock lock(m_lock);
  531. _ASSERTE(m_fIsConfigured == FALSE);
  532. switch (StreamSettings.dwPayloadType)
  533. {
  534. case PAYLOAD_G711U:
  535. case PAYLOAD_G711A:
  536. m_pClsidCodecFilter = &CLSID_G711Codec;
  537. m_pClsidPHFilter = &CLSID_INTEL_SPHAUD;
  538. break;
  539. case PAYLOAD_G723:
  540. m_pClsidCodecFilter = &CLSID_IntelG723Codec;
  541. m_pClsidPHFilter = &CLSID_INTEL_SPHAUD;
  542. break;
  543. default:
  544. LOG((MSP_ERROR,
  545. "unknow payload type, %x", StreamSettings.dwPayloadType));
  546. return E_FAIL;
  547. }
  548. m_Settings = StreamSettings;
  549. m_htChannel = htChannel;
  550. m_fIsConfigured = TRUE;
  551. InternalConfigure();
  552. return S_OK;
  553. }
  554. HRESULT CStreamAudioSend::ConfigureAudioCaptureTerminal(
  555. IN ITTerminalControl * pTerminal,
  556. OUT IPin ** ppIPin
  557. )
  558. /*++
  559. Routine Description:
  560. Configure the audio capture terminal. This function gets a output pin from
  561. the capture terminal and the configure the audio format and media type.
  562. Arguments:
  563. pTerminal - An audio capture terminal.
  564. ppIPin - the address to hold the returned pointer to IPin interface.
  565. Return Value:
  566. HRESULT
  567. --*/
  568. {
  569. LOG((MSP_TRACE, "AudioSend configure audio capture terminal."));
  570. const DWORD MAXPINS = 8;
  571. DWORD dwNumPins = MAXPINS;
  572. IPin * Pins[MAXPINS];
  573. // Get the pins from the first terminal because we only use on terminal
  574. // on this stream.
  575. HRESULT hr = pTerminal->ConnectTerminal(
  576. m_pIGraphBuilder, 0, &dwNumPins, Pins
  577. );
  578. if (FAILED(hr))
  579. {
  580. LOG((MSP_ERROR, "can't connect to terminal, %x", hr));
  581. return hr;
  582. }
  583. // The number of pins should never be 0.
  584. if (dwNumPins == 0)
  585. {
  586. LOG((MSP_ERROR, "terminal has no pins."));
  587. return E_UNEXPECTED;
  588. }
  589. // Save the first pin and release the others.
  590. CComPtr <IPin> pIPin = Pins[0];
  591. for (DWORD i = 0; i < dwNumPins; i ++)
  592. {
  593. Pins[i]->Release();
  594. }
  595. // Set the format of the audio to 8KHZ, 16Bit/Sample, MONO.
  596. hr = ::SetAudioFormat(
  597. pIPin,
  598. g_wAudioCaptureBitPerSample,
  599. g_dwAudioSampleRate
  600. );
  601. if (FAILED(hr))
  602. {
  603. LOG((MSP_ERROR, "can't set audio format, %x", hr));
  604. return hr;
  605. }
  606. // Set the capture buffer size.
  607. hr = ::SetAudioBufferSize(
  608. pIPin,
  609. g_dwAudioCaptureNumBufffers,
  610. AudioCaptureBufferSize(m_Settings.Audio.dwMillisecondsPerPacket)
  611. );
  612. if (FAILED(hr))
  613. {
  614. LOG((MSP_ERROR, "can't set aduio capture buffer size, %x", hr));
  615. return hr;
  616. }
  617. pIPin->AddRef();
  618. *ppIPin = pIPin;
  619. return hr;
  620. }
  621. HRESULT CStreamAudioSend::ConnectTerminal(
  622. IN ITTerminal * pITTerminal
  623. )
  624. /*++
  625. Routine Description:
  626. connect the audio capture terminal to the stream.
  627. Arguments:
  628. pITTerminal - The terminal to be connected.
  629. Return Value:
  630. HRESULT.
  631. --*/
  632. {
  633. LOG((MSP_TRACE, "AudioSend ConnectTerminal, pITTerminal %p", pITTerminal));
  634. CComQIPtr<ITTerminalControl, &IID_ITTerminalControl>
  635. pTerminal(pITTerminal);
  636. if (pTerminal == NULL)
  637. {
  638. LOG((MSP_ERROR, "can't get Terminal Control interface"));
  639. SendStreamEvent(CALL_TERMINAL_FAIL,
  640. CALL_CAUSE_BAD_DEVICE, E_NOINTERFACE, pITTerminal);
  641. return E_NOINTERFACE;
  642. }
  643. // configure the terminal.
  644. CComPtr<IPin> pIPin;
  645. HRESULT hr = ConfigureAudioCaptureTerminal(pTerminal, &pIPin);
  646. if (FAILED(hr))
  647. {
  648. LOG((MSP_ERROR, "configure audio capture terminal failed. %x", hr));
  649. SendStreamEvent(CALL_TERMINAL_FAIL, CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
  650. return hr;
  651. }
  652. // Create other filters to be use in the stream.
  653. hr = CreateSendFilters(pIPin);
  654. if (FAILED(hr))
  655. {
  656. LOG((MSP_ERROR, "Create audio send filters failed. %x", hr));
  657. pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
  658. // clean up internal filters as well.
  659. CleanUpFilters();
  660. return hr;
  661. }
  662. //
  663. // Now we are actually connected. Update our state and perform postconnection
  664. // (ignore postconnection error code).
  665. //
  666. pTerminal->CompleteConnectTerminal();
  667. return hr;
  668. }
  669. HRESULT CStreamAudioSend::SetUpFilters()
  670. /*++
  671. Routine Description:
  672. Insert filters into the graph and connect to the terminals.
  673. Arguments:
  674. Return Value:
  675. HRESULT.
  676. --*/
  677. {
  678. LOG((MSP_TRACE, "AudioSend SetUpFilters"));
  679. // we only support one terminal for this stream.
  680. if (m_Terminals.GetSize() != 1)
  681. {
  682. return E_UNEXPECTED;
  683. }
  684. HRESULT hr;
  685. // Connect the terminal to the rest of the stream.
  686. if (FAILED(hr = ConnectTerminal(
  687. m_Terminals[0]
  688. )))
  689. {
  690. LOG((MSP_ERROR, "connect the terminal to the filters. %x", hr));
  691. return hr;
  692. }
  693. return hr;
  694. }
  695. HRESULT CStreamAudioSend::ConfigureRTPFilter(
  696. IN IBaseFilter * pIBaseFilter
  697. )
  698. /*++
  699. Routine Description:
  700. Configure the source RTP filter. Including set the address, port, TTL,
  701. QOS, thread priority, clcokrate, etc.
  702. Arguments:
  703. pIBaseFilter - The source RTP Filter.
  704. Return Value:
  705. HRESULT.
  706. --*/
  707. {
  708. LOG((MSP_TRACE, "AudioSend ConfigureRTPFilter"));
  709. HRESULT hr;
  710. // Get the IRTPStream interface pointer on the filter.
  711. CComQIPtr<IRTPStream, &IID_IRTPStream> pIRTPStream(pIBaseFilter);
  712. if (pIRTPStream == NULL)
  713. {
  714. LOG((MSP_ERROR, "get IRTPStream interface"));
  715. return E_NOINTERFACE;
  716. }
  717. LOG((MSP_INFO, "set locol Address:%x", m_Settings.dwIPLocal));
  718. // Set the local address and port used in the filter.
  719. if (FAILED(hr = pIRTPStream->SelectLocalIPAddress(
  720. htonl(m_Settings.dwIPLocal)
  721. )))
  722. {
  723. LOG((MSP_ERROR, "set locol Address, hr:%x", hr));
  724. return hr;
  725. }
  726. LOG((MSP_INFO, "set remote Address:%x, port:%d",
  727. m_Settings.dwIPRemote, m_Settings.wRTPPortRemote));
  728. // Set the remote address and port used in the filter.
  729. if (FAILED(hr = pIRTPStream->SetAddress(
  730. 0, // local port.
  731. htons(m_Settings.wRTPPortRemote), // remote port.
  732. htonl(m_Settings.dwIPRemote) // remote IP.
  733. )))
  734. {
  735. LOG((MSP_ERROR, "set remote Address, hr:%x", hr));
  736. return hr;
  737. }
  738. // Get the IRTCPStream interface pointer.
  739. CComQIPtr<IRTCPStream,
  740. &IID_IRTCPStream> pIRTCPStream(pIBaseFilter);
  741. if (pIRTCPStream == NULL)
  742. {
  743. LOG((MSP_ERROR, "get RTCP Stream interface"));
  744. return E_NOINTERFACE;
  745. }
  746. LOG((MSP_INFO, "set remote RTCP Address:%x, port:%d, local port:%d",
  747. m_Settings.dwIPRemote, m_Settings.wRTCPPortRemote,
  748. m_Settings.wRTCPPortLocal));
  749. // Set the remote RTCP address and port.
  750. if (FAILED(hr = pIRTCPStream->SetRTCPAddress(
  751. htons(m_Settings.wRTCPPortLocal),
  752. htons(m_Settings.wRTCPPortRemote),
  753. htonl(m_Settings.dwIPRemote)
  754. )))
  755. {
  756. LOG((MSP_ERROR, "set remote RTCP Address, hr:%x", hr));
  757. return hr;
  758. }
  759. // Set the TTL used in the filter.
  760. if (FAILED(hr = pIRTPStream->SetMulticastScope(DEFAULT_TTL)))
  761. {
  762. LOG((MSP_ERROR, "set TTL. %x", hr));
  763. return hr;
  764. }
  765. // Set the priority of the session
  766. if (FAILED(hr = pIRTPStream->SetSessionClassPriority(
  767. RTP_CLASS_AUDIO,
  768. g_dwAudioThreadPriority
  769. )))
  770. {
  771. LOG((MSP_WARN, "set session class and priority. %x", hr));
  772. }
  773. // Set the sample rate of the session
  774. LOG((MSP_INFO, "setting session sample rate to %d", g_dwAudioSampleRate));
  775. if (FAILED(hr = pIRTPStream->SetDataClock(g_dwAudioSampleRate)))
  776. {
  777. LOG((MSP_WARN, "set session sample rate. %x", hr));
  778. }
  779. // Enable the RTCP events
  780. if (FAILED(hr = ::EnableRTCPEvents(pIBaseFilter)))
  781. {
  782. LOG((MSP_WARN, "can not enable RTCP events %x", hr));
  783. }
  784. DWORD dwBitRate = AudioBitRate(
  785. m_Settings.dwPayloadType,
  786. &m_Settings.Audio
  787. );
  788. if (FAILED(hr = ::SetQOSOption(
  789. pIBaseFilter,
  790. m_Settings.dwPayloadType, // payload
  791. dwBitRate,
  792. FALSE
  793. )))
  794. {
  795. LOG((MSP_ERROR, "set QOS option. %x", hr));
  796. return hr;
  797. }
  798. return S_OK;
  799. }
  800. HRESULT CStreamAudioSend::CreateSendFilters(
  801. IN IPin *pPin
  802. )
  803. /*++
  804. Routine Description:
  805. Insert filters into the graph and connect to the capture pin.
  806. Capturepin->SilenceSuppressor->Encoder->SPH->RTPRender
  807. Arguments:
  808. pPin - The output pin on the capture filter.
  809. Return Value:
  810. HRESULT.
  811. --*/
  812. {
  813. LOG((MSP_TRACE, "AudioSend.CreateSendFilters"));
  814. HRESULT hr;
  815. // if the the internal filters have been created before, just
  816. // connect the terminal to the first filter in the chain.
  817. if (m_pEdgeFilter != NULL)
  818. {
  819. if (FAILED(hr = ::ConnectFilters(
  820. m_pIGraphBuilder,
  821. pPin,
  822. (IBaseFilter *)m_pEdgeFilter
  823. )))
  824. {
  825. LOG((MSP_ERROR, "connect capture and ss %x", hr));
  826. return hr;
  827. }
  828. return hr;
  829. }
  830. DWORD dwSilenceSuppression = 1;
  831. GetRegValue(L"SilenceSuppression", &dwSilenceSuppression);
  832. CComPtr<IBaseFilter> pISSFilter;
  833. if (dwSilenceSuppression)
  834. {
  835. // Create the silence suppression filter and add it into the graph.
  836. // The filter is optional.
  837. if (FAILED(hr = ::AddFilter(
  838. m_pIGraphBuilder,
  839. CLSID_SilenceSuppressionFilter,
  840. L"SS",
  841. &pISSFilter
  842. )))
  843. {
  844. LOG((MSP_ERROR, "can't add SS filter, %x", hr));
  845. return hr;
  846. }
  847. // connect the capture pin with the SS filter.
  848. if (FAILED(hr = ::ConnectFilters(
  849. m_pIGraphBuilder,
  850. pPin,
  851. (IBaseFilter *)pISSFilter
  852. )))
  853. {
  854. LOG((MSP_ERROR, "connect capture and ss %x", hr));
  855. return hr;
  856. }
  857. // enable AGC events.
  858. DWORD dwAGC = 0;
  859. if (FALSE == ::GetRegValue(L"AGC", &dwAGC) || dwAGC != 0)
  860. {
  861. // AGC is not disabled, just do it.
  862. CComQIPtr<ISilenceSuppressor, &IID_ISilenceSuppressor>
  863. pISilcnecSuppressor(pISSFilter);
  864. if (pISilcnecSuppressor != NULL)
  865. {
  866. hr = pISilcnecSuppressor->EnableEvents(
  867. (1 << AGC_INCREASE_GAIN) | (1 << AGC_DECREASE_GAIN),
  868. 2000 // no more that an event every two seconds.
  869. );
  870. if (FAILED(hr))
  871. {
  872. LOG((MSP_WARN, "can't enable AGC events, %x", hr));
  873. }
  874. }
  875. }
  876. }
  877. // Create the codec filter and add it into the graph.
  878. CComPtr<IBaseFilter> pICodecFilter;
  879. if (FAILED(hr = ::AddFilter(
  880. m_pIGraphBuilder,
  881. *m_pClsidCodecFilter,
  882. L"Encoder",
  883. &pICodecFilter)))
  884. {
  885. LOG((MSP_ERROR, "add Codec filter. %x", hr));
  886. return hr;
  887. }
  888. if (*m_pClsidCodecFilter == CLSID_IntelG723Codec)
  889. {
  890. IG723CodecLicense *pCodecLicense;
  891. if (SUCCEEDED(hr = pICodecFilter->QueryInterface(
  892. IID_IG723CodecLicense,
  893. (void **)&pCodecLicense
  894. )))
  895. {
  896. pCodecLicense->put_LicenseKey(G723KEY_PSword0, G723KEY_PSword1);
  897. pCodecLicense->Release();
  898. }
  899. }
  900. if (dwSilenceSuppression)
  901. {
  902. // connect the SS filter and the Codec filter.
  903. if (FAILED(hr = ::ConnectFilters(
  904. m_pIGraphBuilder,
  905. (IBaseFilter *)pISSFilter,
  906. (IBaseFilter *)pICodecFilter
  907. )))
  908. {
  909. LOG((MSP_ERROR, "connect ss filter and codec filter. %x", hr));
  910. return hr;
  911. }
  912. }
  913. else
  914. {
  915. // connect the pin and the Codec filter.
  916. if (FAILED(hr = ::ConnectFilters(
  917. m_pIGraphBuilder,
  918. pPin,
  919. (IBaseFilter *)pICodecFilter
  920. )))
  921. {
  922. LOG((MSP_ERROR, "connect capture output pin and codec filter. %x", hr));
  923. return hr;
  924. }
  925. }
  926. // Create the send payload handler and add it into the graph.
  927. CComPtr<IBaseFilter> pISPHFilter;
  928. if (FAILED(hr = ::AddFilter(
  929. m_pIGraphBuilder,
  930. *m_pClsidPHFilter,
  931. L"SPH",
  932. &pISPHFilter
  933. )))
  934. {
  935. LOG((MSP_ERROR, "add SPH filter. %x", hr));
  936. return hr;
  937. }
  938. // Get the IRTPSPHFilter interface.
  939. CComQIPtr<IRTPSPHFilter,
  940. &IID_IRTPSPHFilter> pIRTPSPHFilter(pISPHFilter);
  941. if (pIRTPSPHFilter == NULL)
  942. {
  943. LOG((MSP_ERROR, "get IRTPSPHFilter interface"));
  944. return E_NOINTERFACE;
  945. }
  946. DWORD dwBufferSize = 0;
  947. switch (m_Settings.dwPayloadType)
  948. {
  949. case PAYLOAD_G711U:
  950. case PAYLOAD_G711A:
  951. dwBufferSize = G711PacketSize(
  952. m_Settings.Audio.dwMillisecondsPerPacket
  953. );
  954. break;
  955. case PAYLOAD_G723:
  956. dwBufferSize = G723PacketSize(
  957. m_Settings.Audio.dwMillisecondsPerPacket
  958. );
  959. break;
  960. }
  961. // Set the packetSize.
  962. if (FAILED(hr= pIRTPSPHFilter->SetMaxPacketSize(dwBufferSize)))
  963. {
  964. LOG((MSP_ERROR, "set SPH filter Max packet size: %d hr: %x",
  965. dwBufferSize, hr));
  966. return hr;
  967. }
  968. if (FAILED(hr = pIRTPSPHFilter->OverridePayloadType(
  969. (BYTE)m_Settings.dwPayloadType
  970. )))
  971. {
  972. LOG((LOG_ERROR, "Set SPH payload type. %x", hr));
  973. return hr;
  974. }
  975. // Connect the Codec filter with the SPH filter .
  976. if (FAILED(hr = ::ConnectFilters(
  977. m_pIGraphBuilder,
  978. (IBaseFilter *)pICodecFilter,
  979. (IBaseFilter *)pISPHFilter
  980. )))
  981. {
  982. LOG((MSP_ERROR, "connect codec filter and SPH filter. %x", hr));
  983. return hr;
  984. }
  985. // Create the RTP render filter and add it into the graph.
  986. CComPtr<IBaseFilter> pRenderFilter;
  987. if (FAILED(hr = ::AddFilter(
  988. m_pIGraphBuilder,
  989. CLSID_RTPRenderFilter,
  990. L"RtpRender",
  991. &pRenderFilter)))
  992. {
  993. LOG((MSP_ERROR, "adding render filter. %x", hr));
  994. return hr;
  995. }
  996. // Set the address for the render fitler.
  997. if (FAILED(hr = ConfigureRTPFilter(pRenderFilter)))
  998. {
  999. LOG((MSP_ERROR, "set destination address. %x", hr));
  1000. return hr;
  1001. }
  1002. // Connect the SPH filter with the RTP Render filter.
  1003. if (FAILED(hr = ::ConnectFilters(
  1004. m_pIGraphBuilder,
  1005. (IBaseFilter *)pISPHFilter,
  1006. (IBaseFilter *)pRenderFilter
  1007. )))
  1008. {
  1009. LOG((MSP_ERROR, "connect SPH filter and Render filter. %x", hr));
  1010. return hr;
  1011. }
  1012. // remember the first filter after the terminal
  1013. if (dwSilenceSuppression)
  1014. {
  1015. m_pEdgeFilter = pISSFilter;
  1016. }
  1017. else
  1018. {
  1019. m_pEdgeFilter = pICodecFilter;
  1020. }
  1021. m_pEdgeFilter->AddRef();
  1022. return S_OK;
  1023. }
  1024. HRESULT AdjustGain(
  1025. IN IUnknown * pIUnknown,
  1026. IN long lPercent
  1027. )
  1028. /*++
  1029. Routine Description:
  1030. This function uses IAMAudioInputMixer interface to adjust the gain.
  1031. Arguments:
  1032. pIUnknown - the object that supports IAMAudioInputMixer
  1033. lPercent - the adjustment, a negative value means decrease.
  1034. Return Value:
  1035. S_OK,
  1036. E_NOINTERFACE,
  1037. E_UNEXPECTED
  1038. --*/
  1039. {
  1040. CComPtr <IAMAudioInputMixer> pMixer;
  1041. HRESULT hr = pIUnknown->QueryInterface(
  1042. IID_IAMAudioInputMixer, (void **)&pMixer
  1043. );
  1044. if (FAILED(hr))
  1045. {
  1046. LOG((MSP_ERROR, "can't get IAMAudioInputMixer interface."));
  1047. return hr;
  1048. }
  1049. BOOL fEnabled;
  1050. hr = pMixer->get_Enable(&fEnabled);
  1051. if (SUCCEEDED(hr) && !fEnabled)
  1052. {
  1053. return S_OK;
  1054. }
  1055. double MixLevel;
  1056. hr = pMixer->get_MixLevel(&MixLevel);
  1057. if (FAILED(hr))
  1058. {
  1059. LOG((MSP_ERROR, "get_MixLevel returned %d", hr));
  1060. return hr;
  1061. }
  1062. LOG((MSP_INFO, "get_MixLevel returned %d", hr));
  1063. MixLevel = MixLevel * (100 + lPercent) / 100;
  1064. hr = pMixer->put_MixLevel(MixLevel);
  1065. if (FAILED(hr))
  1066. {
  1067. LOG((MSP_ERROR, "put_MixLevel returned %d", hr));
  1068. return hr;
  1069. }
  1070. return S_OK;
  1071. }
  1072. HRESULT CStreamAudioSend::ProcessAGCEvent(
  1073. IN AGC_EVENT Event,
  1074. IN long lPercent
  1075. )
  1076. /*++
  1077. Routine Description:
  1078. The filters fire AGC events to requste a change in the microphone gain.
  1079. This function finds the capture terminal and adjust the gain on it.
  1080. Arguments:
  1081. Event - either AGC_INCREASE_GAIN or AGC_DECREASE_GAIN.
  1082. Return Value:
  1083. S_OK,
  1084. E_UNEXPECTED
  1085. --*/
  1086. {
  1087. LOG((MSP_TRACE, "ProcessAGCEvent %s %d percent",
  1088. (Event == AGC_INCREASE_GAIN) ? "Increase" : "Decrease",
  1089. lPercent
  1090. ));
  1091. _ASSERTE(lPercent > 0 && lPercent <= 100);
  1092. CLock lock(m_lock);
  1093. if (m_pEdgeFilter == NULL)
  1094. {
  1095. LOG((MSP_ERROR, "No filter to adjust gain."));
  1096. return E_UNEXPECTED;
  1097. }
  1098. CComPtr<IPin> pMyPin, pCapturePin;
  1099. // find the first pin in the stream
  1100. HRESULT hr = ::FindPin(m_pEdgeFilter, &pMyPin, PINDIR_INPUT, FALSE);
  1101. if (FAILED(hr))
  1102. {
  1103. LOG((MSP_ERROR, "can't get find the first pin the stream, %x", hr));
  1104. return hr;
  1105. }
  1106. // find the capture pin that connects to our first pin.
  1107. hr = pMyPin->ConnectedTo(&pCapturePin);
  1108. if (FAILED(hr))
  1109. {
  1110. LOG((MSP_ERROR, "can't find the capture pin, %x", hr));
  1111. return hr;
  1112. }
  1113. // find the filter that has the capture pin.
  1114. PIN_INFO PinInfo;
  1115. hr = pCapturePin->QueryPinInfo(&PinInfo);
  1116. if (FAILED(hr))
  1117. {
  1118. LOG((MSP_ERROR, "can't find the capture filter, %x", hr));
  1119. return hr;
  1120. }
  1121. // save the filter pointer.
  1122. CComPtr<IBaseFilter> pICaptureFilter = PinInfo.pFilter;
  1123. PinInfo.pFilter->Release();
  1124. // get the amount to adjust.
  1125. if (Event == AGC_DECREASE_GAIN)
  1126. {
  1127. lPercent = -lPercent;
  1128. }
  1129. AdjustGain(pICaptureFilter, lPercent);
  1130. // Get the enumerator of pins on the filter.
  1131. CComPtr<IEnumPins> pIEnumPins;
  1132. if (FAILED(hr = pICaptureFilter->EnumPins(&pIEnumPins)))
  1133. {
  1134. LOG((MSP_ERROR, "enumerate pins on the filter %x", hr));
  1135. return hr;
  1136. }
  1137. // Enumerate all the pins and adjust gains on each active one.
  1138. for (;;)
  1139. {
  1140. CComPtr<IPin> pIPin;
  1141. DWORD dwFeched;
  1142. if (pIEnumPins->Next(1, &pIPin, &dwFeched) != S_OK)
  1143. {
  1144. LOG((MSP_ERROR, "find pin on filter."));
  1145. break;
  1146. }
  1147. AdjustGain(pIPin, lPercent);
  1148. }
  1149. return hr;
  1150. }
  1151. HRESULT CStreamAudioSend::ProcessGraphEvent(
  1152. IN long lEventCode,
  1153. IN long lParam1,
  1154. IN long lParam2
  1155. )
  1156. {
  1157. LOG((MSP_TRACE, "%ws ProcessGraphEvent %d", m_szName, lEventCode));
  1158. switch (lEventCode)
  1159. {
  1160. case AGC_EVENTBASE + AGC_INCREASE_GAIN:
  1161. ProcessAGCEvent(AGC_INCREASE_GAIN, lParam1);
  1162. break;
  1163. case AGC_EVENTBASE + AGC_DECREASE_GAIN:
  1164. ProcessAGCEvent(AGC_DECREASE_GAIN, lParam1);
  1165. break;
  1166. default:
  1167. return CH323MSPStream::ProcessGraphEvent(
  1168. lEventCode, lParam1, lParam2
  1169. );
  1170. }
  1171. return S_OK;
  1172. }