Leaked source code of windows server 2003
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.

1657 lines
38 KiB

  1. //
  2. // FPTrack.cpp
  3. //
  4. #include "stdafx.h"
  5. #include "FPTrack.h"
  6. #include "FPTerm.h"
  7. #include "FPFilter.h"
  8. #include <formats.h>
  9. //////////////////////////////////////////////////////////////////////
  10. //
  11. // Constructor / Destructor - Methods implementation
  12. //
  13. //////////////////////////////////////////////////////////////////////
  14. CFPTrack::CFPTrack() :
  15. m_dwMediaType(TAPIMEDIATYPE_AUDIO),
  16. m_pFPFilter(NULL),
  17. m_pEventSink(NULL),
  18. m_pParentTerminal(NULL),
  19. m_TrackState(TMS_IDLE),
  20. m_pMediaType(NULL),
  21. m_pSource(NULL)
  22. {
  23. LOG((MSP_TRACE, "CFPTrack::CFPTrack - enter"));
  24. m_pIFilter = NULL;
  25. m_szName[0]= (TCHAR)0;
  26. //
  27. // Allocator properties
  28. //
  29. m_AllocProp.cbAlign = -1; // no alignment
  30. m_AllocProp.cbPrefix = -1; // no prefix
  31. m_AllocProp.cbBuffer = 480; // each buffer is 320 bytes = 20 ms worth of data
  32. m_AllocProp.cBuffers = 33; // read 33 buffers
  33. LOG((MSP_TRACE, "CFPTrack::CFPTrack - exit"));
  34. }
  35. CFPTrack::~CFPTrack()
  36. {
  37. LOG((MSP_TRACE, "CFPTrack::~CFPTrack - enter"));
  38. // Clean-up the event sink
  39. if( NULL != m_pEventSink )
  40. {
  41. m_pEventSink->Release();
  42. m_pEventSink = NULL;
  43. }
  44. // Clean-up parent multitrack terminal
  45. if( NULL != m_pParentTerminal )
  46. {
  47. m_pParentTerminal = NULL;
  48. }
  49. //
  50. // tell the filter that we are going away
  51. //
  52. if ( NULL != m_pFPFilter )
  53. {
  54. m_pFPFilter->Orphan();
  55. }
  56. // Clean-up the media type
  57. if( m_pMediaType )
  58. {
  59. DeleteMediaType ( m_pMediaType );
  60. m_pMediaType = NULL;
  61. }
  62. // Clean-up the source stream
  63. if( m_pSource )
  64. {
  65. m_pSource->Release();
  66. m_pSource = NULL;
  67. }
  68. // We don't need to delete m_pFPFilter because
  69. // m_pIFilter take care of it
  70. LOG((MSP_TRACE, "CFPTrack::~CFPTrack - exit"));
  71. }
  72. //////////////////////////////////////////////////////////////////////////////
  73. //
  74. // IDispatch implementation
  75. //
  76. typedef IDispatchImpl<ITFileTrackVtblFPT<CFPTrack> , &IID_ITFileTrack, &LIBID_TAPI3Lib> CTFileTrackFPT;
  77. typedef IDispatchImpl<ITTerminalVtblBase<CBaseTerminal>, &IID_ITTerminal, &LIBID_TAPI3Lib> CTTerminalFPT;
  78. /////////////////////////////////////////////////////////////////////////
  79. //
  80. // CFPTrack::GetIDsOfNames
  81. //
  82. //
  83. STDMETHODIMP CFPTrack::GetIDsOfNames(REFIID riid,
  84. LPOLESTR* rgszNames,
  85. UINT cNames,
  86. LCID lcid,
  87. DISPID* rgdispid
  88. )
  89. {
  90. LOG((MSP_TRACE, "CFPTrack::GetIDsOfNames[%p] - enter. Name [%S]", this, *rgszNames));
  91. HRESULT hr = DISP_E_UNKNOWNNAME;
  92. //
  93. // See if the requsted method belongs to the default interface
  94. //
  95. hr = CTTerminalFPT::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
  96. if (SUCCEEDED(hr))
  97. {
  98. LOG((MSP_TRACE, "CFPTrack::GetIDsOfNames - found %S on ITTerminal", *rgszNames));
  99. rgdispid[0] |= 0;
  100. return hr;
  101. }
  102. //
  103. // If not, then try the ITFileTrack interface
  104. //
  105. hr = CTFileTrackFPT::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
  106. if (SUCCEEDED(hr))
  107. {
  108. LOG((MSP_TRACE, "CFPTrack::GetIDsOfNames - found %S on ITFileTrack", *rgszNames));
  109. rgdispid[0] |= IDISPFILETRACK;
  110. return hr;
  111. }
  112. LOG((MSP_TRACE, "CFPTrack::GetIDsOfNames - finish. didn't find %S on our iterfaces", *rgszNames));
  113. return hr;
  114. }
  115. /////////////////////////////////////////////////////////////////////////
  116. //
  117. // CFPTrack::Invoke
  118. //
  119. //
  120. STDMETHODIMP CFPTrack::Invoke(DISPID dispidMember,
  121. REFIID riid,
  122. LCID lcid,
  123. WORD wFlags,
  124. DISPPARAMS* pdispparams,
  125. VARIANT* pvarResult,
  126. EXCEPINFO* pexcepinfo,
  127. UINT* puArgErr
  128. )
  129. {
  130. LOG((MSP_TRACE, "CFPTrack::Invoke[%p] - enter. dispidMember %lx", this, dispidMember));
  131. HRESULT hr = DISP_E_MEMBERNOTFOUND;
  132. DWORD dwInterface = (dispidMember & INTERFACEMASK);
  133. //
  134. // Call invoke for the required interface
  135. //
  136. switch (dwInterface)
  137. {
  138. case 0:
  139. {
  140. hr = CTTerminalFPT::Invoke(dispidMember,
  141. riid,
  142. lcid,
  143. wFlags,
  144. pdispparams,
  145. pvarResult,
  146. pexcepinfo,
  147. puArgErr
  148. );
  149. LOG((MSP_TRACE, "CFPTrack::Invoke - ITTerminal"));
  150. break;
  151. }
  152. case IDISPFILETRACK:
  153. {
  154. hr = CTFileTrackFPT::Invoke(dispidMember,
  155. riid,
  156. lcid,
  157. wFlags,
  158. pdispparams,
  159. pvarResult,
  160. pexcepinfo,
  161. puArgErr
  162. );
  163. LOG((MSP_TRACE, "CFPTrack::Invoke - ITFileTrack"));
  164. break;
  165. }
  166. } // end switch (dwInterface)
  167. LOG((MSP_TRACE, "CFPTrack::Invoke - finish. hr = %lx", hr));
  168. return hr;
  169. }
  170. //////////////////////////////////////////////////////////////////////
  171. //
  172. // CBaseTerminal - Methods implementation
  173. //
  174. //////////////////////////////////////////////////////////////////////
  175. HRESULT CFPTrack::AddFiltersToGraph()
  176. {
  177. LOG((MSP_TRACE, "CFPTrack::AddFiltersToGraph - enter"));
  178. //
  179. // Validates m_pGraph
  180. //
  181. if ( m_pGraph == NULL)
  182. {
  183. LOG((MSP_ERROR, "CFPTrack::AddFiltersToGraph - "
  184. "we have no graph - returning E_UNEXPECTED"));
  185. return E_UNEXPECTED;
  186. }
  187. //
  188. // Validates m_pIFilter
  189. //
  190. if ( m_pIFilter == NULL)
  191. {
  192. LOG((MSP_ERROR, "CFPTrack::AddFiltersToGraph - "
  193. "we have no filter - returning E_UNEXPECTED"));
  194. return E_UNEXPECTED;
  195. }
  196. //
  197. // AddFilter returns VFW_S_DUPLICATE_NAME if name is duplicate; still succeeds
  198. //
  199. HRESULT hr = m_pGraph->AddFilter(m_pIFilter, m_szName);
  200. if ( FAILED(hr) )
  201. {
  202. LOG((MSP_ERROR, "CFPTrack::AddFiltersToGraph() - "
  203. "Can't add filter. %08x", hr));
  204. return hr;
  205. }
  206. LOG((MSP_TRACE, "CFPTrack::AddFiltersToGraph - exit S_OK"));
  207. return S_OK;
  208. }
  209. //////////////////////////////////////////////////////////////////////
  210. //
  211. // ITPluggableTerminalInitialization - Methods implementation
  212. //
  213. //////////////////////////////////////////////////////////////////////
  214. HRESULT CFPTrack::InitializeDynamic(
  215. IN IID iidTerminalClass,
  216. IN DWORD dwMediaType,
  217. IN TERMINAL_DIRECTION Direction,
  218. IN MSP_HANDLE htAddress
  219. )
  220. {
  221. //
  222. // Critical section
  223. //
  224. CLock lock(m_Lock);
  225. LOG((MSP_TRACE, "CFPTrack::InitializeDynamic - enter"));
  226. //
  227. // Validate direction
  228. //
  229. if( Direction != TD_CAPTURE )
  230. {
  231. LOG((MSP_ERROR, "CFPTrack::InitializeDynamic - "
  232. "invalid direction - returning E_INVALIDARG"));
  233. return E_INVALIDARG;
  234. }
  235. //
  236. // Call the base class method
  237. //
  238. HRESULT hr;
  239. hr = CBaseTerminal::Initialize(iidTerminalClass,
  240. dwMediaType,
  241. Direction,
  242. htAddress);
  243. if ( FAILED(hr) )
  244. {
  245. LOG((MSP_ERROR, "CFPTrack::InitializeDynamic - "
  246. "base class method failed - returning 0x%08x", hr));
  247. return hr;
  248. }
  249. //
  250. // Set the terminal info: name and type
  251. //
  252. hr = SetTerminalInfo();
  253. if( FAILED(hr) )
  254. {
  255. LOG((MSP_ERROR, "CFPTrack::InitializeDynamic - "
  256. "SetTerminalInfo failed - returning 0x%08x", hr));
  257. return hr;
  258. }
  259. //
  260. // Create the filter
  261. //
  262. hr = CreateFilter();
  263. if( FAILED(hr) )
  264. {
  265. LOG((MSP_ERROR, "CFPTrack::InitializeDynamic - "
  266. "CreateFilter failed - returning 0x%08x", hr));
  267. return hr;
  268. }
  269. //
  270. // Get the pin sets m_pIPin
  271. //
  272. hr = FindPin();
  273. if( FAILED(hr) )
  274. {
  275. LOG((MSP_ERROR, "CFPTrack::InitializeDynamic - "
  276. "FindPin failed - returning 0x%08x", hr));
  277. return hr;
  278. }
  279. LOG((MSP_TRACE, "CFPTrack::InitializeDynamic - exit S_OK"));
  280. return S_OK;
  281. }
  282. //////////////////////////////////////////////////////////////////////
  283. //
  284. // ITFileTrack - Methods implementation
  285. //
  286. //////////////////////////////////////////////////////////////////////
  287. HRESULT CFPTrack::get_Format(OUT AM_MEDIA_TYPE **ppmt)
  288. {
  289. //
  290. // Critical section
  291. //
  292. CLock lock(m_Lock);
  293. LOG((MSP_TRACE, "CFPTrack::get_Format - enter [%p]", this));
  294. //
  295. // Validate argument
  296. //
  297. if( IsBadWritePtr( ppmt, sizeof( AM_MEDIA_TYPE*)) )
  298. {
  299. LOG((MSP_ERROR, "CFPTrack::InitializeDynamic - "
  300. "invalid AM_MEDIA_TYPE pointer - returning E_POINTER"));
  301. return E_POINTER;
  302. }
  303. //
  304. // Have we a unit pin?
  305. //
  306. if( NULL == m_pMediaType )
  307. {
  308. LOG((MSP_ERROR, "CFPTrack::InitializeDynamic - "
  309. "no media type - returning E_UNEXPECTED"));
  310. return E_UNEXPECTED;
  311. }
  312. //
  313. // Get the media type from stream
  314. //
  315. HRESULT hr = S_OK;
  316. *ppmt = CreateMediaType( m_pMediaType );
  317. if( *ppmt == NULL )
  318. {
  319. hr = E_OUTOFMEMORY;
  320. }
  321. LOG((MSP_TRACE, "CFPTrack::get_Format - exit 0x%08x", hr));
  322. return hr;
  323. }
  324. HRESULT CFPTrack::put_Format(IN const AM_MEDIA_TYPE *pmt)
  325. {
  326. LOG((MSP_TRACE, "CFPTrack::get_Format - enter [%p]", this));
  327. LOG((MSP_TRACE, "CFPTrack::get_Format - exit E_FAIL"));
  328. return E_FAIL;
  329. }
  330. HRESULT CFPTrack::get_ControllingTerminal(
  331. OUT ITTerminal **ppControllingTerminal
  332. )
  333. {
  334. //
  335. // Critical section
  336. //
  337. CLock lock(m_Lock);
  338. LOG((MSP_TRACE, "CFPTrack::get_ControllingTerminal - enter [%p]", this));
  339. //
  340. // Validates argument
  341. //
  342. if( IsBadWritePtr( ppControllingTerminal, sizeof(ITTerminal*)))
  343. {
  344. LOG((MSP_ERROR, "CFPTrack::get_ControllingTerminal - "
  345. "bad ITTerminal* pointer - returning E_POINTER"));
  346. return E_POINTER;
  347. }
  348. //
  349. // reset value anyway
  350. //
  351. *ppControllingTerminal = NULL;
  352. //
  353. // Validate parent
  354. //
  355. if( NULL == m_pParentTerminal )
  356. {
  357. LOG((MSP_ERROR, "CFPTrack::get_ControllingTerminal - "
  358. "no parent - returning E_UNEXPECTED"));
  359. return E_UNEXPECTED;
  360. }
  361. //
  362. // Set value
  363. //
  364. *ppControllingTerminal = m_pParentTerminal;
  365. m_pParentTerminal->AddRef();
  366. LOG((MSP_TRACE, "CFPTrack::get_ControllingTerminal - exit S_OK"));
  367. return S_OK;
  368. }
  369. HRESULT CFPTrack::get_AudioFormatForScripting(
  370. OUT ITScriptableAudioFormat** ppAudioFormat
  371. )
  372. {
  373. //
  374. // Critical section
  375. //
  376. CLock lock(m_Lock);
  377. LOG((MSP_TRACE, "CFPTrack::get_AudioFormatForScripting - enter"));
  378. //
  379. // Validates argument
  380. //
  381. if( IsBadWritePtr( ppAudioFormat, sizeof( ITScriptableAudioFormat*)) )
  382. {
  383. LOG((MSP_ERROR, "CFPTrack::get_AudioFormatForScripting - "
  384. "bad ITScriptableAudioFormat* pointer - returning E_POINTER"));
  385. return E_POINTER;
  386. }
  387. //
  388. // Mediatype audio?
  389. //
  390. if( TAPIMEDIATYPE_AUDIO != m_dwMediaType)
  391. {
  392. LOG((MSP_ERROR, "CFPTrack::get_AudioFormatForScripting - "
  393. "invalid media type - returning TAPI_E_INVALIDMEDIATYPE"));
  394. return TAPI_E_INVALIDMEDIATYPE;
  395. }
  396. //
  397. // Unit pin valid
  398. //
  399. if( NULL == m_pMediaType )
  400. {
  401. LOG((MSP_ERROR, "CFPTrack::get_AudioFormatForScripting - "
  402. "m_pMediaType is NULL - returning E_UNEXPECTED"));
  403. return E_UNEXPECTED;
  404. }
  405. //
  406. // Create the object
  407. //
  408. CComObject<CTAudioFormat> *pAudioFormat = NULL;
  409. HRESULT hr = CComObject<CTAudioFormat>::CreateInstance(&pAudioFormat);
  410. if( FAILED(hr) )
  411. {
  412. LOG((MSP_ERROR, "CFPTrack::get_AudioFormatForScripting - "
  413. "CreateInstance failed - returning 0x%08x", hr));
  414. return hr;
  415. }
  416. //
  417. // Get the interface
  418. //
  419. hr = pAudioFormat->QueryInterface(
  420. IID_ITScriptableAudioFormat,
  421. (void**)ppAudioFormat
  422. );
  423. if( FAILED(hr) )
  424. {
  425. delete pAudioFormat;
  426. LOG((MSP_ERROR, "CFPTrack::get_AudioFormatForScripting - "
  427. "QueryInterface failed - returning 0x%08x", hr));
  428. return hr;
  429. }
  430. // Format type
  431. if( m_pMediaType->formattype != FORMAT_WaveFormatEx)
  432. {
  433. (*ppAudioFormat)->Release();
  434. *ppAudioFormat = NULL;
  435. LOG((MSP_ERROR, "CFPTrack::get_AudioFormatForScripting - "
  436. "formattype is not WAVEFORMATEX - Returning TAPI_E_INVALIDMEDIATYPE"));
  437. return TAPI_E_INVALIDMEDIATYPE;
  438. }
  439. //
  440. // Get WAVEFORMATEX
  441. //
  442. pAudioFormat->Initialize(
  443. (WAVEFORMATEX*)(m_pMediaType->pbFormat));
  444. LOG((MSP_TRACE, "CFPTrack::get_AudioFormatForScripting - exit S_OK"));
  445. return S_OK;
  446. }
  447. HRESULT CFPTrack::put_AudioFormatForScripting(
  448. IN ITScriptableAudioFormat* pAudioFormat
  449. )
  450. {
  451. LOG((MSP_TRACE, "CFPTrack::put_AudioFormatForScripting - enter"));
  452. LOG((MSP_TRACE, "CFPTrack::put_AudioFormatForScripting - exit E_FAIL"));
  453. return E_FAIL;
  454. }
  455. /*
  456. HRESULT CFPTrack::get_VideoFormatForScripting(
  457. OUT ITScriptableVideoFormat** ppVideoFormat
  458. )
  459. {
  460. //
  461. // Critical section
  462. //
  463. CLock lock(m_Lock);
  464. LOG((MSP_TRACE, "CFPTrack::get_VideoFormatForScripting - enter"));
  465. //
  466. // Validates argument
  467. //
  468. if( IsBadWritePtr( ppVideoFormat, sizeof( ITScriptableVideoFormat*)) )
  469. {
  470. LOG((MSP_ERROR, "CFPTrack::get_VideoFormatForScripting - "
  471. "bad ITScriptableVideoFormat* pointer - returning E_POINTER"));
  472. return E_POINTER;
  473. }
  474. //
  475. // Mediatype video?
  476. //
  477. if( TAPIMEDIATYPE_VIDEO != m_dwMediaType)
  478. {
  479. LOG((MSP_ERROR, "CFPTrack::get_VideoFormatForScripting - "
  480. "invalid media type - returning TAPI_E_INVALIDMEDIATYPE"));
  481. return TAPI_E_INVALIDMEDIATYPE;
  482. }
  483. //
  484. // Pin valid
  485. //
  486. if( NULL == m_pMediaType )
  487. {
  488. LOG((MSP_ERROR, "CFPTrack::get_VideoFormatForScripting - "
  489. "m_pMediaType is NULL - returning E_UNEXPECTED"));
  490. return E_UNEXPECTED;
  491. }
  492. //
  493. // Create the object
  494. //
  495. CComObject<CTVideoFormat> *pVideoFormat = NULL;
  496. HRESULT hr = CComObject<CTVideoFormat>::CreateInstance(&pVideoFormat);
  497. if( FAILED(hr) )
  498. {
  499. LOG((MSP_ERROR, "CFPTrack::get_VideoFormatForScripting - "
  500. "CreateInstance failed - returning 0x%08x", hr));
  501. return hr;
  502. }
  503. //
  504. // Get the interface
  505. //
  506. hr = pVideoFormat->QueryInterface(
  507. IID_ITScriptableVideoFormat,
  508. (void**)ppVideoFormat
  509. );
  510. if( FAILED(hr) )
  511. {
  512. delete pVideoFormat;
  513. LOG((MSP_ERROR, "CFPTrack::get_VideoFormatForScripting - "
  514. "QueryInterface failed - returning 0x%08x", hr));
  515. return hr;
  516. }
  517. //
  518. // Get video format
  519. //
  520. if( m_pMediaType->formattype != FORMAT_VideoInfo)
  521. {
  522. (*ppVideoFormat)->Release();
  523. *ppVideoFormat = NULL;
  524. LOG((MSP_ERROR, "CFPTrack::get_VideoFormatForScripting - "
  525. "formattype is not VIDEOINFOHEADER - Returning TAPI_E_INVALIDMEDIATYPE"));
  526. return TAPI_E_INVALIDMEDIATYPE;
  527. }
  528. //
  529. // Get VIDEOINFOHEADER
  530. //
  531. pVideoFormat->Initialize(
  532. (VIDEOINFOHEADER*)(m_pMediaType->pbFormat));
  533. LOG((MSP_TRACE, "CFPTrack::get_VideoFormatForScripting - exit S_OK"));
  534. return S_OK;
  535. }
  536. HRESULT CFPTrack::put_VideoFormatForScripting(
  537. IN ITScriptableVideoFormat* pVideoFormat
  538. )
  539. {
  540. LOG((MSP_TRACE, "CFPTrack::put_VideoFormatForScripting - enter"));
  541. LOG((MSP_TRACE, "CFPTrack::put_VideoFormatForScripting - exit E_FAIL"));
  542. return E_FAIL;
  543. }
  544. */
  545. HRESULT CFPTrack::get_EmptyAudioFormatForScripting(
  546. OUT ITScriptableAudioFormat** ppAudioFormat
  547. )
  548. {
  549. LOG((MSP_TRACE, "CFPTrack::get_EmptyAudioFormatForScripting - enter"));
  550. //
  551. // Validate argument
  552. //
  553. if( IsBadReadPtr( ppAudioFormat, sizeof(ITScriptableAudioFormat*)) )
  554. {
  555. LOG((MSP_ERROR, "CFPTrack::get_EmptyAudioFormatForScripting - "
  556. "bad ITScriptableAudioFormat* pointer - returning E_POINTER"));
  557. return E_POINTER;
  558. }
  559. //
  560. // Create the object
  561. //
  562. CComObject<CTAudioFormat> *pAudioFormat = NULL;
  563. HRESULT hr = CComObject<CTAudioFormat>::CreateInstance(&pAudioFormat);
  564. if( FAILED(hr) )
  565. {
  566. LOG((MSP_ERROR, "CFPTrack::get_EmptyAudioFormatForScripting - "
  567. "CreateInstance failed - returning 0x%08x", hr));
  568. return hr;
  569. }
  570. //
  571. // Get the interface
  572. //
  573. hr = pAudioFormat->QueryInterface(
  574. IID_ITScriptableAudioFormat,
  575. (void**)ppAudioFormat
  576. );
  577. if( FAILED(hr) )
  578. {
  579. delete pAudioFormat;
  580. LOG((MSP_ERROR, "CFPTrack::get_EmptyAudioFormatForScripting - "
  581. "QueryInterface failed - returning 0x%08x", hr));
  582. return hr;
  583. }
  584. LOG((MSP_TRACE, "CFPTrack::get_EmptyAudioFormatForScripting - exit S_OK"));
  585. return S_OK;
  586. }
  587. /*
  588. HRESULT CFPTrack::get_EmptyVideoFormatForScripting(
  589. OUT ITScriptableVideoFormat** ppVideoFormat
  590. )
  591. {
  592. LOG((MSP_TRACE, "CFPTrack::get_EmptyVideoFormatForScripting - enter"));
  593. //
  594. // Validate argument
  595. //
  596. if( IsBadReadPtr( ppVideoFormat, sizeof(ITScriptableVideoFormat*)) )
  597. {
  598. LOG((MSP_ERROR, "CFPTrack::get_EmptyVideoFormatForScripting - "
  599. "bad ITScriptableVideoFormat* pointer - returning E_POINTER"));
  600. return E_POINTER;
  601. }
  602. //
  603. // Create the object
  604. //
  605. CComObject<CTVideoFormat> *pVideoFormat = NULL;
  606. HRESULT hr = CComObject<CTVideoFormat>::CreateInstance(&pVideoFormat);
  607. if( FAILED(hr) )
  608. {
  609. LOG((MSP_ERROR, "CFPTrack::get_EmptyVideoFormatForScripting - "
  610. "CreateInstance failed - returning 0x%08x", hr));
  611. return hr;
  612. }
  613. //
  614. // Get the interface
  615. //
  616. hr = pVideoFormat->QueryInterface(
  617. IID_ITScriptableVideoFormat,
  618. (void**)ppVideoFormat
  619. );
  620. if( FAILED(hr) )
  621. {
  622. delete pVideoFormat;
  623. LOG((MSP_ERROR, "CFPTrack::get_EmptyVideoFormatForScripting - "
  624. "QueryInterface failed - returning 0x%08x", hr));
  625. return hr;
  626. }
  627. LOG((MSP_TRACE, "CFPTrack::get_EmptyVideoFormatForScripting - exit S_OK"));
  628. return S_OK;
  629. }
  630. */
  631. //////////////////////////////////////////////////////////////////////
  632. //
  633. // ITPluggableTerminalEventSinkRegistration - Methods implementation
  634. //
  635. //////////////////////////////////////////////////////////////////////
  636. HRESULT CFPTrack::RegisterSink(
  637. IN ITPluggableTerminalEventSink *pSink
  638. )
  639. {
  640. //
  641. // Critical section
  642. //
  643. CLock lock(m_Lock);
  644. LOG((MSP_TRACE, "CFPTrack::RegisterSink - enter [%p]", this));
  645. //
  646. // Validates argument
  647. //
  648. if( IsBadReadPtr( pSink, sizeof(ITPluggableTerminalEventSink)) )
  649. {
  650. LOG((MSP_ERROR, "CFPTrack::RegisterSink - exit "
  651. "ITPluggableTerminalEventSink invalid pointer. Returns E_POINTER"));
  652. return E_POINTER;
  653. }
  654. //
  655. // Release the old event sink
  656. //
  657. if( m_pEventSink )
  658. {
  659. m_pEventSink->Release();
  660. m_pEventSink = NULL;
  661. }
  662. //
  663. // Set the new event sink
  664. //
  665. m_pEventSink = pSink;
  666. m_pEventSink->AddRef();
  667. LOG((MSP_TRACE, "CFPTrack::RegisterSink - exit S_OK"));
  668. return S_OK;
  669. }
  670. HRESULT CFPTrack::UnregisterSink()
  671. {
  672. //
  673. // Critical section
  674. //
  675. CLock lock(m_Lock);
  676. LOG((MSP_TRACE, "CFPTrack::UnregisterSink - enter [%p]", this));
  677. //
  678. // Release the old event sink
  679. //
  680. if( m_pEventSink )
  681. {
  682. m_pEventSink->Release();
  683. m_pEventSink = NULL;
  684. }
  685. LOG((MSP_TRACE, "CFPTrack::UnregisterSink - exit S_OK"));
  686. return S_OK;
  687. }
  688. //////////////////////////////////////////////////////////////////////
  689. //
  690. // ITMediaControl - Methods implementation
  691. //
  692. //////////////////////////////////////////////////////////////////////
  693. HRESULT CFPTrack::Start( )
  694. {
  695. //
  696. // Critical section
  697. //
  698. CLock lock(m_Lock);
  699. LOG((MSP_TRACE, "CFPTrack::Start - enter [%p]", this));
  700. //
  701. // Validates filter pointer
  702. //
  703. if( IsBadReadPtr( m_pFPFilter, sizeof( CFPFilter) ))
  704. {
  705. LOG((MSP_ERROR, "CFPTrack::Start - "
  706. "pointer to filter is NULL. Returns E_UNEXPECTED"));
  707. return E_UNEXPECTED;
  708. }
  709. HRESULT hr = S_OK;
  710. hr = m_pFPFilter->StreamStart();
  711. if( SUCCEEDED(hr) )
  712. {
  713. m_TrackState = TMS_ACTIVE;
  714. }
  715. LOG((MSP_TRACE, "CFPTrack::Start - exit 0x%08", hr));
  716. return hr;
  717. }
  718. HRESULT CFPTrack::Stop( )
  719. {
  720. //
  721. // Critical section
  722. //
  723. CLock lock(m_Lock);
  724. LOG((MSP_TRACE, "CFPTrack::Stop - enter [%p]", this));
  725. //
  726. // Validates filter pointer
  727. //
  728. if( IsBadReadPtr( m_pFPFilter, sizeof( CFPFilter) ))
  729. {
  730. LOG((MSP_ERROR, "CFPTrack::Stop - "
  731. "pointer to filter is NULL. Returns E_UNEXPECTED"));
  732. return E_UNEXPECTED;
  733. }
  734. HRESULT hr = S_OK;
  735. hr = m_pFPFilter->StreamStop();
  736. m_TrackState = TMS_IDLE;
  737. LOG((MSP_TRACE, "CFPTrack::Stop - exit 0x%08", hr));
  738. return hr;
  739. }
  740. HRESULT CFPTrack::Pause( )
  741. {
  742. //
  743. // Critical section
  744. //
  745. CLock lock(m_Lock);
  746. LOG((MSP_TRACE, "CFPTrack::Pause - enter [%p]", this));
  747. //
  748. // Validates filter pointer
  749. //
  750. if( IsBadReadPtr( m_pFPFilter, sizeof( CFPFilter) ))
  751. {
  752. LOG((MSP_ERROR, "CFPTrack::Pause - "
  753. "pointer to filter is NULL. Returns E_UNEXPECTED"));
  754. return E_UNEXPECTED;
  755. }
  756. HRESULT hr = S_OK;
  757. hr = m_pFPFilter->StreamPause();
  758. if( SUCCEEDED(hr) )
  759. {
  760. m_TrackState = TMS_PAUSED;
  761. }
  762. LOG((MSP_TRACE, "CFPTrack::Pause - exit 0x%08", hr));
  763. return hr;
  764. }
  765. HRESULT CFPTrack::get_MediaState(
  766. OUT TERMINAL_MEDIA_STATE *pMediaState)
  767. {
  768. //
  769. // Critical section
  770. //
  771. CLock lock(m_Lock);
  772. LOG((MSP_TRACE, "CFPTrack::get_MediaState[%p] - enter.", this));
  773. //
  774. // Validates argument
  775. //
  776. if( IsBadWritePtr( pMediaState, sizeof(TERMINAL_MEDIA_STATE)) )
  777. {
  778. LOG((MSP_ERROR, "CFPTrack::get_MediaState - exit "
  779. "invalid TERMINAL_MEDIA_STATE. Returns E_POINTER"));
  780. return E_POINTER;
  781. }
  782. //
  783. // Return state
  784. //
  785. *pMediaState = m_TrackState;
  786. LOG((MSP_TRACE, "CFPTrack::get_MediaState - exit S_OK"));
  787. return S_OK;
  788. }
  789. HRESULT CFPTrack::SetParent(
  790. IN ITTerminal* pParent,
  791. OUT LONG *plCurrentRefcount
  792. )
  793. {
  794. //
  795. // Critical section
  796. //
  797. CLock lock(m_Lock);
  798. LOG((MSP_TRACE, "CFPTrack::SetParent[%p] - enter. parent [%p]",
  799. this, pParent));
  800. //
  801. // Validates argument (it is ok for parent to be NULL)
  802. //
  803. if( ( NULL != pParent ) && IsBadReadPtr( pParent, sizeof(ITTerminal) ) )
  804. {
  805. LOG((MSP_ERROR, "CFPTrack::SetParent - "
  806. "invalid ITTerminal pointer. Returns E_POINTER"));
  807. return E_POINTER;
  808. }
  809. if( IsBadWritePtr( plCurrentRefcount, sizeof(LONG)) )
  810. {
  811. LOG((MSP_ERROR, "CFPTrack::SetParent - "
  812. "invalid ITTerminal pointer. Returns E_POINTER"));
  813. return E_POINTER;
  814. }
  815. //
  816. // Release the old parent
  817. //
  818. if( NULL != m_pParentTerminal )
  819. {
  820. LOG((MSP_TRACE,
  821. "CFPTrack::SetParent - letting go of an existing parent [%p]",
  822. m_pParentTerminal));
  823. m_pParentTerminal = NULL;
  824. }
  825. //
  826. // Set the new parent
  827. //
  828. if( pParent )
  829. {
  830. LOG((MSP_TRACE,
  831. "CFPTrack::SetParent - keeping the new parent [%p]",
  832. pParent));
  833. m_pParentTerminal = pParent;
  834. }
  835. //
  836. // return current reference count so the parent can update the total it is
  837. // keeping
  838. //
  839. *plCurrentRefcount = m_dwRef;
  840. LOG((MSP_TRACE, "CFPTrack::SetParent - exit S_OK"));
  841. return S_OK;
  842. }
  843. //////////////////////////////////////////////////////////////////////
  844. //
  845. // ITFPEventSink - Methods implementation
  846. //
  847. //////////////////////////////////////////////////////////////////////
  848. HRESULT CFPTrack::FireEvent(TERMINAL_MEDIA_STATE tmsState,
  849. FT_STATE_EVENT_CAUSE ftecEventCause,
  850. HRESULT hrErrorCode)
  851. {
  852. LOG((MSP_TRACE, "CFPTrack::FireEvent - enter [%p]", this));
  853. //
  854. // we need a sync before we can fire an event
  855. //
  856. CLock lock(m_Lock);
  857. if (NULL == m_pEventSink)
  858. {
  859. LOG((MSP_WARN, "CFPTrack::FireEvent - no sink"));
  860. return E_FAIL;
  861. }
  862. //
  863. // initilize the structure
  864. //
  865. MSP_EVENT_INFO mspEventInfo;
  866. mspEventInfo.dwSize = sizeof(MSP_EVENT_INFO);
  867. mspEventInfo.Event = ME_FILE_TERMINAL_EVENT;
  868. mspEventInfo.hCall = NULL;
  869. mspEventInfo.MSP_FILE_TERMINAL_EVENT_INFO.TerminalMediaState = tmsState;
  870. mspEventInfo.MSP_FILE_TERMINAL_EVENT_INFO.ftecEventCause = ftecEventCause;
  871. mspEventInfo.MSP_FILE_TERMINAL_EVENT_INFO.hrErrorCode = hrErrorCode;
  872. //
  873. // keep the pointer to our ITTerminal interface in the structure
  874. //
  875. HRESULT hr = _InternalQueryInterface(IID_ITFileTrack,
  876. (void**)&(mspEventInfo.MSP_FILE_TERMINAL_EVENT_INFO.pFileTrack));
  877. if (FAILED(hr))
  878. {
  879. LOG((MSP_ERROR, "CFPTrack::FireEvent - failed to get ITFileTrack interface"));
  880. return hr;
  881. }
  882. //
  883. // get a pointer to ITTerminal of the parent terminal
  884. //
  885. mspEventInfo.MSP_FILE_TERMINAL_EVENT_INFO.pParentFileTerminal = NULL;
  886. if (NULL != m_pParentTerminal)
  887. {
  888. mspEventInfo.MSP_FILE_TERMINAL_EVENT_INFO.pParentFileTerminal = m_pParentTerminal;
  889. m_pParentTerminal->AddRef();
  890. }
  891. else
  892. {
  893. //
  894. // if we don't have the parent, fail
  895. //
  896. mspEventInfo.MSP_FILE_TERMINAL_EVENT_INFO.pFileTrack->Release();
  897. mspEventInfo.MSP_FILE_TERMINAL_EVENT_INFO.pFileTrack = NULL;
  898. LOG((MSP_ERROR, "CFPTrack::FireEvent - failed to get controlling terminal"));
  899. return E_FAIL;
  900. }
  901. //
  902. // pass event to the msp
  903. //
  904. hr = m_pEventSink->FireEvent(&mspEventInfo);
  905. if (FAILED(hr))
  906. {
  907. //
  908. // release all interfaces that we are holding.
  909. // fire event failed so no one else will release then for us.
  910. //
  911. mspEventInfo.MSP_FILE_TERMINAL_EVENT_INFO.pFileTrack->Release();
  912. mspEventInfo.MSP_FILE_TERMINAL_EVENT_INFO.pFileTrack = NULL;
  913. mspEventInfo.MSP_FILE_TERMINAL_EVENT_INFO.pParentFileTerminal->Release();
  914. mspEventInfo.MSP_FILE_TERMINAL_EVENT_INFO.pParentFileTerminal = NULL;
  915. LOG((MSP_ERROR, "CFPTrack::FireEvent - FireEvent on sink failed. hr = %lx", hr));
  916. return hr;
  917. }
  918. //
  919. // event fired
  920. //
  921. LOG((MSP_TRACE, "CFPTrack::FireEvent - finish"));
  922. return S_OK;
  923. }
  924. //////////////////////////////////////////////////////////////////////
  925. //
  926. // Helper methods - Methods implementation
  927. //
  928. //////////////////////////////////////////////////////////////////////
  929. HRESULT CFPTrack::InitializePrivate(
  930. IN DWORD dwMediaType,
  931. IN AM_MEDIA_TYPE* pMediaType,
  932. IN ITTerminal* pParent,
  933. IN ALLOCATOR_PROPERTIES allocprop,
  934. IN IStream* pStream
  935. )
  936. {
  937. //
  938. // Critical section
  939. //
  940. CLock lock(m_Lock);
  941. LOG((MSP_TRACE, "CFPTrack::InitializePrivate - enter [%p]", this));
  942. if( (m_dwMediaType != TAPIMEDIATYPE_AUDIO) &&
  943. (m_dwMediaType != TAPIMEDIATYPE_VIDEO))
  944. {
  945. LOG((MSP_ERROR, "CFPTrack::InitializePrivate - "
  946. "invalid media type - returns E_INVALIDARG"));
  947. return E_INVALIDARG;
  948. }
  949. //
  950. // Get the mediatype
  951. //
  952. m_pMediaType = CreateMediaType( pMediaType );
  953. if( m_pMediaType == NULL)
  954. {
  955. LOG((MSP_TRACE,
  956. "CFPTrack::InitializePrivate - "
  957. " CreateMediaType failed. return E_OUTOFMEMORY" ));
  958. return E_OUTOFMEMORY;
  959. }
  960. //
  961. // Set media type
  962. //
  963. m_dwMediaType = dwMediaType;
  964. //
  965. // Set the allocator properties
  966. //
  967. m_AllocProp = allocprop;
  968. //
  969. // let go of the existing parent
  970. //
  971. if( m_pParentTerminal )
  972. {
  973. LOG((MSP_TRACE,
  974. "CFPTrack::InitializePrivate - letting go of parent [%p]",
  975. m_pParentTerminal));
  976. m_pParentTerminal = NULL;
  977. }
  978. //
  979. // Set the source stream
  980. //
  981. HRESULT hr = pStream->Clone(&m_pSource);
  982. if( FAILED(hr) )
  983. {
  984. LOG((MSP_ERROR,
  985. "CFPTrack::InitializePrivate - "
  986. " Clone failed. return 0x%08x", hr ));
  987. return hr;
  988. }
  989. //
  990. // keep the new parent
  991. //
  992. m_pParentTerminal = pParent;
  993. LOG((MSP_TRACE,
  994. "CFPTrack::InitializePrivate - exit S_OK. new parent [%p]",
  995. m_pParentTerminal));
  996. return S_OK;
  997. }
  998. /*++
  999. SetTerminalInfo
  1000. Sets the name of the terminal and the terminal type
  1001. Is called by InitializeDynamic
  1002. --*/
  1003. HRESULT CFPTrack::SetTerminalInfo()
  1004. {
  1005. LOG((MSP_TRACE, "CFPTrack::SetTerminalInfo - enter"));
  1006. //
  1007. // Get the name from the resource file
  1008. // if wasn't already read
  1009. //
  1010. if( m_szName[0] == (TCHAR)0)
  1011. {
  1012. //
  1013. // Read the name
  1014. //
  1015. TCHAR szName[ MAX_PATH ];
  1016. if(::LoadString(_Module.GetResourceInstance(), IDS_FPTRACK, szName, MAX_PATH))
  1017. {
  1018. lstrcpyn( m_szName, szName, MAX_PATH);
  1019. }
  1020. else
  1021. {
  1022. LOG((MSP_ERROR, "CFPTrack::SetTerminalInfo - exit "
  1023. "LoadString failed. Returns E_OUTOFMEMORY"));
  1024. return E_OUTOFMEMORY;
  1025. }
  1026. }
  1027. //
  1028. // Sets the terminal type (TT_DYNAMIC)
  1029. //
  1030. m_TerminalType = TT_DYNAMIC;
  1031. LOG((MSP_TRACE, "CFPTrack::SetTerminalInfo - exit S_OK"));
  1032. return S_OK;
  1033. }
  1034. /*++
  1035. CreateFilter
  1036. Create the internal filter
  1037. Is called by InitializeDynamic
  1038. --*/
  1039. HRESULT CFPTrack::CreateFilter()
  1040. {
  1041. LOG((MSP_TRACE, "CFPTrack::CreateFilter - enter"));
  1042. //
  1043. // Create the filter
  1044. //
  1045. CFPFilter* pFilter = new CFPFilter( m_AllocProp );
  1046. if( NULL == pFilter )
  1047. {
  1048. LOG((MSP_ERROR, "CFPTrack::CreateFilter - "
  1049. "create filter failed - returning E_OUTOFMEMORY"));
  1050. return E_OUTOFMEMORY;
  1051. }
  1052. //
  1053. // Keep this reference
  1054. //
  1055. m_pFPFilter = pFilter;
  1056. //
  1057. // Initialize filter
  1058. //
  1059. HRESULT hr = pFilter->InitializePrivate(
  1060. m_dwMediaType,
  1061. &m_Lock,
  1062. m_pMediaType,
  1063. this,
  1064. m_pSource);
  1065. if( FAILED(hr) )
  1066. {
  1067. // Clean-up
  1068. delete m_pFPFilter;
  1069. m_pFPFilter = NULL;
  1070. LOG((MSP_ERROR, "CFPTrack::CreateFilter - "
  1071. "InitializePrivate failed - returning 0x%08x", hr));
  1072. return hr;
  1073. }
  1074. //
  1075. // Get IBaseFilter interface
  1076. //
  1077. hr = pFilter->QueryInterface(
  1078. IID_IBaseFilter,
  1079. (void**)&m_pIFilter
  1080. );
  1081. if( FAILED(hr) )
  1082. {
  1083. // Clean-up
  1084. delete m_pFPFilter;
  1085. m_pFPFilter = NULL;
  1086. LOG((MSP_ERROR, "CFPTrack::CreateFilter - "
  1087. "QI for IBaseFilter failed - returning 0x%08x", hr));
  1088. return hr;
  1089. }
  1090. LOG((MSP_TRACE, "CFPTrack::CreateFilter - exit S_OK"));
  1091. return S_OK;
  1092. }
  1093. /*++
  1094. FindPin
  1095. Get the pin from the filter and set the m_pIPin member
  1096. Is called by InitializeDynamic
  1097. --*/
  1098. HRESULT CFPTrack::FindPin()
  1099. {
  1100. LOG((MSP_TRACE, "CFPTrack::FindPin - enter"));
  1101. //
  1102. // Validates the filter object (smart pointer)
  1103. //
  1104. if (m_pIFilter == NULL)
  1105. {
  1106. LOG((MSP_ERROR, "CFPTrack::FindPin - "
  1107. "filter object is NULL - returning E_POINTER"));
  1108. return E_POINTER;
  1109. }
  1110. //
  1111. // Make sure the IPin object is not initialized
  1112. //
  1113. if (m_pIPin != NULL)
  1114. {
  1115. LOG((MSP_ERROR, "CFPTrack::FindPin - "
  1116. "already got a pin - returning E_INVALIDARG"));
  1117. return E_INVALIDARG;
  1118. }
  1119. HRESULT hr;
  1120. IEnumPins* pIEnumPins;
  1121. ULONG cFetched;
  1122. //
  1123. // Get the pins collection
  1124. //
  1125. hr = m_pIFilter->EnumPins(&pIEnumPins);
  1126. if( FAILED(hr) )
  1127. {
  1128. LOG((MSP_ERROR, "CFPTrack::FindPin - "
  1129. "cannot enums - returning 0x%08x", hr));
  1130. return hr;
  1131. }
  1132. //
  1133. // Get the out pin of the FilePlayback Filter
  1134. //
  1135. hr = pIEnumPins->Next(1, &m_pIPin, &cFetched);
  1136. //
  1137. // Clean-up
  1138. //
  1139. pIEnumPins->Release();
  1140. if( FAILED(hr) )
  1141. {
  1142. LOG((MSP_ERROR,
  1143. "CFPTrack::FindPin - "
  1144. "cannot get a pin - returning 0x%08x", hr));
  1145. }
  1146. LOG((MSP_TRACE, "CFPTrack::FindPin - exit S_OK"));
  1147. return S_OK;
  1148. }
  1149. HRESULT
  1150. CFPTrack::PinSignalsStop(FT_STATE_EVENT_CAUSE why,
  1151. HRESULT hrErrorCode)
  1152. {
  1153. LOG((MSP_TRACE, "CFPTrack::PinSignalsStop[%p] - enter", this));
  1154. ITTerminal *pParentTerminal = NULL;
  1155. {
  1156. //
  1157. // get parent terminal in a lock
  1158. //
  1159. // it is safe to addref/release a parent while in a lock, since parent
  1160. // uses a special lock for addref/release, but we should not make other
  1161. // calls into the parent while holding our lock -- if we do, we may
  1162. // have a deadlock if the parent decides to addref of release us while
  1163. // holding its own lock.
  1164. //
  1165. m_Lock.Lock();
  1166. if (NULL != m_pParentTerminal)
  1167. {
  1168. pParentTerminal = m_pParentTerminal;
  1169. m_pParentTerminal->AddRef();
  1170. }
  1171. m_Lock.Unlock();
  1172. }
  1173. //
  1174. // if we have a parent -- try to notify it
  1175. //
  1176. // note: this should not be done while holding a lock on the track to
  1177. // prevent deadlocks when parent addrefs a track while holding its lock
  1178. //
  1179. if (NULL != pParentTerminal)
  1180. {
  1181. //
  1182. // tell the parent terminal that we stopped
  1183. //
  1184. CFPTerminal *pFilePlaybackTerminal = static_cast<CFPTerminal *>(pParentTerminal);
  1185. if (NULL != pFilePlaybackTerminal)
  1186. {
  1187. LOG((MSP_TRACE, "CFPTrack::PinSignalsStop - notifying parent"));
  1188. pFilePlaybackTerminal->TrackStateChange(TMS_IDLE, why, hrErrorCode);
  1189. //
  1190. // no longer need pointer to the object
  1191. //
  1192. pFilePlaybackTerminal = NULL;
  1193. }
  1194. else
  1195. {
  1196. LOG((MSP_ERROR,
  1197. "CFPTrack::PinSignalsStop - pin stopped, but the parent is not of the right type. cannot notify parent"));
  1198. TM_ASSERT(FALSE);
  1199. }
  1200. pParentTerminal->Release();
  1201. pParentTerminal = NULL;
  1202. }
  1203. else
  1204. {
  1205. LOG((MSP_WARN, "CFPTrack::PinSignalsStop - pin stopped, but there is no parent to notify"));
  1206. }
  1207. LOG((MSP_TRACE, "CFPTrack::PinSignalsStop - finish"));
  1208. return S_OK;
  1209. }
  1210. /////////////////////////////////////////////////////////////////////////////
  1211. ULONG CFPTrack::InternalAddRef()
  1212. {
  1213. LOG((MSP_TRACE, "CFPTrack::InternalAddRef[%p] - enter.", this));
  1214. CLock lock(m_Lock);
  1215. //
  1216. // attempt to notify a parent, if we have one. otherwise simply decrement
  1217. // our refcount
  1218. //
  1219. if (NULL != m_pParentTerminal)
  1220. {
  1221. LOG((MSP_TRACE, "CFPTrack::InternalAddRef - notifying the parent."));
  1222. CFPTerminal *pParentPlaybackObject = static_cast<CFPTerminal *>(m_pParentTerminal);
  1223. //
  1224. // propagate release to the parent
  1225. //
  1226. pParentPlaybackObject->ChildAddRef();
  1227. //
  1228. // if the parent has gone away, it will set my parent pointer to null, and call release on me again.
  1229. // that is ok -- i (the track) will not go away until the first call to release completes and decrements refcount to 0
  1230. //
  1231. }
  1232. ULONG ulReturnValue = InterlockedIncrement(&m_dwRef);
  1233. LOG((MSP_TRACE, "CFPTrack::InternalAddRef - finish. ulReturnValue %lu", ulReturnValue));
  1234. return ulReturnValue;
  1235. }
  1236. /////////////////////////////////////////////////////////////////////////////
  1237. ULONG CFPTrack::InternalRelease()
  1238. {
  1239. LOG((MSP_TRACE, "CFPTrack::InternalRelease[%p] - enter.", this));
  1240. CLock lock(m_Lock);
  1241. //
  1242. // attempt to notify a parent, if we have one. otherwise simply decrement
  1243. // our refcount
  1244. //
  1245. if (NULL != m_pParentTerminal)
  1246. {
  1247. LOG((MSP_TRACE, "CFPTrack::InternalRelease - notifying the parent."));
  1248. CFPTerminal *pParentTerminalObject = static_cast<CFPTerminal *>(m_pParentTerminal);
  1249. //
  1250. // propagate release to the parent
  1251. //
  1252. pParentTerminalObject->ChildRelease();
  1253. //
  1254. // if the parent has gone away, it will set my parent pointer to null, and call release on me again.
  1255. // that is ok -- i (the track) will not go away until the first call to release completes and decrements refcount to 0
  1256. //
  1257. }
  1258. //
  1259. // decrement and return new refcount
  1260. //
  1261. ULONG ulReturnValue = InterlockedDecrement(&m_dwRef);
  1262. LOG((MSP_TRACE, "CFPTrack::InternalRelease - finish. ulReturnValue %lu", ulReturnValue));
  1263. return ulReturnValue;
  1264. }
  1265. //eof