1. 什麼是 Callback Function ?
當我們執行某個應用程式時, 針對 Form 上各個物件所發生的事件, 嚴格來說可以分成兩類:
a. 由使用者觸發, 如使用者透過滑鼠或鍵盤所觸發的事件。
這類事件, 在 object-oriented 程式語言中, 是用 event-driven 的設計, 使用者針對個別物件, 撰寫不同事件處理程序來因應。
b. 非由使用者觸發, 乃系統自行發生的事件。
如程式發生錯誤情況, 或系統做了特定事件, 如從視訊裝置擷取一個畫面。此類事件, 使用者就可以設定 callback function 來因應這些事件的發生。
2. 函數的呼叫 (call) 與 結束(return)
在程式的執行過程中, 當主程式呼叫一個函數 (call function) 時, 程式會跳到函數繼續執行, 當遇到 return 指令時, 就會返回主程式的呼叫點繼續執行。
3. 預覽回呼函數 preview callback function
當視訊擷取驅動程式接收到來自視訊裝置的畫面(frame), 在 preview 模式之下, 將此畫面顯示所連結的視訊視窗之中。
我們可以透過 capSetCallbackOnFrame 這個函數來指定當發生 frame preview 之前, 先執行一個特定的 callback function, 即預覽回呼函數。這個預覽回呼函數的名稱可以由我們自由命名, 但是傳入的參數個數與其資料型態則必須符合 VFW 的規定。
4. 實驗: 當程式開啟視訊擷取視窗後, 顯示預覽的畫面總數到 Label 上。
實驗目的: 了解視訊擷取視窗之預覽回呼函數的設定與使用方法。
範例程式: VGC-2010S-SP07.txt
5. 如何關閉預覽回呼函數
範例程式: VGC-2010S-SP08.txt
6. 預覽回呼函數可以做些什麼?
好處: 就是我們可以在畫面預覽之前, 先對畫面資料做預先處理, 例如: 加入文字、物件或特效等等。
這段影片就是利用預覽回呼函數, 將龍珠放置到畫面之中, 再與攝影機所拍攝的手進行互動...
但, 問題是這些畫面資料到底怎麼存取呢?
7. 如何從記憶體直接擷取視訊畫面?
透過 capSetCallbackOnFrame 這個函式設定: 當視訊擷取視窗(代碼為 hwndViedo) 取得一個 frame 後, 必須執行我們所定義的 callback function: FrameCallBack,
capSetCallbackOnFrame(hwndVideo, &FrameCallBack);
LRESULT CALLBACK FrameCallBack( HWND hwndCapture, PVIDEOHDR lpvhdr );
在我們所自行定義的 FrameCallBack callback function 中, 有兩個輸入參數:
a. HWND hwndCapture,
指明哪一個視訊擷取視窗, 即該視訊擷取視窗的 handle。
b. PVIDEOHDR lpvhdr,
capSetCallbackOnFrame 這個函式會自動把視訊擷取視窗的相關資訊透過 PVIDEOHDR lpvhdr 這個結構變數, 讓 FrameCallBack callback function 來使用。
有關 PVIEDOHDR 這個結構的相關資訊, 可以在 vfw.h 中, 找到如下的結構定義:
/* video data block header */
typedef struct videohdr_tag {
LPBYTE lpData; /* pointer to locked data buffer */
DWORD dwBufferLength; /* Length of data buffer */
DWORD dwBytesUsed; /* Bytes actually used */
DWORD dwTimeCaptured; /* Milliseconds from start of stream */
DWORD_PTR dwUser; /* for client's use */
DWORD dwFlags; /* assorted flags (see defines) */
DWORD_PTR dwReserved[4]; /* reserved for driver */
} VIDEOHDR, NEAR *PVIDEOHDR, FAR * LPVIDEOHDR;
透過 結構 videohdr_tag 的宣告, 我們獲知擷取視窗的 data buffer 的指標可以用 lpData 這個變數來取得, 即
CaptureData = LPVIDEOHDR(lpvhdr);
ptrCapture = CaptureData->lpData;
// 全域變數宣告
LPVIDEOHDR CaptureData;
BYTE *ptrCapture, *ptrImage;
說明:
a. CaptureData 是全域結構變數, 其資料型態亦為 PVIDEOHDR。
b. *ptrCapture 與 *ptrImage 是兩個指向記憶體位址的全域指標變數。
8. 實驗: 將視訊擷取視窗的內容即時顯示於影像物件之中。
實驗目的: 了解視訊擷取視窗資料緩衝區的記憶體存取方法。
本實驗說穿了就是把某一塊記憶體(視訊擷取視窗資料緩衝區/data buffer) 複製到另一塊記憶體(影像物件存放色彩資料的記憶體) 之中。因此, 我們只要弄清楚兩個記憶體的起始位址, 自然可以輕鬆完成實驗。
透過 videohdr_tag 結構中的 lpData, 我們可以得到視訊擷取視窗的資料暫時存放區的起始位址。
CaptureData = LPVIDEOHDR(lpvhdr);
ptrCapture = CaptureData->lpData;
透過 TBitmap::ScanLine 這個 property 我們可以得到影像元件中, 每一列(row) 的影像資料存放的記憶體起始位置。
ptrImage = (BYTE *)Form1->imCW->Picture->Bitmap->ScanLine[i];
範例程式: VGC-2010S-SP09.txt
沒有留言:
張貼留言