Heya - I recently looked at Scronty's vfw32 example (cam viewer) - and I decided to implement the code myself, using a single webcam.
Well, I found a peculiarity in the code Scronty used to save bitmaps (which bears a striking similarity to the dekstop capture code from BackOrifice :tongue: )
Here's the deal.
He fetches the dc of a given window, then creates a compatible bitmap - so far so good - but if the window is minimized, it saves a black rectangle, if the window is hidden, it saves the desktop area under the client window, and if the window is visible but partially obscured, it saves overlapping windows image data (unless you move the client window to the top of the z order at the last moment - which defeats the purpose of wanting to hide it !!)
All I want to do is to save the webcam image to a file, without displaying it onscreen. Why does this have to be difficult??
Posted on 2003-05-17 10:34:06 by Homer
Afternoon, EvilHomer2k.

I'd started to look into saving the image differently after posting the last webcam viewer, however grabbing the dc of the window and saving that was far easier than the "correct" way.

The only other way would be to grab the video feed and load a DIB manually.

Yet again...the PSDK tells you what you *can* do, but not *how* to do it.:mad:


Once code has been built that can actually grab the video feed and save it, then you'd be able to build a proggy that allows you to take a picture from out of someones webcam and upload it to an ftp site without them knowing.


Cheers,
Scronty
Posted on 2003-05-17 21:33:17 by Scronty
Heh - yes, that's true, but then again, there are legitimate reasons for wanting to not display the stream onscreen too - aren't there ? :tongue:
Posted on 2003-05-17 23:03:00 by Homer
Afternoon, EvilHomer2k.

One legitimate reason would be a security system where you want the PC to save the video feed transparently, and only actually *see* the video feed when you want to (i.e. a camera in a dairy/local shop).

I think I've figured it out:alright: .

What you need to do is to set a CallBack proc which gets sent the video feed.

This is what I've modified in the "3 webcam viewer" proggy:

1) Add the prototype at top of code:


FrameCallbackProc proto :DWORD, :DWORD

2) In the CBN_SELCHANGE message handler for hComboCapture1 put:


invoke SendMessage, hWndCap1, WM_CAP_SET_CALLBACK_FRAME, 0, FrameCallbackProc

after the "WM_CAP_SET_PREVIEW" line.

3) In the WM_CLOSE message Handler:


invoke SendMessage, hWndCap1, WM_CAP_SET_CALLBACK_FRAME, 0, NULL

to get rid of the callback routine.

4) In the WM_INITDIALOG message handler:


mov eax, hWnd
mov ghWndMain, eax


5) Add new data:


sztmplPreviewFrame db "Preview frame# %ld ",0
dwFrameNum dd 0

Buffer db 128 DUP (0)
ghWndMain dd 0


6) Add the VIDEOHDR structure definition:


VIDEOHDR STRUCT
lpData DWORD ?
dwBufferLength DWORD ?
dwBytesUsed DWORD ?
dwTimeCaptured DWORD ?
dwUser DWORD ?
dwFlags DWORD ?
dwReserved DWORD ?
VIDEOHDR ENDS


7) And add the CallBack proc:


; FrameCallbackProc: frame callback function
; hWnd: capture window handle
; lpVHdr: pointer to struct containing captured
; frame information
FrameCallbackProc PROC hWnd:DWORD, lpVHdr:DWORD
LOCAL hdc:HDC
LOCAL memdc:HDC
LOCAL hSaveBMFile:HANDLE
LOCAL dwBytes:DWORD
LOCAL bmpinfo:BITMAPINFO
LOCAL dwBPP:DWORD
LOCAL dwNumColors:DWORD
LOCAL hBitmap:HBITMAP
LOCAL pBits:DWORD
LOCAL dwWidth:DWORD
LOCAL dwHeight:DWORD
LOCAL rc:RECT


.if hWnd == 0
xor eax, eax
ret
.endif

invoke wsprintf, ADDR Buffer, ADDR sztmplPreviewFrame, dwFrameNum
inc dwFrameNum

invoke SetWindowText, ghWndMain, ADDR Buffer


; grab the video data and display it

invoke GetDC, hWnd
mov hdc,eax

invoke GetWindowRect, hWnd, ADDR rc
mov eax, rc.right
sub eax, rc.left
mov dwWidth,eax
mov eax, rc.bottom
sub eax, rc.top
mov dwHeight,eax


invoke CreateCompatibleDC, hdc
mov memdc,eax
invoke GetDeviceCaps, hdc, BITSPIXEL
mov dwBPP,eax
mov dwNumColors,0

mov bmpinfo.bmiHeader.biSize,sizeof BITMAPINFOHEADER
mov eax,dwWidth
mov bmpinfo.bmiHeader.biWidth,eax
mov eax,dwHeight
mov bmpinfo.bmiHeader.biHeight,eax
mov bmpinfo.bmiHeader.biPlanes,1
mov ax,word ptr [dwBPP]
mov bmpinfo.bmiHeader.biBitCount,ax
mov bmpinfo.bmiHeader.biCompression,BI_RGB
mov bmpinfo.bmiHeader.biSizeImage,0
mov bmpinfo.bmiHeader.biXPelsPerMeter,0
mov bmpinfo.bmiHeader.biYPelsPerMeter,0
mov eax,dwNumColors
mov bmpinfo.bmiHeader.biClrUsed,eax
mov bmpinfo.bmiHeader.biClrImportant,eax
invoke CreateDIBSection,hdc,addr bmpinfo, DIB_PAL_COLORS,addr pBits, NULL, 0
mov hBitmap,eax
invoke SetBitmapBits, hBitmap, lpVHdr.VIDEOHDR.dwBytesUsed, lpVHdr.VIDEOHDR.lpData
invoke SelectObject, memdc, hBitmap
invoke BitBlt, hdc, 0,0, dwWidth, dwHeight, memdc, 0,0, SRCCOPY

invoke DeleteObject ,hBitmap
invoke DeleteDC, memdc
invoke ReleaseDC, hWnd, hdc

mov eax, 1
ret
FrameCallbackProc ENDP


This proc uses large chunks of code from the "prSaveImage" proc which creates a DIB and bitblts the callback-supplied image data onto the window.

If the "invoke BitBlt, hdc, 0,0, dwWidth, dwHeight, memdc, 0,0, SRCCOPY" line is commented out, you'll see that no image is shown on the capture window.

It is inside this callback proc where you'd add a "copyright" text onto the image/ upload via ftp/ analyse the image and compare it to previously stored image (i.e. for checking if the images have changed)/etc.

Cheers,
Scronty
Posted on 2003-05-17 23:26:05 by Scronty
Thanks Scronty, but NAAAAH !!!

I just want to grab FRAMES, into BMP files, not save an avi stream, and I found a way to do it without using vfw32 and without the need for any bmpsave code...
Here's how I did it:


ReConnectCam PROC
invoke SendMessage, hWndCap1, WM_CAP_DRIVER_DISCONNECT, 0, 0
invoke SendMessage, hWndCap1, WM_CAP_DRIVER_CONNECT, CamIndex , 0
invoke SendMessage, hWndCap1, WM_CAP_SET_PREVIEWRATE, 50, 0
invoke SendMessage, hWndCap1, WM_CAP_SET_SCALE, TRUE, 0
invoke SendMessage, hWndCap1, WM_CAP_SET_PREVIEW, TRUE, 0
ret
ReConnectCam ENDP

MyThread PROC parm:DWORD
@@: invoke Sleep,10000
.if CamAvailable==TRUE
invoke ReConnectCam
invoke SendMessage, hWndCap1, WM_CAP_GRAB_FRAME, 0, 0
invoke SendMessage, hWndCap1, WM_CAP_SAVEDIB, 0, CTXT("CUNT.BMP")
.endif
jmp @B
MyThread ENDP

The main window can be hidden using SW_HIDE and it will still work.
This code only acquires the cam when it is going to grab a frame, so it is available at all other times. The democode is saving every 10 seconds to the same file.
When you send the GRAB_FRAME message, you lose the camera connection, which in the case of this democode is ok, but note theres another message similar to that one which maintains the connection , should you wish this.

I no longer include vfw32 lib or inc files at all.
I send the WM's implicitly, the capBlah functions just do the same thing anyway - useless overhead, all you really require are the WM equates.
WM_CAP_SAVEDIB was base+25.
It's also called WM_CAP_FILE_SAVEDIB.

Thanks for the reply, Scronty, I do appreciate it and I too found little information available for this kind of thing. My code is going to be used in a video conferencing application, where you don't want to see your own ugly mug, you want to see THEIRS ... this arrangement allows me to simply continually send data streamed and compressed from the same image file, opened with FILE_SHARE_WRITE access.
Posted on 2003-05-18 01:48:03 by Homer
Afternoon, EvilHomer2k.

Thanks for supplying another method for this.

You seemed to miss the point of the code I just gave.:tongue:
Even though in the code it bitblts the DIB to the windows DC, it was only doing that to show that the image data was there.
In your case, you'd send the data to a "savebitmap" proc for saving, instead of displaying it.

Also:
Depending on what you wish to do, depends upon which methods to use. For yourself, grabbing a frame and saving it might be best. For myself, I was only saving a bitmap because I didn't know how else to grab a frame and send it to the "attachment" code for emailing.

Using WM_CAP_SET_CALLBACK_FRAME I can now grab a single frame and send the data to the proc for encrypting the base64 attachment.

You've mentioned that you're planning on using this for sending compressed images for video conferencing.
Is the networking code in a separate dll? I don't understand the need to save an image as a bitmap, when you could just send the image data directly to the compression proc.

Definitely interesting stuff, though:grin: :cool: .


Cheers,
Scronty
Posted on 2003-05-18 03:45:32 by Scronty

I don't understand the need to save an image as a bitmap, when you could just send the image data directly to the compression proc.
With the kind of video conferencing he is doing {loook at the BMP name}, he might want to have a look himself after the clients are done 'conferencing'. :)
Posted on 2003-05-18 10:17:17 by bitRAKE
Yep ok, now I'm done messing with that, I'll be trying on some other methods too :)
I can see your point guys, I couldn't understand the difficulty with just grabbing a frame to a bmp outright - but found it was simply not well documented.
Thanks for the heads up, I originally did want to access the stream directly, and could only find info on creating avi's with a callback, which at the time was way off track.
bitRAKE, I just have little imagination for var names, k? :tongue:
I apologise if I offended anyone there heh - an oversight.
Surely if it were part of some evil plot, I might try a bit harder with the filename :)

Scronty, did you have any issues with the dinput8 include?
Posted on 2003-05-21 10:32:23 by Homer
Afternoon, EvilHomer2k.

Scronty, did you have any issues with the dinput8 include?

Nope, since I've never translated them:tongue: . ALso: I haven't go around to checking the DInput inc file you've made, since I've been busy trying it get these Fasm DX example up and running. When they're done, I'll be taking a look at all the other files I never got around to making.

The only time I've used DInput in asm was when I was mucking about with Sergeys' (hardcode) DX example when he had Bizare Creations up and running.

It was during the time I was mucking about with that, and also using DInput in VC++ proggys, that I decided to leave the friggin' stupid thing alone.

I found that DInput has a problem allowing you to select various combinations of keys 9at least...DInput running on my 'puter has problems).

You *should* be able to allow as many keys down at once as the users hands/fingers can. However.. DInput failed quite often when holding down only three keys at the same time.

That's why I use the "key array" solution:
Have an array 256 bytes long. Each byte will represent whether a key is down or up.
During WM_KEYDOWN and WM_KEYUP messages, just place TRUE or FALSE in the array for whichever key is being pressed/released.

During the idle time in the messagepump (where the call to "render" takes place), have a bunch of .if....endif to check for the keys you need to process.

Windows is going to send your application the WM_KEYDOWN and WM_KEYUP messages whether you want it to or not, so there's not going to be much of a speed hit. You could even check for those particular messages *within* the messagepump.

With this system, I found that the users control/"feedback of action" was practically immediate compared with a *very* slight delay when using DInput.

As I mentioned ages ago in a thread far, far away...
DInput would be the better solution if you're coding for a joystick/car wheel/etc. However... since I only have a mouse and keyboard, using a key and mouse array is far faster and easier to implement.

Cheers,
Scronty
Posted on 2003-05-21 17:20:30 by Scronty
I dislike DI for various reasons, but it may be blameless here.
Check out http://www.dribin.org/dave/keyboard/one_html/ .
Does it actually work with the key array, and fail only when using DI?

/* edit: typo */
Posted on 2003-05-21 19:33:13 by Jan Wassenberg
Scronty,
I was using a DI function which fills a 256 byte buffer of flags which are formatted in the same way as the usual api keybuffer, yep, understand that premise, just had no luck with DI8 - I simply followed your lead and modified the m$ header such that it supported mcall - following your d3d includes as a template.
Well, I got DI to initialize without reporting any error codes, but it simply never sets any of the flags in the buffer when I call the polling function - which is meant to refresh the entire 256 byte set whenever it is called.
Nevermind, I'll leave it too for now, I was actually hoping Bogdan might throw some light on the issue, thus I posted my entire support code for DI8 and not just the inc.
Anyways, something else to tinker with when time permits :)
Posted on 2003-05-21 22:59:37 by Homer