windows-nt/Source/XPSP1/NT/enduser/netmeeting/av/dcap/32/dcap32.cpp

2321 lines
65 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//
// 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<HDRVR>(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 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)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 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)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)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)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)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)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 = _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 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)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 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)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 dwlpvh;
LPBYTE lpbuf;
BOOL fRet;
FX_ENTRY("CaptureFrame");
INIT_CAP_DEV_LIST();
VALIDATE_CAPDEV(hcd);
ENTER_DCAP(hcd);
dwlpvh = (DWORD)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 dpBuf;
INIT_CAP_DEV_LIST();
ENTER_DCAP(hcd);
#ifdef __NT_BUILD__
if (dpBuf = (DWORD)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)&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)&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 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)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)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_));
}