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.

2122 lines
50 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. H323vid.cpp
  5. Abstract:
  6. This module contains implementation of the video 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 <irtprph.h> // for IRTPRPHFilter
  14. #include <irtpsph.h> // for IRTPSPHFilter
  15. #include <amrtpuid.h> // AMRTP media types
  16. #include <amrtpnet.h> // rtp guilds
  17. #include <ih26xcd.h> // for the h26X encoder filter
  18. #include <initguid.h>
  19. #include <amrtpdmx.h> // demux guild
  20. #include <viduids.h> // for video CLSIDs
  21. const DWORD c_SlowLinkSpeed = 40000;
  22. /////////////////////////////////////////////////////////////////////////////
  23. //
  24. // CStreamVideoRecv
  25. //
  26. /////////////////////////////////////////////////////////////////////////////
  27. CStreamVideoRecv::CStreamVideoRecv()
  28. : CH323MSPStream(),
  29. m_dwCurrentBitRate(0),
  30. m_dwProposedBitRate(0)
  31. {
  32. m_szName = L"VideoRecv";
  33. m_dwLastIFrameRequestedTime = timeGetTime();
  34. m_dwIFramePending = FALSE;
  35. }
  36. HRESULT CStreamVideoRecv::Configure(
  37. IN HANDLE htChannel,
  38. IN STREAMSETTINGS &StreamSettings
  39. )
  40. /*++
  41. Routine Description:
  42. Configure the settings of this stream.
  43. Arguments:
  44. StreamSettings - The setting structure got from the SDP blob.
  45. Return Value:
  46. HRESULT.
  47. --*/
  48. {
  49. LOG((MSP_TRACE, "VideoRecv configure entered."));
  50. CLock lock(m_lock);
  51. _ASSERTE(m_fIsConfigured == FALSE);
  52. switch (StreamSettings.dwPayloadType)
  53. {
  54. case PAYLOAD_H261:
  55. m_pClsidCodecFilter = &CLSID_H261_DECODE_FILTER;
  56. m_pRPHInputMinorType = &MEDIASUBTYPE_RTP_Payload_H261;
  57. m_pClsidPHFilter = &CLSID_INTEL_RPHH26X;
  58. break;
  59. case PAYLOAD_H263:
  60. m_pClsidCodecFilter = &CLSID_H263_DECODE_FILTER;
  61. m_pRPHInputMinorType = &MEDIASUBTYPE_RTP_Payload_H263;
  62. m_pClsidPHFilter = &CLSID_INTEL_RPHH26X;
  63. break;
  64. default:
  65. LOG((MSP_ERROR, "unknow payload type, %x", StreamSettings.dwPayloadType));
  66. return E_FAIL;
  67. }
  68. m_Settings = StreamSettings;
  69. m_htChannel = htChannel;
  70. m_fIsConfigured = TRUE;
  71. m_dwCurrentBitRate = m_Settings.Video.dwMaxBitRate;
  72. m_dwProposedBitRate = m_dwCurrentBitRate;
  73. InternalConfigure();
  74. return S_OK;
  75. }
  76. HRESULT CStreamVideoRecv::ConfigureRTPFilter(
  77. IN IBaseFilter * pIBaseFilter
  78. )
  79. /*++
  80. Routine Description:
  81. Configure the source RTP filter. Including set the address, port, TTL,
  82. QOS, thread priority, clcokrate, etc.
  83. Arguments:
  84. pIBaseFilter - The source RTP Filter.
  85. Return Value:
  86. HRESULT.
  87. --*/
  88. {
  89. LOG((MSP_TRACE, "VideoRecv ConfigureRTPFilter"));
  90. HRESULT hr;
  91. // Get the IRTPStream interface pointer on the filter.
  92. CComQIPtr<IRTPStream, &IID_IRTPStream> pIRTPStream(pIBaseFilter);
  93. if (pIRTPStream == NULL)
  94. {
  95. LOG((MSP_ERROR, "get RTP Stream interface"));
  96. return E_NOINTERFACE;
  97. }
  98. LOG((MSP_INFO, "set locol Address:%x", m_Settings.dwIPLocal));
  99. // Set the local address and port used in the filter.
  100. if (FAILED(hr = pIRTPStream->SelectLocalIPAddress(
  101. htonl(m_Settings.dwIPLocal)
  102. )))
  103. {
  104. LOG((MSP_ERROR, "set locol Address, hr:%x", hr));
  105. return hr;
  106. }
  107. LOG((MSP_INFO, "set remote Address:%x, port:%d",
  108. m_Settings.dwIPRemote, m_Settings.wRTPPortRemote));
  109. // Set the remote address and port used in the filter.
  110. if (FAILED(hr = pIRTPStream->SetAddress(
  111. htons(m_Settings.wRTPPortLocal), // local port.
  112. 0, // remote port.
  113. htonl(m_Settings.dwIPRemote) // remote address.
  114. )))
  115. {
  116. LOG((MSP_ERROR, "set remote Address, hr:%x", hr));
  117. return hr;
  118. }
  119. // Get the IRTCPStream interface pointer.
  120. CComQIPtr<IRTCPStream,
  121. &IID_IRTCPStream> pIRTCPStream(pIBaseFilter);
  122. if (pIRTCPStream == NULL)
  123. {
  124. LOG((MSP_ERROR, "get RTCP Stream interface"));
  125. return E_NOINTERFACE;
  126. }
  127. LOG((MSP_INFO, "set remote RTCP Address:%x, port:%d, local port: %d",
  128. m_Settings.dwIPRemote, m_Settings.wRTCPPortRemote,
  129. m_Settings.wRTCPPortLocal));
  130. // Set the remote RTCP address and port.
  131. if (FAILED(hr = pIRTCPStream->SetRTCPAddress(
  132. htons(m_Settings.wRTCPPortLocal),
  133. htons(m_Settings.wRTCPPortRemote),
  134. htonl(m_Settings.dwIPRemote)
  135. )))
  136. {
  137. LOG((MSP_ERROR, "set remote RTCP Address, hr:%x", hr));
  138. return hr;
  139. }
  140. // Set the TTL used in the filter.
  141. if (FAILED(hr = pIRTPStream->SetMulticastScope(DEFAULT_TTL)))
  142. {
  143. LOG((MSP_ERROR, "set TTL. %x", hr));
  144. return hr;
  145. }
  146. // Set the priority of the session
  147. if (FAILED(hr = pIRTPStream->SetSessionClassPriority(
  148. RTP_CLASS_VIDEO,
  149. g_dwVideoThreadPriority
  150. )))
  151. {
  152. LOG((MSP_ERROR, "set session class and priority. %x", hr));
  153. }
  154. // Set the sample rate of the session
  155. LOG((MSP_INFO, "setting session sample rate to %d", g_dwVideoSampleRateHigh));
  156. if (FAILED(hr = pIRTPStream->SetDataClock(g_dwVideoSampleRateHigh)))
  157. {
  158. LOG((MSP_ERROR, "set session sample rate. %x", hr));
  159. }
  160. // Enable the RTCP events.
  161. if (FAILED(hr = ::EnableRTCPEvents(pIBaseFilter)))
  162. {
  163. LOG((MSP_WARN, "can not enable RTCP events %x", hr));
  164. }
  165. if (FAILED(hr = ::SetQOSOption(
  166. pIBaseFilter,
  167. m_Settings.dwPayloadType, // payload
  168. m_Settings.Video.dwMaxBitRate,
  169. TRUE
  170. )))
  171. {
  172. LOG((MSP_ERROR, "set QOS option. %x", hr));
  173. return hr;
  174. }
  175. return S_OK;
  176. }
  177. HRESULT CStreamVideoRecv::SetUpInternalFilters()
  178. /*++
  179. Routine Description:
  180. set up the filters used in the stream.
  181. RTP->Demux->RPH->DECODER->Render terminal
  182. Arguments:
  183. Return Value:
  184. HRESULT.
  185. --*/
  186. {
  187. LOG((MSP_TRACE, "VideoRecv.SetUpInternalFilters"));
  188. CComPtr<IBaseFilter> pSourceFilter;
  189. HRESULT hr;
  190. // create and add the source fitler.
  191. if (FAILED(hr = ::AddFilter(
  192. m_pIGraphBuilder,
  193. CLSID_RTPSourceFilter,
  194. L"RtpSource",
  195. &pSourceFilter)))
  196. {
  197. LOG((MSP_ERROR, "adding source filter. %x", hr));
  198. return hr;
  199. }
  200. if (FAILED(hr = ConfigureRTPFilter(pSourceFilter)))
  201. {
  202. LOG((MSP_ERROR, "configure RTP source filter. %x", hr));
  203. return hr;
  204. }
  205. // Create and add the payload handler into the filtergraph.
  206. CComPtr<IBaseFilter> pIRPHFilter;
  207. if (FAILED(hr = ::AddFilter(
  208. m_pIGraphBuilder,
  209. *m_pClsidPHFilter,
  210. L"RPH",
  211. &pIRPHFilter
  212. )))
  213. {
  214. LOG((MSP_ERROR, "add RPH filter. %x", hr));
  215. return hr;
  216. }
  217. // Get the IRPHH26XSettings interface used in H323iguring the RPH
  218. // filter to the right image size.
  219. CComQIPtr<IRPHH26XSettings,
  220. &IID_IRPHH26XSettings> pIRPHH26XSettings(pIRPHFilter);
  221. if (pIRPHH26XSettings == NULL)
  222. {
  223. LOG((MSP_WARN, "can't get IRPHH26XSettings interface"));
  224. }
  225. else if (FAILED(pIRPHH26XSettings->SetCIF(m_Settings.Video.bCIF)))
  226. {
  227. LOG((MSP_WARN, "can't set CIF or QCIF"));
  228. }
  229. // Get the IRTPRPHFilter interface.
  230. CComQIPtr<IRTPRPHFilter, &IID_IRTPRPHFilter>pIRTPRPHFilter(pIRPHFilter);
  231. if (pIRTPRPHFilter == NULL)
  232. {
  233. LOG((MSP_ERROR, "get IRTPRPHFilter interface"));
  234. return hr;
  235. }
  236. if (FAILED(hr = pIRTPRPHFilter->OverridePayloadType(
  237. (BYTE)m_Settings.dwPayloadType
  238. )))
  239. {
  240. LOG((LOG_ERROR, "override payload type. %x", hr));
  241. return FALSE;
  242. }
  243. // Connect the payload handler to the output pin on the source filter.
  244. if (FAILED(hr = ::ConnectFilters(
  245. m_pIGraphBuilder,
  246. (IBaseFilter *)pSourceFilter,
  247. (IBaseFilter *)pIRPHFilter
  248. )))
  249. {
  250. LOG((MSP_ERROR, "connect demux and RPH filter. %x", hr));
  251. return hr;
  252. }
  253. CComPtr<IBaseFilter> pCodecFilter;
  254. if (FAILED(hr = ::AddFilter(
  255. m_pIGraphBuilder,
  256. *m_pClsidCodecFilter,
  257. L"codec",
  258. &pCodecFilter
  259. )))
  260. {
  261. LOG((MSP_ERROR, "add Codec filter. %x", hr));
  262. return hr;
  263. }
  264. // Connect the payload handler to the output pin on the demux.
  265. if (FAILED(hr = ::ConnectFilters(
  266. m_pIGraphBuilder,
  267. (IBaseFilter *)pIRPHFilter,
  268. (IBaseFilter *)pCodecFilter
  269. )))
  270. {
  271. LOG((MSP_ERROR, "connect RPH filter and codec. %x", hr));
  272. return hr;
  273. }
  274. m_pEdgeFilter = pCodecFilter;
  275. m_pEdgeFilter->AddRef();
  276. return hr;
  277. }
  278. HRESULT CStreamVideoRecv::ConnectTerminal(
  279. IN ITTerminal * pITTerminal
  280. )
  281. /*++
  282. Routine Description:
  283. connect the codec to the video render terminal.
  284. Arguments:
  285. pITTerminal - The terminal to be connected.
  286. Return Value:
  287. HRESULT.
  288. --*/
  289. {
  290. LOG((MSP_TRACE, "VideoRecv.ConnectTerminal, pTerminal %p", pITTerminal));
  291. HRESULT hr;
  292. // if our filters have not been contructed, do it now.
  293. if (m_pEdgeFilter == NULL)
  294. {
  295. hr = SetUpInternalFilters();
  296. if (FAILED(hr))
  297. {
  298. LOG((MSP_ERROR, "Set up internal filter failed, %x", hr));
  299. CleanUpFilters();
  300. return hr;
  301. }
  302. }
  303. // get the terminal control interface.
  304. CComQIPtr<ITTerminalControl, &IID_ITTerminalControl>
  305. pTerminal(pITTerminal);
  306. if (pTerminal == NULL)
  307. {
  308. LOG((MSP_ERROR, "can't get Terminal Control interface"));
  309. SendStreamEvent(CALL_TERMINAL_FAIL, CALL_CAUSE_BAD_DEVICE, E_NOINTERFACE, pITTerminal);
  310. return E_NOINTERFACE;
  311. }
  312. // try to disable DDraw because the decoders can't handle DDraw now.
  313. HRESULT hr2;
  314. IDrawVideoImage *pIDrawVideoImage;
  315. hr2 = pTerminal->QueryInterface(IID_IDrawVideoImage, (void **)&pIDrawVideoImage);
  316. if (SUCCEEDED(hr2))
  317. {
  318. hr2 = pIDrawVideoImage->DrawVideoImageBegin();
  319. if (FAILED(hr2))
  320. {
  321. LOG((MSP_WARN, "Can't disable DDraw. %x", hr2));
  322. }
  323. else
  324. {
  325. LOG((MSP_INFO, "DDraw disabled."));
  326. }
  327. pIDrawVideoImage->Release();
  328. }
  329. else
  330. {
  331. LOG((MSP_WARN, "Can't get IDrawVideoImage. %x", hr2));
  332. }
  333. const DWORD MAXPINS = 8;
  334. DWORD dwNumPins = MAXPINS;
  335. IPin * Pins[MAXPINS];
  336. hr = pTerminal->ConnectTerminal(
  337. m_pIGraphBuilder, 0, &dwNumPins, Pins
  338. );
  339. if (FAILED(hr))
  340. {
  341. LOG((MSP_ERROR, "can't connect to terminal, %x", hr));
  342. SendStreamEvent(CALL_TERMINAL_FAIL, CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
  343. return hr;
  344. }
  345. // the number of pins should never be 0.
  346. if (dwNumPins == 0)
  347. {
  348. LOG((MSP_ERROR, "terminal has no pins."));
  349. SendStreamEvent(CALL_TERMINAL_FAIL, CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
  350. pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
  351. return E_UNEXPECTED;
  352. }
  353. // Connect the codec filter to the video render terminal.
  354. hr = ::ConnectFilters(
  355. m_pIGraphBuilder,
  356. (IBaseFilter *)m_pEdgeFilter,
  357. (IPin *)Pins[0],
  358. FALSE // use Connect instead of ConnectDirect.
  359. );
  360. // release the refcounts on the pins.
  361. for (DWORD i = 0; i < dwNumPins; i ++)
  362. {
  363. Pins[i]->Release();
  364. }
  365. if (FAILED(hr))
  366. {
  367. LOG((MSP_ERROR, "connect to the codec filter. %x", hr));
  368. pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
  369. return hr;
  370. }
  371. //
  372. // Now we are actually connected. Update our state and perform postconnection
  373. // (ignore postconnection error code).
  374. //
  375. pTerminal->CompleteConnectTerminal();
  376. return hr;
  377. }
  378. HRESULT CStreamVideoRecv::SetUpFilters()
  379. /*++
  380. Routine Description:
  381. Insert filters into the graph and connect to the terminals.
  382. Arguments:
  383. Return Value:
  384. HRESULT.
  385. --*/
  386. {
  387. LOG((MSP_TRACE, "VideoRecv.SetUpFilters"));
  388. // we only support one terminal for this stream.
  389. if (m_Terminals.GetSize() != 1)
  390. {
  391. return E_UNEXPECTED;
  392. }
  393. HRESULT hr;
  394. // Connect the terminal.
  395. if (FAILED(hr = ConnectTerminal(
  396. m_Terminals[0]
  397. )))
  398. {
  399. LOG((MSP_ERROR, "connect the terminal. %x", hr));
  400. return hr;
  401. }
  402. return hr;
  403. }
  404. HRESULT CStreamVideoRecv::HandlePacketReceiveLoss(
  405. IN DWORD dwLossRate
  406. )
  407. {
  408. LOG((MSP_TRACE, "%ls HandlePacketReceiveLoss, lossRate:%d",
  409. m_szName, dwLossRate));
  410. CLock lock(m_lock);
  411. if (m_pMSPCall == NULL)
  412. {
  413. LOG((MSP_WARN, "The call has shut down the stream."));
  414. return S_OK;
  415. }
  416. DWORD dwCurrentTime = timeGetTime();
  417. if (dwLossRate == 0)
  418. {
  419. if (m_dwIFramePending)
  420. {
  421. ((CH323MSPCall*)m_pMSPCall)->SendTSPMessage(
  422. H323MSP_VIDEO_FAST_UPDATE_PICTURE_COMMAND,
  423. (ITStream *)this,
  424. m_htChannel
  425. );
  426. m_dwLastIFrameRequestedTime = dwCurrentTime;
  427. m_dwIFramePending = 0;
  428. }
  429. if (m_dwCurrentBitRate >= m_Settings.Video.dwMaxBitRate)
  430. {
  431. return S_OK;
  432. }
  433. // Adjust the proposed bitrate and
  434. m_dwProposedBitRate += BITRATEINC;
  435. if (m_dwProposedBitRate > m_Settings.Video.dwMaxBitRate)
  436. {
  437. m_dwProposedBitRate = m_Settings.Video.dwMaxBitRate;
  438. }
  439. if ((m_dwProposedBitRate - m_dwCurrentBitRate >= BITRATEDELTA)
  440. || (m_dwProposedBitRate == m_Settings.Video.dwMaxBitRate))
  441. {
  442. m_dwCurrentBitRate = m_dwProposedBitRate;
  443. // Tell the TSP to send a new flow control command.
  444. ((CH323MSPCall*)m_pMSPCall)->SendTSPMessage(
  445. H323MSP_FLOW_CONTROL_COMMAND,
  446. (ITStream *)this,
  447. m_htChannel,
  448. (m_dwMediaType == TAPIMEDIATYPE_AUDIO) ? MEDIA_AUDIO : MEDIA_VIDEO,
  449. 0,
  450. m_dwCurrentBitRate
  451. );
  452. LOG((MSP_INFO, "%ls New bitrate:%d", m_szName, m_dwCurrentBitRate));
  453. }
  454. return S_OK;
  455. }
  456. _ASSERTE(dwLossRate < 100);
  457. m_dwProposedBitRate = (DWORD)(m_dwCurrentBitRate / 100.0 * (100 - dwLossRate));
  458. if (m_dwProposedBitRate < BITRATELOWERLIMIT)
  459. {
  460. // we don't want the bitRate to go too low.
  461. m_dwProposedBitRate = BITRATELOWERLIMIT;
  462. // TODO, if this happens too many times, close the channel.
  463. }
  464. if (m_dwCurrentBitRate - m_dwProposedBitRate >= BITRATEDELTA)
  465. {
  466. m_dwCurrentBitRate = m_dwProposedBitRate;
  467. // Tell the TSP to send a new flow control command.
  468. ((CH323MSPCall*)m_pMSPCall)->SendTSPMessage(
  469. H323MSP_FLOW_CONTROL_COMMAND,
  470. (ITStream *)this,
  471. m_htChannel,
  472. (m_dwMediaType == TAPIMEDIATYPE_AUDIO) ? MEDIA_AUDIO : MEDIA_VIDEO,
  473. 0,
  474. m_dwCurrentBitRate
  475. );
  476. LOG((MSP_INFO, "%ls New bitrate:%d", m_szName, m_dwCurrentBitRate));
  477. }
  478. if (dwCurrentTime - m_dwLastIFrameRequestedTime > IFRAMEINTERVAL)
  479. {
  480. ((CH323MSPCall*)m_pMSPCall)->SendTSPMessage(
  481. H323MSP_VIDEO_FAST_UPDATE_PICTURE_COMMAND,
  482. (ITStream *)this,
  483. m_htChannel
  484. );
  485. m_dwLastIFrameRequestedTime = dwCurrentTime;
  486. m_dwIFramePending = 0;
  487. }
  488. else
  489. {
  490. // Remember that we need to send an I Frame request when time arrives.
  491. m_dwIFramePending = 1;
  492. }
  493. return S_OK;
  494. }
  495. /////////////////////////////////////////////////////////////////////////////
  496. //
  497. // CStreamVideoSend
  498. //
  499. /////////////////////////////////////////////////////////////////////////////
  500. CStreamVideoSend::CStreamVideoSend()
  501. : CH323MSPStream(),
  502. m_pIEncoderFilter(NULL),
  503. m_dwCurrentBitRate(0),
  504. m_dwProposedBitRate(0)
  505. {
  506. m_szName = L"VideoSend";
  507. m_dwIFramePending = FALSE;
  508. m_dwLastIFrameSentTime = timeGetTime();
  509. }
  510. HRESULT CStreamVideoSend::ShutDown()
  511. /*++
  512. Routine Description:
  513. Shut down the stream. Release our members and then calls the base class's
  514. ShutDown method.
  515. Arguments:
  516. Return Value:
  517. S_OK
  518. --*/
  519. {
  520. CLock lock(m_lock);
  521. if (m_pIEncoderFilter)
  522. {
  523. m_pIEncoderFilter->Release();
  524. m_pIEncoderFilter = NULL;
  525. }
  526. return CH323MSPStream::ShutDown();
  527. }
  528. HRESULT CStreamVideoSend::Configure(
  529. IN HANDLE htChannel,
  530. IN STREAMSETTINGS &StreamSettings
  531. )
  532. /*++
  533. Routine Description:
  534. Configure this stream.
  535. Arguments:
  536. StreamSettings - The setting structure got from the SDP blob.
  537. Return Value:
  538. HRESULT.
  539. --*/
  540. {
  541. LOG((MSP_TRACE, "VideoSend.Configure"));
  542. CLock lock(m_lock);
  543. _ASSERTE(m_fIsConfigured == FALSE);
  544. switch (StreamSettings.dwPayloadType)
  545. {
  546. case PAYLOAD_H261:
  547. m_pClsidCodecFilter = &CLSID_H261_ENCODE_FILTER;
  548. m_pRPHInputMinorType = &MEDIASUBTYPE_RTP_Payload_H261;
  549. m_pClsidPHFilter = &CLSID_INTEL_SPHH26X;
  550. break;
  551. case PAYLOAD_H263:
  552. m_pClsidCodecFilter = &CLSID_H263_ENCODE_FILTER;
  553. m_pRPHInputMinorType = &MEDIASUBTYPE_RTP_Payload_H263;
  554. m_pClsidPHFilter = &CLSID_INTEL_SPHH26X;
  555. break;
  556. default:
  557. LOG((MSP_ERROR, "unknow payload type, %x", StreamSettings.dwPayloadType));
  558. return E_FAIL;
  559. }
  560. m_Settings = StreamSettings;
  561. m_htChannel = htChannel;
  562. m_fIsConfigured = TRUE;
  563. m_dwCurrentBitRate = m_Settings.Video.dwStartUpBitRate;
  564. m_dwProposedBitRate = m_dwCurrentBitRate;
  565. InternalConfigure();
  566. return S_OK;
  567. }
  568. HRESULT
  569. SetVideoFormat(
  570. IN IUnknown * pIUnknown,
  571. IN BOOL bCIF,
  572. IN DWORD dwFramesPerSecond
  573. )
  574. /*++
  575. Routine Description:
  576. Set the video format to be CIF or QCIF and also set the frames per second.
  577. Arguments:
  578. pIUnknown - a capture terminal.
  579. bCIF - CIF or QCIF.
  580. dwFramesPerSecond - Frames per second.
  581. Return Value:
  582. HRESULT
  583. --*/
  584. {
  585. LOG((MSP_TRACE, "SetVideoFormat"));
  586. HRESULT hr;
  587. // first get eht IAMStreamConfig interface.
  588. CComPtr<IAMStreamConfig> pIAMStreamConfig;
  589. if (FAILED(hr = pIUnknown->QueryInterface(
  590. IID_IAMStreamConfig,
  591. (void **)&pIAMStreamConfig
  592. )))
  593. {
  594. LOG((MSP_ERROR, "Can't get IAMStreamConfig interface.%8x", hr));
  595. return hr;
  596. }
  597. // get the current format of the video capture terminal.
  598. AM_MEDIA_TYPE *pmt;
  599. if (FAILED(hr = pIAMStreamConfig->GetFormat(&pmt)))
  600. {
  601. LOG((MSP_ERROR, "GetFormat returns error: %8x", hr));
  602. return hr;
  603. }
  604. VIDEOINFO *pVideoInfo = (VIDEOINFO *)pmt->pbFormat;
  605. if (pVideoInfo == NULL)
  606. {
  607. DeleteMediaType(pmt);
  608. return E_UNEXPECTED;
  609. }
  610. BITMAPINFOHEADER *pHeader = HEADER(pmt->pbFormat);
  611. if (pHeader == NULL)
  612. {
  613. DeleteMediaType(pmt);
  614. return E_UNEXPECTED;
  615. }
  616. LOG((MSP_INFO,
  617. "Video capture: Format BitRate: %d, TimePerFrame: %d",
  618. pVideoInfo->dwBitRate,
  619. pVideoInfo->AvgTimePerFrame));
  620. LOG((MSP_INFO, "Video capture: Format Compression:%c%c%c%c %dbit %dx%d",
  621. (DWORD)pHeader->biCompression & 0xff,
  622. ((DWORD)pHeader->biCompression >> 8) & 0xff,
  623. ((DWORD)pHeader->biCompression >> 16) & 0xff,
  624. ((DWORD)pHeader->biCompression >> 24) & 0xff,
  625. pHeader->biBitCount,
  626. pHeader->biWidth,
  627. pHeader->biHeight));
  628. // The time is in 100ns unit.
  629. pVideoInfo->AvgTimePerFrame = (DWORD) 1e7 / dwFramesPerSecond;
  630. if (bCIF)
  631. {
  632. pHeader->biWidth = CIFWIDTH;
  633. pHeader->biHeight = CIFHEIGHT;
  634. }
  635. else
  636. {
  637. pHeader->biWidth = QCIFWIDTH;
  638. pHeader->biHeight = QCIFHEIGHT;
  639. }
  640. #if defined(ALPHA)
  641. // update bmiSize with new Width/Height
  642. pHeader->biSizeImage = DIBSIZE( ((VIDEOINFOHEADER *)pmt->pbFormat)->bmiHeader );
  643. #endif
  644. if (FAILED(hr = pIAMStreamConfig->SetFormat(pmt)))
  645. {
  646. LOG((MSP_ERROR, "putMediaFormat returns error: %8x", hr));
  647. }
  648. else
  649. {
  650. LOG((MSP_INFO,
  651. "Video capture: Format BitRate: %d, TimePerFrame: %d",
  652. pVideoInfo->dwBitRate,
  653. pVideoInfo->AvgTimePerFrame));
  654. LOG((MSP_INFO, "Video capture: Format Compression:%c%c%c%c %dbit %dx%d",
  655. (DWORD)pHeader->biCompression & 0xff,
  656. ((DWORD)pHeader->biCompression >> 8) & 0xff,
  657. ((DWORD)pHeader->biCompression >> 16) & 0xff,
  658. ((DWORD)pHeader->biCompression >> 24) & 0xff,
  659. pHeader->biBitCount,
  660. pHeader->biWidth,
  661. pHeader->biHeight));
  662. }
  663. DeleteMediaType(pmt);
  664. return hr;
  665. }
  666. HRESULT
  667. SetVideoBufferSize(
  668. IN IUnknown *pIUnknown
  669. )
  670. /*++
  671. Routine Description:
  672. Set the video capture terminal's buffersize.
  673. Arguments:
  674. pIUnknown - a capture terminal.
  675. Return Value:
  676. HRESULT
  677. --*/
  678. {
  679. // The number of capture buffers is four for now.
  680. #define NUMCAPTUREBUFFER 4
  681. LOG((MSP_TRACE, "SetVideoBufferSize"));
  682. HRESULT hr;
  683. CComPtr<IAMBufferNegotiation> pBN;
  684. if (FAILED(hr = pIUnknown->QueryInterface(
  685. IID_IAMBufferNegotiation,
  686. (void **)&pBN
  687. )))
  688. {
  689. LOG((MSP_ERROR, "Can't get buffer negotiation interface.%8x", hr));
  690. return hr;
  691. }
  692. ALLOCATOR_PROPERTIES prop;
  693. #if 0 // Get allocator property is not working.
  694. if (FAILED(hr = pBN->GetAllocatorProperties(&prop)))
  695. {
  696. LOG((MSP_ERROR, "GetAllocatorProperties returns error: %8x", hr));
  697. return hr;
  698. }
  699. // Set the number of buffers.
  700. if (prop.cBuffers > NUMCAPTUREBUFFER)
  701. {
  702. prop.cBuffers = NUMCAPTUREBUFFER;
  703. }
  704. #endif
  705. prop.cBuffers = NUMCAPTUREBUFFER;
  706. prop.cbBuffer = -1;
  707. prop.cbAlign = -1;
  708. prop.cbPrefix = -1;
  709. if (FAILED(hr = pBN->SuggestAllocatorProperties(&prop)))
  710. {
  711. LOG((MSP_ERROR, "SuggestAllocatorProperties returns error: %8x", hr));
  712. }
  713. else
  714. {
  715. LOG((MSP_INFO,
  716. "SetVidedobuffersize"
  717. " buffers: %d, buffersize: %d, align: %d, Prefix: %d",
  718. prop.cBuffers,
  719. prop.cbBuffer,
  720. prop.cbAlign,
  721. prop.cbPrefix
  722. ));
  723. }
  724. return hr;
  725. }
  726. HRESULT CStreamVideoSend::ConfigureVideoCaptureTerminal(
  727. IN ITTerminalControl* pTerminal,
  728. OUT IPin ** ppIPin
  729. )
  730. /*++
  731. Routine Description:
  732. Given a terminal, find the capture pin and configure it.
  733. Arguments:
  734. pTerminal - a capture terminal.
  735. ppIPin - the address to store a pointer to a IPin interface.
  736. Return Value:
  737. HRESULT
  738. --*/
  739. {
  740. LOG((MSP_TRACE, "ConfigureVideoCaptureTerminal, pTerminal %x", pTerminal));
  741. const DWORD MAXPINS = 8;
  742. DWORD dwNumPins = MAXPINS;
  743. IPin * Pins[MAXPINS];
  744. HRESULT hr = pTerminal->ConnectTerminal(
  745. m_pIGraphBuilder, 0, &dwNumPins, Pins
  746. );
  747. if (FAILED(hr))
  748. {
  749. LOG((MSP_ERROR, "can't connect to terminal, %x", hr));
  750. return hr;
  751. }
  752. if (dwNumPins == 0)
  753. {
  754. LOG((MSP_ERROR, "terminal has no pins."));
  755. return hr;
  756. }
  757. // Save the first pin and release the others.
  758. CComPtr <IPin> pIPin = Pins[0];
  759. for (DWORD i = 0; i < dwNumPins; i ++)
  760. {
  761. Pins[i]->Release();
  762. }
  763. // set the video format.
  764. hr = SetVideoFormat(
  765. pIPin,
  766. m_Settings.Video.bCIF,
  767. #ifdef TWOFRAMERATES
  768. (m_dwCurrentBitRate > c_SlowLinkSpeed) ? g_dwVideoSampleRateHigh
  769. : g_dwVideoSampleRateLow
  770. #else
  771. g_dwVideoSampleRateHigh
  772. #endif
  773. );
  774. if (FAILED(hr))
  775. {
  776. LOG((MSP_ERROR, "can't set video format, %x", hr));
  777. return hr;
  778. }
  779. // set the video buffer size.
  780. hr = SetVideoBufferSize(
  781. pIPin
  782. );
  783. if (FAILED(hr))
  784. {
  785. LOG((MSP_ERROR, "can't set aduio capture buffer size, %x", hr));
  786. return hr;
  787. }
  788. pIPin->AddRef();
  789. *ppIPin = pIPin;
  790. return hr;
  791. }
  792. HRESULT CStreamVideoSend::FindPreviewInputPin(
  793. IN ITTerminalControl* pTerminal,
  794. OUT IPin ** ppIPin
  795. )
  796. /*++
  797. Routine Description:
  798. Find the input pin on a preview terminal.
  799. Arguments:
  800. pTerminal - a video render terminal.
  801. ppIPin - the address to store a pointer to a IPin interface.
  802. Return Value:
  803. HRESULT
  804. --*/
  805. {
  806. LOG((MSP_TRACE, "VideoSend.FindPreviewInputPin, pTerminal %x", pTerminal));
  807. // Get the pins from the first terminal because we only use on terminal
  808. // on this stream.
  809. const DWORD MAXPINS = 8;
  810. DWORD dwNumPins = MAXPINS;
  811. IPin * Pins[MAXPINS];
  812. HRESULT hr = pTerminal->ConnectTerminal(
  813. m_pIGraphBuilder, 0, &dwNumPins, Pins
  814. );
  815. if (FAILED(hr))
  816. {
  817. LOG((MSP_ERROR, "can't connect to terminal, %x", hr));
  818. return hr;
  819. }
  820. if (dwNumPins == 0)
  821. {
  822. LOG((MSP_ERROR, "terminal has no pins."));
  823. return hr;
  824. }
  825. // Save the first pin and release the others.
  826. CComPtr <IPin> pIPin = Pins[0];
  827. for (DWORD i = 0; i < dwNumPins; i ++)
  828. {
  829. Pins[i]->Release();
  830. }
  831. pIPin->AddRef();
  832. *ppIPin = pIPin;
  833. return hr;
  834. }
  835. HRESULT CStreamVideoSend::CheckTerminalTypeAndDirection(
  836. IN ITTerminal * pTerminal
  837. )
  838. /*++
  839. Routine Description:
  840. Check if the terminal is allowed on this stream.
  841. VideoSend allows both a capture terminal and a preivew terminal.
  842. Arguments:
  843. pTerminal - the terminal.
  844. Return value:
  845. HRESULT.
  846. S_OK means the terminal is OK.
  847. */
  848. {
  849. LOG((MSP_TRACE, "VideoSend.CheckTerminalTypeAndDirection"));
  850. // This stream only support one capture + one preview terminal
  851. if (m_Terminals.GetSize() > 1)
  852. {
  853. return TAPI_E_MAXTERMINALS;
  854. }
  855. // check the media type of this terminal.
  856. long lMediaType;
  857. HRESULT hr = pTerminal->get_MediaType(&lMediaType);
  858. if (FAILED(hr))
  859. {
  860. LOG((MSP_ERROR, "can't get terminal media type. %x", hr));
  861. return TAPI_E_INVALIDTERMINAL;
  862. }
  863. if ((DWORD)lMediaType != m_dwMediaType)
  864. {
  865. return TAPI_E_INVALIDTERMINAL;
  866. }
  867. // check the direction of this terminal.
  868. TERMINAL_DIRECTION Direction;
  869. hr = pTerminal->get_Direction(&Direction);
  870. if (FAILED(hr))
  871. {
  872. LOG((MSP_ERROR, "can't get terminal direction. %x", hr));
  873. return TAPI_E_INVALIDTERMINAL;
  874. }
  875. if (m_Terminals.GetSize() > 0)
  876. {
  877. // check the direction of this terminal.
  878. TERMINAL_DIRECTION Direction2;
  879. hr = m_Terminals[0]->get_Direction(&Direction2);
  880. if (FAILED(hr))
  881. {
  882. LOG((MSP_ERROR, "can't get terminal direction. %x", hr));
  883. return TAPI_E_INVALIDTERMINAL;
  884. }
  885. if (Direction == Direction2)
  886. {
  887. LOG((MSP_ERROR,
  888. "can't have two terminals with the same direction. %x", hr));
  889. return TAPI_E_MAXTERMINALS;
  890. }
  891. }
  892. return S_OK;
  893. }
  894. HRESULT CStreamVideoSend::SetUpFilters()
  895. /*++
  896. Routine Description:
  897. Insert filters into the graph and connect to the terminals.
  898. Arguments:
  899. Return Value:
  900. HRESULT.
  901. --*/
  902. {
  903. LOG((MSP_TRACE, "VideoSend.SetUpFilters"));
  904. // we only support one capture terminal and one preview
  905. // window on this stream.
  906. if (m_Terminals.GetSize() > 2)
  907. {
  908. return E_UNEXPECTED;
  909. }
  910. int iCaptureIndex = -1, iPreviewIndex = -1;
  911. // Find out which terminal is capture and which is preview.
  912. HRESULT hr;
  913. for (int i = 0; i < m_Terminals.GetSize(); i ++)
  914. {
  915. TERMINAL_DIRECTION Direction;
  916. hr = m_Terminals[i]->get_Direction(&Direction);
  917. if (FAILED(hr))
  918. {
  919. LOG((MSP_ERROR, "can't get terminal direction. %x", hr));
  920. SendStreamEvent(CALL_TERMINAL_FAIL, CALL_CAUSE_BAD_DEVICE, hr, m_Terminals[i]);
  921. return hr;
  922. }
  923. if (Direction == TD_CAPTURE)
  924. {
  925. iCaptureIndex = i;
  926. }
  927. else
  928. {
  929. iPreviewIndex = i;
  930. }
  931. }
  932. // the stream will not work without a capture terminal.
  933. if (iCaptureIndex == -1)
  934. {
  935. LOG((MSP_ERROR, "no capture terminal selected."));
  936. return E_UNEXPECTED;
  937. }
  938. // Connect the capture filter to the terminal.
  939. if (FAILED(hr = ConnectTerminal(
  940. m_Terminals[iCaptureIndex]
  941. )))
  942. {
  943. LOG((MSP_ERROR, "connect the codec filter to terminal. %x", hr));
  944. return hr;
  945. }
  946. if (iPreviewIndex != -1)
  947. {
  948. // Connect the preview filter to the terminal.
  949. if (FAILED(hr = ConnectTerminal(
  950. m_Terminals[iPreviewIndex]
  951. )))
  952. {
  953. LOG((MSP_ERROR, "connect the codec filter to terminal. %x", hr));
  954. return hr;
  955. }
  956. }
  957. return hr;
  958. }
  959. HRESULT CStreamVideoSend::ConfigureRTPFilter(
  960. IN IBaseFilter * pIBaseFilter
  961. )
  962. /*++
  963. Routine Description:
  964. Configure the source RTP filter. Including set the address, port, TTL,
  965. QOS, thread priority, clcokrate, etc.
  966. Arguments:
  967. pIBaseFilter - The source RTP Filter.
  968. Return Value:
  969. HRESULT.
  970. --*/
  971. {
  972. LOG((MSP_TRACE, "VideoSend.ConfigureRTPFilter"));
  973. HRESULT hr;
  974. // Get the IRTPStream interface pointer on the filter.
  975. CComQIPtr<IRTPStream, &IID_IRTPStream> pIRTPStream(pIBaseFilter);
  976. if (pIRTPStream == NULL)
  977. {
  978. LOG((MSP_ERROR, "get RTP Stream interface"));
  979. return E_NOINTERFACE;
  980. }
  981. LOG((MSP_INFO, "set locol Address:%x", m_Settings.dwIPLocal));
  982. // Set the local address and port used in the filter.
  983. if (FAILED(hr = pIRTPStream->SelectLocalIPAddress(
  984. htonl(m_Settings.dwIPLocal)
  985. )))
  986. {
  987. LOG((MSP_ERROR, "set locol Address, hr:%x", hr));
  988. return hr;
  989. }
  990. LOG((MSP_INFO, "set remote Address:%x, port:%d, TTL:%d",
  991. m_Settings.dwIPRemote, m_Settings.wRTPPortRemote, DEFAULT_TTL));
  992. // Set the address and port used in the filter.
  993. if (FAILED(hr = pIRTPStream->SetAddress(
  994. 0, // local port.
  995. htons(m_Settings.wRTPPortRemote), // remote port.
  996. htonl(m_Settings.dwIPRemote) // remote IP.
  997. )))
  998. {
  999. LOG((MSP_ERROR, "set remote Address, hr:%x", hr));
  1000. return hr;
  1001. }
  1002. // Get the IRTCPStream interface pointer.
  1003. CComQIPtr<IRTCPStream,
  1004. &IID_IRTCPStream> pIRTCPStream(pIBaseFilter);
  1005. if (pIRTCPStream == NULL)
  1006. {
  1007. LOG((MSP_ERROR, "get RTCP Stream interface"));
  1008. return E_NOINTERFACE;
  1009. }
  1010. LOG((MSP_INFO, "set remote RTCP Address:%x, port:%d, local port:%d",
  1011. m_Settings.dwIPRemote, m_Settings.wRTCPPortRemote,
  1012. m_Settings.wRTCPPortLocal));
  1013. // Set the remote RTCP address and port.
  1014. if (FAILED(hr = pIRTCPStream->SetRTCPAddress(
  1015. htons(m_Settings.wRTCPPortLocal),
  1016. htons(m_Settings.wRTCPPortRemote),
  1017. htonl(m_Settings.dwIPRemote)
  1018. )))
  1019. {
  1020. LOG((MSP_ERROR, "set remote RTCP Address, hr:%x", hr));
  1021. return hr;
  1022. }
  1023. // Set the TTL used in the filter.
  1024. if (FAILED(hr = pIRTPStream->SetMulticastScope(DEFAULT_TTL)))
  1025. {
  1026. LOG((MSP_ERROR, "set TTL. %x", hr));
  1027. return hr;
  1028. }
  1029. // Set the priority of the session
  1030. if (FAILED(hr = pIRTPStream->SetSessionClassPriority(
  1031. RTP_CLASS_VIDEO,
  1032. g_dwVideoThreadPriority
  1033. )))
  1034. {
  1035. LOG((MSP_ERROR, "set session class and priority. %x", hr));
  1036. }
  1037. // Set the sample rate of the session
  1038. #ifdef TWOFRAMERATES
  1039. LOG((MSP_INFO, "setting session sample rate to %d",
  1040. (m_dwCurrentBitRate > c_SlowLinkSpeed) ? g_dwVideoSampleRateHigh
  1041. : g_dwVideoSampleRateLow
  1042. ));
  1043. #else
  1044. LOG((MSP_INFO, "setting session sample rate to %d",
  1045. g_dwVideoSampleRateHigh
  1046. ));
  1047. #endif
  1048. #ifdef TWOFRAMERATES
  1049. if (FAILED(hr = pIRTPStream->SetDataClock(
  1050. (m_dwCurrentBitRate > c_SlowLinkSpeed) ? g_dwVideoSampleRateHigh
  1051. : g_dwVideoSampleRateLow
  1052. )))
  1053. #else
  1054. if (FAILED(hr = pIRTPStream->SetDataClock(
  1055. g_dwVideoSampleRateHigh
  1056. )))
  1057. #endif
  1058. {
  1059. LOG((MSP_ERROR, "set session sample rate. %x", hr));
  1060. }
  1061. // Enable the RTCP events
  1062. if (FAILED(hr = ::EnableRTCPEvents(pIBaseFilter)))
  1063. {
  1064. LOG((MSP_WARN, "can not enable RTCP events %x", hr));
  1065. }
  1066. if (FAILED(hr = ::SetQOSOption(
  1067. pIBaseFilter,
  1068. m_Settings.dwPayloadType, // payload
  1069. m_Settings.Video.dwMaxBitRate,
  1070. FALSE
  1071. )))
  1072. {
  1073. LOG((MSP_ERROR, "set QOS option. %x", hr));
  1074. return hr;
  1075. }
  1076. return S_OK;
  1077. }
  1078. HRESULT CStreamVideoSend::ConnectTerminal(
  1079. IN ITTerminal * pITTerminal
  1080. )
  1081. /*++
  1082. Routine Description:
  1083. connect the video terminals to the stream.
  1084. Arguments:
  1085. Return Value:
  1086. HRESULT.
  1087. --*/
  1088. {
  1089. LOG((MSP_TRACE, "VideoSend.ConnectTerminal %x", pITTerminal));
  1090. // Get the TerminalControl interface on the terminal
  1091. CComQIPtr<ITTerminalControl, &IID_ITTerminalControl>
  1092. pTerminal(pITTerminal);
  1093. if (pTerminal == NULL)
  1094. {
  1095. LOG((MSP_ERROR, "can't get Terminal Control interface"));
  1096. SendStreamEvent(CALL_TERMINAL_FAIL,
  1097. CALL_CAUSE_BAD_DEVICE, E_NOINTERFACE, pITTerminal);
  1098. return E_NOINTERFACE;
  1099. }
  1100. // Find out the direction of the terminal.
  1101. TERMINAL_DIRECTION Direction;
  1102. HRESULT hr = pITTerminal->get_Direction(&Direction);
  1103. if (FAILED(hr))
  1104. {
  1105. LOG((MSP_ERROR, "can't get terminal direction. %x", hr));
  1106. SendStreamEvent(CALL_TERMINAL_FAIL,
  1107. CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
  1108. return hr;
  1109. }
  1110. if (Direction == TD_CAPTURE)
  1111. {
  1112. // find the capture pin on the capture terminal and configure it.
  1113. CComPtr<IPin> pCapturePin;
  1114. hr = ConfigureVideoCaptureTerminal(pTerminal, &pCapturePin);
  1115. if (FAILED(hr))
  1116. {
  1117. LOG((MSP_ERROR, "configure video capture termianl failed. %x", hr));
  1118. SendStreamEvent(CALL_TERMINAL_FAIL,
  1119. CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
  1120. return hr;
  1121. }
  1122. hr = CreateSendFilters(pCapturePin);
  1123. if (FAILED(hr))
  1124. {
  1125. LOG((MSP_ERROR, "Create video send filters failed. %x", hr));
  1126. // disconnect the terminal.
  1127. pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
  1128. // clean up internal filters as well.
  1129. CleanUpFilters();
  1130. return hr;
  1131. }
  1132. //
  1133. // Now we are actually connected. Update our state and perform postconnection
  1134. // (ignore postconnection error code).
  1135. //
  1136. pTerminal->CompleteConnectTerminal();
  1137. }
  1138. else
  1139. {
  1140. // find the input pin on the preview window. If there is no preview window,
  1141. // we just pass in NULL for the next function.
  1142. CComPtr<IPin> pPreviewInputPin;
  1143. hr = FindPreviewInputPin(pTerminal, &pPreviewInputPin);
  1144. if (FAILED(hr))
  1145. {
  1146. LOG((MSP_ERROR, "find preview input pin failed. %x", hr));
  1147. SendStreamEvent(CALL_TERMINAL_FAIL, CALL_CAUSE_BAD_DEVICE, hr, pITTerminal);
  1148. return hr;
  1149. }
  1150. hr = ConnectPreview(pPreviewInputPin);
  1151. if (FAILED(hr))
  1152. {
  1153. LOG((MSP_ERROR, "Create video send filters failed. %x", hr));
  1154. pTerminal->DisconnectTerminal(m_pIGraphBuilder, 0);
  1155. return hr;
  1156. }
  1157. //
  1158. // Now we are actually connected. Update our state and perform postconnection
  1159. // (ignore postconnection error code).
  1160. //
  1161. pTerminal->CompleteConnectTerminal();
  1162. }
  1163. return hr;
  1164. }
  1165. HRESULT
  1166. EncoderDoCommand(
  1167. IN IBaseFilter * pIFilter,
  1168. IN ENCODERCOMMAND command,
  1169. IN DWORD dwParam1
  1170. )
  1171. /*++
  1172. Routine Description:
  1173. Set the video capture terminal's buffersize.
  1174. Arguments:
  1175. pIFilter - a H26x encoder.
  1176. command - the command needs to be performed on the encoder.
  1177. dwparam1 - the parameter.
  1178. Return Value:
  1179. HRESULT
  1180. --*/
  1181. {
  1182. LOG((MSP_TRACE, "EncoderDoCommand, command:%d, param1: %d",
  1183. command, dwParam1));
  1184. HRESULT hr;
  1185. CComPtr<IH26XEncoderControl> pIH26XEncoderControl;
  1186. if (FAILED(hr = pIFilter->QueryInterface(
  1187. IID_IH26XEncoderControl,
  1188. (void **)&pIH26XEncoderControl
  1189. )))
  1190. {
  1191. LOG((MSP_ERROR, "Can't get pIH26XEncoderControl interface.%8x", hr));
  1192. return hr;
  1193. }
  1194. // get the current encoder properties of the video capture terminal.
  1195. ENC_CMP_DATA prop;
  1196. if (FAILED(hr = pIH26XEncoderControl->get_EncodeCompression(&prop)))
  1197. {
  1198. LOG((MSP_ERROR, "get_EncodeCompression returns error: %8x", hr));
  1199. return hr;
  1200. }
  1201. LOG((MSP_INFO,
  1202. "Video encoder::get_EncodeCompression"
  1203. " FrameRate: %d, DataRate: %d, Width %d, bSendKey: %s, interval: %d, Quality: %d",
  1204. prop.dwFrameRate,
  1205. prop.dwDataRate,
  1206. prop.dwWidth,
  1207. prop.bSendKey ? "TRUE" : "FALSE",
  1208. prop.dwKeyFrameInterval,
  1209. prop.dwQuality
  1210. ));
  1211. switch (command)
  1212. {
  1213. case EC_BITRATE:
  1214. prop.bFrameSizeBRC = FALSE; // control bit rate
  1215. prop.dwDataRate = dwParam1 / 1000; // in kbps
  1216. break;
  1217. case EC_IFRAME:
  1218. prop.bSendKey = TRUE;
  1219. break;
  1220. }
  1221. if (FAILED(hr = pIH26XEncoderControl->set_EncodeCompression(&prop)))
  1222. {
  1223. LOG((MSP_ERROR, "set_EncodeCompression returns error: %8x", hr));
  1224. }
  1225. else
  1226. {
  1227. LOG((MSP_INFO,
  1228. "Video encoder::set_EncodeCompression"
  1229. " FrameRate: %d, DataRate: %d, Width %d, bSendKey: %s, interval: %d, Quality: %d",
  1230. prop.dwFrameRate,
  1231. prop.dwDataRate,
  1232. prop.dwWidth,
  1233. prop.bSendKey ? "TRUE" : "FALSE",
  1234. prop.dwKeyFrameInterval,
  1235. prop.dwQuality
  1236. ));
  1237. }
  1238. return hr;
  1239. }
  1240. HRESULT
  1241. ConfigureEncoder(
  1242. IN IBaseFilter * pIFilter,
  1243. IN BOOL bCIF,
  1244. IN DWORD dwMaxBitRate,
  1245. IN DWORD dwFramesPerSecond
  1246. )
  1247. /*++
  1248. Routine Description:
  1249. Set the video capture terminal's buffersize.
  1250. Arguments:
  1251. pIFilter - a H26x encoder.
  1252. bCIF - CIF or QCIF.
  1253. pdwFramesPerSecond - Frames per second.
  1254. dwKeyFrameInterval - The number of frames before sending a key frame.
  1255. Return Value:
  1256. HRESULT
  1257. --*/
  1258. {
  1259. LOG((MSP_TRACE, "ConfigureEncoder, dwMaxBitRate :%d", dwMaxBitRate));
  1260. HRESULT hr;
  1261. CComPtr<IH26XEncoderControl> pIH26XEncoderControl;
  1262. if (FAILED(hr = pIFilter->QueryInterface(
  1263. IID_IH26XEncoderControl,
  1264. (void **)&pIH26XEncoderControl
  1265. )))
  1266. {
  1267. LOG((MSP_ERROR, "Can't get pIH26XEncoderControl interface.%8x", hr));
  1268. return hr;
  1269. }
  1270. // get the current encoder properties of the video capture terminal.
  1271. ENC_CMP_DATA prop;
  1272. if (FAILED(hr = pIH26XEncoderControl->get_EncodeCompression(&prop)))
  1273. {
  1274. LOG((MSP_ERROR, "get_EncodeCompression returns error: %8x", hr));
  1275. return hr;
  1276. }
  1277. LOG((MSP_INFO,
  1278. "Video encoder::get_EncodeCompression"
  1279. " FrameRate: %d, DataRate: %d, Width %d, bSendKey: %s, interval: %d, Quality: %d",
  1280. prop.dwFrameRate,
  1281. prop.dwDataRate,
  1282. prop.dwWidth,
  1283. prop.bSendKey ? "TRUE" : "FALSE",
  1284. prop.dwKeyFrameInterval,
  1285. prop.dwQuality
  1286. ));
  1287. prop.bSendKey = TRUE;
  1288. prop.dwFrameRate = dwFramesPerSecond;
  1289. prop.bFrameSizeBRC = FALSE; // control bit rate
  1290. prop.dwQuality = 8500;
  1291. prop.dwDataRate = dwMaxBitRate / 1000; // in kbps
  1292. prop.dwKeyFrameInterval = 9999999; // don't send Keyframes.
  1293. if (bCIF)
  1294. {
  1295. prop.dwWidth = CIFWIDTH;
  1296. }
  1297. else
  1298. {
  1299. prop.dwWidth = QCIFWIDTH;
  1300. }
  1301. if (FAILED(hr = pIH26XEncoderControl->set_EncodeCompression(&prop)))
  1302. {
  1303. LOG((MSP_ERROR, "set_EncodeCompression returns error: %8x", hr));
  1304. }
  1305. else
  1306. {
  1307. LOG((MSP_INFO,
  1308. "Video encoder::set_EncodeCompression"
  1309. " FrameRate: %d, DataRate: %d, Width %d, bSendKey: %s, interval: %d, Quality: %d",
  1310. prop.dwFrameRate,
  1311. prop.dwDataRate,
  1312. prop.dwWidth,
  1313. prop.bSendKey ? "TRUE" : "FALSE",
  1314. prop.dwKeyFrameInterval,
  1315. prop.dwQuality
  1316. ));
  1317. }
  1318. return hr;
  1319. }
  1320. HRESULT CStreamVideoSend::ConnectPreview(
  1321. IN IPin *pPreviewInputPin
  1322. )
  1323. /*++
  1324. Routine Description:
  1325. connect the preview pin the the TEE filter.
  1326. Capturepin->TEE+->Encoder->SPH->RTPRender
  1327. +->PreviewInputPin
  1328. Arguments:
  1329. pPin - The output pin on the capture filter.
  1330. Return Value:
  1331. HRESULT.
  1332. --*/
  1333. {
  1334. HRESULT hr;
  1335. if (m_pEdgeFilter == NULL)
  1336. {
  1337. LOG((MSP_ERROR, "no capture to preview"));
  1338. return E_UNEXPECTED;
  1339. }
  1340. // Create the AVI decompressor filter and add it into the graph.
  1341. // This will make the graph construction faster for since the AVI
  1342. // decompressor are always needed for the preview
  1343. CComPtr<IBaseFilter> pAviFilter;
  1344. if (FAILED(hr = ::AddFilter(
  1345. m_pIGraphBuilder,
  1346. CLSID_AVIDec,
  1347. L"Avi",
  1348. &pAviFilter)))
  1349. {
  1350. LOG((MSP_ERROR, "add Avi filter. %x", hr));
  1351. return hr;
  1352. }
  1353. // connect the preview input pin with the smart tee filter.
  1354. if (FAILED(hr = ::ConnectFilters(
  1355. m_pIGraphBuilder,
  1356. (IBaseFilter *)m_pEdgeFilter,
  1357. (IPin *)pPreviewInputPin,
  1358. FALSE // not direct connect
  1359. )))
  1360. {
  1361. LOG((MSP_ERROR, "connect preview input pin with the tee. %x", hr));
  1362. return hr;
  1363. }
  1364. return hr;
  1365. }
  1366. HRESULT CStreamVideoSend::CreateSendFilters(
  1367. IN IPin *pCapturePin
  1368. )
  1369. /*++
  1370. Routine Description:
  1371. Insert filters into the graph and connect to the capture pin.
  1372. Capturepin->TEE+->Encoder->SPH->RTPRender
  1373. Arguments:
  1374. pPin - The output pin on the capture filter.
  1375. Return Value:
  1376. HRESULT.
  1377. --*/
  1378. {
  1379. HRESULT hr;
  1380. if (m_pEdgeFilter)
  1381. {
  1382. // connect the capture pin with the smart tee filter.
  1383. if (FAILED(hr = ::ConnectFilters(
  1384. m_pIGraphBuilder,
  1385. (IPin *)pCapturePin,
  1386. (IBaseFilter *)m_pEdgeFilter
  1387. )))
  1388. {
  1389. LOG((MSP_ERROR, "connect capture pin with the tee. %x", hr));
  1390. return hr;
  1391. }
  1392. return hr;
  1393. }
  1394. // Create the tee filter and add it into the graph.
  1395. CComPtr<IBaseFilter> pTeeFilter;
  1396. if (FAILED(hr = ::AddFilter(
  1397. m_pIGraphBuilder,
  1398. CLSID_SmartTee,
  1399. // CLSID_InfTee,
  1400. L"tee",
  1401. &pTeeFilter)))
  1402. {
  1403. LOG((MSP_ERROR, "add smart tee filter. %x", hr));
  1404. return hr;
  1405. }
  1406. // connect the capture pin with the tee filter.
  1407. if (FAILED(hr = ::ConnectFilters(
  1408. m_pIGraphBuilder,
  1409. (IPin *)pCapturePin,
  1410. (IBaseFilter *)pTeeFilter
  1411. )))
  1412. {
  1413. LOG((MSP_ERROR, "connect capture pin with the tee. %x", hr));
  1414. return hr;
  1415. }
  1416. // Create the codec filter and add it into the graph.
  1417. CComPtr<IBaseFilter> pCodecFilter;
  1418. if (FAILED(hr = ::AddFilter(
  1419. m_pIGraphBuilder,
  1420. *m_pClsidCodecFilter,
  1421. L"Encoder",
  1422. &pCodecFilter)))
  1423. {
  1424. LOG((MSP_ERROR, "add Codec filter. %x", hr));
  1425. return hr;
  1426. }
  1427. // configure the encoder
  1428. #ifdef TWOFRAMERATES
  1429. if (FAILED(hr = ::ConfigureEncoder(
  1430. pCodecFilter,
  1431. m_Settings.Video.bCIF,
  1432. m_dwCurrentBitRate,
  1433. (m_dwCurrentBitRate > c_SlowLinkSpeed) ? g_dwVideoSampleRateHigh
  1434. : g_dwVideoSampleRateLow
  1435. )))
  1436. #else
  1437. if (FAILED(hr = ::ConfigureEncoder(
  1438. pCodecFilter,
  1439. m_Settings.Video.bCIF,
  1440. m_dwCurrentBitRate,
  1441. g_dwVideoSampleRateHigh
  1442. )))
  1443. #endif
  1444. {
  1445. LOG((MSP_WARN, "Configure video encoder. %x", hr));
  1446. }
  1447. // connect the smart tee filter and the Codec filter.
  1448. if (FAILED(hr = ::ConnectFilters(
  1449. m_pIGraphBuilder,
  1450. (IBaseFilter *)pTeeFilter,
  1451. (IBaseFilter *)pCodecFilter
  1452. )))
  1453. {
  1454. LOG((MSP_ERROR, "connect Tee filter and codec filter. %x", hr));
  1455. return hr;
  1456. }
  1457. // Create the send payload handler and add it into the graph.
  1458. CComPtr<IBaseFilter> pISPHFilter;
  1459. if (FAILED(hr = ::AddFilter(
  1460. m_pIGraphBuilder,
  1461. *m_pClsidPHFilter,
  1462. L"SPH",
  1463. &pISPHFilter
  1464. )))
  1465. {
  1466. LOG((MSP_ERROR, "add SPH filter. %x", hr));
  1467. return hr;
  1468. }
  1469. // Connect the Codec filter with the SPH filter .
  1470. if (FAILED(hr = ::ConnectFilters(
  1471. m_pIGraphBuilder,
  1472. (IBaseFilter *)pCodecFilter,
  1473. (IBaseFilter *)pISPHFilter
  1474. )))
  1475. {
  1476. LOG((MSP_ERROR, "connect codec filter and SPH filter. %x", hr));
  1477. return hr;
  1478. }
  1479. // Get the IRTPSPHFilter interface.
  1480. CComQIPtr<IRTPSPHFilter,
  1481. &IID_IRTPSPHFilter> pIRTPSPHFilter(pISPHFilter);
  1482. if (pIRTPSPHFilter == NULL)
  1483. {
  1484. LOG((MSP_ERROR, "get IRTPSPHFilter interface"));
  1485. return E_NOINTERFACE;
  1486. }
  1487. // Create the RTP render filter and add it into the graph.
  1488. CComPtr<IBaseFilter> pRenderFilter;
  1489. if (FAILED(hr = ::AddFilter(
  1490. m_pIGraphBuilder,
  1491. CLSID_RTPRenderFilter,
  1492. L"RtpRender",
  1493. &pRenderFilter)))
  1494. {
  1495. LOG((MSP_ERROR, "adding render filter. %x", hr));
  1496. return hr;
  1497. }
  1498. // Set the address for the render fitler.
  1499. if (FAILED(hr = ConfigureRTPFilter(pRenderFilter)))
  1500. {
  1501. LOG((MSP_ERROR, "configure RTP Filter failed %x", hr));
  1502. return hr;
  1503. }
  1504. // Connect the SPH filter with the RTP Render filter.
  1505. if (FAILED(hr = ::ConnectFilters(
  1506. m_pIGraphBuilder,
  1507. (IBaseFilter *)pISPHFilter,
  1508. (IBaseFilter *)pRenderFilter
  1509. )))
  1510. {
  1511. LOG((MSP_ERROR, "connect SPH filter and Render filter. %x", hr));
  1512. return hr;
  1513. }
  1514. // remember the first filter after the terminal
  1515. m_pEdgeFilter = pTeeFilter;
  1516. m_pEdgeFilter->AddRef();
  1517. m_pIEncoderFilter = pCodecFilter;
  1518. m_pIEncoderFilter->AddRef();
  1519. return S_OK;
  1520. }
  1521. HRESULT CStreamVideoSend::SendIFrame()
  1522. /*++
  1523. Routine Description:
  1524. Now we got a I Frame request from the TSP. Use the encoder filter to
  1525. generate a I frame.
  1526. Arguments:
  1527. Return Value:
  1528. HRESULT.
  1529. --*/
  1530. {
  1531. CLock lock(m_lock);
  1532. if (!m_pIEncoderFilter)
  1533. {
  1534. return E_UNEXPECTED;
  1535. }
  1536. DWORD dwCurrentTime = timeGetTime();
  1537. if (dwCurrentTime - m_dwLastIFrameSentTime > IFRAMEINTERVAL)
  1538. {
  1539. // this function always succeeds.
  1540. EncoderDoCommand(m_pIEncoderFilter, EC_IFRAME, 0);
  1541. m_dwLastIFrameSentTime = dwCurrentTime;
  1542. m_dwIFramePending = 0;
  1543. }
  1544. else
  1545. {
  1546. m_dwIFramePending = 1;
  1547. }
  1548. return S_OK;
  1549. }
  1550. HRESULT CStreamVideoSend::ChangeMaxBitRate(
  1551. IN DWORD dwMaxBitRate
  1552. )
  1553. /*++
  1554. Routine Description:
  1555. The receiver set the max bit-rate to a new value.
  1556. Arguments:
  1557. dwMaxBitRate - the new max bit rate requested by the other endpoint.
  1558. Return Value:
  1559. S_OK;
  1560. --*/
  1561. {
  1562. LOG((MSP_INFO, "new Max bitrate: %d", dwMaxBitRate));
  1563. CLock lock(m_lock);
  1564. m_Settings.Video.dwMaxBitRate = dwMaxBitRate;
  1565. if (m_dwCurrentBitRate > m_Settings.Video.dwMaxBitRate)
  1566. {
  1567. m_dwCurrentBitRate = m_Settings.Video.dwMaxBitRate;
  1568. if (m_pIEncoderFilter)
  1569. {
  1570. // this function always succeeds.
  1571. EncoderDoCommand(
  1572. m_pIEncoderFilter, EC_BITRATE, m_dwCurrentBitRate
  1573. );
  1574. }
  1575. }
  1576. return S_OK;
  1577. }
  1578. HRESULT CStreamVideoSend::HandlePacketTransmitLoss(
  1579. IN DWORD dwLossRate
  1580. )
  1581. /*++
  1582. Routine Description:
  1583. .
  1584. Arguments:
  1585. dwMaxBitRate - the new max bit rate requested by the other endpoint.
  1586. Return Value:
  1587. S_OK;
  1588. --*/
  1589. {
  1590. LOG((MSP_TRACE, "%ls HandlePacketTransmitLoss, lossRate:%d",
  1591. m_szName, dwLossRate));
  1592. CLock lock(m_lock);
  1593. if (m_pMSPCall == NULL)
  1594. {
  1595. LOG((MSP_WARN, "The call has shut down the stream."));
  1596. return S_OK;
  1597. }
  1598. DWORD dwCurrentTime = timeGetTime();
  1599. if (dwLossRate == 0)
  1600. {
  1601. if (m_dwIFramePending)
  1602. {
  1603. // this function always succeeds.
  1604. EncoderDoCommand(m_pIEncoderFilter, EC_IFRAME, 0);
  1605. m_dwLastIFrameSentTime = dwCurrentTime;
  1606. m_dwIFramePending = 0;
  1607. }
  1608. if (m_dwCurrentBitRate >= m_Settings.Video.dwMaxBitRate)
  1609. {
  1610. return S_OK;
  1611. }
  1612. // Adjust the proposed bitrate and
  1613. m_dwProposedBitRate += BITRATEINC;
  1614. if (m_dwProposedBitRate > m_Settings.Video.dwMaxBitRate)
  1615. {
  1616. m_dwProposedBitRate = m_Settings.Video.dwMaxBitRate;
  1617. }
  1618. if ((m_dwProposedBitRate - m_dwCurrentBitRate >= BITRATEDELTA)
  1619. || (m_dwProposedBitRate == m_Settings.Video.dwMaxBitRate))
  1620. {
  1621. m_dwCurrentBitRate = m_dwProposedBitRate;
  1622. // this function always succeeds.
  1623. EncoderDoCommand(
  1624. m_pIEncoderFilter, EC_BITRATE, m_dwCurrentBitRate
  1625. );
  1626. }
  1627. return S_OK;
  1628. }
  1629. _ASSERTE(dwLossRate < 100);
  1630. m_dwProposedBitRate = (DWORD)(m_dwCurrentBitRate / 100.0 * (100 - dwLossRate));
  1631. if (m_dwProposedBitRate < BITRATELOWERLIMIT)
  1632. {
  1633. // we don't want the bitRate to go too low.
  1634. m_dwProposedBitRate = BITRATELOWERLIMIT;
  1635. // TODO, if this happens too many times, close the channel.
  1636. }
  1637. if (m_dwCurrentBitRate - m_dwProposedBitRate >= BITRATEDELTA)
  1638. {
  1639. m_dwCurrentBitRate = m_dwProposedBitRate;
  1640. // this function always succeeds.
  1641. EncoderDoCommand(
  1642. m_pIEncoderFilter, EC_BITRATE, m_dwCurrentBitRate
  1643. );
  1644. }
  1645. if (dwCurrentTime - m_dwLastIFrameSentTime > IFRAMEINTERVAL)
  1646. {
  1647. // this function always succeeds.
  1648. EncoderDoCommand(
  1649. m_pIEncoderFilter, EC_IFRAME, 0
  1650. );
  1651. m_dwLastIFrameSentTime = dwCurrentTime;
  1652. m_dwIFramePending = 0;
  1653. }
  1654. else
  1655. {
  1656. // Remember that we need to send an I Frame when time arrives.
  1657. m_dwIFramePending = 1;
  1658. }
  1659. return S_OK;
  1660. }