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.

777 lines
18 KiB

  1. /*
  2. Copyright (c) 1998-1999 Microsoft Corporation
  3. */
  4. // Stream.cpp : Implementation of CStream
  5. #include "stdafx.h"
  6. /////////////////////////////////////////////////////////////////////////////
  7. // CStream
  8. CStream::CStream() :
  9. m_bCommitted(false),
  10. m_lRequestedBufferCount(0),
  11. m_bFlushing(false),
  12. m_rtWaiting(0),
  13. m_lWaiting(0),
  14. m_hWaitFreeSem(NULL),
  15. m_pFirstFree(NULL),
  16. m_pLastFree(NULL),
  17. m_cAllocated(0),
  18. m_bEndOfStream(false),
  19. m_FilterState(State_Stopped),
  20. m_pFilter(NULL),
  21. m_pFilterGraph(NULL),
  22. m_pMMStream(NULL),
  23. m_rtSegmentStart(0),
  24. m_bStopIfNoSamples(false)
  25. {
  26. InitMediaType(&m_ConnectedMediaType);
  27. InitMediaType(&m_ActualMediaType);
  28. CHECKSAMPLELIST
  29. }
  30. #ifdef DEBUG
  31. bool CStream::CheckSampleList()
  32. {
  33. if (m_pFirstFree) {
  34. CSample *pSample = m_pFirstFree;
  35. if (pSample->m_pPrevFree != NULL) {
  36. return false;
  37. }
  38. while (pSample->m_pNextFree) {
  39. if (pSample->m_pNextFree->m_pPrevFree != pSample) {
  40. return false;
  41. }
  42. pSample = pSample->m_pNextFree;
  43. }
  44. if (pSample != m_pLastFree) {
  45. return false;
  46. }
  47. } else {
  48. if (m_pLastFree) {
  49. return false;
  50. }
  51. }
  52. return true;
  53. }
  54. #endif
  55. HRESULT CStream::FinalConstruct(void)
  56. {
  57. //
  58. // attempt to create a semaphore
  59. //
  60. TCHAR *ptczSemaphoreName = NULL;
  61. #if DBG
  62. //
  63. // in debug build, use named semaphores.
  64. //
  65. TCHAR tszSemaphoreName[MAX_PATH];
  66. _stprintf(tszSemaphoreName,
  67. _T("CStream_Semaphore_pid[0x%lx]_CStream[%p]_"),
  68. GetCurrentProcessId(), this);
  69. LOG((MSP_TRACE, "CStream::FinalConstruct[%p] - creating semaphore[%S]",
  70. this, tszSemaphoreName));
  71. ptczSemaphoreName = &tszSemaphoreName[0];
  72. #endif
  73. m_hWaitFreeSem = CreateSemaphore(NULL, 0, 0x7FFFFFF, ptczSemaphoreName);
  74. return m_hWaitFreeSem ? S_OK : E_OUTOFMEMORY;
  75. }
  76. CStream::~CStream()
  77. {
  78. // Normally we would do:
  79. // SetState(State_Stopped); // Make sure we're decommitted and pump is dead
  80. // but since this is now a pure virtual method it's not linking.
  81. // moved this to the derived class, which will eventually be the only stream class anyway
  82. Disconnect(); // Free any allocated media types and release held references
  83. if (m_hWaitFreeSem) {
  84. CloseHandle(m_hWaitFreeSem);
  85. }
  86. }
  87. STDMETHODIMP CStream::GetMultiMediaStream(IMultiMediaStream **ppMultiMediaStream)
  88. {
  89. LOG((MSP_TRACE, "IMediaStream::GetMultiMediaStream(%p)",
  90. ppMultiMediaStream));
  91. if (NULL == ppMultiMediaStream) {
  92. return E_POINTER;
  93. }
  94. if (m_pMMStream != NULL) {
  95. m_pMMStream->AddRef();
  96. }
  97. *ppMultiMediaStream = m_pMMStream;
  98. return S_OK;
  99. }
  100. STDMETHODIMP CStream::GetInformation(MSPID *pPurposeId, STREAM_TYPE *pType)
  101. {
  102. LOG((MSP_TRACE, "IMediaStream::GetInformation(%p, %p)\n",
  103. pPurposeId, pType));
  104. if (pPurposeId) {
  105. *pPurposeId = m_PurposeId;
  106. }
  107. if (pType) {
  108. *pType = m_StreamType;
  109. }
  110. return S_OK;
  111. }
  112. STDMETHODIMP CStream::SendEndOfStream(DWORD dwFlags)
  113. {
  114. LOG((MSP_TRACE, "IMediaStream::SendEndOfStream(0x%8.8X)",
  115. dwFlags));
  116. if (m_StreamType != STREAMTYPE_WRITE) {
  117. return MS_E_INVALIDSTREAMTYPE;
  118. }
  119. if (m_pConnectedPin) {
  120. return m_pConnectedPin->EndOfStream();
  121. }
  122. return S_OK;
  123. }
  124. STDMETHODIMP CStream::Initialize(IUnknown *pSourceObject, DWORD dwFlags,
  125. REFMSPID PurposeId, const STREAM_TYPE StreamType)
  126. {
  127. LOG((MSP_TRACE, "IMediaStream::Initalize(%p, 0x%8.8X, %p)",
  128. pSourceObject, dwFlags, &StreamType));
  129. HRESULT hr = NOERROR;
  130. if (dwFlags & ~(AMMSF_CREATEPEER | AMMSF_STOPIFNOSAMPLES)) {
  131. return E_INVALIDARG;
  132. }
  133. m_PurposeId = PurposeId;
  134. m_StreamType = StreamType;
  135. m_Direction = (StreamType == STREAMTYPE_WRITE) ? PINDIR_OUTPUT : PINDIR_INPUT;
  136. if (dwFlags & AMMSF_CREATEPEER) {
  137. if (!pSourceObject) {
  138. hr = E_INVALIDARG;
  139. } else {
  140. CComQIPtr<IMediaStream, &IID_IMediaStream> pMediaStream(pSourceObject);
  141. if (!pSourceObject) {
  142. hr = E_INVALIDARG;
  143. } else {
  144. hr = SetSameFormat(pMediaStream, 0);
  145. }
  146. }
  147. }
  148. m_bStopIfNoSamples = dwFlags & AMMSF_STOPIFNOSAMPLES ? true : false;
  149. return hr;
  150. }
  151. STDMETHODIMP CStream::JoinAMMultiMediaStream(IAMMultiMediaStream *pAMMultiMediaStream)
  152. {
  153. TM_ASSERT(pAMMultiMediaStream == NULL || m_pMMStream == NULL);
  154. AUTO_CRIT_LOCK;
  155. HRESULT hr;
  156. if (m_cAllocated) {
  157. hr = MS_E_SAMPLEALLOC;
  158. } else {
  159. m_pMMStream = pAMMultiMediaStream;
  160. }
  161. return S_OK;
  162. }
  163. STDMETHODIMP CStream::JoinFilter(IMediaStreamFilter *pMediaStreamFilter)
  164. {
  165. TM_ASSERT(pMediaStreamFilter == NULL || m_pFilter == NULL);
  166. m_pFilter = pMediaStreamFilter;
  167. pMediaStreamFilter->QueryInterface(IID_IBaseFilter, (void **)&m_pBaseFilter);
  168. m_pBaseFilter->Release();
  169. return S_OK;
  170. }
  171. STDMETHODIMP CStream::JoinFilterGraph(IFilterGraph *pFilterGraph)
  172. {
  173. TM_ASSERT(pFilterGraph == NULL || m_pFilterGraph == NULL);
  174. m_pFilterGraph = pFilterGraph;
  175. return S_OK;
  176. }
  177. //
  178. // IPin Implementation
  179. //
  180. STDMETHODIMP CStream::Disconnect()
  181. {
  182. m_pConnectedPin = NULL;
  183. m_pConnectedMemInputPin.Release(); // Magically sets to NULL here
  184. m_pQC.Release();
  185. m_pAllocator = NULL;
  186. m_lRequestedBufferCount = 0;
  187. FreeMediaType(m_ConnectedMediaType);
  188. FreeMediaType(m_ActualMediaType);
  189. return S_OK;
  190. }
  191. STDMETHODIMP CStream::ConnectedTo(IPin **pPin)
  192. {
  193. *pPin = m_pConnectedPin;
  194. if (*pPin) {
  195. (*pPin)->AddRef();
  196. return S_OK;
  197. } else {
  198. return VFW_E_NOT_CONNECTED;
  199. }
  200. }
  201. STDMETHODIMP CStream::ConnectionMediaType(AM_MEDIA_TYPE *pmt)
  202. {
  203. if (m_pConnectedPin) {
  204. CopyMediaType(pmt, &m_ConnectedMediaType);
  205. return S_OK;
  206. } else {
  207. ZeroMemory(pmt, sizeof(*pmt));
  208. pmt->lSampleSize = 1;
  209. pmt->bFixedSizeSamples = TRUE;
  210. return VFW_E_NOT_CONNECTED;
  211. }
  212. }
  213. void CStream::GetName(LPWSTR pszBuf)
  214. {
  215. if (m_PurposeId == GUID_NULL) {
  216. pszBuf[0] = 0;
  217. } else {
  218. pszBuf[0] = (m_Direction == PINDIR_INPUT) ? (WCHAR)'I' : (WCHAR)'O';
  219. WStringFromGUID(&m_PurposeId, &pszBuf[1]);
  220. }
  221. }
  222. STDMETHODIMP CStream::QueryPinInfo(PIN_INFO * pInfo)
  223. {
  224. pInfo->dir = m_Direction;
  225. GetName(pInfo->achName);
  226. return m_pFilter->QueryInterface(IID_IBaseFilter, (void **)&pInfo->pFilter);
  227. }
  228. STDMETHODIMP CStream::QueryDirection(PIN_DIRECTION * pPinDir)
  229. {
  230. *pPinDir = m_Direction;
  231. return S_OK;
  232. }
  233. STDMETHODIMP CStream::QueryId(LPWSTR * Id)
  234. {
  235. *Id = (LPWSTR)CoTaskMemAlloc(128 * sizeof(WCHAR));
  236. if (*Id) {
  237. GetName(*Id);
  238. return S_OK;
  239. } else {
  240. return E_OUTOFMEMORY;
  241. }
  242. }
  243. //
  244. // Derived classes must override this method
  245. //
  246. STDMETHODIMP CStream::QueryAccept(const AM_MEDIA_TYPE *pmt)
  247. {
  248. return E_NOTIMPL;
  249. };
  250. STDMETHODIMP CStream::QueryInternalConnections(IPin **apPin, ULONG *nPin)
  251. {
  252. //
  253. // Returning E_NOTIMPL tells the filter graph manager that all input pins are connected to
  254. // all output pins.
  255. //
  256. return E_NOTIMPL;
  257. };
  258. STDMETHODIMP CStream::EndOfStream(void)
  259. {
  260. HRESULT hr = S_OK;
  261. Lock();
  262. if (m_bFlushing || m_bEndOfStream) {
  263. hr = E_FAIL;
  264. } else {
  265. m_bEndOfStream = true;
  266. CSample *pSample = m_pFirstFree;
  267. m_pFirstFree = m_pLastFree = NULL; // Out of paranoia, clear these pointers first
  268. while (pSample) {
  269. CSample *pNext = pSample->m_pNextFree;
  270. pSample->SetCompletionStatus(MS_S_ENDOFSTREAM); // WARNING! This sample may go away!!!
  271. pSample = pNext;
  272. }
  273. CHECKSAMPLELIST
  274. }
  275. if (S_OK == hr) {
  276. m_pFilter->EndOfStream();
  277. }
  278. Unlock();
  279. return hr;
  280. }
  281. STDMETHODIMP CStream::BeginFlush(void)
  282. {
  283. HRESULT hr = S_OK;
  284. Lock();
  285. const BOOL bCancelEOS = m_bEndOfStream;
  286. if (m_bFlushing) {
  287. hr = S_FALSE;
  288. } else {
  289. m_bFlushing = true;
  290. m_bEndOfStream = false;
  291. Decommit(); // Force everyone to unblock
  292. }
  293. if (S_OK == hr) {
  294. m_pFilter->Flush(bCancelEOS);
  295. }
  296. Unlock();
  297. return hr;
  298. }
  299. STDMETHODIMP CStream::EndFlush(void)
  300. {
  301. AUTO_CRIT_LOCK;
  302. m_bFlushing = false;
  303. TM_ASSERT(!m_bEndOfStream);
  304. Commit(); // Let getbuffer work again
  305. return S_OK;
  306. }
  307. STDMETHODIMP CStream::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
  308. {
  309. Lock();
  310. m_rtSegmentStart = tStart;
  311. m_bEndOfStream = false;
  312. Unlock();
  313. return S_OK;
  314. }
  315. //
  316. // IMemInputPin
  317. //
  318. STDMETHODIMP CStream::GetAllocator(IMemAllocator ** ppAllocator)
  319. {
  320. return GetControllingUnknown()->QueryInterface(IID_IMemAllocator, (void **)ppAllocator);
  321. }
  322. STDMETHODIMP CStream::NotifyAllocator(IMemAllocator * pAllocator, BOOL bReadOnly)
  323. {
  324. m_bUsingMyAllocator = IsSameObject(pAllocator, GetControllingUnknown());
  325. m_bSamplesAreReadOnly = bReadOnly ? true : false;
  326. HRESULT hr = S_OK;
  327. if (!m_bUsingMyAllocator) {
  328. // Transfer the properties across
  329. ALLOCATOR_PROPERTIES Props;
  330. hr = pAllocator->GetProperties(&Props);
  331. if (FAILED(hr)) {
  332. return hr;
  333. }
  334. ALLOCATOR_PROPERTIES PropsActual;
  335. hr = SetProperties(&Props, &PropsActual);
  336. }
  337. m_pAllocator = pAllocator;
  338. return hr;
  339. }
  340. STDMETHODIMP CStream::GetAllocatorRequirements(ALLOCATOR_PROPERTIES*pProps)
  341. {
  342. // Return E_NOTIMPL to indicate that we don't have any requirement and will not accept someone
  343. // elses allocator.
  344. return E_NOTIMPL;
  345. }
  346. STDMETHODIMP CStream::ReceiveMultiple(IMediaSample **pSamples, long nSamples, long *nSamplesProcessed)
  347. {
  348. HRESULT hr = S_OK;
  349. *nSamplesProcessed = 0;
  350. while (nSamples-- > 0) {
  351. hr = Receive(pSamples[*nSamplesProcessed]);
  352. if (hr != S_OK) {
  353. break;
  354. }
  355. (*nSamplesProcessed)++;
  356. }
  357. return hr;
  358. }
  359. STDMETHODIMP CStream::ReceiveCanBlock()
  360. {
  361. return S_OK; // Pin can block if not using our allocator or using read-only samples
  362. }
  363. //
  364. // This method assumes the critical section is taken.
  365. //
  366. HRESULT CStream::ConnectThisMediaType(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
  367. {
  368. HRESULT hr = pReceivePin->ReceiveConnection(this, pmt);
  369. if (SUCCEEDED(hr)) {
  370. m_pConnectedMemInputPin = pReceivePin; // Does a magic QI here!
  371. if (!m_pConnectedMemInputPin) {
  372. hr = VFW_E_TYPE_NOT_ACCEPTED;
  373. } else {
  374. hr = ReceiveConnection(pReceivePin, pmt);
  375. if (SUCCEEDED(hr)) {
  376. hr = m_pConnectedMemInputPin->NotifyAllocator(this, TRUE);
  377. }
  378. if (SUCCEEDED(hr)) {
  379. m_pAllocator = this;
  380. m_bUsingMyAllocator = true;
  381. } else {
  382. Disconnect();
  383. }
  384. }
  385. }
  386. return hr;
  387. }
  388. STDMETHODIMP CStream::Connect(IPin * pReceivePin, const AM_MEDIA_TYPE *pmt)
  389. {
  390. HRESULT hr;
  391. AUTO_CRIT_LOCK;
  392. if (pmt) {
  393. hr = ConnectThisMediaType(pReceivePin, pmt);
  394. } else {
  395. AM_MEDIA_TYPE *pCurMediaType;
  396. hr = GetMediaType(0, &pCurMediaType);
  397. if (SUCCEEDED(hr)) {
  398. hr = ConnectThisMediaType(pReceivePin, pCurMediaType);
  399. DeleteMediaType(pCurMediaType);
  400. }
  401. }
  402. return hr;
  403. }
  404. STDMETHODIMP CStream::EnumMediaTypes(IEnumMediaTypes **ppEnum)
  405. {
  406. HRESULT hr = S_OK;
  407. CMediaTypeEnum *pNewEnum = new CComObject<CMediaTypeEnum>;
  408. if (pNewEnum == NULL) {
  409. hr = E_OUTOFMEMORY;
  410. } else {
  411. pNewEnum->Initialize(this, 0);
  412. }
  413. pNewEnum->GetControllingUnknown()->QueryInterface(IID_IEnumMediaTypes, (void **)ppEnum);
  414. return hr;
  415. }
  416. //
  417. // This method is not supported and never will be!
  418. //
  419. STDMETHODIMP CStream::ReleaseBuffer(IMediaSample *pBuffer)
  420. {
  421. return E_UNEXPECTED;
  422. };
  423. //
  424. // The caller holds the reference to the sample after this point.
  425. //
  426. HRESULT CStream::AllocSampleFromPool(
  427. const REFERENCE_TIME *pStartTime,
  428. CSample **ppSample,
  429. DWORD dwFlag
  430. )
  431. {
  432. LONGLONG llLate = 0;
  433. Lock();
  434. // Check we are committed -- This can happen after we've blocked and then
  435. // wake back up due to a decommit.
  436. if (!m_bCommitted)
  437. {
  438. Unlock();
  439. return VFW_E_NOT_COMMITTED;
  440. }
  441. //
  442. // if start time has been specified, wait as needed
  443. //
  444. if (pStartTime) {
  445. REFERENCE_TIME CurTime;
  446. if (m_pFilter->GetCurrentStreamTime(&CurTime) == S_OK) {
  447. llLate = CurTime - *pStartTime;
  448. /* Block if more than a millisecond early */
  449. if (-llLate >= 10000) {
  450. m_rtWaiting = *pStartTime;
  451. Unlock();
  452. m_pFilter->WaitUntil(*pStartTime);
  453. Lock();
  454. m_rtWaiting = 0;
  455. //
  456. // make sure we are still commited
  457. //
  458. if (!m_bCommitted)
  459. {
  460. Unlock();
  461. return VFW_E_NOT_COMMITTED;
  462. }
  463. }
  464. }
  465. }
  466. HRESULT hr = VFW_E_BUFFER_NOTSET;
  467. //
  468. // try to get a sample from the pool
  469. //
  470. CSample *pSample = m_pFirstFree;
  471. //
  472. // if no sample was readily available, wait for one.
  473. //
  474. if ( pSample == NULL)
  475. {
  476. //
  477. // if the nowait flag was set, don't bother waiting
  478. //
  479. if ( !(AM_GBF_NOWAIT & dwFlag) )
  480. {
  481. //
  482. // indicate that one more thread is waiting for a sample
  483. //
  484. m_lWaiting++;
  485. //
  486. // unlock to allow a new sample to become available while we are waiting
  487. //
  488. Unlock();
  489. //
  490. // wait for a sample to become available
  491. //
  492. DWORD dwWaitResult = WaitForSingleObject(m_hWaitFreeSem, INFINITE);
  493. //
  494. // if wait succeeded, proceed to the next iteration of the loop and
  495. // try to get a sample again.
  496. //
  497. if (WAIT_OBJECT_0 != dwWaitResult)
  498. {
  499. return VFW_E_BUFFER_NOTSET;
  500. }
  501. //
  502. // get the lock back
  503. //
  504. Lock();
  505. //
  506. // check if the allocator was decommited while we were waiting
  507. //
  508. if (!m_bCommitted)
  509. {
  510. Unlock();
  511. return VFW_E_NOT_COMMITTED;
  512. }
  513. pSample = m_pFirstFree;
  514. } // nowait flag set
  515. } // if ( pSample == NULL )
  516. //
  517. // did we get the sample after all?
  518. //
  519. if ( pSample != NULL )
  520. {
  521. //
  522. // we have the sample. our mission was successful.
  523. //
  524. hr = NOERROR;
  525. //
  526. // remove from the sample we found from the pool of available samples
  527. //
  528. m_pFirstFree = pSample->m_pNextFree;
  529. if (m_pFirstFree)
  530. {
  531. m_pFirstFree->m_pPrevFree = NULL;
  532. }
  533. else
  534. {
  535. m_pLastFree = NULL;
  536. }
  537. pSample->m_pNextFree = NULL;
  538. TM_ASSERT(pSample->m_Status == MS_S_PENDING);
  539. CHECKSAMPLELIST
  540. }
  541. Unlock();
  542. *ppSample = pSample;
  543. return hr;
  544. }
  545. void CStream::AddSampleToFreePool(CSample *pSample)
  546. {
  547. Lock();
  548. TM_ASSERT(pSample->m_pPrevFree == NULL && pSample->m_pNextFree == NULL);
  549. if (m_pFirstFree) {
  550. pSample->m_pPrevFree = m_pLastFree;
  551. m_pLastFree->m_pNextFree = pSample;
  552. } else {
  553. pSample->m_pPrevFree = NULL;
  554. m_pFirstFree = pSample;
  555. }
  556. pSample->m_pNextFree = NULL; // We know that the prev ptr is already null
  557. m_pLastFree = pSample;
  558. CHECKSAMPLELIST
  559. if (m_lWaiting > 0) {
  560. ReleaseSemaphore(m_hWaitFreeSem, 1, 0);
  561. m_lWaiting--;
  562. }
  563. Unlock();
  564. }
  565. //
  566. // The caller holds the reference to the sample after this point!
  567. //
  568. bool CStream::StealSampleFromFreePool(CSample *pSample, BOOL bAbort)
  569. {
  570. bool bWorked = false;
  571. Lock();
  572. if (m_pFirstFree) {
  573. if (m_pFirstFree == pSample) {
  574. // We'll only steal the first sample if there's nobody waiting for it right now.
  575. bool bTakeFirstFree = true;
  576. if (!bAbort && m_bCommitted) {
  577. REFERENCE_TIME CurTime;
  578. if (m_rtWaiting && m_pFilter->GetCurrentStreamTime(&CurTime) == S_OK) {
  579. bTakeFirstFree = m_rtWaiting > CurTime;
  580. }
  581. }
  582. if (bTakeFirstFree) {
  583. m_pFirstFree = pSample->m_pNextFree;
  584. if (m_pFirstFree) {
  585. m_pFirstFree->m_pPrevFree = NULL;
  586. } else {
  587. m_pLastFree = NULL;
  588. }
  589. pSample->m_pNextFree = NULL; // We know the prev ptr is already null!
  590. TM_ASSERT(pSample->m_pPrevFree == NULL);
  591. bWorked = true;
  592. }
  593. } else {
  594. if (pSample->m_pPrevFree) {
  595. pSample->m_pPrevFree->m_pNextFree = pSample->m_pNextFree;
  596. if (pSample->m_pNextFree) {
  597. pSample->m_pNextFree->m_pPrevFree = pSample->m_pPrevFree;
  598. } else {
  599. m_pLastFree = pSample->m_pPrevFree;
  600. }
  601. pSample->m_pNextFree = pSample->m_pPrevFree = NULL;
  602. bWorked = true;
  603. }
  604. }
  605. CHECKSAMPLELIST
  606. }
  607. Unlock();
  608. return bWorked;
  609. }
  610. HRESULT CStream::CheckReceiveConnectionPin(IPin * pPin)
  611. {
  612. HRESULT hr;
  613. if (!pPin) {
  614. hr = E_POINTER;
  615. } else {
  616. if (m_pConnectedPin != NULL) {
  617. hr = VFW_E_ALREADY_CONNECTED;
  618. } else {
  619. PIN_INFO pinfo;
  620. hr = pPin->QueryPinInfo(&pinfo);
  621. if (hr == NOERROR) {
  622. pinfo.pFilter->Release();
  623. if (pinfo.dir == m_Direction) {
  624. hr = VFW_E_INVALID_DIRECTION;
  625. }
  626. }
  627. }
  628. }
  629. return hr;
  630. }