No han sido pocas las ocaciones en que personas conocidas han acudido a mi a formularme la misma pregunta. ¿Cómo puedo espiar una WebCam? A través de un ejemplo pienso exponer de la manera más clara posible los pasos a seguir para tomar el control de este dispositivo de captura. Por esta véz nada de componentes visuales mediocres que hagan todo el trabajo por nosotros, el código va dirigido a las personas que de verdad quieren saber como trabajar con los dispositivos de captura en Windows, y la única manera de hacerlo es a través de las API.
Este conocimiento puede ser utilizado para identificar primitivas geoméricas con la imagen obtenida, puedes usarlo para poner un sensor de movimiento en tu cuarto, o puedes usarlo para espiar la WebCam de tu vecina que está muy buena. Con esto quiero decir que el uso que le des depende de ti, puedes hacer cosas muy constructivas o muy retorcidas. Me encantaría hacer un artículo más explicativo del tema pero desafortunadamente el tiempo no me alcanza. Espero que basten los comentarios que acompañan al código, cualquier duda pueden consultar el SDK de Windows o hacermela llegar directamente a mi correo.
A continuación el código. El ejemplo compilado lo puedes bajar de aquí.
//---------------------------------------------------------------------------
/*
** Nombre: WebCamPreview.cpp
** Autor: h0aX
** Fecha: 27/08/2007
** Descripción: Ejemplo para utilización de dispositivos de captura en Windows.
*/
//---------------------------------------------------------------------------
#include <windows.h>
#include <vfw.h> /*aquí encontraremos todo lo necesario para
dispositivos de captura*/
#include <stdio.h>
//--------------------------VARIABLES GLOBALES-------------------------------
HWND hWndMain, hWnd_btnPicture, hWnd_btnSequence,
hWnd_btnStop, hWnd_btnAbout,
hWnd_cbxNegative,
hWndCapture; //manejador del dispositivo de captura
HINSTANCE hInst;
//-----------------------DECLARACIÓN DE FUNCIONES----------------------------
LRESULT CALLBACK Frame(HWND w, LPVIDEOHDR h);
BOOL IniciaWebCam(HANDLE Parent);
LRESULT CALLBACK WndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
//---------------------------------------------------------------------------
/* Esta función será llamada antes del frame ser mostrado, así
** que podremos usarla para muchas cosas, enviar el frame por la
** red, detectar movimiento, cambiar algo en el frame, etc.
** En este caso lo uso para poner la imagen en negativo.
*/
LRESULT CALLBACK Frame(HWND w, LPVIDEOHDR h)
{
typedef struct {BYTE b,g,r;} PIXEL;
for (DWORD i=0; i<h->dwBytesUsed/3; i++)
{
PIXEL *pixel = &((PIXEL*)h->lpData)[i];
pixel->r = ~pixel->r;
pixel->g = ~pixel->g;
pixel->b = ~pixel->b;
}
return TRUE;
}
//---------------------------------------------------------------------------
/*
** Inicialización de nuestro dispositivo de captura
*/
BOOL IniciaWebCam(HANDLE Parent)
{
//creamos la ventana de captura
hWndCapture = capCreateCaptureWindow("hXWebCam", //nombre
WS_CHILD | WS_VISIBLE, //estilo de ventana
0, 0, //posición
320, 240, //tamaño
Parent, 0); //ventana padre
//chequea si se creó la ventana de captura
if (!hWndCapture)
{
MessageBoxA(Parent, "Error creando ventana de captura",
"Error", MB_ICONERROR);
return FALSE;
}
//conectamos al dispositivo de captura
if (!capDriverConnect(hWndCapture,0))
{
MessageBoxA(Parent, "Error conectando al dispositivo de captura.\r\n"
"Es posible que no haya ningun dispositivo de este "
"tipo instalado.",
"Error", MB_ICONERROR);
return FALSE;
}
//aquí especificamos el formato
BITMAPINFO bmFormat;
memset(&bmFormat, 0, sizeof(BITMAPINFO));
bmFormat.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmFormat.bmiHeader.biWidth = 320;
bmFormat.bmiHeader.biHeight = 240;
bmFormat.bmiHeader.biBitCount = 24;
bmFormat.bmiHeader.biCompression = 0;
//el formato definido aquí es 320x240x24
capSetVideoFormat(hWndCapture, &bmFormat, sizeof(BITMAPINFO));
//cada 30 milisegundo se refresca la imagen
capPreviewRate(hWndCapture,30);
//Preview activado
capPreview(hWndCapture,TRUE);
return TRUE;
}
//---------------------------------------------------------------------------
/*
** Procedimiento de ventana
*/
LRESULT CALLBACK WndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch (uMsg)
{
case WM_COMMAND:
{
//botón foto
if (hWnd_btnPicture == (HWND) lParam)
{
//esto es para guardar el frame actual en un Bitmap
capFileSaveDIB(hWndCapture, "picture.bmp");
Beep(600,50);
}
else
//botón secuencia
if (hWnd_btnSequence == (HWND) lParam)
{
SetWindowTextA(hWndMain, "Grabando Sequencia Audio/Video");
EnableWindow(hWnd_btnStop, TRUE);
EnableWindow(hWnd_btnSequence, FALSE);
EnableWindow(hWnd_btnPicture, FALSE);
EnableWindow(hWnd_cbxNegative, FALSE);
//especifica el lugar donde guardará la secuencia de video, por
//defecto es C:\\Capture.avi
capFileSetCaptureFile(hWndCapture, "video.avi");
//Inicia captura de video
capCaptureSequence(hWndCapture);
}
else
//botón Detener
if (hWnd_btnStop == (HWND) lParam)
{
SetWindowTextA(hWndMain, "WebCam Preview por h0aX");
EnableWindow(hWnd_btnStop, FALSE);
EnableWindow(hWnd_btnSequence, TRUE);
EnableWindow(hWnd_btnPicture, TRUE);
EnableWindow(hWnd_cbxNegative, TRUE);
//Termina captura de video
capCaptureStop(hWndCapture);
}
else
//botón Acerca de
if (hWnd_btnAbout == (HWND) lParam)
{
MessageBoxA(hWndMain, "WebCam Preview v1.0\r\n\r\n"
"por h0aX [hoax_ws@yahoo.es] 27/08/2007 9:28 pm\r\n"
"para la comunidad BlackHat [blackhat4all@gmail.com]\r\n",
"Acerca de", MB_ICONINFORMATION);
}
else
//checkbox Negativo
if (hWnd_cbxNegative == (HWND) lParam)
{
BOOL bChecked;
bChecked = SendMessage(hWnd_cbxNegative, BM_GETCHECK, 0, 0);
if (!bChecked)
{
capSetCallbackOnFrame(hWndCapture, (FARPROC) Frame);
SendMessage(hWnd_cbxNegative, BM_SETCHECK, 1, 0);
}
else
{
capSetCallbackOnFrame(hWndCapture, NULL);
SendMessage(hWnd_cbxNegative, BM_SETCHECK, 0, 0);
}
}
return 0;
}
case WM_DESTROY:
{
//desconecta del dispositivo de captura
capDriverDisconnect(hWndCapture);
PostQuitMessage(0);
return 0;
}
}
return DefWindowProcA(hwnd,uMsg,wParam,lParam);
}
//---------------------------------------------------------------------------
WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
WNDCLASS wndclass;
char szClassName[] = "hXWebCamPreview";
hInst = hInstance;
wndclass.style = CS_VREDRAW | CS_HREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIconA(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursorA(NULL, IDC_ARROW);
wndclass.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szClassName;
RegisterClassA(&wndclass);
//Ventana padre
hWndMain = CreateWindow(szClassName,"WebCam Preview por h0aX", WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, 415, 272,
NULL, NULL, hInstance, 0);
//Botones
hWnd_btnPicture = CreateWindow("Button","Foto", WS_VISIBLE | WS_CHILD,
325,30, 75,25, hWndMain, 0, hInst, NULL);
hWnd_btnSequence = CreateWindow("Button","Secuencia", WS_VISIBLE | WS_CHILD,
325,60, 75,25, hWndMain, 0, hInst, NULL);
hWnd_btnStop = CreateWindow("Button","Detener", WS_VISIBLE | WS_CHILD,
325,90, 75,25, hWndMain, 0, hInst, NULL);
EnableWindow(hWnd_btnStop, FALSE);
hWnd_btnAbout = CreateWindow("Button","Acerca de", WS_VISIBLE | WS_CHILD,
325,120, 75,25, hWndMain, 0, hInst, NULL);
//CheckBox
hWnd_cbxNegative = CreateWindow("Button","Negativo", WS_VISIBLE | WS_CHILD | BS_CHECKBOX,
325,150, 78,25, hWndMain, 0, hInst, NULL);
ShowWindow(hWndMain, nCmdShow);
//Si no logra iniciar la WebCam cierra.
if (!IniciaWebCam(hWndMain))
{
PostQuitMessage(0);
}
while (GetMessageA(&msg, NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
return msg.wParam;
}
//---------------------------------------------------------------------------.
Escrito por H0ax [hoax_ws@yahoo.es]
