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.

929 lines
31 KiB

  1. // This file adds native support for streaming WDM video capture
  2. // PhilF-: This needs to be rewritten. You should have two classes
  3. // (CVfWCap & WDMCap) that derive from the same capture class instead
  4. // of those C-like functions...
  5. #include "Precomp.h"
  6. void
  7. WDMFrameCallback(
  8. HVIDEO hvideo,
  9. WORD wMsg,
  10. HCAPDEV hcd, // (Actually refdata)
  11. LPCAPBUFFER lpcbuf, // (Actually LPVIDEOHDR) Only returned from MM_DRVM_DATA!
  12. DWORD dwParam2
  13. );
  14. // Globals
  15. extern HINSTANCE g_hInst;
  16. /****************************************************************************
  17. * @doc EXTERNAL WDMFUNC
  18. *
  19. * @func BOOL | WDMGetDevices | This function enumerates the installed WDM video
  20. * capture devices and adds them to the list of VfW capture devices.
  21. *
  22. * @parm PDWORD | [OUT] pdwOverallCPUUsage | Specifies a pointer to a DWORD to
  23. * receive the current CPU usage.
  24. *
  25. * @rdesc Returns TRUE on success, and FALSE otherwise.
  26. *
  27. * @devnote MSDN references:
  28. * DirectX 5, DirectX Media, DirectShow, Application Developer's Guide
  29. * "Enumerate and Access Hardware Devices in DirectShow Applications"
  30. ***************************************************************************/
  31. BOOL WDMGetDevices(void)
  32. {
  33. HRESULT hr;
  34. ICreateDevEnum *pCreateDevEnum;
  35. IEnumMoniker *pEm;
  36. FX_ENTRY("WDMGetDevices");
  37. // First, create a system hardware enumerator
  38. // This call loads the following DLLs - total 1047 KBytes!!!:
  39. // 'C:\WINDOWS\SYSTEM\DEVENUM.DLL' = 60 KBytes
  40. // 'C:\WINDOWS\SYSTEM\RPCRT4.DLL' = 316 KBytes
  41. // 'C:\WINDOWS\SYSTEM\CFGMGR32.DLL' = 44 KBytes
  42. // 'C:\WINDOWS\SYSTEM\WINSPOOL.DRV' = 23 KBytes
  43. // 'C:\WINDOWS\SYSTEM\COMDLG32.DLL' = 180 KBytes
  44. // 'C:\WINDOWS\SYSTEM\LZ32.DLL' = 24 KBytes
  45. // 'C:\WINDOWS\SYSTEM\SETUPAPI.DLL' = 400 KBytes
  46. // According to LonnyM, there's no way to go around SETUPAPI.DLL
  47. // when dealing with PnP device interfaces....
  48. if ((CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pCreateDevEnum)) != S_OK)
  49. {
  50. return FALSE;
  51. }
  52. // Second, create an enumerator for a specific type of hardware device: video capture cards only
  53. hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEm, CDEF_BYPASS_CLASS_MANAGER);
  54. pCreateDevEnum->Release();
  55. // Third, enumerate the list itself
  56. if (hr == S_OK)
  57. {
  58. ULONG cFetched;
  59. IMoniker *pM;
  60. IPropertyBag *pPropBag = 0;
  61. hr = pEm->Reset();
  62. while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
  63. {
  64. pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
  65. if (pPropBag)
  66. {
  67. VARIANT var;
  68. LPINTERNALCAPDEV lpcd;
  69. if (!(lpcd = (LPINTERNALCAPDEV)LocalAlloc(LPTR, sizeof (INTERNALCAPDEV))))
  70. {
  71. ERRORMESSAGE(("%s: Failed to allocate an INTERNALCAPDEV buffer\r\n", _fx_));
  72. break; // break from the WHILE loop
  73. }
  74. // Get friendly name of the device
  75. var.vt = VT_BSTR;
  76. if ((hr = pPropBag->Read(L"FriendlyName", &var, 0)) == S_OK)
  77. {
  78. WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, lpcd->szDeviceDescription, MAX_PATH, 0, 0);
  79. SysFreeString(var.bstrVal);
  80. }
  81. else
  82. LoadString(g_hInst, IDS_UNKNOWN_DEVICE_NAME, lpcd->szDeviceDescription, CCHMAX(lpcd->szDeviceDescription));
  83. // Get DevicePath of the device
  84. hr = pPropBag->Read(L"DevicePath", &var, 0);
  85. if (hr == S_OK)
  86. {
  87. WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, lpcd->szDeviceName, MAX_PATH, 0, 0);
  88. SysFreeString(var.bstrVal);
  89. // There's no reg key for version information for WDM devices
  90. // Those devices can't be disabled from the MM control panel
  91. // lpcd->dwFlags |= CAPTURE_DEVICE_DISABLED;
  92. // Mark device as a WDM device
  93. lpcd->dwFlags |= WDM_CAPTURE_DEVICE;
  94. g_aCapDevices[g_cDevices] = lpcd;
  95. g_aCapDevices[g_cDevices]->nDeviceIndex = g_cDevices;
  96. g_cDevices++;
  97. }
  98. }
  99. pPropBag->Release();
  100. pM->Release();
  101. }
  102. pEm->Release();
  103. }
  104. return TRUE;
  105. }
  106. /****************************************************************************
  107. * @doc EXTERNAL WDMFUNC
  108. *
  109. * @func BOOL | WDMOpenDevice | This function opens a WDM video capture
  110. * devices and adds them to the list of VfW capture devices.
  111. *
  112. * @parm DWORD | [IN] dwDeviceID | Specifies the ID of the device to open.
  113. *
  114. * @rdesc Returns TRUE on success, and FALSE otherwise.
  115. ***************************************************************************/
  116. BOOL WDMOpenDevice(DWORD dwDeviceID)
  117. {
  118. FX_ENTRY("WDMOpenDevice");
  119. DEBUGMSG(ZONE_INIT, ("%s: dwDeviceID=%ld\r\n", _fx_, dwDeviceID));
  120. ASSERT(g_cDevices && (dwDeviceID <= (DWORD)g_cDevices) && (lstrlen(g_aCapDevices[dwDeviceID]->szDeviceName) != 0));
  121. // Validate globals and parameters
  122. if (!g_cDevices)
  123. {
  124. SetLastError(ERROR_DCAP_BAD_INSTALL);
  125. return FALSE;
  126. }
  127. if ((dwDeviceID > (DWORD)g_cDevices) || (lstrlen(g_aCapDevices[dwDeviceID]->szDeviceName) == 0))
  128. {
  129. SetLastError(ERROR_INVALID_PARAMETER);
  130. return FALSE;
  131. }
  132. // Open streaming class driver
  133. CWDMPin *pCWDMPin;
  134. if (!(pCWDMPin = new CWDMPin(dwDeviceID)))
  135. {
  136. ERRORMESSAGE(("%s: Insufficient resource or fail to create CWDMPin class\r\n", _fx_));
  137. return FALSE;
  138. }
  139. else
  140. {
  141. // Open the WDM driver and create a video pin
  142. if (!pCWDMPin->OpenDriverAndPin())
  143. {
  144. goto Error0;
  145. }
  146. }
  147. // Create video stream on the pin
  148. CWDMStreamer *pCWDMStreamer;
  149. if (!(pCWDMStreamer = new CWDMStreamer(pCWDMPin)))
  150. {
  151. ERRORMESSAGE(("%s: Insufficient resource or fail to create CWDMStreamer\r\n", _fx_));
  152. goto Error0;
  153. }
  154. g_aCapDevices[dwDeviceID]->pCWDMPin = (PVOID)pCWDMPin;
  155. g_aCapDevices[dwDeviceID]->pCWDMStreamer = (PVOID)pCWDMStreamer;
  156. return TRUE;
  157. Error0:
  158. delete pCWDMPin;
  159. g_aCapDevices[dwDeviceID]->pCWDMPin = (PVOID)NULL;
  160. g_aCapDevices[dwDeviceID]->pCWDMStreamer = (PVOID)NULL;
  161. return FALSE;
  162. }
  163. /****************************************************************************
  164. * @doc EXTERNAL WDMFUNC
  165. *
  166. * @func BOOL | WDMCloseDevice | This function closes a WDM video capture
  167. * device.
  168. *
  169. * @parm DWORD | [IN] dwDeviceID | Specifies the ID of the device to close.
  170. *
  171. * @rdesc Returns TRUE on success, and FALSE otherwise.
  172. ***************************************************************************/
  173. BOOL WDMCloseDevice(DWORD dwDeviceID)
  174. {
  175. FX_ENTRY("WDMCloseDevice");
  176. DEBUGMSG(ZONE_INIT, ("%s: dwDeviceID=%ld\r\n", _fx_, dwDeviceID));
  177. ASSERT(g_cDevices && (dwDeviceID <= (DWORD)g_cDevices));
  178. // Validate globals and parameters
  179. if (!g_cDevices)
  180. {
  181. SetLastError(ERROR_DCAP_BAD_INSTALL);
  182. return FALSE;
  183. }
  184. if ((dwDeviceID > (DWORD)g_cDevices))
  185. {
  186. SetLastError(ERROR_INVALID_PARAMETER);
  187. return FALSE;
  188. }
  189. // Close video channel
  190. if (g_aCapDevices[dwDeviceID]->pCWDMStreamer)
  191. {
  192. delete ((CWDMStreamer *)g_aCapDevices[dwDeviceID]->pCWDMStreamer);
  193. g_aCapDevices[dwDeviceID]->pCWDMStreamer = (PVOID)NULL;
  194. }
  195. // Close driver and pin
  196. if (g_aCapDevices[dwDeviceID]->pCWDMPin)
  197. {
  198. delete ((CWDMPin *)g_aCapDevices[dwDeviceID]->pCWDMPin);
  199. g_aCapDevices[dwDeviceID]->pCWDMPin = (PVOID)NULL;
  200. }
  201. return TRUE;
  202. }
  203. /****************************************************************************
  204. * @doc EXTERNAL WDMFUNC
  205. *
  206. * @func BOOL | WDMGetVideoFormatSize | This function returns the size of the
  207. * structure used to describe the video format.
  208. *
  209. * @parm DWORD | [IN] dwDeviceID | Specifies the ID of the device to query.
  210. *
  211. * @rdesc Always returns the size of a BITMAPINFOHEADER structure.
  212. ***************************************************************************/
  213. DWORD WDMGetVideoFormatSize(DWORD dwDeviceID)
  214. {
  215. FX_ENTRY("WDMGetVideoFormatSize");
  216. DEBUGMSG(ZONE_INIT, ("%s: dwDeviceID=%ld\r\n", _fx_, dwDeviceID));
  217. ASSERT(g_cDevices && (dwDeviceID <= (DWORD)g_cDevices) && g_aCapDevices[dwDeviceID]->pCWDMPin);
  218. // Validate globals and parameters
  219. if (!g_cDevices)
  220. {
  221. SetLastError(ERROR_DCAP_BAD_INSTALL);
  222. return FALSE;
  223. }
  224. if ((dwDeviceID > (DWORD)g_cDevices) || (!g_aCapDevices[dwDeviceID]->pCWDMPin))
  225. {
  226. SetLastError(ERROR_INVALID_PARAMETER);
  227. return FALSE;
  228. }
  229. DEBUGMSG(ZONE_INIT, ("%s: return size=%ld\r\n", _fx_, (DWORD)sizeof(BITMAPINFOHEADER)));
  230. // Return size of BITMAPINFOHEADER structure
  231. return (DWORD)sizeof(BITMAPINFOHEADER);
  232. }
  233. /****************************************************************************
  234. * @doc EXTERNAL WDMFUNC
  235. *
  236. * @func BOOL | WDMGetVideoFormat | This function returns the structure used
  237. * to describe the video format.
  238. *
  239. * @parm DWORD | [IN] dwDeviceID | Specifies the ID of the device to query.
  240. *
  241. * @parm DWORD | [OUT] pbmih | Specifies a pointer to a BITMAPINFOHEADER
  242. * structure to receive the video format.
  243. *
  244. * @rdesc Returns TRUE on success, and FALSE otherwise.
  245. ***************************************************************************/
  246. BOOL WDMGetVideoFormat(DWORD dwDeviceID, PBITMAPINFOHEADER pbmih)
  247. {
  248. FX_ENTRY("WDMGetVideoFormat");
  249. DEBUGMSG(ZONE_INIT, ("%s: dwDeviceID=%ld, pbmih=0x%08lX\r\n", _fx_, dwDeviceID, pbmih));
  250. ASSERT(g_cDevices && (dwDeviceID <= (DWORD)g_cDevices) && g_aCapDevices[dwDeviceID]->pCWDMPin && pbmih);
  251. // Validate globals and parameters
  252. if (!g_cDevices)
  253. {
  254. SetLastError(ERROR_DCAP_BAD_INSTALL);
  255. return FALSE;
  256. }
  257. if ((dwDeviceID > (DWORD)g_cDevices) || (!g_aCapDevices[dwDeviceID]->pCWDMPin) || !pbmih)
  258. {
  259. SetLastError(ERROR_INVALID_PARAMETER);
  260. return FALSE;
  261. }
  262. // Make sure the size information is correct
  263. if (!pbmih->biSize)
  264. pbmih->biSize = WDMGetVideoFormatSize(dwDeviceID);
  265. // Get the BITMAPINFOHEADER structure
  266. if ((((CWDMPin *)g_aCapDevices[dwDeviceID]->pCWDMPin)->GetBitmapInfo((PKS_BITMAPINFOHEADER)pbmih, (WORD)pbmih->biSize)))
  267. {
  268. DEBUGMSG(ZONE_INIT, ("%s: return\r\n biSize=%ld\r\n biWidth=%ld\r\n biHeight=%ld\r\n biPlanes=%ld\r\n biBitCount=%ld\r\n biCompression=%ld\r\n biSizeImage=%ld\r\n", _fx_, pbmih->biSize, pbmih->biWidth, pbmih->biHeight, pbmih->biPlanes, pbmih->biBitCount, pbmih->biCompression, pbmih->biSizeImage));
  269. return TRUE;
  270. }
  271. else
  272. {
  273. ERRORMESSAGE(("%s: failed!!!\r\n", _fx_));
  274. return FALSE;
  275. }
  276. }
  277. /****************************************************************************
  278. * @doc EXTERNAL WDMFUNC
  279. *
  280. * @func BOOL | WDMSetVideoFormat | This function sets the video format on
  281. * a WDM video capture device.
  282. *
  283. * @parm DWORD | [IN] dwDeviceID | Specifies the ID of the device to initialize.
  284. *
  285. * @parm DWORD | [OUT] pbmih | Specifies a pointer to a BITMAPINFOHEADER
  286. * structure describing the video format.
  287. *
  288. * @rdesc Returns TRUE on success, and FALSE otherwise.
  289. ***************************************************************************/
  290. BOOL WDMSetVideoFormat(DWORD dwDeviceID, PBITMAPINFOHEADER pbmih)
  291. {
  292. FX_ENTRY("WDMSetVideoFormat");
  293. DEBUGMSG(ZONE_INIT, ("%s: dwDeviceID=%ld, pbmih=0x%08lX\r\n", _fx_, dwDeviceID, pbmih));
  294. ASSERT(g_cDevices && (dwDeviceID <= (DWORD)g_cDevices) && g_aCapDevices[dwDeviceID]->pCWDMPin && pbmih && pbmih->biSize);
  295. // Validate globals and parameters
  296. if (!g_cDevices)
  297. {
  298. SetLastError(ERROR_DCAP_BAD_INSTALL);
  299. return FALSE;
  300. }
  301. if ((dwDeviceID > (DWORD)g_cDevices) || (!g_aCapDevices[dwDeviceID]->pCWDMPin) || !pbmih ||!pbmih->biSize)
  302. {
  303. SetLastError(ERROR_INVALID_PARAMETER);
  304. return FALSE;
  305. }
  306. // Set the BITMAPINFOHEADER on the device
  307. if (((CWDMPin *)g_aCapDevices[dwDeviceID]->pCWDMPin)->SetBitmapInfo((PKS_BITMAPINFOHEADER)pbmih))
  308. {
  309. DEBUGMSG(ZONE_INIT, ("%s: return\r\n biSize=%ld\r\n biWidth=%ld\r\n biHeight=%ld\r\n biPlanes=%ld\r\n biBitCount=%ld\r\n biCompression=%ld\r\n biSizeImage=%ld\r\n", _fx_, pbmih->biSize, pbmih->biWidth, pbmih->biHeight, pbmih->biPlanes, pbmih->biBitCount, pbmih->biCompression, pbmih->biSizeImage));
  310. return TRUE;
  311. }
  312. else
  313. {
  314. // PhilF-: This sometimes fail, but we keep on streaming... fix that
  315. ERRORMESSAGE(("%s: failed!!!\r\n", _fx_));
  316. return FALSE;
  317. }
  318. }
  319. /****************************************************************************
  320. * @doc EXTERNAL WDMFUNC
  321. *
  322. * @func BOOL | WDMGetVideoFormat | This function returns the structure used
  323. * to describe the video format.
  324. *
  325. * @parm DWORD | [IN] dwDeviceID | Specifies the ID of the device to query.
  326. *
  327. * @parm DWORD | [OUT] pbmih | Specifies a pointer to a BITMAPINFOHEADER
  328. * structure to receive the video format.
  329. *
  330. * @rdesc Returns TRUE on success, and FALSE otherwise.
  331. ***************************************************************************/
  332. BOOL WDMGetVideoPalette(DWORD dwDeviceID, CAPTUREPALETTE* lpcp, DWORD dwcbSize)
  333. {
  334. FX_ENTRY("WDMGetVideoPalette");
  335. DEBUGMSG(ZONE_INIT, ("%s: dwDeviceID=%ld, lpcp=0x%08lX\r\n", _fx_, dwDeviceID, lpcp));
  336. ASSERT(g_cDevices && (dwDeviceID <= (DWORD)g_cDevices) && g_aCapDevices[dwDeviceID]->pCWDMPin && lpcp);
  337. // Validate globals and parameters
  338. if (!g_cDevices)
  339. {
  340. SetLastError(ERROR_DCAP_BAD_INSTALL);
  341. return FALSE;
  342. }
  343. if ((dwDeviceID > (DWORD)g_cDevices) || (!g_aCapDevices[dwDeviceID]->pCWDMPin) || !lpcp)
  344. {
  345. SetLastError(ERROR_INVALID_PARAMETER);
  346. return FALSE;
  347. }
  348. // Get the palette information
  349. if ((((CWDMPin *)g_aCapDevices[dwDeviceID]->pCWDMPin)->GetPaletteInfo(lpcp, dwcbSize)))
  350. {
  351. DEBUGMSG(ZONE_INIT, ("%s: succeeded\r\n", _fx_));
  352. return TRUE;
  353. }
  354. else
  355. {
  356. ERRORMESSAGE(("%s: failed!!!\r\n", _fx_));
  357. return FALSE;
  358. }
  359. }
  360. /****************************************************************************
  361. * @doc EXTERNAL WDMFUNC
  362. *
  363. * @func BOOL | WDMInitializeExternalVideoStream | This function initializes
  364. * an input video stream on the external video channel of a WDM video
  365. * capture device.
  366. *
  367. * @parm DWORD | [IN] dwDeviceID | Specifies the ID of the device to initialize.
  368. *
  369. * @rdesc Returns TRUE on success, and FALSE otherwise.
  370. ***************************************************************************/
  371. BOOL WDMInitializeExternalVideoStream(DWORD dwDeviceID)
  372. {
  373. FX_ENTRY("WDMInitializeExternalVideoStream");
  374. DEBUGMSG(ZONE_INIT, ("%s: dwDeviceID=%ld\r\n", _fx_, dwDeviceID));
  375. DEBUGMSG(ZONE_INIT, ("%s: succeeded\r\n", _fx_));
  376. return TRUE;
  377. }
  378. /****************************************************************************
  379. * @doc EXTERNAL WDMFUNC
  380. *
  381. * @func BOOL | WDMInitializeVideoStream | This function initializes
  382. * an input video stream on the videoin channel of a WDM video capture
  383. * device.
  384. *
  385. * @parm DWORD | [IN] dwDeviceID | Specifies the ID of the device to initialize.
  386. *
  387. * @rdesc Returns TRUE on success, and FALSE otherwise.
  388. ***************************************************************************/
  389. BOOL WDMInitializeVideoStream(HCAPDEV hcd, DWORD dwDeviceID, DWORD dwMicroSecPerFrame)
  390. {
  391. FX_ENTRY("WDMInitializeVideoStream");
  392. DEBUGMSG(ZONE_INIT, ("%s: dwDeviceID=%ld, FPS=%ld\r\n", _fx_, dwDeviceID, 1000000UL / dwMicroSecPerFrame));
  393. VIDEO_STREAM_INIT_PARMS vsip = {0};
  394. ASSERT(g_cDevices && (dwDeviceID <= (DWORD)g_cDevices) && g_aCapDevices[dwDeviceID]->pCWDMStreamer);
  395. // Validate globals and parameters
  396. if (!g_cDevices)
  397. {
  398. SetLastError(ERROR_DCAP_BAD_INSTALL);
  399. return FALSE;
  400. }
  401. if ((dwDeviceID > (DWORD)g_cDevices) || (!g_aCapDevices[dwDeviceID]->pCWDMStreamer))
  402. {
  403. SetLastError(ERROR_INVALID_PARAMETER);
  404. return FALSE;
  405. }
  406. // Initialize channel
  407. vsip.dwMicroSecPerFrame = dwMicroSecPerFrame;
  408. vsip.dwCallback = (DWORD)WDMFrameCallback;
  409. vsip.dwCallbackInst = (DWORD)hcd;
  410. vsip.dwFlags = CALLBACK_FUNCTION;
  411. // vsip.hVideo = (DWORD)hvideo;
  412. if ((((CWDMStreamer *)g_aCapDevices[dwDeviceID]->pCWDMStreamer)->Open(&vsip)))
  413. {
  414. DEBUGMSG(ZONE_INIT, ("%s: succeeded\r\n", _fx_));
  415. return TRUE;
  416. }
  417. else
  418. {
  419. ERRORMESSAGE(("%s: failed!!!\r\n", _fx_));
  420. return FALSE;
  421. }
  422. }
  423. /****************************************************************************
  424. * @doc EXTERNAL WDMFUNC
  425. *
  426. * @func BOOL | WDMUnInitializeVideoStream | This function requests a WDM
  427. * video capture device to close a capture stream on the videoin channel.
  428. *
  429. * @parm DWORD | [IN] dwDeviceID | Specifies the ID of the device to initialize.
  430. *
  431. * @rdesc Returns TRUE on success, and FALSE otherwise.
  432. ***************************************************************************/
  433. BOOL WDMUnInitializeVideoStream(DWORD dwDeviceID)
  434. {
  435. FX_ENTRY("WDMUnInitializeVideoStream");
  436. DEBUGMSG(ZONE_INIT, ("%s: dwDeviceID=%ld\r\n", _fx_, dwDeviceID));
  437. ASSERT(g_cDevices && (dwDeviceID <= (DWORD)g_cDevices) && g_aCapDevices[dwDeviceID]->pCWDMStreamer);
  438. // Validate globals and parameters
  439. if (!g_cDevices)
  440. {
  441. SetLastError(ERROR_DCAP_BAD_INSTALL);
  442. return FALSE;
  443. }
  444. if ((dwDeviceID > (DWORD)g_cDevices) || (!g_aCapDevices[dwDeviceID]->pCWDMStreamer))
  445. {
  446. SetLastError(ERROR_INVALID_PARAMETER);
  447. return FALSE;
  448. }
  449. // Close streaming on channel
  450. if ((((CWDMStreamer *)g_aCapDevices[dwDeviceID]->pCWDMStreamer)->Close()))
  451. {
  452. DEBUGMSG(ZONE_INIT, ("%s: succeeded\r\n", _fx_));
  453. return TRUE;
  454. }
  455. else
  456. {
  457. ERRORMESSAGE(("%s: failed!!!\r\n", _fx_));
  458. return FALSE;
  459. }
  460. }
  461. /****************************************************************************
  462. * @doc EXTERNAL WDMFUNC
  463. *
  464. * @func BOOL | WDMVideoStreamStart | This function requests a WDM video
  465. * capture device to start a video stream.
  466. *
  467. * @parm DWORD | [IN] dwDeviceID | Specifies the ID of the device to start.
  468. *
  469. * @rdesc Returns TRUE on success, and FALSE otherwise.
  470. ***************************************************************************/
  471. BOOL WDMVideoStreamStart(DWORD dwDeviceID)
  472. {
  473. FX_ENTRY("WDMVideoStreamStart");
  474. DEBUGMSG(ZONE_INIT, ("%s: dwDeviceID=%ld\r\n", _fx_, dwDeviceID));
  475. ASSERT(g_cDevices && (dwDeviceID <= (DWORD)g_cDevices) && g_aCapDevices[dwDeviceID]->pCWDMStreamer);
  476. // Validate globals and parameters
  477. if (!g_cDevices)
  478. {
  479. SetLastError(ERROR_DCAP_BAD_INSTALL);
  480. return FALSE;
  481. }
  482. if ((dwDeviceID > (DWORD)g_cDevices) || (!g_aCapDevices[dwDeviceID]->pCWDMStreamer))
  483. {
  484. SetLastError(ERROR_INVALID_PARAMETER);
  485. return FALSE;
  486. }
  487. // Start streaming
  488. if ((((CWDMStreamer *)g_aCapDevices[dwDeviceID]->pCWDMStreamer)->Start()))
  489. {
  490. DEBUGMSG(ZONE_INIT, ("%s: succeeded\r\n", _fx_));
  491. return TRUE;
  492. }
  493. else
  494. {
  495. ERRORMESSAGE(("%s: failed!!!\r\n", _fx_));
  496. return FALSE;
  497. }
  498. }
  499. /****************************************************************************
  500. * @doc EXTERNAL WDMFUNC
  501. *
  502. * @func BOOL | WDMVideoStreamStop | This function requests a WDM video
  503. * capture device to stop a video stream.
  504. *
  505. * @parm DWORD | [IN] dwDeviceID | Specifies the ID of the device to freeze.
  506. *
  507. * @rdesc Returns TRUE on success, and FALSE otherwise.
  508. ***************************************************************************/
  509. BOOL WDMVideoStreamStop(DWORD dwDeviceID)
  510. {
  511. FX_ENTRY("WDMVideoStreamStop");
  512. DEBUGMSG(ZONE_INIT, ("%s: dwDeviceID=%ld\r\n", _fx_, dwDeviceID));
  513. ASSERT(g_cDevices && (dwDeviceID <= (DWORD)g_cDevices) && g_aCapDevices[dwDeviceID]->pCWDMStreamer);
  514. // Validate globals and parameters
  515. if (!g_cDevices)
  516. {
  517. SetLastError(ERROR_DCAP_BAD_INSTALL);
  518. return FALSE;
  519. }
  520. if ((dwDeviceID > (DWORD)g_cDevices) || (!g_aCapDevices[dwDeviceID]->pCWDMStreamer))
  521. {
  522. SetLastError(ERROR_INVALID_PARAMETER);
  523. return FALSE;
  524. }
  525. // Stop streaming
  526. if ((((CWDMStreamer *)g_aCapDevices[dwDeviceID]->pCWDMStreamer)->Stop()))
  527. {
  528. DEBUGMSG(ZONE_INIT, ("%s: succeeded\r\n", _fx_));
  529. return TRUE;
  530. }
  531. else
  532. {
  533. ERRORMESSAGE(("%s: failed!!!\r\n", _fx_));
  534. return FALSE;
  535. }
  536. }
  537. /****************************************************************************
  538. * @doc EXTERNAL WDMFUNC
  539. *
  540. * @func BOOL | WDMVideoStreamReset | This function resets a WDM video capture
  541. * devie to stop input of a capture stream and return all buffers to the
  542. * client.
  543. *
  544. * @parm DWORD | [IN] dwDeviceID | Specifies the ID of the device to reset.
  545. *
  546. * @rdesc Returns TRUE on success, and FALSE otherwise.
  547. ***************************************************************************/
  548. BOOL WDMVideoStreamReset(DWORD dwDeviceID)
  549. {
  550. FX_ENTRY("WDMVideoStreamReset");
  551. DEBUGMSG(ZONE_INIT, ("%s: dwDeviceID=%ld\r\n", _fx_, dwDeviceID));
  552. ASSERT(g_cDevices && (dwDeviceID <= (DWORD)g_cDevices) && g_aCapDevices[dwDeviceID]->pCWDMStreamer);
  553. // Validate globals and parameters
  554. if (!g_cDevices)
  555. {
  556. SetLastError(ERROR_DCAP_BAD_INSTALL);
  557. return FALSE;
  558. }
  559. if ((dwDeviceID > (DWORD)g_cDevices) || (!g_aCapDevices[dwDeviceID]->pCWDMStreamer))
  560. {
  561. SetLastError(ERROR_INVALID_PARAMETER);
  562. return FALSE;
  563. }
  564. // Reset streaming
  565. if ((((CWDMStreamer *)g_aCapDevices[dwDeviceID]->pCWDMStreamer)->Reset()))
  566. {
  567. DEBUGMSG(ZONE_INIT, ("%s: succeeded\r\n", _fx_));
  568. return TRUE;
  569. }
  570. else
  571. {
  572. ERRORMESSAGE(("%s: failed!!!\r\n", _fx_));
  573. return FALSE;
  574. }
  575. }
  576. /****************************************************************************
  577. * @doc EXTERNAL WDMFUNC
  578. *
  579. * @func BOOL | WDMVideoStreamAddBuffer | This function requests a WDM video
  580. * capture device to add an empty input buffer to its input buffer queue.
  581. *
  582. * @parm DWORD | [IN] dwDeviceID | Specifies the ID of the device to initialize.
  583. *
  584. * @rdesc Returns TRUE on success, and FALSE otherwise.
  585. ***************************************************************************/
  586. BOOL WDMVideoStreamAddBuffer(DWORD dwDeviceID, PVOID pBuff)
  587. {
  588. FX_ENTRY("WDMVideoStreamAddBuffer");
  589. DEBUGMSG(ZONE_STREAMING, (" %s: dwDeviceID=%ld, pBuff=0x%08lX\r\n", _fx_, dwDeviceID, pBuff));
  590. ASSERT(g_cDevices && pBuff && (dwDeviceID <= (DWORD)g_cDevices) && g_aCapDevices[dwDeviceID]->pCWDMStreamer);
  591. // Validate globals and parameters
  592. if (!g_cDevices)
  593. {
  594. SetLastError(ERROR_DCAP_BAD_INSTALL);
  595. return FALSE;
  596. }
  597. if (!pBuff || (dwDeviceID > (DWORD)g_cDevices) || (!g_aCapDevices[dwDeviceID]->pCWDMStreamer))
  598. {
  599. SetLastError(ERROR_INVALID_PARAMETER);
  600. return FALSE;
  601. }
  602. // Reset streaming
  603. if ((((CWDMStreamer *)g_aCapDevices[dwDeviceID]->pCWDMStreamer)->AddBuffer((LPVIDEOHDR)pBuff)))
  604. {
  605. DEBUGMSG(ZONE_STREAMING, (" %s: succeeded\r\n", _fx_));
  606. return TRUE;
  607. }
  608. else
  609. {
  610. ERRORMESSAGE((" %s: failed!!!\r\n", _fx_));
  611. return FALSE;
  612. }
  613. }
  614. /****************************************************************************
  615. * @doc EXTERNAL WDMFUNC
  616. *
  617. * @func BOOL | WDMGetFrame | This function requests a WDM video
  618. * capture device to transfer a single frame to or from the video device.
  619. *
  620. * @parm DWORD | [IN] dwDeviceID | Specifies the ID of the device to request.
  621. *
  622. * @parm PVOID | [OUT] pBuff | Specifies a pointer to a <t VIDEOHDR> structure.
  623. *
  624. * @rdesc Returns TRUE on success, and FALSE otherwise.
  625. ***************************************************************************/
  626. BOOL WDMGetFrame(DWORD dwDeviceID, PVOID pBuff)
  627. {
  628. FX_ENTRY("WDMGetFrame");
  629. DEBUGMSG(ZONE_STREAMING, ("%s: dwDeviceID=%ld, pBuff=0x%08lX\r\n", _fx_, dwDeviceID, pBuff));
  630. LPVIDEOHDR lpVHdr = (LPVIDEOHDR)pBuff;
  631. ASSERT(g_cDevices && pBuff && (dwDeviceID <= (DWORD)g_cDevices) && g_aCapDevices[dwDeviceID]->pCWDMPin);
  632. // Validate globals and parameters
  633. if (!g_cDevices)
  634. {
  635. SetLastError(ERROR_DCAP_BAD_INSTALL);
  636. return FALSE;
  637. }
  638. if (!pBuff || (dwDeviceID > (DWORD)g_cDevices) || (!g_aCapDevices[dwDeviceID]->pCWDMPin))
  639. {
  640. SetLastError(ERROR_INVALID_PARAMETER);
  641. return FALSE;
  642. }
  643. // Get the frame from the device
  644. if (((CWDMPin *)g_aCapDevices[dwDeviceID]->pCWDMPin)->GetFrame(lpVHdr))
  645. return TRUE;
  646. else
  647. return FALSE;
  648. }
  649. /****************************************************************************
  650. * @doc EXTERNAL WDMFUNC
  651. *
  652. * @func BOOL | WDMShowSettingsDialog | This function puts up a property
  653. * sheet with a VideoProcAmp and CameraControl page for a WDM video capture
  654. * device.
  655. *
  656. * @parm DWORD | [IN] dwDeviceID | Specifies the ID of the device to request.
  657. *
  658. * @rdesc Returns TRUE on success, and FALSE otherwise.
  659. ***************************************************************************/
  660. BOOL WDMShowSettingsDialog(DWORD dwDeviceID, HWND hWndParent)
  661. {
  662. PROPSHEETHEADER Psh;
  663. HPROPSHEETPAGE Pages[MAX_PAGES];
  664. FX_ENTRY("WDMShowSettingsDialog");
  665. DEBUGMSG(ZONE_STREAMING, ("%s: dwDeviceID=%ld\r\n", _fx_, dwDeviceID));
  666. ASSERT(g_cDevices && (dwDeviceID <= (DWORD)g_cDevices) && g_aCapDevices[dwDeviceID]->pCWDMPin);
  667. // Validate globals and parameters
  668. if (!g_cDevices)
  669. {
  670. SetLastError(ERROR_DCAP_BAD_INSTALL);
  671. return FALSE;
  672. }
  673. if ((dwDeviceID > (DWORD)g_cDevices) || (!g_aCapDevices[dwDeviceID]->pCWDMPin))
  674. {
  675. SetLastError(ERROR_INVALID_PARAMETER);
  676. return FALSE;
  677. }
  678. // Initialize property sheet header and common controls
  679. Psh.dwSize = sizeof(Psh);
  680. Psh.dwFlags = PSH_DEFAULT;
  681. Psh.hInstance = g_hInst;
  682. Psh.hwndParent = hWndParent;
  683. Psh.pszCaption = g_aCapDevices[dwDeviceID]->szDeviceDescription;
  684. Psh.nPages = 0;
  685. Psh.nStartPage = 0;
  686. Psh.pfnCallback = NULL;
  687. Psh.phpage = Pages;
  688. // Create the video settings property page and add it to the video settings sheet
  689. CWDMDialog VideoSettings(IDD_VIDEO_SETTINGS, NumVideoSettings, PROPSETID_VIDCAP_VIDEOPROCAMP, g_VideoSettingControls, g_VideoSettingsHelpIDs, (CWDMPin *)g_aCapDevices[dwDeviceID]->pCWDMPin);
  690. if (Pages[Psh.nPages] = VideoSettings.Create())
  691. Psh.nPages++;
  692. // Create the camera control property page and add it to the video settings sheet
  693. CWDMDialog CamControl(IDD_CAMERA_CONTROL, NumCameraControls, PROPSETID_VIDCAP_CAMERACONTROL, g_CameraControls, g_CameraControlsHelpIDs, (CWDMPin *)g_aCapDevices[dwDeviceID]->pCWDMPin);
  694. if (Pages[Psh.nPages] = CamControl.Create())
  695. Psh.nPages++;
  696. // Put up the property sheet
  697. if (Psh.nPages && PropertySheet(&Psh) >= 0)
  698. return TRUE;
  699. else
  700. return FALSE;
  701. }
  702. void
  703. WDMFrameCallback(
  704. HVIDEO hvideo,
  705. WORD wMsg,
  706. HCAPDEV hcd, // (Actually refdata)
  707. LPCAPBUFFER lpcbuf, // (Actually LPVIDEOHDR) Only returned from MM_DRVM_DATA!
  708. DWORD dwParam2
  709. )
  710. {
  711. FX_ENTRY("WDMFrameCallback");
  712. DEBUGMSG(ZONE_CALLBACK, (" %s: wMsg=%s, hcd=0x%08lX, lpcbuf=0x%08lX, hcd->hevWait=0x%08lX\r\n", _fx_, (wMsg == MM_DRVM_OPEN) ? "MM_DRVM_OPEN" : (wMsg == MM_DRVM_CLOSE) ? "MM_DRVM_CLOSE" : (wMsg == MM_DRVM_ERROR) ? "MM_DRVM_ERROR" : (wMsg == MM_DRVM_DATA) ? "MM_DRVM_DATA" : "MM_DRVM_?????", hcd, lpcbuf, hcd->hevWait));
  713. // If it's not a data ready message, just set the event and get out.
  714. // The reason we do this is that if we get behind and start getting a stream
  715. // of MM_DRVM_ERROR messages (usually because we're stopped in the debugger),
  716. // we want to make sure we are getting events so we get restarted to handle
  717. // the frames that are 'stuck.'
  718. if (wMsg != MM_DRVM_DATA)
  719. {
  720. DEBUGMSG(ZONE_CALLBACK, (" %s: Setting hcd->hevWait - no data\r\n", _fx_));
  721. SetEvent(hcd->hevWait);
  722. return;
  723. }
  724. //--------------------
  725. // Buffer ready queue:
  726. // We maintain a doubly-linked list of our buffers so that we can buffer up
  727. // multiple ready frames when the app isn't ready to handle them. Two things
  728. // complicate what ought to be a very simple thing: (1) Thunking issues: the pointers
  729. // used on the 16-bit side are 16:16 (2) Interrupt time issues: the FrameCallback
  730. // gets called at interrupt time. GetNextReadyBuffer must handle the fact that
  731. // buffers get added to the list asynchronously.
  732. //
  733. // To handle this, the scheme implemented here is to have a double-linked list
  734. // of buffers with all insertions and deletions happening in FrameCallback
  735. // (interrupt time). This allows the GetNextReadyBuffer routine to simply
  736. // find the previous block on the list any time it needs a new buffer without
  737. // fear of getting tromped (as would be the case if it had to dequeue buffers).
  738. // The FrameCallback routine is responsible to dequeue blocks that GetNextReadyBuffer
  739. // is done with. Dequeueing is simple since we don't need to unlink the blocks:
  740. // no code ever walks the list! All we have to do is move the tail pointer back up
  741. // the list. All the pointers, head, tail, next, prev, are all 16:16 pointers
  742. // since all the list manipulation is on the 16-bit side AND because MapSL is
  743. // much more efficient and safer than MapLS since MapLS has to allocate selectors.
  744. //--------------------
  745. // Move the tail back to skip all buffers already used.
  746. // Note that there is no need to actually unhook the buffer pointers since no one
  747. // ever walks the list!
  748. // This makes STRICT assumptions that the current pointer will always be earlier in
  749. // the list than the tail and that the tail will never be NULL unless the
  750. // current pointer is too.
  751. while (hcd->lpTail != hcd->lpCurrent)
  752. hcd->lpTail = hcd->lpTail->lpPrev;
  753. // If all buffers have been used, then the tail pointer will fall off the list.
  754. // This is normal and the most common code path. In this event, just set the head
  755. // to NULL as the list is now empty.
  756. if (!hcd->lpTail)
  757. hcd->lpHead = NULL;
  758. // Add the new buffer to the ready queue
  759. lpcbuf->lpNext = hcd->lpHead;
  760. lpcbuf->lpPrev = NULL;
  761. if (hcd->lpHead)
  762. hcd->lpHead->lpPrev = lpcbuf;
  763. else
  764. hcd->lpTail = lpcbuf;
  765. hcd->lpHead = lpcbuf;
  766. #if 1
  767. if (hcd->lpCurrent) {
  768. if (!(hcd->dwFlags & HCAPDEV_STREAMING_PAUSED)) {
  769. // if client hasn't consumed last frame, then release it
  770. lpcbuf = hcd->lpCurrent;
  771. hcd->lpCurrent = hcd->lpCurrent->lpPrev;
  772. DEBUGMSG(ZONE_CALLBACK, (" %s: We already have current buffer (lpcbuf=0x%08lX). Returning this buffer to driver. Set new current buffer hcd->lpCurrent=0x%08lX\r\n", _fx_, lpcbuf, hcd->lpCurrent));
  773. if (!WDMVideoStreamAddBuffer(hcd->nDeviceIndex, (PVOID)lpcbuf))
  774. {
  775. ERRORMESSAGE((" %s: Attempt to reuse unconsumed buffer failed\r\n", _fx_));
  776. }
  777. }
  778. }
  779. else {
  780. #else
  781. if (!hcd->lpCurrent) {
  782. // If there was no current buffer before, we have one now, so set it to the end.
  783. #endif
  784. hcd->lpCurrent = hcd->lpTail;
  785. }
  786. // Now set the event saying it's time to process the ready frame
  787. DEBUGMSG(ZONE_CALLBACK, (" %s: Setting hcd->hevWait - some data\r\n", _fx_));
  788. SetEvent(hcd->hevWait);
  789. }