This article explains a method to perform previewing and grabbing simultaneously from a Webcam using DirectShow. Rather than using the conventional ISampleGrabber interface, this application used Directshow VMR filters & IPin interfaces.
There are many solutions for capturing and previewing, but only a few combine both. I tried to make an application that serves this purpose by using the ISampleGrabber and IVideoWindow interfaces. In this this article, I will discuss the "VMR filters & IPin interfaces" method, which worked on Win2000 and WinXP.
The DirectShow "Windowless Sample" (Windowless mode in the Video Mixing Renderer) gives the clue for capturing frames while previewing.
Using the Code
I had created a class named CVMR_Capture, and then inserted the VMR_Capture.h and VMR_Capture.cpp files into project.
Link strmiids.lib quartz.lib in VC++ Project Settings. Add a static control into your dialog resource. Add a member variable for that static control. Declare a member variable of CVMR_Capture.
Functions and Explanation
The public interfaces provided are:
int EnumDevices(HWND hList); HRESULT Init(int iDeviceID,HWND hWnd,int iWidth,int iHeight); DWORD GetFrame(BYTE ** pFrame); BOOL StopVMR(); BOOL Pause(); DWORD ImageCapture(LPCTSTR szFile); DWORD GrabFrame();
int EnumDevices(HWND hList); (Optional)
Enumerate the available devices connected to the computer. HWND is the HANDLE to a combo box that displays the connected Devices.
HRESULT Init(int iDeviceID,HWND hWnd,int iWidth,int iHeight);
Intitializing, "iDeviceID" is the selected device index (from the combo box) and "hWnd" is the handle of the display window. iWidth and iHeight are the expected resolution (eg 320/240). The display will adjust iteself to the size of the window.
DWORD GrabFrame(); DWORD GetFrame(BYTE ** pFrame);
Call this function to get the captured buffer. GrabFrame(); will grab the image, store it in a buffer, and return the value that is the size of the buffer. The user can get the buffer pointer by calling GetFrame(BYTE ** pFrame), whichs return the size of the buffer.
Note: DONT DELETE the "pFrame" BUFFER VALUE, which the class destructor will delete. Use the Pointer(pFrame) for processing or copying the memory to a user-allocated Buffer.
Pause capturing. Call this method again to restart capturing.
DWORD ImageCapture(LPCTSTR szFile);
Capture and write the raw image BYTES (RGB) to a file provided.
Special Functions and Explanation
bool BindFilter(int deviceId, IBaseFilter **pFilter); HRESULT InitializeWindowlessVMR(HWND hWnd); HRESULT InitVideoWindow(HWND hWnd,int width, int height); void StopCapture(); void CloseInterfaces(void); void DeleteMediaType(AM_MEDIA_TYPE *pmt); bool Convert24Image(BYTE *p32Img,BYTE *p24Img,DWORD dwSize32);
bool BindFilter(int deviceId, IBaseFilter **pFilter);
Bind the filter to the given device ID.
HRESULT InitializeWindowlessVMR(HWND hWnd);
VMR intialization to the HWND provided.
HRESULT InitVideoWindow(HWND hWnd,int width, int height);
Adjusts the display video size according to the size of display window.
Release & delete the interfaces/allocated memory.
void DeleteMediaType(AM_MEDIA_TYPE *pmt);
Delete media type object.
bool Convert24Image(BYTE *p32Img,BYTE *p24Img,DWORD dwSize32);
The captured image using VMR is a 32-bit RGB. This function will convert the 32-bit RGB to a 24-bit RGB.
Points of Interest
This class has been tested on Windows 2000 & Windows XP with DirectX9.0. Both VMR7 & VMR9 are supported. I haven't included the DirectShow Event Handling part in this sample. Please attach the required exception/error handling mechanism when you use it.
Please drop me an e-mail about any bugs or suggestions to enhance the class. Your suggestions and guidance will be most valuable.