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.

2341 lines
55 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. confcall.cpp
  5. Abstract:
  6. This module contains implementation of CIPConfMSPCall.
  7. Author:
  8. Mu Han (muhan) 5-September-1998
  9. --*/
  10. #include "stdafx.h"
  11. #include "common.h"
  12. #include <confpdu.h>
  13. CIPConfMSPCall::CIPConfMSPCall()
  14. : m_fLocalInfoRetrieved(FALSE)
  15. {
  16. ZeroMemory(m_InfoItems, sizeof(m_InfoItems));
  17. }
  18. STDMETHODIMP CIPConfMSPCall::CreateStream(
  19. IN long lMediaType,
  20. IN TERMINAL_DIRECTION Direction,
  21. IN OUT ITStream ** ppStream
  22. )
  23. {
  24. // This MSP doesn't support creating new streams on the fly.
  25. return TAPI_E_NOTSUPPORTED;
  26. }
  27. STDMETHODIMP CIPConfMSPCall::RemoveStream(
  28. IN ITStream * pStream
  29. )
  30. {
  31. // This MSP doesn't support removing streams either.
  32. return TAPI_E_NOTSUPPORTED;
  33. }
  34. HRESULT CIPConfMSPCall::InitializeLocalParticipant()
  35. /*++
  36. Routine Description:
  37. This function uses the RTP filter to find out the local information that
  38. will be used in the call. The infomation is stored in a local participant
  39. object.
  40. Arguments:
  41. Return Value:
  42. HRESULT.
  43. --*/
  44. {
  45. m_fLocalInfoRetrieved = TRUE;
  46. // Create the RTP fitler.
  47. IRTCPStream *pIRTCPStream;
  48. HRESULT hr = CoCreateInstance(
  49. CLSID_RTPSourceFilter,
  50. NULL,
  51. CLSCTX_INPROC_SERVER,
  52. IID_IRTCPStream,
  53. (void **) &pIRTCPStream
  54. );
  55. if (FAILED(hr))
  56. {
  57. LOG((MSP_ERROR, "can't create RTP filter for local info. %x", hr));
  58. return hr;
  59. }
  60. // Get the available local SDES info from the filter.
  61. char Buffer[MAX_PARTICIPANT_TYPED_INFO_LENGTH];
  62. DWORD dwLen = MAX_PARTICIPANT_TYPED_INFO_LENGTH;
  63. for (int i = 0; i < RTCP_SDES_LAST - 1; i ++)
  64. {
  65. if (Buffer == NULL)
  66. {
  67. pIRTCPStream->Release();
  68. return E_OUTOFMEMORY;
  69. }
  70. hr = pIRTCPStream->GetLocalSDESItem(
  71. RTCP_SDES_CNAME + i,
  72. (BYTE*)Buffer,
  73. &dwLen
  74. );
  75. if (SUCCEEDED(hr) && dwLen > 0)
  76. {
  77. if (dwLen > MAX_PARTICIPANT_TYPED_INFO_LENGTH)
  78. {
  79. dwLen = MAX_PARTICIPANT_TYPED_INFO_LENGTH;
  80. }
  81. // allocate memory to store the string.
  82. m_InfoItems[i] = (WCHAR *)malloc(dwLen * sizeof(WCHAR));
  83. if (m_InfoItems[i] == NULL)
  84. {
  85. LOG((MSP_ERROR, "out of mem for local info"));
  86. pIRTCPStream->Release();
  87. return E_OUTOFMEMORY;
  88. }
  89. // conver the char string to WCHAR string.
  90. if (!MultiByteToWideChar(
  91. GetACP(),
  92. 0,
  93. Buffer,
  94. dwLen,
  95. m_InfoItems[i],
  96. dwLen
  97. ))
  98. {
  99. LOG((MSP_ERROR, "coverting failed, error:%x", GetLastError()));
  100. free(m_InfoItems[i]);
  101. m_InfoItems[i] = NULL;
  102. pIRTCPStream->Release();
  103. return E_FAIL;
  104. }
  105. }
  106. }
  107. pIRTCPStream->Release();
  108. return S_OK;
  109. }
  110. HRESULT CIPConfMSPCall::Init(
  111. IN CMSPAddress * pMSPAddress,
  112. IN MSP_HANDLE htCall,
  113. IN DWORD dwReserved,
  114. IN DWORD dwMediaType
  115. )
  116. /*++
  117. Routine Description:
  118. This method is called when the call is first created. It sets
  119. up the streams based on the mediatype specified.
  120. Arguments:
  121. pMSPAddress - The pointer to the address object.
  122. htCall - The handle to the Call in TAPI's space.
  123. Used in sending events.
  124. dwReserved - Reserved.
  125. dwMediaType - The media type of this call.
  126. Return Value:
  127. HRESULT.
  128. --*/
  129. {
  130. LOG((MSP_TRACE,
  131. "IPConfMSP call %x initialize entered,"
  132. " pMSPAddress:%x, htCall %x, dwMediaType %x",
  133. this, pMSPAddress, htCall, dwMediaType
  134. ));
  135. #ifdef DEBUG_REFCOUNT
  136. if (g_lStreamObjects != 0)
  137. {
  138. LOG((MSP_ERROR, "Number of Streams alive: %d", g_lStreamObjects));
  139. DebugBreak();
  140. }
  141. #endif
  142. // initialize the participant array so that the array is not NULL.
  143. if (!m_Participants.Grow())
  144. {
  145. LOG((MSP_ERROR, "out of mem for participant list"));
  146. return E_OUTOFMEMORY;
  147. }
  148. // Call the base class's init.
  149. HRESULT hr= CMSPCallMultiGraph::Init(
  150. pMSPAddress,
  151. htCall,
  152. dwReserved,
  153. dwMediaType
  154. );
  155. if (FAILED(hr))
  156. {
  157. LOG((MSP_ERROR, "MSPCallMultiGraph init failed:%x", hr));
  158. return hr;
  159. }
  160. // create streams based on the media types.
  161. if (dwMediaType & TAPIMEDIATYPE_AUDIO)
  162. {
  163. ITStream * pStream;
  164. // create a stream object.
  165. hr = InternalCreateStream(TAPIMEDIATYPE_AUDIO, TD_RENDER, &pStream);
  166. if (FAILED(hr))
  167. {
  168. LOG((MSP_ERROR, "create audio render stream failed:%x", hr));
  169. return hr;
  170. }
  171. // The stream is already in our array, we don't need this pointer.
  172. pStream->Release();
  173. // create a stream object.
  174. hr = InternalCreateStream(TAPIMEDIATYPE_AUDIO, TD_CAPTURE, &pStream);
  175. if (FAILED(hr))
  176. {
  177. LOG((MSP_ERROR, "create audio capture stream failed:%x", hr));
  178. return hr;
  179. }
  180. // The stream is already in our array, we don't need this pointer.
  181. pStream->Release();
  182. }
  183. if (dwMediaType & TAPIMEDIATYPE_VIDEO)
  184. {
  185. ITStream * pStream;
  186. // create a stream object.
  187. hr = InternalCreateStream(TAPIMEDIATYPE_VIDEO, TD_RENDER, &pStream);
  188. if (FAILED(hr))
  189. {
  190. LOG((MSP_ERROR, "create video render stream failed:%x", hr));
  191. return hr;
  192. }
  193. // The stream is already in our array, we don't need this pointer.
  194. pStream->Release();
  195. // create a stream object.
  196. hr = InternalCreateStream(TAPIMEDIATYPE_VIDEO, TD_CAPTURE, &pStream);
  197. if (FAILED(hr))
  198. {
  199. LOG((MSP_ERROR, "create video capture stream failed:%x", hr));
  200. return hr;
  201. }
  202. // The stream is already in our array, we don't need this pointer.
  203. pStream->Release();
  204. }
  205. m_fShutDown = FALSE;
  206. return S_OK;
  207. }
  208. HRESULT CIPConfMSPCall::ShutDown()
  209. /*++
  210. Routine Description:
  211. Shutdown the call.
  212. Arguments:
  213. Return Value:
  214. HRESULT.
  215. --*/
  216. {
  217. InternalShutDown();
  218. // acquire the lock on call.
  219. m_lock.Lock();
  220. // release all the streams
  221. for (int i = m_Streams.GetSize() - 1; i >= 0; i --)
  222. {
  223. m_Streams[i]->Release();
  224. }
  225. m_Streams.RemoveAll();
  226. for (i = 0; i < RTCP_SDES_LAST - 1; i ++)
  227. {
  228. if (m_InfoItems[i])
  229. {
  230. free(m_InfoItems[i]);
  231. m_InfoItems[i] = NULL;
  232. }
  233. }
  234. m_lock.Unlock();
  235. return S_OK;
  236. }
  237. HRESULT CIPConfMSPCall::InternalShutDown()
  238. /*++
  239. Routine Description:
  240. First call the base class's shutdown and then release all the participant
  241. objects.
  242. Arguments:
  243. Return Value:
  244. HRESULT.
  245. --*/
  246. {
  247. if (InterlockedCompareExchange((long*)&m_fShutDown, TRUE, FALSE))
  248. {
  249. return S_OK;
  250. }
  251. LOG((MSP_TRACE, "ConfMSPCall.InternalShutdown, entered"));
  252. // acquire the lock on the call.
  253. m_lock.Lock();
  254. // Shutdown all the streams
  255. for (int i = m_Streams.GetSize() - 1; i >= 0; i --)
  256. {
  257. UnregisterWaitEvent(i);
  258. ((CMSPStream*)m_Streams[i])->ShutDown();
  259. }
  260. m_ThreadPoolWaitBlocks.RemoveAll();
  261. m_lock.Unlock();
  262. // release all the participants
  263. m_ParticipantLock.Lock();
  264. for (i = 0; i < m_Participants.GetSize(); i ++)
  265. {
  266. m_Participants[i]->Release();
  267. }
  268. m_Participants.RemoveAll();
  269. m_ParticipantLock.Unlock();
  270. return S_OK;
  271. }
  272. template <class T>
  273. HRESULT CreateStreamHelper(
  274. IN T * pT,
  275. IN HANDLE hAddress,
  276. IN CIPConfMSPCall* pMSPCall,
  277. IN IMediaEvent * pGraph,
  278. IN DWORD dwMediaType,
  279. IN TERMINAL_DIRECTION Direction,
  280. OUT ITStream ** ppITStream
  281. )
  282. /*++
  283. Routine Description:
  284. Create a stream object and initialize it. This method is called internally
  285. to create a stream object of different class.
  286. Arguments:
  287. hAddress - the handle to the address object.
  288. pCall - the call object.
  289. pGraph - the filter graph for this stream.
  290. dwMediaType - the media type of the stream.
  291. Direction - the direction of the steam.
  292. ppITStream - the interface on this stream object.
  293. Return Value:
  294. HRESULT.
  295. --*/
  296. {
  297. CComObject<T> * pCOMMSPStream;
  298. HRESULT hr = CComObject<T>::CreateInstance(&pCOMMSPStream);
  299. if (NULL == pCOMMSPStream)
  300. {
  301. LOG((MSP_ERROR, "CreateMSPStream:could not create stream:%x", hr));
  302. return hr;
  303. }
  304. // get the interface pointer.
  305. hr = pCOMMSPStream->_InternalQueryInterface(
  306. IID_ITStream,
  307. (void **)ppITStream
  308. );
  309. if (FAILED(hr))
  310. {
  311. LOG((MSP_ERROR, "CreateMSPStream:QueryInterface failed: %x", hr));
  312. delete pCOMMSPStream;
  313. return hr;
  314. }
  315. // Initialize the object.
  316. hr = pCOMMSPStream->Init(
  317. hAddress,
  318. pMSPCall,
  319. pGraph,
  320. dwMediaType,
  321. Direction
  322. );
  323. if (FAILED(hr))
  324. {
  325. LOG((MSP_ERROR, "CreateMSPStream:call init failed: %x", hr));
  326. (*ppITStream)->Release();
  327. return hr;
  328. }
  329. return S_OK;
  330. }
  331. HRESULT CIPConfMSPCall::CreateStreamObject(
  332. IN DWORD dwMediaType,
  333. IN TERMINAL_DIRECTION Direction,
  334. IN IMediaEvent * pGraph,
  335. IN ITStream ** ppStream
  336. )
  337. /*++
  338. Routine Description:
  339. Create a media stream object based on the mediatype and direction.
  340. Arguments:
  341. pMediaType - TAPI3 media type.
  342. Direction - direction of this stream.
  343. IMediaEvent - The filter graph used in this stream.
  344. ppStream - the return pointer of the stream interface
  345. Return Value:
  346. HRESULT.
  347. --*/
  348. {
  349. LOG((MSP_TRACE, "CreateStreamObject, entered"));
  350. HRESULT hr = S_OK;
  351. ITStream * pIMSPStream = NULL;
  352. // Create a stream object based on the media type.
  353. if (dwMediaType == TAPIMEDIATYPE_AUDIO)
  354. {
  355. if (Direction == TD_RENDER)
  356. {
  357. CStreamAudioRecv *pAudioRecv = NULL;
  358. hr = ::CreateStreamHelper(
  359. pAudioRecv,
  360. m_pMSPAddress,
  361. this,
  362. pGraph,
  363. TAPIMEDIATYPE_AUDIO,
  364. TD_RENDER,
  365. &pIMSPStream
  366. );
  367. LOG((MSP_TRACE, "create audio receive:%x, hr:%x", pIMSPStream,hr));
  368. }
  369. else if (Direction == TD_CAPTURE)
  370. {
  371. CStreamAudioSend *pAudioSend = NULL;
  372. hr = ::CreateStreamHelper(
  373. pAudioSend,
  374. m_pMSPAddress,
  375. this,
  376. pGraph,
  377. TAPIMEDIATYPE_AUDIO,
  378. TD_CAPTURE,
  379. &pIMSPStream
  380. );
  381. LOG((MSP_TRACE, "create audio send:%x, hr:%x", pIMSPStream,hr));
  382. }
  383. }
  384. else if (dwMediaType == TAPIMEDIATYPE_VIDEO)
  385. {
  386. if (Direction == TD_RENDER)
  387. {
  388. CStreamVideoRecv *pVideoRecv = NULL;
  389. hr = ::CreateStreamHelper(
  390. pVideoRecv,
  391. m_pMSPAddress,
  392. this,
  393. pGraph,
  394. TAPIMEDIATYPE_VIDEO,
  395. TD_RENDER,
  396. &pIMSPStream
  397. );
  398. LOG((MSP_TRACE, "create video Recv:%x, hr:%x", pIMSPStream,hr));
  399. }
  400. else if (Direction == TD_CAPTURE)
  401. {
  402. CStreamVideoSend *pVideoSend = NULL;
  403. hr = ::CreateStreamHelper(
  404. pVideoSend,
  405. m_pMSPAddress,
  406. this,
  407. pGraph,
  408. TAPIMEDIATYPE_VIDEO,
  409. TD_CAPTURE,
  410. &pIMSPStream
  411. );
  412. LOG((MSP_TRACE, "create video send:%x, hr:%x", pIMSPStream,hr));
  413. }
  414. }
  415. if (FAILED(hr))
  416. {
  417. LOG((MSP_ERROR, "create stream failed. %x", hr));
  418. return hr;
  419. }
  420. *ppStream = pIMSPStream;
  421. return S_OK;
  422. }
  423. DWORD CIPConfMSPCall::FindInterfaceByName(IN WCHAR *pMachineName)
  424. /*++
  425. Routine Description:
  426. Given the machine name of the originator, find out which local interface
  427. can be used to reach that machine.
  428. Arguments:
  429. pMachineName - The machine name of the originator.
  430. Return Value:
  431. INADDR_NONE - nothing can be found.
  432. valid IP - succeeded.
  433. --*/
  434. {
  435. char buffer[MAXIPADDRLEN + 1];
  436. if (WideCharToMultiByte(
  437. GetACP(),
  438. 0,
  439. pMachineName,
  440. -1,
  441. buffer,
  442. MAXIPADDRLEN,
  443. NULL,
  444. NULL
  445. ) == 0)
  446. {
  447. LOG((MSP_ERROR, "can't convert originator's address:%ws", pMachineName));
  448. return INADDR_NONE;
  449. }
  450. DWORD dwAddr;
  451. if ((dwAddr = inet_addr(buffer)) != INADDR_NONE)
  452. {
  453. dwAddr = ntohl(dwAddr);
  454. LOG((MSP_INFO, "originator's IP:%x", dwAddr));
  455. return ((CIPConfMSP *)m_pMSPAddress)->FindLocalInterface(dwAddr);
  456. }
  457. struct hostent * pHost;
  458. // attempt to lookup hostname
  459. pHost = gethostbyname(buffer);
  460. // validate pointer
  461. if (pHost == NULL)
  462. {
  463. LOG((MSP_ERROR, "can't resolve address:%s", buffer));
  464. return INADDR_NONE;
  465. }
  466. // for each of the addresses returned, find the local interface.
  467. for (DWORD i = 0; TRUE; i ++)
  468. {
  469. if (pHost->h_addr_list[i] == NULL)
  470. {
  471. break;
  472. }
  473. // retrieve host address from structure
  474. dwAddr = ntohl(*(unsigned long *)pHost->h_addr_list[i]);
  475. LOG((MSP_INFO, "originator's IP:%x", dwAddr));
  476. DWORD dwInterface =
  477. ((CIPConfMSP *)m_pMSPAddress)->FindLocalInterface(dwAddr);
  478. if (dwInterface != INADDR_NONE)
  479. {
  480. return dwInterface;
  481. }
  482. }
  483. return INADDR_NONE;
  484. }
  485. HRESULT CIPConfMSPCall::CheckOrigin(
  486. IN ITSdp * pITSdp,
  487. OUT BOOL * pFlag,
  488. OUT DWORD * pdwIP
  489. )
  490. /*++
  491. Routine Description:
  492. Check to see if the current user is the originator of the conference.
  493. If he is, he can send to a receive only conference.
  494. Arguments:
  495. pITSdp - a pointer to the ITSdp interface.
  496. pFlag - The result.
  497. pdwIP - The local IP interface that should be used to reach the originator.
  498. Return Value:
  499. HRESULT.
  500. --*/
  501. {
  502. const DWORD MAXUSERNAMELEN = 127;
  503. DWORD dwUserNameLen = MAXUSERNAMELEN;
  504. WCHAR szUserName[MAXUSERNAMELEN+1];
  505. // determine the name of the current user
  506. if (!GetUserNameW(szUserName, &dwUserNameLen))
  507. {
  508. LOG((MSP_ERROR, "cant' get user name. %x", GetLastError()));
  509. return E_UNEXPECTED;
  510. }
  511. LOG((MSP_INFO, "current user: %ws", szUserName));
  512. // find out if the current user is the originator of the conference.
  513. BSTR Originator = NULL;
  514. HRESULT hr = pITSdp->get_Originator(&Originator);
  515. if (FAILED(hr))
  516. {
  517. LOG((MSP_ERROR, "cant' get originator. %x", hr));
  518. return hr;
  519. }
  520. LOG((MSP_INFO, "originator: %ws", Originator));
  521. *pFlag = (_wcsnicmp(szUserName, Originator, lstrlenW(szUserName)) == 0);
  522. SysFreeString(Originator);
  523. // Get the machine IP address of the originator.
  524. BSTR MachineAddress = NULL;
  525. hr = pITSdp->get_MachineAddress(&MachineAddress);
  526. if (FAILED(hr))
  527. {
  528. LOG((MSP_ERROR, "cant' get MachineAddress. %x", hr));
  529. return hr;
  530. }
  531. LOG((MSP_INFO, "MachineAddress: %ws", MachineAddress));
  532. DWORD dwIP = FindInterfaceByName(MachineAddress);
  533. SysFreeString(MachineAddress);
  534. *pdwIP = dwIP;
  535. LOG((MSP_INFO, "Interface to use:%x", *pdwIP));
  536. return S_OK;
  537. }
  538. HRESULT GetAddress(
  539. IN IUnknown * pIUnknown,
  540. OUT DWORD * pdwAddress,
  541. OUT DWORD * pdwTTL
  542. )
  543. /*++
  544. Routine Description:
  545. Get the IP address and TTL value from a connection. It is a "c=" line
  546. in the SDP blob.
  547. Arguments:
  548. pIUnknow - an object that might contain connection information.
  549. pdwAddress - the mem address to store the IP address.
  550. pdwTTL - the mem address to store the TTL value.
  551. Return Value:
  552. HRESULT.
  553. --*/
  554. {
  555. // query for the ITConnection i/f
  556. CComPtr<ITConnection> pITConnection;
  557. HRESULT hr = pIUnknown->QueryInterface(IID_ITConnection, (void **)&pITConnection);
  558. if (FAILED(hr))
  559. {
  560. LOG((MSP_ERROR, "get connection interface. %x", hr));
  561. return hr;
  562. }
  563. // get the start address,
  564. BSTR StartAddress = NULL;
  565. hr = pITConnection->get_StartAddress(&StartAddress);
  566. if (FAILED(hr))
  567. {
  568. LOG((MSP_ERROR, "get start address. %x", hr));
  569. return hr;
  570. }
  571. // Get the IP address from the string.
  572. const DWORD MAXIPADDRLEN = 20;
  573. char Buffer[MAXIPADDRLEN+1];
  574. // first convert the string to ascii.
  575. Buffer[0] = '\0';
  576. if (!WideCharToMultiByte(
  577. CP_ACP,
  578. 0,
  579. StartAddress,
  580. -1,
  581. Buffer,
  582. MAXIPADDRLEN,
  583. NULL,
  584. NULL
  585. ))
  586. {
  587. LOG((MSP_ERROR, "converting address. %ws", StartAddress));
  588. SysFreeString(StartAddress);
  589. return E_UNEXPECTED;
  590. }
  591. SysFreeString(StartAddress);
  592. // convert the string to DWORD IP address.
  593. DWORD dwIP = ntohl(inet_addr(Buffer));
  594. if (dwIP == INADDR_NONE)
  595. {
  596. LOG((MSP_ERROR, "invalid IP address. %s", Buffer));
  597. return E_UNEXPECTED;
  598. }
  599. // get the TTL value.
  600. BYTE Ttl;
  601. hr = pITConnection->get_Ttl(&Ttl);
  602. if (FAILED(hr))
  603. {
  604. LOG((MSP_ERROR, "can't get TTL."));
  605. return hr;
  606. }
  607. *pdwAddress = dwIP;
  608. *pdwTTL = Ttl;
  609. return S_OK;
  610. }
  611. HRESULT CheckAttributes(
  612. IN IUnknown * pIUnknown,
  613. OUT BOOL * pbSendOnly,
  614. OUT BOOL * pbRecvOnly,
  615. OUT DWORD * pdwMSPerPacket,
  616. OUT BOOL * pbCIF
  617. )
  618. /*++
  619. Routine Description:
  620. Check the direction of the media, find out if it is send only or
  621. receive only.
  622. Arguments:
  623. pIUnknow - an object that might have a attribute list.
  624. pbSendOnly - the mem address to store the returned BOOL.
  625. pbRecvOnly - the mem address to store the returned BOOL.
  626. pbCIF - if CIF is used for video.
  627. Return Value:
  628. HRESULT.
  629. --*/
  630. {
  631. // query for the ITAttributeList i/f
  632. CComPtr<ITAttributeList> pIAttList;
  633. HRESULT hr = pIUnknown->QueryInterface(IID_ITAttributeList, (void **)&pIAttList);
  634. if (FAILED(hr))
  635. {
  636. LOG((MSP_ERROR, "get attribute interface. %x", hr));
  637. return hr;
  638. }
  639. // get the number of attributes
  640. long lCount;
  641. hr = pIAttList->get_Count(&lCount);
  642. if (FAILED(hr))
  643. {
  644. LOG((MSP_ERROR, "get attribute count. %x", hr));
  645. return hr;
  646. }
  647. *pbRecvOnly = FALSE;
  648. *pbSendOnly = FALSE;
  649. *pdwMSPerPacket = 0;
  650. *pbCIF = FALSE;
  651. const WCHAR * const SENDONLY = L"sendonly";
  652. const WCHAR * const RECVONLY = L"recvonly";
  653. const WCHAR * const FORMAT = L"fmtp";
  654. const WCHAR * const PTIME = L"ptime:";
  655. const WCHAR * const CIF = L" CIF=";
  656. for (long i = 1; i <= lCount; i ++)
  657. {
  658. // get the attributes and check if sendonly of recvonly is specified.
  659. BSTR Attribute = NULL;
  660. hr = pIAttList->get_Item(i, &Attribute);
  661. if (FAILED(hr))
  662. {
  663. LOG((MSP_ERROR, "get attribute item. %x", hr));
  664. return hr;
  665. }
  666. if (_wcsnicmp(SENDONLY, Attribute, lstrlen(SENDONLY)) == 0)
  667. {
  668. *pbSendOnly = TRUE;
  669. }
  670. else if (_wcsnicmp(RECVONLY, Attribute, lstrlen(RECVONLY)) == 0)
  671. {
  672. *pbRecvOnly = TRUE;
  673. }
  674. else if (_wcsnicmp(PTIME, Attribute, lstrlen(PTIME)) == 0)
  675. {
  676. // read the number of milliseconds per packet.
  677. *pdwMSPerPacket = (DWORD)_wtol(Attribute + lstrlen(PTIME));
  678. // RFC 1890 only requires an app to support 200ms packets.
  679. if (*pdwMSPerPacket > 200)
  680. {
  681. // invalid tag, we just use our default.
  682. *pdwMSPerPacket = 0;
  683. }
  684. }
  685. else if (_wcsnicmp(FORMAT, Attribute, lstrlen(FORMAT)) == 0)
  686. {
  687. if (wcsstr(Attribute, CIF))
  688. {
  689. *pbCIF = TRUE;
  690. }
  691. }
  692. SysFreeString(Attribute);
  693. }
  694. return S_OK;
  695. }
  696. HRESULT CIPConfMSPCall::ProcessMediaItem(
  697. IN ITMedia * pITMedia,
  698. IN DWORD dwMediaTypeMask,
  699. OUT DWORD * pdwMediaType,
  700. OUT WORD * pwPort,
  701. OUT DWORD * pdwPayloadType
  702. )
  703. /*++
  704. Routine Description:
  705. Process a "m=" line, find out the media type, port, and payload type.
  706. Arguments:
  707. dwMediaTypeMask - the media type of this call.
  708. pdwMediaType - return the media type of this media item.
  709. pwPort - return the port number used for this media.
  710. pdwPayloadType - the RTP payload type.
  711. Return Value:
  712. HRESULT.
  713. S_FALSE - everything is all right but the media type is not needed.
  714. --*/
  715. {
  716. // get the name of the media.
  717. BSTR MediaName = NULL;
  718. HRESULT hr = pITMedia->get_MediaName(&MediaName);
  719. if (FAILED(hr))
  720. {
  721. LOG((MSP_ERROR, "get media name. %x", hr));
  722. return hr;
  723. }
  724. LOG((MSP_INFO, "media name: %ws", MediaName));
  725. // check if the media is audio or video.
  726. const WCHAR * const AUDIO = L"audio";
  727. const WCHAR * const VIDEO = L"video";
  728. const DWORD NAMELEN = 5;
  729. DWORD dwMediaType = 0;
  730. if (_wcsnicmp(AUDIO, MediaName, NAMELEN) == 0)
  731. {
  732. dwMediaType = TAPIMEDIATYPE_AUDIO;
  733. }
  734. else if (_wcsnicmp(VIDEO, MediaName, NAMELEN) == 0)
  735. {
  736. dwMediaType = TAPIMEDIATYPE_VIDEO;
  737. }
  738. SysFreeString(MediaName);
  739. // check if the call wants this media type.
  740. if ((dwMediaType & dwMediaTypeMask) == 0)
  741. {
  742. // We don't need this media type in this call.
  743. LOG((MSP_INFO, "media skipped."));
  744. return S_FALSE;
  745. }
  746. // get start port
  747. long lStartPort;
  748. hr = pITMedia->get_StartPort(&lStartPort);
  749. if (FAILED(hr))
  750. {
  751. LOG((MSP_ERROR, "get start port. %x", hr));
  752. return hr;
  753. }
  754. // get the transport Protocol
  755. BSTR TransportProtocol = NULL;
  756. hr = pITMedia->get_TransportProtocol(&TransportProtocol);
  757. if (FAILED(hr))
  758. {
  759. LOG((MSP_ERROR, "get transport Protocol. %x", hr));
  760. return hr;
  761. }
  762. // varify that the protocol is RTP.
  763. const WCHAR * const RTP = L"RTP";
  764. const DWORD PROTOCOLLEN = 3;
  765. if (_wcsnicmp(RTP, TransportProtocol, PROTOCOLLEN) != 0)
  766. {
  767. LOG((MSP_ERROR, "wrong transport Protocol:%ws", TransportProtocol));
  768. SysFreeString(TransportProtocol);
  769. return S_FALSE;
  770. }
  771. SysFreeString(TransportProtocol);
  772. // get the format code list
  773. VARIANT Variant;
  774. VariantInit(&Variant);
  775. hr = pITMedia->get_FormatCodes(&Variant);
  776. if (FAILED(hr))
  777. {
  778. LOG((MSP_ERROR, "get format codes. %x", hr));
  779. return hr;
  780. }
  781. // Verify that the SafeArray is in proper shape.
  782. if(SafeArrayGetDim(V_ARRAY(&Variant)) != 1)
  783. {
  784. LOG((MSP_ERROR, "wrong dimension for the format code. %x", hr));
  785. VariantClear(&Variant);
  786. return E_UNEXPECTED;
  787. }
  788. long index = 1;
  789. hr = SafeArrayGetLBound(V_ARRAY(&Variant), 1, &index);
  790. if (FAILED(hr))
  791. {
  792. LOG((MSP_ERROR, "Can't get the lower bound. %x", hr));
  793. VariantClear(&Variant);
  794. return E_UNEXPECTED;
  795. }
  796. // Get the first format code because we only support one format.
  797. BSTR Format = NULL;
  798. hr = SafeArrayGetElement(V_ARRAY(&Variant), &index, &Format);
  799. // clear the variant because we don't need it any more
  800. VariantClear(&Variant);
  801. if (FAILED(hr))
  802. {
  803. LOG((MSP_ERROR, "get first format code. %x", hr));
  804. return hr;
  805. }
  806. LOG((MSP_INFO, "format code: %ws", Format));
  807. DWORD dwPayloadType = (DWORD)_wtoi(Format);
  808. SysFreeString(Format);
  809. *pdwMediaType = dwMediaType;
  810. *pwPort = (WORD)lStartPort;
  811. *pdwPayloadType = dwPayloadType;
  812. return S_OK;
  813. }
  814. HRESULT CIPConfMSPCall::ConfigStreamsBasedOnSDP(
  815. IN ITSdp * pITSdp,
  816. IN DWORD dwAudioQOSLevel,
  817. IN DWORD dwVideoQOSLevel
  818. )
  819. /*++
  820. Routine Description:
  821. Configure the streams based on the information in the SDP blob.
  822. Arguments:
  823. pITSdp - the SDP object. It contains parsed information.
  824. Return Value:
  825. HRESULT.
  826. --*/
  827. {
  828. // find out if the current user is the originator of the conference.
  829. BOOL fIsOriginator;
  830. DWORD dwLocalInterface = INADDR_NONE;
  831. HRESULT hr = CheckOrigin(pITSdp, &fIsOriginator, &dwLocalInterface);
  832. if (FAILED(hr))
  833. {
  834. LOG((MSP_ERROR, "check origin. %x", hr));
  835. return hr;
  836. }
  837. LOG((MSP_INFO, "Local interface: %x", dwLocalInterface));
  838. // get the start IP address and TTL value from the connection.
  839. DWORD dwIPGlobal, dwTTLGlobal;
  840. hr = GetAddress(pITSdp, &dwIPGlobal, &dwTTLGlobal);
  841. if (FAILED(hr))
  842. {
  843. LOG((MSP_ERROR, "get global address. %x", hr));
  844. return hr;
  845. }
  846. // find out if this conference is sendonly or recvonly.
  847. BOOL fSendOnlyGlobal = FALSE, fRecvOnlyGlobal = FALSE, fCIF = FALSE;
  848. DWORD dwMSPerPacket;
  849. hr = CheckAttributes(
  850. pITSdp, &fSendOnlyGlobal, &fRecvOnlyGlobal, &dwMSPerPacket, &fCIF);
  851. if (FAILED(hr))
  852. {
  853. LOG((MSP_ERROR, "check global attributes. %x", hr));
  854. return hr;
  855. }
  856. // get the media information
  857. CComPtr<ITMediaCollection> pICollection;
  858. hr = pITSdp->get_MediaCollection(&pICollection);
  859. if (FAILED(hr))
  860. {
  861. LOG((MSP_ERROR, "get the media collection. %x", hr));
  862. return hr;
  863. }
  864. // find out how many media sessions are in the blobl.
  865. long lCount;
  866. hr = pICollection->get_Count(&lCount);
  867. if (FAILED(hr))
  868. {
  869. LOG((MSP_ERROR, "get number of media items. %x", hr));
  870. return hr;
  871. }
  872. if (lCount > 0)
  873. {
  874. // change the call into connected state since the SDP is OK.
  875. // We are going to set up each every streams next.
  876. SendTSPMessage(CALL_CONNECTED, 0);
  877. }
  878. DWORD dwNumSucceeded = 0;
  879. // for each media session, get info configure a stream.
  880. for(long i=1; i <= lCount; i++)
  881. {
  882. // get the media item first.
  883. ITMedia *pITMedia;
  884. hr = pICollection->get_Item(i, &pITMedia);
  885. if (FAILED(hr))
  886. {
  887. LOG((MSP_ERROR, "get media item. %x", hr));
  888. continue;
  889. }
  890. DWORD dwMediaType;
  891. STREAMSETTINGS Setting;
  892. ZeroMemory(&Setting, sizeof(STREAMSETTINGS));
  893. // find out the information about the media. Here we pass in the media
  894. // type of call so that we won't wasting time reading the attributes
  895. // for a media type we don't need.
  896. hr = ProcessMediaItem(
  897. pITMedia,
  898. m_dwMediaType,
  899. &dwMediaType,
  900. &Setting.wRTPPortRemote,
  901. &Setting.dwPayloadType
  902. );
  903. if (FAILED(hr))
  904. {
  905. LOG((MSP_ERROR, "process media. %x", hr));
  906. continue;
  907. }
  908. // if the return value is S_FALSE from the previous call, this media
  909. // type is not needed for the call.
  910. if (hr != S_OK)
  911. {
  912. // the media is not needed.
  913. continue;
  914. }
  915. Setting.dwQOSLevel = (dwMediaType == TAPIMEDIATYPE_AUDIO)
  916. ? dwAudioQOSLevel : dwVideoQOSLevel;
  917. // Get the local connect information.
  918. DWORD dwIP, dwTTL;
  919. hr = GetAddress(pITMedia, &dwIP, &dwTTL);
  920. if (FAILED(hr))
  921. {
  922. LOG((MSP_WARN, "no local address, use global one", hr));
  923. Setting.dwIPRemote = dwIPGlobal;
  924. Setting.dwTTL = dwTTLGlobal;
  925. }
  926. else
  927. {
  928. Setting.dwIPRemote = dwIP;
  929. Setting.dwTTL = dwTTL;
  930. }
  931. // find out if this media is sendonly or recvonly.
  932. BOOL fSendOnly = FALSE, fRecvOnly = FALSE, fCIF = FALSE;
  933. hr = CheckAttributes(
  934. pITMedia, &fSendOnly, &fRecvOnly, &dwMSPerPacket, &fCIF);
  935. if (FAILED(hr))
  936. {
  937. LOG((MSP_ERROR, "check local attributes. %x", hr));
  938. }
  939. fSendOnly = fSendOnly || fSendOnlyGlobal;
  940. fRecvOnly = (fRecvOnly || fRecvOnlyGlobal) && (!fIsOriginator);
  941. Setting.dwMSPerPacket = dwMSPerPacket;
  942. Setting.fCIF = fCIF;
  943. // The media item is not needed after this point.
  944. pITMedia->Release();
  945. // Go through the existing streams and find out if any stream
  946. // can be configured.
  947. // Note: we are not creating any new streams now. We might want to
  948. // do it in the future if we want to support two sessions of the
  949. // same media type.
  950. CLock lock(m_lock);
  951. for (long j = 0; j < m_Streams.GetSize(); j ++)
  952. {
  953. CIPConfMSPStream* pStream = (CIPConfMSPStream*)m_Streams[j];
  954. if ((pStream->MediaType() != dwMediaType)
  955. || pStream->IsConfigured()
  956. || (fSendOnly && pStream->Direction() == TD_RENDER)
  957. || (fRecvOnly && pStream->Direction() == TD_CAPTURE)
  958. )
  959. {
  960. // this stream should not be configured.
  961. continue;
  962. }
  963. // set the local interface that the call should bind to.
  964. Setting.dwIPLocal = m_dwIPInterface;
  965. if ((m_dwIPInterface == INADDR_ANY)
  966. && (dwLocalInterface != INADDR_NONE))
  967. {
  968. Setting.dwIPLocal = dwLocalInterface;
  969. }
  970. // configure the stream, it will be started as well.
  971. hr = pStream->Configure(Setting);
  972. if (FAILED(hr))
  973. {
  974. LOG((MSP_ERROR, "configure stream failed. %x", hr));
  975. }
  976. else
  977. {
  978. dwNumSucceeded ++;
  979. }
  980. }
  981. }
  982. if (dwNumSucceeded == 0)
  983. {
  984. LOG((MSP_ERROR, "No media succeeded."));
  985. return E_FAIL;
  986. }
  987. return S_OK;
  988. }
  989. HRESULT CIPConfMSPCall::ParseSDP(
  990. IN WCHAR * pSDP,
  991. IN DWORD dwAudioQOSLevel,
  992. IN DWORD dwVideoQOSLevel
  993. )
  994. /*++
  995. Routine Description:
  996. Parse the SDP string. The function uses the SdpConferenceBlob object
  997. to parse the string.
  998. Arguments:
  999. pSDP - the SDP string.
  1000. dwAudioQOSLevel - the QOS requirement for audio.
  1001. dwVideoQOSLevel - the QOS requirement for video.
  1002. Return Value:
  1003. HRESULT.
  1004. --*/
  1005. {
  1006. // co-create an sdp conference blob component
  1007. // query for the ITConferenceBlob interface
  1008. CComPtr<ITConferenceBlob> pIConfBlob;
  1009. HRESULT hr = ::CoCreateInstance(
  1010. CLSID_SdpConferenceBlob,
  1011. NULL,
  1012. CLSCTX_INPROC_SERVER,
  1013. IID_ITConferenceBlob,
  1014. (void **)&pIConfBlob
  1015. );
  1016. if (FAILED(hr))
  1017. {
  1018. LOG((MSP_ERROR, "creating a SDPBlob object. %x", hr));
  1019. return hr;
  1020. }
  1021. // conver the sdp into a BSTR to use the interface.
  1022. BSTR bstrSDP = SysAllocString(pSDP);
  1023. if (bstrSDP == NULL)
  1024. {
  1025. LOG((MSP_ERROR, "out of mem converting SDP to a BSTR."));
  1026. return E_OUTOFMEMORY;
  1027. }
  1028. // Parse the SDP string.
  1029. hr = pIConfBlob->Init(NULL, BCS_ASCII, bstrSDP);
  1030. // the string is not needed any more.
  1031. SysFreeString(bstrSDP);
  1032. if (FAILED(hr))
  1033. {
  1034. LOG((MSP_ERROR, "parse the SDPBlob object. %x", hr));
  1035. return hr;
  1036. }
  1037. // Get the ITSdp interface.
  1038. CComPtr<ITSdp> pITSdp;
  1039. hr = pIConfBlob->QueryInterface(IID_ITSdp, (void **)&pITSdp);
  1040. if (FAILED(hr))
  1041. {
  1042. LOG((MSP_ERROR, "can't get the ITSdp interface. %x", hr));
  1043. return hr;
  1044. }
  1045. // check main sdp validity
  1046. VARIANT_BOOL IsValid;
  1047. hr = pITSdp->get_IsValid(&IsValid);
  1048. if (FAILED(hr))
  1049. {
  1050. LOG((MSP_ERROR, "can't get the valid flag on the SDP %x", hr));
  1051. return hr;
  1052. }
  1053. if (!IsValid)
  1054. {
  1055. LOG((MSP_ERROR, "the SDP is not valid %x", hr));
  1056. return E_FAIL;
  1057. }
  1058. return ConfigStreamsBasedOnSDP(
  1059. pITSdp,
  1060. dwAudioQOSLevel,
  1061. dwVideoQOSLevel
  1062. );
  1063. }
  1064. HRESULT CIPConfMSPCall::SendTSPMessage(
  1065. IN TSP_MSP_COMMAND command,
  1066. IN DWORD dwParam1,
  1067. IN DWORD dwParam2
  1068. ) const
  1069. /*++
  1070. Routine Description:
  1071. Send the TSP a message from the MSP.
  1072. Arguments:
  1073. command - the command to be sent.
  1074. dwParam1 - the first DWORD used in the command.
  1075. dwParam2 - the second DWORD used in the command.
  1076. Return Value:
  1077. HRESULT.
  1078. --*/
  1079. {
  1080. LOG((MSP_TRACE, "SendTSPMessage, command %d, dwParam1 %d, dwParam2",
  1081. command, dwParam1, dwParam2));
  1082. if (InterlockedCompareExchange((long*)&m_fShutDown, TRUE, TRUE))
  1083. {
  1084. return E_UNEXPECTED;
  1085. }
  1086. // first allocate the memory.
  1087. DWORD dwSize = sizeof(MSG_TSPMSPDATA);
  1088. MSPEVENTITEM* pEventItem = AllocateEventItem(dwSize);
  1089. if (pEventItem == NULL)
  1090. {
  1091. LOG((MSP_ERROR, "No memory for the TSPMSP data, size: %d", dwSize));
  1092. return E_OUTOFMEMORY;
  1093. }
  1094. // Fill in the necessary fields for the event structure.
  1095. pEventItem->MSPEventInfo.dwSize =
  1096. sizeof(MSP_EVENT_INFO) + sizeof(MSG_TSPMSPDATA);
  1097. pEventItem->MSPEventInfo.Event = ME_TSP_DATA;
  1098. pEventItem->MSPEventInfo.hCall = m_htCall;
  1099. // Fill in the data for the TSP.
  1100. pEventItem->MSPEventInfo.MSP_TSP_DATA.dwBufferSize = sizeof(MSG_TSPMSPDATA);
  1101. MSG_TSPMSPDATA *pData = (MSG_TSPMSPDATA *)
  1102. pEventItem->MSPEventInfo.MSP_TSP_DATA.pBuffer;
  1103. pData->command = command;
  1104. switch (command)
  1105. {
  1106. case CALL_DISCONNECTED:
  1107. pData->CallDisconnected.dwReason = dwParam1;
  1108. break;
  1109. case CALL_QOS_EVENT:
  1110. pData->QosEvent.dwEvent = dwParam1;
  1111. pData->QosEvent.dwMediaMode = dwParam2;
  1112. break;
  1113. case CALL_CONNECTED:
  1114. break;
  1115. default:
  1116. LOG((MSP_ERROR, "Wrong command type for TSP"));
  1117. FreeEventItem(pEventItem);
  1118. return E_UNEXPECTED;
  1119. }
  1120. HRESULT hr = m_pMSPAddress->PostEvent(pEventItem);
  1121. if (FAILED(hr))
  1122. {
  1123. LOG((MSP_ERROR, "Post event failed %x", hr));
  1124. FreeEventItem(pEventItem);
  1125. return hr;
  1126. }
  1127. return S_OK;
  1128. }
  1129. HRESULT CIPConfMSPCall::CheckUnusedStreams()
  1130. /*++
  1131. Routine Description:
  1132. Find out which streams are not used and send tapi events about them.
  1133. Arguments:
  1134. Return Value:
  1135. HRESULT.
  1136. --*/
  1137. {
  1138. LOG((MSP_TRACE, "CheckUnusedStreams"));
  1139. CLock lock(m_lock);
  1140. for (long j = 0; j < m_Streams.GetSize(); j ++)
  1141. {
  1142. CIPConfMSPStream* pStream = (CIPConfMSPStream*)m_Streams[j];
  1143. if (pStream->IsConfigured())
  1144. {
  1145. // find the next.
  1146. continue;
  1147. }
  1148. MSPEVENTITEM* pEventItem = AllocateEventItem();
  1149. if (pEventItem == NULL)
  1150. {
  1151. LOG((MSP_ERROR, "No memory for the TSPMSP data, size: %d", sizeof(MSPEVENTITEM)));
  1152. return E_OUTOFMEMORY;
  1153. }
  1154. // Fill in the necessary fields for the event structure.
  1155. pEventItem->MSPEventInfo.dwSize = sizeof(MSP_EVENT_INFO);;
  1156. pEventItem->MSPEventInfo.Event = ME_CALL_EVENT;
  1157. pEventItem->MSPEventInfo.hCall = m_htCall;
  1158. pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.Type = CALL_STREAM_NOT_USED;
  1159. pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.Cause = CALL_CAUSE_REMOTE_REQUEST;
  1160. pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.pStream = m_Streams[j];
  1161. // Addref to prevent it from going away.
  1162. m_Streams[j]->AddRef();
  1163. pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.pTerminal = NULL;
  1164. pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.hrError= 0;
  1165. // send the event to tapi.
  1166. HRESULT hr = m_pMSPAddress->PostEvent(pEventItem);
  1167. if (FAILED(hr))
  1168. {
  1169. LOG((MSP_ERROR, "Post event failed %x", hr));
  1170. FreeEventItem(pEventItem);
  1171. return hr;
  1172. }
  1173. }
  1174. return S_OK;
  1175. }
  1176. DWORD WINAPI CIPConfMSPCall::WorkerCallbackDispatcher(VOID *pContext)
  1177. /*++
  1178. Routine Description:
  1179. Because Parsing the SDP and configure the streams uses a lot of COM
  1180. stuff, we can't rely on the RPC thread the calls into the MSP to
  1181. receive the TSP data. So, we let our own working thread do the work.
  1182. This method is the callback function for the queued work item. It
  1183. just gets the call object from the context structure and calls a method
  1184. on the call object to handle the work item.
  1185. Arguments:
  1186. pContext - A pointer to a CALLWORKITEM structure.
  1187. Return Value:
  1188. HRESULT.
  1189. --*/
  1190. {
  1191. _ASSERTE(!IsBadReadPtr(pContext, sizeof CALLWORKITEM));
  1192. CALLWORKITEM *pItem = (CALLWORKITEM *)pContext;
  1193. pItem->pCall->ProcessWorkerCallBack(pItem->Buffer, pItem->dwLen);
  1194. pItem->pCall->MSPCallRelease();
  1195. free(pItem);
  1196. return NOERROR;
  1197. }
  1198. DWORD CIPConfMSPCall::ProcessWorkerCallBack(
  1199. IN PBYTE pBuffer,
  1200. IN DWORD dwSize
  1201. )
  1202. /*++
  1203. Routine Description:
  1204. This function handles the work item given by the TSP.
  1205. Arguments:
  1206. pBuffer - a buffer that contains a TSP_MSP command block.
  1207. dwSize - the size of the buffer.
  1208. Return Value:
  1209. NOERROR.
  1210. --*/
  1211. {
  1212. LOG((MSP_TRACE, "PreocessWorkerCallBAck"));
  1213. _ASSERTE(!IsBadReadPtr(pBuffer, dwSize));
  1214. MSG_TSPMSPDATA * pData = (MSG_TSPMSPDATA *)pBuffer;
  1215. HRESULT hr;
  1216. switch (pData->command)
  1217. {
  1218. case CALL_START:
  1219. // Parse the SDP contained in the command block.
  1220. hr = ParseSDP(pData->CallStart.szSDP,
  1221. pData->CallStart.dwAudioQOSLevel,
  1222. pData->CallStart.dwVideoQOSLevel
  1223. );
  1224. if (FAILED(hr))
  1225. {
  1226. // disconnect the call if someting terrible happend.
  1227. SendTSPMessage(CALL_DISCONNECTED, 0);
  1228. LOG((MSP_ERROR, "parsing theSDPBlob object. %x", hr));
  1229. return NOERROR;
  1230. }
  1231. // go through the streams and send events if they are not used.
  1232. hr = CheckUnusedStreams();
  1233. if (FAILED(hr))
  1234. {
  1235. LOG((MSP_ERROR, "start the streams failed. %x", hr));
  1236. }
  1237. break;
  1238. case CALL_STOP:
  1239. InternalShutDown();
  1240. break;
  1241. }
  1242. return NOERROR;
  1243. }
  1244. HRESULT CIPConfMSPCall::ReceiveTSPCallData(
  1245. IN PBYTE pBuffer,
  1246. IN DWORD dwSize
  1247. )
  1248. /*++
  1249. Routine Description:
  1250. This function handles the work item given by the TSP.
  1251. Arguments:
  1252. pBuffer - a buffer that contains a TSP_MSP command block.
  1253. dwSize - the size of the buffer.
  1254. Return Value:
  1255. NOERROR.
  1256. --*/
  1257. {
  1258. LOG((MSP_TRACE,
  1259. "ReceiveTSPCallData, pBuffer %x, dwSize %d", pBuffer, dwSize));
  1260. MSG_TSPMSPDATA * pData = (MSG_TSPMSPDATA *)pBuffer;
  1261. switch (pData->command)
  1262. {
  1263. case CALL_START:
  1264. // make sure the string is valid.
  1265. if ((IsBadReadPtr(pData->CallStart.szSDP,
  1266. (pData->CallStart.dwSDPLen + 1) * sizeof (WCHAR)))
  1267. || (pData->CallStart.szSDP[pData->CallStart.dwSDPLen] != 0))
  1268. {
  1269. LOG((MSP_ERROR, "the TSP data is invalid."));
  1270. return E_UNEXPECTED;
  1271. }
  1272. LOG((MSP_INFO, "SDP string\n%ws", pData->CallStart.szSDP));
  1273. break;
  1274. case CALL_STOP:
  1275. break;
  1276. default:
  1277. LOG((MSP_ERROR,
  1278. "wrong command received from the TSP:%x", pData->command));
  1279. return E_UNEXPECTED;
  1280. }
  1281. // allocate a work item structure for our worker thread.
  1282. CALLWORKITEM *pItem = (CALLWORKITEM *)malloc(sizeof(CALLWORKITEM) + dwSize);
  1283. if (pItem == NULL)
  1284. {
  1285. // Disconnect the call because of out of memory.
  1286. SendTSPMessage(CALL_DISCONNECTED, 0);
  1287. LOG((MSP_ERROR, "out of memory for work item."));
  1288. return E_OUTOFMEMORY;
  1289. }
  1290. this->MSPCallAddRef();
  1291. pItem->pCall = this;
  1292. pItem->dwLen = dwSize;
  1293. CopyMemory(pItem->Buffer, pBuffer, dwSize);
  1294. // post a work item to our worker thread.
  1295. HRESULT hr = g_Thread.QueueWorkItem(
  1296. WorkerCallbackDispatcher, // the callback
  1297. pItem, // the context.
  1298. FALSE // sync (FALSE means asyn)
  1299. );
  1300. if (FAILED(hr))
  1301. {
  1302. if (pData->command == CALL_START)
  1303. {
  1304. // Disconnect the call because we can't handle the work.
  1305. SendTSPMessage(CALL_DISCONNECTED, 0);
  1306. }
  1307. this->MSPCallRelease();
  1308. free(pItem);
  1309. LOG((MSP_ERROR, "queue work item failed."));
  1310. }
  1311. return hr;
  1312. }
  1313. STDMETHODIMP CIPConfMSPCall::EnumerateParticipants(
  1314. OUT IEnumParticipant ** ppEnumParticipant
  1315. )
  1316. /*++
  1317. Routine Description:
  1318. This method returns an enumerator to the participants.
  1319. Arguments:
  1320. ppEnumParticipant - the memory location to store the returned pointer.
  1321. Return Value:
  1322. S_OK
  1323. E_POINTER
  1324. E_OUTOFMEMORY
  1325. --*/
  1326. {
  1327. LOG((MSP_TRACE,
  1328. "EnumerateParticipants entered. ppEnumParticipant:%x", ppEnumParticipant));
  1329. //
  1330. // Check parameters.
  1331. //
  1332. if (IsBadWritePtr(ppEnumParticipant, sizeof(VOID *)))
  1333. {
  1334. LOG((MSP_ERROR, "CIPConfMSPCall::EnumerateParticipants - "
  1335. "bad pointer argument - exit E_POINTER"));
  1336. return E_POINTER;
  1337. }
  1338. //
  1339. // First see if this call has been shut down.
  1340. // acquire the lock before accessing the Participant object list.
  1341. //
  1342. CLock lock(m_ParticipantLock);
  1343. if (m_Participants.GetData() == NULL)
  1344. {
  1345. LOG((MSP_ERROR, "CIPConfMSPCall::EnumerateParticipants - "
  1346. "call appears to have been shut down - exit E_UNEXPECTED"));
  1347. // This call has been shut down.
  1348. return E_UNEXPECTED;
  1349. }
  1350. //
  1351. // Create an enumerator object.
  1352. //
  1353. HRESULT hr = CreateParticipantEnumerator(
  1354. m_Participants.GetData(), // the begin itor
  1355. m_Participants.GetData() + m_Participants.GetSize(), // the end itor,
  1356. ppEnumParticipant
  1357. );
  1358. if (FAILED(hr))
  1359. {
  1360. LOG((MSP_ERROR, "CIPConfMSPCall::EnumerateParticipants - "
  1361. "create enumerator object failed, %x", hr));
  1362. return hr;
  1363. }
  1364. LOG((MSP_TRACE, "CIPConfMSPCall::EnumerateParticipants - exit S_OK"));
  1365. return hr;
  1366. }
  1367. STDMETHODIMP CIPConfMSPCall::get_Participants(
  1368. OUT VARIANT * pVariant
  1369. )
  1370. {
  1371. LOG((MSP_TRACE, "CIPConfMSPCall::get_Participants - enter"));
  1372. //
  1373. // Check parameters.
  1374. //
  1375. if ( IsBadWritePtr(pVariant, sizeof(VARIANT) ) )
  1376. {
  1377. LOG((MSP_ERROR, "CIPConfMSPCall::get_Participants - "
  1378. "bad pointer argument - exit E_POINTER"));
  1379. return E_POINTER;
  1380. }
  1381. //
  1382. // See if this call has been shut down. Acquire the lock before accessing
  1383. // the Participant object list.
  1384. //
  1385. CLock lock(m_ParticipantLock);
  1386. if (m_Participants.GetData() == NULL)
  1387. {
  1388. LOG((MSP_ERROR, "CIPConfMSPCall::get_Participants - "
  1389. "call appears to have been shut down - exit E_UNEXPECTED"));
  1390. // This call has been shut down.
  1391. return E_UNEXPECTED;
  1392. }
  1393. //
  1394. // create the collection object - see mspcoll.h
  1395. //
  1396. HRESULT hr = CreateParticipantCollection(
  1397. m_Participants.GetData(), // the begin itor
  1398. m_Participants.GetData() + m_Participants.GetSize(), // the end itor,
  1399. m_Participants.GetSize(), // the size
  1400. pVariant
  1401. );
  1402. if (FAILED(hr))
  1403. {
  1404. LOG((MSP_ERROR, "CIPConfMSPCall::get_Participants - "
  1405. "create collection failed - exit 0x%08x", hr));
  1406. return hr;
  1407. }
  1408. LOG((MSP_TRACE, "CIPConfMSPCall::get_Participants - exit S_OK"));
  1409. return S_OK;
  1410. }
  1411. // ITLocalParticipant methods, called by the app.
  1412. STDMETHODIMP CIPConfMSPCall::get_LocalParticipantTypedInfo(
  1413. IN PARTICIPANT_TYPED_INFO InfoType,
  1414. OUT BSTR * ppInfo
  1415. )
  1416. /*++
  1417. Routine Description:
  1418. Get a information item for the local participant. This information is
  1419. sent out to other participants in the conference.
  1420. Arguments:
  1421. InfoType - The type of the information asked.
  1422. ppInfo - the mem address to store a BSTR.
  1423. Return Value:
  1424. S_OK,
  1425. E_INVALIDARG,
  1426. E_POINTER,
  1427. E_OUTOFMEMORY,
  1428. TAPI_E_NOITEMS
  1429. */
  1430. {
  1431. LOG((MSP_TRACE, "CParticipant get info, type:%d", InfoType));
  1432. if (InfoType > PTI_PRIVATE || InfoType < PTI_CANONICALNAME)
  1433. {
  1434. LOG((MSP_ERROR, "CParticipant get info - invalid type:%d", InfoType));
  1435. return E_INVALIDARG;
  1436. }
  1437. if (IsBadWritePtr(ppInfo, sizeof(BSTR)))
  1438. {
  1439. LOG((MSP_ERROR, "CParticipant get info - exit E_POINTER"));
  1440. return E_POINTER;
  1441. }
  1442. // check if we have that info.
  1443. CLock lock(m_lock);
  1444. if (!m_fLocalInfoRetrieved)
  1445. {
  1446. HRESULT hr = InitializeLocalParticipant();
  1447. if (FAILED(hr))
  1448. {
  1449. return hr;
  1450. }
  1451. }
  1452. int index = (int)InfoType;
  1453. if (m_InfoItems[index] == NULL)
  1454. {
  1455. LOG((MSP_INFO, "no local participant info item for %d", InfoType));
  1456. return TAPI_E_NOITEMS;
  1457. }
  1458. // make a BSTR out of it.
  1459. BSTR pName = SysAllocString(m_InfoItems[index]);
  1460. if (pName == NULL)
  1461. {
  1462. LOG((MSP_ERROR, "CParticipant get info - exit out of mem"));
  1463. return E_POINTER;
  1464. }
  1465. // return the BSTR.
  1466. *ppInfo = pName;
  1467. return S_OK;
  1468. }
  1469. // ITLocalParticipant methods, called by the app.
  1470. STDMETHODIMP CIPConfMSPCall::put_LocalParticipantTypedInfo(
  1471. IN PARTICIPANT_TYPED_INFO InfoType,
  1472. IN BSTR pInfo
  1473. )
  1474. /*++
  1475. Routine Description:
  1476. Set a information item for the local participant. This information is
  1477. sent out to other participants in the conference.
  1478. Arguments:
  1479. InfoType - The type of the information item.
  1480. pInfo - the information item.
  1481. Return Value:
  1482. S_OK,
  1483. E_INVALIDARG,
  1484. E_POINTER,
  1485. E_OUTOFMEMORY,
  1486. TAPI_E_NOITEMS
  1487. */
  1488. {
  1489. LOG((MSP_TRACE, "set local info, type:%d", InfoType));
  1490. // We don't allow the app to change canonical name
  1491. if (InfoType > PTI_PRIVATE || InfoType <= PTI_CANONICALNAME)
  1492. {
  1493. LOG((MSP_ERROR, "set local info - invalid type:%d", InfoType));
  1494. return E_INVALIDARG;
  1495. }
  1496. if (IsBadStringPtr(pInfo, MAX_PARTICIPANT_TYPED_INFO_LENGTH))
  1497. {
  1498. LOG((MSP_ERROR, "set local info, bad ptr:%p", pInfo));
  1499. return E_POINTER;
  1500. }
  1501. DWORD dwLen = lstrlenW(pInfo) + 1;
  1502. if (dwLen > MAX_PARTICIPANT_TYPED_INFO_LENGTH)
  1503. {
  1504. LOG((MSP_ERROR, "local info too long"));
  1505. return E_INVALIDARG;
  1506. }
  1507. // check if we have that info.
  1508. CLock lock(m_lock);
  1509. if (!m_fLocalInfoRetrieved)
  1510. {
  1511. HRESULT hr = InitializeLocalParticipant();
  1512. if (FAILED(hr))
  1513. {
  1514. return hr;
  1515. }
  1516. }
  1517. int index = (int)InfoType;
  1518. if (m_InfoItems[index] != NULL)
  1519. {
  1520. if (lstrcmpW(m_InfoItems[index], pInfo) == 0)
  1521. {
  1522. // The info is the same as what we are using.
  1523. return S_OK;
  1524. }
  1525. // the infomation is different, release the old info.
  1526. free(m_InfoItems[index]);
  1527. m_InfoItems[index] = NULL;
  1528. }
  1529. // save the info.
  1530. m_InfoItems[index] = (WCHAR *)malloc(dwLen * sizeof(WCHAR));
  1531. if (m_InfoItems[index] == NULL)
  1532. {
  1533. LOG((MSP_ERROR, "out of mem for local info"));
  1534. return E_OUTOFMEMORY;
  1535. }
  1536. CopyMemory(m_InfoItems[index], pInfo, dwLen * sizeof(WCHAR));
  1537. //
  1538. // The info is new, we need to set it on the streams.
  1539. //
  1540. // conver the WCHAR string to multibytes.
  1541. char Buffer[MAX_PARTICIPANT_TYPED_INFO_LENGTH];
  1542. DWORD dwNumBytes = WideCharToMultiByte(
  1543. GetACP(),
  1544. 0,
  1545. pInfo,
  1546. dwLen,
  1547. Buffer,
  1548. MAX_PARTICIPANT_TYPED_INFO_LENGTH,
  1549. NULL,
  1550. NULL
  1551. );
  1552. if (dwNumBytes == 0)
  1553. {
  1554. LOG((MSP_ERROR, "coverting failed, error:%x", GetLastError()));
  1555. return E_FAIL;
  1556. }
  1557. for (int i = 0; i < m_Streams.GetSize(); i ++)
  1558. {
  1559. ((CIPConfMSPStream*)m_Streams[i])->SetLocalParticipantInfo(
  1560. InfoType,
  1561. Buffer,
  1562. dwNumBytes
  1563. );
  1564. }
  1565. return S_OK;
  1566. }
  1567. HRESULT CIPConfMSPCall::NewParticipant(
  1568. IN ITStream * pITStream,
  1569. IN DWORD dwSSRC,
  1570. IN DWORD dwSendRecv,
  1571. IN DWORD dwMediaType,
  1572. IN char * szCName,
  1573. OUT ITParticipant ** ppITParticipant
  1574. )
  1575. /*++
  1576. Routine Description:
  1577. This method is called by a stream object when a new participant appears.
  1578. It looks throught the call's participant list, if the partcipant is
  1579. already in the list, it returns the pointer to the object. If it is not
  1580. found, a new object will be created and added into the list.
  1581. Arguments:
  1582. pITStream - the stream object.
  1583. dwSSRC - the SSRC of the participant in the stream.
  1584. dwSendRecv - a sender or a receiver.
  1585. dwMediaType - the media type of the stream.
  1586. szCName - the canonical name of the participant.
  1587. ppITParticipant - the address to store the returned pointer.
  1588. Return Value:
  1589. S_OK
  1590. E_OUTOFMEMORY
  1591. --*/
  1592. {
  1593. CLock lock(m_ParticipantLock);
  1594. HRESULT hr;
  1595. // First check to see if the participant is in our list. If he is already
  1596. // in the list, just return the object.
  1597. int index;
  1598. if (m_Participants.FindByCName(szCName, &index))
  1599. {
  1600. hr = ((CParticipant *)m_Participants[index])->
  1601. AddStream(pITStream, dwSSRC, dwSendRecv, dwMediaType);
  1602. if (FAILED(hr))
  1603. {
  1604. LOG((MSP_ERROR, "can not add a stream to a participant:%x", hr));
  1605. return hr;
  1606. }
  1607. *ppITParticipant = m_Participants[index];
  1608. (*ppITParticipant)->AddRef();
  1609. return S_OK;
  1610. }
  1611. // create a new participant object.
  1612. CComObject<CParticipant> * pCOMParticipant;
  1613. hr = CComObject<CParticipant>::CreateInstance(&pCOMParticipant);
  1614. if (NULL == pCOMParticipant)
  1615. {
  1616. LOG((MSP_ERROR, "can not create a new participant:%x", hr));
  1617. return hr;
  1618. }
  1619. ITParticipant* pITParticipant;
  1620. // get the interface pointer.
  1621. hr = pCOMParticipant->_InternalQueryInterface(
  1622. IID_ITParticipant,
  1623. (void **)&pITParticipant
  1624. );
  1625. if (FAILED(hr))
  1626. {
  1627. LOG((MSP_ERROR, "Participant QueryInterface failed: %x", hr));
  1628. delete pCOMParticipant;
  1629. return hr;
  1630. }
  1631. // Initialize the object.
  1632. hr = pCOMParticipant->Init(
  1633. szCName, pITStream, dwSSRC, dwSendRecv, dwMediaType
  1634. );
  1635. if (FAILED(hr))
  1636. {
  1637. LOG((MSP_ERROR, "Create participant:call init failed: %x", hr));
  1638. pITParticipant->Release();
  1639. return hr;
  1640. }
  1641. // Add the Participant into our list of Participants.
  1642. if (!m_Participants.InsertAt(index, pITParticipant))
  1643. {
  1644. pITParticipant->Release();
  1645. LOG((MSP_ERROR, "out of memory in adding a Participant."));
  1646. return E_OUTOFMEMORY;
  1647. }
  1648. // AddRef the interface pointer and return it.
  1649. pITParticipant->AddRef();
  1650. *ppITParticipant = pITParticipant;
  1651. SendParticipantEvent(PE_NEW_PARTICIPANT, pITParticipant);
  1652. return S_OK;
  1653. }
  1654. HRESULT CIPConfMSPCall::ParticipantLeft(
  1655. IN ITParticipant * pITParticipant
  1656. )
  1657. /*++
  1658. Routine Description:
  1659. This method is called by a stream object when a participant left the
  1660. conference.
  1661. Arguments:
  1662. pITParticipant - the participant that left.
  1663. Return Value:
  1664. S_OK
  1665. --*/
  1666. {
  1667. m_ParticipantLock.Lock();
  1668. BOOL fRemoved = m_Participants.Remove(pITParticipant);
  1669. m_ParticipantLock.Unlock();
  1670. if (fRemoved)
  1671. {
  1672. SendParticipantEvent(PE_PARTICIPANT_LEAVE, pITParticipant);
  1673. pITParticipant->Release();
  1674. }
  1675. else
  1676. {
  1677. LOG((MSP_ERROR, "can't remove Participant %p", pITParticipant));
  1678. }
  1679. return S_OK;
  1680. }
  1681. void CIPConfMSPCall::SendParticipantEvent(
  1682. IN PARTICIPANT_EVENT Event,
  1683. IN ITParticipant * pITParticipant,
  1684. IN ITSubStream * pITSubStream
  1685. ) const
  1686. /*++
  1687. Routine Description:
  1688. This method is called by a stream object to send a participant related
  1689. event to the app.
  1690. Arguments:
  1691. Event - the event code.
  1692. pITParticipant - the participant object.
  1693. pITSubStream - the substream object, if any.
  1694. Return Value:
  1695. nothing.
  1696. --*/
  1697. {
  1698. LOG((MSP_TRACE, "send participant event, event %d, participant: %p",
  1699. Event, pITParticipant));
  1700. // Just want to be safe here.
  1701. if (InterlockedCompareExchange((long*)&m_fShutDown, TRUE, TRUE))
  1702. {
  1703. return;
  1704. }
  1705. // Create a private event object.
  1706. CComPtr<IDispatch> pEvent;
  1707. HRESULT hr = CreateParticipantEvent(
  1708. Event,
  1709. pITParticipant,
  1710. pITSubStream,
  1711. &pEvent
  1712. );
  1713. if (FAILED(hr))
  1714. {
  1715. LOG((MSP_ERROR, "create event returned: %x", hr));
  1716. return;
  1717. }
  1718. MSPEVENTITEM* pEventItem = AllocateEventItem();
  1719. if (pEventItem == NULL)
  1720. {
  1721. LOG((MSP_ERROR, "No memory for the TSPMSP data, size: %d", sizeof(MSPEVENTITEM)));
  1722. return;
  1723. }
  1724. // Fill in the necessary fields for the event structure.
  1725. pEventItem->MSPEventInfo.dwSize = sizeof(MSP_EVENT_INFO);;
  1726. pEventItem->MSPEventInfo.Event = ME_PRIVATE_EVENT;
  1727. pEventItem->MSPEventInfo.hCall = m_htCall;
  1728. pEventItem->MSPEventInfo.MSP_PRIVATE_EVENT_INFO.pEvent = pEvent;
  1729. pEventItem->MSPEventInfo.MSP_PRIVATE_EVENT_INFO.lEventCode = Event;
  1730. pEvent->AddRef();
  1731. // send the event to tapi.
  1732. hr = m_pMSPAddress->PostEvent(pEventItem);
  1733. if (FAILED(hr))
  1734. {
  1735. LOG((MSP_ERROR, "Post event failed %x", hr));
  1736. pEvent->Release();
  1737. FreeEventItem(pEventItem);
  1738. }
  1739. }
  1740. VOID CIPConfMSPCall::HandleGraphEvent(
  1741. IN MSPSTREAMCONTEXT * pContext
  1742. )
  1743. {
  1744. long lEventCode;
  1745. LONG_PTR lParam1, lParam2; // win64 fix
  1746. HRESULT hr = pContext->pIMediaEvent->GetEvent(&lEventCode, &lParam1, &lParam2, 0);
  1747. if (FAILED(hr))
  1748. {
  1749. LOG((MSP_ERROR, "Can not get the actual event. %x", hr));
  1750. return;
  1751. }
  1752. LOG((MSP_EVENT, "ProcessGraphEvent, code:%d param1:%x param2:%x",
  1753. lEventCode, lParam1, lParam2));
  1754. if (lEventCode == EC_PALETTE_CHANGED
  1755. || lEventCode == EC_VIDEO_SIZE_CHANGED)
  1756. {
  1757. LOG((MSP_EVENT, "event %d ignored", lEventCode));
  1758. return;
  1759. }
  1760. //
  1761. // Create an event data structure that we will pass to the worker thread.
  1762. //
  1763. MULTI_GRAPH_EVENT_DATA * pData;
  1764. pData = new MULTI_GRAPH_EVENT_DATA;
  1765. if (pData == NULL)
  1766. {
  1767. LOG((MSP_ERROR, "Out of memory for event data."));
  1768. return;
  1769. }
  1770. pData->pCall = this;
  1771. pData->pITStream = pContext->pITStream;
  1772. pData->lEventCode = lEventCode;
  1773. pData->lParam1 = (long) lParam1; // win64 fix -- also need to change struct?
  1774. pData->lParam2 = (long) lParam2; // win64 fix -- also need to change struct?
  1775. //
  1776. // Make sure the call and stream don't go away while we handle the event.
  1777. // but use our special inner object addref for the call
  1778. //
  1779. pData->pCall->MSPCallAddRef();
  1780. pData->pITStream->AddRef();
  1781. //
  1782. // Queue an async work item to call ProcessGraphEvent.
  1783. //
  1784. hr = g_Thread.QueueWorkItem(AsyncMultiGraphEvent,
  1785. (void *) pData,
  1786. FALSE); // asynchronous
  1787. if (FAILED(hr))
  1788. {
  1789. LOG((MSP_ERROR, "QueueWorkItem failed, return code:%x", hr));
  1790. }
  1791. }