📄 vfw.cxx
字号:
virtual BOOL GetFrameData(
BYTE * buffer, /// Buffer to receive frame
PINDEX * bytesReturned = NULL /// OPtional bytes returned.
);
/**Grab a frame. Do not delay according to the current frame rate parameter.
*/
virtual BOOL GetFrameDataNoDelay(
BYTE * buffer, /// Buffer to receive frame
PINDEX * bytesReturned = NULL /// OPtional bytes returned.
);
/**Try all known video formats & see which ones are accepted by the video driver
*/
virtual BOOL TestAllFormats();
protected:
/**Check the hardware can do the asked for size.
Note that not all cameras can provide all frame sizes.
*/
virtual BOOL VerifyHardwareFrameSize(unsigned width, unsigned height);
PDECLARE_NOTIFIER(PThread, PVideoInputDevice_VideoForWindows, HandleCapture);
static LRESULT CALLBACK ErrorHandler(HWND hWnd, int id, LPCSTR err);
LRESULT HandleError(int id, LPCSTR err);
static LRESULT CALLBACK VideoHandler(HWND hWnd, LPVIDEOHDR vh);
LRESULT HandleVideo(LPVIDEOHDR vh);
BOOL InitialiseCapture();
PThread * captureThread;
PSyncPoint threadStarted;
HWND hCaptureWindow;
PSyncPoint frameAvailable;
LPBYTE lastFramePtr;
unsigned lastFrameSize;
PMutex lastFrameMutex;
BOOL isCapturingNow;
};
///////////////////////////////////////////////////////////////////////////////
class PCapStatus : public CAPSTATUS
{
public:
PCapStatus(HWND hWnd);
BOOL IsOK()
{ return uiImageWidth != 0; }
};
///////////////////////////////////////////////////////////////////////////////
static struct FormatTableEntry {
const char * colourFormat;
WORD bitCount;
BOOL negHeight; // MS documentation suggests that negative height will request
// top down scan direction from video driver
// HOWEVER, not all drivers honor this request
DWORD compression;
} FormatTable[] = {
{ "BGR32", 32, TRUE, BI_RGB },
{ "BGR24", 24, TRUE, BI_RGB },
{ "Grey", 8, TRUE, BI_RGB },
{ "Gray", 8, TRUE, BI_RGB },
{ "RGB565", 16, TRUE, BI_BITFIELDS },
{ "RGB555", 15, TRUE, BI_BITFIELDS },
// http://support.microsoft.com/support/kb/articles/q294/8/80.asp
{ "YUV420P", 12, FALSE, mmioFOURCC('I','Y','U','V') },
{ "IYUV", 12, FALSE, mmioFOURCC('I','Y','U','V') }, // Synonym for IYUV
{ "I420", 12, FALSE, mmioFOURCC('I','4','2','0') }, // Synonym for IYUV
{ "YV12", 12, FALSE, mmioFOURCC('Y','V','1','2') }, // same as IYUV except that U and V planes are switched
{ "YUV422", 16, FALSE, mmioFOURCC('Y','U','Y','2') },
{ "YUY2", 16, FALSE, mmioFOURCC('Y','U','Y','2') },
{ "UYVY", 16, FALSE, mmioFOURCC('U','Y','V','Y') }, // Like YUY2 except for ordering
{ "YVYU", 16, FALSE, mmioFOURCC('Y','V','Y','U') }, // Like YUY2 except for ordering
{ "YVU9", 16, FALSE, mmioFOURCC('Y','V','U','9') },
{ "MJPEG", 12, FALSE, mmioFOURCC('M','J','P','G') },
{ NULL },
};
static struct {
unsigned device_width, device_height;
} winTestResTable[] = {
{ 176, 144 },
{ 352, 288 },
{ 320, 240 },
{ 160, 120 },
{ 640, 480 },
{ 704, 576 },
{1024, 768 },
};
///////////////////////////////////////////////////////////////////////////////
class PVideoDeviceBitmap : PBYTEArray
{
public:
// the following method is replaced by PVideoDeviceBitmap(HWND hWnd, WORD bpp)
// PVideoDeviceBitmap(unsigned width, unsigned height, const PString & fmt);
//returns object with gray color pallet if needed for 8 bpp formats
PVideoDeviceBitmap(HWND hWnd, WORD bpp);
// does not build color pallet
PVideoDeviceBitmap(HWND hWnd);
// apply video format to capture device
BOOL ApplyFormat(HWND hWnd, const FormatTableEntry & formatTableEntry);
BITMAPINFO * operator->() const
{ return (BITMAPINFO *)theArray; }
};
PVideoDeviceBitmap::PVideoDeviceBitmap(HWND hCaptureWindow)
{
PINDEX sz = capGetVideoFormatSize(hCaptureWindow);
SetSize(sz);
if (!capGetVideoFormat(hCaptureWindow, theArray, sz)) {
PTRACE(1, "PVidInp\tcapGetVideoFormat(hCaptureWindow) failed - " << ::GetLastError());
SetSize(0);
return;
}
}
PVideoDeviceBitmap::PVideoDeviceBitmap(HWND hCaptureWindow, WORD bpp)
{
PINDEX sz = capGetVideoFormatSize(hCaptureWindow);
SetSize(sz);
if (!capGetVideoFormat(hCaptureWindow, theArray, sz)) {
PTRACE(1, "PVidInp\tcapGetVideoFormat(hCaptureWindow) failed - " << ::GetLastError());
SetSize(0);
return;
}
if (8 == bpp && bpp != ((BITMAPINFO*)theArray)->bmiHeader.biBitCount) {
SetSize(sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256);
RGBQUAD * bmiColors = ((BITMAPINFO*)theArray)->bmiColors;
for (int i = 0; i < 256; i++)
bmiColors[i].rgbBlue = bmiColors[i].rgbGreen = bmiColors[i].rgbRed = (BYTE)i;
}
((BITMAPINFO*)theArray)->bmiHeader.biBitCount = bpp;
}
BOOL PVideoDeviceBitmap::ApplyFormat(HWND hWnd, const FormatTableEntry & formatTableEntry)
{
// NB it is necessary to set the biSizeImage value appropriate to frame size
// assume bmiHeader.biBitCount has already been set appropriatly for format
BITMAPINFO & bmi = *(BITMAPINFO*)theArray;
bmi.bmiHeader.biPlanes = 1;
int height = bmi.bmiHeader.biHeight<0 ? -bmi.bmiHeader.biHeight : bmi.bmiHeader.biHeight;
bmi.bmiHeader.biSizeImage = height*4*((bmi.bmiHeader.biBitCount * bmi.bmiHeader.biWidth + 31)/32);
// set .biHeight according to .negHeight value
if (formatTableEntry.negHeight)
bmi.bmiHeader.biHeight = -height;
#if PTRACING
PTimeInterval startTime = PTimer::Tick();
#endif
if (capSetVideoFormat(hWnd, theArray, GetSize())) {
PTRACE(3, "PVidInp\tcapSetVideoFormat succeeded: "
<< formatTableEntry.colourFormat << ' '
<< bmi.bmiHeader.biWidth << "x" << bmi.bmiHeader.biHeight
<< " sz=" << bmi.bmiHeader.biSizeImage << " time=" << (PTimer::Tick() - startTime));
return TRUE;
}
if (formatTableEntry.negHeight) {
bmi.bmiHeader.biHeight = height;
if (capSetVideoFormat(hWnd, theArray, GetSize())) {
PTRACE(3, "PVidInp\tcapSetVideoFormat succeeded: "
<< formatTableEntry.colourFormat << ' '
<< bmi.bmiHeader.biWidth << "x" << bmi.bmiHeader.biHeight
<< " sz=" << bmi.bmiHeader.biSizeImage << " time=" << (PTimer::Tick() - startTime));
return TRUE;
}
}
PTRACE(1, "PVidInp\tcapSetVideoFormat failed: "
<< formatTableEntry.colourFormat << ' '
<< bmi.bmiHeader.biWidth << "x" << bmi.bmiHeader.biHeight
<< " sz=" << bmi.bmiHeader.biSizeImage << " time=" << (PTimer::Tick() - startTime)
<< " - lastError=" << ::GetLastError());
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
PCapStatus::PCapStatus(HWND hWnd)
{
memset(this, 0, sizeof(*this));
if (capGetStatus(hWnd, this, sizeof(*this)))
return;
PTRACE(1, "PVidInp\tcapGetStatus: failed - " << ::GetLastError());
}
///////////////////////////////////////////////////////////////////////////////
// PVideoInputDevice_VideoForWindows
PCREATE_VIDINPUT_PLUGIN(VideoForWindows);
PVideoInputDevice_VideoForWindows::PVideoInputDevice_VideoForWindows()
{
captureThread = NULL;
hCaptureWindow = NULL;
lastFramePtr = NULL;
lastFrameSize = 0;
isCapturingNow = FALSE;
}
BOOL PVideoInputDevice_VideoForWindows::Open(const PString & devName, BOOL startImmediate)
{
Close();
deviceName = devName;
captureThread = PThread::Create(PCREATE_NOTIFIER(HandleCapture), 0,
PThread::NoAutoDeleteThread, PThread::NormalPriority,
"VidIn:%x");
threadStarted.Wait();
if (hCaptureWindow == NULL) {
delete captureThread;
captureThread = NULL;
return FALSE;
}
if (startImmediate)
return Start();
return TRUE;
}
BOOL PVideoInputDevice_VideoForWindows::IsOpen()
{
return hCaptureWindow != NULL;
}
BOOL PVideoInputDevice_VideoForWindows::Close()
{
if (!IsOpen())
return FALSE;
Stop();
::PostThreadMessage(captureThread->GetThreadId(), WM_QUIT, 0, 0L);
// Some brain dead drivers may hang so we provide a timeout.
if (!captureThread->WaitForTermination(5000))
{
// Two things may happen if we are forced to terminate the capture thread:
// 1. As the VIDCAP window is associated to that thread the OS itself will
// close the window and release the driver
// 2. the driver will not be released and we will not have video until we
// terminate the process
// Any of the two ios better than just hanging
captureThread->Terminate();
hCaptureWindow = NULL;
PTRACE(1, "PVidInp\tCapture thread failed to stop. Terminated");
}
delete captureThread;
captureThread = NULL;
return TRUE;
}
BOOL PVideoInputDevice_VideoForWindows::Start()
{
if (IsCapturing())
return TRUE;
#if STEP_GRAB_CAPTURE
isCapturingNow = TRUE;
return capGrabFrameNoStop(hCaptureWindow);
#else
if (capCaptureSequenceNoFile(hCaptureWindow)) {
PCapStatus status(hCaptureWindow);
isCapturingNow = status.fCapturingNow;
return isCapturingNow;
}
lastError = ::GetLastError();
PTRACE(1, "PVidInp\tcapCaptureSequenceNoFile: failed - " << lastError);
return FALSE;
#endif
}
BOOL PVideoInputDevice_VideoForWindows::Stop()
{
if (!IsCapturing())
return FALSE;
isCapturingNow = FALSE;
#if STEP_GRAB_CAPTURE
return IsOpen() && frameAvailable.Wait(1000);
#else
if (capCaptureStop(hCaptureWindow))
return TRUE;
lastError = ::GetLastError();
PTRACE(1, "PVidInp\tcapCaptureStop: failed - " << lastError);
return FALSE;
#endif
}
BOOL PVideoInputDevice_VideoForWindows::IsCapturing()
{
return isCapturingNow;
}
BOOL PVideoInputDevice_VideoForWindows::SetColourFormat(const PString & colourFmt)
{
BOOL running = IsCapturing();
if (running)
Stop();
PString oldFormat = colourFormat;
if (!PVideoDevice::SetColourFormat(colourFmt))
return FALSE;
PINDEX i = 0;
while (FormatTable[i].colourFormat != NULL && !(colourFmt *= FormatTable[i].colourFormat))
i++;
PVideoDeviceBitmap bi(hCaptureWindow, FormatTable[i].bitCount);
if (FormatTable[i].colourFormat != NULL)
bi->bmiHeader.biCompression = FormatTable[i].compression;
else if (colourFmt.GetLength() == 4)
bi->bmiHeader.biCompression = mmioFOURCC(colourFmt[0],colourFmt[1],colourFmt[2],colourFmt[3]);
else {
bi->bmiHeader.biCompression = 0xffffffff; // Indicate invalid colour format
return FALSE;
}
// set frame width and height
bi->bmiHeader.biWidth = frameWidth;
bi->bmiHeader.biHeight = frameHeight;
if (!bi.ApplyFormat(hCaptureWindow, FormatTable[i])) {
lastError = ::GetLastError();
PVideoDevice::SetColourFormat(oldFormat);
return FALSE;
}
// Didn't do top down, tell everything we are up side down
nativeVerticalFlip = FormatTable[i].negHeight && bi->bmiHeader.biHeight > 0;
if (running)
return Start();
return TRUE;
}
BOOL PVideoInputDevice_VideoForWindows::SetFrameRate(unsigned rate)
{
if (!PVideoDevice::SetFrameRate(rate))
return FALSE;
BOOL running = IsCapturing();
if (running)
Stop();
CAPTUREPARMS parms;
memset(&parms, 0, sizeof(parms));
if (!capCaptureGetSetup(hCaptureWindow, &parms, sizeof(parms))) {
lastError = ::GetLastError();
PTRACE(1, "PVidInp\tcapCaptureGetSetup: failed - " << lastError);
return FALSE;
}
// keep current (default) framerate if 0==frameRate
if (0 != frameRate)
parms.dwRequestMicroSecPerFrame = 1000000 / frameRate;
parms.fMakeUserHitOKToCapture = FALSE;
parms.wPercentDropForError = 100;
parms.fCaptureAudio = FALSE;
parms.fAbortLeftMouse = FALSE;
parms.fAbortRightMouse = FALSE;
parms.fLimitEnabled = FALSE;
if (!capCaptureSetSetup(hCaptureWindow, &parms, sizeof(parms))) {
lastError = ::GetLastError();
PTRACE(1, "PVidInp\tcapCaptureSetSetup: failed - " << lastError);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -