Page 1 of 2

ESCAPI problems

Posted: Tue Jan 16, 2007 5:16 pm
by Hi-Toro
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: Select all

; 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]

Posted: Tue Jan 16, 2007 8:33 pm
by freak
Boy, this one was hard to spot :)

Its actually only your drawing code:

Code: Select all

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: Select all

          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: Select all

;/* 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: Select all

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 :)

Posted: Tue Jan 16, 2007 9:00 pm
by Hi-Toro
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!

Posted: Tue Jan 16, 2007 11:02 pm
by Hi-Toro
I got the Blitz version working too -- thanks again, Freak!

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

Posted: Wed Jan 17, 2007 10:45 am
by Hi-Toro
Freak, is it OK if Jari (the ESCAPI developer) includes this code in his distribution (basically, as public domain code)?

Posted: Wed Jan 17, 2007 10:57 am
by Flype
it's very interessant - i tried and it works perfectly.

but i think this part is very slow :

Code: Select all

    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.

Posted: Wed Jan 17, 2007 11:13 am
by Hi-Toro
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.

Posted: Wed Jan 17, 2007 12:00 pm
by Flype
thank you for the answer Hi-Toro.

so something like this should be possible :?: ?

Code: Select all

;- 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

;-

Posted: Wed Jan 17, 2007 1:24 pm
by freak
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.

Posted: Wed Jan 17, 2007 4:47 pm
by Hi-Toro
Cool, thanks -- I've let him know.

Posted: Wed Jan 17, 2007 4:50 pm
by Hi-Toro
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!)

Re: ESCAPI problems

Posted: Thu Sep 27, 2012 11:24 pm
by chris319
Please see the post below for an improved ESCAPI webcam program:

http://www.purebasic.fr/english/viewtop ... 44#p391944

The ESCAPI project:

http://sol.gfxile.net/escapi/index.html

Re: ESCAPI problems

Posted: Mon Feb 29, 2016 11:09 pm
by chris319
I have tried escapi.dll from escapi 3 in my own purebasic webcam program and it works OK.

I will look at the blitzmax program but make no promises.

Re: ESCAPI problems

Posted: Tue Mar 01, 2016 12:07 pm
by chris319
The existing escapi.pbi code works with escapi version 3.

Code: Select all

;/* 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

Re: ESCAPI problems

Posted: Wed Mar 02, 2016 10:40 am
by chris319
Please disregard.