// // Created 31-Jul-96 [JonT] // PhilF-: This needs to be rewritten. You should have two classes // (CVfWCap & WDMCap) that derive from the same capture class instead // of those C-like functions... #include "Precomp.h" #ifndef WIDTHBYTES #define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4) #endif #ifdef _DEBUG static PTCHAR _rgZonesCap[] = { TEXT("dcap"), TEXT("Init"), TEXT("Streaming"), TEXT("Callback"), TEXT("Dialogs"), TEXT("Trace") }; #endif #ifndef __NT_BUILD__ extern "C" { // Special thunk prototype BOOL thk_ThunkConnect32(LPSTR pszDll16, LPSTR pszDll32, HINSTANCE hInst, DWORD dwReason); //; Magic Function code values for DeviceIOControl code. //DCAPVXD_THREADTIMESERVICE equ 101h //DCAPVXD_R0THREADIDSERVICE equ 102h #define DCAPVXD_THREADTIMESERVICE 0x101 #define DCAPVXD_R0THREADIDSERVICE 0x102 // KERNEL32 prototypes (not in headers but are exported by name on Win95) void* WINAPI MapSL(DWORD dw1616Ptr); HANDLE WINAPI OpenVxDHandle(HANDLE h); } #endif // Helper function prototypes BOOL initializeCaptureDeviceList(void); HVIDEO openVideoChannel(DWORD dwDeviceID, DWORD dwFlags); BOOL allocateBuffers(HCAPDEV hcd, int nBuffers); void freeBuffers(HCAPDEV hcd); // Globals HINSTANCE g_hInst; int g_cDevices; LPINTERNALCAPDEV g_aCapDevices[DCAP_MAX_DEVICES]; BOOL g_fInitCapDevList; #define INIT_CAP_DEV_LIST() if (g_fInitCapDevList) { initializeCaptureDeviceList(); } #ifndef __NT_BUILD__ HANDLE s_hVxD = NULL; #endif //__NT_BUILD__ // Strings #ifdef __NT_BUILD__ char g_szVFWRegKey[] = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Drivers32"; char g_szVFWRegDescKey[] = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\drivers.desc"; char g_szDriverName[] = "MSVIDEOx"; #ifndef SHOW_VFW2WDM_MAPPER char g_szVfWToWDMMapperDescription[] = "WDM Video For Windows Capture Driver (Win32)"; char g_szVfWToWDMMapperName[] = "VfWWDM32.dll"; #endif #else char g_szVFWRegKey[] = "SYSTEM\\CurrentControlSet\\Control\\MediaResources\\msvideo"; char g_szRegDescription[] = "Description"; char g_szRegName[] = "Driver"; char g_szRegDisabled[] = "Disabled"; char g_szDevNode[] = "DevNode"; char g_szSystemIni[] = "system.ini"; char g_szDriverSection[] = "drivers"; char g_szDriverKey[] = "MSVIDEOx"; #ifndef SHOW_VFW2WDM_MAPPER char g_szVfWToWDMMapperDescription[] = "VfW MM 16bit Driver for WDM V. Cap. Devices"; char g_szVfWToWDMMapperName[] = "vfwwdm.drv"; #endif #endif char g_szMSOfficeCamcorderDescription[] = "Screen Capture Device Driver for AVI"; char g_szMSOfficeCamcorderName[] = "Gdicap97.drv"; char g_szVerQueryForDesc[] = "\\StringFileInfo\\040904E4\\FileDescription"; void DoClose(HCAPDEV hcd); #define ENTER_DCAP(hcd) InterlockedIncrement(&(hcd)->busyCount); #define LEAVE_DCAP(hcd) if (InterlockedDecrement(&(hcd)->busyCount) == 0) DoClose((hcd)); // DllEntryPoint extern "C" BOOL DllEntryPoint( HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved ) { static int s_nProcesses = 0; FX_ENTRY("DllEntryPoint"); #ifndef __NT_BUILD__ // We want to load the VxD even before initializing the thunks // because the 16-bit half initializes the VxD during the thk_ThunkConnect32 call if (!s_hVxD) { s_hVxD = CreateFile("\\\\.\\DCAPVXD.VXD", 0,0,0,0, FILE_FLAG_DELETE_ON_CLOSE, 0); if (s_hVxD == INVALID_HANDLE_VALUE) { ERRORMESSAGE(("%s: Failure loading VxD - Fatal\r\n", _fx_)); return FALSE; } } // Initialize the thunks if (!(thk_ThunkConnect32("DCAP16.DLL", "DCAP32.DLL", hInst, dwReason))) { ERRORMESSAGE(("%s: thk_ThunkConnect32 failed!\r\n", _fx_)); return FALSE; } #endif switch (dwReason) { case DLL_PROCESS_ATTACH: // Save global hinst g_hInst = hInst; // Only initialize on the first DLL load if (s_nProcesses++ == 0) { DBGINIT(&ghDbgZoneCap, _rgZonesCap); DBG_INIT_MEMORY_TRACKING(hInst); g_fInitCapDevList = TRUE; } else return FALSE; // fail to load multiple instances break; case DLL_PROCESS_DETACH: if (--s_nProcesses == 0) // Are we going away? { #ifndef __NT_BUILD__ CloseHandle(s_hVxD); s_hVxD = NULL; #endif DBG_CHECK_MEMORY_TRACKING(hInst); DBGDEINIT(&ghDbgZoneCap); } break; } return TRUE; } void GetVersionData (LPINTERNALCAPDEV lpcd) { int j; DWORD dwVerInfoSize; LPSTR lpstrInfo; LPSTR lpDesc; // Version number // You must find the size first before getting any file info dwVerInfoSize = GetFileVersionInfoSize(lpcd->szDeviceName, NULL); if (dwVerInfoSize && (lpstrInfo = (LPSTR)LocalAlloc(LPTR, dwVerInfoSize))) { // Read from the file into our block if (GetFileVersionInfo(lpcd->szDeviceName, 0L, dwVerInfoSize, lpstrInfo)) { lpDesc = NULL; if (VerQueryValue(lpstrInfo, g_szVerQueryForDesc, (LPVOID *)&lpDesc, (PUINT)&j) && lpDesc) { lstrcpyn(lpcd->szDeviceDescription, lpDesc, j); wsprintf(lpcd->szDeviceVersion, TEXT("Version: %d.%d.%d.%d"), HIWORD(((VS_VERSION *)lpstrInfo)->vffInfo.dwFileVersionMS), LOWORD(((VS_VERSION *)lpstrInfo)->vffInfo.dwFileVersionMS), HIWORD(((VS_VERSION *)lpstrInfo)->vffInfo.dwFileVersionLS), LOWORD(((VS_VERSION *)lpstrInfo)->vffInfo.dwFileVersionLS)); } } LocalFree(lpstrInfo); } } #ifdef __NT_BUILD__ // initializeCaptureDeviceList // Sets up our static array of available capture devices from the registry // Returns FALSE iff there are no video devices. BOOL initializeCaptureDeviceList(void) { HKEY hkeyVFW, hkeyVFWdesc; DWORD dwType; DWORD dwSize; int i; LPINTERNALCAPDEV lpcd; HCAPDEV hCapDev; FX_ENTRY("initializeCaptureDeviceList"); // Clear the entire array and start with zero devices g_cDevices = 0; ZeroMemory(g_aCapDevices, sizeof (g_aCapDevices)); // Open the reg key in question if (RegOpenKey(HKEY_LOCAL_MACHINE, g_szVFWRegKey, &hkeyVFW) == ERROR_SUCCESS) { if (RegOpenKey(HKEY_LOCAL_MACHINE, g_szVFWRegDescKey, &hkeyVFWdesc) != ERROR_SUCCESS) hkeyVFWdesc = 0; lpcd = (LPINTERNALCAPDEV)LocalAlloc(LPTR, sizeof (INTERNALCAPDEV)); if (lpcd) { // Loop through all possible VFW drivers in registry for (i = 0 ; i < DCAP_MAX_VFW_DEVICES ; i++) { // Create the key name if (i == 0) g_szDriverName[sizeof (g_szDriverName) - 2] = 0; else g_szDriverName[sizeof (g_szDriverName) - 2] = (BYTE)i + '0'; // Name dwSize = sizeof(lpcd->szDeviceName); if (RegQueryValueEx(hkeyVFW, g_szDriverName, NULL, &dwType, (LPBYTE)lpcd->szDeviceName, &dwSize) == ERROR_SUCCESS) { // Description if (hkeyVFWdesc) { dwSize = sizeof(lpcd->szDeviceDescription); RegQueryValueEx(hkeyVFWdesc, lpcd->szDeviceName, NULL, &dwType, (LPBYTE)lpcd->szDeviceDescription, &dwSize); } else lstrcpy (lpcd->szDeviceDescription, lpcd->szDeviceName); // Devnode lpcd->dwDevNode = 0; lpcd->nDeviceIndex = g_cDevices; GetVersionData(lpcd); #ifndef SHOW_VFW2WDM_MAPPER // Remove bogus Camcorder capture device from list of devices shown to the user // The Camcorder driver is a fake capture device used by the MS Office Camcorder // to capture screen activity to an AVI file. This not a legit capture device driver // and is extremely buggy. // We also remove the VfW to WDM mapper if we are on NT5. if (lstrcmp(lpcd->szDeviceDescription, g_szMSOfficeCamcorderDescription) && lstrcmp(lpcd->szDeviceName, g_szMSOfficeCamcorderName) && lstrcmp(lpcd->szDeviceDescription, g_szVfWToWDMMapperDescription) && lstrcmp(lpcd->szDeviceName, g_szVfWToWDMMapperName)) { #endif g_aCapDevices[g_cDevices] = lpcd; g_aCapDevices[g_cDevices]->nDeviceIndex = g_cDevices; g_cDevices++; #ifndef SHOW_VFW2WDM_MAPPER } else LocalFree(lpcd); #endif lpcd = (LPINTERNALCAPDEV)LocalAlloc(LPTR, sizeof (INTERNALCAPDEV)); if (!lpcd) { ERRORMESSAGE(("%s: Failed to allocate an INTERNALCAPDEV buffer\r\n", _fx_)); break; // break out of the FOR loop } } } } else { ERRORMESSAGE(("%s: Failed to allocate an INTERNALCAPDEV buffer\r\n", _fx_)); } if (lpcd) LocalFree (lpcd); // free the extra buffer RegCloseKey(hkeyVFW); if (hkeyVFWdesc) RegCloseKey(hkeyVFWdesc); } #ifndef HIDE_WDM_DEVICES WDMGetDevices(); #endif g_fInitCapDevList = FALSE; return TRUE; } #else //__NT_BUILD__ // initializeCaptureDeviceList // Sets up our static array of available capture devices from the registry and // from SYSTEM.INI. // Returns FALSE iff there are no video devices. BOOL initializeCaptureDeviceList(void) { int i, j, index; HKEY hkeyVFW; HKEY hkeyEnum; DWORD dwType; DWORD dwSize; LPINTERNALCAPDEV lpcd; char szEnumName[MAX_PATH]; char szDisabled[3]; HCAPDEV hCapDev; OSVERSIONINFO osvInfo = {0}; FX_ENTRY("initializeCaptureDeviceList"); // Clear the entire array and start with zero devices g_cDevices = 0; ZeroMemory(g_aCapDevices, sizeof (g_aCapDevices)); // If we are on a version on Win95 (OSRx) use the mapper to talk to WDM devices. // The WDM drivers used on OSR2 are not stream class minidrivers so we fail // to handle them properly. Let the mapper do this for us. osvInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osvInfo); // Open the reg key in question if (RegOpenKey(HKEY_LOCAL_MACHINE, g_szVFWRegKey, &hkeyVFW) == ERROR_SUCCESS) { // Loop through all possible VFW drivers in registry for (i = 0 ; i < DCAP_MAX_VFW_DEVICES ; i++) { // See if the key is there and if not, we're done. Note that registry // keys have to be sequential, no holes allowed since the only way // to query is sequential... if (RegEnumKey(hkeyVFW, i, szEnumName, MAX_PATH) != ERROR_SUCCESS || RegOpenKey(hkeyVFW, szEnumName, &hkeyEnum) != ERROR_SUCCESS) break; lpcd = (LPINTERNALCAPDEV)LocalAlloc(LPTR, sizeof (INTERNALCAPDEV)); if (!lpcd) { ERRORMESSAGE(("%s: Failed to allocate an INTERNALCAPDEV buffer\r\n", _fx_)); break; // break from the FOR loop } // Description dwSize = sizeof (lpcd->szDeviceDescription); RegQueryValueEx(hkeyEnum, g_szRegDescription, NULL, &dwType, (LPBYTE)lpcd->szDeviceDescription, &dwSize); // Name dwSize = sizeof (lpcd->szDeviceName); RegQueryValueEx(hkeyEnum, g_szRegName, NULL, &dwType, (LPBYTE)lpcd->szDeviceName, &dwSize); // Disabled dwSize = sizeof (szDisabled); if (RegQueryValueEx(hkeyEnum, g_szRegDisabled, NULL, &dwType, (LPBYTE)szDisabled, &dwSize) == ERROR_SUCCESS && szDisabled[0] == '1') lpcd->dwFlags |= CAPTURE_DEVICE_DISABLED; // Devnode dwSize = sizeof (DWORD); RegQueryValueEx(hkeyEnum, g_szDevNode, NULL, &dwType, (BYTE*)&lpcd->dwDevNode, &dwSize); GetVersionData(lpcd); #ifndef SHOW_VFW2WDM_MAPPER // Remove bogus Camcorder capture device from list of devices shown to the user // The Camcorder driver is a fake capture device used by the MS Office Camcorder // to capture screen activity to an AVI file. This not a legit capture device driver // and is extremely buggy. // We also remove the VfW to WDM mapper if we are on Win98. On Win95 we still use // it to get access to USB devices developed for OSR2. if ((lstrcmp(lpcd->szDeviceDescription, g_szMSOfficeCamcorderDescription) && lstrcmp(lpcd->szDeviceName, g_szMSOfficeCamcorderName)) && (((osvInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) && (osvInfo.dwMinorVersion == 0)) || lstrcmp(lpcd->szDeviceDescription, g_szVfWToWDMMapperDescription) && lstrcmp(lpcd->szDeviceName, g_szVfWToWDMMapperName))) { #endif g_aCapDevices[g_cDevices] = lpcd; g_aCapDevices[g_cDevices]->nDeviceIndex = g_cDevices; g_cDevices++; #ifndef SHOW_VFW2WDM_MAPPER } else LocalFree(lpcd); #endif RegCloseKey(hkeyEnum); } RegCloseKey(hkeyVFW); } // Now get the rest from system.ini, if any for (i = 0 ; i < DCAP_MAX_VFW_DEVICES ; i++) { // Create the key name if (i == 0) g_szDriverKey[sizeof (g_szDriverKey) - 2] = 0; else g_szDriverKey[sizeof (g_szDriverKey) - 2] = (BYTE)i + '0'; // See if there's a profile string if (GetPrivateProfileString(g_szDriverSection, g_szDriverKey, "", szEnumName, MAX_PATH, g_szSystemIni)) { // First check to see if this is a dupe. If it is, go no further. if (g_cDevices) { for (j = 0 ; j < g_cDevices ; j++) if (!lstrcmpi(g_aCapDevices[j]->szDeviceName, szEnumName)) goto NextDriver; } lpcd = (LPINTERNALCAPDEV)LocalAlloc(LPTR, sizeof (INTERNALCAPDEV)); if (!lpcd) { ERRORMESSAGE(("%s: Failed to allocate an INTERNALCAPDEV buffer\r\n", _fx_)); break; // break from the FOR loop } // We have a unique name, copy in the driver name and find the description // by reading the driver's versioninfo resource. lstrcpy(lpcd->szDeviceName, szEnumName); GetVersionData(lpcd); #ifndef SHOW_VFW2WDM_MAPPER // Remove bogus Camcorder capture device from list of devices shown to the user // The Camcorder driver is a fake capture device used by the MS Office Camcorder // to capture screen activity to an AVI file. This not a legit capture device driver // and is extremely buggy. // We also remove the VfW to WDM mapper if we are on Win98. On Win95 we still use // it to get access to USB devices developed for OSR2. if ((lstrcmp(lpcd->szDeviceDescription, g_szMSOfficeCamcorderDescription) && lstrcmp(lpcd->szDeviceName, g_szMSOfficeCamcorderName)) && (((osvInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) && (osvInfo.dwMinorVersion == 0)) || lstrcmp(lpcd->szDeviceDescription, g_szVfWToWDMMapperDescription) && lstrcmp(lpcd->szDeviceName, g_szVfWToWDMMapperName))) { #endif g_aCapDevices[g_cDevices] = lpcd; g_aCapDevices[g_cDevices]->nDeviceIndex = g_cDevices; g_cDevices++; #ifndef SHOW_VFW2WDM_MAPPER } else LocalFree(lpcd); #endif } NextDriver: ; } #ifndef HIDE_WDM_DEVICES WDMGetDevices(); #endif g_fInitCapDevList = FALSE; return TRUE; } #endif //__NT_BUILD__ // GetNumCaptureDevice // Returns the number of *ENABLED* capture devices /**************************************************************************** * @doc EXTERNAL DCAP32 * * @func int DCAPI | GetNumCaptureDevices | This function returns the number * of *ENABLED* capture devices. * * @rdesc Returns the number of *ENABLE* capture devices. ***************************************************************************/ int DCAPI GetNumCaptureDevices() { int nNumCapDevices = 0; int nDeviceIndex = 0; INIT_CAP_DEV_LIST(); while (nDeviceIndex < g_cDevices) if (!(g_aCapDevices[nDeviceIndex++]->dwFlags & CAPTURE_DEVICE_DISABLED)) nNumCapDevices++; return nNumCapDevices; } // FindFirstCaptureDevice // Returns the first capture device available that matches the string // or the first one registered if szDeviceDescription is NULL BOOL DCAPI FindFirstCaptureDevice( IN OUT FINDCAPTUREDEVICE* lpfcd, char* szDeviceDescription ) { int i; static HCAPDEV hcap = NULL; INIT_CAP_DEV_LIST(); // Validate size if (lpfcd->dwSize != sizeof (FINDCAPTUREDEVICE)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // hack to avoid quickcam driver problem when hardware not installed if (g_cDevices && !hcap) { for (i = 0; ((i < g_cDevices) && (g_aCapDevices[i]->dwFlags & CAPTURE_DEVICE_DISABLED)); i++); if ((i < g_cDevices) && (hcap = OpenCaptureDevice(i))) { CloseCaptureDevice (hcap); } else { if (i < g_cDevices) { g_aCapDevices[i]->dwFlags |= CAPTURE_DEVICE_DISABLED; #ifdef _DEBUG OutputDebugString((i == 0) ? "DCAP32: 1st capture device fails to open!\r\n" : (i == 1) ? "DCAP32: 2nd capture device fails to open!\r\n" : (i == 2) ? "DCAP32: 3rd capture device fails to open!\r\n" : "DCAP32: 4th capture device fails to open!\r\n"); #endif } } } // Search if necessary if (szDeviceDescription) { for (i = 0 ; i < g_cDevices ; i++) if (!lstrcmpi(g_aCapDevices[i]->szDeviceDescription, szDeviceDescription) && !(g_aCapDevices[i]->dwFlags & CAPTURE_DEVICE_DISABLED)) break; } else for (i = 0; ((i < g_cDevices) && (g_aCapDevices[i]->dwFlags & CAPTURE_DEVICE_DISABLED)); i++); // Return the info if (i == g_cDevices) { SetLastError(ERROR_FILE_NOT_FOUND); return FALSE; } else { lpfcd->nDeviceIndex = i; lstrcpy(lpfcd->szDeviceName, g_aCapDevices[lpfcd->nDeviceIndex]->szDeviceName); lstrcpy(lpfcd->szDeviceDescription, g_aCapDevices[i]->szDeviceDescription); lstrcpy(lpfcd->szDeviceVersion, g_aCapDevices[i]->szDeviceVersion); return TRUE; } } // FindFirstCaptureDeviceByIndex // Returns the device with the specified index. BOOL DCAPI FindFirstCaptureDeviceByIndex( IN OUT FINDCAPTUREDEVICE* lpfcd, int nDeviceIndex ) { INIT_CAP_DEV_LIST(); // Validate size and index if (lpfcd->dwSize != sizeof (FINDCAPTUREDEVICE) || nDeviceIndex >= g_cDevices || (nDeviceIndex < 0) || (g_aCapDevices[nDeviceIndex]->dwFlags & CAPTURE_DEVICE_DISABLED)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Return the info lpfcd->nDeviceIndex = nDeviceIndex; lstrcpy(lpfcd->szDeviceName, g_aCapDevices[lpfcd->nDeviceIndex]->szDeviceName); lstrcpy(lpfcd->szDeviceDescription, g_aCapDevices[nDeviceIndex]->szDeviceDescription); lstrcpy(lpfcd->szDeviceVersion, g_aCapDevices[nDeviceIndex]->szDeviceVersion); return TRUE; } // FindNextCaptureDevice // Returns the next capture device in list. BOOL DCAPI FindNextCaptureDevice( IN OUT FINDCAPTUREDEVICE* lpfcd ) { HCAPDEV hcap = NULL; INIT_CAP_DEV_LIST(); // Parameter validate the passed in structure if (lpfcd->dwSize != sizeof (FINDCAPTUREDEVICE)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } while (++lpfcd->nDeviceIndex < g_cDevices) { if ((!(g_aCapDevices[lpfcd->nDeviceIndex]->dwFlags & CAPTURE_DEVICE_DISABLED))) { if (g_aCapDevices[lpfcd->nDeviceIndex]->dwFlags & CAPTURE_DEVICE_OPEN) break; else { if (hcap = OpenCaptureDevice(lpfcd->nDeviceIndex)) { CloseCaptureDevice (hcap); break; } else g_aCapDevices[lpfcd->nDeviceIndex]->dwFlags |= CAPTURE_DEVICE_DISABLED; } } } // See if we're at the end if (lpfcd->nDeviceIndex >= g_cDevices) { SetLastError(ERROR_NO_MORE_FILES); return FALSE; } // Otherwise, fill in the info for the next one lstrcpy(lpfcd->szDeviceName, g_aCapDevices[lpfcd->nDeviceIndex]->szDeviceName); lstrcpy(lpfcd->szDeviceDescription, g_aCapDevices[lpfcd->nDeviceIndex]->szDeviceDescription); lstrcpy(lpfcd->szDeviceVersion, g_aCapDevices[lpfcd->nDeviceIndex]->szDeviceVersion); return TRUE; } // OpenCaptureDevice HCAPDEV DCAPI OpenCaptureDevice( int nDeviceIndex ) { LPINTERNALCAPDEV hcd; LPBITMAPINFOHEADER lpbmih = NULL; DWORD err, dwLen; BOOL fl; FX_ENTRY("OpenCaptureDevice"); DEBUGMSG(ZONE_CALLS, ("%s() - Begin\r\n", _fx_)); INIT_CAP_DEV_LIST(); // Validate the device index if ((unsigned)nDeviceIndex >= (unsigned)g_cDevices || (g_aCapDevices[nDeviceIndex]->dwFlags & (CAPTURE_DEVICE_DISABLED | CAPTURE_DEVICE_OPEN))) { SetLastError(ERROR_INVALID_PARAMETER); DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return NULL; } hcd = g_aCapDevices[nDeviceIndex]; hcd->busyCount = 1; // we start at 1 to say that we're open // DoClose happens when count goes to 0 if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) { #ifndef __NT_BUILD__ // Allocate some memory we can lock for the LOCKEDINFO structure hcd->wselLockedInfo = _AllocateLockableBuffer(sizeof (LOCKEDINFO)); if (!hcd->wselLockedInfo) { err = ERROR_OUTOFMEMORY; goto Error; } // Do our own thunking so we can track the selector for this buffer hcd->lpli = (LPLOCKEDINFO)MapSL(((DWORD)hcd->wselLockedInfo) << 16); #endif // Open the necessary video channels if (!(hcd->hvideoIn = openVideoChannel(nDeviceIndex, VIDEO_IN)) || !(hcd->hvideoCapture = openVideoChannel(nDeviceIndex, VIDEO_EXTERNALIN))) { ERRORMESSAGE(("%s: Couldn't open video channel(s)\r\n", _fx_)); if (hcd->hvideoIn) _CloseDriver((HDRVR)hcd->hvideoIn, 0, 0); SetLastError(ERROR_DCAP_DEVICE_IN_USE); DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return FALSE; } #ifdef USE_VIDEO_OVERLAY if (hcd->hvideoOverlay = openVideoChannel(nDeviceIndex, VIDEO_EXTERNALOUT)) { DEBUGMSG(ZONE_INIT, ("%s: Capture device supports overlay!\r\n", _fx_)); } else { DEBUGMSG(ZONE_INIT, ("%s: Capture device does not support overlay\r\n", _fx_)); } #endif } else { if (!WDMOpenDevice(nDeviceIndex)) { ERRORMESSAGE(("%s: Couldn't open WDM device\r\n", _fx_)); SetLastError(ERROR_DCAP_DEVICE_IN_USE); DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return FALSE; } } hcd->dwFlags |= CAPTURE_DEVICE_OPEN; // Get the initial format and set the values dwLen = GetCaptureDeviceFormatHeaderSize(hcd); if (lpbmih = (LPBITMAPINFOHEADER)LocalAlloc(LPTR, dwLen)) { lpbmih->biSize = dwLen; fl = GetCaptureDeviceFormat(hcd, lpbmih); //If we can't get a format, or height and/or width are 0, don't use this device if (!fl || lpbmih->biWidth == 0 || lpbmih->biHeight == 0) { ERRORMESSAGE(("%s: GetCaptureDeviceFormat failed\r\n", _fx_)); err = ERROR_DCAP_NO_DRIVER_SUPPORT; goto Error; } fl = SetCaptureDeviceFormat(hcd, lpbmih, 0, 0); if (!fl) { ERRORMESSAGE(("%s: SetCaptureDeviceFormat failed\r\n", _fx_)); err = ERROR_DCAP_NO_DRIVER_SUPPORT; goto Error; } #if 0 _SetCaptureRect(hcd->hvideoIn, DVM_DST_RECT, 0, 0, lpbmih->biWidth, lpbmih->biHeight); _SetCaptureRect(hcd->hvideoCapture, DVM_SRC_RECT, 0, 0, lpbmih->biWidth, lpbmih->biHeight); _SetCaptureRect(hcd->hvideoCapture, DVM_DST_RECT, 0, 0, lpbmih->biWidth, lpbmih->biHeight); #endif LocalFree((HANDLE)lpbmih); } else { err = ERROR_OUTOFMEMORY; goto Error; } // Keep a stream running all the time on EXTERNALIN (capture->frame buffer). if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) { #ifdef USE_VIDEO_OVERLAY if (hcd->hvideoOverlay) _InitializeExternalVideoStream(hcd->hvideoOverlay); #else _InitializeExternalVideoStream(hcd->hvideoCapture); #endif #ifndef __NT_BUILD__ // Lock our structure so it can be touched at interrupt time _LockBuffer(hcd->wselLockedInfo); #endif } DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return hcd; Error: hcd->dwFlags &= ~CAPTURE_DEVICE_OPEN; if (lpbmih) { LocalFree((HANDLE)lpbmih); lpbmih = NULL; } if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) { if (hcd->hvideoIn) { _CloseDriver((HDRVR)hcd->hvideoIn, 0, 0); hcd->hvideoIn = NULL; } if (hcd->hvideoCapture) { _CloseDriver((HDRVR)hcd->hvideoCapture, 0, 0); hcd->hvideoCapture = NULL; } #ifdef USE_VIDEO_OVERLAY if (hcd->hvideoOverlay) { _CloseDriver((HDRVR)hcd->hvideoOverlay, 0, 0); hcd->hvideoOverlay = NULL; } #endif } else { WDMCloseDevice(nDeviceIndex); } SetLastError(err); DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return NULL; } void DoClose( HCAPDEV hcd ) { FX_ENTRY("DoClose"); DEBUGMSG(ZONE_CALLS, ("%s() - Begin\r\n", _fx_)); // Clean up streaming on main channel, including freeing all buffers if (hcd->dwFlags & HCAPDEV_STREAMING_INITIALIZED) UninitializeStreaming(hcd); // Stop streaming on the capture channel if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) { #ifdef USE_VIDEO_OVERLAY if (hcd->hvideoOverlay) { _SendDriverMessage((HDRVR)hcd->hvideoOverlay, DVM_STREAM_FINI, 0L, 0L); _CloseDriver((HDRVR)hcd->hvideoOverlay, 0, 0); hcd->hvideoOverlay = NULL; } #else _SendDriverMessage((HDRVR)hcd->hvideoCapture, DVM_STREAM_FINI, 0L, 0L); #endif #ifdef USE_VIDEO_OVERLAY if (hcd->hvideoOverlay) { _CloseDriver((HDRVR)hcd->hvideoOverlay, 0, 0); hcd->hvideoOverlay = NULL; } #endif // Close the driver channels if (!_CloseDriver((HDRVR)hcd->hvideoCapture, 0, 0) || !_CloseDriver((HDRVR)hcd->hvideoIn, 0, 0)) { SetLastError(ERROR_DCAP_NONSPECIFIC); ERRORMESSAGE(("%s: Couldn't close channel, error unknown\r\n", _fx_)); // with delayed close this is catastrophic, we can't just return that the device is still // open, but we can't get the device to close either, so we'll have to just leave it in this // hung open state - hopefully this never happens... } hcd->hvideoCapture = NULL; hcd->hvideoIn = NULL; #ifndef __NT_BUILD__ // Free the LOCKEDINFO structure _FreeLockableBuffer(hcd->wselLockedInfo); hcd->wselLockedInfo = 0; #endif } else { WDMCloseDevice(hcd->nDeviceIndex); } hcd->dwFlags &= ~CAPTURE_DEVICE_OPEN; DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); } BOOL DCAPI CloseCaptureDevice( HCAPDEV hcd ) { FX_ENTRY("CloseCaptureDevice"); DEBUGMSG(ZONE_CALLS, ("%s() - Begin\r\n", _fx_)); INIT_CAP_DEV_LIST(); VALIDATE_CAPDEV(hcd); hcd->dwFlags &= ~CAPTURE_DEVICE_OPEN; // clear flag to disable other API's LEAVE_DCAP(hcd); // dec our enter count, if no other thread is in a DCAP // service, then this dec will go to 0 and we'll call // DoClose; else we won't call DoClose until the other // active service dec's the count to 0 DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return TRUE; } DWORD DCAPI GetCaptureDeviceFormatHeaderSize( HCAPDEV hcd ) { DWORD res; INIT_CAP_DEV_LIST(); VALIDATE_CAPDEV(hcd); ENTER_DCAP(hcd); if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) res = _GetVideoFormatSize(reinterpret_cast(hcd->hvideoIn)); else res = WDMGetVideoFormatSize(hcd->nDeviceIndex); LEAVE_DCAP(hcd); return res; } BOOL DCAPI GetCaptureDeviceFormat( HCAPDEV hcd, LPBITMAPINFOHEADER lpbmih ) { BOOL fRes; FX_ENTRY("GetCaptureDeviceFormat"); INIT_CAP_DEV_LIST(); VALIDATE_CAPDEV(hcd); ENTER_DCAP(hcd); // Call the driver to get the bitmap information if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) fRes = _GetVideoFormat(hcd->hvideoIn, lpbmih); else fRes = WDMGetVideoFormat(hcd->nDeviceIndex, lpbmih); if (!fRes) { // This is DOOM if the driver doesn't support this. // It might be useful have some sort of fallback code here, // or else we should try this when the connection is made and // fail it unless this call works. ERRORMESSAGE(("%s: Failed to get video format\r\n", _fx_)); SetLastError(ERROR_NOT_SUPPORTED); LEAVE_DCAP(hcd); return FALSE; } if (lpbmih->biCompression == BI_RGB) lpbmih->biSizeImage = WIDTHBYTES(lpbmih->biWidth * lpbmih->biBitCount) * lpbmih->biHeight; // Keep track of current buffer size needed hcd->dwcbBuffers = sizeof(CAPBUFFERHDR) + lpbmih->biSizeImage; LEAVE_DCAP(hcd); return TRUE; } BOOL DCAPI SetCaptureDeviceFormat( HCAPDEV hcd, LPBITMAPINFOHEADER lpbmih, LONG srcwidth, LONG srcheight ) { BOOL fRes; #ifdef USE_VIDEO_OVERLAY RECT rect; #endif INIT_CAP_DEV_LIST(); VALIDATE_CAPDEV(hcd); // Don't allow this if streaming if (hcd->dwFlags & HCAPDEV_STREAMING) { SetLastError(ERROR_DCAP_NOT_WHILE_STREAMING); return FALSE; } ENTER_DCAP(hcd); // Call the driver to set the format if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) { fRes = _SetVideoFormat(hcd->hvideoCapture, hcd->hvideoIn, lpbmih); #ifdef USE_VIDEO_OVERLAY if (fRes && hcd->hvideoOverlay) { // Get the current rectangles _SendDriverMessage((HDRVR)hcd->hvideoOverlay, DVM_DST_RECT, (LPARAM)(LPVOID)&rect, VIDEO_CONFIGURE_GET); DEBUGMSG(ZONE_INIT, ("%s: Current overlay dst rect is rect.left=%ld, rect.top=%ld, rect.right=%ld, rect.bottom=%ld\r\n", _fx_, rect.left, rect.top, rect.right, rect.bottom)); _SendDriverMessage((HDRVR)hcd->hvideoOverlay, DVM_SRC_RECT, (LPARAM)(LPVOID)&rect, VIDEO_CONFIGURE_GET); DEBUGMSG(ZONE_INIT, ("%s: Current overlay src rect is rect.left=%ld, rect.top=%ld, rect.right=%ld, rect.bottom=%ld\r\n", _fx_, rect.left, rect.top, rect.right, rect.bottom)); // Set the rectangles rect.left = rect.top = 0; rect.right = (WORD)lpbmih->biWidth; rect.bottom = (WORD)lpbmih->biHeight; _SendDriverMessage((HDRVR)hcd->hvideoOverlay, DVM_DST_RECT, (LPARAM)(LPVOID)&rect, VIDEO_CONFIGURE_SET); _SendDriverMessage((HDRVR)hcd->hvideoOverlay, DVM_SRC_RECT, (LPARAM)(LPVOID)&rect, VIDEO_CONFIGURE_SET); if (hcd->hvideoOverlay) _InitializeExternalVideoStream(hcd->hvideoOverlay); } #endif } else fRes = WDMSetVideoFormat(hcd->nDeviceIndex, lpbmih); if (!fRes) { SetLastError(ERROR_DCAP_FORMAT_NOT_SUPPORTED); LEAVE_DCAP(hcd); return FALSE; } // Cache the bitmap size we're dealing with now if (lpbmih->biCompression == BI_RGB) hcd->dwcbBuffers = sizeof (CAPBUFFERHDR) + lpbmih->biWidth * lpbmih->biHeight * lpbmih->biBitCount / 8; else hcd->dwcbBuffers = sizeof (CAPBUFFERHDR) + lpbmih->biSizeImage; LEAVE_DCAP(hcd); return TRUE; } // GetCaptureDevicePalette // Gets the current palette from the capture device. The entries are returned to // the caller who normally calls CreatePalette on the structure. It may, however, // want to translate the palette entries into some preexisting palette or identity // palette before calling CreatePalette, hence the need for passing back the entries. BOOL DCAPI GetCaptureDevicePalette( HCAPDEV hcd, CAPTUREPALETTE* lpcp ) { BOOL fRes; FX_ENTRY("GetCaptureDevicePalette"); INIT_CAP_DEV_LIST(); VALIDATE_CAPDEV(hcd); ENTER_DCAP(hcd); // The caller doesn't have to initialize the structure. // The driver should fill it in, but it may want it preininitialized so we do that here. lpcp->wVersion = 0x0300; lpcp->wcEntries = 256; // Get the palette entries from the driver and return to the user if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) fRes = _GetVideoPalette(hcd->hvideoIn, lpcp, sizeof (CAPTUREPALETTE)); else fRes = WDMGetVideoPalette(hcd->nDeviceIndex, lpcp, sizeof (CAPTUREPALETTE)); if (!fRes) { ERRORMESSAGE(("%s: No palette returned from driver\r\n", _fx_)); SetLastError(ERROR_DCAP_NO_DRIVER_SUPPORT); LEAVE_DCAP(hcd); return FALSE; } LEAVE_DCAP(hcd); return TRUE; } void TerminateStreaming( HCAPDEV hcd ) { DWORD dwTicks; LPCAPBUFFER lpcbuf; DWORD_PTR dwlpvh; BOOL fRes; FX_ENTRY("TerminateStreaming"); DEBUGMSG(ZONE_CALLS, ("%s() - Begin\r\n", _fx_)); StopStreaming(hcd); if (!(hcd->dwFlags & HCAPDEV_STREAMING_FRAME_GRAB)) { hcd->dwFlags |= HCAPDEV_STREAMING_PAUSED; // Make sure we aren't streaming if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) { #ifndef __NT_BUILD__ hcd->lpli->dwFlags |= LIF_STOPSTREAM; #endif _SendDriverMessage((HDRVR)hcd->hvideoIn, DVM_STREAM_RESET, 0, 0); } else WDMVideoStreamReset(hcd->nDeviceIndex); dwTicks = GetTickCount(); lpcbuf = hcd->lpcbufList; while (lpcbuf && GetTickCount() < dwTicks + 1000) { dwlpvh = (DWORD_PTR)lpcbuf->vh.lpData - sizeof(CAPBUFFERHDR); // 16:16 ptr to vh = 16:16 ptr to data - sizeof(CAPBUFFERHDR) // 32bit ptr to vh = 32bit ptr to data - sizeof(CAPBUFFERHDR) if (!(lpcbuf->vh.dwFlags & VHDR_DONE)) { if (WaitForSingleObject(hcd->hevWait, 500) == WAIT_TIMEOUT) { ERRORMESSAGE(("%s: Timeout waiting for all buffers done after DVM_STREAM_RESET\r\n", _fx_)); break; // looks like it isn't going to happen, so quit waiting } //else recheck done bit on current buffer if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE) && (lpcbuf->vh.dwFlags & VHDR_DONE) && (lpcbuf->vh.dwFlags & VHDR_PREPARED)) { // AVICap32 clears the prepared flag even if the driver failed the operation - do the same thing _SendDriverMessage((HDRVR)hcd->hvideoIn, DVM_STREAM_UNPREPAREHEADER, dwlpvh, sizeof(VIDEOHDR)); lpcbuf->vh.dwFlags &= ~VHDR_PREPARED; } } else { if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE) && (lpcbuf->vh.dwFlags & VHDR_PREPARED)) { // AVICap32 clears the prepared flag even if the driver failed the operation - do the same thing _SendDriverMessage((HDRVR)hcd->hvideoIn, DVM_STREAM_UNPREPAREHEADER, dwlpvh, sizeof(VIDEOHDR)); lpcbuf->vh.dwFlags &= ~VHDR_PREPARED; } lpcbuf = (LPCAPBUFFER)lpcbuf->vh.dwUser; // next buffer } } DEBUGMSG(ZONE_STREAMING, ("%s: Done trying to clear buffers\r\n", _fx_)); // Clean up flags in order to reuse buffers - drivers do not like to be // given buffers with a dirty dwFlags at the start of streaming... for (lpcbuf = hcd->lpcbufList ; lpcbuf ; lpcbuf = (LPCAPBUFFER)lpcbuf->vh.dwUser) lpcbuf->vh.dwFlags = 0; // Terminate streaming with the driver if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) fRes = _UninitializeVideoStream(hcd->hvideoIn); else fRes = WDMUnInitializeVideoStream(hcd->nDeviceIndex); if (!fRes) { ERRORMESSAGE(("%s: Error returned from XXXUninitializeVideoStream\r\n", _fx_)); } } DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); } BOOL ReinitStreaming( HCAPDEV hcd ) { LPCAPBUFFER lpcbuf; DWORD_PTR dwlpvh; BOOL fRes; FX_ENTRY("ReinitStreaming"); DEBUGMSG(ZONE_CALLS, ("%s() - Begin\r\n", _fx_)); if (!(hcd->dwFlags & HCAPDEV_STREAMING_FRAME_GRAB)) { // Tell the driver to prepare for streaming. This sets up the callback if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) #ifdef __NT_BUILD__ fRes = _InitializeVideoStream(hcd->hvideoIn, hcd->dw_usecperframe, (DWORD_PTR)hcd); #else fRes = _InitializeVideoStream(hcd->hvideoIn, hcd->dw_usecperframe, (DWORD)hcd->wselLockedInfo << 16); #endif else fRes = WDMInitializeVideoStream(hcd, hcd->nDeviceIndex, hcd->dw_usecperframe); if (!fRes) { ERRORMESSAGE(("%s: Error returned from XXXInitializeVideoStream\r\n", _fx_)); SetLastError(ERROR_DCAP_BAD_FRAMERATE); DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return FALSE; } // Sleep (10); hcd->dwFlags &= ~HCAPDEV_STREAMING_PAUSED; // If any buffers are not marked DONE, then give them back to the driver; let all // DONE buffers get processed by the app first for (lpcbuf = hcd->lpcbufList ; lpcbuf ; lpcbuf = (LPCAPBUFFER)lpcbuf->vh.dwUser) { if (!(lpcbuf->vh.dwFlags & VHDR_DONE)) { dwlpvh = (DWORD_PTR)lpcbuf->vh.lpData - sizeof(CAPBUFFERHDR); // 16:16 ptr to vh = 16:16 ptr to data - sizeof(CAPBUFFERHDR) // 32bit ptr to vh = 32bit ptr to data - sizeof(CAPBUFFERHDR) if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) { // AVICap32 sets the prepared flag even if the driver failed the operation - do the same thing _SendDriverMessage((HDRVR)hcd->hvideoIn, DVM_STREAM_PREPAREHEADER, dwlpvh, sizeof(VIDEOHDR)); lpcbuf->vh.dwFlags |= VHDR_PREPARED; fRes = (_SendDriverMessage((HDRVR)hcd->hvideoIn, DVM_STREAM_ADDBUFFER, dwlpvh, sizeof(VIDEOHDR)) == DV_ERR_OK); } else fRes = WDMVideoStreamAddBuffer(hcd->nDeviceIndex, (PVOID)dwlpvh); if (!fRes) { DEBUGMSG(ZONE_STREAMING, ("%s: Failed with lpcbuf=0x%08lX, lpcbuf->vh.lpData=0x%08lX, dwlpvh=0x%08lX\r\n", _fx_, lpcbuf, lpcbuf->vh.lpData, dwlpvh)); DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return FALSE; } else { DEBUGMSG(ZONE_STREAMING, ("%s: Succeeded with lpcbuf=0x%08lX, lpcbuf->vh.lpData=0x%08lX, dwlpvh=0x%08lX\r\n", _fx_, lpcbuf, lpcbuf->vh.lpData, dwlpvh)); } } } } DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return TRUE; } // CaptureDeviceDialog // Puts up one of the driver's dialogs for the user to twiddle. // If I can figure out ANY way to avoid this, I will. BOOL DCAPI CaptureDeviceDialog( HCAPDEV hcd, HWND hwndParent, DWORD dwFlags, LPBITMAPINFOHEADER lpbmih //OPTIONAL ) { DWORD dwDriverFlags = 0; HVIDEO hvid; DWORD dwSize; LPBITMAPINFOHEADER lpbmihCur; #ifdef _DEBUG LPBITMAPINFOHEADER lpbmihPre = NULL; #endif BOOL res = TRUE; FX_ENTRY("CaptureDeviceDialog"); INIT_CAP_DEV_LIST(); VALIDATE_CAPDEV(hcd); if (hcd->dwFlags & HCAPDEV_IN_DRIVER_DIALOG) return FALSE; // don't allow re-entering ENTER_DCAP(hcd); if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) { // See if we are just querying the driver for existence if (dwFlags & CAPDEV_DIALOG_QUERY) dwDriverFlags |= VIDEO_DLG_QUERY; // Select the correct channel to query if (dwFlags & CAPDEV_DIALOG_SOURCE) { hvid = hcd->hvideoCapture; if (!(dwFlags & CAPDEV_DIALOG_QUERY)) { dwDriverFlags |= VIDEO_DLG_QUERY; if (_SendDriverMessage((HDRVR)hvid, DVM_DIALOG, (DWORD_PTR)hwndParent, dwDriverFlags) == DV_ERR_NOTSUPPORTED) { hvid = hcd->hvideoIn; } dwDriverFlags &= ~VIDEO_DLG_QUERY; } } else hvid = hcd->hvideoIn; // Don't stop streaming. This make the source dialog totally useless // if the user can't see what is going on. #ifdef _DEBUG if (!lpbmih) { dwSize = GetCaptureDeviceFormatHeaderSize(hcd); if (lpbmihPre = (LPBITMAPINFOHEADER)LocalAlloc(LPTR, dwSize)) { lpbmihPre->biSize = dwSize; GetCaptureDeviceFormat(hcd, lpbmihPre); } lpbmih = lpbmihPre; } #endif // Call the driver hcd->dwFlags |= HCAPDEV_IN_DRIVER_DIALOG; if (_SendDriverMessage((HDRVR)hvid, DVM_DIALOG, (DWORD_PTR)hwndParent, dwDriverFlags)) { SetLastError(ERROR_DCAP_NO_DRIVER_SUPPORT); res = FALSE; // restart still ok } else if (lpbmih) { dwSize = GetCaptureDeviceFormatHeaderSize(hcd); if (lpbmihCur = (LPBITMAPINFOHEADER)LocalAlloc(LPTR, dwSize)) { lpbmihCur->biSize = dwSize; GetCaptureDeviceFormat(hcd, lpbmihCur); if (lpbmih->biSize != lpbmihCur->biSize || lpbmih->biWidth != lpbmihCur->biWidth || lpbmih->biHeight != lpbmihCur->biHeight || lpbmih->biBitCount != lpbmihCur->biBitCount || lpbmih->biCompression != lpbmihCur->biCompression) { ERRORMESSAGE(("%s: Format changed in dialog!!\r\n", _fx_)); #ifdef _DEBUG DebugBreak(); #endif // dialog changed format, so try to set it back if (!SetCaptureDeviceFormat(hcd, lpbmih, 0, 0)) { SetLastError (ERROR_DCAP_DIALOG_FORMAT); res = FALSE; } } LocalFree ((HANDLE)lpbmihCur); } #ifdef _DEBUG if (lpbmih == lpbmihPre) { LocalFree ((HANDLE)lpbmihPre); lpbmih = NULL; lpbmihPre = NULL; } #endif } hcd->dwFlags &= ~HCAPDEV_IN_DRIVER_DIALOG; if (hcd->dwFlags & HCAPDEV_STREAMING) { // The Intel Smart Video Recorder Pro stops streaming // on exit from the source dialog (!?!?). Make sure // we reset the streaming on any kind of device right // after we exit the source dialog. I verified this on // the CQC, ISVR Pro, Video Stinger and Video Blaster SE100. // They all seem to take this pretty well... TerminateStreaming(hcd); if (ReinitStreaming(hcd)) StartStreaming(hcd); else { SetLastError(ERROR_DCAP_DIALOG_STREAM); res = FALSE; ERRORMESSAGE(("%s: Couldn't reinit streaming after dialog!\r\n", _fx_)); } } } else { // See if we are just querying the driver for existence if (dwFlags & CAPDEV_DIALOG_QUERY) { // We only expose a settings dialog if (dwFlags & CAPDEV_DIALOG_IMAGE) { SetLastError(ERROR_DCAP_NO_DRIVER_SUPPORT); res = FALSE; ERRORMESSAGE(("%s: Driver does not support this dialog!\r\n", _fx_)); } } else { if (!WDMShowSettingsDialog(hcd->nDeviceIndex, hwndParent)) { SetLastError(ERROR_DCAP_NO_DRIVER_SUPPORT); res = FALSE; ERRORMESSAGE(("%s: Driver does not support this dialog!\r\n", _fx_)); } } hcd->dwFlags &= ~HCAPDEV_IN_DRIVER_DIALOG; // No need to restart streaming on WDM devices tested so far // Will add this feature if problems come up } LEAVE_DCAP(hcd); return res; } // InitializeStreaming // Allocates all memory and other objects necessary for streaming. BOOL DCAPI InitializeStreaming( HCAPDEV hcd, CAPSTREAM* lpcs, DWORD flags ) { LPCAPBUFFER lpcbuf; DWORD dwRound; LPBITMAPINFOHEADER lpbmih; BOOL bHaveBuffers = FALSE; FX_ENTRY("InitializeStreaming"); DEBUGMSG(ZONE_CALLS, ("%s() - Begin\r\n", _fx_)); INIT_CAP_DEV_LIST(); VALIDATE_CAPDEV(hcd); // It doesn't make sense to stream with less than 2 buffers if (lpcs->ncCapBuffers < MIN_STREAMING_CAPTURE_BUFFERS || flags & 0xfffffffe || hcd->dwFlags & HCAPDEV_STREAMING_INITIALIZED) { SetLastError(ERROR_INVALID_PARAMETER); DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return FALSE; } ENTER_DCAP(hcd); hcd->dwFlags &= ~(HCAPDEV_STREAMING | HCAPDEV_STREAMING_INITIALIZED | HCAPDEV_STREAMING_FRAME_GRAB | HCAPDEV_STREAMING_FRAME_TIME | HCAPDEV_STREAMING_PAUSED); // Before allocating, make sure we have the current format. // This sets our idea of the current size we need for the buffer by // setting hcd->dwcbBuffers as a side effect dwRound = GetCaptureDeviceFormatHeaderSize(hcd); if (lpbmih = (LPBITMAPINFOHEADER)LocalAlloc(LPTR, dwRound)) { lpbmih->biSize = dwRound; GetCaptureDeviceFormat(hcd, lpbmih); LocalFree ((HANDLE)lpbmih); } else { SetLastError(ERROR_OUTOFMEMORY); goto Error; } // BUGBUG - add logic to determine if we should automatically use FRAME_GRAB mode // Try allocating the number asked for if (flags & STREAMING_PREFER_FRAME_GRAB) { hcd->dwFlags |= HCAPDEV_STREAMING_FRAME_GRAB; } if (!allocateBuffers(hcd, lpcs->ncCapBuffers)) { SetLastError(ERROR_OUTOFMEMORY); goto Error; } // Create the event we need so we can signal at interrupt time if (!(hcd->hevWait = CreateEvent(NULL, FALSE, FALSE, NULL))) { ERRORMESSAGE(("%s: CreateEvent failed!\r\n", _fx_)); SetLastError(ERROR_OUTOFMEMORY); goto Error; } // Init CS used to serialize buffer list management InitializeCriticalSection(&hcd->bufferlistCS); // We were given frames per second times 100. Converting this to // usec per frame is 1/fps * 1,000,000 * 100. Here, do 1/fps * 1,000,000,000 // to give us an extra digit to do rounding on, then do a final / 10 hcd->dw_usecperframe = (unsigned)1000000000 / (unsigned)lpcs->nFPSx100; dwRound = hcd->dw_usecperframe % 10; // Could have done with one less divide, hcd->dw_usecperframe /= 10; // but this is clearer, and this is just // an init call... if (dwRound >= 5) hcd->dw_usecperframe++; hcd->lpCurrent = NULL; hcd->lpHead = NULL; hcd->lpTail = NULL; if (hcd->dwFlags & HCAPDEV_STREAMING_FRAME_GRAB) { #ifndef __NT_BUILD__ if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) hcd->lpli->pevWait = 0; #endif // link the buffers into the available list // start with empty list hcd->lpHead = (LPCAPBUFFER)(((LPBYTE)&hcd->lpHead) - sizeof(VIDEOHDR)); // fake CAPBUFFERHDR hcd->lpTail = (LPCAPBUFFER)(((LPBYTE)&hcd->lpHead) - sizeof(VIDEOHDR)); // fake CAPBUFFERHDR // now insert the buffers for (lpcbuf = hcd->lpcbufList ; lpcbuf ; lpcbuf = (LPCAPBUFFER)lpcbuf->vh.dwUser) { lpcbuf->lpPrev = hcd->lpTail; hcd->lpTail = lpcbuf; lpcbuf->lpNext = lpcbuf->lpPrev->lpNext; lpcbuf->lpPrev->lpNext = lpcbuf; lpcbuf->vh.dwFlags |= VHDR_INQUEUE; } } else { #ifndef __NT_BUILD__ if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) { hcd->lpli->pevWait = (DWORD)OpenVxDHandle(hcd->hevWait); // Lock down the LOCKEDINFO structure if (!_LockBuffer(hcd->wselLockedInfo)) { SetLastError(ERROR_OUTOFMEMORY); goto Error; } hcd->lpli->lp1616Head = 0; hcd->lpli->lp1616Tail = 0; hcd->lpli->lp1616Current = 0; } #endif if (!ReinitStreaming(hcd)) goto Error; } lpcs->hevWait = hcd->hevWait; // Flag that streaming is initialized hcd->dwFlags |= HCAPDEV_STREAMING_INITIALIZED; LEAVE_DCAP(hcd); DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return TRUE; Error: freeBuffers(hcd); if (hcd->hevWait) { CloseHandle(hcd->hevWait); #ifndef __NT_BUILD__ if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE) && hcd->lpli->pevWait) _CloseVxDHandle(hcd->lpli->pevWait); #endif } LEAVE_DCAP(hcd); DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return FALSE; } // SetStreamFrameRate // Changes the frame rate of a stream initialized channel. // PhilF-: This call is not used by NMCAP and NAC. So remove it or // start using it. BOOL DCAPI SetStreamFrameRate( HCAPDEV hcd, int nFPSx100 ) { DWORD dwNew, dwRound; BOOL restart; BOOL res = TRUE; INIT_CAP_DEV_LIST(); VALIDATE_CAPDEV(hcd); if (!(hcd->dwFlags & HCAPDEV_STREAMING_INITIALIZED)) { // must already have the channel initialized for streaming SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } ENTER_DCAP(hcd); restart = (hcd->dwFlags & HCAPDEV_STREAMING); // We were given frames per second times 100. Converting this to // usec per frame is 1/fps * 1,000,000 * 100. Here, do 1/fps * 1,000,000,000 // to give us an extra digit to do rounding on, then do a final / 10 dwNew = (unsigned)1000000000 / (unsigned)nFPSx100; dwRound = dwNew % 10; // Could have done with one less divide, dwNew /= 10; // but this is clearer, and this is just an init call... if (dwRound >= 5) dwNew++; if (dwNew != hcd->dw_usecperframe) { TerminateStreaming(hcd); hcd->dw_usecperframe = dwNew; res = ReinitStreaming(hcd); if (restart && res) StartStreaming(hcd); } LEAVE_DCAP(hcd); return res; } // UninitializeStreaming // Frees all memory and objects associated with streaming. BOOL DCAPI UninitializeStreaming( HCAPDEV hcd ) { DWORD dwTicks; LPCAPBUFFER lpcbuf; FX_ENTRY("UninitializeStreaming"); DEBUGMSG(ZONE_CALLS, ("%s() - Begin\r\n", _fx_)); INIT_CAP_DEV_LIST(); VALIDATE_CAPDEV(hcd); if (!(hcd->dwFlags & HCAPDEV_STREAMING_INITIALIZED)) { SetLastError(ERROR_INVALID_PARAMETER); DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return FALSE; } ENTER_DCAP(hcd); TerminateStreaming(hcd); #ifndef __NT_BUILD__ if (!(hcd->dwFlags & HCAPDEV_STREAMING_FRAME_GRAB) && !(hcd->dwFlags & WDM_CAPTURE_DEVICE)) { // Unlock our locked structure _UnlockBuffer(hcd->wselLockedInfo); // Free the event _CloseVxDHandle(hcd->lpli->pevWait); } #endif DeleteCriticalSection(&hcd->bufferlistCS); CloseHandle(hcd->hevWait); // BUGBUG - what about app still owning buffers // Loop through freeing all the buffers freeBuffers(hcd); hcd->dwFlags &= ~(HCAPDEV_STREAMING_INITIALIZED + HCAPDEV_STREAMING_PAUSED); LEAVE_DCAP(hcd); DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return TRUE; } void CALLBACK TimeCallback( UINT uID, UINT uMsg, HCAPDEV hcd, DWORD dw1, DWORD dw2 ) { hcd->dwFlags |= HCAPDEV_STREAMING_FRAME_TIME; // flag time for a new frame SetEvent (hcd->hevWait); // signal client to initiate frame grab } // StartStreaming // Begins streaming. BOOL DCAPI StartStreaming( HCAPDEV hcd ) { BOOL fRet; DWORD dwRet; FX_ENTRY("StartStreaming"); DEBUGMSG(ZONE_CALLS, ("%s() - Begin\r\n", _fx_)); INIT_CAP_DEV_LIST(); VALIDATE_CAPDEV(hcd); ENTER_DCAP(hcd); if (hcd->dwFlags & HCAPDEV_STREAMING_FRAME_GRAB) { fRet = ((hcd->timerID = timeSetEvent(hcd->dw_usecperframe/1000, 5, (LPTIMECALLBACK)&TimeCallback, (DWORD_PTR)hcd, TIME_PERIODIC)) != 0); } else { int i; fRet = FALSE; #ifndef __NT_BUILD__ if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) hcd->lpli->dwFlags &= ~LIF_STOPSTREAM; #endif for (i = 0; i < 5; i++) { if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) { dwRet = (DWORD)_SendDriverMessage((HDRVR)hcd->hvideoIn, DVM_STREAM_START, 0, 0); fRet = (dwRet == DV_ERR_OK); if (dwRet) { ERRORMESSAGE(("%s: DVM_STREAM_START failed, return code was %ld\r\n", _fx_, dwRet)); } } else fRet = WDMVideoStreamStart(hcd->nDeviceIndex); if (fRet) break; else if (i > 1) Sleep(10); } } if (fRet) hcd->dwFlags |= HCAPDEV_STREAMING; LEAVE_DCAP(hcd); DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return fRet; } // StopStreaming // Stops streaming but doesn't free any memory associated with streaming // so that it can be restarted with StartStreaming. BOOL DCAPI StopStreaming( HCAPDEV hcd ) { BOOL fRet; FX_ENTRY("StopStreaming"); DEBUGMSG(ZONE_CALLS, ("%s() - Begin\r\n", _fx_)); INIT_CAP_DEV_LIST(); VALIDATE_CAPDEV(hcd); ENTER_DCAP(hcd); if (hcd->dwFlags & HCAPDEV_STREAMING_FRAME_GRAB) { timeKillEvent(hcd->timerID); hcd->dwFlags &= ~HCAPDEV_STREAMING; // grab CS to ensure that no frame grab is in progress EnterCriticalSection(&hcd->bufferlistCS); LeaveCriticalSection(&hcd->bufferlistCS); fRet = TRUE; } else { if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) fRet = (_SendDriverMessage((HDRVR)hcd->hvideoIn, DVM_STREAM_STOP, 0, 0) == DV_ERR_OK); else fRet = WDMVideoStreamStop(hcd->nDeviceIndex); } if (fRet) hcd->dwFlags &= ~HCAPDEV_STREAMING; LEAVE_DCAP(hcd); DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return fRet; } // GetNextReadyBuffer // Called by the app to find the next buffer that has been marked as // done by the driver and has data to be displayed. LPSTR DCAPI GetNextReadyBuffer( HCAPDEV hcd, CAPFRAMEINFO* lpcfi ) { LPCAPBUFFER lpcbuf; DWORD_PTR dwlpvh; BOOL fRet; FX_ENTRY("GetNextReadyBuffer"); INIT_CAP_DEV_LIST(); ENTER_DCAP(hcd); if (hcd->dwFlags & HCAPDEV_STREAMING_FRAME_GRAB) { lpcbuf = (LPCAPBUFFER)hcd->lpHead; if ((hcd->dwFlags & HCAPDEV_STREAMING_FRAME_TIME) && (lpcbuf != (LPCAPBUFFER)(((LPBYTE)&hcd->lpHead) - sizeof(VIDEOHDR)))) /* fake CAPBUFFERHDR */ { // remove buffer from list EnterCriticalSection(&hcd->bufferlistCS); hcd->dwFlags &= ~HCAPDEV_STREAMING_FRAME_TIME; lpcbuf->lpPrev->lpNext = lpcbuf->lpNext; lpcbuf->lpNext->lpPrev = lpcbuf->lpPrev; lpcbuf->vh.dwFlags &= ~VHDR_INQUEUE; lpcbuf->vh.dwFlags |= VHDR_DONE; LeaveCriticalSection(&hcd->bufferlistCS); dwlpvh = (DWORD_PTR)lpcbuf->vh.lpData - sizeof(CAPBUFFERHDR); // 16:16 ptr to vh = 16:16 ptr to data - sizeof(CAPBUFFERHDR) // 32bit ptr to vh = 32bit ptr to data - sizeof(CAPBUFFERHDR) if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) fRet = (SendDriverMessage((HDRVR)hcd->hvideoIn, DVM_FRAME, dwlpvh, sizeof(VIDEOHDR)) == DV_ERR_OK); else fRet = WDMGetFrame(hcd->nDeviceIndex, (PVOID)dwlpvh); if (!fRet) { // put buffer back into list EnterCriticalSection(&hcd->bufferlistCS); lpcbuf->lpPrev = hcd->lpTail; hcd->lpTail = lpcbuf; lpcbuf->lpNext = lpcbuf->lpPrev->lpNext; lpcbuf->lpPrev->lpNext = lpcbuf; lpcbuf->vh.dwFlags |= VHDR_INQUEUE; LeaveCriticalSection(&hcd->bufferlistCS); lpcbuf = NULL; } } else lpcbuf = NULL; } else { #ifdef __NT_BUILD__ // If the current pointer is NULL, there is no frame ready so bail if (!hcd->lpCurrent) lpcbuf = NULL; else { // Get the linear address of the buffer lpcbuf = hcd->lpCurrent; // Move to the next ready buffer hcd->lpCurrent = lpcbuf->lpPrev; } #else //-------------------- // Buffer ready queue: // We maintain a doubly-linked list of our buffers so that we can buffer up // multiple ready frames when the app isn't ready to handle them. Two things // complicate what ought to be a very simple thing: (1) Thunking issues: the pointers // used on the 16-bit side are 16:16 (2) Interrupt time issues: the FrameCallback // gets called at interrupt time. GetNextReadyBuffer must handle the fact that // buffers get added to the list asynchronously. // // To handle this, the scheme implemented here is to have a double-linked list // of buffers with all insertions and deletions happening in FrameCallback // (interrupt time). This allows the GetNextReadyBuffer routine to simply // find the previous block on the list any time it needs a new buffer without // fear of getting tromped (as would be the case if it had to dequeue buffers). // The FrameCallback routine is responsible to dequeue blocks that GetNextReadyBuffer // is done with. Dequeueing is simple since we don't need to unlink the blocks: // no code ever walks the list! All we have to do is move the tail pointer back up // the list. All the pointers, head, tail, next, prev, are all 16:16 pointers // since all the list manipulation is on the 16-bit side AND because MapSL is // much more efficient and safer than MapLS since MapLS has to allocate selectors. //-------------------- // If the current pointer is NULL, there is no frame ready so bail if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) { if (!hcd->lpli->lp1616Current) lpcbuf = NULL; else { // Get the linear address of the buffer lpcbuf = (LPCAPBUFFER)MapSL(hcd->lpli->lp1616Current); // Move to the next ready buffer hcd->lpli->lp1616Current = lpcbuf->lp1616Prev; } } else { // If the current pointer is NULL, there is no frame ready so bail if (!hcd->lpCurrent) lpcbuf = NULL; else { // Get the linear address of the buffer lpcbuf = hcd->lpCurrent; // Move to the next ready buffer hcd->lpCurrent = lpcbuf->lpPrev; } } #endif } if (!lpcbuf) { lpcfi->lpData = NULL; DEBUGMSG(ZONE_STREAMING, ("\r\n { %s: Fails with lpcbuf=NULL\r\n", _fx_)); LEAVE_DCAP(hcd); return NULL; } // Build the CAPFRAMEINFO from the VIDEOHDR information lpcfi->lpData = ((LPSTR)lpcbuf) + sizeof(CAPBUFFERHDR); lpcfi->dwcbData = lpcbuf->vh.dwBytesUsed; lpcfi->dwTimestamp = lpcbuf->vh.dwTimeCaptured; lpcfi->dwFlags = 0; lpcbuf->lpNext = NULL; DEBUGMSG(ZONE_STREAMING, ("\r\n { %s: Succeeded with lpcbuf=0x%08lX\r\n lpcbuf->vh.lpData=0x%08lX\r\n lpcbuf->vh.dwBufferLength=%ld\r\n", _fx_, lpcbuf, lpcbuf->vh.lpData, lpcbuf->vh.dwBufferLength)); DEBUGMSG(ZONE_STREAMING, (" lpcbuf->vh.dwBytesUsed=%ld\r\n lpcbuf->vh.dwTimeCaptured=%ld\r\n lpcbuf->vh.dwFlags=0x%08lX\r\n", lpcbuf->vh.dwBytesUsed, lpcbuf->vh.dwTimeCaptured, lpcbuf->vh.dwFlags)); LEAVE_DCAP(hcd); return lpcfi->lpData; } // PutBufferIntoStream // When the app is finished using a buffer, it must allow it to be requeued // by calling this API. BOOL DCAPI PutBufferIntoStream( HCAPDEV hcd, BYTE* lpBits ) { LPCAPBUFFER lpcbuf; DWORD_PTR dwlpvh; BOOL res; FX_ENTRY("PutBufferIntoStream"); INIT_CAP_DEV_LIST(); ENTER_DCAP(hcd); // From the CAPFRAMEINFO, find the appropriate CAPBUFFER pointer lpcbuf = (LPCAPBUFFER)(lpBits - sizeof(CAPBUFFERHDR)); DEBUGMSG(ZONE_STREAMING, ("\r\n%s: Returning buffer lpcbuf=0x%08lX\r\n", _fx_, lpcbuf)); lpcbuf->vh.dwFlags &= ~VHDR_DONE; // mark that app no longer owns buffer if (hcd->dwFlags & HCAPDEV_STREAMING_FRAME_GRAB) { EnterCriticalSection(&hcd->bufferlistCS); lpcbuf->lpPrev = hcd->lpTail; hcd->lpTail = lpcbuf; lpcbuf->lpNext = lpcbuf->lpPrev->lpNext; lpcbuf->lpPrev->lpNext = lpcbuf; lpcbuf->vh.dwFlags |= VHDR_INQUEUE; res = TRUE; LeaveCriticalSection(&hcd->bufferlistCS); } else if (!(hcd->dwFlags & HCAPDEV_STREAMING_PAUSED)) { // if streaming is paused, then just return with the busy bit cleared, we'll add the // buffer into the stream in ReinitStreaming // // if streaming isn't paused, then call the driver to add the buffer dwlpvh = (DWORD_PTR)lpcbuf->vh.lpData - sizeof(CAPBUFFERHDR); // 16:16 ptr to vh = 16:16 ptr to data - sizeof(CAPBUFFERHDR) // 32bit ptr to vh = 32bit ptr to data - sizeof(CAPBUFFERHDR) if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) res = (_SendDriverMessage((HDRVR)hcd->hvideoIn, DVM_STREAM_ADDBUFFER, dwlpvh, sizeof(VIDEOHDR)) == DV_ERR_OK); else res = WDMVideoStreamAddBuffer(hcd->nDeviceIndex, (PVOID)dwlpvh); if (res) { DEBUGMSG(ZONE_STREAMING, (" } %s: Succeeded with lpcbuf=0x%08lX\r\n lpcbuf->vh.lpData=0x%08lX\r\n dwlpvh=0x%08lX\r\n", _fx_, lpcbuf, lpcbuf->vh.lpData, dwlpvh)); } else { DEBUGMSG(ZONE_STREAMING, (" } %s: Failed with lpcbuf=0x%08lX\r\n lpcbuf->vh.lpData=0x%08lX\r\n dwlpvh=0x%08lX\r\n", _fx_, lpcbuf, lpcbuf->vh.lpData, dwlpvh)); } } LEAVE_DCAP(hcd); return res; } // CaptureFrame LPBYTE DCAPI CaptureFrame( HCAPDEV hcd, HFRAMEBUF hbuf ) { DWORD_PTR dwlpvh; LPBYTE lpbuf; BOOL fRet; FX_ENTRY("CaptureFrame"); INIT_CAP_DEV_LIST(); VALIDATE_CAPDEV(hcd); ENTER_DCAP(hcd); dwlpvh = (DWORD_PTR)hbuf->vh.lpData - sizeof(CAPBUFFERHDR); // 16:16 ptr to vh = 16:16 ptr to data - sizeof(CAPBUFFERHDR) // 32bit ptr to vh = 32bit ptr to data - sizeof(CAPBUFFERHDR) if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) fRet = (_SendDriverMessage((HDRVR)hcd->hvideoIn, DVM_FRAME, dwlpvh, sizeof(VIDEOHDR)) == DV_ERR_OK); else fRet = WDMGetFrame(hcd->nDeviceIndex, (PVOID)dwlpvh); if (!fRet) { ERRORMESSAGE(("%s: DVM_FRAME failed!\r\n", _fx_)); lpbuf = NULL; } else lpbuf = ((LPBYTE)hbuf) + sizeof(CAPBUFFERHDR); // return ptr to buffer immediately following hdr LEAVE_DCAP(hcd); return lpbuf; } LPBYTE DCAPI GetFrameBufferPtr( HCAPDEV hcd, HFRAMEBUF hbuf ) { INIT_CAP_DEV_LIST(); if (hbuf) return ((LPBYTE)hbuf) + sizeof(CAPBUFFERHDR); // return ptr to buffer immediately following hdr else return NULL; } HFRAMEBUF DCAPI AllocFrameBuffer( HCAPDEV hcd ) { LPCAPBUFFER hbuf = NULL; DWORD_PTR dpBuf; INIT_CAP_DEV_LIST(); ENTER_DCAP(hcd); #ifdef __NT_BUILD__ if (dpBuf = (DWORD_PTR)LocalAlloc(LPTR, hcd->dwcbBuffers)) { hbuf = (LPCAPBUFFER)dpBuf; #else if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) { dpBuf = (DWORD)_AllocateLockableBuffer(hcd->dwcbBuffers) << 16; hbuf = (LPCAPBUFFER)MapSL(dpBuf); } else { dpBuf = (DWORD)LocalAlloc(LPTR, hcd->dwcbBuffers); hbuf = (LPCAPBUFFER)dpBuf; } if (dpBuf) { #endif // Initialize the VIDEOHDR structure hbuf->vh.lpData = (LPBYTE)(dpBuf + sizeof(CAPBUFFERHDR)); hbuf->vh.dwBufferLength = hcd->dwcbBuffers - sizeof(CAPBUFFERHDR); hbuf->vh.dwFlags = 0UL; } LEAVE_DCAP(hcd); return (HFRAMEBUF)hbuf; } VOID DCAPI FreeFrameBuffer( HCAPDEV hcd, HFRAMEBUF hbuf ) { INIT_CAP_DEV_LIST(); if (hbuf) { ENTER_DCAP(hcd); #ifdef __NT_BUILD__ LocalFree((HANDLE)hbuf); #else if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) _FreeLockableBuffer(HIWORD((DWORD)hbuf->vh.lpData)); else LocalFree((HANDLE)hbuf); #endif LEAVE_DCAP(hcd); } } //===================================================================== // Helper functions HVIDEO openVideoChannel( DWORD dwDeviceID, DWORD dwFlags ) { HVIDEO hvidRet = NULL; VIDEO_OPEN_PARMS vop; #ifdef __NT_BUILD__ WCHAR devName[MAX_PATH]; #else #define LPWSTR LPSTR #define devName g_aCapDevices[dwDeviceID]->szDeviceName #endif FX_ENTRY("openVideoChannel"); DEBUGMSG(ZONE_CALLS, ("%s() - Begin\r\n", _fx_)); // Validate parameters if (!g_cDevices) { SetLastError(ERROR_DCAP_BAD_INSTALL); DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return NULL; } if (dwDeviceID > (DWORD)g_cDevices) { SetLastError(ERROR_INVALID_PARAMETER); DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return NULL; } // Prepare to call the driver vop.dwSize = sizeof (VIDEO_OPEN_PARMS); vop.fccType = OPEN_TYPE_VCAP; vop.fccComp = 0L; vop.dwVersion = VIDEOAPIVERSION; vop.dwFlags = dwFlags; // In, Out, External In, External Out vop.dwError = 0; vop.dnDevNode = g_aCapDevices[dwDeviceID]->dwDevNode; #ifdef __NT_BUILD__ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (LPSTR)&(g_aCapDevices[dwDeviceID]->szDeviceName), -1, (LPWSTR)devName, MAX_PATH); #endif hvidRet = (HVIDEO)_OpenDriver((LPWSTR)devName, NULL, (LONG_PTR)&vop); #ifndef NO_DRIVER_HACKS if (!hvidRet) { // backward compatibility hack // Some drivers fail to open because of the extra fields that were added to // VIDEO_OPEN_PARAMS struct for Win95. Therefore, if the open fails, try // decrementing the dwSize field back to VFW1.1 size and try again. Also try // decrementing the API version field. vop.dwSize -= sizeof(DWORD) + sizeof(LPVOID)*2; #if 0 while (--vop.dwVersion > 2 && !hvidRet) #endif while (--vop.dwVersion > 0 && !hvidRet) hvidRet = (HVIDEO)_OpenDriver((LPWSTR)devName, NULL, (LONG_PTR)&vop); } #endif //NO_DRIVER_HACKS // BUGBUG [JonT] 31-Jul-96 // Translate error values from DV_ERR_* values if (!hvidRet) SetLastError(vop.dwError); DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return hvidRet; } // allocateBuffers BOOL allocateBuffers( HCAPDEV hcd, int nBuffers ) { int i; LPCAPBUFFER lpcbuf; DWORD_PTR dpBuf; FX_ENTRY("allocateBuffers"); DEBUGMSG(ZONE_CALLS, ("%s() - Begin\r\n", _fx_)); // Try to allocate all they ask for for (i = 0 ; i < nBuffers ; i++) { #ifdef __NT_BUILD__ if (!(dpBuf = (DWORD_PTR)LocalAlloc(LPTR, hcd->dwcbBuffers))) goto Error; else lpcbuf = (LPCAPBUFFER)dpBuf; #else if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) { if (!(dpBuf = (DWORD)_AllocateLockableBuffer(hcd->dwcbBuffers) << 16) || !_LockBuffer((WORD)(dpBuf >> 16))) goto Error; else lpcbuf = (LPCAPBUFFER)MapSL(dpBuf); } else { if (!(dpBuf = (DWORD)LocalAlloc(LPTR, hcd->dwcbBuffers))) goto Error; else lpcbuf = (LPCAPBUFFER)dpBuf; } #endif // Initialize the VIDEOHDR structure lpcbuf->vh.lpData = (LPBYTE)(dpBuf + sizeof(CAPBUFFERHDR)); lpcbuf->vh.dwUser = (DWORD_PTR)hcd->lpcbufList; hcd->lpcbufList = lpcbuf; lpcbuf->vh.dwBufferLength = hcd->dwcbBuffers - sizeof(CAPBUFFERHDR); lpcbuf->vh.dwFlags = 0UL; } #ifdef _DEBUG // Show buffer map DEBUGMSG(ZONE_STREAMING, ("%s: Streaming Buffer map:\r\n", _fx_)); DEBUGMSG(ZONE_STREAMING, ("Root: hcd->lpcbufList=0x%08lX\r\n", hcd->lpcbufList)); for (i = 0, lpcbuf=hcd->lpcbufList ; i < nBuffers ; i++, lpcbuf=(LPCAPBUFFER)lpcbuf->vh.dwUser) { DEBUGMSG(ZONE_STREAMING, ("Buffer[%ld]: lpcbuf=0x%08lX\r\n lpcbuf->vh.lpData=0x%08lX\r\n", i, lpcbuf, lpcbuf->vh.lpData)); DEBUGMSG(ZONE_STREAMING, (" lpcbuf->vh.dwBufferLength=%ld\r\n lpcbuf->vh.dwBytesUsed=%ld\r\n", lpcbuf->vh.dwBufferLength, lpcbuf->vh.dwBytesUsed)); DEBUGMSG(ZONE_STREAMING, (" lpcbuf->vh.dwTimeCaptured=%ld\r\n lpcbuf->vh.dwUser=0x%08lX\r\n", lpcbuf->vh.dwTimeCaptured, lpcbuf->vh.dwUser)); } #endif DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return TRUE; // In the error case, we have to get rid of this page locked memory Error: freeBuffers(hcd); DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); return FALSE; } // freeBuffers void freeBuffers( HCAPDEV hcd ) { LPCAPBUFFER lpcbuf; FX_ENTRY("freeBuffers"); DEBUGMSG(ZONE_CALLS, ("%s() - Begin\r\n", _fx_)); while (hcd->lpcbufList) { lpcbuf = hcd->lpcbufList; hcd->lpcbufList = (LPCAPBUFFER)lpcbuf->vh.dwUser; #ifdef __NT_BUILD__ LocalFree((HANDLE)lpcbuf); #else if (!(hcd->dwFlags & WDM_CAPTURE_DEVICE)) { _UnlockBuffer(HIWORD((DWORD)lpcbuf->vh.lpData)); _FreeLockableBuffer(HIWORD((DWORD)lpcbuf->vh.lpData)); } else LocalFree((HANDLE)lpcbuf); #endif } DEBUGMSG(ZONE_CALLS, ("%s() - End\r\n", _fx_)); }