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.

632 lines
17 KiB

  1. /*
  2. Copyright (c) 1998-1999 Microsoft Corporation
  3. */
  4. // Sample.cpp: implementation of the Sample class.
  5. //
  6. //////////////////////////////////////////////////////////////////////
  7. /*
  8. Overview of handling of sample states
  9. -------------------------------------
  10. A sample can be in one of the following states:
  11. -- Application - Application owned - not updated
  12. -- Stream owned (in our queue)
  13. -- Owned by a filter for update
  14. The state can only change under the protection of the stream
  15. critical section.
  16. Stealing a sample occurs on WaitForCompletion with NOUPDATEOK or
  17. ABORT specified.
  18. Also, not that WaitForCompletion turns off continuous updates
  19. if and of the 3 flags are set.
  20. Action
  21. Owner Update GetBuffer Receive Release Steal
  22. completion sample
  23. ---------------------------------------------------------------------------
  24. Application Note 3 Impossible Impossible Impossible Application
  25. ---------------------------------------------------------------------------
  26. Stream Invalid Filter Impossible Impossible Application
  27. ---------------------------------------------------------------------------
  28. Filter Invalid Impossible Note 1 Note 2 Filter
  29. ---------------------------------------------------------------------------
  30. Notes:
  31. 1. New owner is
  32. Stream for continuous update
  33. Application otherwise
  34. 2. New owner is
  35. Application if at end of stream or abort
  36. Stream otherwise
  37. 3. If at end of stream status is MS_S_ENDOFSTREAM
  38. */
  39. #include "stdafx.h"
  40. CSample::CSample() :
  41. m_pStream(NULL),
  42. m_pMediaSample(NULL),
  43. m_hUserHandle(NULL),
  44. m_UserAPC(NULL),
  45. m_Status(S_OK),
  46. m_MediaSampleIoStatus(S_OK),
  47. m_pNextFree(NULL),
  48. m_pPrevFree(NULL),
  49. m_hCompletionEvent(NULL),
  50. m_bReceived(false),
  51. m_bTemp(false)
  52. {
  53. LOG((MSP_TRACE, "CSample::CSample[%p] - enter", this));
  54. LOG((MSP_TRACE, "CSample::CSample - exit"));
  55. }
  56. HRESULT CSample::InitSample(CStream *pStream, bool bIsInternalSample)
  57. {
  58. // this check at beginning apparently added by Rajeev
  59. TM_ASSERT(NULL != pStream);
  60. if ( pStream == NULL )
  61. {
  62. return E_INVALIDARG;
  63. }
  64. // original DShow code starts here
  65. if (!m_pMediaSample) {
  66. m_pMediaSample = new CMediaSampleTM(this);
  67. if (!m_pMediaSample) {
  68. return E_OUTOFMEMORY;
  69. }
  70. }
  71. m_pStream = pStream;
  72. m_bInternal = bIsInternalSample;
  73. if (!bIsInternalSample) {
  74. pStream->Lock();
  75. pStream->m_cAllocated++;
  76. pStream->Unlock();
  77. //
  78. // Hold a strong reference to the stream and the multi media stream.
  79. // The pMMStream can not change once we have incremented m_cAllocted on the stream, so we're sure that this
  80. // addref and the final release of the multi-media stream won't change.
  81. //
  82. pStream->GetControllingUnknown()->AddRef();
  83. if (pStream->m_pMMStream) {
  84. pStream->m_pMMStream->AddRef();
  85. }
  86. }
  87. TCHAR *ptczEventName = NULL;
  88. #if DBG
  89. //
  90. // in debug build, use named events
  91. //
  92. TCHAR tszEventName[MAX_PATH];
  93. _stprintf(tszEventName,
  94. _T("CSample_CompletionEvent_pid[0x%lx]_CStream[%p]_"),
  95. GetCurrentProcessId(), this);
  96. ptczEventName = &tszEventName[0];
  97. #endif
  98. TM_ASSERT(NULL == m_hCompletionEvent);
  99. m_hCompletionEvent = CreateEvent(NULL, FALSE, TRUE, ptczEventName);
  100. return m_hCompletionEvent ? S_OK : E_OUTOFMEMORY;
  101. }
  102. void CSample::FinalRelease(void)
  103. {
  104. CompletionStatus(COMPSTAT_WAIT | COMPSTAT_ABORT, INFINITE);
  105. }
  106. CSample::~CSample()
  107. {
  108. LOG((MSP_TRACE, "CSample::~CSample[%p] - enter", this));
  109. CompletionStatus(COMPSTAT_NOUPDATEOK | COMPSTAT_ABORT, 0);
  110. if (m_hCompletionEvent) {
  111. CloseHandle(m_hCompletionEvent);
  112. }
  113. if (!m_bInternal) {
  114. m_pStream->Lock();
  115. IMultiMediaStream *pMMStream = m_pStream->m_pMMStream;
  116. m_pStream->m_cAllocated--;
  117. if (m_pStream->m_bStopIfNoSamples && m_pStream->m_cAllocated == 0) {
  118. if (m_pStream->m_pAllocator) {
  119. m_pStream->m_pAllocator->Decommit();
  120. }
  121. }
  122. m_pStream->Unlock(); // Unlock it before we release it!
  123. if (pMMStream) {
  124. pMMStream->Release();
  125. }
  126. m_pStream->GetControllingUnknown()->Release();
  127. }
  128. if (m_pMediaSample) {
  129. delete m_pMediaSample;
  130. }
  131. LOG((MSP_TRACE, "CSample::~CSample - exit"));
  132. }
  133. //
  134. // IStreamSample
  135. //
  136. STDMETHODIMP CSample::GetMediaStream(IMediaStream **ppMediaStream)
  137. {
  138. LOG((MSP_TRACE, "IStreamSample::GetMediaStream(%p)",
  139. ppMediaStream));
  140. *ppMediaStream = m_pStream;
  141. (*ppMediaStream)->AddRef();
  142. return S_OK;
  143. }
  144. STDMETHODIMP CSample::GetSampleTimes(STREAM_TIME *pStartTime, STREAM_TIME *pEndTime,
  145. STREAM_TIME *pCurrentTime)
  146. {
  147. LOG((MSP_TRACE, "IStreamSample::GetSampleTimes(%p, %p, %p)",
  148. pStartTime, pEndTime, pCurrentTime));
  149. REFERENCE_TIME rtSegmentStart = m_pStream->m_rtSegmentStart;
  150. m_pMediaSample->GetTime(pStartTime, pEndTime);
  151. if (pStartTime) {
  152. *pStartTime += rtSegmentStart;
  153. }
  154. if (pEndTime) {
  155. *pEndTime += rtSegmentStart;
  156. }
  157. HRESULT hr = S_OK;
  158. if (pCurrentTime)
  159. {
  160. //
  161. // if the filter is still around, ask it for stream time
  162. //
  163. m_pStream->Lock();
  164. if (NULL != m_pStream->m_pFilter)
  165. {
  166. m_pStream->m_pFilter->GetCurrentStreamTime(pCurrentTime);
  167. }
  168. else
  169. {
  170. LOG((MSP_WARN, "CSample::GetSampleTimes - m_pStream->m_pFilter is NULL. returning VFW_E_NOT_IN_GRAPH"));
  171. hr = VFW_E_NOT_IN_GRAPH;
  172. }
  173. m_pStream->Unlock();
  174. }
  175. return hr;
  176. }
  177. STDMETHODIMP CSample::SetSampleTimes(const STREAM_TIME *pStartTime, const STREAM_TIME *pEndTime)
  178. {
  179. LOG((MSP_TRACE, "IStreamSample::SetSampleTimes(%p, %p)",
  180. pStartTime, pEndTime));
  181. /* Only settable for writable streams */
  182. if (m_pStream->m_StreamType != STREAMTYPE_WRITE) {
  183. return MS_E_INVALIDSTREAMTYPE;
  184. }
  185. /* Since writable streams can't be seeked we don't need to
  186. compensate here for any seek offsets
  187. */
  188. return m_pMediaSample->SetTime((REFERENCE_TIME *)pStartTime, (REFERENCE_TIME *)pEndTime);
  189. }
  190. STDMETHODIMP CSample::Update(DWORD dwFlags, HANDLE hEvent, PAPCFUNC pfnAPC, DWORD_PTR dwAPCData)
  191. {
  192. LOG((MSP_TRACE, "IStreamSample::Update(0x%8.8X, %p, %p, %p)",
  193. dwFlags, hEvent, pfnAPC, dwAPCData));
  194. LOCK_SAMPLE;
  195. HRESULT hr = InternalUpdate(dwFlags, hEvent, pfnAPC, dwAPCData);
  196. UNLOCK_SAMPLE;
  197. if (S_OK == hr) {
  198. hr = CompletionStatus(COMPSTAT_WAIT, INFINITE);
  199. }
  200. return hr;
  201. }
  202. void CSample::FinalMediaSampleRelease(void)
  203. {
  204. if (m_bTemp) {
  205. GetControllingUnknown()->Release();
  206. return;
  207. }
  208. LOCK_SAMPLE;
  209. HRESULT hrStatus = m_MediaSampleIoStatus;
  210. if (hrStatus != S_OK) {
  211. m_MediaSampleIoStatus = S_OK; // Reset this here so we don't need to reset it every time.
  212. } else {
  213. if (!m_bReceived) {
  214. if (m_pStream->m_bEndOfStream) {
  215. hrStatus = MS_S_ENDOFSTREAM;
  216. } else {
  217. if (m_bWantAbort) {
  218. m_bWantAbort = false;
  219. hrStatus = E_ABORT;
  220. } else {
  221. // Upstream guy just allocated the sample and never used it! -- Keep it pending.
  222. hrStatus = MS_S_PENDING;
  223. }
  224. }
  225. }
  226. }
  227. UNLOCK_SAMPLE;
  228. SetCompletionStatus(hrStatus);
  229. // DANGER! Sample may be dead right here
  230. }
  231. //
  232. // Set the sample's status and signal completion if necessary.
  233. //
  234. // Note that when the application has been signalled by whatever method
  235. // the application can immediately turn around on another thread
  236. // and Release() the sample. This is most likely when the completion
  237. // status is set from the quartz thread that's pushing the data.
  238. //
  239. // Should we actually keep a reference count on the sample ourselves while
  240. // it's being updated? Currently we don't.
  241. //
  242. HRESULT CSample::SetCompletionStatus(HRESULT hrStatus)
  243. {
  244. LOCK_SAMPLE;
  245. TM_ASSERT(m_Status == MS_S_PENDING);
  246. if (hrStatus == MS_S_PENDING || (hrStatus == S_OK && m_bContinuous)) {
  247. m_pStream->AddSampleToFreePool(this);
  248. UNLOCK_SAMPLE;
  249. } else {
  250. HANDLE handle = m_hUserHandle;
  251. PAPCFUNC pfnAPC = m_UserAPC;
  252. DWORD_PTR dwAPCData = m_dwptrUserAPCData;
  253. m_hUserHandle = m_UserAPC = NULL;
  254. m_dwptrUserAPCData = 0;
  255. m_Status = hrStatus;
  256. HANDLE hCompletionEvent = m_hCompletionEvent;
  257. UNLOCK_SAMPLE;
  258. // DANGER DANGER - sample can go away here
  259. SetEvent(hCompletionEvent);
  260. if (pfnAPC) {
  261. QueueUserAPC(pfnAPC, handle, dwAPCData);
  262. BOOL bClose = CloseHandle(handle);
  263. TM_ASSERT(bClose);
  264. } else {
  265. if (handle) {
  266. SetEvent(handle);
  267. }
  268. }
  269. }
  270. return hrStatus;
  271. }
  272. STDMETHODIMP CSample::CompletionStatus(DWORD dwFlags, DWORD dwMilliseconds)
  273. {
  274. LOG((MSP_TRACE, "IStreamSample::CompletionStatus(0x%8.8X, 0x%8.8X)",
  275. dwFlags, dwMilliseconds));
  276. LOCK_SAMPLE;
  277. HRESULT hr = m_Status;
  278. if (hr == MS_S_PENDING) {
  279. if (dwFlags & (COMPSTAT_NOUPDATEOK | COMPSTAT_ABORT) ||
  280. (m_bContinuous && m_bModified && (dwFlags & COMPSTAT_WAIT))) {
  281. m_bContinuous = false;
  282. if (dwFlags & COMPSTAT_ABORT) {
  283. m_bWantAbort = true; // Set this so we won't add it back to the free pool if released
  284. }
  285. if (m_pStream->StealSampleFromFreePool(this, dwFlags & COMPSTAT_ABORT)) {
  286. UNLOCK_SAMPLE;
  287. return SetCompletionStatus(m_bModified ? S_OK : MS_S_NOUPDATE);
  288. } // If doesn't work then return MS_S_PENDING unless we're told to wait!
  289. }
  290. if (dwFlags & COMPSTAT_WAIT) {
  291. m_bContinuous = false; // Make sure it will complete!
  292. UNLOCK_SAMPLE;
  293. WaitForSingleObject(m_hCompletionEvent, dwMilliseconds);
  294. LOCK_SAMPLE;
  295. hr = m_Status;
  296. }
  297. }
  298. UNLOCK_SAMPLE;
  299. return hr;
  300. }
  301. void CSample::CopyFrom(CSample *pSrcSample)
  302. {
  303. m_bModified = true;
  304. m_pMediaSample->m_rtStartTime = pSrcSample->m_pMediaSample->m_rtStartTime;
  305. m_pMediaSample->m_rtEndTime = pSrcSample->m_pMediaSample->m_rtEndTime;
  306. m_pMediaSample->m_dwFlags = pSrcSample->m_pMediaSample->m_dwFlags;
  307. m_pMediaSample->m_bIsPreroll = pSrcSample->m_pMediaSample->m_bIsPreroll;
  308. }
  309. void CSample::CopyFrom(IMediaSample *pSrcMediaSample)
  310. {
  311. m_bModified = true;
  312. pSrcMediaSample->GetTime(&m_pMediaSample->m_rtStartTime, &m_pMediaSample->m_rtEndTime);
  313. m_pMediaSample->m_dwFlags = (pSrcMediaSample->IsSyncPoint() == S_OK) ? 0 : AM_GBF_NOTASYNCPOINT;
  314. m_pMediaSample->m_dwFlags |= (pSrcMediaSample->IsDiscontinuity() == S_OK) ? AM_GBF_PREVFRAMESKIPPED : 0;
  315. m_pMediaSample->m_bIsPreroll = (pSrcMediaSample->IsPreroll() == S_OK);
  316. }
  317. /////////////////////////////////////////////////////////////////////////////////////////////
  318. //
  319. // Implementation of IMediaSample
  320. //
  321. CMediaSampleTM::CMediaSampleTM(CSample *pSample) :
  322. m_pSample(pSample),
  323. m_cRef(0),
  324. m_dwFlags(0),
  325. m_bIsPreroll(FALSE),
  326. m_pMediaType(NULL),
  327. m_rtStartTime(0),
  328. m_rtEndTime(0)
  329. {
  330. LOG((MSP_TRACE, "CMediaSampleTM::CMediaSampleTM[%p] - enter", this));
  331. LOG((MSP_TRACE, "CMediaSampleTM::CMediaSampleTM - exit"));
  332. }
  333. CMediaSampleTM::~CMediaSampleTM()
  334. {
  335. LOG((MSP_TRACE, "CMediaSampleTM::~CMediaSampleTM[%p] - enter", this));
  336. if (m_pMediaType) {
  337. DeleteMediaType(m_pMediaType);
  338. }
  339. LOG((MSP_TRACE, "CMediaSampleTM::~CMediaSampleTM - exit"));
  340. }
  341. STDMETHODIMP CMediaSampleTM::QueryInterface(REFIID riid, void ** ppv)
  342. {
  343. if (riid==IID_IUnknown || riid==IID_IMediaSample) {
  344. *ppv = (IMediaSample *)this;
  345. AddRef();
  346. return S_OK;
  347. }
  348. return E_NOINTERFACE;
  349. }
  350. STDMETHODIMP_(ULONG) CMediaSampleTM::AddRef()
  351. {
  352. return InterlockedIncrement(&m_cRef);
  353. }
  354. STDMETHODIMP_(ULONG) CMediaSampleTM::Release()
  355. {
  356. long lRef = InterlockedDecrement(&m_cRef);
  357. if (lRef == 0) {
  358. m_pSample->FinalMediaSampleRelease();
  359. }
  360. return lRef;
  361. }
  362. STDMETHODIMP CMediaSampleTM::GetPointer(BYTE ** ppBuffer)
  363. {
  364. return m_pSample->MSCallback_GetPointer(ppBuffer);
  365. }
  366. STDMETHODIMP_(LONG) CMediaSampleTM::GetSize(void)
  367. {
  368. return m_pSample->MSCallback_GetSize();
  369. }
  370. // get the stream time at which this sample should start and finish.
  371. // changed from original CMediaSampleTM code -- borrowed from amovie\sdk\classes\base\amfilter.cpp
  372. STDMETHODIMP
  373. CMediaSampleTM::GetTime(
  374. REFERENCE_TIME * pTimeStart, // put time here
  375. REFERENCE_TIME * pTimeEnd
  376. )
  377. {
  378. if ( TM_IsBadWritePtr(pTimeStart, sizeof(REFERENCE_TIME) )) return E_INVALIDARG;
  379. if ( TM_IsBadWritePtr(pTimeEnd, sizeof(REFERENCE_TIME) )) return E_INVALIDARG;
  380. if (!(m_dwFlags & AM_SAMPLE_STOPVALID)) {
  381. if (!(m_dwFlags & AM_SAMPLE_TIMEVALID)) {
  382. return VFW_E_SAMPLE_TIME_NOT_SET;
  383. } else {
  384. *pTimeStart = m_rtStartTime;
  385. // Make sure old stuff works
  386. *pTimeEnd = m_rtStartTime + 1;
  387. return VFW_S_NO_STOP_TIME;
  388. }
  389. }
  390. *pTimeStart = m_rtStartTime;
  391. *pTimeEnd = m_rtEndTime;
  392. return NOERROR;
  393. }
  394. // Set the stream time at which this sample should start and finish.
  395. // NULL pointers means the time is reset
  396. // changed from original CMediaSampleTM code -- borrowed from sdk\classes\base\amfilter.cpp
  397. STDMETHODIMP
  398. CMediaSampleTM::SetTime(
  399. REFERENCE_TIME * pTimeStart,
  400. REFERENCE_TIME * pTimeEnd
  401. )
  402. {
  403. if ( (pTimeStart != NULL) && IsBadReadPtr(pTimeStart, sizeof(REFERENCE_TIME) ) )
  404. {
  405. LOG((MSP_ERROR, "IMediaSample::SetTime - bad pointer pTimeStart"));
  406. return E_POINTER;
  407. }
  408. if ( (pTimeEnd != NULL) && IsBadReadPtr(pTimeEnd, sizeof(REFERENCE_TIME) ) )
  409. {
  410. LOG((MSP_ERROR, "IMediaSample::SetTime - bad pointer pTimeEnd"));
  411. return E_POINTER;
  412. }
  413. if ( pTimeStart == NULL ) {
  414. TM_ASSERT(pTimeEnd == NULL);
  415. m_dwFlags &= ~(AM_SAMPLE_TIMEVALID | AM_SAMPLE_STOPVALID);
  416. } else {
  417. if ( pTimeEnd == NULL ) {
  418. m_rtStartTime = *pTimeStart;
  419. m_dwFlags |= AM_SAMPLE_TIMEVALID;
  420. m_dwFlags &= ~AM_SAMPLE_STOPVALID;
  421. } else {
  422. TM_ASSERT(*pTimeEnd >= *pTimeStart);
  423. m_rtStartTime = *pTimeStart;
  424. m_rtEndTime = *pTimeEnd;
  425. m_dwFlags |= AM_SAMPLE_TIMEVALID | AM_SAMPLE_STOPVALID;
  426. }
  427. }
  428. return NOERROR;
  429. }
  430. STDMETHODIMP CMediaSampleTM::IsSyncPoint(void)
  431. {
  432. return ((m_dwFlags & AM_GBF_NOTASYNCPOINT) ? S_FALSE : S_OK);
  433. }
  434. STDMETHODIMP CMediaSampleTM::SetSyncPoint(BOOL bIsSyncPoint)
  435. {
  436. if (bIsSyncPoint) {
  437. m_dwFlags &= (~AM_GBF_NOTASYNCPOINT);
  438. } else {
  439. m_dwFlags |= AM_GBF_NOTASYNCPOINT;
  440. }
  441. return NOERROR;
  442. }
  443. STDMETHODIMP CMediaSampleTM::IsPreroll(void)
  444. {
  445. return (m_bIsPreroll ? S_OK : S_FALSE);
  446. }
  447. STDMETHODIMP CMediaSampleTM::SetPreroll(BOOL bIsPreroll)
  448. {
  449. m_bIsPreroll = bIsPreroll;
  450. return S_OK;
  451. }
  452. STDMETHODIMP_(LONG) CMediaSampleTM::GetActualDataLength(void)
  453. {
  454. return m_pSample->MSCallback_GetActualDataLength();
  455. }
  456. STDMETHODIMP CMediaSampleTM::SetActualDataLength(LONG lActual)
  457. {
  458. return m_pSample->MSCallback_SetActualDataLength(lActual);
  459. }
  460. STDMETHODIMP CMediaSampleTM::GetMediaType(AM_MEDIA_TYPE **ppMediaType)
  461. {
  462. if (m_pMediaType) {
  463. *ppMediaType = CreateMediaType(m_pMediaType);
  464. if (*ppMediaType) {
  465. return NOERROR;
  466. } else {
  467. return E_OUTOFMEMORY;
  468. }
  469. } else {
  470. *ppMediaType = NULL;
  471. return S_FALSE;
  472. }
  473. }
  474. STDMETHODIMP CMediaSampleTM::SetMediaType(AM_MEDIA_TYPE *pMediaType)
  475. {
  476. if ((!m_pMediaType && !pMediaType) ||
  477. (m_pMediaType && pMediaType && IsEqualMediaType(*m_pMediaType, *pMediaType))) {
  478. return S_OK;
  479. }
  480. if (!m_pSample->MSCallback_AllowSetMediaTypeOnMediaSample()) {
  481. return VFW_E_TYPE_NOT_ACCEPTED;
  482. }
  483. if (m_pMediaType) {
  484. DeleteMediaType(m_pMediaType);
  485. }
  486. m_pMediaType = NULL;
  487. if (pMediaType) {
  488. m_pMediaType = CreateMediaType(pMediaType);
  489. if (!m_pMediaType) {
  490. return E_OUTOFMEMORY;
  491. }
  492. }
  493. return S_OK;
  494. }
  495. STDMETHODIMP CMediaSampleTM::IsDiscontinuity(void)
  496. {
  497. return ((m_dwFlags & AM_GBF_PREVFRAMESKIPPED) ? S_OK : S_FALSE);
  498. }
  499. STDMETHODIMP CMediaSampleTM::SetDiscontinuity(BOOL bDiscontinuity)
  500. {
  501. if (bDiscontinuity) {
  502. m_dwFlags |= AM_GBF_PREVFRAMESKIPPED;
  503. } else {
  504. m_dwFlags &= (~AM_GBF_PREVFRAMESKIPPED);
  505. }
  506. return NOERROR;
  507. }
  508. STDMETHODIMP CMediaSampleTM::GetMediaTime(LONGLONG * pTimeStart, LONGLONG * pTimeEnd)
  509. {
  510. return E_NOTIMPL;
  511. }
  512. STDMETHODIMP CMediaSampleTM::SetMediaTime(LONGLONG * pTimeStart, LONGLONG * pTimeEnd)
  513. {
  514. return E_NOTIMPL;
  515. }