Entwickler-Ecke

Multimedia / Grafik - [Webcam]FrameCallback-Funktion in einer Klasse?


mtin - Di 21.03.06 19:28
Titel: [Webcam]FrameCallback-Funktion in einer Klasse?
also, ich hab jetzt immernoch Probleme mit meiner Webcam :D
nachdem jetzt normal alles klappt, würde ich das ganze gerne in eine eigene Klasse TWebcam packen...
das funktioniert soweit auch ganz gut, nur müsste ich die Callback-funktion ja mit reinnehmen, da sie auf Variablen der Klasse zugreift!
nochmal zum Verständnis: Die Callback-Funktion sieht so aus:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
function OnFrame(hCapWnd: HWND; lpVHDR: PVideoHdr): DWord; stdcall;
begin
  Result:=1;
  SendMessage(CapHandle, WM_CAP_GET_VIDEOFORMAT, SizeOf(BitmapInfo), Integer(@BitmapInfo));

  src:=lpVHdr^.lpData;
  dest:=Frame.ScanLine[0];

  case bitmapinfo.bmiHeader.biCompression of
    UYVY: UYVYtoARGB(src, dest, w, h);
    YUY2: YUY2toARGB(src, dest, w, h);
    I420: I420toARGB(src, dest, w, h);
  end;

  Frame.Changed;
end;
Und der Webcam sage ich so, wie sie die Callback-Funktion findet:

Delphi-Quelltext
1:
SendMessage(CapHandle, WM_CAP_SET_CALLBACK_FRAME, 0, Integer(@OnFrame));                    

So funktioniert alles außer, dass ich alle benötigte Variablen nicht in die TWebcam reinschreiben kann sondern Global machen muss. Damit genau das funktioniert, hab ich mir gedacht, ich schreib einfach sowohl vor die Funktion an sich als auch in dieser Zeile ein TWebcam.OnFrame:

Delphi-Quelltext
1:
SendMessage(CapHandle, WM_CAP_SET_CALLBACK_FRAME, 0, Integer(@TWebcam.OnFrame));                    

(und dann natürlich noch in der Klasse TWebcam die Funktion deklarieren)

allerdings klappt dann irgendwas nicht mehr, bei der Zeile

Delphi-Quelltext
1:
2:
src:=lpVHdr^.lpData;
dest:=Frame.ScanLine[0];
kommt irgendeine exception...ich weiß auch nicht genau was da schiefläuft...
sobald ich dann wieder das aus der Klasse rausnehme und einfach global mache klappt wieder alles...

hatt irgendjemand davon ne Ahnung?


digi_c - Di 21.03.06 22:20

Was für eine Exception den? In Messageboxen kann man mit STRG+C den Text kopieren.

Vermutlich greifst du auf Speicher zu der eigentlich nicht dir gehört, kann er denn lpVHdr^ dereferenzieren?


mtin - Di 21.03.06 22:54

First chance exception at $7C81EB33. Exception class EAccessViolation with message 'Access violation at address 0046DE0C in module 'TWebcam.exe'. Read of address 00DD0944'. Process TWebcam.exe (1568)hm, du hattest Recht, lpVHdr^ zeigt auf nil...also nichts... :cry:

hast du eine Ahnung wieso das immer auftritt wenn ich das in die TWebcam-Klasse mit aufnehme?
Ich mach hier einfach mal meine gesamte Unit rein...
wenn du die Graphics32-Komponenten hast könntest du das ja vielleicht mal ausprobieren?

einfach in einem neuen Projekt die Unit einbinden und dann

Delphi-Quelltext
1:
2:
3:
4:
5:
var cam:TWebcam;

cam:=TWebcam.Create(Form1.Handle);

Image321.Bitmap:=cam.Frame;


hier ist sie:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
unit webcamdriver;

interface

uses GR32, UConvert, Windows, Messages, ExtCtrls, SysUtils;


function capCreateCaptureWindow(lpszWindowName  : LPCSTR;
                                dwStyle         : DWORD;
                                x, y            : Integer;
                                nWidth          : Integer;
                                nHeight         : Integer;
                                hwndParent      : HWND;
                                nID             : Integer ): HWND; stdcall;
                                external 'AVICAP32.DLL' name 'capCreateCaptureWindowA';

 type
  PVIDEOHDR = ^TVIDEOHDR;
  TVIDEOHDR = packed record
    lpData         : PBYTE;                // pointer to locked data buffer
    dwBufferLength : DWORD;                // Length of data buffer
    dwBytesUsed    : DWORD;                // Bytes actually used
    dwTimeCaptured : DWORD;                // Milliseconds from start of stream
    dwUser         : DWORD;                // for client's use
    dwFlags        : DWORD;                // assorted flags (see defines)
    dwReserved     : array[0..3of DWORD; // reserved for driver
  end;




type
  TWebcam = class
  private
  //---------------PRIVATE--------------
    var
      CapHandle:  HWND;
      w,h:        Integer;
    const
      WM_CAP_SET_CALLBACK_FRAME       = (WM_USER +  5);
      WM_CAP_DRIVER_CONNECT           = (WM_USER + 10);
      WM_CAP_DRIVER_DISCONNECT        = (WM_USER + 11);
      WM_CAP_EDIT_COPY                = (WM_USER + 30);
      WM_CAP_DLG_VIDEOFORMAT          = (WM_USER + 41);
      WM_CAP_DLG_VIDEOSOURCE          = (WM_USER + 42);
      WM_CAP_DLG_VIDEODISPLAY         = (WM_USER + 43);
      WM_CAP_GET_VIDEOFORMAT          = (WM_USER + 44);
      WM_CAP_SET_VIDEOFORMAT          = (WM_USER + 45);
      WM_CAP_DLG_VIDEOCOMPRESSION     = (WM_USER + 46);
      WM_CAP_SET_PREVIEW              = (WM_USER + 50);
      WM_CAP_SET_OVERLAY              = (WM_USER + 51);
      WM_CAP_SET_PREVIEWRATE          = (WM_USER + 52);
      WM_CAP_SET_SCALE                = (WM_USER + 53);
    procedure bildgroesse_anpassen;
  public
  //---------------PUBLIC--------------
    var resolution,bit,compression: string;
        Frame:                      TBitmap32;
    constructor create(FormHandle:HWND);
    function OnFrame(hCapWnd: HWND; lpVHDR: PVideoHdr): DWord; stdcall;
    procedure DLG_VideoSource;
    procedure DLG_VideoFormat;
    procedure DLG_VideoCompression;
  end;

implementation

//------------------CALLBACK-FUNCTION--------------------|
//                                                       |
function TWebcam.OnFrame(hCapWnd: HWND; lpVHDR: PVideoHdr): DWord; stdcall;
var BitmapInfo: TBitmapInfo;
    src, dest: Pointer;
begin
  Result:=1;
  FillChar(BitmapInfo, SizeOf(BitmapInfo), 0);
  SendMessage(CapHandle, WM_CAP_GET_VIDEOFORMAT, SizeOf(BitmapInfo), Integer(@BitmapInfo));

  src:=lpVHdr^.lpData;
  dest:=Frame.ScanLine[0];

  case bitmapinfo.bmiHeader.biCompression of
    BI_RGB: case bitmapinfo.bmiHeader.biBitCount of
              16: Conv16To32(src, dest, w, h);
              24: Conv24To32(src, dest, w, h);
              32: Move(src^, dest^, w*h*4);
            end;
    UYVY: UYVYtoARGB(src, dest, w, h);
    YUY2: YUY2toARGB(src, dest, w, h);
    I420: I420toARGB(src, dest, w, h);
  end;

  Frame.Changed;
end;
//                                                       |
//------------------/CALLBACK-FUNCTION-------------------|

//------------------CONSTRUCTOR--------------------------|
//                                                       |
constructor TWebcam.create(FormHandle:HWND);
begin
  inherited Create;
  CapHandle:=capCreateCaptureWindow('Video',
                                    ws_child + ws_visible,
                                    00,
                                    11,
                                    FormHandle,
                                    1);

  SendMessage(CapHandle, WM_CAP_DRIVER_CONNECT, 00);
  SendMessage(CapHandle, WM_CAP_SET_PREVIEWRATE,10);  //25
  sendMessage(Caphandle, WM_CAP_SET_OVERLAY, 10);
//  sendMessage(Caphandle, WM_CAP_SET_SCALE, Byte(True), 0);
  SendMessage(CapHandle, WM_CAP_SET_CALLBACK_FRAME, 0, Integer(@TWebcam.OnFrame));

  Frame:=TBitmap32.Create;
  bildgroesse_anpassen;

end;
//                                                       |
//------------------/CONSTRUCTOR-------------------------|

//------------------bildgröße...-------------------------|
//                                                       |
procedure TWebcam.bildgroesse_anpassen;
var BitmapInfo: TBitmapInfo;
begin
  SendMessage(CapHandle, WM_CAP_GET_VIDEOFORMAT, SizeOf(BitmapInfo), Integer(@BitmapInfo));
  w:=bitmapinfo.bmiHeader.biWidth;
  h:=bitmapinfo.bmiHeader.biHeight;
  Frame.SetSize(w, h);
// Video-Daten
  resolution:=IntToStr(w)+'x'+IntToStr(h);
  bit:=IntToStr(bitmapinfo.bmiHeader.biBitCount)+'bit';
  compression:=GETFOURCC(bitmapinfo.bmiHeader.biCompression);
end;
//                                                       |
//------------------/bildgröße...------------------------|

//------------------DIALOGE------------------------------|
//                                                       |
procedure TWebcam.DLG_VideoFormat;
begin
sendMessage(CapHandle, WM_CAP_DLG_VIDEOFORMAT, 10);
bildgroesse_anpassen;
end;
procedure TWebcam.DLG_VideoSource;
begin
sendMessage(CapHandle, WM_CAP_DLG_VIDEOSOURCE, 10);
bildgroesse_anpassen;
end;

procedure TWebcam.DLG_VideoCompression;
begin
sendMessage(CapHandle, WM_CAP_DLG_VIDEOCOMPRESSION, 10);
bildgroesse_anpassen;
end;
//                                                       |
//------------------/DIALOGE-----------------------------|


end.


ich häng mal auch gleich ein Beispielprojekt an...da sind alle jetzigen funktionen der Unit drin...

EDIT: hab vergessen das du auch noch die UConvert.pas brauchst...die ist in der angehängten Datei!


digi_c - Mi 22.03.06 09:06

Sry aber ich bearbeite gerade mein Projekt für meine Prüfung und habe daher recht wenig Zeit. Ich habe auch keine Erfahrung auf dem Gebiet. Ich denke du könntest das der Community veranschaulichen, wenn du mal komplett die Klasse vorher und hinterher postest. Vermutlich hängt das mit dem StdCall oder ähnlichen Dingen zusammen.

Wäre es nicht einfacher die Callback so zu lassen aber darin die eigentliche Verarbeitung durch eine Klassenmethode anzustoßen? IMHO wäre das o.k. und verständlich.


mtin - Mi 22.03.06 17:26

mhm, gut ich überarbeite die nochma und stell das wichtigste dann rein...aber wie meinst du das?
Wie soll ich die eigentliche Verarbeitung in eine Klassenmethode auslagern?
Die kann ich ja nicht aufrufen...also sowas wie hier im nachfolgenden geht natürlich nicht

Delphi-Quelltext
1:
2:
3:
4:
5:
function OnFrame(hCapWnd: HWND; lpVHDR: PVideoHdr): DWord; stdcall;
begin
  Result:=1;
  TWebcam.AfterFrame(lpVHDR);
end;



gut, hier ist jetzt das wichtigste (wirklich nur das was die Callback-Funktion betrifft, so funktioniert das ganze sicher nicht ;)

Die Deklaration dieses "TVIDEOHDR"

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
unit webcamdriver;

interface

uses GR32, UConvert, Windows, Messages, ExtCtrls, SysUtils;


 type
  PVIDEOHDR = ^TVIDEOHDR;
  TVIDEOHDR = packed record
    lpData         : PBYTE;                // pointer to locked data buffer
    dwBufferLength : DWORD;                // Length of data buffer
    dwBytesUsed    : DWORD;                // Bytes actually used
    dwTimeCaptured : DWORD;                // Milliseconds from start of stream
    dwUser         : DWORD;                // for client's use
    dwFlags        : DWORD;                // assorted flags (see defines)
    dwReserved     : array[0..3of DWORD; // reserved for driver
  end;


und jetzt die beiden Teile, einfach nur die Callback-Funktion mit in die Klasse TWebcam aufgenommen oder eben nicht, wäre aber wegen der Variablen sehr viel schöner!

AUßERHALB der Klasse

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
      var
       CapHandle:  HWND;
      const
       WM_CAP_SET_CALLBACK_FRAME  = (WM_USER +  5);

      var Frame:   TBitmap32;

type
  TWebcam = class
  private

  public
      constructor create(FormHandle:HWND);
  end;

implementation

//------------------CALLBACK-FUNCTION--------------------|
//                                                       |
function OnFrame(hCapWnd: HWND; lpVHDR: PVideoHdr): DWord; stdcall;
begin

----> lpVHDR enthält die Videodaten
                        (alles funktioniert perfekt)
end;

//------------------CONSTRUCTOR--------------------------|
constructor TWebcam.create(FormHandle:HWND);
begin
  inherited Create;
  SendMessage(CapHandle, WM_CAP_SET_CALLBACK_FRAME, 0, Integer(@OnFrame));
end;
INNERHALB der Klasse

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
type
  TWebcam = class
  private
      var
       CapHandle:  HWND;
      const
       WM_CAP_SET_CALLBACK_FRAME  = (WM_USER +  5);
      var 
       Frame:   TBitmap32;

  public
      
      constructor create(FormHandle:HWND);
      function OnFrame(hCapWnd: HWND; lpVHDR: PVideoHdr): DWord; stdcall;

  end;

implementation

//------------------CALLBACK-FUNCTION--------------------|
//                                                       |
function TWebcam.OnFrame(hCapWnd: HWND; lpVHDR: PVideoHdr): DWord; stdcall;
begin

----> lpVHDR ist "NIL"
          (exceptions über exceptions^^)
end;

//------------------CONSTRUCTOR--------------------------|
constructor TWebcam.create(FormHandle:HWND);
begin
  inherited Create;
  SendMessage(CapHandle, WM_CAP_SET_CALLBACK_FRAME, 0, Integer(@TWebcam.OnFrame));
end;
(Veränderungen sind hervorgehoben)

ahja.. :oops: ich hab keine Ahnung was das StdCall überhaupt bedeutet, ich les es jetzt mal nach aber vielleicht kann mir trotzdem jemand helfen :)


mtin - Sa 25.03.06 15:55

kennt sich da denn keiner aus?
oder beschreib ich das zu schlecht?

brauche H I L F E!!


digi_c - Di 28.03.06 15:12

Callbacks zu machen ohne wissen über DLLs,Speichermanagement,... kann sehr langwierig werden, weil einen Delphi da nichtmehr unterstützen kann da die Daten auch aus anderen Sprachen,Formaten,... kommen können.

STDCll ist eine Aufrufkonvention und gibt An in welcher Reihenfolge Parameter erwartet werden.

Ja so wie du das im ersten Source gemacht hast meinte ich das eine Funktion außerhalb der Klasse, die die Klassenmethode anstößt.

Hast du auch deine Methode als external; gekennzeichnet? Ansonsten sind die von außen nicht verfügbar!

Ich habe wie gesagt leider nicht das Wissen noch die Zeit noch die Möglichkeiten das detailiert nachzutesten, sry :oops: