I'm using the following code to capture the camera:
unit Webcam;
interface
uses
Windows, Messages, SysUtils, Graphics, ExtCtrls, Classes, VFW {https://drkb.ru/multimedia/audio/extract_track/ed7dcb6994c641e4};
type
TCamera = class(TObject)
private
class var VideoHwnd: HWND;
class function FrameCallback(hCapWnd: HWND; lpVHdr: PVIDEOHDR): DWORD;
stdcall; static;
public
constructor Create(Owner: TPanel);
destructor Destroy; override;
end;
var
Camera: TCamera;
implementation
constructor TCamera.Create(Owner: TPanel);
begin
VideoHwnd := capCreateCaptureWindowA('', WS_CHILD or WS_VISIBLE, 0, 0, 640,
480, Owner.Handle, 0);
if (SendMessage(VideoHwnd, WM_CAP_DRIVER_CONNECT, 0, 0) <> 0) then
begin
SendMessage(VideoHwnd, WM_CAP_SET_PREVIEW, 1, 0);
SendMessage(VideoHwnd, WM_CAP_SET_PREVIEWRATE, 1, 0);
SendMessage(VideoHwnd, WM_CAP_SET_OVERLAY, 1, 0);
SendMessage(VideoHwnd, WM_CAP_SET_SCALE, 1, 0);
SendMessage(VideoHwnd, WM_CAP_SET_CALLBACK_FRAME, 1,
lParam(@FrameCallback));
SendMessage(VideoHwnd,
{ WM_CAP_GRAB_FRAME } WM_CAP_GRAB_FRAME_NOSTOP, 1, 0);
end;
end;
destructor TCamera.Destroy;
begin
if (VideoHwnd <> 0) then
begin
SendMessage(VideoHwnd, WM_CAP_DRIVER_DISCONNECT, 1, 0);
SendMessage(VideoHwnd, WM_CLOSE, 1, 0);
end;
inherited;
end;
class function TCamera.FrameCallback(hCapWnd: HWND; lpVHdr: PVIDEOHDR)
: DWORD; stdcall;
var
MemoryStream: TMemoryStream;
BitmapInfo: TBitmapInfo;
Bitmap: TBitmap;
Hdb: Thandle;
begin
FillChar(BitmapInfo, SizeOf(BitmapInfo), 0);
if (SendMessage(hCapWnd, WM_CAP_GET_VIDEOFORMAT, SizeOf(BitmapInfo),
lParam(@BitmapInfo)) <> 0) then
begin
MemoryStream := TMemoryStream.Create;
try
Bitmap := TBitmap.Create;
try
with Bitmap do
begin
Width := BitmapInfo.bmiHeader.biWidth;
Height := BitmapInfo.bmiHeader.biHeight;
case BitmapInfo.bmiHeader.biBitCount of
1:
PixelFormat := pf1bit;
4:
PixelFormat := pf4bit;
8:
PixelFormat := pf8bit;
15:
PixelFormat := pf15bit;
16:
PixelFormat := pf16bit;
24:
PixelFormat := pf24bit;
32:
PixelFormat := pf32bit;
end;
Hdb := DrawDibOpen;
DrawDibDraw(Hdb, Canvas.Handle, 0, 0, BitmapInfo.bmiHeader.biWidth,
BitmapInfo.bmiHeader.biHeight, @BitmapInfo.bmiHeader,
lpVHdr^.lpdata, 0, 0, BitmapInfo.bmiHeader.biWidth,
BitmapInfo.bmiHeader.biHeight, 0);
DrawDibClose(Hdb);
SaveToStream(MemoryStream);
end;
MemoryStream.Position := 0;
finally
Bitmap.Free;
end;
finally
MemoryStream.Free;
end;
end;
end;
procedure GetDriverList(List: TStrings);
var
wIndex: Word;
szDeviceName: array [0 .. MAX_PATH] of AnsiChar;
szDeviceVersion: array [0 .. MAX_PATH] of AnsiChar;
begin
List.Clear;
for wIndex := 0 to 9 do
begin
if capGetDriverDescriptionA(wIndex, @szDeviceName, SizeOf(szDeviceName),
@szDeviceVersion, SizeOf(szDeviceVersion)) then
List.AddObject(szDeviceName, Pointer(wIndex));
end;
if List.Count = 0 then
RaiseLastOSError;
end;
end.
When my Form is visible, all works fine. On the other hand, when my Form is configured with wsMinimize
, or hidden via code for example, only one frame is displayed/captured and then it stops.
Is there a solution for that?
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.FormActivate(Sender: TObject);
begin
ShowWindow(Handle, SW_HIDE);
end;
end.
EDIT:
I inserted a error callback function suggested in a comment, but all seems well (no error is reported).
// inside TCamera class (private declarations):
class function ErrorCallback(hCapWnd: HWND; nErrID: Integer;
lpErrorText: LPTSTR): LRESULT; stdcall; static;
//...
class function TCamera.ErrorCallback(hCapWnd: HWND; nErrID: Integer;
lpErrorText: LPTSTR): LRESULT; stdcall;
begin
Result := 1;
if hCapWnd <= 0 then
begin
Result := 0;
Exit;
end;
if nErrID = 0 then
begin
Result := 1;
Exit;
end;
Writeln(IntToStr(nErrID) + ' : ' + PAnsiChar(lpErrorText) + ' : ' +
IntToStr(hCapWnd));
end;