Every once in a while I have something which I need a digital photo of. Often it's even worse, it's that I need more of lq document scan. Sure I could use a regular camera. But I do have a webcam and too much time it seems.

The idea

Webcam makes pictures -- that's essential all there is to say when it comes to the idea. I want to use my webcam to take a picture. Unfortunately Windows 7 (which my webcam is attached to) does not feature a simple program to satisfy this essential need. Which is why it had to be created in the most painful way there is.

The implementation

Nowerdays, a webcam is usually accessed by the means of DirectX (on Microsoft Windows systems, that is). But historically, there's also the good old-fashioned way of using avicap32.dll -- that is, by using Windows core concepts such as child-windows and window messages.

Looking at the requirements, the list is pretty short:

...looks manageable. So the UI is essentially one window displaying a live preview with a set of control knobs enabling change of input device, calling for the camera settings dialog (which is provided by the driver) and the snapshot button. The first camera (if existent) will be connected on startup. KISS at it's best.

To access a camera using avicap32.dll, one needs essentially one function call and a number of window-messages; insight to the list of available cameras is provided using yet another function call -- and that's it. For shortness sake, these are the relevant parts of the code which may be helpful for someone else.
Function call definitions:

function capGetDriverDescriptionA(wDriverIndex: UINT; lpszName: LPSTR;
   cbName: integer; lpszVer: LPSTR; cbVer: integer): BOOL; stdcall;
   external 'AVICAP32.DLL';
function capCreateCaptureWindowA(lpszWindowName: PChar; dwStyle: dword;
   x, y, nWidth, nHeight: word; ParentWin: dword; nId: word): dword;
   stdcall external 'AVICAP32.DLL';

Constants (which I have been googling myself so maybe the definitions are somewhat diverging from what it should be defined as (i.e. being relative to WM_USER and not to WM_CAP_START)):
const
   WM_CAP_START = WM_USER;
   WM_CAP_SET_PREVIEW = WM_USER + 50;
   WM_CAP_DRIVER_CONNECT = WM_USER + 10;
   WM_CAP_DRIVER_DISCONNECT = WM_USER + 11;
   WM_CAP_SAVEDIB = WM_USER + 25;
   WM_CAP_GRAB_FRAME = WM_USER + 60;
   WM_CAP_SET_VIDEOFORMAT = WM_USER + 45;
   WM_CAP_DLG_VIDEOSOURCE = WM_USER + 42;
   WM_CAP_EDIT_COPY = WM_USER + 30;
   WM_CAP_SET_OVERLAY = WM_USER + 51;
   WM_CAP_SET_PREVIEWRATE = WM_USER + 52;
   WM_CAP_DLG_VIDEOFORMAT = WM_CAP_START+ 41;

Updating the list of available devices in case their number just changed and activating the first device in case there is only one device:
procedure TForm1.RefreshDeviceList;
var
        n, v: array[0..MAX_PATH] of char;
        sl : TStringList;
        i: integer;
begin
        sl := tstringList.Create;
        i := 0;
        while true do
        begin
                if not capGetDriverDescriptionA(i, @n, sizeof(n), @v, sizeof(v)) then break;
                sl.Add(n + ' (' + v + ')');
                i := i + 1;
        end;
        if (sl.Count <> self.ComboBox1.Items.Count) then
        begin
                self.ComboBox1.Items.assign(sl);
                if sl.Count = 1 then
                begin
                        self.ComboBox1.ItemIndex := 0;
                        self.ComboBox1Change(nil);
                end;
        end;
        sl.Free;
end;

Connecting to a specific device and activating live (overlay) preview by the means of a visible child window:
CaptureWindow := capCreateCaptureWindowA('CaptureWindow', WS_CHILD or WS_VISIBLE, 0, 0, panel2.ClientWidth, panel2.ClientHeight, self.panel2.handle, self.ComboBox1.ItemIndex);
SendMessage(CaptureWindow, WM_CAP_DLG_VIDEOSOURCE, self.ComboBox1.ItemIndex, 0);
SendMessage(CaptureWindow, WM_CAP_DRIVER_CONNECT, self.ComboBox1.ItemIndex, 0);
SendMessage(CaptureWindow, WM_CAP_SET_PREVIEWRATE, 60, 0);
sendMessage(CaptureWindow, WM_CAP_SET_OVERLAY, 1, 0);
sendMessage(CaptureWindow, WM_CAP_SET_PREVIEW, 1, 0);
showwindow(CaptureWindow, SW_SHOW);

Taking a snapshot and saving it to a file, then returning to live preview:
SendMessage(CaptureWindow, WM_CAP_GRAB_FRAME, 0, 0);
if savedialog1.Execute then SendMessage(CaptureWindow, WM_CAP_SAVEDIB, 0, longint(PChar(savedialog1.FileName)));
sendMessage(CaptureWindow, WM_CAP_SET_PREVIEW, 1, 0);

Finally: Closing the device.
if captureWindow <> 0 then sendmessage(captureWindow, WM_CAP_DRIVER_DISCONNECT, 0, 0);

License and Downloads

You find the sources at down/wcp-src.zip and an executeable at down/wcp-app.zip. Be reminded not to run binaries you downloaded from untrusted sites such as this site. Instead read, understand, and then (!) compile the source. Enjoy responsibly. Things are released under BSD license.

MD5 (wcp-app.zip) = 0cef52aef120c590d1679dfad051fba9
MD5 (wcp-src.zip) = 52ec4be2dd846ae516405f70c9febe34
SHA256 (wcp-app.zip) = 8e61387b4efdd5b6c4e95d5997bb56d374b880d8ecdb9138d6e9c5a4454065f4
SHA256 (wcp-src.zip) = a0e3ea2ee4dac1d014661f91dcda42cd8592396cd150ab8242b2f398d52d043f

Stichworte:


Impressum