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.

4018 lines
110 KiB

  1. /*
  2. Copyright (c) 1998-1999 Microsoft Corporation
  3. */
  4. /*
  5. NOTE: regarding AMOVIE code
  6. 1. Timing info is messed up (as noted by the owner, robin speed himself)
  7. 2. Atleast a few obvious bugs - mostly in the derived classes, but a few in
  8. the base classes as well. The current approach is to try and cover it up
  9. by over-riding the method
  10. ZoltanS note: and now that the amovie code is folded into this directory,
  11. I am trying to collapse the humongous class hierarchy and just replace
  12. the original methods with the fixed overrides
  13. */
  14. #include "stdafx.h"
  15. #include "atlconv.h"
  16. #include "termmgr.h"
  17. #include "medpump.h"
  18. #include "meterf.h"
  19. // for some reason ddstream.lib requires this .. TO DO ** (get rid of it)
  20. #ifdef DBG
  21. BOOL bDbgTraceFunctions;
  22. BOOL bDbgTraceInterfaces;
  23. BOOL bDbgTraceTimes;
  24. #endif // DBG
  25. #ifdef DBG
  26. #include <stdio.h>
  27. #endif // DBG
  28. // static variables
  29. // implements single thread pump for the write media streaming terminal
  30. // filters. it creates a thread if necessary when a write terminal registers
  31. // itself (in commit). the filter signals its wait handle in decommit,
  32. // causing the thread to wake up and remove the filter from its data
  33. // structures. the thread returns when there are no more filters to service
  34. // ZoltanS: now a pool of pump threads
  35. CMediaPumpPool CMediaTerminalFilter::ms_MediaPumpPool;
  36. // checks if the two am media type structs are same
  37. // a simple equality of struct won't do here
  38. BOOL
  39. IsSameAMMediaType(
  40. IN const AM_MEDIA_TYPE *pmt1,
  41. IN const AM_MEDIA_TYPE *pmt2
  42. )
  43. {
  44. // we don't expect the the two pointers to be null
  45. TM_ASSERT(NULL != pmt1);
  46. TM_ASSERT(NULL != pmt2);
  47. // if the two pointer values are same, there is nothing more
  48. // to check
  49. if (pmt1 == pmt2) return TRUE;
  50. // each of the members of the AM_MEDIA_TYPE struct must be
  51. // the same (majortype, subtype, formattype
  52. // and (cbFormat, pbFormat))
  53. if ( (pmt1->majortype != pmt2->majortype) ||
  54. (pmt1->subtype != pmt2->subtype) ||
  55. (pmt1->formattype != pmt2->formattype) )
  56. // || (pmt1->cbFormat != pmt2->cbFormat) )
  57. {
  58. return FALSE;
  59. }
  60. // if the pbFormat pointer is null for either, they can't be
  61. // the same
  62. if ( (NULL == pmt1->pbFormat) || (NULL == pmt2->pbFormat) )
  63. {
  64. return FALSE;
  65. }
  66. DWORD dwSize = ( pmt1->cbFormat < pmt2->cbFormat ) ? pmt1->cbFormat :
  67. pmt2->cbFormat;
  68. // we don't handle anything other than waveformatex or videoinfo,
  69. // and as these two don't have any member pointers, a bitwise
  70. // comparison is sufficient to check for equality
  71. if ( (FORMAT_WaveFormatEx == pmt1->formattype) ||
  72. (FORMAT_VideoInfo == pmt1->formattype) )
  73. {
  74. return !memcmp(
  75. pmt1->pbFormat,
  76. pmt2->pbFormat,
  77. dwSize
  78. );
  79. }
  80. return FALSE;
  81. }
  82. /////////////////////////////////////////////////////////////////////////////
  83. //
  84. // Equal
  85. //
  86. // this is a helper method that returns TRUE if the properties are identical
  87. //
  88. BOOL Equal(const ALLOCATOR_PROPERTIES *pA, const ALLOCATOR_PROPERTIES *pB)
  89. {
  90. if ( pA->cBuffers != pB->cBuffers )
  91. {
  92. return FALSE;
  93. }
  94. if ( pA->cbBuffer != pB->cbBuffer )
  95. {
  96. return FALSE;
  97. }
  98. if ( pA->cbPrefix != pB->cbPrefix )
  99. {
  100. return FALSE;
  101. }
  102. if ( pA->cbPrefix != pB->cbPrefix )
  103. {
  104. return FALSE;
  105. }
  106. if ( pA->cbAlign != pB->cbAlign )
  107. {
  108. return FALSE;
  109. }
  110. return TRUE;
  111. }
  112. //////////////////////////////////////////////////////////////////////////////
  113. //////////////////////////////////////////////////////////////////////////////
  114. //
  115. // Helper function to test if two sets of allocator properties are
  116. // significantly different.
  117. //
  118. BOOL AllocatorPropertiesDifferSignificantly(
  119. const ALLOCATOR_PROPERTIES * pRequested,
  120. const ALLOCATOR_PROPERTIES * pActual
  121. )
  122. {
  123. if ( pActual->cBuffers != pRequested->cBuffers )
  124. {
  125. return TRUE;
  126. }
  127. if ( pActual->cbBuffer != pRequested->cbBuffer )
  128. {
  129. return TRUE;
  130. }
  131. //
  132. // we do not care about alignment - cbAlign
  133. //
  134. if ( pActual->cbPrefix != pRequested->cbPrefix )
  135. {
  136. return TRUE;
  137. }
  138. return FALSE;
  139. }
  140. // free the allocated member variables
  141. // virtual
  142. CMediaTerminalFilter::~CMediaTerminalFilter(
  143. )
  144. {
  145. LOG((MSP_TRACE, "CMediaTerminalFilter::~CMediaTerminalFilter called"));
  146. // if memory was allocated for the media type, release it
  147. // it checks for NULL == m_pSuggestedMediaType
  148. DeleteMediaType(m_pSuggestedMediaType);
  149. // Moved from base class:
  150. SetState(State_Stopped); // Make sure we're decommitted and pump is dead
  151. }
  152. // calls the IAMMediaStream::Initialize(NULL, 0, PurposeId, StreamType),
  153. // sets certain member variables
  154. // ex. m_pAmovieMajorType
  155. HRESULT
  156. CMediaTerminalFilter::Init(
  157. IN REFMSPID PurposeId,
  158. IN const STREAM_TYPE StreamType,
  159. IN const GUID &AmovieMajorType
  160. )
  161. {
  162. LOG((MSP_TRACE, "CMediaTerminalFilter::Init[%p] (%p, %p, %p) called",
  163. this, &PurposeId, &StreamType, &AmovieMajorType));
  164. HRESULT hr;
  165. // initialize the CStream by calling IAMMediaStream::Initialize
  166. hr = Initialize(
  167. NULL,
  168. (StreamType == STREAMTYPE_READ) ? AMMSF_STOPIFNOSAMPLES : 0,
  169. PurposeId,
  170. StreamType
  171. );
  172. BAIL_ON_FAILURE(hr);
  173. // set member variables
  174. m_bIsAudio = (MSPID_PrimaryAudio == PurposeId) ? TRUE : FALSE;
  175. m_pAmovieMajorType = &AmovieMajorType;
  176. SetDefaultAllocatorProperties();
  177. LOG((MSP_TRACE, "CMediaTerminalFilter::Init - succeeded"));
  178. return S_OK;
  179. }
  180. // CMediaPump calls this to get the next filled buffer to pass downstream
  181. // for audio filters, this method is also responsible for waiting
  182. // for 20ms and sending the data in one filled buffer
  183. HRESULT
  184. CMediaTerminalFilter::GetFilledBuffer(
  185. OUT IMediaSample *&pMediaSample,
  186. OUT DWORD &WaitTime
  187. )
  188. {
  189. LOG((MSP_TRACE, "CMediaTerminalFilter::GetFilledBuffer[%p] ([out]pMediaSample=%p, [out]WaitTime=%lx) called",
  190. this, pMediaSample, WaitTime));
  191. HRESULT hr = S_OK;
  192. Lock();
  193. if ( ! m_bCommitted )
  194. {
  195. Unlock();
  196. return VFW_E_NOT_COMMITTED;
  197. }
  198. if ( m_pSampleBeingFragmented == NULL )
  199. {
  200. // get a sample
  201. CSample *pSample = m_pFirstFree;
  202. // if no sample, someone must have forced termination
  203. // of the sample(s) which caused signaling of the wait
  204. // event, or it must have decommitted and committed again
  205. if (NULL == pSample)
  206. {
  207. // we'll wait for someone to add a sample to the pool
  208. m_lWaiting = 1;
  209. Unlock();
  210. return S_FALSE;
  211. }
  212. m_pFirstFree = pSample->m_pNextFree;
  213. if (m_pFirstFree) m_pFirstFree->m_pPrevFree = NULL;
  214. else m_pLastFree = NULL;
  215. pSample->m_pNextFree = NULL; // Just to be tidy
  216. TM_ASSERT(pSample->m_Status == MS_S_PENDING);
  217. CHECKSAMPLELIST
  218. // all samples in the pool have a refcnt on them, this
  219. // must be released before m_pSampleBeingFragmented is set to NULL
  220. m_pSampleBeingFragmented = (CUserMediaSample *)pSample;
  221. // note the current time only for audio samples
  222. m_pSampleBeingFragmented->BeginFragment(m_bIsAudio);
  223. }
  224. //
  225. // Implementations diverge here depending on whether we are using the
  226. // sample queue (CNBQueue; m_bUsingMyAllocator == FALSE) and fragmenting
  227. // samples by reference, or using a downstream allocator and copying
  228. // samples.
  229. //
  230. BOOL fDone;
  231. if ( m_bUsingMyAllocator )
  232. {
  233. hr = FillMyBuffer(
  234. pMediaSample, // OUT
  235. WaitTime, // OUT
  236. & fDone // OUT
  237. );
  238. }
  239. else
  240. {
  241. hr = FillDownstreamAllocatorBuffer(
  242. pMediaSample, // OUT
  243. WaitTime, // OUT
  244. & fDone // OUT
  245. );
  246. }
  247. //
  248. // S_OK means everything is ok, and we need to return the wait time,
  249. // update m_pSampleBeingFragmented, and addref the IMediaSample.
  250. // other success codes (or failure codes) mean return immediately.
  251. //
  252. if ( hr != S_OK )
  253. {
  254. Unlock();
  255. return hr;
  256. }
  257. // return the time to wait in milliseconds
  258. if (m_bIsAudio)
  259. {
  260. WaitTime = m_pSampleBeingFragmented->GetTimeToWait(m_AudioDelayPerByte);
  261. }
  262. else
  263. {
  264. WaitTime = m_VideoDelayPerFrame;
  265. }
  266. //
  267. // ZoltanS: make the second sample after we start playing
  268. // a little early, to account for a bit of jitter in delivery to
  269. // the waveout filter, and also to "prime" buggy wave drivers
  270. // such as Dialogic, which need a backlog to operate properly.
  271. // This is fine for IP as long as the timestamps are set correctly
  272. // (note that the most advancement we ever do is one packet's worth
  273. // on the second packet only).
  274. //
  275. // This needs to be done before SetTime, because SetTime
  276. // changes m_rtLastSampleEndedAt.
  277. //
  278. const DWORD SECOND_SAMPLE_EARLINESS = 500;
  279. if ( m_rtLastSampleEndedAt == 0 )
  280. {
  281. LOG((MSP_TRACE, "CMediaTerminalFilter::GetFilledBuffer - "
  282. "this is the first sample; making the next sample %d ms early",
  283. SECOND_SAMPLE_EARLINESS));
  284. if ( WaitTime < SECOND_SAMPLE_EARLINESS )
  285. {
  286. WaitTime = 0;
  287. }
  288. else
  289. {
  290. WaitTime -= SECOND_SAMPLE_EARLINESS;
  291. }
  292. }
  293. //
  294. // if the sample came in late, set discontinuity flag. this flag may be
  295. // used by the downstream filters in their dejitter algorithms.
  296. //
  297. // this needs to be called before settime (because settime will reset
  298. // m_rtLastSampleDuration to duration of the _current_ sample
  299. //
  300. hr = SetDiscontinuityIfNeeded(pMediaSample);
  301. if ( FAILED(hr) )
  302. {
  303. //
  304. // not fatal, log and continue
  305. //
  306. LOG((MSP_ERROR,
  307. "CMediaTerminalFilter::GetFilledBuffer - SetDiscontinuityIfNeeded failed. "
  308. "hr = 0x%lx", hr));
  309. }
  310. //
  311. // put a timestamp on the sample
  312. //
  313. hr = SetTime( pMediaSample );
  314. if ( FAILED(hr) )
  315. {
  316. LOG((MSP_ERROR,
  317. "CMediaTerminalFilter::GetFilledBuffer() "
  318. "Failed putting timestamp on the sample; hr = 0x%lx", hr));
  319. }
  320. // if fDone, we reached the end of the buffer we were fragmenting
  321. if ( fDone )
  322. {
  323. ((IStreamSample *)m_pSampleBeingFragmented)->Release();
  324. m_pSampleBeingFragmented = NULL;
  325. }
  326. Unlock();
  327. LOG((MSP_TRACE, "CMediaTerminalFilter::GetFilledBuffer(%p, %u) succeeded",
  328. pMediaSample, WaitTime));
  329. return S_OK;
  330. }
  331. //////////////////////////////////////////////////////////////////////////////
  332. //
  333. // FillDownstreamAllocatorBuffer
  334. //
  335. // This is called in GetFilledBuffer when we are using the downstream
  336. // allocator. It gets a sample from our outgoing sample pool and copies the
  337. // data
  338. //
  339. // Note that GetBuffer can block here, which means we are just as hosed as if
  340. // Receive blocks (but there is no deadlock situation possible as is remedied
  341. // in FillMyBuffer).
  342. //
  343. HRESULT CMediaTerminalFilter::FillDownstreamAllocatorBuffer(
  344. OUT IMediaSample *& pMediaSample,
  345. OUT DWORD & WaitTime,
  346. OUT BOOL * pfDone
  347. )
  348. {
  349. //
  350. // Get a buffer from the downstream allocator.
  351. //
  352. TM_ASSERT( ! m_bUsingMyAllocator );
  353. HRESULT hr;
  354. hr = m_pAllocator->GetBuffer(
  355. & pMediaSample,
  356. NULL, // no start time (used by video renderer only)
  357. NULL, // no no end time (used by video renderer only)
  358. 0 // no flags (could be: not a sync point, prev frame skipped)
  359. );
  360. if ( FAILED(hr) )
  361. {
  362. return hr;
  363. }
  364. //
  365. // Got a free output buffer, so put some data in it.
  366. //
  367. // if audio, fragment the buffer else, pass the whole buffer
  368. // (no fragmentation for video as video data is frame based)
  369. //
  370. // CUserMediaSample::CopyFragment is just like CUserMediaSample::Fragment
  371. // except that the outgoing sample is an IMediaSample interface instead
  372. // of our own CQueueMediaSample.
  373. //
  374. hr = m_pSampleBeingFragmented->CopyFragment(
  375. m_bIsAudio, // allow fragmentation if audio, not if video (IN)
  376. m_AllocProps.cbBuffer, // outgoing buffer size (IN)
  377. pMediaSample, // outgoing sample (IN)
  378. *pfDone // done with user sample? reference (OUT)
  379. );
  380. //
  381. // We've filled out pMediaSample. The caller fills out the wait time if the
  382. // result of this method is S_OK.
  383. //
  384. return hr;
  385. }
  386. //////////////////////////////////////////////////////////////////////////////
  387. //
  388. // FillMyBuffer
  389. //
  390. // This is called in GetFilledBuffer when we are using our own allocator. It
  391. // gets a sample from our outgoing sample pool. If no sample is available
  392. // right now, it sets the wait time and returns a special success code.
  393. // If a sample is available, it sets it up to point to an appropriate chunk
  394. // of the "fragmented" source buffer.
  395. //
  396. HRESULT CMediaTerminalFilter::FillMyBuffer(
  397. OUT IMediaSample *& pMediaSample,
  398. OUT DWORD & WaitTime,
  399. OUT BOOL * pfDone
  400. )
  401. {
  402. //
  403. // Try to dequeue an output sample to send to. FALSE tells it to return NULL
  404. // rather than blocking if there is nothing on the queue. If there is nothing
  405. // on the queue, that means that the fact that we are holding the lock is
  406. // preventing the sample from being returned to the queue. We've only seen
  407. // this happen when ksproxy is used as the MSP's transport, since ksproxy
  408. // releases its samples asynchronously (on a separate Io completion thread).
  409. // With other transports, samples are released asynchronously each time,
  410. // and so we never get this situation.
  411. //
  412. // If the transport is truly messed up and no samples are completing, then
  413. // there is the additional consideration that we don't want to use up 100%
  414. // CPU and we want to be able to service other filters (this is on the
  415. // pump thread, serving up to 63 filters per thread). So rather than
  416. // retrying immediately we need to set a short dormancy time (for the
  417. // PumpMainLoop wait) before we try again.
  418. //
  419. CQueueMediaSample * pQueueSample;
  420. pQueueSample = m_SampleQueue.DeQueue( FALSE );
  421. if ( pQueueSample == NULL )
  422. {
  423. //
  424. // After return we will unlock, allowing async
  425. // FinalMediaSampleRelease to release sample.
  426. //
  427. //
  428. // Let's try again in three milliseconds. This is short enough not
  429. // to cause a noticeable quality degradation, and long enough to
  430. // prevent eating 100% CPU when the transport is broken and does not
  431. // return samples.
  432. //
  433. WaitTime = 3;
  434. LOG((MSP_TRACE, "CMediaTerminalFilter::FillMyBuffer - no available "
  435. "output samples in queue; returning "
  436. "VFW_S_NO_MORE_ITEMS"));
  437. return VFW_S_NO_MORE_ITEMS;
  438. }
  439. //
  440. // Got a free output buffer, so put some data in it.
  441. //
  442. // if audio, fragment the buffer else, pass the whole buffer
  443. // (no fragmentation for video as video data is frame based)
  444. //
  445. m_pSampleBeingFragmented->Fragment(
  446. m_bIsAudio, // allow fragmentation if audio, not if video
  447. m_AllocProps.cbBuffer, // outgoing buffer size
  448. *pQueueSample, // outgoing sample -- reference (IN parameter)
  449. *pfDone // done with user sample? -- reference (OUT parameter)
  450. );
  451. //
  452. // CODEWORK: to support combining as well as fragmentation, we would need to
  453. // (1) modify CUserMediaSample::Fragment and code underneath to append to outgoing
  454. // buffer (copy into it) -- this may get interesting considering we are dealing
  455. // with CQueueMediaSample as the outgoing samples!
  456. // (2) introduce a loop here in GetFilledBuffer -- keep getting more input samples
  457. // until the output sample is full or the input queue has been exhausted.
  458. // Interesting case is what to do if the input queue has been exhausted -- we
  459. // can perhaps just send the outgoing sample at that point. NOTE that this is
  460. // always going to happen on the last sample in a long stretch, and it will happen
  461. // a lot if the app is written to not submit all the samples at once (need to
  462. // document the latter)
  463. //
  464. //
  465. // Fill out pMediaSample. The caller fills out the wait time if the
  466. // result of this method is S_OK.
  467. //
  468. pMediaSample = (IMediaSample *)(pQueueSample->m_pMediaSample);
  469. pMediaSample->AddRef();
  470. return S_OK;
  471. }
  472. //////////////////////////////////////////////////////////////////////////////
  473. //
  474. // SetTime
  475. //
  476. // This is called from GetFilledBuffer() to set the timestamp on the sample
  477. // before sending it down the filter graph.
  478. //
  479. // The timestamp is determined based on the duration of the sample and the
  480. // timestamp of the last sample.
  481. //
  482. //////////////////////////////////////////////////////////////////////////////
  483. HRESULT CMediaTerminalFilter::SetTime(IMediaSample *pMediaSample)
  484. {
  485. HRESULT hr = S_OK;
  486. // the sample starts when the previous sample ended
  487. REFERENCE_TIME rtStartTime = m_rtLastSampleEndedAt;
  488. REFERENCE_TIME rtEndTime = rtStartTime;
  489. // calculate sample's duration
  490. if (m_bIsAudio)
  491. {
  492. HRESULT nSampleSize = pMediaSample->GetSize();
  493. m_rtLastSampleDuration =
  494. (REFERENCE_TIME)((double)nSampleSize * m_AudioDelayPerByte) * 10000;
  495. }
  496. else
  497. {
  498. // NOTE: assumption is that if not audio, it is video.
  499. // another assumption is that one media sample is one frame
  500. m_rtLastSampleDuration = m_VideoDelayPerFrame * 10000;
  501. }
  502. // when does the sample end?
  503. rtEndTime += m_rtLastSampleDuration;
  504. LOG((MSP_TRACE,
  505. "CMediaTerminal::SetTime setting timestamp to (%lu, %lu) ",
  506. (DWORD)(rtStartTime/10000), (DWORD)(rtEndTime/10000)));
  507. // we know when it started and when it ended. set the timestamp
  508. hr = pMediaSample->SetTime(&rtStartTime, &rtEndTime);
  509. m_rtLastSampleEndedAt = rtEndTime;
  510. return hr;
  511. }
  512. //////////////////////////////////////////////////////////////////////////////
  513. //
  514. // CMediaTerminalFilter::SetDiscontinuityIfNeeded
  515. //
  516. // this function sets discontinuity flag if the sample came too late to
  517. // smoothly continue the data flow. the assumption is that if the app did not
  518. // feed us with data for some time, then that means there was no data, and the
  519. // new data coming after the pause is a new part of the data stream, with a new
  520. // timeline
  521. //
  522. HRESULT CMediaTerminalFilter::SetDiscontinuityIfNeeded(IMediaSample *pMediaSample)
  523. {
  524. //
  525. // have a filter? (need it to get clock to get real time)
  526. //
  527. if ( NULL == m_pBaseFilter )
  528. {
  529. LOG((MSP_ERROR,
  530. "CMediaTerminalFilter::SetDiscontinuityIfNeeded() - no filter"));
  531. return E_UNEXPECTED;
  532. }
  533. //
  534. // ask filter for clock
  535. //
  536. IReferenceClock *pClock = NULL;
  537. HRESULT hr = m_pBaseFilter->GetSyncSource(&pClock);
  538. if (FAILED(hr))
  539. {
  540. //
  541. // no clock...
  542. //
  543. LOG((MSP_ERROR,
  544. "CMediaTerminalFilter::SetDiscontinuityIfNeeded() - no clock. hr = %lx", hr));
  545. return hr;
  546. }
  547. //
  548. // try to get real time
  549. //
  550. REFERENCE_TIME rtRealTimeNow = 0;
  551. hr = pClock->GetTime(&rtRealTimeNow);
  552. pClock->Release();
  553. pClock = NULL;
  554. if (FAILED(hr))
  555. {
  556. LOG((MSP_ERROR,
  557. "CMediaTerminalFilter::SetDiscontinuityIfNeeded() - failed to get time. "
  558. "hr = %lx", hr));
  559. return hr;
  560. }
  561. //
  562. // how much time passed since the last sample was sent?
  563. //
  564. REFERENCE_TIME rtRealTimeDelta = rtRealTimeNow - m_rtRealTimeOfLastSample;
  565. //
  566. // keep current real time as the "last sample's" real time, to be used for
  567. // in continuity determination for the next sample
  568. //
  569. m_rtRealTimeOfLastSample = rtRealTimeNow;
  570. //
  571. // how long was it supposed to take for the last sample to play? if too
  572. // much time passed since the sample was supposed to be done, this is
  573. // a discontinuity.
  574. //
  575. // note that SetTime on this sample should be called _after_ this method so
  576. // it does not set m_rtLastSampleDuration to duration of the current
  577. // sample before we figure out if this is a discontinuity or not
  578. //
  579. REFERENCE_TIME rtMaximumAllowedJitter = m_rtLastSampleDuration * 2;
  580. if ( rtRealTimeDelta > rtMaximumAllowedJitter )
  581. {
  582. //
  583. // too much real time passed since the last sample. discontinuity.
  584. //
  585. LOG((MSP_TRACE,
  586. "CMediaTerminalFilter::SetDiscontinuityIfNeeded - late sample. setting discontinuity"));
  587. hr = pMediaSample->SetDiscontinuity(TRUE);
  588. //
  589. // did we fail to set the discontinuity flag? propagate error to the caller
  590. //
  591. if (FAILED(hr))
  592. {
  593. LOG((MSP_ERROR,
  594. "CMediaTerminalFilter::SetDiscontinuityIfNeeded() - pMediaSample->SetTime failed. "
  595. "hr = 0x%lx", hr));
  596. return hr;
  597. }
  598. } // late sample
  599. return S_OK;
  600. }
  601. // the application is supposed to call DeleteMediaType(*ppmt) (on success)
  602. // if the pin is not connected, return the suggested media type if
  603. // one exists, else return error
  604. // else return the media format of the connected pin - this is stored in
  605. // m_ConnectedMediaType during Connect or ReceiveConnection
  606. HRESULT
  607. CMediaTerminalFilter::GetFormat(
  608. OUT AM_MEDIA_TYPE **ppmt
  609. )
  610. {
  611. AUTO_CRIT_LOCK;
  612. LOG((MSP_TRACE, "CMediaTerminal::GetFormat(%p) called", ppmt));
  613. // validate the parameter
  614. BAIL_IF_NULL(ppmt, E_POINTER);
  615. // the operator == is defined on CComPtr, hence, NULL comes second
  616. if (m_pConnectedPin == NULL)
  617. {
  618. // if a media type was suggested by the user before connection
  619. // create and return a media type structure with those values. The pin need not
  620. // be connected
  621. if (NULL != m_pSuggestedMediaType)
  622. {
  623. // create and copy the media type
  624. *ppmt = CreateMediaType(m_pSuggestedMediaType);
  625. return S_OK;
  626. }
  627. return VFW_E_NOT_CONNECTED;
  628. }
  629. // create and copy the media type
  630. *ppmt = CreateMediaType(&m_ConnectedMediaType);
  631. LOG((MSP_TRACE, "CMediaTerminal::GetFormat(%p) succeeded", ppmt));
  632. return S_OK;
  633. }
  634. // if this is called when the stream is connected,
  635. // an error value is returned.
  636. // it is only used in unconnected terminals to set the media format to negotiate
  637. // when connected to the filter graph.
  638. HRESULT
  639. CMediaTerminalFilter::SetFormat(
  640. IN AM_MEDIA_TYPE *pmt
  641. )
  642. {
  643. AUTO_CRIT_LOCK;
  644. LOG((MSP_TRACE, "CMediaTerminalFilter::SetFormat(%p) - enter", pmt));
  645. // check if already connected
  646. if (m_pConnectedPin != NULL)
  647. {
  648. LOG((MSP_ERROR, "CMediaTerminalFilter::SetFormat(%p) - "
  649. "already connected - exit VFW_E_ALREADY_CONNECTED", pmt));
  650. return VFW_E_ALREADY_CONNECTED;
  651. }
  652. //
  653. // ZoltanS: To aid the MSPs in conveying to the application the media type
  654. // being used on a stream, SetFormat can no longer be successfully called
  655. // more than once with different media types.
  656. //
  657. // if pmt == NULL and m_psmt == NULL then do nothing, return S_OK
  658. // if pmt == NULL and m_psmt != NULL then do nothing, return error
  659. // if pmt != NULL and m_psmt != NULL then
  660. // if media types are the same then do nothing, return S_OK
  661. // if media types differ then do nothing, return error
  662. //
  663. // only if pmt != NULL and m_psmt == NULL then try to set the media type
  664. //
  665. if ( pmt == NULL )
  666. {
  667. if ( m_pSuggestedMediaType == NULL )
  668. {
  669. LOG((MSP_WARN, "CMediaTerminalFilter::SetFormat(%p) - "
  670. "was NULL, set to NULL - this does nothing - exit S_OK",
  671. pmt));
  672. return S_OK;
  673. }
  674. else
  675. {
  676. LOG((MSP_ERROR, "CMediaTerminalFilter::SetFormat(%p) - "
  677. "was non-NULL, tried to set to NULL - rejected because once "
  678. "a type is set it is permanent - exit VFW_E_TYPE_NOT_ACCEPTED",
  679. pmt));
  680. return VFW_E_TYPE_NOT_ACCEPTED;
  681. }
  682. }
  683. else if ( m_pSuggestedMediaType != NULL )
  684. {
  685. if ( IsSameAMMediaType(pmt, m_pSuggestedMediaType) )
  686. {
  687. LOG((MSP_WARN, "CMediaTerminalFilter::SetFormat(%p) - "
  688. "was non-NULL, set same type again - this does nothing - "
  689. "exit S_OK", pmt));
  690. return S_OK;
  691. }
  692. else
  693. {
  694. LOG((MSP_ERROR, "CMediaTerminalFilter::SetFormat(%p) - "
  695. "was non-NULL, tried to set to new, different type - "
  696. "rejected because once a type is set it is permanent - "
  697. "exit VFW_E_TYPE_NOT_ACCEPTED", pmt));
  698. return VFW_E_TYPE_NOT_ACCEPTED;
  699. }
  700. }
  701. LOG((MSP_TRACE, "CMediaTerminalFilter::SetFormat(%p) - OK to try setting "
  702. "format - calling QueryAccept", pmt));
  703. //
  704. // check if the media type is acceptable for the terminal
  705. // return VFW_E_INVALIDMEDIATYPE if we can't accept it
  706. //
  707. HRESULT hr = QueryAccept(pmt);
  708. if ( hr != S_OK ) // NOTE: S_FALSE from QueryAccept indicates rejection.
  709. {
  710. LOG((MSP_ERROR, "CMediaTerminalFilter::SetFormat(%p) - "
  711. "QueryAccept rejected type - exit VFW_E_INVALIDMEDIATYPE", pmt));
  712. return VFW_E_INVALIDMEDIATYPE;
  713. }
  714. //
  715. // Accepted. Create an am media type initialized with the pmt value.
  716. //
  717. m_pSuggestedMediaType = CreateMediaType(pmt);
  718. if ( m_pSuggestedMediaType == NULL )
  719. {
  720. LOG((MSP_ERROR, "CMediaTerminalFilter::SetFormat(%p) - "
  721. "out of memory in CreateMediaType - exit E_OUTOFMEMORY", pmt));
  722. return E_OUTOFMEMORY;
  723. }
  724. LOG((MSP_TRACE, "CMediaTerminalFilter::SetFormat succeeded - new media "
  725. "type (%p) set", pmt));
  726. return S_OK;
  727. }
  728. // This method may only be called before connection and will
  729. // force the MST to convert buffers to this buffer size.
  730. // If this is set then we try these values during filter negotiation
  731. // and if the connecting filter doesn't accept these, then we convert
  732. STDMETHODIMP
  733. CMediaTerminalFilter::SetAllocatorProperties(
  734. IN ALLOCATOR_PROPERTIES *pAllocProperties
  735. )
  736. {
  737. LOG((MSP_TRACE,
  738. "CMediaTerminal::SetAllocatorProperties[%p] - enter. pAllocProperties[%p]",
  739. this, pAllocProperties));
  740. AUTO_CRIT_LOCK;
  741. //
  742. // check if already connected
  743. //
  744. if (m_pConnectedPin != NULL)
  745. {
  746. LOG((MSP_WARN,
  747. "CMediaTerminal::SetAllocatorProperties - VFW_E_ALREADY_CONNECTED"));
  748. return VFW_E_ALREADY_CONNECTED;
  749. }
  750. if (NULL == pAllocProperties)
  751. {
  752. m_bUserAllocProps = FALSE;
  753. if ( ! m_bSuggestedAllocProps )
  754. {
  755. SetDefaultAllocatorProperties();
  756. }
  757. return S_OK;
  758. }
  759. if (!CUserMediaSample::VerifyAllocatorProperties(
  760. m_bAllocateBuffers,
  761. *pAllocProperties
  762. ))
  763. {
  764. return E_FAIL;
  765. }
  766. DUMP_ALLOC_PROPS("CMediaTerminal::SetAllocatorProperties - new properties:", pAllocProperties);
  767. //
  768. // the user wants to use these properties on their samples
  769. //
  770. m_bUserAllocProps = TRUE;
  771. m_UserAllocProps = *pAllocProperties;
  772. m_AllocProps = m_UserAllocProps;
  773. LOG((MSP_TRACE,
  774. "CMediaTerminal::SetAllocatorProperties - succeeded"));
  775. return S_OK;
  776. }
  777. // an ITAllocatorProperties method
  778. // calls to IAMBufferNegotiation::GetAllocatorProperties are also forwarded to
  779. // this method by the terminal
  780. //
  781. // gets current values for the allocator properties
  782. // after connection, this provides the negotiated values
  783. // it is invalid before connection. The MST will accept
  784. // any values suggested by the filters it connects to
  785. STDMETHODIMP
  786. CMediaTerminalFilter::GetAllocatorProperties(
  787. OUT ALLOCATOR_PROPERTIES *pAllocProperties
  788. )
  789. {
  790. LOG((MSP_TRACE,
  791. "CMediaTerminalFilter::GetAllocatorProperties(%p) called",
  792. pAllocProperties));
  793. BAIL_IF_NULL(pAllocProperties, E_POINTER);
  794. AUTO_CRIT_LOCK;
  795. *pAllocProperties = m_AllocProps;
  796. DUMP_ALLOC_PROPS("CMediaTerminalFilter::GetAllocatorProperties", pAllocProperties);
  797. LOG((MSP_TRACE,
  798. "CMediaTerminalFilter::GetAllocatorProperties - succeeded"));
  799. return S_OK;
  800. }
  801. // an IAMBufferNegotiation method, forwarded to us by the terminal
  802. STDMETHODIMP
  803. CMediaTerminalFilter::SuggestAllocatorProperties(
  804. IN const ALLOCATOR_PROPERTIES *pAllocProperties
  805. )
  806. {
  807. LOG((MSP_TRACE,
  808. "CMediaTerminal::SuggestAllocatorProperties(%p) called",
  809. pAllocProperties));
  810. AUTO_CRIT_LOCK;
  811. // check if already connected
  812. if (m_pConnectedPin != NULL)
  813. {
  814. return VFW_E_ALREADY_CONNECTED;
  815. }
  816. //
  817. // Passing in NULL sets us back to our defaults. This would seem to make
  818. // sense, but it's nowhere to be found in the spec for the interface.
  819. //
  820. if (NULL == pAllocProperties)
  821. {
  822. m_bSuggestedAllocProps = FALSE;
  823. if ( m_bUserAllocProps )
  824. {
  825. m_AllocProps = m_UserAllocProps;
  826. }
  827. else
  828. {
  829. SetDefaultAllocatorProperties();
  830. }
  831. return S_OK;
  832. }
  833. //
  834. // If any of the fields in the suggested allocator properties
  835. // structure is negative, then we use the current values for those
  836. // fields. This is as per the interface definition. We can't just
  837. // change pAllocProperties because it's const.
  838. //
  839. ALLOCATOR_PROPERTIES FinalProps = * pAllocProperties;
  840. if ( FinalProps.cbAlign < 0 )
  841. {
  842. FinalProps.cbAlign = DEFAULT_AM_MST_BUFFER_ALIGNMENT;
  843. }
  844. if ( FinalProps.cbBuffer < 0 )
  845. {
  846. FinalProps.cbBuffer = DEFAULT_AM_MST_SAMPLE_SIZE;
  847. }
  848. if ( FinalProps.cbPrefix < 0 )
  849. {
  850. FinalProps.cbPrefix = DEFAULT_AM_MST_BUFFER_PREFIX;
  851. }
  852. if ( FinalProps.cBuffers < 0 )
  853. {
  854. FinalProps.cBuffers = DEFAULT_AM_MST_NUM_BUFFERS;
  855. }
  856. //
  857. // Sanity-check the resulting properties.
  858. //
  859. if (!CUserMediaSample::VerifyAllocatorProperties(
  860. m_bAllocateBuffers,
  861. FinalProps
  862. ))
  863. {
  864. return E_FAIL;
  865. }
  866. DUMP_ALLOC_PROPS("CMediaTerminalFilter::SuggestAllocatorProperties - suggested:", &FinalProps);
  867. //
  868. // if allocator properties were already set using SetAllocatorProperties,
  869. // fail -- suggesting does not override the value that has been set.
  870. //
  871. if (m_bUserAllocProps)
  872. {
  873. //
  874. // the properties have been Set by SetAllocatorProperties, whoever is
  875. // suggesting new properties had better be suggesting exactly same set,
  876. // otherwise we will fail the call.
  877. //
  878. if ( !Equal(&m_UserAllocProps, pAllocProperties) )
  879. {
  880. //
  881. // the application has requested specific allocator properties.
  882. // but someone is now suggesting a different set of properties.
  883. // the properties that have been set can only be re-set.
  884. //
  885. LOG((MSP_WARN,
  886. "CMediaTerminal::SuggestAllocatorProperties "
  887. "- can't override SetAllocatorProperties settings. VFW_E_WRONG_STATE"));
  888. return VFW_E_WRONG_STATE;
  889. }
  890. }
  891. // the MSP wants us to try these properties
  892. m_bSuggestedAllocProps = TRUE;
  893. m_AllocProps = FinalProps;
  894. DUMP_ALLOC_PROPS("CMediaTerminalFilter::SuggestAllocatorProperties - kept:", &m_AllocProps);
  895. LOG((MSP_TRACE, "CMediaTerminal::SuggestAllocatorProperties - finish"));
  896. return S_OK;
  897. }
  898. // TRUE by default. when set to FALSE, the sample allocated
  899. // by the MST don't have any buffers and they must be supplied
  900. // before Update is called on the samples
  901. STDMETHODIMP
  902. CMediaTerminalFilter::SetAllocateBuffers(
  903. IN BOOL bAllocBuffers
  904. )
  905. {
  906. LOG((MSP_TRACE,
  907. "CMediaTerminal::SetAllocateBuffers(%u) called",
  908. bAllocBuffers));
  909. AUTO_CRIT_LOCK;
  910. // check if already connected
  911. if (m_pConnectedPin != NULL) return VFW_E_ALREADY_CONNECTED;
  912. if (!CUserMediaSample::VerifyAllocatorProperties(
  913. bAllocBuffers,
  914. m_AllocProps
  915. ))
  916. {
  917. return E_FAIL;
  918. }
  919. // set flag for allocating buffers for samples
  920. m_bAllocateBuffers = bAllocBuffers;
  921. LOG((MSP_TRACE,
  922. "CMediaTerminal::SetAllocateBuffers(%u) succeeded",
  923. bAllocBuffers));
  924. return S_OK;
  925. }
  926. // returns the current value of this boolean configuration parameter
  927. STDMETHODIMP
  928. CMediaTerminalFilter::GetAllocateBuffers(
  929. OUT BOOL *pbAllocBuffers
  930. )
  931. {
  932. LOG((MSP_TRACE,
  933. "CMediaTerminal::GetAllocateBuffers(%p) called",
  934. pbAllocBuffers));
  935. BAIL_IF_NULL(pbAllocBuffers, E_POINTER);
  936. AUTO_CRIT_LOCK;
  937. *pbAllocBuffers = m_bAllocateBuffers;
  938. LOG((MSP_TRACE,
  939. "CMediaTerminal::GetAllocateBuffers(*%p = %u) succeeded",
  940. pbAllocBuffers, *pbAllocBuffers));
  941. return S_OK;
  942. }
  943. // this size is used for allocating buffers when AllocateSample is
  944. // called (if 0, the negotiated allocator properties' buffer size is
  945. // used). this is only valid when we have been told to allocate buffers
  946. STDMETHODIMP
  947. CMediaTerminalFilter::SetBufferSize(
  948. IN DWORD BufferSize
  949. )
  950. {
  951. AUTO_CRIT_LOCK;
  952. m_AllocateSampleBufferSize = BufferSize;
  953. return S_OK;
  954. }
  955. // returns the value used to allocate buffers when AllocateSample is
  956. // called. this is only valid when we have been told to allocate buffers
  957. STDMETHODIMP
  958. CMediaTerminalFilter::GetBufferSize(
  959. OUT DWORD *pBufferSize
  960. )
  961. {
  962. BAIL_IF_NULL(pBufferSize, E_POINTER);
  963. AUTO_CRIT_LOCK;
  964. *pBufferSize = m_AllocateSampleBufferSize;
  965. return S_OK;
  966. }
  967. // over-ride this to return failure. we don't allow it to join a multi-media
  968. // stream because the multi-media stream thinks it owns the stream
  969. STDMETHODIMP
  970. CMediaTerminalFilter::JoinAMMultiMediaStream(
  971. IN IAMMultiMediaStream *pAMMultiMediaStream
  972. )
  973. {
  974. AUTO_CRIT_LOCK;
  975. LOG((MSP_TRACE, "CMediaTerminalFilter::JoinAMMultiMediaStream(%p) called",
  976. pAMMultiMediaStream));
  977. return E_FAIL;
  978. }
  979. // over-ride this to return failure if a non-null proposed filter is
  980. // anything other than the media stream filter that is acceptable to us
  981. // This acceptable filter is set in SetMediaStreamFilter()
  982. STDMETHODIMP
  983. CMediaTerminalFilter::JoinFilter(
  984. IN IMediaStreamFilter *pMediaStreamFilter
  985. )
  986. {
  987. AUTO_CRIT_LOCK;
  988. LOG((MSP_TRACE, "CMediaTerminalFilter::JoinFilter(%p) called", pMediaStreamFilter));
  989. // check if the caller is trying to remove references to the media stream filter
  990. if (NULL == pMediaStreamFilter)
  991. {
  992. // null out references to the filter that were set when JoinFilter was called
  993. // with a valid media stream filter
  994. m_pFilter = NULL;
  995. m_pBaseFilter = NULL;
  996. return S_OK;
  997. }
  998. // check if the passed in filter is different from the one
  999. // that is acceptable
  1000. if (pMediaStreamFilter != m_pMediaStreamFilterToAccept)
  1001. {
  1002. return E_FAIL;
  1003. }
  1004. // if the filter is already set, it must have joined the graph already
  1005. if (NULL != m_pFilter)
  1006. {
  1007. return S_OK;
  1008. }
  1009. // save a pointer to the media stream filter
  1010. m_pFilter = pMediaStreamFilter;
  1011. // get the base filter i/f
  1012. HRESULT hr;
  1013. hr = pMediaStreamFilter->QueryInterface(IID_IBaseFilter, (void **)&m_pBaseFilter);
  1014. BAIL_ON_FAILURE(hr);
  1015. // release a reference as this method is not supposed to increase ref count on the filter
  1016. // NOTE: this is being done in CStream - merely following it. TO DO ** why?
  1017. m_pBaseFilter->Release();
  1018. LOG((MSP_TRACE, "CMediaTerminalFilter::JoinFilter(%p) succeeded", pMediaStreamFilter));
  1019. return S_OK;
  1020. }
  1021. // create an instance of CUserMediaSample and initialize it
  1022. STDMETHODIMP
  1023. CMediaTerminalFilter::AllocateSample(
  1024. IN DWORD dwFlags,
  1025. OUT IStreamSample **ppSample
  1026. )
  1027. {
  1028. LOG((MSP_TRACE,
  1029. "CMediaTerminalFilter::AllocateSample(dwFlags:%u, ppSample:%p)",
  1030. dwFlags, ppSample));
  1031. // validate parameters
  1032. // we don't support any flags
  1033. if (0 != dwFlags) return E_INVALIDARG;
  1034. BAIL_IF_NULL(ppSample, E_POINTER);
  1035. AUTO_CRIT_LOCK;
  1036. // create a sample and initialize it
  1037. HRESULT hr;
  1038. CComObject<CUserMediaSample> *pUserSample;
  1039. hr = CComObject<CUserMediaSample>::CreateInstance(&pUserSample);
  1040. BAIL_ON_FAILURE(hr);
  1041. // uses the allocator properties to allocate a bufer, if the
  1042. // user has asked for one to be created
  1043. hr = pUserSample->Init(
  1044. *this, m_bAllocateBuffers,
  1045. m_AllocateSampleBufferSize, m_AllocProps
  1046. );
  1047. if (HRESULT_FAILURE(hr))
  1048. {
  1049. delete pUserSample;
  1050. return hr;
  1051. }
  1052. hr = pUserSample->QueryInterface(IID_IStreamSample, (void **)ppSample);
  1053. if ( FAILED(hr) )
  1054. {
  1055. delete pUserSample;
  1056. return hr;
  1057. }
  1058. LOG((MSP_TRACE,
  1059. "CMediaTerminalFilter::AllocateSample(dwFlags:%u, ppSample:%p) succeeded",
  1060. dwFlags, ppSample));
  1061. return S_OK;
  1062. }
  1063. // return E_NOTIMPL - we don't have a mechanism to share a sample currently
  1064. STDMETHODIMP
  1065. CMediaTerminalFilter::CreateSharedSample(
  1066. IN IStreamSample *pExistingSample,
  1067. IN DWORD dwFlags,
  1068. OUT IStreamSample **ppNewSample
  1069. )
  1070. {
  1071. AUTO_CRIT_LOCK;
  1072. LOG((MSP_TRACE, "CMediaTerminalFilter::CreateSharedSample called"));
  1073. return E_NOTIMPL;
  1074. }
  1075. // supposed to get the format information for the passed in IMediaStream
  1076. // set this instance's media format
  1077. // not implemented currently
  1078. STDMETHODIMP
  1079. CMediaTerminalFilter::SetSameFormat(
  1080. IN IMediaStream *pStream,
  1081. IN DWORD dwFlags
  1082. )
  1083. {
  1084. AUTO_CRIT_LOCK;
  1085. LOG((MSP_TRACE, "CMediaTerminalFilter::SetSameFormat called"));
  1086. return E_NOTIMPL;
  1087. }
  1088. // CMediaTerminalFilter uses CMediaPump ms_MediaPumpPool instead of
  1089. // CPump *m_WritePump. therefore, CStream::SetState which uses CPump
  1090. // had to be overridden here
  1091. // also, it resets the end of stream flag when a connected stream is
  1092. // told to run
  1093. STDMETHODIMP
  1094. CMediaTerminalFilter::SetState(
  1095. IN FILTER_STATE State
  1096. )
  1097. {
  1098. LOG((MSP_TRACE, "IMediaStream::SetState(%d) called",State));
  1099. Lock();
  1100. if (m_pConnectedPin == NULL) {
  1101. Unlock();
  1102. if (State == STREAMSTATE_RUN) {
  1103. EndOfStream();
  1104. }
  1105. } else {
  1106. TM_ASSERT(m_pAllocator != NULL);
  1107. m_FilterState = State;
  1108. if (State == State_Stopped) {
  1109. m_pAllocator->Decommit();
  1110. if (!m_bUsingMyAllocator) {
  1111. Decommit();
  1112. }
  1113. Unlock();
  1114. } else {
  1115. // rajeevb - clear the end of stream flag
  1116. m_bEndOfStream = false;
  1117. // zoltans - moved Unlock here to avoid deadlock in commit.
  1118. // some of what's done within Commit needs to have the lock
  1119. // released. That's to avoid holding the stream lock while
  1120. // trying to acquire the pump lock.
  1121. Unlock();
  1122. m_pAllocator->Commit();
  1123. if (!m_bUsingMyAllocator) {
  1124. Commit();
  1125. }
  1126. }
  1127. }
  1128. if (State == State_Stopped)
  1129. {
  1130. LOG((MSP_TRACE, "CMediaTerminalFilter::SetState stopped. "));
  1131. m_rtLastSampleEndedAt = 0;
  1132. }
  1133. LOG((MSP_TRACE, "IMediaStream::SetState(%d) succeeded",State));
  1134. return S_OK;
  1135. }
  1136. // if own allocator, just set completion on the sample
  1137. // NOTE: this is not being done in the derived classes
  1138. // else, get a stream sample from pool,
  1139. // copy the media sample onto the stream sample
  1140. // set completion
  1141. // NOTE: find out why the quality notifications are being sent in the
  1142. // derived classes
  1143. STDMETHODIMP
  1144. CMediaTerminalFilter::Receive(
  1145. IN IMediaSample *pSample
  1146. )
  1147. {
  1148. LOG((MSP_TRACE, "CMediaTerminalFilter::Receive(%p) called", pSample));
  1149. COM_LOCAL_CRIT_LOCK LocalLock(this);
  1150. if (m_bFlushing) return S_FALSE;
  1151. if (0 > pSample->GetActualDataLength()) return S_FALSE;
  1152. if (m_bUsingMyAllocator)
  1153. {
  1154. CUserMediaSample *pSrcSample =
  1155. (CUserMediaSample *)((CMediaSampleTM *)pSample)->m_pSample;
  1156. pSrcSample->m_bReceived = true;
  1157. // the completion state need not be set because, the caller holds the last reference
  1158. // on the media sample and when it is released (after the return of this fn),
  1159. // the completion state gets set
  1160. return S_OK;
  1161. }
  1162. CUserMediaSample *pDestSample;
  1163. REFERENCE_TIME rtStart, rtEnd;
  1164. pSample->GetTime(&rtStart, &rtEnd);
  1165. LOG((MSP_TRACE,
  1166. "CMediaTerminalFilter::Receive: (start - %l, stop - %l)\n",
  1167. rtStart, rtEnd));
  1168. // unlock to wait for a sample
  1169. LocalLock.Unlock();
  1170. HRESULT hr;
  1171. // get the buffer ptr
  1172. BYTE *pSrcBuffer = NULL;
  1173. // ignore error code, the pointer will be checked in the loop
  1174. pSample->GetPointer(&pSrcBuffer);
  1175. // determine the number of bytes to copy
  1176. LONG SrcDataSize = pSample->GetActualDataLength();
  1177. //
  1178. // allocate samples from the pool, copy the received sample data into
  1179. // the allocated sample buffer until all of the data has been copied
  1180. // NOTE: This works for both combining and splitting samples.
  1181. // * splitting: loop until distributed to enough samples
  1182. // * combining: stick it in the first sample; SetCompletionStatus
  1183. // queues it up again for next time
  1184. //
  1185. do
  1186. {
  1187. //
  1188. // Get a destination / user / outgoing sample from the pool.
  1189. // Wait for it if none is available right now.
  1190. // The obtained CSample has a ref count on it that must be released
  1191. // after we are done with it.
  1192. //
  1193. hr = AllocSampleFromPool(NULL, (CSample **)&pDestSample, 0);
  1194. BAIL_ON_FAILURE(hr);
  1195. //
  1196. // Copy the media sample into the destination sample and
  1197. // signal destination sample completion.
  1198. //
  1199. // CUserMediaSample::CopyFrom returns ERROR_MORE_DATA if there's more
  1200. // data that can fit into this user sample
  1201. // CUserMediaSample::SetCompletionStatus passes through the HRESULT
  1202. // value that's passed into it, unless it encounters an error of
  1203. // its own
  1204. //
  1205. LONG OldSrcDataSize = SrcDataSize;
  1206. hr = pDestSample->SetCompletionStatus(
  1207. pDestSample->CopyFrom(pSample, pSrcBuffer, SrcDataSize)
  1208. );
  1209. //
  1210. // Release the destination sample.
  1211. //
  1212. ((IStreamSample *)pDestSample)->Release();
  1213. }
  1214. while(ERROR_MORE_DATA == HRESULT_CODE(hr));
  1215. LOG((MSP_TRACE, "CMediaTerminalFilter::Receive(%p) succeeded", pSample));
  1216. return S_OK;
  1217. }
  1218. STDMETHODIMP
  1219. CMediaTerminalFilter::GetBuffer(
  1220. IMediaSample **ppBuffer,
  1221. REFERENCE_TIME * pStartTime,
  1222. REFERENCE_TIME * pEndTime,
  1223. DWORD dwFlags
  1224. )
  1225. {
  1226. LOG((MSP_TRACE, "CMediaTerminalFilter::GetBuffer(%p, %p, %p, %u) called",
  1227. ppBuffer, pStartTime, pEndTime, dwFlags));
  1228. if (NULL == ppBuffer) return E_POINTER;
  1229. #ifdef DBG
  1230. {
  1231. COM_LOCAL_CRIT_LOCK LocalLock(this);
  1232. TM_ASSERT(m_bUsingMyAllocator);
  1233. }
  1234. #endif // DBG
  1235. // no lock needed here as AllocSampleFromPool acquires lock.
  1236. // shouldn't hold lock at this point as the fn waits on a single
  1237. // event inside
  1238. *ppBuffer = NULL;
  1239. CUserMediaSample *pSample;
  1240. HRESULT hr = AllocSampleFromPool(NULL, (CSample **)&pSample, dwFlags);
  1241. BAIL_ON_FAILURE(hr);
  1242. // the sample has a refcnt on it. this will be released after we
  1243. // signal the user in FinalMediaSampleRelease
  1244. pSample->m_bReceived = false;
  1245. pSample->m_bModified = true;
  1246. *ppBuffer = (IMediaSample *)(pSample->m_pMediaSample);
  1247. (*ppBuffer)->AddRef();
  1248. LOG((MSP_TRACE, "CMediaTerminalFilter::GetBuffer(%p, %p, %p, %u) succeeded",
  1249. ppBuffer, pStartTime, pEndTime, dwFlags));
  1250. return hr;
  1251. }
  1252. STDMETHODIMP
  1253. CMediaTerminalFilter::SetProperties(
  1254. ALLOCATOR_PROPERTIES* pRequest,
  1255. ALLOCATOR_PROPERTIES* pActual
  1256. )
  1257. {
  1258. LOG((MSP_TRACE,
  1259. "CMediaTerminalFilter::SetProperties[%p] - enter. requested[%p] actual[%p]",
  1260. this, pRequest, pActual));
  1261. //
  1262. // check pRequest argument
  1263. //
  1264. if (IsBadReadPtr(pRequest, sizeof(ALLOCATOR_PROPERTIES)))
  1265. {
  1266. LOG((MSP_ERROR,
  1267. "CMediaTerminalFilter::SetProperties - bad requested [%p] passed in",
  1268. pRequest));
  1269. return E_POINTER;
  1270. }
  1271. //
  1272. // if the caller passed us a non-NULL pointer for allocator properties for us to
  1273. // return, it'd better be good.
  1274. //
  1275. if ( (NULL != pActual) && IsBadWritePtr(pActual, sizeof(ALLOCATOR_PROPERTIES)))
  1276. {
  1277. LOG((MSP_ERROR,
  1278. "CMediaTerminalFilter::SetProperties - bad actual [%p] passed in",
  1279. pRequest));
  1280. return E_POINTER;
  1281. }
  1282. // this critical section is needed for the allocator
  1283. AUTO_CRIT_LOCK;
  1284. if (m_bCommitted)
  1285. {
  1286. LOG((MSP_WARN,
  1287. "CMediaTerminalFilter::SetProperties - already commited"));
  1288. return VFW_E_ALREADY_COMMITTED;
  1289. }
  1290. if (!CUserMediaSample::VerifyAllocatorProperties(
  1291. m_bAllocateBuffers,
  1292. *pRequest
  1293. ))
  1294. {
  1295. LOG((MSP_ERROR,
  1296. "CMediaTerminalFilter::SetProperties - requested properties failed verification"));
  1297. return E_FAIL;
  1298. }
  1299. DUMP_ALLOC_PROPS("CMediaTerminalFilter::SetProperties. Requested:", pRequest);
  1300. //
  1301. // if the app set alloc properties by calling SetAllocatorProperties, we
  1302. // can only use those properties, and no others
  1303. //
  1304. if (m_bUserAllocProps)
  1305. {
  1306. //
  1307. // properties were already set
  1308. //
  1309. LOG((MSP_TRACE,
  1310. "CMediaTerminalFilter::SetProperties - properties were configured through SetAllocatorProperties"));
  1311. }
  1312. else
  1313. {
  1314. //
  1315. // no one has asked us for specific properties before, so
  1316. // we can accept the properties that we are given now.
  1317. //
  1318. LOG((MSP_TRACE,
  1319. "CMediaTerminalFilter::SetProperties - accepting requested properties"));
  1320. m_AllocProps = *pRequest;
  1321. }
  1322. //
  1323. // tell the caller what properties we can actually provide
  1324. //
  1325. if (NULL != pActual)
  1326. {
  1327. *pActual = m_AllocProps;
  1328. }
  1329. DUMP_ALLOC_PROPS("CMediaTerminalFilter::SetProperties - ours:", &m_AllocProps);
  1330. LOG((MSP_TRACE, "CMediaTerminalFilter::SetProperties - succeeded"));
  1331. return S_OK;
  1332. }
  1333. STDMETHODIMP
  1334. CMediaTerminalFilter::GetProperties(
  1335. ALLOCATOR_PROPERTIES* pProps
  1336. )
  1337. {
  1338. LOG((MSP_TRACE, "CMediaTerminalFilter::GetProperties(%p) called",
  1339. pProps));
  1340. BAIL_IF_NULL(pProps, E_POINTER);
  1341. // this critical section is required for the allocator
  1342. AUTO_CRIT_LOCK;
  1343. *pProps = m_AllocProps;
  1344. DUMP_ALLOC_PROPS("CMediaTerminalFilter::GetProperties - our properties:", pProps);
  1345. LOG((MSP_TRACE, "CMediaTerminalFilter::GetProperties - succeeded"));
  1346. return NOERROR;
  1347. }
  1348. // the thread pump calls the filter back during the registration
  1349. // to tell it that registration succeeded and that the pump will be
  1350. // waiting on the m_hWaitFreeSem handle
  1351. HRESULT
  1352. CMediaTerminalFilter::SignalRegisteredAtPump(
  1353. )
  1354. {
  1355. LOG((MSP_TRACE,
  1356. "CMediaTerminalFilter::SignalRegisteredAtPump[%p] - started",
  1357. this));
  1358. AUTO_CRIT_LOCK;
  1359. TM_ASSERT(PINDIR_OUTPUT == m_Direction);
  1360. // check if not committed
  1361. if (!m_bCommitted)
  1362. {
  1363. // if we missed a decommit in between,
  1364. // signal the thread that we have been decommited
  1365. ReleaseSemaphore(m_hWaitFreeSem, 1, 0);
  1366. return VFW_E_NOT_COMMITTED;
  1367. }
  1368. TM_ASSERT(0 == m_lWaiting);
  1369. m_lWaiting = 1;
  1370. LOG((MSP_TRACE, "CMediaTerminalFilter::SignalRegisteredAtPump - completed, m_lWaiting = 1"));
  1371. return S_OK;
  1372. }
  1373. //
  1374. // Override Commit to provide the number of buffers promised in negotiation
  1375. // as well as to register with the media pump thread.
  1376. // no need to call the adapted base class Commit
  1377. STDMETHODIMP CMediaTerminalFilter::Commit()
  1378. {
  1379. LOG((MSP_TRACE, "CMediaTerminalFilter[0x%p]::Commit - entered", this));
  1380. HRESULT hr = E_FAIL;
  1381. COM_LOCAL_CRIT_LOCK LocalLock(this);
  1382. if (m_bCommitted) return S_OK;
  1383. m_bCommitted = true;
  1384. // Now actually allocate whatever number of buffers we've promised.
  1385. // We need to do this only if we are a WRITE terminal, the read
  1386. // terminal doesn't expose an allocator and thus hasn't promised any
  1387. // samples
  1388. // NOTE: the direction only changes in Init so we can safely access
  1389. // m_Direction without the lock
  1390. if (PINDIR_OUTPUT == m_Direction)
  1391. {
  1392. LOG((MSP_TRACE, "CMediaTerminalFilter::Commit pindir_output"));
  1393. //
  1394. // If we are a WRITE MST but we are using a downstream allocator,
  1395. // then we do not use the NBQueue; instead we copy into the
  1396. // downstream allocator's samples.
  1397. //
  1398. if ( m_bUsingMyAllocator )
  1399. {
  1400. LOG((MSP_TRACE, "CMediaTerminalFilter::Commit using myallocator"));
  1401. //
  1402. // initialize sample queue
  1403. //
  1404. BOOL bQueueInitialized = m_SampleQueue.InitializeQ(m_AllocProps.cBuffers);
  1405. if ( ! bQueueInitialized )
  1406. {
  1407. LOG((MSP_ERROR,
  1408. "CMediaTerminalFilter::Commit - failed to initialize sample queue."));
  1409. return Decommit();
  1410. }
  1411. //
  1412. // allocate samples and put them into the queue
  1413. //
  1414. for (LONG i = 0; i < m_AllocProps.cBuffers; i++)
  1415. {
  1416. //
  1417. // create a sample
  1418. //
  1419. CComObject<CQueueMediaSample> *pQueueSample = NULL;
  1420. hr = CComObject<CQueueMediaSample>::CreateInstance(&pQueueSample);
  1421. if (FAILED(hr))
  1422. {
  1423. LOG((MSP_ERROR,
  1424. "CMediaTerminalFilter::Commit - failed to create queue sample"));
  1425. return Decommit();
  1426. }
  1427. //
  1428. // initialize the sample
  1429. //
  1430. hr = pQueueSample->Init(*this, m_SampleQueue);
  1431. if (FAILED(hr))
  1432. {
  1433. LOG((MSP_ERROR,
  1434. "CMediaTerminalFilter::Commit - failed to initialize queue sample"));
  1435. //
  1436. // failed to initializ. cleanup.
  1437. //
  1438. delete pQueueSample;
  1439. return Decommit();
  1440. }
  1441. //
  1442. // put sample into the queue
  1443. //
  1444. BOOL QnQSuccess = m_SampleQueue.EnQueue(pQueueSample);
  1445. if ( ! QnQSuccess )
  1446. {
  1447. LOG((MSP_ERROR,
  1448. "CMediaTerminalFilter::Commit - failed to enqueue queue sample"));
  1449. //
  1450. // failed to put sample into the queue. cleanup.
  1451. //
  1452. delete pQueueSample;
  1453. return Decommit();
  1454. } // failed to enqueue sample
  1455. } // for ( allocate and enqueue samples )
  1456. } // if m_bUsingMyAllocator
  1457. // register this write filter with the thread pump.
  1458. // we have to release our own lock - scenario - if we had a prior
  1459. // registration which was decommitted, but the thread pump didn't
  1460. // remove our entry (to be done when it waits on the wait event), the
  1461. // thread could be trying to get the filter lock while holding its own
  1462. // we should not try to get the pump lock while holding our own
  1463. HANDLE hWaitEvent = m_hWaitFreeSem;
  1464. LocalLock.Unlock();
  1465. hr = ms_MediaPumpPool.Register(this, hWaitEvent);
  1466. if ( HRESULT_FAILURE(hr) )
  1467. {
  1468. return Decommit();
  1469. }
  1470. }
  1471. LOG((MSP_TRACE, "CMediaTerminalFilter::Commit - completed"));
  1472. return S_OK;
  1473. }
  1474. STDMETHODIMP CMediaTerminalFilter::ProcessSample(IMediaSample *pSample)
  1475. {
  1476. LOG((MSP_TRACE, "CMediaTerminalFilter[%p]::ProcessSample - entered", this));
  1477. Lock();
  1478. //
  1479. // in a lock, get a reference to the connected pin.
  1480. //
  1481. IMemInputPin *pConnectedPin = m_pConnectedMemInputPin;
  1482. //
  1483. // not connected? do nothing
  1484. //
  1485. if ( NULL == pConnectedPin )
  1486. {
  1487. Unlock();
  1488. LOG((MSP_TRACE,
  1489. "CMediaTerminalFilter::ProcessSample - not connected. dropping sample. "
  1490. "VFW_E_NOT_CONNECTED"));
  1491. return VFW_E_NOT_CONNECTED;
  1492. }
  1493. //
  1494. // addref so we can safely use this outside the lock
  1495. //
  1496. pConnectedPin->AddRef();
  1497. //
  1498. // receive may take a while, do not call inside a lock...
  1499. //
  1500. Unlock();
  1501. //
  1502. // pass the sample along to the connected pin
  1503. //
  1504. HRESULT hr = pConnectedPin->Receive(pSample);
  1505. //
  1506. // done, release the outstanding reference we requested.
  1507. //
  1508. pConnectedPin->Release();
  1509. pConnectedPin = NULL;
  1510. LOG((MSP_(hr), "CMediaTerminalFilter::ProcessSample - finish. hr = %lx", hr));
  1511. return hr;
  1512. }
  1513. // release any waiting threads, dump the samples we allocated,
  1514. // abort the sample being fragmented and all the samples in the pool
  1515. STDMETHODIMP CMediaTerminalFilter::Decommit()
  1516. {
  1517. LOG((MSP_TRACE, "CMediaTerminalFilter[%p]::Decommit - entered", this));
  1518. Lock();
  1519. // if not committed, do nothing
  1520. if ( !m_bCommitted )
  1521. {
  1522. Unlock();
  1523. return S_OK;
  1524. }
  1525. m_bCommitted = false;
  1526. if ( m_lWaiting > 0 )
  1527. {
  1528. LOG((MSP_TRACE,
  1529. "CMediaTerminalFilter::Decommit - releasing m_hWaitFreeSem by %ld",
  1530. m_lWaiting));
  1531. ReleaseSemaphore(m_hWaitFreeSem, m_lWaiting, 0);
  1532. m_lWaiting = 0;
  1533. }
  1534. //
  1535. // unlock while calling into Unregister, to avoid a deadlock on trying to lock the pump
  1536. // while another thread has pump locked in CMediaPump::ServiceFilter while waiting to get
  1537. // to filter's lock which we are holding
  1538. //
  1539. Unlock();
  1540. //
  1541. // unregister filter from media pump
  1542. //
  1543. ms_MediaPumpPool.UnRegister(m_hWaitFreeSem);
  1544. //
  1545. // lock the object again
  1546. //
  1547. Lock();
  1548. //
  1549. // If we are a write filter and we are using our own allocator, then
  1550. // we have internal samples to destroy.
  1551. // The read filter doesn't maintain a queue.
  1552. //
  1553. if ( ( PINDIR_OUTPUT == m_Direction ) && m_bUsingMyAllocator )
  1554. {
  1555. // don't wait for the samples, return if there are no more
  1556. CQueueMediaSample *pSamp = NULL;
  1557. while ((pSamp = m_SampleQueue.DeQueue(FALSE)) != NULL)
  1558. {
  1559. delete pSamp;
  1560. }
  1561. m_SampleQueue.ShutdownQ();
  1562. }
  1563. if (NULL != m_pSampleBeingFragmented)
  1564. {
  1565. // abort the sample when the last refcnt to its internal
  1566. // IMediaStream is released
  1567. m_pSampleBeingFragmented->AbortDuringFragmentation();
  1568. ((IStreamSample *)m_pSampleBeingFragmented)->Release();
  1569. m_pSampleBeingFragmented = NULL;
  1570. }
  1571. // all the threads waiting for a CStream pool buffer
  1572. // have been woken up by now
  1573. // NOTE: no more samples can be added to the CStream pool
  1574. // from now as we have decommitted (so no race conditions
  1575. // possible if other threads are trying to add buffers to it)
  1576. // go through the CStream sample q and for each sample
  1577. // remove sample, unlock, abort the sample
  1578. CSample *pSample = m_pFirstFree;
  1579. while (NULL != pSample)
  1580. {
  1581. // remove sample from q
  1582. m_pFirstFree = pSample->m_pNextFree;
  1583. if (NULL != m_pFirstFree) m_pFirstFree->m_pPrevFree = NULL;
  1584. else m_pLastFree = NULL;
  1585. pSample->m_pNextFree = NULL;
  1586. TM_ASSERT(pSample->m_Status == MS_S_PENDING);
  1587. CHECKSAMPLELIST
  1588. // unlock so that we don't have a deadlock due to the sample
  1589. // trying to access the stream
  1590. Unlock();
  1591. // we know that the sample must be alive now because
  1592. // we have a reference to it
  1593. // abort the sample, ignore error code (it'll return E_ABORT)
  1594. pSample->SetCompletionStatus(E_ABORT);
  1595. pSample->Release();
  1596. // obtain lock
  1597. Lock();
  1598. // reset pSample to the top of the q
  1599. pSample = m_pFirstFree;
  1600. }
  1601. Unlock();
  1602. LOG((MSP_TRACE, "CMediaTerminalFilter::Decommit - finish"));
  1603. // at this point, we hold a lock
  1604. // return the result of PumpOverrideDecommit
  1605. return S_OK;
  1606. }
  1607. // try to follow the cstream code when possible for Connect
  1608. // the cstream implementation calls ReceiveConnection on self! hence need
  1609. // to over-ride it
  1610. // use the pmt parameter or the suggested media type if not null?, else
  1611. // enumerate the input pin's media types and save a ptr to the first
  1612. // media type (to be written into the m_ConnectedMediaType, m_ActualMediaType
  1613. // on success). we accept any media type and we want to use our own allocator
  1614. // so, NotifyAllocator() and on success, set the allocator and copy the
  1615. // media type
  1616. STDMETHODIMP
  1617. CMediaTerminalFilter::Connect(
  1618. IPin * pReceivePin,
  1619. const AM_MEDIA_TYPE *pmt
  1620. )
  1621. {
  1622. AUTO_CRIT_LOCK;
  1623. LOG((MSP_TRACE, "CMediaTerminalFilter::Connect(%p, %p) called",
  1624. pReceivePin, pmt));
  1625. // if there is a suggested media type, we can't accept any
  1626. // suggested media type that is not same as it
  1627. if ( (NULL != pmt) &&
  1628. (NULL != m_pSuggestedMediaType) &&
  1629. (!IsSameAMMediaType(pmt, m_pSuggestedMediaType)) )
  1630. {
  1631. return VFW_E_TYPE_NOT_ACCEPTED;
  1632. }
  1633. // get IMemInputPin i/f on the pReceivePin
  1634. CComQIPtr<IMemInputPin, &IID_IMemInputPin>
  1635. pConnectedMemInputPin = pReceivePin;
  1636. BAIL_IF_NULL(pConnectedMemInputPin, VFW_E_TYPE_NOT_ACCEPTED);
  1637. HRESULT hr;
  1638. const AM_MEDIA_TYPE *pMediaType;
  1639. // check if the passed in media type is non-null
  1640. if (NULL != pmt)
  1641. {
  1642. // we have already checked it to be same as the suggested
  1643. // media type, so no more checks are required
  1644. pMediaType = pmt;
  1645. }
  1646. else if (NULL != m_pSuggestedMediaType) // try the suggested terminal media type
  1647. {
  1648. // we verified that the suggested media type was acceptable in put_MediaType
  1649. pMediaType = m_pSuggestedMediaType;
  1650. }
  1651. else // NOTE: we still try if the passed in media type (non-null) is not acceptable
  1652. {
  1653. // else, enumerate the input pin for media types and call QueryAccept for each to
  1654. // check if the media type is acceptable
  1655. // get the enumerator
  1656. IEnumMediaTypes *pEnum;
  1657. hr = EnumMediaTypes(&pEnum);
  1658. BAIL_ON_FAILURE(hr);
  1659. TM_ASSERT(NULL != pEnum);
  1660. // for each media type, call QueryAccept. we are looking for the first acceptable media type
  1661. DWORD NumObtained;
  1662. while (S_OK == (hr = pEnum->Next(1, (AM_MEDIA_TYPE **)&pMediaType, &NumObtained)))
  1663. {
  1664. hr = QueryAccept(pMediaType);
  1665. if (HRESULT_FAILURE(hr))
  1666. {
  1667. break;
  1668. }
  1669. }
  1670. BAIL_ON_FAILURE(hr);
  1671. // found an acceptable media type
  1672. }
  1673. if (NULL == pMediaType->pbFormat)
  1674. {
  1675. return VFW_E_INVALIDMEDIATYPE;
  1676. }
  1677. // call the input pin with the media type
  1678. hr = pReceivePin->ReceiveConnection(this, pMediaType);
  1679. BAIL_ON_FAILURE(hr);
  1680. //////////////////////////////////////////////////////////////////////////
  1681. //
  1682. // Formats negotiated. Now deal with allocator properties.
  1683. //
  1684. // if ( other pin has allocator requirements: )
  1685. // if they exist and are usable then we must use them, overriding our
  1686. // own wishes. If they exist and are unusable then we fail to connect.
  1687. // if we use these, we do m_AllocProps = <required props>
  1688. //
  1689. //
  1690. // The rest of the logic is not done here, but rather in the two
  1691. // methods: SuggestAllocatorProperties (msp method) and
  1692. // SetAllocatorProperties (user method). The net effect is:
  1693. //
  1694. // else if ( m_bSuggestedAllocProps == TRUE )
  1695. // MSP has set properties that they want us to use on the graph. If
  1696. // this is true then we try to use these properties.
  1697. // these are in m_AllocProps
  1698. // else if ( m_bUserAllocProps == TRUE )
  1699. // user has properties they want us to convert to. if the suggested
  1700. // (MSP) setting hasn't happened, it'll be most efficient if we use
  1701. // these. these are in both m_AllocProps and m_UserAllocProps so that
  1702. // if the MSP un-suggests then we can go back to the user's settings
  1703. // else
  1704. // just use m_AllocProps (these are set to default values on creation)
  1705. //
  1706. // in any of these cases we just use m_AllocProps as it already is filled
  1707. // in with the correct values.
  1708. //
  1709. //////////////////////////////////////////////////////////////////////////
  1710. //
  1711. // So first of all, try to get the other pin's requirements.
  1712. //
  1713. ALLOCATOR_PROPERTIES DownstreamPreferred;
  1714. //
  1715. // downstream pin hints us of its preferences. we don't have to use them
  1716. hr = pConnectedMemInputPin->GetAllocatorRequirements(&DownstreamPreferred);
  1717. if ( ( hr != S_OK ) && ( hr != E_NOTIMPL ) )
  1718. {
  1719. // strange failure -- something's very wrong
  1720. Disconnect();
  1721. pReceivePin->Disconnect();
  1722. return hr;
  1723. }
  1724. else if ( hr == S_OK )
  1725. {
  1726. //
  1727. // This means that the downstream filter has allocator requirements.
  1728. //
  1729. if (!CUserMediaSample::VerifyAllocatorProperties(
  1730. m_bAllocateBuffers,
  1731. DownstreamPreferred
  1732. ))
  1733. {
  1734. Disconnect();
  1735. pReceivePin->Disconnect();
  1736. return E_FAIL;
  1737. }
  1738. DUMP_ALLOC_PROPS("CMediaTerminalFilter::Connect - downstream preferences:",
  1739. &DownstreamPreferred);
  1740. //
  1741. // see if the application asked for or suggested specific allocator properties
  1742. //
  1743. if (m_bUserAllocProps || m_bSuggestedAllocProps )
  1744. {
  1745. LOG((MSP_WARN,
  1746. "CMediaTerminalFilter::Connect "
  1747. "- connected pin wants allocator props different from set or suggested"));
  1748. }
  1749. else
  1750. {
  1751. //
  1752. // accept allocator properties asked by the downstream pin
  1753. //
  1754. m_AllocProps = DownstreamPreferred;
  1755. }
  1756. }
  1757. DUMP_ALLOC_PROPS("CMediaTerminalFilter::Connect - properties to use:", &m_AllocProps);
  1758. //
  1759. // At this point, we know what allocator properties we are going to
  1760. // use -- it's in m_AllocProps.
  1761. //
  1762. // Next we determine if the downstream filter has an allocator that
  1763. // we can use.
  1764. //
  1765. IMemAllocator * pAllocatorToUse = NULL;
  1766. //
  1767. // Don't bother using a downstream allocator if the buffers are less than
  1768. // a minimum size.
  1769. //
  1770. if ( m_AllocProps.cbBuffer < 2000 )
  1771. {
  1772. LOG((MSP_TRACE, "CMediaTerminalFilter::Connect - "
  1773. "small buffers - using our allocator"));
  1774. hr = E_FAIL; // don't use downstream allocator
  1775. }
  1776. else
  1777. {
  1778. hr = pConnectedMemInputPin->GetAllocator( & pAllocatorToUse );
  1779. if ( SUCCEEDED(hr) )
  1780. {
  1781. LOG((MSP_TRACE, "CMediaTerminalFilter::Connect - "
  1782. "downstream filter has an allocator"));
  1783. //
  1784. // The input pin on the downstream filter has its own allocator.
  1785. // See if it will accept our allocator properties.
  1786. //
  1787. ALLOCATOR_PROPERTIES ActualAllocProps;
  1788. hr = pAllocatorToUse->SetProperties( & m_AllocProps, & ActualAllocProps );
  1789. if ( FAILED(hr) )
  1790. {
  1791. LOG((MSP_TRACE, "CMediaTerminalFilter::Connect - "
  1792. "downstream allocator did not allow us to SetProperties - "
  1793. "0x%08x", hr));
  1794. pAllocatorToUse->Release();
  1795. pAllocatorToUse = NULL;
  1796. }
  1797. else if ( AllocatorPropertiesDifferSignificantly( & ActualAllocProps,
  1798. & m_AllocProps ) )
  1799. {
  1800. LOG((MSP_TRACE, "CMediaTerminalFilter::Connect - "
  1801. "downstream allocator did allow us to SetProperties "
  1802. "but it changed the properties rather than accepting them"));
  1803. // It succeeded but it modified the allocator properties
  1804. // we gave it. To be safe, we use our own allocator instead.
  1805. //
  1806. // Note: The waveout filter enforces minimum sizes when its
  1807. // allocator is used. This means that we only use the waveout
  1808. // filter's allocator when our buffer size is sufficiently large
  1809. // that the waveout filter doesn't tamper with them.
  1810. hr = E_FAIL;
  1811. pAllocatorToUse->Release();
  1812. pAllocatorToUse = NULL;
  1813. }
  1814. else
  1815. {
  1816. LOG((MSP_TRACE, "CMediaTerminalFilter::Connect - "
  1817. "downstream allocator accepted our allocator properties"));
  1818. }
  1819. } // if downstream filter has an allocator
  1820. } // if large buffers
  1821. //
  1822. // At this point we have hr (success or failure) and a reference to the
  1823. // downstream filter's alloactor if hr is success.
  1824. //
  1825. // If hr is success, then the downstream filter has an allocator that we can
  1826. // use, and pAllocatorToUse points to that allocator. If hr is failure, then
  1827. // we use our own allocator.
  1828. //
  1829. if ( FAILED(hr) )
  1830. {
  1831. LOG((MSP_TRACE, "CMediaTerminalFilter::Connect - "
  1832. "using our own allocator"));
  1833. pAllocatorToUse = this;
  1834. pAllocatorToUse->AddRef(); // to match Release below
  1835. m_bUsingMyAllocator = true;
  1836. }
  1837. else
  1838. {
  1839. LOG((MSP_TRACE, "CMediaTerminalFilter::Connect - "
  1840. "using downstream allocator"));
  1841. m_bUsingMyAllocator = false;
  1842. }
  1843. //
  1844. // Notify the downstream input pin of the allocator we decided to use.
  1845. //
  1846. hr = pConnectedMemInputPin->NotifyAllocator(
  1847. pAllocatorToUse,
  1848. m_bUsingMyAllocator // if user's buffers, then read-only
  1849. );
  1850. if ( FAILED(hr) )
  1851. {
  1852. LOG((MSP_ERROR, "CMediaTerminalFilter::Connect - "
  1853. "downstream filter rejected our allocator choice - exit 0x%08x", hr));
  1854. pAllocatorToUse->Release();
  1855. Disconnect();
  1856. pReceivePin->Disconnect();
  1857. return hr;
  1858. }
  1859. //
  1860. // Connection has succeeded.
  1861. // Make sure we let ourselves know that this is our allocator!
  1862. // NOTE: m_pAllocator is a CComPtr!!! It holds a reference to
  1863. // the object after the assignment, so we need to release our
  1864. // local reference now.
  1865. //
  1866. m_pAllocator = pAllocatorToUse;
  1867. pAllocatorToUse->Release();
  1868. // copy the media type into m_ConnectedMediaType
  1869. CopyMediaType(&m_ConnectedMediaType, pMediaType);
  1870. // member ptr to IMemInputPin i/f of the input pin
  1871. m_pConnectedMemInputPin = pConnectedMemInputPin;
  1872. // save a pointer to the connected pin
  1873. m_pConnectedPin = pReceivePin;
  1874. // get time to delay samples
  1875. GetTimingInfo(m_ConnectedMediaType);
  1876. LOG((MSP_TRACE, "CMediaTerminalFilter::Connect(%p, %p) succeeded",
  1877. pReceivePin, pmt));
  1878. return hr;
  1879. }
  1880. // Return E_NOTIMPL to indicate that we have no requirements,
  1881. // since even if the user has specified properties, we now
  1882. // accept connections with other propertoes.
  1883. STDMETHODIMP CMediaTerminalFilter::GetAllocatorRequirements(
  1884. OUT ALLOCATOR_PROPERTIES *pProps
  1885. )
  1886. {
  1887. LOG((MSP_TRACE,
  1888. "CMediaTerminalFilter::GetAllocatorRequirements[%p] - enter", this));
  1889. //
  1890. // make sure some filter did not pass us a bad pointer
  1891. //
  1892. if (IsBadWritePtr(pProps, sizeof(ALLOCATOR_PROPERTIES)))
  1893. {
  1894. LOG((MSP_ERROR,
  1895. "CMediaTerminalFilter::GetAllocatorRequirements - bad pointer [%p]", pProps));
  1896. return E_POINTER;
  1897. }
  1898. AUTO_CRIT_LOCK;
  1899. //
  1900. // were allocator properties set or suggested?
  1901. //
  1902. // fail if not -- this will indicate that we don't have a preference for
  1903. // specific allocator properties
  1904. //
  1905. if ( !m_bUserAllocProps && !m_bSuggestedAllocProps )
  1906. {
  1907. LOG((MSP_TRACE,
  1908. "CMediaTerminalFilter::GetAllocatorRequirements - allocator properties were not set."));
  1909. //
  1910. // E_NOTIMPL is the base class' way to show that we don't care about allocator properties.
  1911. // return E_NOTIMPL so we don't break callers that depend on this error as a sign that
  1912. // allocator properties make not difference to us.
  1913. //
  1914. return E_NOTIMPL;
  1915. }
  1916. //
  1917. // allocator properties were set -- return them.
  1918. //
  1919. *pProps = m_AllocProps;
  1920. DUMP_ALLOC_PROPS("CMediaTerminalFilter::GetAllocatorRequirements - ours:",
  1921. pProps);
  1922. LOG((MSP_TRACE,
  1923. "CMediaTerminalFilter::GetAllocatorRequirements - exit. "
  1924. "returning previously set allocator properties."));
  1925. return S_OK;
  1926. }
  1927. // we accept any media type
  1928. // call CheckReceiveConnectionPin to verify the pin and if successful
  1929. // copy media type and save the connector in m_pConnectedPin
  1930. STDMETHODIMP
  1931. CMediaTerminalFilter::ReceiveConnection(
  1932. IPin * pConnector,
  1933. const AM_MEDIA_TYPE *pmt
  1934. )
  1935. {
  1936. LOG((MSP_TRACE, "CMediaTerminalFilter::ReceiveConnection(%p, %p) called",
  1937. pConnector, pmt));
  1938. // validate the passed-in media type pointer
  1939. BAIL_IF_NULL(pmt, E_POINTER);
  1940. BAIL_IF_NULL(pmt->pbFormat, VFW_E_INVALIDMEDIATYPE);
  1941. AUTO_CRIT_LOCK;
  1942. //
  1943. // This helper function in CStream checks basic parameters for the Pin such as
  1944. // the connecting pin's direction (we need to check this -- Sometimes the filter
  1945. // graph will try to connect us to ourselves!) and other errors like already being
  1946. // connected, etc.
  1947. //
  1948. HRESULT hr;
  1949. hr= CheckReceiveConnectionPin(pConnector);
  1950. BAIL_ON_FAILURE(hr);
  1951. // if there is a suggested media type, we can't accept any
  1952. // suggested media type that is not same as it
  1953. if ( (NULL != m_pSuggestedMediaType) &&
  1954. (!IsSameAMMediaType(pmt, m_pSuggestedMediaType)) )
  1955. {
  1956. return VFW_E_TYPE_NOT_ACCEPTED;
  1957. }
  1958. // copy media type and save the connector in m_pConnectedPin
  1959. CopyMediaType(&m_ConnectedMediaType, pmt);
  1960. m_pConnectedPin = pConnector;
  1961. // get time to delay samples
  1962. GetTimingInfo(m_ConnectedMediaType);
  1963. LOG((MSP_TRACE, "CMediaTerminalFilter::ReceiveConnection(%p, %p) succeeded",
  1964. pConnector, pmt));
  1965. return S_OK;
  1966. }
  1967. // the base class implementation doesn't validate the parameter
  1968. // validate the parameter and call base class
  1969. STDMETHODIMP
  1970. CMediaTerminalFilter::ConnectionMediaType(
  1971. AM_MEDIA_TYPE *pmt
  1972. )
  1973. {
  1974. AUTO_CRIT_LOCK;
  1975. LOG((MSP_TRACE, "CMediaTerminalFilter::ConnectionMediaType(%p) called", pmt));
  1976. BAIL_IF_NULL(pmt, E_POINTER);
  1977. return CStream::ConnectionMediaType(pmt);
  1978. }
  1979. // should accept all media types which match the major type corresponding to the purpose id
  1980. STDMETHODIMP
  1981. CMediaTerminalFilter::QueryAccept(
  1982. const AM_MEDIA_TYPE *pmt
  1983. )
  1984. {
  1985. AUTO_CRIT_LOCK;
  1986. LOG((MSP_TRACE, "CMediaTerminalFilter::QueryAccept(%p) called", pmt));
  1987. BAIL_IF_NULL(pmt, E_POINTER);
  1988. BAIL_IF_NULL(m_pAmovieMajorType, MS_E_NOTINIT);
  1989. // compare the filter major type with the queried AM_MEDIA_TYPE's major type
  1990. if (0 != memcmp(&pmt->majortype, m_pAmovieMajorType, sizeof(GUID)))
  1991. return S_FALSE;
  1992. // if read side, return S_OK as we accepts any format
  1993. // NOTE: FOR THE READ SIDE, QueryAccept is only called in SetFormat.
  1994. // In ReceiveConnect, it checks against any user set properties
  1995. // directly instead of calling QueryAccept
  1996. if (PINDIR_INPUT == m_Direction) return S_OK;
  1997. TM_ASSERT(NULL != pmt->pbFormat);
  1998. if (NULL == pmt->pbFormat)
  1999. {
  2000. LOG((MSP_ERROR, "CMediaTerminalFilter::QueryAccept(%p) - returning S_FALSE, \
  2001. pbFormat = NULL",
  2002. pmt));
  2003. return S_FALSE;
  2004. }
  2005. if (m_bIsAudio)
  2006. {
  2007. // if not the acceptable media type, return error
  2008. TM_ASSERT(FORMAT_WaveFormatEx == pmt->formattype);
  2009. if (FORMAT_WaveFormatEx != pmt->formattype)
  2010. {
  2011. LOG((MSP_ERROR, "CMediaTerminalFilter::QueryAccept(%p) - returning S_FALSE, \
  2012. audio format is not WaveFormatEx",
  2013. pmt));
  2014. return S_FALSE;
  2015. }
  2016. if (0 == ((WAVEFORMATEX *)pmt->pbFormat)->nAvgBytesPerSec)
  2017. {
  2018. LOG((MSP_ERROR, "CMediaTerminalFilter::QueryAccept(%p) - returning S_FALSE, \
  2019. nAvgBytesPerSec = 0",
  2020. pmt));
  2021. return S_FALSE;
  2022. }
  2023. }
  2024. else
  2025. {
  2026. TM_ASSERT(MSPID_PrimaryVideo == m_PurposeId);
  2027. TM_ASSERT(FORMAT_VideoInfo == pmt->formattype);
  2028. if (FORMAT_VideoInfo != pmt->formattype)
  2029. {
  2030. LOG((MSP_ERROR, "CMediaTerminalFilter::QueryAccept(%p) - returning S_FALSE, \
  2031. video format is not VideoInfo",
  2032. pmt));
  2033. return S_FALSE;
  2034. }
  2035. TM_ASSERT(0 != ((VIDEOINFO *)pmt->pbFormat)->AvgTimePerFrame);
  2036. if (0 == ((VIDEOINFO *)pmt->pbFormat)->AvgTimePerFrame)
  2037. {
  2038. LOG((MSP_ERROR, "CMediaTerminalFilter::QueryAccept(%p) - returning S_FALSE, \
  2039. AvgTimePerFrame = 0",
  2040. pmt));
  2041. return S_FALSE;
  2042. }
  2043. }
  2044. LOG((MSP_TRACE, "CMediaTerminalFilter::QueryAccept(%p) succeeded", pmt));
  2045. return S_OK;
  2046. }
  2047. // CStream doesn't reset the end of stream flag, so over-ride
  2048. STDMETHODIMP
  2049. CMediaTerminalFilter::Disconnect(
  2050. )
  2051. {
  2052. LOG((MSP_TRACE, "CMediaTerminalFilter::Disconnect[%p] - enter", this));
  2053. Lock();
  2054. m_bEndOfStream = false; // this is a bool value
  2055. //
  2056. // If the MSP suggested allocator properties, then those
  2057. // are never touched.
  2058. // If the user has provided the allocator properties, then
  2059. // reset modified allocator properties to the user's values.
  2060. // If the user hasn't provided the allocator properties
  2061. // reset modified allocator properties to default values.
  2062. //
  2063. if ( ! m_bSuggestedAllocProps )
  2064. {
  2065. if ( m_bUserAllocProps )
  2066. {
  2067. m_AllocProps = m_UserAllocProps;
  2068. }
  2069. else
  2070. {
  2071. SetDefaultAllocatorProperties();
  2072. }
  2073. }
  2074. HRESULT hr = CStream::Disconnect();
  2075. Unlock();
  2076. LOG((MSP_(hr), "CMediaTerminalFilter::Disconnect- finish. hr = %lx", hr));
  2077. return hr;
  2078. }
  2079. // if the ds queries us for our preferred media types
  2080. // - we return the user suggested media type (suggested in SetFormat) if
  2081. // there is one
  2082. HRESULT
  2083. CMediaTerminalFilter::GetMediaType(
  2084. ULONG Index,
  2085. AM_MEDIA_TYPE **ppMediaType
  2086. )
  2087. {
  2088. LOG((MSP_TRACE,
  2089. "CMediaTerminalFilter::GetMediaType(%u, %p) called",
  2090. Index, ppMediaType));
  2091. // we can have atmost one user suggested media type
  2092. if (0 != Index)
  2093. {
  2094. LOG((MSP_ERROR,
  2095. "CMediaTerminalFilter::GetMediaType(%u, %p) - invalid index,\
  2096. returning S_FALSE",
  2097. Index, ppMediaType));
  2098. return S_FALSE;
  2099. }
  2100. //
  2101. // ppMediaType must point to a pointer to AM_MEDIA_TYPE
  2102. //
  2103. if (TM_IsBadWritePtr(ppMediaType, sizeof(AM_MEDIA_TYPE *)))
  2104. {
  2105. LOG((MSP_ERROR,
  2106. "CMediaTerminalFilter::GetMediaType(%u, %p) - bad input pointer "
  2107. "returning E_POINTER", Index, ppMediaType));
  2108. return E_POINTER;
  2109. }
  2110. AUTO_CRIT_LOCK;
  2111. // if no user suggested media type, return error
  2112. if (NULL == m_pSuggestedMediaType)
  2113. {
  2114. LOG((MSP_ERROR,
  2115. "CMediaTerminalFilter::GetMediaType(%u, %p) - \
  2116. no suggested media type, returning S_FALSE",
  2117. Index, ppMediaType));
  2118. return S_FALSE;
  2119. }
  2120. // copy the user suggested media type into the passed in ppMediaType
  2121. // create media type if necessary
  2122. TM_ASSERT(NULL != m_pSuggestedMediaType);
  2123. // creates an am media type initialized with the pmt value
  2124. *ppMediaType = CreateMediaType(m_pSuggestedMediaType);
  2125. BAIL_IF_NULL(*ppMediaType, E_OUTOFMEMORY);
  2126. LOG((MSP_TRACE,
  2127. "CMediaTerminalFilter::GetMediaType(%u, %p) succeeded",
  2128. Index, ppMediaType));
  2129. return S_OK;
  2130. }
  2131. HRESULT
  2132. CMediaTerminalFilter::AddToPoolIfCommitted(
  2133. IN CSample *pSample
  2134. )
  2135. {
  2136. TM_ASSERT(NULL != pSample);
  2137. AUTO_CRIT_LOCK;
  2138. // check if committed
  2139. if (!m_bCommitted) return VFW_E_NOT_COMMITTED;
  2140. // addref the sample and
  2141. // call AddSampleToFreePool
  2142. pSample->AddRef();
  2143. AddSampleToFreePool(pSample);
  2144. // we must have atleast one item in our queue
  2145. TM_ASSERT(m_pFirstFree != NULL);
  2146. return S_OK;
  2147. }
  2148. // first check if this sample is the one being fragmented currently, then
  2149. // check the free pool
  2150. BOOL
  2151. CMediaTerminalFilter::StealSample(
  2152. IN CSample *pSample
  2153. )
  2154. {
  2155. LOG((MSP_TRACE, "CMediaTerminalFilter::StealSample(%p) called",
  2156. pSample));
  2157. BOOL bWorked = FALSE;
  2158. AUTO_CRIT_LOCK;
  2159. // if not committed, do nothing
  2160. if ( !m_bCommitted )
  2161. {
  2162. LOG((MSP_TRACE, "CMediaTerminalFilter::StealSample(%p) \
  2163. not committed - can't find sample", pSample));
  2164. return FALSE;
  2165. }
  2166. if (pSample == m_pSampleBeingFragmented)
  2167. {
  2168. // abort the sample when the last refcnt to its internal
  2169. // IMediaStream is released
  2170. m_pSampleBeingFragmented->AbortDuringFragmentation();
  2171. ((IStreamSample *)m_pSampleBeingFragmented)->Release();
  2172. m_pSampleBeingFragmented = NULL;
  2173. LOG((MSP_TRACE, "CMediaTerminalFilter::StealSample(%p) \
  2174. was being fragmented - aborting", pSample));
  2175. // the caller wants to abort this sample immediately. since
  2176. // we must wait for the last refcnt on IMediaStream to be released
  2177. // tell the caller that the sample was not found
  2178. return FALSE;
  2179. }
  2180. if (m_pFirstFree)
  2181. {
  2182. if (m_pFirstFree == pSample)
  2183. {
  2184. m_pFirstFree = pSample->m_pNextFree;
  2185. if (m_pFirstFree) m_pFirstFree->m_pPrevFree = NULL;
  2186. else m_pLastFree = NULL;
  2187. pSample->m_pNextFree = NULL; // We know the prev ptr is already null!
  2188. TM_ASSERT(pSample->m_pPrevFree == NULL);
  2189. bWorked = TRUE;
  2190. }
  2191. else
  2192. {
  2193. if (pSample->m_pPrevFree)
  2194. {
  2195. pSample->m_pPrevFree->m_pNextFree = pSample->m_pNextFree;
  2196. if (pSample->m_pNextFree)
  2197. pSample->m_pNextFree->m_pPrevFree = pSample->m_pPrevFree;
  2198. else
  2199. m_pLastFree = pSample->m_pPrevFree;
  2200. pSample->m_pNextFree = pSample->m_pPrevFree = NULL;
  2201. bWorked = TRUE;
  2202. }
  2203. }
  2204. CHECKSAMPLELIST
  2205. }
  2206. LOG((MSP_TRACE, "CMediaTerminalFilter::StealSample(%p) returns %d",
  2207. pSample, bWorked));
  2208. return bWorked;
  2209. }
  2210. // sets the time to delay - per byte for audio, per frame for video
  2211. void
  2212. CMediaTerminalFilter::GetTimingInfo(
  2213. IN const AM_MEDIA_TYPE &MediaType
  2214. )
  2215. {
  2216. AUTO_CRIT_LOCK;
  2217. if (m_bIsAudio)
  2218. {
  2219. // assert if not an audio format
  2220. TM_ASSERT(FORMAT_WaveFormatEx == MediaType.formattype);
  2221. TM_ASSERT(NULL != MediaType.pbFormat);
  2222. // number of milliseconds to delay per byte
  2223. m_AudioDelayPerByte =
  2224. DOUBLE(1000)/((WAVEFORMATEX *)MediaType.pbFormat)->nAvgBytesPerSec;
  2225. }
  2226. else
  2227. {
  2228. TM_ASSERT(MSPID_PrimaryVideo == m_PurposeId);
  2229. TM_ASSERT(FORMAT_VideoInfo == MediaType.formattype);
  2230. // number of milliseconds to delay per frame
  2231. // AvgTimePerFrame is in 100ns units - convert to milliseconds
  2232. m_VideoDelayPerFrame =
  2233. DWORD(10000*((VIDEOINFO *)MediaType.pbFormat)->AvgTimePerFrame);
  2234. }
  2235. }
  2236. void
  2237. CMediaTerminalFilter::SetDefaultAllocatorProperties(
  2238. )
  2239. {
  2240. m_AllocProps.cbBuffer = DEFAULT_AM_MST_SAMPLE_SIZE;
  2241. m_AllocProps.cBuffers = DEFAULT_AM_MST_NUM_BUFFERS;
  2242. m_AllocProps.cbAlign = DEFAULT_AM_MST_BUFFER_ALIGNMENT;
  2243. m_AllocProps.cbPrefix = DEFAULT_AM_MST_BUFFER_PREFIX;
  2244. }
  2245. // CTMStreamSample
  2246. // calls InitSample(pStream, bIsInternalSample)
  2247. // sets member variables
  2248. HRESULT
  2249. CTMStreamSample::Init(
  2250. CStream &Stream,
  2251. bool bIsInternalSample,
  2252. PBYTE pBuffer,
  2253. LONG BufferSize
  2254. )
  2255. {
  2256. LOG((MSP_TRACE, "CTMStreamSample::Init(&%p, %d, %p, %d) called",
  2257. &Stream, bIsInternalSample, pBuffer, BufferSize));
  2258. TM_ASSERT(NULL == m_pBuffer);
  2259. HRESULT hr;
  2260. hr = InitSample(&Stream, bIsInternalSample);
  2261. BAIL_ON_FAILURE(hr);
  2262. m_BufferSize = BufferSize;
  2263. m_pBuffer = pBuffer;
  2264. LOG((MSP_TRACE, "CTMStreamSample::Init(&%p, %d, %p, %d) succeeded",
  2265. &Stream, bIsInternalSample, pBuffer, BufferSize));
  2266. return S_OK;
  2267. }
  2268. void
  2269. CTMStreamSample::CopyFrom(
  2270. IMediaSample *pSrcMediaSample
  2271. )
  2272. {
  2273. m_bModified = true;
  2274. HRESULT HResult = pSrcMediaSample->GetTime(
  2275. &m_pMediaSample->m_rtStartTime,
  2276. &m_pMediaSample->m_rtEndTime
  2277. );
  2278. m_pMediaSample->m_dwFlags = (!HRESULT_FAILURE(HResult)) ?
  2279. AM_SAMPLE_TIMEVALID | AM_SAMPLE_STOPVALID :
  2280. 0;
  2281. m_pMediaSample->m_dwFlags |= (pSrcMediaSample->IsSyncPoint() == S_OK) ?
  2282. 0 : AM_GBF_NOTASYNCPOINT;
  2283. m_pMediaSample->m_dwFlags |= (pSrcMediaSample->IsDiscontinuity() == S_OK) ?
  2284. AM_GBF_PREVFRAMESKIPPED : 0;
  2285. m_pMediaSample->m_bIsPreroll = (pSrcMediaSample->IsPreroll() == S_OK);
  2286. }
  2287. // calls CTMStreamSample::Init, sets members
  2288. HRESULT
  2289. CQueueMediaSample::Init(
  2290. IN CStream &Stream,
  2291. IN CNBQueue<CQueueMediaSample> &Queue
  2292. )
  2293. {
  2294. m_pSampleQueue = &Queue;
  2295. return CTMStreamSample::Init(Stream, TRUE, NULL, 0);
  2296. }
  2297. // this is used to hold a ptr to a segment of a user sample buffer
  2298. // it also holds a reference to the user sample's IMediaSample i/f and
  2299. // releases it when done
  2300. void
  2301. CQueueMediaSample::HoldFragment(
  2302. IN DWORD FragSize,
  2303. IN BYTE *pbData,
  2304. IN IMediaSample &FragMediaSample
  2305. )
  2306. {
  2307. LOG((MSP_TRACE,
  2308. "CQueueMediaSample::HoldFragment(%u, %p, &%p) called",
  2309. FragSize, pbData, &FragMediaSample));
  2310. AUTO_SAMPLE_LOCK;
  2311. TM_ASSERT(0 < (LONG) FragSize);
  2312. TM_ASSERT(NULL != pbData);
  2313. //
  2314. // set media sample properties
  2315. // timestamp is set by the CMediaTerminalFilter
  2316. //
  2317. m_pMediaSample->m_dwFlags = 0;
  2318. m_bReceived = FALSE;
  2319. m_bModified = TRUE;
  2320. SetBufferInfo(FragSize, // buffer size
  2321. pbData, // pointer to buffer
  2322. FragSize // amount of buffer currently used
  2323. );
  2324. // ref to the user sample's media sample
  2325. // NOTE: m_pFragMediaSample is a CComPtr
  2326. m_pFragMediaSample = &FragMediaSample;
  2327. LOG((MSP_TRACE,
  2328. "CQueueMediaSample::HoldFragment(%u, %p, &%p) succeeded",
  2329. FragSize, pbData, &FragMediaSample));
  2330. }
  2331. void
  2332. CQueueMediaSample::FinalMediaSampleRelease(
  2333. )
  2334. {
  2335. LOG((MSP_TRACE, "CQueueMediaSample::FinalMediaSampleRelease[%p] - enter", this));
  2336. // NOTE : no one holds a reference to the media sample at this point
  2337. LOCK_SAMPLE;
  2338. // if we hold a reference to the IMediaSample i/f of a user sample
  2339. // being fragmented, release it
  2340. // NOTE: m_pFragMediaSample is a CComPtr
  2341. if (m_pFragMediaSample != NULL) m_pFragMediaSample = NULL;
  2342. // check if the stream is still committed, otherwise destroy self
  2343. if ( m_pStream->m_bCommitted )
  2344. {
  2345. BOOL bNQSuccess = m_pSampleQueue->EnQueue(this);
  2346. UNLOCK_SAMPLE;
  2347. //
  2348. // if failed to enqueue -- kill itself, no one cares.
  2349. //
  2350. if (!bNQSuccess)
  2351. {
  2352. LOG((MSP_WARN,
  2353. "CQueueMediaSample::FinalMediaSampleRelease - failed to enqueue. delete this"));
  2354. delete this;
  2355. }
  2356. }
  2357. else
  2358. {
  2359. // this is in case the stream has already been decommitted
  2360. UNLOCK_SAMPLE;
  2361. delete this;
  2362. LOG((MSP_WARN,
  2363. "CQueueMediaSample::FinalMediaSampleRelease - stream not committed. delete this"));
  2364. }
  2365. LOG((MSP_TRACE, "CQueueMediaSample::FinalMediaSampleRelease succeeded"));
  2366. }
  2367. #if DBG
  2368. // virtual
  2369. CQueueMediaSample::~CQueueMediaSample(
  2370. )
  2371. {
  2372. }
  2373. #endif // DBG
  2374. // if asked to allocate buffers, verify allocator properties
  2375. /* static */
  2376. BOOL
  2377. CUserMediaSample::VerifyAllocatorProperties(
  2378. IN BOOL bAllocateBuffers,
  2379. IN const ALLOCATOR_PROPERTIES &AllocProps
  2380. )
  2381. {
  2382. if (!bAllocateBuffers) return TRUE;
  2383. if (0 != AllocProps.cbPrefix) return FALSE;
  2384. if (0 == AllocProps.cbAlign) return FALSE;
  2385. return TRUE;
  2386. }
  2387. // this is called in AllocateSample (creates an instance and initializes it)
  2388. // creates a data buffer if none is provided (current behaviour)
  2389. // also calls CTMStreamSample::InitSample(pStream, bIsInternalSample)
  2390. HRESULT
  2391. CUserMediaSample::Init(
  2392. IN CStream &Stream,
  2393. IN BOOL bAllocateBuffer,
  2394. IN DWORD ReqdBufferSize,
  2395. IN const ALLOCATOR_PROPERTIES &AllocProps
  2396. )
  2397. {
  2398. LOG((MSP_TRACE, "CUserMediaSample::Init[%p](&%p, %u, &%p) called",
  2399. this, &Stream, bAllocateBuffer, &AllocProps));
  2400. TM_ASSERT(VerifyAllocatorProperties(bAllocateBuffer, AllocProps));
  2401. TM_ASSERT(FALSE == m_bWeAllocatedBuffer);
  2402. TM_ASSERT(NULL == m_pBuffer);
  2403. HRESULT hr;
  2404. hr = CTMStreamSample::InitSample(&Stream, FALSE);
  2405. BAIL_ON_FAILURE(hr);
  2406. // the caller wants us to create the buffer
  2407. if (bAllocateBuffer)
  2408. {
  2409. // determine size of buffer to allocate
  2410. // we use the user suggested buffer size (if not 0), else
  2411. // we use the negotiated allocator properties' buffer size
  2412. m_BufferSize =
  2413. (0 != ReqdBufferSize) ? ReqdBufferSize : AllocProps.cbBuffer;
  2414. LOG((MSP_TRACE,
  2415. "CUserMediaSample::Init creating buffer buffersize[%d]",
  2416. m_BufferSize));
  2417. m_pBuffer = new BYTE[m_BufferSize];
  2418. BAIL_IF_NULL(m_pBuffer, E_OUTOFMEMORY);
  2419. m_bWeAllocatedBuffer = TRUE;
  2420. }
  2421. else // the user will provide the buffer later
  2422. {
  2423. //
  2424. // the user will need to submit buffers of at least this size -- filter
  2425. // promised this during allocator properties negotiation
  2426. //
  2427. m_dwRequiredBufferSize = AllocProps.cbBuffer;
  2428. LOG((MSP_TRACE,
  2429. "CUserMediaSample::Init -- the app will need to provide buffers of size 0x%lx",
  2430. m_dwRequiredBufferSize));
  2431. m_BufferSize = 0;
  2432. m_pBuffer = NULL;
  2433. TM_ASSERT(!m_bWeAllocatedBuffer);
  2434. }
  2435. TM_ASSERT(0 == m_DataSize);
  2436. LOG((MSP_TRACE, "CUserMediaSample::Init(&%p, %u, &%p) succeeded",
  2437. &Stream, bAllocateBuffer, &AllocProps));
  2438. return S_OK;
  2439. }
  2440. void
  2441. CUserMediaSample::BeginFragment(
  2442. IN BOOL bNoteCurrentTime
  2443. )
  2444. {
  2445. LOG((MSP_TRACE,
  2446. "CUserMediaSample::BeginFragment (frag=%p)", this));
  2447. AUTO_SAMPLE_LOCK;
  2448. // we are being fragmented
  2449. m_bBeingFragmented = TRUE;
  2450. // note current time
  2451. if (bNoteCurrentTime) m_BeginFragmentTime = timeGetTime();
  2452. // nothing has been fragmented yet
  2453. m_NumBytesFragmented = 0;
  2454. // increment refcnt to the internal media sample. this ensures that
  2455. // FinalMediaSampleRelease is not called until the last fragment is
  2456. // completed
  2457. m_pMediaSample->AddRef();
  2458. // increment refcnt to self. this ensures that we'll exist while
  2459. // the last fragment has not returned
  2460. ((IStreamSample *)this)->AddRef();
  2461. }
  2462. //////////////////////////////////////////////////////////////////////////////
  2463. //
  2464. // Fragment
  2465. //
  2466. // For write -- Assigns a chunk of this user media sample to an outgoing
  2467. // sample in the filter graph, a CQueueMediaSample. The data is not actually
  2468. // copied; instead, CQeueMediaSample::HoldFragment is called to set the pointers
  2469. // to the appropriate pointer of the user media sample.
  2470. //
  2471. void
  2472. CUserMediaSample::Fragment(
  2473. IN BOOL bFragment, // actually fragment? false if video
  2474. IN LONG AllocBufferSize, // max amount of data to copy
  2475. IN OUT CQueueMediaSample &QueueMediaSample, // destination sample
  2476. OUT BOOL &bDone // out: set to true if no data left in source
  2477. )
  2478. {
  2479. LOG((MSP_TRACE,
  2480. "CUserMediaSample::Fragment(%u, %l, &%p, &%p) called (frag=%p)",
  2481. bFragment, AllocBufferSize, &QueueMediaSample, &bDone, this));
  2482. AUTO_SAMPLE_LOCK;
  2483. TM_ASSERT(m_bBeingFragmented);
  2484. TM_ASSERT(m_NumBytesFragmented < m_DataSize);
  2485. //
  2486. // DestSize = amount of data we are actually going to copy
  2487. //
  2488. LONG DestSize;
  2489. if (bFragment)
  2490. {
  2491. DestSize = min(AllocBufferSize, m_DataSize - m_NumBytesFragmented);
  2492. }
  2493. else
  2494. {
  2495. TM_ASSERT(0 == m_NumBytesFragmented);
  2496. DestSize = m_DataSize;
  2497. }
  2498. //
  2499. // pass the fragment to the queue sample
  2500. //
  2501. QueueMediaSample.HoldFragment(
  2502. DestSize,
  2503. m_pBuffer + m_NumBytesFragmented,
  2504. *m_pMediaSample
  2505. );
  2506. //
  2507. // increment number of bytes fragmented
  2508. //
  2509. m_NumBytesFragmented += DestSize;
  2510. //
  2511. // let the caller know if we are done with fragmenting our buffer
  2512. //
  2513. bDone = ((m_NumBytesFragmented >= m_DataSize) ? TRUE : FALSE);
  2514. //
  2515. // if we are done, we should release our reference to the internal
  2516. // IMediaSample instance. this was acquired when BeginFragment was called
  2517. //
  2518. if (bDone)
  2519. {
  2520. m_bReceived = TRUE; // needed for FinalMediaSampleRelease
  2521. m_pMediaSample->Release();
  2522. }
  2523. LOG((MSP_TRACE,
  2524. "CUserMediaSample::Fragment(%u, %l, &%p, &%p) succeeded (frag=%p)",
  2525. bFragment, AllocBufferSize, &QueueMediaSample, &bDone, this));
  2526. }
  2527. //////////////////////////////////////////////////////////////////////////////
  2528. //
  2529. // CopyFragment
  2530. //
  2531. // For write -- copies a chunk of this user media sample to an outgoing
  2532. // sample in the filter graph. This is for when we are using a downstream
  2533. // allocator.
  2534. //
  2535. HRESULT
  2536. CUserMediaSample::CopyFragment(
  2537. IN BOOL bFragment, // actually fragment? false if video
  2538. IN LONG AllocBufferSize, // max amount of data to copy
  2539. IN OUT IMediaSample * pDestMediaSample, // destination sample
  2540. OUT BOOL & bDone // out: set to true if no data left in source
  2541. )
  2542. {
  2543. LOG((MSP_TRACE,
  2544. "CUserMediaSample::CopyFragment(%u, %ld, &%p, &%p) called (frag=%p)",
  2545. bFragment, AllocBufferSize, &pDestMediaSample, &bDone, this));
  2546. AUTO_SAMPLE_LOCK;
  2547. TM_ASSERT(m_bBeingFragmented);
  2548. TM_ASSERT(m_NumBytesFragmented < m_DataSize);
  2549. //
  2550. // DestSize = amount of data we are actually going to copy
  2551. //
  2552. // IMediaSmaple::GetSize has a weird prototype -- returns HRESULT
  2553. // but it's actually just the size as a LONG
  2554. //
  2555. LONG lDestSize;
  2556. if ( bFragment )
  2557. {
  2558. //
  2559. // We copy as much as we have left to copy or as much as will fit in
  2560. // a sample, whichever is less.
  2561. //
  2562. lDestSize = min( AllocBufferSize, m_DataSize - m_NumBytesFragmented );
  2563. //
  2564. // If the sample has less space than the allocator propeties said it
  2565. // would have, then trim lDestSize to that value. We don't just use
  2566. // pDestMediaSample->GetSize() instead of AllocBufferSize above because
  2567. // we want to use the allocator properties size if the sample has *more*
  2568. // space than the allocator properties specify.
  2569. //
  2570. lDestSize = min( pDestMediaSample->GetSize(), lDestSize );
  2571. }
  2572. else
  2573. {
  2574. // video case -- copy entire sample
  2575. // we bail if the destination sample isn't big enough
  2576. TM_ASSERT(0 == m_NumBytesFragmented);
  2577. lDestSize = m_DataSize;
  2578. if ( ( lDestSize > AllocBufferSize ) ||
  2579. ( lDestSize > pDestMediaSample->GetSize() ) )
  2580. {
  2581. return VFW_E_BUFFER_OVERFLOW;
  2582. }
  2583. }
  2584. //
  2585. // copy the fragment to the destination sample
  2586. // instead of CQUeueMediaSample::HoldFragment
  2587. //
  2588. HRESULT hr;
  2589. BYTE * pDestBuffer;
  2590. hr = pDestMediaSample->GetPointer( & pDestBuffer );
  2591. if ( FAILED(hr) )
  2592. {
  2593. return hr;
  2594. }
  2595. CopyMemory(
  2596. pDestBuffer, // destination buffer
  2597. m_pBuffer + m_NumBytesFragmented, // source buffer
  2598. lDestSize
  2599. );
  2600. hr = pDestMediaSample->SetActualDataLength( lDestSize );
  2601. if ( FAILED(hr) )
  2602. {
  2603. return hr;
  2604. }
  2605. //
  2606. // increment number of bytes fragmented
  2607. //
  2608. m_NumBytesFragmented += lDestSize;
  2609. //
  2610. // let the caller know if we are done with fragmenting our buffer
  2611. //
  2612. bDone = ((m_NumBytesFragmented >= m_DataSize) ? TRUE : FALSE);
  2613. //
  2614. // if we are done, we should release our reference to the internal
  2615. // IMediaSample instance. this was acquired when BeginFragment was called
  2616. //
  2617. if (bDone)
  2618. {
  2619. m_bReceived = TRUE; // needed for FinalMediaSampleRelease
  2620. m_pMediaSample->Release();
  2621. }
  2622. LOG((MSP_TRACE,
  2623. "CUserMediaSample::CopyFragment(%u, %ld, &%p, &%p) succeeded (frag=%p)",
  2624. bFragment, AllocBufferSize, &pDestMediaSample, &bDone, this));
  2625. return S_OK;
  2626. }
  2627. // computes the time to wait. it checks the time at which the first
  2628. // byte of the current fragment would be due and subtracts the
  2629. // time delay since the beginning of fragmentation
  2630. DWORD
  2631. CUserMediaSample::GetTimeToWait(
  2632. IN DOUBLE DelayPerByte
  2633. )
  2634. {
  2635. LOG((MSP_TRACE,
  2636. "CUserMediaSample::GetTimeToWait(%f) called",
  2637. DelayPerByte));
  2638. // get current time
  2639. DWORD CurrentTime = timeGetTime();
  2640. AUTO_SAMPLE_LOCK;
  2641. // calculate the time elapsed since BeginFragment was called,
  2642. // account for wrap around
  2643. DWORD TimeSinceBeginFragment =
  2644. (CurrentTime >= m_BeginFragmentTime) ?
  2645. (CurrentTime - m_BeginFragmentTime) :
  2646. (DWORD(-1) - m_BeginFragmentTime + CurrentTime);
  2647. DWORD DueTime = DWORD(m_NumBytesFragmented*DelayPerByte);
  2648. LOG((MSP_INFO,
  2649. "DueTime = %u, TimeSinceBeginFragment = %u",
  2650. DueTime, TimeSinceBeginFragment));
  2651. // if due in future, return the difference, else return 0
  2652. DWORD TimeToWait;
  2653. if (DueTime > TimeSinceBeginFragment)
  2654. TimeToWait = DueTime - TimeSinceBeginFragment;
  2655. else
  2656. TimeToWait = 0;
  2657. LOG((MSP_INFO,
  2658. "CUserMediaSample::GetTimeToWait(%f) returns %u successfully",
  2659. DelayPerByte, TimeToWait));
  2660. return TimeToWait;
  2661. }
  2662. // when we are decommitted/aborted while being fragmented, we
  2663. // need to get rid of our refcnt on internal IMediaSample and set
  2664. // the error code to E_ABORT. this will be signaled to the user only when
  2665. // the last refcnt on IMediaSample is released (possibly by an outstanding
  2666. // queue sample)
  2667. void
  2668. CUserMediaSample::AbortDuringFragmentation(
  2669. )
  2670. {
  2671. LOG((MSP_TRACE,
  2672. "CUserMediaSample::AbortDuringFragmentation (frag=%p)", this));
  2673. AUTO_SAMPLE_LOCK;
  2674. TM_ASSERT(m_bBeingFragmented);
  2675. m_MediaSampleIoStatus = E_ABORT;
  2676. // release reference on internal IMediaSample instance
  2677. // this was acquired when BeginFragment was called
  2678. m_pMediaSample->Release();
  2679. }
  2680. STDMETHODIMP
  2681. CUserMediaSample::SetBuffer(
  2682. IN DWORD cbSize,
  2683. IN BYTE * pbData,
  2684. IN DWORD dwFlags
  2685. )
  2686. {
  2687. LOG((MSP_TRACE, "CUserMediaSample::SetBuffer[%p](%lu, %p, %lu) called",
  2688. this, cbSize, pbData, dwFlags));
  2689. if (dwFlags != 0 || cbSize == 0)
  2690. {
  2691. return E_INVALIDARG;
  2692. }
  2693. // cannot accept a positive value that doesn't fit in a LONG
  2694. // currently (based upon CSample implementation)
  2695. if ((LONG)cbSize < 0)
  2696. {
  2697. LOG((MSP_WARN,
  2698. "CUserMediaSample::SetBuffer - the buffer is too large. "
  2699. "returning E_FAIL"));
  2700. return E_FAIL;
  2701. }
  2702. // cannot accept null data buffer
  2703. //
  2704. // we don't want to do IsBadWritePtr here as this method could be called
  2705. // on every sample, so veryfying the memory could be expensive
  2706. //
  2707. if (NULL == pbData)
  2708. {
  2709. LOG((MSP_WARN,
  2710. "CUserMediaSample::SetBuffer - buffer pointer is NULL "
  2711. "returning E_POINTER"));
  2712. return E_POINTER;
  2713. }
  2714. //
  2715. // the app needs to give us at least as much memory as we have promised to
  2716. // other filters in the graph (this number was specified when the sample was
  2717. // created and initialized).
  2718. //
  2719. // if we don't do this check, a downstream filter may av because it expects
  2720. // a bigger buffer.
  2721. //
  2722. if ( m_dwRequiredBufferSize > cbSize )
  2723. {
  2724. LOG((MSP_WARN,
  2725. "CUserMediaSample::SetBuffer - the app did not allocate enough memory "
  2726. "Need 0x%lx bytes, app allocated 0x%lx. returning TAPI_E_NOTENOUGHMEMORY",
  2727. m_dwRequiredBufferSize, cbSize));
  2728. return TAPI_E_NOTENOUGHMEMORY;
  2729. }
  2730. AUTO_SAMPLE_LOCK;
  2731. // Free anything we allocated ourselves
  2732. // -- We allow multiple calls to this method
  2733. if (m_bWeAllocatedBuffer)
  2734. {
  2735. delete m_pBuffer;
  2736. m_bWeAllocatedBuffer = FALSE;
  2737. m_pBuffer = NULL;
  2738. }
  2739. m_BufferSize = cbSize;
  2740. m_DataSize = 0;
  2741. m_pBuffer = pbData;
  2742. LOG((MSP_TRACE, "CUserMediaSample::SetBuffer(%u, %p, %u) succeeded",
  2743. cbSize, pbData, dwFlags));
  2744. return S_OK;
  2745. }
  2746. STDMETHODIMP
  2747. CUserMediaSample::GetInfo(
  2748. OUT DWORD *pdwLength,
  2749. OUT BYTE **ppbData,
  2750. OUT DWORD *pcbActualData
  2751. )
  2752. {
  2753. AUTO_SAMPLE_LOCK;
  2754. LOG((MSP_TRACE, "CUserMediaSample::GetInfo(%p, %p, %p) called",
  2755. pdwLength, ppbData, pcbActualData));
  2756. if (m_BufferSize == 0)
  2757. {
  2758. LOG((MSP_WARN, "CUserMediaSample::GetInfo - sample not initialized"));
  2759. return MS_E_NOTINIT;
  2760. }
  2761. if (NULL != pdwLength)
  2762. {
  2763. LOG((MSP_TRACE,
  2764. "CUserMediaSample::GetInfo - pdwLength is not NULL."));
  2765. *pdwLength = m_BufferSize;
  2766. }
  2767. if (NULL != ppbData)
  2768. {
  2769. LOG((MSP_TRACE,
  2770. "CUserMediaSample::GetInfo - ppbData is not NULL."));
  2771. *ppbData = m_pBuffer;
  2772. }
  2773. if (NULL != pcbActualData)
  2774. {
  2775. LOG((MSP_TRACE,
  2776. "CUserMediaSample::GetInfo - pcbActualData is not NULL."));
  2777. *pcbActualData = m_DataSize;
  2778. }
  2779. LOG((MSP_TRACE,
  2780. "CUserMediaSample::GetInfo - succeeded. "
  2781. "m_BufferSize[%lu(decimal)] m_pBuffer[%p] m_DataSize[%lx]",
  2782. m_BufferSize, m_pBuffer, m_DataSize));
  2783. return S_OK;
  2784. }
  2785. STDMETHODIMP
  2786. CUserMediaSample::SetActual(
  2787. IN DWORD cbDataValid
  2788. )
  2789. {
  2790. AUTO_SAMPLE_LOCK;
  2791. LOG((MSP_TRACE, "CUserMediaSample::SetActual(%u) called", cbDataValid));
  2792. // cannot accept a positive value that doesn't fit in a LONG
  2793. // currently (based upon CSample implementation)
  2794. if ((LONG)cbDataValid < 0) return E_FAIL;
  2795. if ((LONG)cbDataValid > m_BufferSize) return E_INVALIDARG;
  2796. m_DataSize = cbDataValid;
  2797. LOG((MSP_TRACE, "CUserMediaSample::SetActual(%u) succeeded", cbDataValid));
  2798. return S_OK;
  2799. }
  2800. // redirects this call to ((CMediaTerminalFilter *)m_pStream)
  2801. STDMETHODIMP
  2802. CUserMediaSample::get_MediaFormat(
  2803. /* [optional]??? */ OUT AM_MEDIA_TYPE **ppFormat
  2804. )
  2805. {
  2806. AUTO_SAMPLE_LOCK;
  2807. LOG((MSP_TRACE, "CUserMediaSample::get_MediaFormat(%p) called", ppFormat));
  2808. return ((CMediaTerminalFilter *)m_pStream)->GetFormat(ppFormat);
  2809. }
  2810. // this is not allowed
  2811. STDMETHODIMP
  2812. CUserMediaSample::put_MediaFormat(
  2813. IN const AM_MEDIA_TYPE *pFormat
  2814. )
  2815. {
  2816. AUTO_SAMPLE_LOCK;
  2817. LOG((MSP_TRACE, "CUserMediaSample::put_MediaFormat(%p) called", pFormat));
  2818. return E_NOTIMPL;
  2819. }
  2820. // calls the base class FinalMediaSampleRelease and then releases
  2821. // self refcnt. this self refcnt ensures that when this method is
  2822. // called, the sample still exists
  2823. void
  2824. CUserMediaSample::FinalMediaSampleRelease(
  2825. )
  2826. {
  2827. AUTO_SAMPLE_LOCK;
  2828. // this signals the user that the sample has completed
  2829. CTMStreamSample::FinalMediaSampleRelease();
  2830. // release self reference if we are being fragmented
  2831. // this is only needed to ensure that we exist when the last
  2832. // reference to the internal IMediaSample interface is released
  2833. if (m_bBeingFragmented) m_bBeingFragmented = FALSE;
  2834. // this self reference was obtained when
  2835. // when fragmentation began (for write side) or
  2836. // when the sample refcnt was not released on removal from pool in
  2837. // GetBuffer (for read side)
  2838. // NOTE: the sample may go away after this release
  2839. ((IStreamSample *)this)->Release();
  2840. }
  2841. HRESULT
  2842. CUserMediaSample::CopyFrom(
  2843. IN IMediaSample *pSrcMediaSample
  2844. )
  2845. {
  2846. LOG((MSP_TRACE, "CUserMediaSample::CopyFrom(%p) called", pSrcMediaSample));
  2847. AUTO_SAMPLE_LOCK;
  2848. TM_ASSERT(NULL != m_pBuffer);
  2849. // sets the "non-data" member values
  2850. CTMStreamSample::CopyFrom(pSrcMediaSample);
  2851. // get the buffer ptr
  2852. BYTE *pBuffer;
  2853. HRESULT hr;
  2854. hr = pSrcMediaSample->GetPointer(&pBuffer);
  2855. BAIL_ON_FAILURE(hr);
  2856. TM_ASSERT(NULL != pBuffer);
  2857. // determine the number of bytes to copy
  2858. LONG lDataSize = pSrcMediaSample->GetActualDataLength();
  2859. TM_ASSERT(0 <= lDataSize);
  2860. if (0 > lDataSize) return E_FAIL;
  2861. if (lDataSize > m_BufferSize)
  2862. {
  2863. hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA);
  2864. lDataSize = m_BufferSize;
  2865. }
  2866. // copy data and set the data size to the number of bytes copied
  2867. memcpy(m_pBuffer, pBuffer, lDataSize);
  2868. m_DataSize = lDataSize;
  2869. LOG((MSP_TRACE, "CUserMediaSample::CopyFrom(%p) returns hr=%u",
  2870. pSrcMediaSample, hr));
  2871. // we may return ERROR_MORE_DATA after copying the buffer, so return hr
  2872. return hr;
  2873. }
  2874. // copies the non-data members of pSrcMediaSample
  2875. // copies as much as possible of the data buffer into own buffer
  2876. // and advances pBuffer and DataLength beyond the copied data
  2877. HRESULT
  2878. CUserMediaSample::CopyFrom(
  2879. IN IMediaSample *pSrcMediaSample,
  2880. IN OUT BYTE *&pBuffer,
  2881. IN OUT LONG &DataLength
  2882. )
  2883. {
  2884. LOG((MSP_TRACE,
  2885. "CUserMediaSample::CopyFrom(%p, &%p, &%l) called",
  2886. pSrcMediaSample, pBuffer, DataLength));
  2887. if (NULL == pBuffer) return E_FAIL;
  2888. if (0 > DataLength) return E_FAIL;
  2889. AUTO_SAMPLE_LOCK;
  2890. TM_ASSERT(NULL != m_pBuffer);
  2891. TM_ASSERT(NULL != pBuffer);
  2892. TM_ASSERT(0 <= DataLength);
  2893. // sets the "non-data" member values
  2894. CTMStreamSample::CopyFrom(pSrcMediaSample);
  2895. HRESULT hr = S_OK;
  2896. LONG lDataSize = DataLength;
  2897. if (lDataSize > m_BufferSize)
  2898. {
  2899. hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA);
  2900. lDataSize = m_BufferSize;
  2901. }
  2902. // copy data and set the data size to the number of bytes copied
  2903. memcpy(m_pBuffer, pBuffer, lDataSize);
  2904. m_DataSize = lDataSize;
  2905. // advance the parameters beyond the copied data
  2906. pBuffer += lDataSize;
  2907. DataLength -= lDataSize;
  2908. LOG((MSP_TRACE,
  2909. "CUserMediaSample::CopyFrom(&%p, &%p, %l) returns hr=%u",
  2910. pSrcMediaSample, pBuffer, DataLength, hr));
  2911. // we may return ERROR_MORE_DATA after copying the buffer, so return hr
  2912. return hr;
  2913. }
  2914. // NOTE : This has been copied from the CSample base class because
  2915. // StealSampleFromFreePool doesn't Release the ref count on the stolen
  2916. // sample
  2917. // in this implementation, we have made sure that each sample in the
  2918. // CStream free pool has a refcnt increase. therefore, we need to decrease
  2919. // it if stealing is successful. Moreover, we also try to steal the
  2920. // sample being fragmented currently although its not in the free pool
  2921. STDMETHODIMP
  2922. CUserMediaSample::CompletionStatus(DWORD dwFlags, DWORD dwMilliseconds)
  2923. {
  2924. LOG((MSP_TRACE, "CUserMediaSample::CompletionStatus(0x%8.8X, 0x%8.8X) called",
  2925. dwFlags, dwMilliseconds));
  2926. LOCK_SAMPLE;
  2927. HRESULT hr = m_Status;
  2928. if (hr == MS_S_PENDING) {
  2929. if (dwFlags & (COMPSTAT_NOUPDATEOK | COMPSTAT_ABORT) ||
  2930. (m_bContinuous && m_bModified && (dwFlags & COMPSTAT_WAIT))) {
  2931. m_bContinuous = false;
  2932. if (dwFlags & COMPSTAT_ABORT) {
  2933. m_bWantAbort = true; // Set this so we won't add it back to the free pool if released
  2934. }
  2935. if (((CMediaTerminalFilter *)m_pStream)->StealSample(this)) {
  2936. UNLOCK_SAMPLE;
  2937. hr = SetCompletionStatus(m_bModified ? S_OK : MS_S_NOUPDATE);
  2938. ((IStreamSample *)this)->Release();
  2939. return hr;
  2940. } // If doesn't work then return MS_S_PENDING unless we're told to wait!
  2941. }
  2942. if (dwFlags & COMPSTAT_WAIT) {
  2943. m_bContinuous = false; // Make sure it will complete!
  2944. UNLOCK_SAMPLE;
  2945. WaitForSingleObject(m_hCompletionEvent, dwMilliseconds);
  2946. LOCK_SAMPLE;
  2947. hr = m_Status;
  2948. }
  2949. }
  2950. UNLOCK_SAMPLE;
  2951. LOG((MSP_TRACE, "CUserMediaSample::CompletionStatus(0x%8.8X, 0x%8.8X) succeeded",
  2952. dwFlags, dwMilliseconds));
  2953. return hr;
  2954. }
  2955. // NOTE : This has been copied from the CSample base class
  2956. // because it calls m_pStream->AddSampleToFreePool to add sample
  2957. // to the CStream q. Since this shouldn't be happening after Decommit,
  2958. // m_pStream->AddSampleToFreePool has been replaced by another call
  2959. // m_pStream->AddToPoolIfCommitted that checks m_bCommitted and if
  2960. // FALSE, returns error
  2961. //
  2962. // Set the sample's status and signal completion if necessary.
  2963. //
  2964. // Note that when the application has been signalled by whatever method
  2965. // the application can immediately turn around on another thread
  2966. // and Release() the sample. This is most likely when the completion
  2967. // status is set from the quartz thread that's pushing the data.
  2968. //
  2969. HRESULT
  2970. CUserMediaSample::SetCompletionStatus(
  2971. IN HRESULT hrStatus
  2972. )
  2973. {
  2974. LOCK_SAMPLE;
  2975. TM_ASSERT(m_Status == MS_S_PENDING);
  2976. if (hrStatus == MS_S_PENDING || (hrStatus == S_OK && m_bContinuous))
  2977. {
  2978. //
  2979. // We are not done with the sample -- put it back in our pool so that
  2980. // we can use it again.
  2981. //
  2982. HRESULT hr;
  2983. hr = ((CMediaTerminalFilter *)m_pStream)->AddToPoolIfCommitted(this);
  2984. // there is an error, so signal this to the user
  2985. if (HRESULT_FAILURE(hr)) hrStatus = hr;
  2986. else
  2987. {
  2988. UNLOCK_SAMPLE;
  2989. return hrStatus;
  2990. }
  2991. }
  2992. //
  2993. // The sample is ready to be returned to the app -- signal comletion.
  2994. // We still have a lock.
  2995. //
  2996. HANDLE handle = m_hUserHandle;
  2997. PAPCFUNC pfnAPC = m_UserAPC;
  2998. DWORD_PTR dwptrAPCData = m_dwptrUserAPCData; // win64 fix
  2999. m_hUserHandle = m_UserAPC = NULL;
  3000. m_dwptrUserAPCData = 0;
  3001. m_Status = hrStatus;
  3002. HANDLE hCompletionEvent = m_hCompletionEvent;
  3003. UNLOCK_SAMPLE;
  3004. // DANGER DANGER - sample can go away here
  3005. SetEvent(hCompletionEvent);
  3006. if (pfnAPC) {
  3007. // queue the APC and close the targe thread handle
  3008. // the calling thread handle was duplicated when Update
  3009. // was called
  3010. QueueUserAPC(pfnAPC, handle, dwptrAPCData);
  3011. CloseHandle(handle);
  3012. } else {
  3013. if (handle) {
  3014. SetEvent(handle);
  3015. }
  3016. }
  3017. return hrStatus;
  3018. }
  3019. // this method has been copied from the CSample implementation
  3020. // Now SetCompletionStatus returns an error code that can be failure
  3021. // this method didn't check for the error code and hence had to
  3022. // be over-ridden and modified to do so
  3023. // we must not reset user event as the user may pass in the same event for
  3024. // all samples
  3025. HRESULT
  3026. CUserMediaSample::InternalUpdate(
  3027. DWORD dwFlags,
  3028. HANDLE hEvent,
  3029. PAPCFUNC pfnAPC,
  3030. DWORD_PTR dwptrAPCData
  3031. )
  3032. {
  3033. if ((hEvent && pfnAPC) || (dwFlags & (~(SSUPDATE_ASYNC | SSUPDATE_CONTINUOUS)))) {
  3034. return E_INVALIDARG;
  3035. }
  3036. if (m_Status == MS_S_PENDING) {
  3037. return MS_E_BUSY;
  3038. }
  3039. // if we don't have a buffer to operate upon, return error
  3040. if (NULL == m_pBuffer) return E_FAIL;
  3041. if (NULL != m_pStream->m_pMMStream) {
  3042. STREAM_STATE StreamState;
  3043. m_pStream->m_pMMStream->GetState(&StreamState);
  3044. if (StreamState != STREAMSTATE_RUN) {
  3045. return MS_E_NOTRUNNING;
  3046. }
  3047. }
  3048. ResetEvent(m_hCompletionEvent);
  3049. m_Status = MS_S_PENDING;
  3050. m_bWantAbort = false;
  3051. m_bModified = false;
  3052. m_bContinuous = (dwFlags & SSUPDATE_CONTINUOUS) != 0;
  3053. m_UserAPC = pfnAPC;
  3054. TM_ASSERT(NULL == m_hUserHandle);
  3055. if (pfnAPC) {
  3056. BOOL bDuplicated =
  3057. DuplicateHandle(
  3058. GetCurrentProcess(),
  3059. GetCurrentThread(),
  3060. GetCurrentProcess(),
  3061. &m_hUserHandle,
  3062. 0, // ignored
  3063. TRUE,
  3064. DUPLICATE_SAME_ACCESS
  3065. );
  3066. if (!bDuplicated)
  3067. {
  3068. DWORD LastError = GetLastError();
  3069. LOG((MSP_ERROR, "CUserMediaSample::InternalUpdate - \
  3070. couldn't duplicate calling thread handle - error %u",
  3071. LastError));
  3072. return HRESULT_FROM_ERROR_CODE(LastError);
  3073. }
  3074. m_dwptrUserAPCData = dwptrAPCData;
  3075. } else {
  3076. m_hUserHandle = hEvent;
  3077. // rajeevb - also used to reset the user provided event
  3078. // this is not being done any more as the user may provide
  3079. // the same event for more than one sample and we may reset
  3080. // a signaled event
  3081. }
  3082. //
  3083. // If we're at the end of the stream, wait until this point before punting it
  3084. // because we need to signal the event or fire the APC.
  3085. //
  3086. if (m_pStream->m_bEndOfStream) {
  3087. // Because this is called synchronously from Update the
  3088. // application must have a ref count on the sample until we
  3089. // return so we don't have to worry about it going away here
  3090. return SetCompletionStatus(MS_S_ENDOFSTREAM);
  3091. }
  3092. // rajeevb - need to check for SetCompletionStatus error code
  3093. HRESULT hr;
  3094. hr = SetCompletionStatus(MS_S_PENDING); // This adds us to the free pool.
  3095. BAIL_ON_FAILURE(hr);
  3096. if (hEvent || pfnAPC || (dwFlags & SSUPDATE_ASYNC)) {
  3097. return MS_S_PENDING;
  3098. } else {
  3099. return S_OK;
  3100. }
  3101. }