Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

799 lines
24 KiB

  1. /****************************************************************************
  2. * @doc INTERNAL DIALOGS
  3. *
  4. * @module WDMStrmr.cpp | Source file for <c CWDMStreamer> class used to get a
  5. * stream of video data flowing from WDM devices.
  6. *
  7. * @comm This code is based on the VfW to WDM mapper code written by
  8. * FelixA and E-zu Wu. The original code can be found on
  9. * \\redrum\slmro\proj\wdm10\\src\image\vfw\win9x\raytube.
  10. *
  11. * Documentation by George Shaw on kernel streaming can be found in
  12. * \\popcorn\razzle1\src\spec\ks\ks.doc.
  13. *
  14. * WDM streaming capture is discussed by Jay Borseth in
  15. * \\blues\public\jaybo\WDMVCap.doc.
  16. ***************************************************************************/
  17. #include "Precomp.h"
  18. /****************************************************************************
  19. * @doc INTERNAL CWDMSTREAMERMETHOD
  20. *
  21. * @mfunc void | CWDMStreamer | CWDMStreamer | WDM filter class constructor.
  22. *
  23. * @parm CWDMPin * | pWDMVideoPin | Pointer to the kernel streaming
  24. * object we will get the frames from.
  25. ***************************************************************************/
  26. CWDMStreamer::CWDMStreamer(CWDMPin * pWDMVideoPin)
  27. {
  28. m_pWDMVideoPin = pWDMVideoPin;
  29. m_lpVHdrFirst = (LPVIDEOHDR)NULL;
  30. m_lpVHdrLast = (LPVIDEOHDR)NULL;
  31. m_fVideoOpen = FALSE;
  32. m_fStreamingStarted = FALSE;
  33. m_pBufTable = (PBUFSTRUCT)NULL;
  34. m_cntNumVidBuf = 0UL;
  35. m_idxNextVHdr = 0UL;
  36. m_hThread = NULL;
  37. m_bKillThread = FALSE;
  38. }
  39. /****************************************************************************
  40. * @doc INTERNAL CWDMSTREAMERMETHOD
  41. *
  42. * @mfunc void | CWDMStreamer | videoCallback | This function calls the
  43. * callback function provided by the appplication.
  44. *
  45. * @parm WORD | msg | Message value.
  46. *
  47. * @parm DWORD | dwParam1 | 32-bit message-dependent parameter.
  48. ***************************************************************************/
  49. void CWDMStreamer::videoCallback(WORD msg, DWORD dwParam1)
  50. {
  51. if (m_CaptureStreamParms.dwCallback)
  52. DriverCallback (m_CaptureStreamParms.dwCallback, HIWORD(m_CaptureStreamParms.dwFlags), (HDRVR) m_CaptureStreamParms.hVideo, msg, m_CaptureStreamParms.dwCallbackInst, dwParam1, 0UL);
  53. }
  54. /****************************************************************************
  55. * @doc INTERNAL CWDMSTREAMERMETHOD
  56. *
  57. * @mfunc LPVIDEOHDR | CWDMStreamer | DeQueueHeader | This function dequeues a
  58. * video buffer from the list of video buffers used for streaming.
  59. *
  60. * @rdesc Returns a valid pointer if successful, or NULL otherwise.
  61. ***************************************************************************/
  62. LPVIDEOHDR CWDMStreamer::DeQueueHeader()
  63. {
  64. FX_ENTRY("CWDMStreamer::DeQueueHeader");
  65. LPVIDEOHDR lpVHdr;
  66. if (m_pBufTable)
  67. {
  68. if (m_pBufTable[m_idxNextVHdr].fReady)
  69. {
  70. DEBUGMSG(ZONE_STREAMING, (" %s: DeQueuing idxNextVHdr (idx=%d) with data to be filled at lpVHdr=0x%08lX\r\n", _fx_, m_idxNextVHdr, m_pBufTable[m_idxNextVHdr].lpVHdr));
  71. lpVHdr = m_pBufTable[m_idxNextVHdr].lpVHdr;
  72. lpVHdr->dwFlags &= ~VHDR_INQUEUE;
  73. m_pBufTable[m_idxNextVHdr].fReady = FALSE;
  74. }
  75. else
  76. {
  77. m_idxNextVHdr++;
  78. if (m_idxNextVHdr >= m_cntNumVidBuf)
  79. m_idxNextVHdr = 0;
  80. if (m_pBufTable[m_idxNextVHdr].fReady)
  81. {
  82. DEBUGMSG(ZONE_STREAMING, (" %s: DeQueuing idxNextVHdr (idx=%d) with data to be filled at lpVHdr=0x%08lX\r\n", _fx_, m_idxNextVHdr, m_pBufTable[m_idxNextVHdr].lpVHdr));
  83. lpVHdr = m_pBufTable[m_idxNextVHdr].lpVHdr;
  84. lpVHdr->dwFlags &= ~VHDR_INQUEUE;
  85. m_pBufTable[m_idxNextVHdr].fReady = FALSE;
  86. }
  87. else
  88. {
  89. DEBUGMSG(ZONE_STREAMING, (" %s: idxNextVHdr (idx=%d) has not been returned by client\r\n", _fx_, m_idxNextVHdr));
  90. lpVHdr = NULL;
  91. }
  92. }
  93. }
  94. else
  95. {
  96. lpVHdr = m_lpVHdrFirst;
  97. if (lpVHdr) {
  98. lpVHdr->dwFlags &= ~VHDR_INQUEUE;
  99. m_lpVHdrFirst = (LPVIDEOHDR)(lpVHdr->dwReserved[0]);
  100. if (m_lpVHdrFirst == NULL)
  101. m_lpVHdrLast = NULL;
  102. }
  103. }
  104. return lpVHdr;
  105. }
  106. /****************************************************************************
  107. * @doc INTERNAL CWDMSTREAMERMETHOD
  108. *
  109. * @mfunc void | CWDMStreamer | QueueHeader | This function actually adds the
  110. * video buffer to the list of video buffers used for streaming.
  111. *
  112. * @parm LPVIDEOHDR | lpVHdr | Pointer to a <t VIDEOHDR> structure describing
  113. * a video buffer to add to the list of streaming buffers.
  114. ***************************************************************************/
  115. void CWDMStreamer::QueueHeader(LPVIDEOHDR lpVHdr)
  116. {
  117. FX_ENTRY("CWDMStreamer::QueHeader");
  118. // Initialize status flags
  119. lpVHdr->dwFlags &= ~VHDR_DONE;
  120. lpVHdr->dwFlags |= VHDR_INQUEUE;
  121. lpVHdr->dwBytesUsed = 0;
  122. // Add buffer to list
  123. if (m_pBufTable)
  124. {
  125. if (lpVHdr->dwReserved[1] < m_cntNumVidBuf)
  126. {
  127. if (m_pBufTable[lpVHdr->dwReserved[1]].lpVHdr != lpVHdr)
  128. {
  129. DEBUGMSG(ZONE_STREAMING, (" %s: index (%d) Match but lpVHdr does not(%x)\r\n", _fx_, lpVHdr->dwReserved[1], lpVHdr));
  130. }
  131. m_pBufTable[lpVHdr->dwReserved[1]].fReady = TRUE;
  132. DEBUGMSG(ZONE_STREAMING, (" %s: Buffer lpVHdr=0x%08lX was succesfully queued\r\n", _fx_, lpVHdr));
  133. }
  134. else
  135. {
  136. DEBUGMSG(ZONE_STREAMING, (" %s: lpVHdr->dwReserved[1](%d) >= m_cntNumVidBuf (%d)\r\n", _fx_, lpVHdr->dwReserved[1], m_cntNumVidBuf));
  137. }
  138. }
  139. else
  140. {
  141. *(lpVHdr->dwReserved) = NULL;
  142. if (m_lpVHdrLast)
  143. *(m_lpVHdrLast->dwReserved) = (DWORD)(LPVOID)lpVHdr;
  144. else
  145. m_lpVHdrFirst = lpVHdr;
  146. m_lpVHdrLast = lpVHdr;
  147. }
  148. }
  149. /****************************************************************************
  150. * @doc INTERNAL CWDMSTREAMERMETHOD
  151. *
  152. * @mfunc BOOL | CWDMStreamer | AddBuffer | This function adds a buffer to the
  153. * list of video buffers to be used when streaming video data from the WDM
  154. * device.
  155. *
  156. * @parm LPVIDEOHDR | lpVHdr | Pointer to a <t VIDEOHDR> structure describing
  157. * a video buffer to add to the list of streaming buffers.
  158. *
  159. * @rdesc Returns TRUE if successful, or FALSE otherwise.
  160. *
  161. * @devnote This function handles what was the DVM_STREAM_ADDBUFFER message in VfW.
  162. ***************************************************************************/
  163. BOOL CWDMStreamer::AddBuffer(LPVIDEOHDR lpVHdr)
  164. {
  165. FX_ENTRY("CWDMStreamer::AddBuffer");
  166. ASSERT(m_fVideoOpen && lpVHdr && !(lpVHdr->dwFlags & VHDR_INQUEUE));
  167. // Make sure this is a valid call
  168. if (!m_fVideoOpen)
  169. {
  170. DEBUGMSG(ZONE_STREAMING, ("%s: Buffer lpVHdr=0x%08lX can't be queued because m_fVideoOpen=FALSE\r\n", _fx_, lpVHdr));
  171. return FALSE;
  172. }
  173. if (!lpVHdr)
  174. {
  175. DEBUGMSG(ZONE_STREAMING, ("%s: Buffer lpVHdr=0x%08lX can't be queued because lpVHdr=NULL\r\n", _fx_, lpVHdr));
  176. return FALSE;
  177. }
  178. if (lpVHdr->dwFlags & VHDR_INQUEUE)
  179. {
  180. DEBUGMSG(ZONE_STREAMING, ("%s: Buffer lpVHdr=0x%08lX can't be queued because buffer is already queued\r\n", _fx_, lpVHdr));
  181. return FALSE;
  182. }
  183. // Does the size of the buffer match the size of the buffers the streaming pin will generate?
  184. if (lpVHdr->dwBufferLength < m_pWDMVideoPin->GetFrameSize())
  185. {
  186. ERRORMESSAGE(("%s: Buffer lpVHdr=0x%08lX can't be queued because the length of that buffer is too small\r\n", _fx_, lpVHdr));
  187. return FALSE;
  188. }
  189. if (!m_pBufTable)
  190. {
  191. lpVHdr->dwReserved[1] = m_cntNumVidBuf;
  192. m_cntNumVidBuf++;
  193. DEBUGMSG(ZONE_STREAMING, ("%s: Queue buffer (%d) lpVHdr=0x%08lX\r\n", _fx_, lpVHdr->dwReserved[1], lpVHdr));
  194. }
  195. QueueHeader(lpVHdr);
  196. return TRUE;
  197. }
  198. /****************************************************************************
  199. * @doc INTERNAL CWDMSTREAMERMETHOD
  200. *
  201. * @mfunc BOOL | CWDMStreamer | Stop | This function stops a stream of
  202. * video data coming from the WDM device.
  203. *
  204. * @rdesc Returns TRUE if successful, or FALSE otherwise.
  205. *
  206. * @devnote This function handles what was the DVM_STREAM_STOP message in VfW.
  207. ***************************************************************************/
  208. BOOL CWDMStreamer::Stop()
  209. {
  210. FX_ENTRY("CWDMStreamer::Stop");
  211. ASSERT(m_fVideoOpen);
  212. // Make sure this is a valid call
  213. if (!m_fVideoOpen)
  214. {
  215. DEBUGMSG(ZONE_STREAMING, ("%s: Stream is not even opened\r\n", _fx_));
  216. return FALSE;
  217. }
  218. DEBUGMSG(ZONE_STREAMING, ("%s()\r\n", _fx_));
  219. // Reset data members - stop streaming thread
  220. m_fStreamingStarted = FALSE;
  221. if (m_hThread)
  222. {
  223. DEBUGMSG(ZONE_STREAMING, ("%s: Stopping the thread\r\n", _fx_));
  224. // Signal the streaming thread to stop
  225. m_bKillThread = TRUE;
  226. // wait until thread has self-terminated, and clear the event.
  227. DEBUGMSG(ZONE_STREAMING, ("%s: WaitingForSingleObject...\r\n", _fx_));
  228. WaitForSingleObject(m_hThread, INFINITE);
  229. DEBUGMSG(ZONE_STREAMING, ("%s: ...thread stopped\r\n", _fx_));
  230. // Close the thread handle
  231. CloseHandle(m_hThread);
  232. m_hThread = NULL;
  233. // Ask the pin to stop streaming.
  234. m_pWDMVideoPin->Stop();
  235. for (UINT i=0; i<m_cntNumVidBuf; i++)
  236. {
  237. if (m_pWDMVideoBuff[i].Overlap.hEvent)
  238. {
  239. SetEvent(m_pWDMVideoBuff[i].Overlap.hEvent);
  240. CloseHandle(m_pWDMVideoBuff[i].Overlap.hEvent);
  241. m_pWDMVideoBuff[i].Overlap.hEvent = NULL;
  242. }
  243. }
  244. if (m_pWDMVideoBuff)
  245. {
  246. delete []m_pWDMVideoBuff;
  247. m_pWDMVideoBuff = (WDMVIDEOBUFF *)NULL;
  248. }
  249. }
  250. return TRUE;
  251. }
  252. /****************************************************************************
  253. * @doc INTERNAL CWDMSTREAMERMETHOD
  254. *
  255. * @mfunc BOOL | CWDMStreamer | Reset | This function resets a stream of
  256. * video data coming from the WDM device so that prepared buffer may be
  257. * freed correctly.
  258. *
  259. * @rdesc Returns TRUE if successful, or FALSE otherwise.
  260. *
  261. * @devnote This function handles what was the DVM_STREAM_RESET message in VfW.
  262. ***************************************************************************/
  263. BOOL CWDMStreamer::Reset()
  264. {
  265. LPVIDEOHDR lpVHdr;
  266. FX_ENTRY("CWDMStreamer::Reset");
  267. ASSERT(m_fVideoOpen);
  268. // Make sure this is a valid call
  269. if (!m_fVideoOpen)
  270. {
  271. DEBUGMSG(ZONE_STREAMING, ("%s: Stream is not even opened\r\n", _fx_));
  272. return FALSE;
  273. }
  274. DEBUGMSG(ZONE_STREAMING, ("%s()\r\n", _fx_));
  275. // Terminate streaming thread
  276. Stop();
  277. // Return all buffers to the application one last time
  278. while (lpVHdr = DeQueueHeader ())
  279. {
  280. lpVHdr->dwFlags |= VHDR_DONE;
  281. videoCallback(MM_DRVM_DATA, (DWORD) lpVHdr);
  282. }
  283. // Reset data members
  284. m_lpVHdrFirst = (LPVIDEOHDR)NULL;
  285. m_lpVHdrLast = (LPVIDEOHDR)NULL;
  286. if (m_pBufTable)
  287. {
  288. delete []m_pBufTable;
  289. m_pBufTable = NULL;
  290. }
  291. return TRUE;
  292. }
  293. /****************************************************************************
  294. * @doc INTERNAL CWDMSTREAMERMETHOD
  295. *
  296. * @mfunc BOOL | CWDMStreamer | Open | This function opens a stream of
  297. * video data coming from the WDM device.
  298. *
  299. * @parm LPVIDEO_STREAM_INIT_PARMS | lpStreamInitParms | Pointer to
  300. * initialization data.
  301. *
  302. * @rdesc Returns TRUE if successful, or FALSE otherwise.
  303. *
  304. * @devnote This function handles what was the DVM_STREAM_INIT message in VfW.
  305. ***************************************************************************/
  306. BOOL CWDMStreamer::Open(LPVIDEO_STREAM_INIT_PARMS lpStreamInitParms)
  307. {
  308. FX_ENTRY("CWDMStreamer::Open");
  309. ASSERT(!m_fVideoOpen);
  310. // Make sure this is a valid call
  311. if (m_fVideoOpen)
  312. {
  313. DEBUGMSG(ZONE_STREAMING, ("%s: Stream is already opened\r\n", _fx_));
  314. return FALSE;
  315. }
  316. DEBUGMSG(ZONE_STREAMING, ("%s()\r\n", _fx_));
  317. // Initialize data memmbers
  318. m_CaptureStreamParms = *lpStreamInitParms;
  319. m_fVideoOpen = TRUE;
  320. m_lpVHdrFirst = (LPVIDEOHDR)NULL;
  321. m_lpVHdrLast = (LPVIDEOHDR)NULL;
  322. m_cntNumVidBuf = 0UL;
  323. // Set frame rate on the pin
  324. m_pWDMVideoPin->SetAverageTimePerFrame(lpStreamInitParms->dwMicroSecPerFrame * 10);
  325. // Let the app know we just opened a stream
  326. videoCallback(MM_DRVM_OPEN, 0L);
  327. if (lpStreamInitParms->dwMicroSecPerFrame != 0)
  328. {
  329. DEBUGMSG(ZONE_STREAMING, ("%s: Capturing at %d frames/sec\r\n", _fx_, 100000 / lpStreamInitParms->dwMicroSecPerFrame));
  330. }
  331. return TRUE;
  332. }
  333. /****************************************************************************
  334. * @doc INTERNAL CWDMSTREAMERMETHOD
  335. *
  336. * @mfunc BOOL | CWDMStreamer | Close | This function closes the stream of
  337. * video data coming from the WDM device.
  338. *
  339. * @rdesc Returns TRUE if successful, or FALSE otherwise.
  340. *
  341. * @devnote This function handles what was the DVM_STREAM_FINI message in VfW.
  342. ***************************************************************************/
  343. BOOL CWDMStreamer::Close()
  344. {
  345. FX_ENTRY("CWDMStreamer::Close");
  346. ASSERT(m_fVideoOpen && !m_lpVHdrFirst);
  347. // Make sure this is a valid call
  348. if (!m_fVideoOpen || m_lpVHdrFirst)
  349. {
  350. DEBUGMSG(ZONE_STREAMING, ("%s: Invalid parameters\r\n", _fx_));
  351. return FALSE;
  352. }
  353. DEBUGMSG(ZONE_STREAMING, ("%s()\r\n", _fx_));
  354. // Terminate streaming thread
  355. Stop();
  356. // Reset data members
  357. m_fVideoOpen = FALSE;
  358. m_lpVHdrFirst = m_lpVHdrLast = (LPVIDEOHDR)NULL;
  359. m_idxNextVHdr = 0UL;
  360. // Release table of pointers to video buffers
  361. if (m_pBufTable)
  362. {
  363. delete []m_pBufTable;
  364. m_pBufTable = NULL;
  365. }
  366. // Let the app know that we just closed the stream
  367. videoCallback(MM_DRVM_CLOSE, 0L);
  368. return TRUE;
  369. }
  370. /****************************************************************************
  371. * @doc INTERNAL CWDMSTREAMERMETHOD
  372. *
  373. * @mfunc void | CWDMStreamer | BufferDone | This function lets the application
  374. * know that there is video data available coming from the WDM device.
  375. *
  376. * @devnote This method is called by the kernel streaming object (Pin)
  377. ***************************************************************************/
  378. void CWDMStreamer::BufferDone(LPVIDEOHDR lpVHdr)
  379. {
  380. FX_ENTRY("CWDMStreamer::BufferDone");
  381. // Make sure this is a valid call
  382. if (!m_fStreamingStarted)
  383. {
  384. DEBUGMSG(ZONE_STREAMING, ("%s: Video has not been started or just been stopped\r\n", _fx_));
  385. return;
  386. }
  387. if (lpVHdr == NULL)
  388. {
  389. // No buffers available - the app hasn't returned the buffers to us yet
  390. DEBUGMSG(ZONE_STREAMING, (" %s: Let the app know that we don't have any buffers anymore since lpVHdr=NULL\r\n", _fx_));
  391. // Let the app know something wrong happened
  392. videoCallback(MM_DRVM_ERROR, 0UL);
  393. return;
  394. }
  395. lpVHdr->dwFlags |= VHDR_DONE;
  396. // Sanity check
  397. if (lpVHdr->dwBytesUsed == 0)
  398. {
  399. DEBUGMSG(ZONE_STREAMING, (" %s: Let the app know that there is no valid data available in lpVHdr=0x%08lX\r\n", _fx_, lpVHdr));
  400. // Return frame to the pool before notifying app
  401. AddBuffer(lpVHdr);
  402. videoCallback(MM_DRVM_ERROR, 0UL);
  403. }
  404. else
  405. {
  406. DEBUGMSG(ZONE_STREAMING, (" %s: Let the app know that there is data available in lpVHdr=0x%08lX\r\n", _fx_, lpVHdr));
  407. lpVHdr->dwTimeCaptured = timeGetTime() - m_dwTimeStart;
  408. // Let the app know there's some valid video data available
  409. videoCallback(MM_DRVM_DATA, (DWORD)lpVHdr);
  410. }
  411. }
  412. /****************************************************************************
  413. * @doc INTERNAL CWDMSTREAMERMETHOD
  414. *
  415. * @mfunc BOOL | CWDMStreamer | Start | This function starts streaming
  416. * video data coming from the WDM device.
  417. *
  418. * @rdesc Returns TRUE if successful, or FALSE otherwise.
  419. *
  420. * @devnote This function handles what was the DVM_STREAM_START message in VfW.
  421. ***************************************************************************/
  422. BOOL CWDMStreamer::Start()
  423. {
  424. FX_ENTRY("CWDMStreamer::Start");
  425. ULONG i;
  426. LPVIDEOHDR lpVHdr;
  427. DWORD dwThreadID;
  428. ASSERT(m_fVideoOpen && m_pWDMVideoPin->GetAverageTimePerFrame() && !m_hThread);
  429. // Make sure this is a valid call
  430. if (!m_fVideoOpen || !m_pWDMVideoPin->GetAverageTimePerFrame() || m_hThread)
  431. {
  432. DEBUGMSG(ZONE_STREAMING, ("%s: Invalid parameters\r\n", _fx_));
  433. return FALSE;
  434. }
  435. DEBUGMSG(ZONE_STREAMING, ("%s: Streaming in %d video buffers at %d frames/sec\r\n", _fx_, m_cntNumVidBuf, 1000000 / m_pWDMVideoPin->GetAverageTimePerFrame()));
  436. // Allocate and initialize the video buffer structures
  437. m_pBufTable = (PBUFSTRUCT) new BUFSTRUCT[m_cntNumVidBuf];
  438. if (m_pBufTable)
  439. {
  440. lpVHdr = m_lpVHdrFirst;
  441. for (i = 0; i < m_cntNumVidBuf && lpVHdr; i++)
  442. {
  443. m_pBufTable[i].fReady = TRUE;
  444. m_pBufTable[i].lpVHdr = lpVHdr;
  445. lpVHdr = (LPVIDEOHDR) lpVHdr->dwReserved[0];
  446. }
  447. }
  448. else
  449. {
  450. DEBUGMSG(ZONE_STREAMING, ("%s: m_pBufTable allocation failed! AsynIO may be out of sequence\r\n", _fx_));
  451. }
  452. m_idxNextVHdr = 0UL; // 0..m_cntNumVidBuf-1
  453. m_dwTimeStart = timeGetTime();
  454. m_fStreamingStarted = TRUE;
  455. m_bKillThread = FALSE;
  456. DEBUGMSG(ZONE_STREAMING, ("%s: Creating %d read video buffers\r\n", _fx_, m_cntNumVidBuf));
  457. if (!(m_pWDMVideoBuff = (WDMVIDEOBUFF *) new WDMVIDEOBUFF[m_cntNumVidBuf]))
  458. {
  459. DEBUGMSG(ZONE_STREAMING, ("%s: m_Overlap allocation failed!\r\n", _fx_));
  460. return FALSE;
  461. }
  462. for(i=0; i<m_cntNumVidBuf; i++)
  463. {
  464. // Create the overlapped structures
  465. ZeroMemory( &(m_pWDMVideoBuff[i].Overlap), sizeof(OVERLAPPED) );
  466. m_pWDMVideoBuff[i].Overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  467. DEBUGMSG(ZONE_STREAMING, ("%s: Event %d is handle 0x%08lX\r\n", _fx_, i, m_pWDMVideoBuff[i].Overlap.hEvent));
  468. }
  469. m_dwNextToComplete=0;
  470. // Create the streaming thread
  471. m_hThread = CreateThread((LPSECURITY_ATTRIBUTES)NULL,
  472. 0,
  473. (LPTHREAD_START_ROUTINE)ThreadStub,
  474. this,
  475. CREATE_SUSPENDED,
  476. &dwThreadID);
  477. if (m_hThread == NULL)
  478. {
  479. ERRORMESSAGE(("%s: Couldn't create the thread\r\n", _fx_));
  480. for (UINT i=0; i<m_cntNumVidBuf; i++)
  481. {
  482. if (m_pWDMVideoBuff[i].Overlap.hEvent)
  483. CloseHandle(m_pWDMVideoBuff[i].Overlap.hEvent);
  484. }
  485. delete []m_pWDMVideoBuff;
  486. m_pWDMVideoBuff = (WDMVIDEOBUFF *)NULL;
  487. m_lpVHdrFirst = (LPVIDEOHDR)NULL;
  488. m_lpVHdrLast = (LPVIDEOHDR)NULL;
  489. if (m_pBufTable)
  490. {
  491. delete []m_pBufTable;
  492. m_pBufTable = NULL;
  493. }
  494. return FALSE;
  495. }
  496. SetThreadPriority(m_hThread, THREAD_PRIORITY_ABOVE_NORMAL);
  497. ResumeThread(m_hThread);
  498. DEBUGMSG(ZONE_STREAMING, ("%s: Thread created OK\r\n", _fx_));
  499. return TRUE;
  500. }
  501. /****************************************************************************
  502. * @doc INTERNAL CWDMSTREAMERMETHOD
  503. *
  504. * @mfunc BOOL | CWDMStreamer | Stream | This function does the actual
  505. * streaming.
  506. ***************************************************************************/
  507. void CWDMStreamer::Stream()
  508. {
  509. FX_ENTRY("CWDMStreamer::Stream");
  510. DEBUGMSG(ZONE_STREAMING, ("%s: Starting to process StreamingThread\r\n", _fx_));
  511. // Put the pin in streaming mode
  512. m_pWDMVideoPin->Start();
  513. // Queue all the reads
  514. for (UINT i = 0; i<m_cntNumVidBuf; i++)
  515. {
  516. QueueRead(i);
  517. }
  518. m_dwNextToComplete=0;
  519. #ifdef _DEBUG
  520. m_dwFrameCount=0;
  521. #endif
  522. BOOL bGotAFrame=FALSE;
  523. DWORD dwRes;
  524. DEBUGMSG(ZONE_STREAMING, ("\r\n%s: Starting to wait on reads to complete\r\n", _fx_));
  525. while (!m_bKillThread)
  526. {
  527. bGotAFrame = FALSE;
  528. if (m_pWDMVideoBuff[m_dwNextToComplete].fBlocking)
  529. {
  530. DEBUGMSG(ZONE_STREAMING, ("\r\n%s: Waiting on read to complete...\r\n", _fx_));
  531. // Waiting for the asynchronous read to complete
  532. dwRes = WaitForSingleObject(m_pWDMVideoBuff[m_dwNextToComplete].Overlap.hEvent, 1000*1);
  533. if (dwRes == WAIT_FAILED)
  534. {
  535. DEBUGMSG(ZONE_STREAMING, ("%s: ...we couldn't perform the wait as requested\r\n", _fx_));
  536. }
  537. if (dwRes == WAIT_OBJECT_0)
  538. {
  539. DEBUGMSG(ZONE_STREAMING, ("%s: ...wait is over - we now have a frame\r\n", _fx_));
  540. bGotAFrame = TRUE;
  541. }
  542. else
  543. {
  544. // time out waiting for frames.
  545. if (dwRes == WAIT_TIMEOUT)
  546. {
  547. DEBUGMSG(ZONE_STREAMING, ("%s: Waiting failed with timeout, last error=%d\r\n", _fx_, GetLastError()));
  548. }
  549. }
  550. }
  551. else
  552. {
  553. // We didn't have to wait - this means the read executed synchronously
  554. bGotAFrame = TRUE;
  555. }
  556. if (bGotAFrame)
  557. {
  558. DEBUGMSG(ZONE_STREAMING, ("%s: Trying to give frame #%ld to the client\r\n", _fx_, m_dwFrameCount++));
  559. LPVIDEOHDR lpVHdr;
  560. lpVHdr = m_pWDMVideoBuff[m_dwNextToComplete].pVideoHdr;
  561. if (lpVHdr)
  562. {
  563. lpVHdr->dwBytesUsed = m_pWDMVideoBuff[m_dwNextToComplete].SHGetImage.StreamHeader.DataUsed;
  564. if ((m_pWDMVideoBuff[m_dwNextToComplete].SHGetImage.FrameInfo.dwFrameFlags & 0x00f0) == KS_VIDEO_FLAG_I_FRAME)
  565. lpVHdr->dwFlags |= VHDR_KEYFRAME;
  566. }
  567. // Mark the buffer as done - signal the app
  568. BufferDone(lpVHdr);
  569. // Queue a new read
  570. QueueRead(m_dwNextToComplete);
  571. }
  572. m_dwNextToComplete++;
  573. m_dwNextToComplete %= m_cntNumVidBuf;
  574. }
  575. DEBUGMSG(ZONE_STREAMING, ("%s: End of the streaming thread\r\n", _fx_));
  576. ExitThread(0);
  577. }
  578. /****************************************************************************
  579. * @doc INTERNAL CWDMSTREAMERMETHOD
  580. *
  581. * @mfunc BOOL | CWDMStreamer | QueueRead | This function queues a read
  582. * operation on a video streaming pin.
  583. *
  584. * @parm DWORD | dwIndex | Index of the video structure in read buffer.
  585. *
  586. * @rdesc Returns TRUE if successful, or FALSE otherwise.
  587. ***************************************************************************/
  588. BOOL CWDMStreamer::QueueRead(DWORD dwIndex)
  589. {
  590. FX_ENTRY("CWDMStreamer::QueueRead");
  591. DWORD cbReturned;
  592. BOOL bShouldBlock = FALSE;
  593. DEBUGMSG(ZONE_STREAMING, ("\r\n%s: Queue read buffer %d on pin handle 0x%08lX\r\n", _fx_, dwIndex, m_pWDMVideoPin->GetPinHandle()));
  594. // Get a buffer from the queue of video buffers
  595. m_pWDMVideoBuff[dwIndex].pVideoHdr = DeQueueHeader();
  596. if (m_pWDMVideoBuff[dwIndex].pVideoHdr)
  597. {
  598. ZeroMemory(&m_pWDMVideoBuff[dwIndex].SHGetImage, sizeof(m_pWDMVideoBuff[dwIndex].SHGetImage));
  599. m_pWDMVideoBuff[dwIndex].SHGetImage.StreamHeader.Size = sizeof (KS_HEADER_AND_INFO);
  600. m_pWDMVideoBuff[dwIndex].SHGetImage.FrameInfo.ExtendedHeaderSize = sizeof (KS_FRAME_INFO);
  601. m_pWDMVideoBuff[dwIndex].SHGetImage.StreamHeader.Data = m_pWDMVideoBuff[dwIndex].pVideoHdr->lpData;
  602. m_pWDMVideoBuff[dwIndex].SHGetImage.StreamHeader.FrameExtent = m_pWDMVideoPin->GetFrameSize();
  603. // Submit the read
  604. BOOL bRet = DeviceIoControl(m_pWDMVideoPin->GetPinHandle(), IOCTL_KS_READ_STREAM, &m_pWDMVideoBuff[dwIndex].SHGetImage, sizeof(m_pWDMVideoBuff[dwIndex].SHGetImage), &m_pWDMVideoBuff[dwIndex].SHGetImage, sizeof(m_pWDMVideoBuff[dwIndex].SHGetImage), &cbReturned, &m_pWDMVideoBuff[dwIndex].Overlap);
  605. if (!bRet)
  606. {
  607. DWORD dwErr = GetLastError();
  608. switch(dwErr)
  609. {
  610. case ERROR_IO_PENDING:
  611. DEBUGMSG(ZONE_STREAMING, ("%s: An overlapped IO is going to take place\r\n", _fx_));
  612. bShouldBlock = TRUE;
  613. break;
  614. // Something bad happened
  615. default:
  616. DEBUGMSG(ZONE_STREAMING, ("%s: DeviceIoControl() failed badly dwErr=%d\r\n", _fx_, dwErr));
  617. break;
  618. }
  619. }
  620. else
  621. {
  622. DEBUGMSG(ZONE_STREAMING, ("%s: Overlapped IO won't take place - no need to wait\r\n", _fx_));
  623. }
  624. }
  625. else
  626. {
  627. DEBUGMSG(ZONE_STREAMING, ("%s: We won't queue the read - no buffer available\r\n", _fx_));
  628. }
  629. m_pWDMVideoBuff[dwIndex].fBlocking = bShouldBlock;
  630. return bShouldBlock;
  631. }
  632. /****************************************************************************
  633. * @doc INTERNAL CWDMSTREAMERMETHOD
  634. *
  635. * @mfunc BOOL | CWDMStreamer | ThreadStub | Thread stub.
  636. ***************************************************************************/
  637. LPTHREAD_START_ROUTINE CWDMStreamer::ThreadStub(CWDMStreamer *pCWDMStreamer)
  638. {
  639. FX_ENTRY("CWDMStreamer::ThreadStub");
  640. DEBUGMSG(ZONE_STREAMING, ("%s: Thread stub called, starting streaming...\r\n", _fx_));
  641. pCWDMStreamer->Stream();
  642. DEBUGMSG(ZONE_STREAMING, ("%s: ...capture thread has stopped\r\n", _fx_));
  643. return(0);
  644. }