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.

589 lines
15 KiB

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