PureBasic Forum
http://www.purebasic.fr/english/

ESCAPI problems
http://www.purebasic.fr/english/viewtopic.php?f=7&t=25412
Page 1 of 1

Author:  Hi-Toro [ Tue Jan 16, 2007 5:16 pm ]
Post subject:  ESCAPI problems

Anyone had any luck with ESCAPI 2.0 for easy webcam capture on Windows?

http://sol.gfxile.net/code.html

I wrote this code (just place it in the same folder as escapi.dll from the escapi20.zip archive and make sure your camera is plugged in!), and all the function calls seem to work, but I just get black data (all zeroes) from the webcam after capture:

Code:
; PB4...

Structure SimpleCapParams
   mTargetBuf.l   ; Must be at least mWidth * mHeight * SizeOf(int) of size!
   mWidth.l
   mHeight.l
EndStructure

camnum = 0 ; First device found; adjust to suit...

esc = OpenLibrary (#PB_Any, "escapi.dll")

If esc
   
   initCOM = GetFunction (esc, "initCOM")
   countCaptureDevices = GetFunction (esc, "countCaptureDevices")
   initCapture = GetFunction (esc, "initCapture")
   deinitCapture = GetFunction (esc, "deinitCapture")
   doCapture = GetFunction (esc, "doCapture")
   isCaptureDone = GetFunction (esc, "isCaptureDone")
   getCaptureDeviceName = GetFunction (esc, "getCaptureDeviceName")
   ESCAPIDLLVersion = GetFunction (esc, "ESCAPIDLLVersion")

   CallFunctionFast (initCOM)

   Debug "ESCAPIDLLVersion:"
   Debug "$" + Hex (CallFunctionFast (ESCAPIDLLVersion))
   Debug ""
   
   Debug "countCaptureDevices:"
   Debug CallFunctionFast (countCaptureDevices)
   Debug ""

   cam$ = Space (#MAX_PATH)
   CallFunctionFast (getCaptureDeviceName, camnum, @cam$, #MAX_PATH)
   Debug cam$
   Debug ""

   scp.SimpleCapParams
   scp\mWidth = 320
   scp\mHeight = 240
   scp\mTargetBuf = AllocateMemory (scp\mWidth * scp\mHeight * 4)
   
   Debug "initCapture:"
   Debug CallFunctionFast (initCapture, camnum, @scp)
   Debug ""

   For a = 1 To 10
      CallFunctionFast (doCapture, camnum)
      Repeat
         Delay (1)
      Until CallFunctionFast (isCaptureDone, camnum)
   Next

   Debug "Size:"
   Debug scp\mWidth
   Debug scp\mHeight
   
   image = CreateImage (#PB_Any, scp\mWidth, scp\mHeight)
   StartDrawing (ImageOutput (image))
   
   For y = 0 To scp\mHeight - 1
      For x = 0 To scp\mWidth - 1
         Plot (x, y, scp\mTargetBuf + count)
         count = count + 1
      Next
   Next
   
   StopDrawing ()

   CallFunctionFast (deinitCapture, camnum)

   CloseLibrary (esc)
   
EndIf

window = OpenWindow (#PB_Any, 320, 200, 320, 200, "ESCAPI 2")

CreateGadgetList (WindowID (window))
ImageGadget (#PB_Any, 0, 0, 320, 200, ImageID (image))

Repeat

Until WaitWindowEvent () = #PB_Event_CloseWindow


I used the setupESCAPI() function from escapi.cpp in the archive as a guide to setting things up, as well as using the "simplest" example as a guide to capturing. The only thing I can imagine is that something's going wrong with the initCOM call... I'll never understand COM stuff as long as I live.

This also fails in BlitzMax (see my post here: http://www.blitzmax.com/Community/posts.php?topic=66505), yet the provided binary examples work fine, and it's just a simple C-style API.

Anyone got any ideas as to what might be wrong?

(Heh, spot the forum bug with ? in URLs!)
[/url]

Author:  freak [ Tue Jan 16, 2007 8:33 pm ]
Post subject: 

Boy, this one was hard to spot :)

Its actually only your drawing code:
Code:
Plot (x, y, scp\mTargetBuf + count)

What you do here is calculate the position of the pixel in memory, and then use the
memory location as the pixel value.
You have to PeekL() the value from the address.
Also the calculation is wrong. Each pixel is 4 bytes wide, so you have to increase the count by 4 each time.
Also the pixel order is different, it is stored in BGR, so this needs to be reversed too.

Something like this:
Code:
          pixel = PeekL(scp\mTargetBuf + (y*scp\mWidth + x) * 4)
          rgb   = RGB((pixel >> 16) & $FF, (pixel >> 8) & $FF, pixel & $FF)
          Plot(x, y, rgb)


Another note:
The dll functions are in the cdecl calling convention (the definitions have no _stdcall or macro that stands for it)
So you have to use CallCFunctionFast() or you will get trouble in procedures etc because the stack is wrong.

As i did not get your code to run at all at first, i wrote my own.
It uses prototypes, which is much cleaner than Call(C)FunctionFast.

escapi.pb includefile
Code:
;/* Extremely Simple Capture API */

Structure SimpleCapParams
   *mTargetBuf ; Must be at least mWidth * mHeight * SizeOf(int) of size!
   mWidth.l
   mHeight.l
EndStructure

;/* Return the number of capture devices found */
PrototypeC countCaptureDevicesProc()

; /* initCapture tries To open the video capture device.
;  * Returns 0 on failure, 1 on success.
;  * Note: Capture parameter values must Not change While capture device
;  *       is in use (i.e. between initCapture And deinitCapture).
;  *       Do *Not* free the target buffer, Or change its pointer!
;  */
PrototypeC initCaptureProc(deviceno, *aParams.SimpleCapParams)

;/* deinitCapture closes the video capture device. */
PrototypeC deinitCaptureProc(deviceno)

;/* doCapture requests video frame To be captured. */
PrototypeC doCaptureProc(deviceno)

;/* isCaptureDone returns 1 when the requested frame has been captured.*/
PrototypeC isCaptureDoneProc(deviceno)


;/* Get the user-friendly name of a capture device. */
PrototypeC getCaptureDeviceNameProc(deviceno, *namebuffer, bufferlength)


;/* Returns the ESCAPI DLL version. 0x200 For 2.0 */
PrototypeC ESCAPIDLLVersionProc()

; marked as "internal" in the example
PrototypeC initCOMProc()

Global countCaptureDevices.countCaptureDevicesProc
Global initCapture.initCaptureProc
Global deinitCapture.deinitCaptureProc
Global doCapture.doCaptureProc
Global isCaptureDone.isCaptureDoneProc
Global getCaptureDeviceName.getCaptureDeviceNameProc
Global ESCAPIDLLVersion.ESCAPIDLLVersionProc


Procedure setupESCAPI()
   
  ; load library
  capdll = OpenLibrary(#PB_Any, "escapi.dll")
  If capdll = 0
    ProcedureReturn 0
  EndIf
 
   ;/* Fetch function entry points */
   countCaptureDevices  = GetFunction(capdll, "countCaptureDevices")
   initCapture          = GetFunction(capdll, "initCapture")
   deinitCapture        = GetFunction(capdll, "deinitCapture")
   doCapture            = GetFunction(capdll, "doCapture")
   isCaptureDone        = GetFunction(capdll, "isCaptureDone")
   initCOM.initCOMProc  = GetFunction(capdll, "initCOM")
   getCaptureDeviceName = GetFunction(capdll, "getCaptureDeviceName")
   ESCAPIDLLVersion     = GetFunction(capdll, "ESCAPIDLLVersion")
   
   If countCaptureDevices = 0 Or initCapture = 0 Or deinitCapture = 0 Or doCapture = 0 Or isCaptureDone = 0 Or initCOM = 0 Or getCaptureDeviceName = 0 Or ESCAPIDLLVersion = 0
     ProcedureReturn 0
   EndIf
   
   ;/* Verify DLL version */
   If ESCAPIDLLVersion() < $200
     ProcedureReturn 0
   EndIf
 
   ;/* Initialize COM.. */
   initCOM(); 
 
  ; returns number of devices found
  ProcedureReturn countCaptureDevices()
EndProcedure




test code
Code:
XIncludeFile "escapi.pb"

device = 0

count = setupESCAPI()
Debug "init: " + Str(count)

If count = 0
  End
EndIf

name$ = Space(1000)
getCaptureDeviceName(device, @name$, 1000)
Debug "name: " + name$

scp.SimpleCapParams
scp\mWidth = 320
scp\mHeight = 240
scp\mTargetBuf = AllocateMemory (scp\mWidth * scp\mHeight * 4)

If initCapture(device, @scp)
  Debug "cap init successful" 
 
  image = CreateImage(#PB_Any, 320, 240)
 
  OpenWindow(0, 0, 0, 320, 240, name$, #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
  CreateGadgetList(WindowID(0))
  ImageGadget(0, 0, 0, 320, 240, ImageID(image))
 
  Quit = 0
  Repeat
 
    doCapture(device)
    While isCaptureDone(device) = 0
      If WaitWindowEvent(1) = #PB_Event_CloseWindow
        Quit = 1
        Break
      EndIf       
    Wend
   
    If StartDrawing(ImageOutput(image))   
      For y = 0 To scp\mHeight - 1
        For x = 0 To scp\mWidth - 1
          pixel = PeekL(scp\mTargetBuf + (y*scp\mWidth + x) * 4)
          rgb   = RGB((pixel >> 16) & $FF, (pixel >> 8) & $FF, pixel & $FF)
          Plot(x, y, rgb)
        Next
      Next
     
      StopDrawing()
      SetGadgetState(0, ImageID(image))
    EndIf
     
 
  Until Quit

  deinitCapture(device)
Else
  Debug "init capture failed!"
EndIf

End


I can watch my TV-cards input now from PB :)

Author:  Hi-Toro [ Tue Jan 16, 2007 9:00 pm ]
Post subject: 

Ah, brilliant, thanks Freak. I pretty much knew my drawing code would be completely wrong but was desperately trying to get *anything* out of it to start with...

Prototypes look interesting -- haven't tried those yet. I would never have noticed the problem with the calling convention...

Thanks again!

Author:  Hi-Toro [ Tue Jan 16, 2007 11:02 pm ]
Post subject: 

I got the Blitz version working too -- thanks again, Freak!

http://www.blitzmax.com/Community/posts.php?topic=66505

Author:  Hi-Toro [ Wed Jan 17, 2007 10:45 am ]
Post subject: 

Freak, is it OK if Jari (the ESCAPI developer) includes this code in his distribution (basically, as public domain code)?

Author:  Flype [ Wed Jan 17, 2007 10:57 am ]
Post subject: 

it's very interessant - i tried and it works perfectly.

but i think this part is very slow :

Code:
    If StartDrawing(ImageOutput(image))   
      For y = 0 To scp\mHeight - 1
        For x = 0 To scp\mWidth - 1
          pixel = PeekL(scp\mTargetBuf + (y*scp\mWidth + x) * 4)
          rgb   = RGB((pixel >> 16) & $FF, (pixel >> 8) & $FF, pixel & $FF)
          Plot(x, y, rgb)
        Next
      Next
      StopDrawing()
      SetGadgetState(0, ImageID(image))
    EndIf


320x240 is acceptable but highest resolution are just too slow.

is this image-format a BMP format without the header ?

No way to catch the buffer and draw the entire image in one or two function (CatchImage/DrawImage) ?

Ok, this way, I understand that it is handy for sharing the imagedata via networks / differents operating systems. It allow many perspectives.

In all cases it's a very handy lib.

Author:  Hi-Toro [ Wed Jan 17, 2007 11:13 am ]
Post subject: 

It should be perfectly possible to grab directly to a buffer and display more directly. Freak was just showing me how to extract and draw the raw data.

I found I can grab directly to a 'PixMap' in BlitzMax for a large speedup in the drawing process. The data is in what BlitzMax calls "PF_BGRA8888" format, ie. four bytes per pixel, ordered as blue, green, red and alpha components, with 8 bits per component. I think this is the same as BMP with no header, as you suggest.

Author:  Flype [ Wed Jan 17, 2007 12:00 pm ]
Post subject: 

thank you for the answer Hi-Toro.

so something like this should be possible :?: ?

Code:
;- initialization.

w = 320
h = 240

ImageHdrLen.l = 128 ; don't know this one - to be modified.
ImageBufLen.l = (w * h * 4)
ImageMemLen.l = ImageHdrLen + ImageBufLen
ImageMemPtr.l = AllocateMemory(ImageMemLen)
ImageBufPtr.l = ImageMemPtr + ImageHdrLen

scp.SimpleCapParams
scp\mWidth     = w
scp\mHeight    = h
scp\mTargetBuf = ImageBufPtr

;- inside the loop.

If CatchImage(0, ImageMemPtr, ImageMemLen, #PB_Image_DisplayFormat)
  DrawImage(0, 0, 0, w, h)
EndIf

;-

Author:  freak [ Wed Jan 17, 2007 1:24 pm ]
Post subject: 

Hi-Toro wrote:
Freak, is it OK if Jari (the ESCAPI developer) includes this code in his distribution (basically, as public domain code)?


Sure, why not.

Author:  Hi-Toro [ Wed Jan 17, 2007 4:47 pm ]
Post subject: 

Cool, thanks -- I've let him know.

Author:  Hi-Toro [ Wed Jan 17, 2007 4:50 pm ]
Post subject: 

Oops, sorry Flype... I think that looks about right, but I haven't played with graphical stuff much in PB. You'll have to experiment here, sorry. (Unless someone else knows!)

Page 1 of 1 All times are UTC + 1 hour
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/