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.

3555 lines
94 KiB

  1. /*++
  2. Copyright (c) 1998-1999 Microsoft Corporation
  3. Module Name:
  4. wavestrm.cpp
  5. Abstract:
  6. This module contains implementation of CWaveMSPStream.
  7. Author:
  8. Zoltan Szilagyi (zoltans) September 7, 1998
  9. --*/
  10. #include "stdafx.h"
  11. #include <audevcod.h> // audio device error codes
  12. #include <initguid.h>
  13. #include <g711uids.h>
  14. ///////////////////////////////////////////////////////////////////////////////
  15. ///////////////////////////////////////////////////////////////////////////////
  16. //
  17. // Custom logging helper macro, usable only within this class.
  18. //
  19. #ifdef MSPLOG
  20. #define STREAM_PREFIX(x) m_Direction == TD_RENDER ? \
  21. "CWaveMSPStream(RENDER)::" x : \
  22. "CWaveMSPStream(CAPTURE)::" x
  23. #endif
  24. ///////////////////////////////////////////////////////////////////////////////
  25. ///////////////////////////////////////////////////////////////////////////////
  26. //
  27. CWaveMSPStream::CWaveMSPStream() : CMSPStream()
  28. {
  29. LOG((MSP_TRACE, STREAM_PREFIX("CWaveMSPStream entered.")));
  30. m_fTerminalConnected = FALSE;
  31. m_fHaveWaveID = FALSE;
  32. m_dwSuspendCount = 0;
  33. m_DesiredGraphState = State_Stopped;
  34. m_ActualGraphState = State_Stopped;
  35. m_pFilter = NULL;
  36. m_pG711Filter = NULL;
  37. LOG((MSP_TRACE, STREAM_PREFIX("CWaveMSPStream exited.")));
  38. }
  39. ///////////////////////////////////////////////////////////////////////////////
  40. ///////////////////////////////////////////////////////////////////////////////
  41. //
  42. CWaveMSPStream::~CWaveMSPStream()
  43. {
  44. LOG((MSP_TRACE, STREAM_PREFIX("~CWaveMSPStream entered.")));
  45. LOG((MSP_TRACE, STREAM_PREFIX("~CWaveMSPStream exited.")));
  46. }
  47. ///////////////////////////////////////////////////////////////////////////////
  48. ///////////////////////////////////////////////////////////////////////////////
  49. //
  50. // FinalRelease
  51. //
  52. // Called on destruction of the stream object, before the destructor. Releases
  53. // all of the stream's references to filters.
  54. //
  55. // Arguments: none
  56. //
  57. // Returns: nothing
  58. //
  59. void CWaveMSPStream::FinalRelease()
  60. {
  61. LOG((MSP_TRACE, STREAM_PREFIX("FinalRelease entered.")));
  62. //
  63. // At this point we should have no terminals selected, since
  64. // Shutdown is supposed to be called before we are destructed.
  65. //
  66. _ASSERTE( 0 == m_Terminals.GetSize() );
  67. //
  68. // Remove our filter from the graph and release it.
  69. //
  70. if ( m_fHaveWaveID )
  71. {
  72. _ASSERTE( m_pFilter );
  73. m_pIGraphBuilder->RemoveFilter( m_pFilter );
  74. m_pFilter->Release();
  75. }
  76. if ( m_pG711Filter )
  77. {
  78. m_pIGraphBuilder->RemoveFilter( m_pG711Filter );
  79. m_pG711Filter->Release();
  80. }
  81. //
  82. // Call the base class method to clean up everything else.
  83. //
  84. CMSPStream::FinalRelease();
  85. LOG((MSP_TRACE, STREAM_PREFIX("FinalRelease exited.")));
  86. }
  87. ///////////////////////////////////////////////////////////////////////////////
  88. ///////////////////////////////////////////////////////////////////////////////
  89. //
  90. // ITStream::get_Name
  91. //
  92. // This method returns the name of the stream. The stream name is a friendly
  93. // name which the app can use for UI. The name is determined by the direction
  94. // of the stream and is retrieved from the string table.
  95. //
  96. // Arguments:
  97. // OUT ppName - Returns a BSTR containing the name. The caller is
  98. // responsible for calling SysFreeString when it is done
  99. // with this string.
  100. //
  101. // Returns HRESULT:
  102. // S_OK - success
  103. // E_POINTER - ppName argument is invalid
  104. // E_UNEXPECTED - string cannot be loaded from string table
  105. // E_OUTOFMEMORY - not enough memory to allocate return string
  106. //
  107. STDMETHODIMP CWaveMSPStream::get_Name (
  108. OUT BSTR * ppName
  109. )
  110. {
  111. LOG((MSP_TRACE, STREAM_PREFIX("get_Name - enter")));
  112. //
  113. // Check argument.
  114. //
  115. if ( IsBadWritePtr(ppName, sizeof(BSTR) ) )
  116. {
  117. LOG((MSP_TRACE, STREAM_PREFIX("get_Name - "
  118. "bad return pointer - returning E_POINTER")));
  119. return E_POINTER;
  120. }
  121. //
  122. // Decide what string to return based on which stream this is.
  123. //
  124. ULONG ulID;
  125. if ( m_Direction == TD_CAPTURE )
  126. {
  127. ulID = IDS_CAPTURE_STREAM;
  128. }
  129. else
  130. {
  131. ulID = IDS_RENDER_STREAM;
  132. }
  133. //
  134. // Get the string from the string table.
  135. //
  136. const int ciAllocSize = 2048;
  137. WCHAR wszName[ciAllocSize];
  138. int iReturn = LoadStringW( _Module.GetModuleInstance(),
  139. ulID,
  140. wszName,
  141. ciAllocSize - 1 );
  142. if ( iReturn == 0 )
  143. {
  144. _ASSERTE( FALSE );
  145. *ppName = NULL;
  146. LOG((MSP_ERROR, STREAM_PREFIX("get_Name - "
  147. "LoadString failed - returning E_UNEXPECTED")));
  148. return E_UNEXPECTED;
  149. }
  150. //
  151. // Convert to a BSTR and return the BSTR.
  152. //
  153. *ppName = SysAllocString(wszName);
  154. if ( *ppName == NULL )
  155. {
  156. LOG((MSP_ERROR, STREAM_PREFIX("get_Name - "
  157. "SysAllocString failed - returning E_OUTOFMEMORY")));
  158. return E_OUTOFMEMORY;
  159. }
  160. LOG((MSP_TRACE, STREAM_PREFIX("get_Name - exit S_OK")));
  161. return S_OK;
  162. }
  163. ///////////////////////////////////////////////////////////////////////////////
  164. ///////////////////////////////////////////////////////////////////////////////
  165. //
  166. // ITStream::SelectTerminal
  167. //
  168. // The app calls this method to indicate that the given terminal should be used
  169. // on this stream. Only one terminal per stream is supported at this time.
  170. // If the stream's desired graph state is not stopped, then a successful
  171. // terminal selection causes the stream to attempt to regain the desired graph
  172. // state. (The desired graph state is manipulated using ITStream::StartStream,
  173. // etc.) If such a state change is unsuccessful, an event is fired but the
  174. // SelectTerminal call still returns S_OK. This is to maintain consistency
  175. // between synchronous and asynchronous streaming failures.
  176. //
  177. // Arguments:
  178. // IN pTerminal - Pointer to the terminal to select.
  179. //
  180. // Returns HRESULT:
  181. // S_OK - success
  182. // TAPI_E_MAXTERMINALS - a terminal is already selected
  183. // other - from CMSPStream::SelectTerminal
  184. //
  185. STDMETHODIMP CWaveMSPStream::SelectTerminal(
  186. IN ITTerminal * pTerminal
  187. )
  188. {
  189. LOG((MSP_TRACE, STREAM_PREFIX("SelectTerminal - enter")));
  190. //
  191. // We are going to access the terminal list -- grab the lock
  192. //
  193. CLock lock(m_lock);
  194. //
  195. // Reject if we already have a terminal selected.
  196. //
  197. if ( 0 != m_Terminals.GetSize() )
  198. {
  199. LOG((MSP_ERROR, STREAM_PREFIX("SelectTerminal - "
  200. "exit TAPI_E_MAXTERMINALS")));
  201. return TAPI_E_MAXTERMINALS;
  202. }
  203. //
  204. // Use base class method to add it to our list of terminals.
  205. //
  206. HRESULT hr = CMSPStream::SelectTerminal(pTerminal);
  207. if ( FAILED(hr) )
  208. {
  209. LOG((MSP_ERROR, STREAM_PREFIX("SelectTerminal - "
  210. "base class method failed - exit 0x%08x"), hr));
  211. return hr;
  212. }
  213. //
  214. // Re-pause or re-start the stream if needed.
  215. //
  216. // Note that our behavior does not depend on whether TAPI
  217. // has suspended the stream. If TAPI has suspended the stream,
  218. // that's handled with these methods.
  219. //
  220. // Also note that if an error occurs on trying to regain the
  221. // desired graph state, we leave the terminal selected and
  222. // do not return an error code. This is as it should be, for
  223. // consistency with asynchronous failure cases.
  224. //
  225. if ( m_DesiredGraphState == State_Paused )
  226. {
  227. PauseStream();
  228. }
  229. else if ( m_DesiredGraphState == State_Running )
  230. {
  231. StartStream();
  232. }
  233. else
  234. {
  235. _ASSERTE( m_DesiredGraphState == State_Stopped );
  236. hr = S_OK;
  237. }
  238. if ( FAILED(hr) )
  239. {
  240. LOG((MSP_WARN, STREAM_PREFIX("SelectTerminal - "
  241. "can't regain old graph state "
  242. "0x%08x - continuing anyway"), hr));
  243. }
  244. LOG((MSP_TRACE, STREAM_PREFIX("SelectTerminal - exit S_OK")));
  245. return S_OK;
  246. }
  247. ///////////////////////////////////////////////////////////////////////////////
  248. ///////////////////////////////////////////////////////////////////////////////
  249. //
  250. // ITStream::UnselectTerminal
  251. //
  252. // The app calls this method to indicate that the given terminal should no
  253. // longer be used on this stream. This is relatively easy to do if the
  254. // terminal is not connected to the stream, as is the case for a stream whose
  255. // desired graph state has never deviated from State_Stopped since the terminal
  256. // was selected, or for a stream where connection with the terminal was
  257. // attempted but the connection failed. In these cases the terminal is simply
  258. // removed from the stream's list.
  259. //
  260. // However, if the terminal was successfully connected to the stream earlier
  261. // ( m_fTerminalConnected == TRUE ) then the terminal must also be
  262. // disconnected from the stream. This also requires that the stream's graph
  263. // be stopped. Stopping the graph to disconnect the terminal does not affect
  264. // the desired graph state, so that reselecting a terminal without any changes
  265. // to the desired graph state will result in an attempt to regain the same
  266. // desired graph state that was present before the terminal was unselected.
  267. // (The desired graph state is manipulated using ITStream::StartStream,
  268. // etc.)
  269. //
  270. // Another complication arises from the fact that the G711 codec filter may
  271. // be present in the graph and conected. In order to assure that future
  272. // connections can succeed, this method disconnects and removes the G711
  273. // codec filter from the graph.
  274. //
  275. // Arguments:
  276. // IN pTerminal - Pointer to the terminal to unselect.
  277. //
  278. // Returns HRESULT:
  279. // S_OK - success
  280. // other - from CMSPStream::UnselectTerminal
  281. // or QI for ITTerminalControl
  282. // or ITTerminalControl::DisconnectTerminal
  283. //
  284. STDMETHODIMP CWaveMSPStream::UnselectTerminal (
  285. IN ITTerminal * pTerminal
  286. )
  287. {
  288. LOG((MSP_TRACE, STREAM_PREFIX("UnselectTerminal - enter")));
  289. //
  290. // check the argument -- it has to be a reasonably good pointer
  291. //
  292. if (IsBadReadPtr(pTerminal, sizeof(ITTerminal)))
  293. {
  294. LOG((MSP_ERROR, STREAM_PREFIX("UnselectTerminal - "
  295. "bad terminal pointer passed in. returning E_POINTER")));
  296. return E_POINTER;
  297. }
  298. CLock lock(m_lock);
  299. //
  300. // check the argument -- it has to be in the array of terminals
  301. //
  302. if (m_Terminals.Find(pTerminal) < 0)
  303. {
  304. LOG((MSP_ERROR, STREAM_PREFIX("UnselectTerminal - "
  305. "terminal [%p] is not selected on this stream. "
  306. "returning TAPI_E_INVALIDTERMINAL"), pTerminal));
  307. return TAPI_E_INVALIDTERMINAL;
  308. }
  309. //
  310. // Add an extra reference to the terminal so it doesn't go away
  311. // after we call CMSPStream::UnselectTerminal. We need it later
  312. // in the function.
  313. //
  314. pTerminal->AddRef();
  315. //
  316. // Use base class method to remove terminal from our list of terminals.
  317. //
  318. // Note that if the graph won't stop or the terminal won't disconnect,
  319. // then we will never be able to try again, as the terminal will be gone
  320. // from our list. That's bad. If we just check here if we have the
  321. // terminal selected and then do the base class unselection at the end,
  322. // then we have a different problem -- we can potentially never release a
  323. // terminal from our list that for some reason won't stop/disconnect.
  324. // Therefore we should probably unselect it from our list even if stop/
  325. // disconnect fails. But then we have the same problem we started with.
  326. // Need to think more about how to solve this.
  327. //
  328. HRESULT hr = CMSPStream::UnselectTerminal(pTerminal);
  329. if (FAILED(hr))
  330. {
  331. LOG((MSP_ERROR, STREAM_PREFIX("UnselectTerminal - "
  332. "base class method failed - exit 0x%08x"), hr));
  333. pTerminal->Release();
  334. return hr;
  335. }
  336. //
  337. // Stop the graph and disconnect the terminal if this call had it
  338. // connected. (We need a stopped graph to disconnect properly, and
  339. // couldn't have started the graph if the terminal isn't connected.)
  340. //
  341. if ( m_fTerminalConnected )
  342. {
  343. //
  344. // At this point we need to make sure the stream is stopped.
  345. // We can't use our own StopStream method because it
  346. // (1) changes the desired graph state to Stopped and
  347. // (2) does nothing if no terminal has been selected (which it now
  348. // thinks is the case)
  349. //
  350. // Note also that our behavior does not depend on whether TAPI
  351. // has suspended the stream. If TAPI has suspended the stream,
  352. // it just means we're already stopped.
  353. //
  354. _ASSERTE( m_fHaveWaveID );
  355. //
  356. // Stop the stream via the base class method.
  357. //
  358. hr = CMSPStream::StopStream();
  359. if ( FAILED(hr) )
  360. {
  361. LOG((MSP_ERROR, STREAM_PREFIX("UnselectTerminal - "
  362. "Stop failed - 0x%08x"), hr));
  363. // don't return hr -- we really want to continue and
  364. // disconnect if we can!
  365. }
  366. if ( m_ActualGraphState == State_Running )
  367. {
  368. FireEvent(CALL_STREAM_INACTIVE, hr, CALL_CAUSE_LOCAL_REQUEST);
  369. }
  370. m_ActualGraphState = State_Stopped;
  371. //
  372. // Get the ITTerminalControl interface.
  373. //
  374. ITTerminalControl * pTerminalControl;
  375. hr = pTerminal->QueryInterface(IID_ITTerminalControl,
  376. (void **) &pTerminalControl);
  377. if ( FAILED(hr) )
  378. {
  379. LOG((MSP_ERROR, STREAM_PREFIX("UnselectTerminal - "
  380. "QI for ITTerminalControl failed - exit 0x%08x"), hr));
  381. pTerminal->Release();
  382. return hr;
  383. }
  384. //
  385. // Disconnect the terminal.
  386. //
  387. hr = pTerminalControl->DisconnectTerminal(m_pIGraphBuilder, 0);
  388. pTerminalControl->Release();
  389. m_fTerminalConnected = FALSE;
  390. if ( FAILED(hr) )
  391. {
  392. LOG((MSP_ERROR, STREAM_PREFIX("UnselectTerminal - "
  393. "DisconnectTerminal failed - exit 0x%08x"), hr));
  394. pTerminal->Release();
  395. return hr;
  396. }
  397. //
  398. // Our graph now contains our wave filter and possibly the G711
  399. // codec. The G711 codec may or may not be connected to the wave
  400. // filter. Alternatively, various other filters, pulled in as a
  401. // side-effect of an earlier intelligent connection, may be connected
  402. // to the wave filter. Clean this up.
  403. //
  404. hr = CleanFilterGraph();
  405. if (FAILED(hr))
  406. {
  407. //
  408. // the graph is in a bad state and can no longer be used.
  409. // this is not very good, but we cannot rollback to the original
  410. // state at this point, so we will have to live with this
  411. //
  412. LOG((MSP_ERROR,
  413. STREAM_PREFIX("UnselectTerminal - CleanFilterGraph failed. hr = %lx"),
  414. hr));
  415. pTerminal->Release();
  416. return hr;
  417. }
  418. }
  419. pTerminal->Release();
  420. pTerminal = NULL;
  421. LOG((MSP_TRACE, STREAM_PREFIX("UnselectTerminal - exit")));
  422. return S_OK;
  423. }
  424. ///////////////////////////////////////////////////////////////////////////////
  425. ///////////////////////////////////////////////////////////////////////////////
  426. //
  427. // ITStream::StartStream
  428. //
  429. // The app calls this method to indicate that the stream should start playing.
  430. // Tapi3.dll also calls this method when the call is connected (so that
  431. // streaming starts by default on a newly connected call).
  432. //
  433. // First, a new desired graph state is set on the stream. If there is no
  434. // terminal selected, then this is all that happens, and the stream will start
  435. // the next time a terminal is selected.
  436. //
  437. // If a terminal has been selected, then this method checks if the wave ID
  438. // has been set. If the wave ID has not been set, this indicates a problem
  439. // with the TSP or the transport filter. An event is fired and an error code
  440. // is returned. If the wave ID has been set, then the terminal is connected
  441. // (if this has not already occured) and, unless TAPI3.DLL has suspended the
  442. // stream due to an outstanding TSPI call, the stream is started and an event
  443. // is fired. (But the event is only fired if this is an actual Active/Inactive
  444. // transition.)
  445. //
  446. // Arguments: none
  447. //
  448. // Returns HRESULT:
  449. // S_OK - success
  450. // E_FAIL - wave ID not set
  451. // other - from ConnectTerminal
  452. // or FireEvent
  453. // or CMSPStream::StartStream
  454. //
  455. STDMETHODIMP CWaveMSPStream::StartStream (void)
  456. {
  457. LOG((MSP_TRACE, STREAM_PREFIX("StartStream - enter")));
  458. CLock lock(m_lock);
  459. m_DesiredGraphState = State_Running;
  460. //
  461. // Can't start the stream if no terminal has been selected.
  462. //
  463. if ( 0 == m_Terminals.GetSize() )
  464. {
  465. LOG((MSP_WARN, STREAM_PREFIX("StartStream - "
  466. "no Terminal so nothing to do yet - exit S_OK")));
  467. return S_OK;
  468. }
  469. //
  470. // Can't start the stream if we don't know the waveid.
  471. // (We create our filters on discovery of the waveid.)
  472. // Here we fire a failure event, as this indicates an
  473. // improperly-installed TSP or a failure during filter
  474. // creation or setup, rendering this stream unusable.
  475. //
  476. if ( ! m_fHaveWaveID )
  477. {
  478. LOG((MSP_WARN, STREAM_PREFIX("StartStream - "
  479. "no waveid - event + exit E_FAIL")));
  480. FireEvent(CALL_STREAM_FAIL, E_FAIL, CALL_CAUSE_UNKNOWN);
  481. return E_FAIL;
  482. }
  483. //
  484. // Connect the terminal. This does nothing if this call already
  485. // connected the terminal and fails if another call has the
  486. // terminal connected.
  487. //
  488. HRESULT hr;
  489. hr = ConnectTerminal(m_Terminals[0]);
  490. if ( FAILED(hr) )
  491. {
  492. FireEvent(CALL_TERMINAL_FAIL, hr, CALL_CAUSE_CONNECT_FAIL);
  493. FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_CONNECT_FAIL);
  494. LOG((MSP_ERROR, STREAM_PREFIX("StartStream - "
  495. "our ConnectTerminal failed - exit 0x%08x"), hr));
  496. return hr;
  497. }
  498. //
  499. // If the stream is suspended, we're done. Since we've set the
  500. // desired graph state to State_Running, the rest of this
  501. // process will complete when TAPI resumes the stream.
  502. //
  503. if ( 0 != m_dwSuspendCount )
  504. {
  505. //
  506. // By the way, this is quite normal as the suspend/resume happens
  507. // behind the scenes -- use MSP_TRACE rather than MSP_WARN
  508. //
  509. LOG((MSP_TRACE, STREAM_PREFIX("StartStream - "
  510. "stream is suspended so terminal is connected but we are not "
  511. "running the graph yet - exit S_OK")));
  512. return S_OK;
  513. }
  514. //
  515. // Run the stream via the base class method.
  516. //
  517. hr = CMSPStream::StartStream();
  518. if ( FAILED(hr) )
  519. {
  520. //
  521. // Failed to run -- tell the app.
  522. //
  523. FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_UNKNOWN);
  524. LOG((MSP_ERROR, STREAM_PREFIX("StartStream - "
  525. "Run failed - exit 0x%08x"), hr));
  526. return hr;
  527. }
  528. //
  529. // Fire event if this just made us active.
  530. //
  531. if ( m_ActualGraphState != State_Running )
  532. {
  533. m_ActualGraphState = State_Running;
  534. HRESULT hr2 = FireEvent(CALL_STREAM_ACTIVE, hr, CALL_CAUSE_LOCAL_REQUEST);
  535. if ( FAILED(hr2) )
  536. {
  537. LOG((MSP_ERROR, STREAM_PREFIX("StartStream - "
  538. "FireEvent failed - exit 0x%08x"), hr2));
  539. return hr2;
  540. }
  541. }
  542. LOG((MSP_TRACE, STREAM_PREFIX("StartStream - exit S_OK")));
  543. return S_OK;
  544. }
  545. ///////////////////////////////////////////////////////////////////////////////
  546. ///////////////////////////////////////////////////////////////////////////////
  547. //
  548. // ITStream::PauseStream
  549. //
  550. // The app calls this method to indicate that the stream should transition to
  551. // the paused state.
  552. //
  553. // First, a new desired graph state is set on the stream. If there is no
  554. // terminal selected, then this is all that happens, and the stream will pause
  555. // the next time a terminal is selected.
  556. //
  557. // If a terminal has been selected, then this method checks if the wave ID
  558. // has been set. If the wave ID has not been set, this indicates a problem
  559. // with the TSP or the transport filter. An event is fired and an error code
  560. // is returned. If the wave ID has been set, then the terminal is connected
  561. // (if this has not already occured) and, unless TAPI3.DLL has suspended the
  562. // stream due to an outstanding TSPI call, the stream is paused and an event
  563. // is fired. (But the event is only fired if this is an actual Active/Inactive
  564. // transition.)
  565. //
  566. // Arguments: none
  567. //
  568. // Returns HRESULT:
  569. // S_OK - success
  570. // E_FAIL - wave ID not set
  571. // other - from ConnectTerminal
  572. // or FireEvent
  573. // or CMSPStream::StartStream
  574. //
  575. STDMETHODIMP CWaveMSPStream::PauseStream (void)
  576. {
  577. LOG((MSP_TRACE, STREAM_PREFIX("PauseStream - enter")));
  578. CLock lock(m_lock);
  579. m_DesiredGraphState = State_Paused;
  580. //
  581. // Can't pause the stream if no terminal has been selected.
  582. //
  583. if ( 0 == m_Terminals.GetSize() )
  584. {
  585. LOG((MSP_WARN, STREAM_PREFIX("PauseStream - "
  586. "no Terminal so nothing to do yet - exit S_OK")));
  587. return S_OK;
  588. }
  589. //
  590. // Can't start the stream if we don't know the waveid.
  591. // (We create our filters on discovery of the waveid.)
  592. // Here we fire a failure event, as this indicates an
  593. // improperly-installed TSP or a failure during filter
  594. // creation or setup, rendering this stream unusable.
  595. //
  596. if ( ! m_fHaveWaveID )
  597. {
  598. LOG((MSP_WARN, STREAM_PREFIX("PauseStream - "
  599. "no waveid - event + exit E_FAIL")));
  600. FireEvent(CALL_STREAM_FAIL, E_FAIL, CALL_CAUSE_UNKNOWN);
  601. return E_FAIL;
  602. }
  603. //
  604. // Connect the terminal. This does nothing if this call already
  605. // connected the terminal and fails if another call has the
  606. // terminal connected.
  607. //
  608. HRESULT hr;
  609. hr = ConnectTerminal(m_Terminals[0]);
  610. if ( FAILED(hr) )
  611. {
  612. FireEvent(CALL_TERMINAL_FAIL, hr, CALL_CAUSE_CONNECT_FAIL);
  613. FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_CONNECT_FAIL);
  614. LOG((MSP_ERROR, STREAM_PREFIX("StartStream - "
  615. "our ConnectTerminal failed - exit 0x%08x"), hr));
  616. return hr;
  617. }
  618. //
  619. // If the stream is suspended, we're done. Since we've set the
  620. // desired graph state to State_Paused, the rest of this
  621. // process will complete when TAPI resumes the stream.
  622. //
  623. if ( 0 != m_dwSuspendCount )
  624. {
  625. //
  626. // By the way, this is quite normal as the suspend/resume happens
  627. // behind the scenes -- use MSP_TRACE rather than MSP_WARN
  628. //
  629. LOG((MSP_TRACE, STREAM_PREFIX("PauseStream - "
  630. "stream is suspended so terminal is connected but we are not "
  631. "pausing the graph yet - exit S_OK")));
  632. return S_OK;
  633. }
  634. //
  635. // Pause the stream via the base class method.
  636. //
  637. hr = CMSPStream::PauseStream();
  638. if ( FAILED(hr) )
  639. {
  640. //
  641. // Failed to pause -- tell the app.
  642. //
  643. FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_UNKNOWN);
  644. LOG((MSP_ERROR, STREAM_PREFIX("PauseStream - "
  645. "Pause failed - exit 0x%08x"), hr));
  646. return hr;
  647. }
  648. //
  649. // Fire event if this just made us inactive.
  650. //
  651. if ( m_ActualGraphState == State_Running )
  652. {
  653. HRESULT hr2 = FireEvent(CALL_STREAM_INACTIVE, hr, CALL_CAUSE_LOCAL_REQUEST);
  654. if ( FAILED(hr2) )
  655. {
  656. m_ActualGraphState = State_Paused;
  657. LOG((MSP_ERROR, STREAM_PREFIX("PauseStream - "
  658. "FireEvent failed - exit 0x%08x"), hr2));
  659. return hr2;
  660. }
  661. }
  662. m_ActualGraphState = State_Paused;
  663. LOG((MSP_TRACE, STREAM_PREFIX("PauseStream - exit S_OK")));
  664. return S_OK;
  665. }
  666. ///////////////////////////////////////////////////////////////////////////////
  667. ///////////////////////////////////////////////////////////////////////////////
  668. //
  669. // ITStream::StopStream
  670. //
  671. // The app calls this method to indicate that the stream should transition to
  672. // the stopped state. Tapi3.dll also calls this method when the call is
  673. // disconnected (so that streaming stops by default on a disconnected call).
  674. //
  675. // First, a new desired graph state is set on the stream. If there is no
  676. // terminal selected, then this is all that happens, and the stream will start
  677. // the next time a terminal is selected.
  678. //
  679. // If a terminal has been selected, then this method checks if the wave ID
  680. // has been set. If the wave ID has not been set, then there is nothing to do.
  681. // If the wave ID has been set, then the stream is stopped and an event
  682. // is fired. (But the event is only fired if this is an actual Active/Inactive
  683. // transition.)
  684. //
  685. // Arguments: none
  686. //
  687. // Returns HRESULT:
  688. // S_OK - success
  689. // E_FAIL - wave ID not set
  690. // other - from ConnectTerminal
  691. // or FireEvent
  692. // or CMSPStream::StartStream
  693. //
  694. STDMETHODIMP CWaveMSPStream::StopStream (void)
  695. {
  696. LOG((MSP_TRACE, STREAM_PREFIX("StopStream - enter")));
  697. CLock lock(m_lock);
  698. m_DesiredGraphState = State_Stopped;
  699. //
  700. // Nothing to do if we don't know our waveid.
  701. //
  702. if ( ! m_fHaveWaveID )
  703. {
  704. LOG((MSP_WARN, STREAM_PREFIX("StopStream - "
  705. "no waveid - exit S_OK")));
  706. return S_OK;
  707. }
  708. //
  709. // Nothing to do if no terminal has been selected.
  710. //
  711. if ( 0 == m_Terminals.GetSize() )
  712. {
  713. LOG((MSP_WARN, STREAM_PREFIX("StopStream - "
  714. "no Terminal - exit S_OK")));
  715. return S_OK;
  716. }
  717. //
  718. // Nothing special here if we are suspended. Stopping while suspended just
  719. // means that we are already stopped. The StopStream call will do nothing
  720. // and no event will be fired in this case.
  721. //
  722. //
  723. // Stop the stream via the base class method.
  724. //
  725. HRESULT hr;
  726. hr = CMSPStream::StopStream();
  727. if ( FAILED(hr) )
  728. {
  729. //
  730. // Failed to stop -- tell the app.
  731. //
  732. FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_UNKNOWN);
  733. m_DesiredGraphState = m_ActualGraphState;
  734. LOG((MSP_ERROR, STREAM_PREFIX("StopStream - "
  735. "Stop failed - exit 0x%08x"), hr));
  736. return hr;
  737. }
  738. //
  739. // Fire event if this just made us inactive.
  740. //
  741. if ( m_ActualGraphState == State_Running )
  742. {
  743. HRESULT hr2 = FireEvent(CALL_STREAM_INACTIVE, hr, CALL_CAUSE_LOCAL_REQUEST);
  744. if ( FAILED(hr2) )
  745. {
  746. m_ActualGraphState = State_Stopped;
  747. LOG((MSP_ERROR, STREAM_PREFIX("StopStream - "
  748. "FireEvent failed - exit 0x%08x"), hr2));
  749. return hr2;
  750. }
  751. }
  752. m_ActualGraphState = State_Stopped;
  753. LOG((MSP_TRACE, STREAM_PREFIX("StopStream - exit S_OK")));
  754. return S_OK;
  755. }
  756. ///////////////////////////////////////////////////////////////////////////////
  757. ///////////////////////////////////////////////////////////////////////////////
  758. //
  759. HRESULT CWaveMSPStream::SetWaveID(DWORD dwWaveID)
  760. {
  761. LOG((MSP_TRACE, STREAM_PREFIX("SetWaveID - enter")));
  762. CLock lock(m_lock);
  763. //
  764. // create the correct wave filter
  765. //
  766. HRESULT hr;
  767. hr = CoCreateInstance(
  768. (m_Direction == TD_RENDER) ? CLSID_AudioRecord :
  769. CLSID_AudioRender,
  770. NULL,
  771. CLSCTX_INPROC_SERVER,
  772. IID_IBaseFilter,
  773. (void **) &m_pFilter
  774. );
  775. if ( FAILED(hr) )
  776. {
  777. LOG((MSP_ERROR, STREAM_PREFIX("SetWaveID - "
  778. "Filter creation failed - exit 0x%08x"), hr));
  779. return hr;
  780. }
  781. //
  782. // If this is a wavein filter, turn on sample dropping for
  783. // live graphs. Ignore failure here.
  784. //
  785. if ( m_Direction == TD_RENDER )
  786. {
  787. SetLiveMode( TRUE, m_pFilter );
  788. }
  789. CComObject< CMyPropertyBag > * pMyBag;
  790. IPropertyBag * pPropertyBag;
  791. VARIANT var;
  792. //
  793. // create a propertybag
  794. //
  795. hr = CComObject< CMyPropertyBag >::CreateInstance( &pMyBag );
  796. if ( FAILED(hr) )
  797. {
  798. LOG((MSP_ERROR, STREAM_PREFIX("SetWaveID - "
  799. "failed to create property bag - exit 0x%08x")));
  800. m_pFilter->Release();
  801. m_pFilter = NULL;
  802. return hr;
  803. }
  804. //
  805. // create the variant for the propertybag
  806. //
  807. VariantInit( &var );
  808. var.vt = VT_I4;
  809. var.lVal = dwWaveID;
  810. //
  811. // get the correct interface
  812. //
  813. hr = pMyBag->QueryInterface(
  814. IID_IPropertyBag,
  815. (void **) &pPropertyBag
  816. );
  817. if ( FAILED(hr) )
  818. {
  819. LOG((MSP_ERROR, STREAM_PREFIX("SetWaveID - "
  820. "failed to get the proppag interface - exit 0x%08x")));
  821. delete pMyBag;
  822. m_pFilter->Release();
  823. m_pFilter = NULL;
  824. return hr;
  825. }
  826. //
  827. // save the variant in the property bag
  828. //
  829. hr = pPropertyBag->Write(
  830. ( (m_Direction == TD_RENDER) ? (L"WaveInId") :
  831. (L"WaveOutID")),
  832. &var
  833. );
  834. if ( FAILED(hr) )
  835. {
  836. LOG((MSP_ERROR, STREAM_PREFIX("SetWaveID - "
  837. "failed to write to the proppag interface - exit 0x%08x")));
  838. pPropertyBag->Release();
  839. m_pFilter->Release();
  840. m_pFilter = NULL;
  841. return hr;
  842. }
  843. //
  844. // get the IPersistPropertyBag interface
  845. // and save the propertybag through it
  846. //
  847. IPersistPropertyBag * pPersistPropertyBag;
  848. hr = m_pFilter->QueryInterface(
  849. IID_IPersistPropertyBag,
  850. (void **) &pPersistPropertyBag
  851. );
  852. if ( FAILED(hr) )
  853. {
  854. LOG((MSP_ERROR, STREAM_PREFIX("SetWaveID - "
  855. "failed to get the IPersisPropertyBag interface - exit 0x%08x")));
  856. pPropertyBag->Release();
  857. m_pFilter->Release();
  858. m_pFilter = NULL;
  859. return hr;
  860. }
  861. //
  862. // Load() tells the filter object to read in the
  863. // properties it is interested in from the property bag
  864. //
  865. hr = pPersistPropertyBag->Load( pPropertyBag, NULL );
  866. pPropertyBag->Release();
  867. pPersistPropertyBag->Release();
  868. if ( FAILED(hr) )
  869. {
  870. LOG((MSP_ERROR, STREAM_PREFIX("SetWaveID - "
  871. "failed to save device id - exit 0x%08x")));
  872. m_pFilter->Release();
  873. m_pFilter = NULL;
  874. return hr;
  875. }
  876. //
  877. // Before adding a RENDER filter to the graph (ie, the wave filter on
  878. // a CAPTURE stream), do SetDefaultSyncSource
  879. // to enable drop-sample code in DirectShow which will prevent
  880. // forever-increasing latency with mismatched wave clocks.
  881. //
  882. if ( m_Direction == TD_CAPTURE )
  883. {
  884. hr = m_pIGraphBuilder->SetDefaultSyncSource();
  885. if ( FAILED(hr) )
  886. {
  887. LOG((MSP_WARN, STREAM_PREFIX("SetWaveID - "
  888. "SetDefaultSyncSource failed 0x%08x - continuing anyway"), hr));
  889. }
  890. }
  891. //
  892. // Add the filter. Supply a name to make debugging easier.
  893. //
  894. WCHAR * pName = (m_Direction == TD_RENDER) ?
  895. (L"The Stream's WaveIn (on line device)") :
  896. (L"The Stream's WaveOut (on line device)");
  897. hr = m_pIGraphBuilder->AddFilter(m_pFilter, pName);
  898. if ( FAILED(hr) )
  899. {
  900. LOG((MSP_ERROR, STREAM_PREFIX("SetWaveID - "
  901. "AddFilter failed - exit 0x%08x"), hr));
  902. m_pFilter->Release();
  903. m_pFilter = NULL;
  904. return hr;
  905. }
  906. //
  907. // We now have the wave ID.
  908. //
  909. m_fHaveWaveID = TRUE;
  910. LOG((MSP_TRACE, STREAM_PREFIX("SetWaveID - exit S_OK")));
  911. return S_OK;
  912. }
  913. //////////////////////////////////////////////////////////////////////////////
  914. //////////////////////////////////////////////////////////////////////////////
  915. //
  916. // Suspend the stream. Only TAPI itself can ask us to do this. Tapi asks us
  917. // to do this when it's calling a tapi call control function, and the TSP
  918. // requires that its wave devices be closed for the call control function to
  919. // work.
  920. //
  921. HRESULT CWaveMSPStream::SuspendStream(void)
  922. {
  923. LOG((MSP_TRACE, STREAM_PREFIX("SuspendStream - enter")));
  924. CLock lock(m_lock);
  925. m_dwSuspendCount++;
  926. if ( m_dwSuspendCount > 1 )
  927. {
  928. LOG((MSP_TRACE, STREAM_PREFIX("SuspendStream - "
  929. "just bumping up suspend count - exit S_OK")));
  930. return S_OK;
  931. }
  932. //
  933. // We are now suspended. If we're actually not stopped (this
  934. // implies wave ids present, terminal selected, etc.) then
  935. // we need to close the wave device by stopping the stream
  936. // if it's not stopped, send an event to the
  937. // the app saying that the stream is inactive, and update
  938. // m_ActualGraphState so that resuming the stream will fire a
  939. // a stream active event. However we must not change our
  940. // m_DesiredGraphState, as doing so would prevent us from being
  941. // resumed to the correct state.
  942. //
  943. HRESULT hr = S_OK;
  944. if ( m_ActualGraphState != State_Stopped )
  945. {
  946. //
  947. // Stop the stream via the base class method.
  948. //
  949. hr = CMSPStream::StopStream();
  950. //
  951. // Send an event to the app.
  952. //
  953. HRESULT hr2;
  954. if ( SUCCEEDED(hr) )
  955. {
  956. hr2 = FireEvent(CALL_STREAM_INACTIVE, hr, CALL_CAUSE_LOCAL_REQUEST);
  957. }
  958. else
  959. {
  960. hr2 = FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_UNKNOWN);
  961. }
  962. //
  963. // Update the the actual graph state; desired graph state remains the
  964. // same.
  965. //
  966. m_ActualGraphState = State_Stopped;
  967. //
  968. // Debug log only for failed FireEvent.
  969. //
  970. if ( FAILED(hr2) )
  971. {
  972. LOG((MSP_ERROR, STREAM_PREFIX("SuspendStream - "
  973. "FireEvent failed - 0x%08x"), hr2));
  974. }
  975. }
  976. if ( FAILED(hr) )
  977. {
  978. LOG((MSP_ERROR, STREAM_PREFIX("SuspendStream - "
  979. "Stop failed - 0x%08x"), hr));
  980. return hr;
  981. }
  982. LOG((MSP_TRACE, STREAM_PREFIX("SuspendStream - exit S_OK")));
  983. return S_OK;
  984. }
  985. //////////////////////////////////////////////////////////////////////////////
  986. //////////////////////////////////////////////////////////////////////////////
  987. //
  988. // Resume the stream after a SuspendStream. Only TAPI itself can ask us to do
  989. // this. Tapi asks us to do this when it's finished calling a tapi call
  990. // control function, and we can now start using our wave device again.
  991. //
  992. // Tapi does this on the (callback? / async events?) thread, so we post an
  993. // async work item to our own thread to avoid blocking tapi's thread.
  994. //
  995. HRESULT CWaveMSPStream::ResumeStream (void)
  996. {
  997. LOG((MSP_TRACE, STREAM_PREFIX("ResumeStream - enter")));
  998. HRESULT hr;
  999. this->AddRef();
  1000. hr = g_Thread.QueueWorkItem(
  1001. ResumeStreamWI, // the callback
  1002. this, // the context
  1003. FALSE // FALSE -> asynchronous
  1004. );
  1005. if ( FAILED(hr) )
  1006. {
  1007. LOG((MSP_ERROR, STREAM_PREFIX("ResumeStream - "
  1008. "failed to queue work item - exit 0x%08x"), hr));
  1009. this->Release();
  1010. return hr;
  1011. }
  1012. LOG((MSP_TRACE, STREAM_PREFIX("ResumeStream - queued async work item - " \
  1013. "exit S_OK")));
  1014. return S_OK;
  1015. }
  1016. DWORD WINAPI CWaveMSPStream::ResumeStreamWI (VOID * pContext)
  1017. {
  1018. CWaveMSPStream * pStream = (CWaveMSPStream *) pContext;
  1019. pStream->ResumeStreamAsync();
  1020. pStream->Release();
  1021. return S_OK;
  1022. }
  1023. //////////////////////////////////////////////////////////////////////////////
  1024. //////////////////////////////////////////////////////////////////////////////
  1025. //
  1026. // Resume the stream after a SuspendStream. Only TAPI itself can ask us to do
  1027. // this. Tapi asks us to do this when it's finished calling a tapi call
  1028. // control function, and we can now start using our wave device again.
  1029. //
  1030. // This is the actual routine, processed on our worker thread (see above).
  1031. //
  1032. HRESULT CWaveMSPStream::ResumeStreamAsync (void)
  1033. {
  1034. LOG((MSP_TRACE, STREAM_PREFIX("ResumeStreamAsync - enter")));
  1035. CLock lock(m_lock);
  1036. if ( 0 == m_dwSuspendCount )
  1037. {
  1038. LOG((MSP_ERROR, STREAM_PREFIX("ResumeStreamAsync - "
  1039. "resume count was already zero - exit E_UNEXPECTED")));
  1040. return S_OK;
  1041. }
  1042. m_dwSuspendCount--;
  1043. if ( 0 != m_dwSuspendCount )
  1044. {
  1045. LOG((MSP_TRACE, STREAM_PREFIX("ResumeStreamAsync - "
  1046. "just decrementing suspend count - exit S_OK")));
  1047. return S_OK;
  1048. }
  1049. //
  1050. // We are no longer suspended. Try to regain the desired graph state.
  1051. // These methods fire all applicable events automatically.
  1052. //
  1053. HRESULT hr;
  1054. if ( m_DesiredGraphState == State_Paused )
  1055. {
  1056. hr = PauseStream();
  1057. }
  1058. else if ( m_DesiredGraphState == State_Running )
  1059. {
  1060. hr = StartStream();
  1061. }
  1062. else
  1063. {
  1064. _ASSERTE( m_DesiredGraphState == State_Stopped );
  1065. hr = S_OK;
  1066. }
  1067. if ( FAILED(hr) )
  1068. {
  1069. LOG((MSP_TRACE, STREAM_PREFIX("ResumeStreamAsync - "
  1070. "can't regain old graph state - exit 0x%08x"), hr));
  1071. return hr;
  1072. }
  1073. LOG((MSP_TRACE, STREAM_PREFIX("ResumeStreamAsync - exit S_OK")));
  1074. return S_OK;
  1075. }
  1076. //////////////////////////////////////////////////////////////////////////////
  1077. //
  1078. // ProcessSoundDeviceEvent
  1079. //
  1080. // Called only from within ProcessGraphEvent. This function outputs some trace
  1081. // info indicating the details of the sound device event received, and fires
  1082. // appropriate events to the application.
  1083. //
  1084. HRESULT CWaveMSPStream::ProcessSoundDeviceEvent(
  1085. IN long lEventCode,
  1086. IN LONG_PTR lParam1,
  1087. IN LONG_PTR lParam2
  1088. )
  1089. {
  1090. LOG((MSP_EVENT, STREAM_PREFIX("ProcessSoundDeviceEvent - enter")));
  1091. #ifdef MSPLOG
  1092. //
  1093. // Spew some debug output to indicate what this is.
  1094. //
  1095. char * pszType;
  1096. switch ( lParam1 )
  1097. {
  1098. case SNDDEV_ERROR_Open:
  1099. pszType = "SNDDEV_ERROR_Open";
  1100. break;
  1101. case SNDDEV_ERROR_Close:
  1102. pszType = "SNDDEV_ERROR_Close";
  1103. break;
  1104. case SNDDEV_ERROR_GetCaps:
  1105. pszType = "SNDDEV_ERROR_GetCaps";
  1106. break;
  1107. case SNDDEV_ERROR_PrepareHeader:
  1108. pszType = "SNDDEV_ERROR_PrepareHeader";
  1109. break;
  1110. case SNDDEV_ERROR_UnprepareHeader:
  1111. pszType = "SNDDEV_ERROR_UnprepareHeader";
  1112. break;
  1113. case SNDDEV_ERROR_Reset:
  1114. pszType = "SNDDEV_ERROR_Reset";
  1115. break;
  1116. case SNDDEV_ERROR_Restart:
  1117. pszType = "SNDDEV_ERROR_Restart";
  1118. break;
  1119. case SNDDEV_ERROR_GetPosition:
  1120. pszType = "SNDDEV_ERROR_GetPosition";
  1121. break;
  1122. case SNDDEV_ERROR_Write:
  1123. pszType = "SNDDEV_ERROR_Write";
  1124. break;
  1125. case SNDDEV_ERROR_Pause:
  1126. pszType = "SNDDEV_ERROR_Pause";
  1127. break;
  1128. case SNDDEV_ERROR_Stop:
  1129. pszType = "USNDDEV_ERROR_Stop";
  1130. break;
  1131. case SNDDEV_ERROR_Start:
  1132. pszType = "SNDDEV_ERROR_Start";
  1133. break;
  1134. case SNDDEV_ERROR_AddBuffer:
  1135. pszType = "SNDDEV_ERROR_AddBuffer";
  1136. break;
  1137. case SNDDEV_ERROR_Query:
  1138. pszType = "SNDDEV_ERROR_Query";
  1139. break;
  1140. default:
  1141. pszType = "Unknown sound device call";
  1142. break;
  1143. }
  1144. LOG((MSP_EVENT, STREAM_PREFIX("ProcessSoundDeviceEvent - "
  1145. "EVENT DUMP: type = %s; "), pszType));
  1146. LOG((MSP_EVENT, STREAM_PREFIX("ProcessSoundDeviceEvent - "
  1147. "EVENT DUMP: this event is for a %s device"),
  1148. ( lEventCode == EC_SNDDEV_IN_ERROR ) ? "capture" :
  1149. "render"));
  1150. //
  1151. // The rest of the info is dumped in FireEvent if logging is enabled.
  1152. //
  1153. #endif // ifdef MSPLOG
  1154. //
  1155. // Determine the error code to use when firing events.
  1156. //
  1157. HRESULT hr;
  1158. switch ( lParam2 )
  1159. {
  1160. case MMSYSERR_NOERROR: // no error
  1161. hr = S_OK;
  1162. break;
  1163. case MMSYSERR_ERROR: // unspecified error
  1164. case MMSYSERR_BADDB: // bad registry database
  1165. case MMSYSERR_KEYNOTFOUND: // registry key not found
  1166. case MMSYSERR_READERROR: // registry read error
  1167. case MMSYSERR_WRITEERROR: // registry write error
  1168. case MMSYSERR_DELETEERROR: // registry delete error
  1169. case MMSYSERR_VALNOTFOUND: // registry value not found
  1170. hr = E_FAIL;
  1171. break;
  1172. case MMSYSERR_ALLOCATED: // device already allocated
  1173. hr = TAPI_E_ALLOCATED;
  1174. break;
  1175. case MMSYSERR_NOMEM: // memory allocation error
  1176. hr = E_OUTOFMEMORY;
  1177. break;
  1178. case MMSYSERR_BADDEVICEID: // device ID out of range
  1179. case MMSYSERR_NOTENABLED: // driver failed enable
  1180. case MMSYSERR_INVALHANDLE: // device handle is invalid
  1181. case MMSYSERR_NODRIVER: // no device driver present
  1182. case MMSYSERR_NOTSUPPORTED: // function isn't supported
  1183. case MMSYSERR_BADERRNUM: // error value out of range
  1184. case MMSYSERR_INVALFLAG: // invalid flag passed
  1185. case MMSYSERR_INVALPARAM: // invalid parameter passed
  1186. case MMSYSERR_HANDLEBUSY: // handle being used simultaneously on another
  1187. // thread (eg callback)
  1188. case MMSYSERR_INVALIDALIAS: // specified alias not found
  1189. case MMSYSERR_NODRIVERCB: // driver does not call DriverCallback
  1190. case MMSYSERR_MOREDATA: // more data to be returned
  1191. default:
  1192. hr = E_UNEXPECTED; // these would appear to indicate a bug in the wave
  1193. // driver, Quartz, or Qcap)
  1194. break;
  1195. }
  1196. //
  1197. // If this event concerns our terminal, fire a terminal fail event to the
  1198. // app.
  1199. //
  1200. if ( ( m_Direction == TD_CAPTURE ) ==
  1201. ( lEventCode == EC_SNDDEV_IN_ERROR ) )
  1202. {
  1203. FireEvent(CALL_TERMINAL_FAIL, hr, CALL_CAUSE_BAD_DEVICE);
  1204. }
  1205. //
  1206. // Fire a stream failed event to the app. Even if the event concerned a
  1207. // terminal and not the stream, since we currently have only one
  1208. // terminal per stream, the failure of a terminal results in the failure
  1209. // of a stream.
  1210. //
  1211. FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_BAD_DEVICE);
  1212. LOG((MSP_EVENT, STREAM_PREFIX("ProcessSoundDeviceEvent - exit S_OK")));
  1213. return S_OK;
  1214. }
  1215. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1216. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1217. //
  1218. // ProcessGraphEvent
  1219. //
  1220. // Sends an event to the app when we get an event from the filter graph.
  1221. //
  1222. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1223. HRESULT CWaveMSPStream::ProcessGraphEvent(
  1224. IN long lEventCode,
  1225. IN LONG_PTR lParam1,
  1226. IN LONG_PTR lParam2
  1227. )
  1228. {
  1229. LOG((MSP_EVENT, STREAM_PREFIX("ProcessGraphEvent - enter")));
  1230. HRESULT hr = S_OK;
  1231. switch (lEventCode)
  1232. {
  1233. case EC_COMPLETE:
  1234. hr = FireEvent(CALL_STREAM_INACTIVE,
  1235. (HRESULT) lParam1,
  1236. CALL_CAUSE_UNKNOWN);
  1237. break;
  1238. case EC_USERABORT:
  1239. hr = FireEvent(CALL_STREAM_INACTIVE, S_OK, CALL_CAUSE_UNKNOWN);
  1240. break;
  1241. case EC_ERRORABORT:
  1242. case EC_STREAM_ERROR_STOPPED:
  1243. case EC_STREAM_ERROR_STILLPLAYING:
  1244. case EC_ERROR_STILLPLAYING:
  1245. hr = FireEvent(CALL_STREAM_FAIL,
  1246. (HRESULT) lParam1,
  1247. CALL_CAUSE_UNKNOWN);
  1248. break;
  1249. case EC_SNDDEV_IN_ERROR:
  1250. case EC_SNDDEV_OUT_ERROR:
  1251. //
  1252. // async error accessing an audio device
  1253. //
  1254. ProcessSoundDeviceEvent(lEventCode, lParam1, lParam2);
  1255. break;
  1256. default:
  1257. LOG((MSP_EVENT, STREAM_PREFIX("ProcessGraphEvent - "
  1258. "ignoring event code %d"), lEventCode));
  1259. break;
  1260. }
  1261. if ( FAILED(hr) )
  1262. {
  1263. LOG((MSP_ERROR, STREAM_PREFIX("ProcessGraphEvent - "
  1264. "FireEvent failed - exit 0x%08x"), hr));
  1265. return hr;
  1266. }
  1267. LOG((MSP_EVENT, STREAM_PREFIX("ProcessGraphEvent - exit S_OK")));
  1268. return S_OK;
  1269. }
  1270. ////////////////////////////////////////////////////////////////////////
  1271. //
  1272. // FireEvent
  1273. //
  1274. // Fires an event to the application. Does its own locking.
  1275. //
  1276. HRESULT CWaveMSPStream::FireEvent(
  1277. IN MSP_CALL_EVENT type,
  1278. IN HRESULT hrError,
  1279. IN MSP_CALL_EVENT_CAUSE cause
  1280. )
  1281. {
  1282. LOG((MSP_EVENT, STREAM_PREFIX("FireEvent - enter")));
  1283. //
  1284. // First, need to check if the call is shutting down. This is important
  1285. // because UnselectTerminal can fire an event, and UnselectTerminal can
  1286. // be called within ITStream::Shutdown. We can safely discard such
  1287. // events because there is nothing the app can do with them anyway.
  1288. //
  1289. // Note on locking: It is convenient to check the m_pMSPCall here
  1290. // and we don't use it until the end of the method, so we simply lock
  1291. // during the entire method. This could be optimized at the expense of
  1292. // some code complexity; note that we also need to lock while accessing
  1293. // m_Terminals.
  1294. //
  1295. CLock lock(m_lock);
  1296. if ( m_pMSPCall == NULL )
  1297. {
  1298. LOG((MSP_EVENT, STREAM_PREFIX("FireEvent - "
  1299. "call is shutting down; dropping event - exit S_OK")));
  1300. return S_OK;
  1301. }
  1302. //
  1303. // Create the event structure. Must use "new" as it will be
  1304. // "delete"d later.
  1305. //
  1306. MSPEVENTITEM * pEventItem = AllocateEventItem();
  1307. if (pEventItem == NULL)
  1308. {
  1309. LOG((MSP_ERROR, STREAM_PREFIX("FireEvent - "
  1310. "can't create MSPEVENTITEM structure - exit E_OUTOFMEMORY")));
  1311. return E_OUTOFMEMORY;
  1312. }
  1313. //
  1314. // Fill in the necessary fields for the event structure.
  1315. //
  1316. pEventItem->MSPEventInfo.dwSize = sizeof(MSP_EVENT_INFO);
  1317. pEventItem->MSPEventInfo.Event = ME_CALL_EVENT;
  1318. ITTerminal * pTerminal = NULL;
  1319. if ( 0 != m_Terminals.GetSize() )
  1320. {
  1321. _ASSERTE( 1 == m_Terminals.GetSize() );
  1322. pTerminal = m_Terminals[0];
  1323. pTerminal->AddRef();
  1324. }
  1325. ITStream * pStream = (ITStream *) this;
  1326. pStream->AddRef();
  1327. pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.Type = type;
  1328. pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.Cause = cause;
  1329. pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.pStream = pStream;
  1330. pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.pTerminal = pTerminal;
  1331. pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.hrError = hrError;
  1332. #ifdef MSPLOG
  1333. //
  1334. // Spew some debug output to indicate what this is.
  1335. //
  1336. char * pszType;
  1337. DWORD dwLevel;
  1338. switch (type)
  1339. {
  1340. case CALL_NEW_STREAM:
  1341. pszType = "CALL_NEW_STREAM (unexpected)";
  1342. dwLevel = MSP_ERROR;
  1343. break;
  1344. case CALL_STREAM_FAIL:
  1345. pszType = "CALL_STREAM_FAIL";
  1346. dwLevel = MSP_INFO;
  1347. break;
  1348. case CALL_TERMINAL_FAIL:
  1349. pszType = "CALL_TERMINAL_FAIL";
  1350. dwLevel = MSP_INFO;
  1351. break;
  1352. case CALL_STREAM_NOT_USED:
  1353. pszType = "CALL_STREAM_NOT_USED (unexpected)";
  1354. dwLevel = MSP_ERROR;
  1355. break;
  1356. case CALL_STREAM_ACTIVE:
  1357. pszType = "CALL_STREAM_ACTIVE";
  1358. dwLevel = MSP_INFO;
  1359. break;
  1360. case CALL_STREAM_INACTIVE:
  1361. pszType = "CALL_STREAM_INACTIVE";
  1362. dwLevel = MSP_INFO;
  1363. break;
  1364. default:
  1365. pszType = "UNKNOWN EVENT TYPE";
  1366. dwLevel = MSP_ERROR;
  1367. break;
  1368. }
  1369. LOG((dwLevel, STREAM_PREFIX("FireEvent - "
  1370. "EVENT DUMP: type = %s"), pszType));
  1371. LOG((dwLevel, STREAM_PREFIX("FireEvent - "
  1372. "EVENT DUMP: pStream = %p"), pStream));
  1373. LOG((dwLevel, STREAM_PREFIX("FireEvent - "
  1374. "EVENT DUMP: pTerminal = %p"), pTerminal));
  1375. LOG((dwLevel, STREAM_PREFIX("FireEvent - "
  1376. "EVENT DUMP: hrError = %08x"), hrError));
  1377. #endif // ifdef MSPLOG
  1378. //
  1379. // Send the event to the app.
  1380. //
  1381. HRESULT hr = m_pMSPCall->HandleStreamEvent(pEventItem);
  1382. if (FAILED(hr))
  1383. {
  1384. LOG((MSP_ERROR, STREAM_PREFIX("FireEvent - "
  1385. "HandleStreamEvent failed - returning 0x%08x"), hr));
  1386. pStream->Release();
  1387. pTerminal->Release();
  1388. FreeEventItem(pEventItem);
  1389. return hr;
  1390. }
  1391. LOG((MSP_EVENT, STREAM_PREFIX("FireEvent - exit S_OK")));
  1392. return S_OK;
  1393. }
  1394. //////////////////////////////////////////////////////////////////////////////
  1395. //////////////////////////////////////////////////////////////////////////////
  1396. //////////////////////////////////////////////////////////////////////////////
  1397. //
  1398. // The rest of this file deals with the connection path.
  1399. // This could be pulled out into separate file in the future.
  1400. //
  1401. //////////////////////////////////////////////////////////////////////////////
  1402. //////////////////////////////////////////////////////////////////////////////
  1403. //////////////////////////////////////////////////////////////////////////////
  1404. //////////////////////////////////////////////////////////////////////////////
  1405. //////////////////////////////////////////////////////////////////////////////
  1406. //
  1407. // Create the G711 filter, which we will try to connect if direct
  1408. // connection fails.
  1409. //
  1410. HRESULT CWaveMSPStream::AddG711()
  1411. {
  1412. LOG((MSP_TRACE, STREAM_PREFIX("AddG711 - enter")));
  1413. //
  1414. // if we don't yet have the G711 filter, create it
  1415. //
  1416. HRESULT hr = S_OK;
  1417. if (NULL == m_pG711Filter)
  1418. {
  1419. hr = CoCreateInstance(
  1420. CLSID_G711Codec,
  1421. NULL,
  1422. CLSCTX_INPROC_SERVER,
  1423. IID_IBaseFilter,
  1424. (void **) &m_pG711Filter
  1425. );
  1426. if ( FAILED(hr) )
  1427. {
  1428. LOG((MSP_ERROR, STREAM_PREFIX("AddG711 - Failed to create G711 codec: %lx"), hr));
  1429. //
  1430. // Method #2 for connection will not be available.
  1431. //
  1432. m_pG711Filter = NULL;
  1433. return hr;
  1434. }
  1435. LOG((MSP_TRACE, STREAM_PREFIX("AddG711 - created filter [%p]"), m_pG711Filter));
  1436. }
  1437. //
  1438. // add the G711 filter to the graph
  1439. //
  1440. hr = m_pIGraphBuilder->AddFilter(
  1441. m_pG711Filter,
  1442. NULL
  1443. );
  1444. if ( FAILED(hr) )
  1445. {
  1446. LOG((MSP_ERROR, STREAM_PREFIX("AddG711 - Failed to add G711 filter: %lx"), hr));
  1447. //
  1448. // If we couldn't add it to the graph, then it's useless to us.
  1449. // Method #2 for connection will not be available.
  1450. //
  1451. m_pG711Filter->Release();
  1452. m_pG711Filter = NULL;
  1453. return hr;
  1454. }
  1455. LOG((MSP_TRACE, STREAM_PREFIX("AddG711 - finish")));
  1456. return S_OK;
  1457. }
  1458. //////////////////////////////////////////////////////////////////////////////
  1459. //
  1460. // CWaveMSPStream::RemoveAllFilters
  1461. //
  1462. // this method cleans filter graph by removing and releasing all the filters
  1463. // if the graph cannot be cleaned, this method returns an error that indicates
  1464. // a failure and does not guarantee that the graph remains in its original
  1465. // state. in fact, in case of error, the caller should assume that the graph
  1466. // can no longer be used.
  1467. //
  1468. HRESULT CWaveMSPStream::RemoveAllFilters()
  1469. {
  1470. LOG((MSP_INFO, STREAM_PREFIX("RemoveAllFilters - enter.")));
  1471. //
  1472. // get an enumeration with all the filters in the filter graph
  1473. //
  1474. IEnumFilters *pFilterEnumeration = NULL;
  1475. HRESULT hr = m_pIGraphBuilder->EnumFilters( &pFilterEnumeration );
  1476. if (FAILED(hr))
  1477. {
  1478. LOG((MSP_ERROR,
  1479. STREAM_PREFIX("RemoveAllFilters - failed to enumerate filters. hr = %lx"), hr));
  1480. return hr;
  1481. }
  1482. //
  1483. // we will keep the last error from RemoveFilter if it fails
  1484. //
  1485. HRESULT hrFIlterRemovalError = S_OK;
  1486. //
  1487. // walk through the enumeration and remove and release each filter
  1488. //
  1489. while (TRUE)
  1490. {
  1491. IBaseFilter *pFilter = NULL;
  1492. ULONG nFiltersFetched = 0;
  1493. hr = pFilterEnumeration->Next(1, &pFilter, &nFiltersFetched);
  1494. //
  1495. // did the enumeration fail?
  1496. //
  1497. if (FAILED(hr))
  1498. {
  1499. LOG((MSP_ERROR,
  1500. STREAM_PREFIX("RemoveAllFilters - failed to fetch another filter. hr = %lx"), hr));
  1501. break;
  1502. }
  1503. //
  1504. // did we reach the end of the enumeration?
  1505. //
  1506. if ( hr != S_OK )
  1507. {
  1508. LOG((MSP_INFO,
  1509. STREAM_PREFIX("RemoveAllFilters - no more filters in the enumeration")));
  1510. //
  1511. // if there was an error removing a filter, keep it in hr
  1512. //
  1513. hr = hrFIlterRemovalError;
  1514. break;
  1515. }
  1516. LOG((MSP_INFO, STREAM_PREFIX("RemoveAllFilters - removing filter %p."), pFilter));
  1517. //
  1518. // we got a filter. remove it from the graph and then release it
  1519. //
  1520. hr = m_pIGraphBuilder->RemoveFilter( pFilter );
  1521. if (FAILED(hr))
  1522. {
  1523. //
  1524. // we failed to remove a filter from the graph. it is not safe to
  1525. // continue to use the graph. so we will continue to remove all
  1526. // other filters and then return the error
  1527. //
  1528. hrFIlterRemovalError = hr;
  1529. LOG((MSP_ERROR,
  1530. STREAM_PREFIX("RemoveAllFilters - failed to remove filter [%p]. hr = %lx"),
  1531. pFilter, hr));
  1532. }
  1533. else
  1534. {
  1535. //
  1536. // reset enumeration -- the set of filters in the enumeration needs
  1537. // to be updated.
  1538. //
  1539. //
  1540. // note: we only need to reset enumeration if Remove succeeded.
  1541. // otherwise, we could get into an infinite loop trying to remove
  1542. // failing filter
  1543. //
  1544. hr = pFilterEnumeration->Reset();
  1545. if (FAILED(hr))
  1546. {
  1547. //
  1548. // log a message, but don't do anything else -- next() will most
  1549. // likely fail and that error will be handled
  1550. //
  1551. LOG((MSP_ERROR,
  1552. STREAM_PREFIX("RemoveAllFilters - failed to reset enumeration. hr = %lx"),
  1553. hr));
  1554. }
  1555. }
  1556. pFilter->Release();
  1557. pFilter = NULL;
  1558. }
  1559. //
  1560. // done with the enumeration
  1561. //
  1562. pFilterEnumeration->Release();
  1563. pFilterEnumeration = NULL;
  1564. //
  1565. // note that an error returned from this method means that the graph could
  1566. // not be cleaned and is not guaranteed to be in useable state.
  1567. //
  1568. LOG((MSP_(hr), STREAM_PREFIX("RemoveAllFilters - finish. hr = %lx"), hr));
  1569. return hr;
  1570. }
  1571. //////////////////////////////////////////////////////////////////////////////
  1572. //
  1573. // CWaveMSPStream::CleanFilterGraph
  1574. //
  1575. //
  1576. // this function removes all the filters from the filter graph and then readds
  1577. // the wave filter
  1578. //
  1579. // if the method returns a failure, the graph is in undefined state and cannot
  1580. // be used.
  1581. //
  1582. HRESULT CWaveMSPStream::CleanFilterGraph()
  1583. {
  1584. LOG((MSP_TRACE, STREAM_PREFIX("CleanFilterGraph - enter")));
  1585. //
  1586. // completely clean filter graph
  1587. //
  1588. HRESULT hr = RemoveAllFilters();
  1589. if (FAILED(hr))
  1590. {
  1591. LOG((MSP_ERROR,
  1592. STREAM_PREFIX("CleanFilterGraph - remove all filters 0x%x"), hr));
  1593. return hr;
  1594. }
  1595. //
  1596. // Add the wave filter back to the graph
  1597. //
  1598. hr = m_pIGraphBuilder->AddFilter( m_pFilter,
  1599. NULL );
  1600. if ( FAILED(hr) )
  1601. {
  1602. LOG((MSP_ERROR,
  1603. STREAM_PREFIX("CleanFilterGraph - failed to re-add filter: 0x%x"), hr));
  1604. return hr;
  1605. }
  1606. LOG((MSP_TRACE, STREAM_PREFIX("CleanFilterGraph - exit")));
  1607. return S_OK;
  1608. }
  1609. //////////////////////////////////////////////////////////////////////////////
  1610. //////////////////////////////////////////////////////////////////////////////
  1611. //
  1612. // Remove and readd the terminal. This is only needed between a successful
  1613. // intelligent connection and the subsequent reconnection.
  1614. //
  1615. //////////////////////////////////////////////////////////////////////////////
  1616. HRESULT CWaveMSPStream::RemoveTerminal()
  1617. {
  1618. LOG((MSP_TRACE, STREAM_PREFIX("RemoveTerminal - enter")));
  1619. //
  1620. // verify the assumptin that we have exactly one terminal
  1621. //
  1622. if (1 != m_Terminals.GetSize() )
  1623. {
  1624. LOG((MSP_ERROR,
  1625. STREAM_PREFIX("RemoveTerminal - expecting one terminal. have %d "),
  1626. m_Terminals.GetSize()));
  1627. _ASSERTE(FALSE);
  1628. return E_UNEXPECTED;
  1629. }
  1630. //
  1631. // Get the ITTerminalControl interface.
  1632. //
  1633. ITTerminalControl *pTerminalControl = NULL;
  1634. HRESULT hr = m_Terminals[0]->QueryInterface(IID_ITTerminalControl,
  1635. (void **) &pTerminalControl);
  1636. if ( FAILED(hr) )
  1637. {
  1638. LOG((MSP_ERROR,
  1639. STREAM_PREFIX("RemoveTerminal - QI for ITTerminalControl failed hr = 0x%x"), hr));
  1640. return hr;
  1641. }
  1642. //
  1643. // Disconnect the terminal (this also removes it from the filter graph)
  1644. //
  1645. hr = pTerminalControl->DisconnectTerminal(m_pIGraphBuilder, 0);
  1646. pTerminalControl->Release();
  1647. pTerminalControl = NULL;
  1648. if ( FAILED(hr) )
  1649. {
  1650. LOG((MSP_ERROR,
  1651. STREAM_PREFIX("RemoveTerminal - DisconnectTerminal failed hr = 0x%x"), hr));
  1652. return hr;
  1653. }
  1654. LOG((MSP_TRACE, STREAM_PREFIX("RemoveTerminal - exit")));
  1655. return S_OK;
  1656. }
  1657. //////////////////////////////////////////////////////////////////////////////
  1658. HRESULT CWaveMSPStream::ReAddTerminal()
  1659. {
  1660. LOG((MSP_TRACE, STREAM_PREFIX("ReAddTerminal - enter")));
  1661. //
  1662. // verify the assumptin that we have exactly one terminal
  1663. //
  1664. if (1 != m_Terminals.GetSize() )
  1665. {
  1666. LOG((MSP_ERROR,
  1667. STREAM_PREFIX("RemoveTerminal - expecting one terminal. have %d "),
  1668. m_Terminals.GetSize()));
  1669. _ASSERTE(FALSE);
  1670. return E_UNEXPECTED;
  1671. }
  1672. //
  1673. // Get the ITTerminalControl interface.
  1674. //
  1675. ITTerminalControl *pTerminalControl = NULL;
  1676. HRESULT hr = m_Terminals[0]->QueryInterface(IID_ITTerminalControl,
  1677. (void **) &pTerminalControl);
  1678. if ( FAILED(hr) )
  1679. {
  1680. LOG((MSP_ERROR,
  1681. STREAM_PREFIX("ReAddTerminal - QI for ITTerminalControl failed hr = 0x%x"), hr));
  1682. return hr;
  1683. }
  1684. //
  1685. // Find out how many pins the terminal has. If not one then bail as
  1686. // we have no idea what to do with multiple-pin terminals at this point.
  1687. //
  1688. DWORD dwNumPinsAvailable = 0;
  1689. hr = pTerminalControl->ConnectTerminal(m_pIGraphBuilder,
  1690. m_Direction,
  1691. &dwNumPinsAvailable,
  1692. NULL);
  1693. if ( FAILED(hr) )
  1694. {
  1695. LOG((MSP_ERROR, STREAM_PREFIX("ReAddTerminal - "
  1696. "query for number of terminal pins failed 0x%x)"), hr));
  1697. pTerminalControl->Release();
  1698. pTerminalControl = NULL;
  1699. return hr;
  1700. }
  1701. if ( 1 != dwNumPinsAvailable )
  1702. {
  1703. LOG((MSP_ERROR,
  1704. STREAM_PREFIX("ReAddTerminal - unsupported number of terminal pins %ld ")));
  1705. pTerminalControl->Release();
  1706. pTerminalControl = NULL;
  1707. return E_INVALIDARG;
  1708. }
  1709. //
  1710. // Before adding a RENDER filter to the graph (ie, the terminal on
  1711. // a RENDER stream), do SetDefaultSyncSource
  1712. // to enable drop-sample code in DirectShow which will prevent
  1713. // forever-increasing latency with mismatched wave clocks.
  1714. //
  1715. if ( m_Direction == TD_RENDER )
  1716. {
  1717. hr = m_pIGraphBuilder->SetDefaultSyncSource();
  1718. if ( FAILED(hr) )
  1719. {
  1720. LOG((MSP_WARN,
  1721. STREAM_PREFIX(
  1722. "ReAddTerminal - SetDefaultSyncSource failed hr = 0x%x - continuing anyway"),
  1723. hr));
  1724. }
  1725. }
  1726. //
  1727. // Actually connect the terminal.
  1728. //
  1729. IPin *pTerminalPin = NULL;
  1730. hr = pTerminalControl->ConnectTerminal(m_pIGraphBuilder,
  1731. m_Direction,
  1732. &dwNumPinsAvailable,
  1733. &pTerminalPin);
  1734. pTerminalControl->Release();
  1735. pTerminalControl = NULL;
  1736. if ( FAILED(hr) )
  1737. {
  1738. LOG((MSP_ERROR,
  1739. STREAM_PREFIX("ReAddTerminal - ConnectTerminal on terminal failed hr = 0x%x"),
  1740. hr));
  1741. return hr;
  1742. }
  1743. //
  1744. // also try to check if the terminal returned a bad pin.
  1745. //
  1746. if ( IsBadReadPtr(pTerminalPin, sizeof(IPin)) )
  1747. {
  1748. LOG((MSP_ERROR, STREAM_PREFIX("ReAddTerminal - "
  1749. "ConnectTerminal on terminal succeeded but returned a bad pin ")));
  1750. return E_FAIL;
  1751. }
  1752. pTerminalPin->Release();
  1753. pTerminalPin = NULL;
  1754. LOG((MSP_TRACE, STREAM_PREFIX("ReAddTerminal- exit")));
  1755. return S_OK;
  1756. }
  1757. //////////////////////////////////////////////////////////////////////////////
  1758. //////////////////////////////////////////////////////////////////////////////
  1759. //
  1760. // DecideDesiredCaptureBufferSize
  1761. //
  1762. // This method must be called when the graph is connected. It uses the
  1763. // connection format on the passed-in pin to determine how many bytes should
  1764. // be used in each buffer to achieve DESIRED_BUFFER_SIZE_MS milliseconds of
  1765. // sound in each buffer.
  1766. //
  1767. // In the past, on slow machines with buggy wave drivers, small sample sizes
  1768. // have led to bad audio quality, invariably on devices that are not designed
  1769. // for interactive use anyway, where latency would be important. We've been
  1770. // successful in getting key wave drivers fixed to work well with small
  1771. // buffer sizes. It doesn't make sense to increase the default buffer size for
  1772. // the benefit of buggy drivers, as we want to achieve low latency with the
  1773. // good drivers, which is now pretty much all of them. If someone wants to use
  1774. // the WaveMSP for interactive calls and they have a really lousy driver, they
  1775. // need to get the driver fixed. Nevertheless it may be a good idea to make
  1776. // a registry value for this, for the rare case where fixing the driver may
  1777. // not be possible or convenient.
  1778. //
  1779. static const long DESIRED_BUFFER_SIZE_MS = 20; // milliseconds
  1780. HRESULT CWaveMSPStream::DecideDesiredCaptureBufferSize(IPin * pPin,
  1781. long * plDesiredSize)
  1782. {
  1783. LOG((MSP_TRACE, STREAM_PREFIX("DecideDesiredCaptureBufferSize - "
  1784. "enter")));
  1785. _ASSERTE( ! IsBadReadPtr(pPin, sizeof(IPin)) );
  1786. _ASSERTE( ! IsBadWritePtr(plDesiredSize, sizeof(long)) );
  1787. //
  1788. // Get the format being used for this pin's connection.
  1789. //
  1790. HRESULT hr;
  1791. AM_MEDIA_TYPE MediaType;
  1792. hr = pPin->ConnectionMediaType( & MediaType );
  1793. if ( FAILED(hr) )
  1794. {
  1795. LOG((MSP_ERROR, STREAM_PREFIX("DecideDesiredCaptureBufferSize - "
  1796. "ConnectionMediaType failed - hr = 0x%08x"), hr));
  1797. return hr;
  1798. }
  1799. _ASSERTE( MediaType.formattype == FORMAT_WaveFormatEx );
  1800. _ASSERTE( MediaType.cbFormat >= sizeof(WAVEFORMATEX) );
  1801. //
  1802. // Calculate the desired capture buffer size.
  1803. //
  1804. *plDesiredSize = DESIRED_BUFFER_SIZE_MS *
  1805. ((WAVEFORMATEX *) (MediaType.pbFormat) )->nChannels *
  1806. ( ((WAVEFORMATEX *) (MediaType.pbFormat) )->nSamplesPerSec / 1000 ) *
  1807. ( ((WAVEFORMATEX *) (MediaType.pbFormat) )->wBitsPerSample / 8 );
  1808. FreeMediaType( MediaType );
  1809. LOG((MSP_TRACE, STREAM_PREFIX("DecideDesiredCaptureBufferSize - "
  1810. "exit S_OK")));
  1811. return S_OK;
  1812. }
  1813. //////////////////////////////////////////////////////////////////////////////
  1814. //////////////////////////////////////////////////////////////////////////////
  1815. //
  1816. // ConfigureCapture
  1817. //
  1818. // This is a helper function that sets up the allocator properties on the
  1819. // capture filter, given the terminal's pin and our filter's pin. This
  1820. // involves determining if either of the pins belongs to an MST, since we
  1821. // don't want to set the default buffer size in the non-interactive case,
  1822. // but if the input pin belongs to an MST, we want to propagate the MST's
  1823. // properties to the output pin. If no MSTs are involved, then we set the
  1824. // passed-in default buffer size.
  1825. //
  1826. // We are already in a lock; no need to do locking here.
  1827. //
  1828. HRESULT CWaveMSPStream::ConfigureCapture(
  1829. IN IPin * pOutputPin,
  1830. IN IPin * pInputPin,
  1831. IN long lDefaultBufferSize
  1832. )
  1833. {
  1834. LOG((MSP_TRACE, STREAM_PREFIX("ConfigureCapture - enter")));
  1835. //
  1836. // If the output pin belongs to an MST, then we do not want
  1837. // to mess with its allocator properties.
  1838. //
  1839. HRESULT hr;
  1840. ITAllocatorProperties * pProperties;
  1841. hr = pOutputPin->QueryInterface(IID_ITAllocatorProperties,
  1842. (void **) &pProperties);
  1843. if ( SUCCEEDED(hr) )
  1844. {
  1845. pProperties->Release();
  1846. LOG((MSP_TRACE, STREAM_PREFIX("ConfigureCapture - "
  1847. "output pin is on an MST - not changing capture "
  1848. "allocator properties - exit S_OK")));
  1849. return S_OK;
  1850. }
  1851. //
  1852. // Ask the output pin for its buffer negotiation interface.
  1853. // This will be used to suggest allocator propreties on the
  1854. // output pin.
  1855. //
  1856. IAMBufferNegotiation * pNegotiation;
  1857. hr = pOutputPin->QueryInterface(IID_IAMBufferNegotiation,
  1858. (void **) &pNegotiation);
  1859. if ( FAILED(hr) )
  1860. {
  1861. LOG((MSP_ERROR, STREAM_PREFIX("ConfigureCapture - "
  1862. "IAMBufferNegotiation QI failed - exit 0x%08x"), hr));
  1863. return hr;
  1864. }
  1865. //
  1866. // If the input pin belongs to an MST and the MST divulges its
  1867. // allocator properties, then we just propage them to the output
  1868. // pin. Otherwise we set our own default properties on the
  1869. // output pin.
  1870. //
  1871. ALLOCATOR_PROPERTIES props;
  1872. hr = pInputPin->QueryInterface(IID_ITAllocatorProperties,
  1873. (void **) &pProperties);
  1874. if ( SUCCEEDED(hr) )
  1875. {
  1876. hr = pProperties->GetAllocatorProperties(&props);
  1877. pProperties->Release();
  1878. }
  1879. if ( SUCCEEDED(hr) )
  1880. {
  1881. LOG((MSP_TRACE, STREAM_PREFIX("ConfigureCapture - "
  1882. "using downstream MST allocator properties")));
  1883. }
  1884. else
  1885. {
  1886. LOG((MSP_TRACE, STREAM_PREFIX("ConfigureCapture - "
  1887. "using our default allocator properties")));
  1888. props.cBuffers = 32; // we use 32 to avoid starvation
  1889. props.cbBuffer = lDefaultBufferSize;
  1890. props.cbAlign = -1; // means "default"
  1891. props.cbPrefix = -1; // means "default"
  1892. }
  1893. //
  1894. // "props" now contains the properties that we need to set.
  1895. // Suggest them to the output pin.
  1896. //
  1897. hr = pNegotiation->SuggestAllocatorProperties( &props );
  1898. pNegotiation->Release();
  1899. if ( FAILED(hr) )
  1900. {
  1901. LOG((MSP_ERROR, STREAM_PREFIX("ConfigureCapture - "
  1902. "SuggestAllocatorProperties failed - exit 0x%08x"), hr));
  1903. return hr;
  1904. }
  1905. LOG((MSP_TRACE, STREAM_PREFIX("ConfigureCapture - exit S_OK")));
  1906. return S_OK;
  1907. }
  1908. //////////////////////////////////////////////////////////////////////////////
  1909. //////////////////////////////////////////////////////////////////////////////
  1910. //
  1911. // This function is for debugging purposes only. It pops up a
  1912. // couple of message boxes telling you various information about
  1913. // media formats and allocator properties. It's called after
  1914. // connection has taken place. pPin is the output pin of the
  1915. // wavein filter.
  1916. //
  1917. HRESULT CWaveMSPStream::ExamineCaptureProperties(IPin *pPin)
  1918. {
  1919. LOG((MSP_TRACE, STREAM_PREFIX("ExamineCaptureProperties - enter")));
  1920. HRESULT hr;
  1921. IAMBufferNegotiation * pNegotiation = NULL;
  1922. hr = pPin->QueryInterface(IID_IAMBufferNegotiation,
  1923. (void **) &pNegotiation
  1924. );
  1925. if ( FAILED(hr) )
  1926. {
  1927. LOG((MSP_ERROR, STREAM_PREFIX("ExamineCaptureProperties - "
  1928. "IAMBufferNegotiation QI failed on pin 0x%08x; hr = 0x%08x"),
  1929. pPin, hr));
  1930. return hr;
  1931. }
  1932. ALLOCATOR_PROPERTIES prop;
  1933. hr = pNegotiation->GetAllocatorProperties(&prop);
  1934. pNegotiation->Release();
  1935. if ( FAILED(hr) )
  1936. {
  1937. LOG((MSP_ERROR, STREAM_PREFIX("ExamineCaptureProperties - "
  1938. "GetAllocatorProperties failed; hr = 0x%08x"),
  1939. hr));
  1940. return hr;
  1941. }
  1942. LOG((MSP_TRACE, STREAM_PREFIX("GetAllocatorProperties info:\n"
  1943. "buffer count: %d\n"
  1944. "size of each buffer: %d bytes\n"
  1945. "alignment multiple: %d\n"
  1946. "each buffer has a prefix: %d bytes"),
  1947. prop.cBuffers,
  1948. prop.cbBuffer,
  1949. prop.cbAlign,
  1950. prop.cbPrefix
  1951. ));
  1952. AM_MEDIA_TYPE MediaType;
  1953. hr = pPin->ConnectionMediaType( & MediaType );
  1954. if ( FAILED(hr) )
  1955. {
  1956. LOG((MSP_ERROR, STREAM_PREFIX("ExamineCaptureProperties - "
  1957. "ConnectionMediaType failed - hr = 0x%08x"), hr));
  1958. return hr;
  1959. }
  1960. //
  1961. // Check if this media type has a WAVE format.
  1962. //
  1963. if ( MediaType.formattype != FORMAT_WaveFormatEx )
  1964. {
  1965. //
  1966. // might want to print the format type guid here if we ever care
  1967. //
  1968. _ASSERTE( FALSE );
  1969. LOG((MSP_TRACE, STREAM_PREFIX("connected media type: NON-WAVE")));
  1970. }
  1971. else
  1972. {
  1973. _ASSERTE( MediaType.cbFormat >= sizeof(WAVEFORMATEX) );
  1974. LOG((MSP_TRACE, STREAM_PREFIX("connected media type:\n"
  1975. "sample size: %d bytes\n"
  1976. "format tag: %d\n"
  1977. "channels: %d\n"
  1978. "samples per second: %d\n"
  1979. "bits per sample: %d\n"),
  1980. MediaType.lSampleSize,
  1981. ((WAVEFORMATEX *) (MediaType.pbFormat) )->wFormatTag,
  1982. ((WAVEFORMATEX *) (MediaType.pbFormat) )->nChannels,
  1983. ((WAVEFORMATEX *) (MediaType.pbFormat) )->nSamplesPerSec,
  1984. ((WAVEFORMATEX *) (MediaType.pbFormat) )->wBitsPerSample
  1985. ));
  1986. }
  1987. FreeMediaType( MediaType );
  1988. LOG((MSP_TRACE, STREAM_PREFIX("ExamineCaptureProperties - "
  1989. "exit S_OK")));
  1990. return S_OK;
  1991. }
  1992. //////////////////////////////////////////////////////////////////////////////
  1993. //////////////////////////////////////////////////////////////////////////////
  1994. //
  1995. // SetLiveMode
  1996. //
  1997. // If this is a wavein filter, tell it that it should do its best to
  1998. // counter the effects of mismatched clocks and drop samples when the
  1999. // latency gets too great. We really should do this on terminal
  2000. // selection dependin on whether we have at least one terminal selected on
  2001. // the stream that requires real-time performance, but this will have to
  2002. // do for now.
  2003. //
  2004. HRESULT CWaveMSPStream::SetLiveMode(BOOL fEnable, IBaseFilter * pFilter)
  2005. {
  2006. return S_OK;
  2007. #if 0
  2008. HRESULT hr;
  2009. IAMPushSource * pPushSource;
  2010. hr = pFilter->QueryInterface(
  2011. IID_IAMPushSource,
  2012. (void **) & pPushSource
  2013. );
  2014. if ( FAILED(hr) )
  2015. {
  2016. LOG((MSP_INFO, STREAM_PREFIX("SetLiveMode - "
  2017. "QI for IAMPushSource returned 0x%08x - continuing"), hr));
  2018. }
  2019. else
  2020. {
  2021. hr = pPushSource->SetLiveMode( fEnable );
  2022. if ( FAILED(hr) )
  2023. {
  2024. LOG((MSP_INFO, STREAM_PREFIX("SetLiveMode - "
  2025. "IAMPushSource::SetLiveMode( %d ) returned 0x%08x"
  2026. " - continuing"), fEnable, hr));
  2027. }
  2028. else
  2029. {
  2030. LOG((MSP_INFO, STREAM_PREFIX("SetLiveMode - "
  2031. "IAMPushSource::SetLiveMode( %d ) succeeded"
  2032. " - continuing"), fEnable, hr));
  2033. }
  2034. pPushSource->Release();
  2035. }
  2036. return hr;
  2037. #endif
  2038. }
  2039. ///////////////////////////////////////////////////////////////////////////////
  2040. ///////////////////////////////////////////////////////////////////////////////
  2041. //
  2042. // Add the terminal to the graph and connect it to our
  2043. // filters, if it is not already in use.
  2044. //
  2045. HRESULT CWaveMSPStream::ConnectTerminal(ITTerminal * pTerminal)
  2046. {
  2047. LOG((MSP_TRACE, STREAM_PREFIX("ConnectTerminal - enter")));
  2048. //
  2049. // If we've already connected the terminal on this stream, then
  2050. // there is nothing for us to do.
  2051. //
  2052. if ( m_fTerminalConnected )
  2053. {
  2054. LOG((MSP_ERROR, STREAM_PREFIX("ConnectTerminal - "
  2055. "terminal already connected on this stream - exit S_OK")));
  2056. return S_OK;
  2057. }
  2058. //
  2059. // Get the ITTerminalControl interface.
  2060. //
  2061. ITTerminalControl * pTerminalControl = NULL;
  2062. HRESULT hr = m_Terminals[0]->QueryInterface(IID_ITTerminalControl,
  2063. (void **) &pTerminalControl);
  2064. if ( FAILED(hr) )
  2065. {
  2066. LOG((MSP_ERROR, STREAM_PREFIX("ConnectTerminal - "
  2067. "QI for ITTerminalControl failed - exit 0x%08x"), hr));
  2068. return hr;
  2069. }
  2070. //
  2071. // Find out how many pins the terminal has. If not one then bail as
  2072. // we have no idea what to do with multiple-pin terminals at this point.
  2073. //
  2074. DWORD dwNumPinsAvailable;
  2075. hr = pTerminalControl->ConnectTerminal(m_pIGraphBuilder,
  2076. m_Direction,
  2077. &dwNumPinsAvailable,
  2078. NULL);
  2079. if ( FAILED(hr) )
  2080. {
  2081. LOG((MSP_ERROR, STREAM_PREFIX("ConnectTerminal - "
  2082. "query for number of terminal pins failed - exit 0x%08x"), hr));
  2083. pTerminalControl->Release();
  2084. return hr;
  2085. }
  2086. if ( 1 != dwNumPinsAvailable )
  2087. {
  2088. LOG((MSP_ERROR, STREAM_PREFIX("ConnectTerminal - "
  2089. "unsupported number of terminal pins - exit E_FAIL")));
  2090. pTerminalControl->Release();
  2091. return E_FAIL;
  2092. }
  2093. //
  2094. // Before adding a RENDER filter to the graph (ie, the terminal on
  2095. // a RENDER stream), do SetDefaultSyncSource
  2096. // to enable drop-sample code in DirectShow which will prevent
  2097. // forever-increasing latency with mismatched wave clocks.
  2098. //
  2099. if ( m_Direction == TD_RENDER )
  2100. {
  2101. hr = m_pIGraphBuilder->SetDefaultSyncSource();
  2102. if ( FAILED(hr) )
  2103. {
  2104. LOG((MSP_WARN, STREAM_PREFIX("ConnectTerminal - "
  2105. "SetDefaultSyncSource failed 0x%08x - continuing anyway"), hr));
  2106. }
  2107. }
  2108. //
  2109. // Actually connect the terminal.
  2110. //
  2111. IPin * pTerminalPin;
  2112. hr = pTerminalControl->ConnectTerminal(m_pIGraphBuilder,
  2113. m_Direction,
  2114. &dwNumPinsAvailable,
  2115. &pTerminalPin);
  2116. if ( FAILED(hr) )
  2117. {
  2118. pTerminalControl->Release();
  2119. LOG((MSP_ERROR, STREAM_PREFIX("ConnectTerminal - "
  2120. "ConnectTerminal on terminal failed - exit 0x%08x"), hr));
  2121. return hr;
  2122. }
  2123. //
  2124. // also try to check if the terminal returned a bad pin.
  2125. //
  2126. if ( IsBadReadPtr(pTerminalPin, sizeof(IPin)) )
  2127. {
  2128. pTerminalControl->Release();
  2129. LOG((MSP_ERROR, STREAM_PREFIX("ConnectTerminal - "
  2130. "ConnectTerminal on terminal succeeded but returned a bad pin "
  2131. "- returning E_POINTER")));
  2132. return E_POINTER;
  2133. }
  2134. //
  2135. // For a CAPTURE filter's pin, (ie, the terminal on a CAPTURE stream), get
  2136. // the filter and turn on sample dropping for live graphs. Ignore failure
  2137. // here. Note -- this will not work for multi-filter terminals. Luckily
  2138. // our interactive audio terminals are single-filter terminals.
  2139. // Multi-filter terminals can do this themselves.
  2140. //
  2141. if ( m_Direction == TD_CAPTURE )
  2142. {
  2143. PIN_INFO info;
  2144. hr = pTerminalPin->QueryPinInfo( & info );
  2145. if ( FAILED(hr) )
  2146. {
  2147. LOG((MSP_WARN, STREAM_PREFIX("ConnectTerminal - "
  2148. "get filter in preparation for SetLiveMode failed "
  2149. "0x%08x - continuing anyway"), hr));
  2150. }
  2151. else
  2152. {
  2153. SetLiveMode( TRUE, info.pFilter );
  2154. info.pFilter->Release();
  2155. }
  2156. }
  2157. //
  2158. // Now make the connection between our filters and the terminal's pin.
  2159. //
  2160. hr = ConnectToTerminalPin(pTerminalPin);
  2161. pTerminalPin->Release();
  2162. if ( FAILED(hr) )
  2163. {
  2164. pTerminalControl->DisconnectTerminal(m_pIGraphBuilder, 0);
  2165. pTerminalControl->Release();
  2166. //
  2167. // remove all the filters except for the wave filter
  2168. //
  2169. HRESULT hr2 = CleanFilterGraph();
  2170. if (FAILED(hr2))
  2171. {
  2172. LOG((MSP_ERROR,
  2173. STREAM_PREFIX("ConnectTerminal - CleanFilterGraph failed- exit 0x%x"),
  2174. hr2));
  2175. //
  2176. // filter graph may be in a bad shape, but there is nothing we can really do at this point.
  2177. //
  2178. }
  2179. LOG((MSP_ERROR, STREAM_PREFIX("ConnectTerminal - "
  2180. "ConnectToTerminalPin failed - exit 0x%08x"), hr));
  2181. return hr;
  2182. }
  2183. //
  2184. // Now we are actually connected. Update our state and perform postconnection
  2185. // (ignore postconnection error code).
  2186. //
  2187. m_fTerminalConnected = TRUE;
  2188. pTerminalControl->CompleteConnectTerminal();
  2189. pTerminalControl->Release();
  2190. LOG((MSP_TRACE, STREAM_PREFIX("ConnectTerminal - exit S_OK")));
  2191. return S_OK;
  2192. }
  2193. //////////////////////////////////////////////////////////////////////////////
  2194. //
  2195. //
  2196. void ShowMediaTypes(IEnumMediaTypes * pEnum)
  2197. {
  2198. //
  2199. // Look at each media type in the enumerator.
  2200. //
  2201. AM_MEDIA_TYPE * pMediaType;
  2202. while (pEnum->Next(1, &pMediaType, NULL) == S_OK)
  2203. {
  2204. //
  2205. // Check if this media type has a WAVE format.
  2206. //
  2207. if ( pMediaType->formattype != FORMAT_WaveFormatEx )
  2208. {
  2209. //
  2210. // might want to print the format type guid here if we ever care
  2211. //
  2212. LOG((MSP_TRACE, "Media Type: *** non-wave"));
  2213. }
  2214. else
  2215. {
  2216. LOG((MSP_TRACE,"Media Type: [format tag %d][%d channels]"
  2217. "[%d samples/sec][%d bits/sample]",
  2218. ((WAVEFORMATEX *) (pMediaType->pbFormat) )->wFormatTag,
  2219. ((WAVEFORMATEX *) (pMediaType->pbFormat) )->nChannels,
  2220. ((WAVEFORMATEX *) (pMediaType->pbFormat) )->nSamplesPerSec,
  2221. ((WAVEFORMATEX *) (pMediaType->pbFormat) )->wBitsPerSample
  2222. ));
  2223. }
  2224. //
  2225. // Release the format info.
  2226. //
  2227. DeleteMediaType(pMediaType);
  2228. }
  2229. }
  2230. //////////////////////////////////////////////////////////////////////////////
  2231. //
  2232. // ConnectUsingG711
  2233. //
  2234. // This method connects pOutputPin to pInputPin using the G711 codec and
  2235. // returns success if the connection was successful. If the connection was
  2236. // unsuccessful, it does its best to fully disconnect the filters and then
  2237. // returns a failure code.
  2238. //
  2239. // Assumptions:
  2240. // * direct connection has already failed
  2241. // * the g711 codec has been created and added to the graph
  2242. //
  2243. // Parameters:
  2244. // IN IPin * pOutputPin -- output pin on the capture filter or terminal
  2245. // IN IPin * pInputPin -- input pin on the render filter or terminal
  2246. //
  2247. //
  2248. HRESULT CWaveMSPStream::ConnectUsingG711(
  2249. IN IPin * pOutputPin,
  2250. IN IPin * pInputPin
  2251. )
  2252. {
  2253. HRESULT hr;
  2254. IPin * pG711InputPin = NULL;
  2255. hr = FindPinInFilter(
  2256. false, // want input pin
  2257. m_pG711Filter,
  2258. &pG711InputPin
  2259. );
  2260. if ( SUCCEEDED(hr) )
  2261. {
  2262. hr = m_pIGraphBuilder->ConnectDirect(
  2263. pOutputPin,
  2264. pG711InputPin,
  2265. NULL
  2266. );
  2267. // We don't release the G711's input pin here because we must
  2268. // hang onto it in order to break the connection if any of the
  2269. // subsequent steps fail.
  2270. if ( SUCCEEDED(hr) )
  2271. {
  2272. IPin * pG711OutputPin = NULL;
  2273. hr = FindPinInFilter(
  2274. true, // want output pin
  2275. m_pG711Filter,
  2276. &pG711OutputPin
  2277. );
  2278. if ( SUCCEEDED(hr) )
  2279. {
  2280. hr = m_pIGraphBuilder->ConnectDirect(
  2281. pG711OutputPin,
  2282. pInputPin,
  2283. NULL
  2284. );
  2285. pG711OutputPin->Release();
  2286. if ( SUCCEEDED(hr) )
  2287. {
  2288. LOG((MSP_TRACE, STREAM_PREFIX("ConnectUsingG711 - G711 connection succeeded - exit S_OK")));
  2289. // Held onto this in case of failure... see above
  2290. pG711InputPin->Release();
  2291. return S_OK;
  2292. }
  2293. else
  2294. {
  2295. LOG((MSP_ERROR, STREAM_PREFIX("ConnectUsingG711 - could not connect "
  2296. "G711 codec's output pin - %lx"), hr));
  2297. }
  2298. }
  2299. else
  2300. {
  2301. LOG((MSP_ERROR, STREAM_PREFIX("ConnectUsingG711 - could not find "
  2302. "G711 codec's input pin - %lx"), hr));
  2303. }
  2304. if ( FAILED(hr) )
  2305. {
  2306. //
  2307. // The first G711 connection succeeded but something else
  2308. // subsequently failed. This means we must disconnect the left
  2309. // end of the G711 filter. Luckily, we held onto the G711 filter's
  2310. // input pin above. We must disconnect the them here, otherwise
  2311. // method #3 won't work.
  2312. //
  2313. HRESULT hr2;
  2314. hr2 = m_pIGraphBuilder->Disconnect(pOutputPin);
  2315. if ( FAILED(hr2) )
  2316. {
  2317. LOG((MSP_ERROR, STREAM_PREFIX("ConnectUsingG711 - error undoing g711 "
  2318. "connection attempt - could not disconnect the "
  2319. "wave filter's output pin! hr = 0x%08x"), hr2));
  2320. }
  2321. hr2 = m_pIGraphBuilder->Disconnect(pG711InputPin);
  2322. if ( FAILED(hr2) )
  2323. {
  2324. LOG((MSP_ERROR, STREAM_PREFIX("ConnectUsingG711 - error undoing g711 "
  2325. "connection attempt - could not disconnect the "
  2326. "g711 filter's input pin! hr = 0x%08x"), hr2));
  2327. }
  2328. //
  2329. // Now we no longer need to talk to the pin...
  2330. //
  2331. pG711InputPin->Release();
  2332. //
  2333. // And the G711 filter itself sticks around in the graph for next time.
  2334. //
  2335. }
  2336. }
  2337. else
  2338. {
  2339. LOG((MSP_ERROR, STREAM_PREFIX("ConnectUsingG711 - could not connect "
  2340. "G711 codec's input pin - %lx"), hr));
  2341. }
  2342. }
  2343. else
  2344. {
  2345. LOG((MSP_ERROR, STREAM_PREFIX("ConnectUsingG711 - could not find "
  2346. "G711 codec's input pin - %lx"), hr));
  2347. }
  2348. return hr;
  2349. }
  2350. //////////////////////////////////////////////////////////////////////////////
  2351. //
  2352. // TryToConnect
  2353. //
  2354. // This is a private helper method.
  2355. //
  2356. // This method connects an output pin to an input pin. It first tries
  2357. // direct connection; failing this, it adds the G711 filter to the graph
  2358. // and tries a G711 connection; failing that, it tries an intelligent
  2359. // connection (which can be disabled at compile time).
  2360. //
  2361. // Arguments:
  2362. // pOutputPin - IN - output pin on the capture filter or terminal
  2363. // pInputPin - IN - input pin on the render filter or terminal
  2364. // pfIntelligent - OUT - if NULL, then this parameter is ignored
  2365. // otherwise, BOOl value at this location is
  2366. // set to TRUE if intelligent connection was
  2367. // used, FALSE otherwise. Invalid if connection
  2368. // was unsuccessful.
  2369. //
  2370. // Return values:
  2371. // S_OK -- success
  2372. // various -- from other helpers and DShow methods
  2373. //
  2374. //
  2375. HRESULT CWaveMSPStream::TryToConnect(
  2376. IN IPin * pOutputPin,
  2377. IN IPin * pInputPin,
  2378. OUT BOOL * pfIntelligent
  2379. )
  2380. {
  2381. LOG((MSP_TRACE, STREAM_PREFIX("TryToConnect - enter")));
  2382. //
  2383. // Assume unintelligent connection unless we actually happen
  2384. // to use it.
  2385. //
  2386. if ( pfIntelligent != NULL )
  2387. {
  2388. _ASSERTE( ! IsBadWritePtr( pfIntelligent, sizeof( BOOL ) ) );
  2389. *pfIntelligent = FALSE;
  2390. }
  2391. HRESULT hr;
  2392. //
  2393. // Method 1: direct connection
  2394. //
  2395. hr = m_pIGraphBuilder->ConnectDirect(
  2396. pOutputPin,
  2397. pInputPin,
  2398. NULL
  2399. );
  2400. if ( SUCCEEDED(hr) )
  2401. {
  2402. LOG((MSP_TRACE, STREAM_PREFIX("TryToConnect: direct connection worked - exit S_OK")));
  2403. return S_OK;
  2404. }
  2405. LOG((MSP_ERROR, STREAM_PREFIX("TryToConnect - direct connection failed - %lx"), hr));
  2406. //
  2407. // Method 2: direct connection with G711 filter in between.
  2408. // If we haven't created and added the G711 filter to the graph yet,
  2409. // do so now.
  2410. //
  2411. hr = AddG711();
  2412. //
  2413. // If the AddG711 method worked, try to use the G711.
  2414. //
  2415. if (SUCCEEDED(hr) && m_pG711Filter)
  2416. {
  2417. hr = ConnectUsingG711(pOutputPin,
  2418. pInputPin);
  2419. if ( SUCCEEDED(hr) )
  2420. {
  2421. LOG((MSP_TRACE, STREAM_PREFIX("TryToConnect - "
  2422. "g711 connection worked - exit S_OK")));
  2423. return S_OK;
  2424. }
  2425. else
  2426. {
  2427. LOG((MSP_TRACE, STREAM_PREFIX("TryToConnect - "
  2428. "G711 connection failed - %lx"), hr));
  2429. }
  2430. }
  2431. else
  2432. {
  2433. LOG((MSP_ERROR, STREAM_PREFIX("TryToConnect - G711 codec does not exist. hr = %lx"), hr));
  2434. hr = E_FAIL;
  2435. }
  2436. //
  2437. // Method 3: intelligent connection, which may pull in who knows what
  2438. // other filters
  2439. //
  2440. #ifdef ALLOW_INTELLIGENT_CONNECTION
  2441. //
  2442. // Before intelligent connection, create the DShow filter mapper object if
  2443. // it doesn't already exist, and save it until the address is shut down.
  2444. // This will make all intelligent connects after the first much faster.
  2445. // No need to check the return code; if it fails, we just continue. The
  2446. // WaveMspCall object forwards this call to our address object.
  2447. //
  2448. // m_pMSPCall is valid here, because it is released in
  2449. // CMSPStream::ShutDown. ShutDown grabs the stream lock, releases
  2450. // m_pMSPCall, and unselects all terminals. The connection process starts
  2451. // with a StartStream or PauseStream, and those methods all grab the
  2452. // stream lock and return immediately if there are no terminals selected.
  2453. // Therefore, there is no danger of the call pointer being invalid during
  2454. // connection on a stream.
  2455. //
  2456. ((CWaveMSPCall *) m_pMSPCall)->CreateFilterMapper();
  2457. hr = m_pIGraphBuilder->Connect(pOutputPin,
  2458. pInputPin);
  2459. if ( FAILED(hr) )
  2460. {
  2461. LOG((MSP_ERROR, STREAM_PREFIX("TryToConnect - "
  2462. "intelligent connection failed - %lx"), hr));
  2463. return hr;
  2464. }
  2465. LOG((MSP_TRACE, STREAM_PREFIX("TryToConnect - "
  2466. "intelligent connection worked - exit S_OK")));
  2467. if ( pfIntelligent != NULL )
  2468. {
  2469. *pfIntelligent = TRUE;
  2470. }
  2471. return S_OK;
  2472. #else // ALLOW_INTELLIGENT_CONNECTION
  2473. LOG((MSP_ERROR, STREAM_PREFIX("TryToConnect - NOTE: we never allow intelligent "
  2474. "connection - exit 0x%08x"), hr));
  2475. return hr;
  2476. #endif // ALLOW_INTELLIGENT_CONNECTION
  2477. }
  2478. //////////////////////////////////////////////////////////////////////////////
  2479. //////////////////////////////////////////////////////////////////////////////
  2480. HRESULT CWaveMSPStream::ConnectToTerminalPin(IPin * pTerminalPin)
  2481. {
  2482. LOG((MSP_TRACE, STREAM_PREFIX("ConnectToTerminalPin - enter")));
  2483. HRESULT hr = S_OK;
  2484. //
  2485. // Find our own filter's pin.
  2486. //
  2487. IPin * pMyPin;
  2488. hr = FindPin( &pMyPin );
  2489. if ( FAILED(hr) )
  2490. {
  2491. LOG((MSP_ERROR, STREAM_PREFIX("ConnectToTerminalPin - "
  2492. "could not find pin - exit 0x%08x"), hr));
  2493. return hr; // we can't continue without this pin
  2494. }
  2495. // The OUTPUT pin from WAVEIN; the INPUT pin from WAVEOUT
  2496. IPin * pOutputPin = ( m_Direction == TD_RENDER ) ? pMyPin : pTerminalPin;
  2497. IPin * pInputPin = ( m_Direction == TD_CAPTURE ) ? pMyPin : pTerminalPin;
  2498. #ifdef MSPLOG
  2499. //
  2500. // In the interests of easier diagnosis, do some tracing of the formats
  2501. // that are available.
  2502. //
  2503. IEnumMediaTypes * pEnum;
  2504. hr = pOutputPin->EnumMediaTypes(&pEnum);
  2505. if ( SUCCEEDED(hr) )
  2506. {
  2507. LOG((MSP_TRACE, STREAM_PREFIX("Output pin media types:")));
  2508. ShowMediaTypes(pEnum);
  2509. pEnum->Release();
  2510. }
  2511. hr = pInputPin->EnumMediaTypes(&pEnum);
  2512. if ( SUCCEEDED(hr) )
  2513. {
  2514. LOG((MSP_TRACE, STREAM_PREFIX("Input pin media types:")));
  2515. ShowMediaTypes(pEnum);
  2516. pEnum->Release();
  2517. }
  2518. #endif // #ifdef MSPLOG
  2519. //
  2520. // Do a preliminary connection between the terminal and our filter,
  2521. // without having configured the capturer's allocator properties.
  2522. //
  2523. // fIntelligent is assigned TRUE if intelligent connection was used,
  2524. // FALSE otherwise -- only valid on success.
  2525. //
  2526. BOOL fIntelligent;
  2527. hr = TryToConnect(pOutputPin,
  2528. pInputPin,
  2529. & fIntelligent
  2530. );
  2531. if ( SUCCEEDED(hr) )
  2532. {
  2533. LOG((MSP_TRACE, STREAM_PREFIX("ConnectToTerminalPin - "
  2534. "preliminary connection succeeded")));
  2535. //
  2536. // Now that we are connected, find out the default buffer size we
  2537. // should use at the capture filter with interactive terminals.
  2538. // This can only be gleaned when the capture filter is connected, but
  2539. // we cannot make use of the information until we disconnect the
  2540. // filters.
  2541. //
  2542. long lDefaultBufferSize;
  2543. hr = DecideDesiredCaptureBufferSize(pOutputPin,
  2544. & lDefaultBufferSize);
  2545. if ( SUCCEEDED(hr) )
  2546. {
  2547. LOG((MSP_TRACE, STREAM_PREFIX("ConnectToTerminalPin - "
  2548. "default buffer size determination succeeded")));
  2549. //
  2550. // remove the terminal from the graph
  2551. //
  2552. hr = RemoveTerminal();
  2553. if (FAILED(hr))
  2554. {
  2555. LOG((MSP_ERROR,
  2556. STREAM_PREFIX("ConnectToTerminalPin - RemoveTerminal Failed hr=0x%x"), hr));
  2557. }
  2558. else
  2559. {
  2560. //
  2561. // clean filter graph by removing all the filters other than
  2562. // the wave filter
  2563. //
  2564. CleanFilterGraph();
  2565. //
  2566. // we can now re-add the terminal.
  2567. //
  2568. hr = ReAddTerminal();
  2569. if ( FAILED(hr) )
  2570. {
  2571. LOG((MSP_ERROR,
  2572. STREAM_PREFIX("ConnectToTerminalPin - ReAddTerminal failed - hr=0x%x"),
  2573. hr));
  2574. }
  2575. else
  2576. {
  2577. //
  2578. // Perform our settings on the capture filter.
  2579. // We don't need to bail if this fails -- we will just have worse
  2580. // latency / performance.
  2581. //
  2582. ConfigureCapture(pOutputPin,
  2583. pInputPin,
  2584. lDefaultBufferSize);
  2585. //
  2586. // Now do the actual connection between the terminal and our filter.
  2587. // Last argument is NULL because we don't care if it's intelligent
  2588. // this time
  2589. //
  2590. hr = TryToConnect(pOutputPin,
  2591. pInputPin,
  2592. NULL
  2593. );
  2594. }
  2595. }
  2596. }
  2597. }
  2598. #ifdef MSPLOG
  2599. if ( SUCCEEDED(hr) )
  2600. {
  2601. // Do some extra debug output.
  2602. // don't care if something fails in here...
  2603. ExamineCaptureProperties(pOutputPin);
  2604. }
  2605. #endif
  2606. pMyPin->Release();
  2607. if ( FAILED(hr) )
  2608. {
  2609. LOG((MSP_ERROR, STREAM_PREFIX("ConnectToTerminalPin - "
  2610. "could not connect to pin - exit 0x%08x"), hr));
  2611. //
  2612. // cleanup by removing all filters except for the wave filter
  2613. //
  2614. CleanFilterGraph();
  2615. return hr;
  2616. }
  2617. LOG((MSP_TRACE, STREAM_PREFIX("ConnectToTerminalPin - exit S_OK")));
  2618. return S_OK;
  2619. }
  2620. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2621. HRESULT CWaveMSPStream::FindPinInFilter(
  2622. BOOL bWantOutputPin, // IN: if false, we want the input pin
  2623. IBaseFilter * pFilter, // IN: the filter to examine
  2624. IPin ** ppPin // OUT: the pin we found
  2625. )
  2626. {
  2627. HRESULT hr;
  2628. IEnumPins * pEnumPins;
  2629. *ppPin = NULL;
  2630. // enumerate the pins on the filter
  2631. hr = pFilter->EnumPins( &pEnumPins );
  2632. if ( FAILED(hr) )
  2633. {
  2634. return hr;
  2635. }
  2636. // go through the pins
  2637. while (TRUE)
  2638. {
  2639. PIN_DIRECTION pd;
  2640. hr = pEnumPins->Next( 1, ppPin, NULL );
  2641. if (S_OK != hr)
  2642. {
  2643. // didn't find a pin!
  2644. break;
  2645. }
  2646. // get the pin info
  2647. hr = (*ppPin)->QueryDirection( &pd );
  2648. // does it meet the criteria?
  2649. if (bWantOutputPin && (pd == PINDIR_OUTPUT))
  2650. {
  2651. // yes
  2652. break;
  2653. }
  2654. if ( ! bWantOutputPin && (pd == PINDIR_INPUT))
  2655. {
  2656. // yes
  2657. break;
  2658. }
  2659. (*ppPin)->Release();
  2660. *ppPin = NULL;
  2661. }
  2662. pEnumPins->Release();
  2663. if (NULL == *ppPin)
  2664. {
  2665. // error
  2666. return E_FAIL;
  2667. }
  2668. return S_OK;
  2669. }
  2670. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2671. // FindPin
  2672. //
  2673. // Finds the first pin in the filter that meets criteria.
  2674. // For bWaveIn == TRUE, the pin must be direction PINDIR_OUTPUT
  2675. // For bWaveIn == FALSE, the pin must be direction PINDIR_INPUT
  2676. //
  2677. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2678. HRESULT
  2679. CWaveMSPStream::FindPin(
  2680. IPin ** ppPin
  2681. )
  2682. {
  2683. return FindPinInFilter(m_Direction == TD_RENDER,
  2684. m_pFilter,
  2685. ppPin);
  2686. }
  2687. // eof