|
|
//
// Created 5-Nov-96 [RichP]
#include "Precomp.h"
DWORD _GetVideoFormatSize( HDRVR hvideo ) { DWORD bufsize; VIDEOCONFIGPARMS vcp;
vcp.lpdwReturn = &bufsize; vcp.lpData1 = NULL; vcp.dwSize1 = 0; vcp.lpData2 = NULL; vcp.dwSize2 = 0L;
#if 0
// it makes sense to query if DVM_FORMAT is available, but not all drivers support it!
if (SendDriverMessage(hvideo, DVM_FORMAT, (LPARAM)(DWORD)(VIDEO_CONFIGURE_GET | VIDEO_CONFIGURE_QUERY), (LPARAM)(LPVOID)&vcp) == DV_ERR_OK) { #endif
SendDriverMessage(hvideo, DVM_FORMAT, (LPARAM)(DWORD)(VIDEO_CONFIGURE_GET | VIDEO_CONFIGURE_QUERYSIZE), (LPARAM)(LPVOID)&vcp); if (!bufsize) bufsize = sizeof(BITMAPINFOHEADER); return bufsize; #if 0
} else return sizeof(BITMAPINFOHEADER); #endif
}
BOOL _GetVideoFormat( HVIDEO hvideo, LPBITMAPINFOHEADER lpbmih ) { BOOL res; VIDEOCONFIGPARMS vcp;
vcp.lpdwReturn = NULL; vcp.lpData1 = lpbmih; vcp.dwSize1 = lpbmih->biSize; vcp.lpData2 = NULL; vcp.dwSize2 = 0L;
res = !SendDriverMessage((HDRVR)hvideo, DVM_FORMAT, (LPARAM)(DWORD)(VIDEO_CONFIGURE_GET | VIDEO_CONFIGURE_CURRENT), (LPARAM)(LPVOID)&vcp); if (res) { // hack for Connectix QuickCam - set format needs to be called
// to set internal globals so that streaming can be enabled
SendDriverMessage((HDRVR)hvideo, DVM_FORMAT, (LPARAM)(DWORD)VIDEO_CONFIGURE_SET, (LPARAM)(LPVOID)&vcp); } return res; }
BOOL _SetVideoFormat( HVIDEO hvideoExtIn, HVIDEO hvideoIn, LPBITMAPINFOHEADER lpbmih ) { RECT rect; VIDEOCONFIGPARMS vcp;
vcp.lpdwReturn = NULL; vcp.lpData1 = lpbmih; vcp.dwSize1 = lpbmih->biSize; vcp.lpData2 = NULL; vcp.dwSize2 = 0L;
// See if the driver likes the format
if (SendDriverMessage((HDRVR)hvideoIn, DVM_FORMAT, (LPARAM)(DWORD)VIDEO_CONFIGURE_SET, (LPARAM)(LPVOID)&vcp)) return FALSE;
// Set the rectangles
rect.left = rect.top = 0; rect.right = (WORD)lpbmih->biWidth; rect.bottom = (WORD)lpbmih->biHeight; SendDriverMessage((HDRVR)hvideoExtIn, DVM_DST_RECT, (LPARAM)(LPVOID)&rect, VIDEO_CONFIGURE_SET); SendDriverMessage((HDRVR)hvideoIn, DVM_SRC_RECT, (LPARAM)(LPVOID)&rect, VIDEO_CONFIGURE_SET);
return TRUE; }
BOOL _GetVideoPalette( HVIDEO hvideo, LPCAPTUREPALETTE lpcp, DWORD dwcbSize ) { VIDEOCONFIGPARMS vcp;
vcp.lpdwReturn = NULL; vcp.lpData1 = (LPVOID)lpcp; vcp.dwSize1 = dwcbSize; vcp.lpData2 = NULL; vcp.dwSize2 = 0;
return !SendDriverMessage((HDRVR)hvideo, DVM_PALETTE, (DWORD)(VIDEO_CONFIGURE_GET | VIDEO_CONFIGURE_CURRENT), (DWORD_PTR)&vcp);
}
void FrameCallback( HVIDEO hvideo, WORD wMsg, HCAPDEV hcd, // (Actually refdata)
LPCAPBUFFER lpcbuf, // (Actually LPVIDEOHDR) Only returned from MM_DRVM_DATA!
DWORD dwParam2 ) { FX_ENTRY("FrameCallback");
DEBUGMSG(ZONE_CALLBACK, ("%s: wMsg=%s, hcd=0x%08lX, lpcbuf=0x%08lX, hcd->hevWait=0x%08lX\r\n", _fx_, (wMsg == MM_DRVM_OPEN) ? "MM_DRVM_OPEN" : (wMsg == MM_DRVM_CLOSE) ? "MM_DRVM_CLOSE" : (wMsg == MM_DRVM_ERROR) ? "MM_DRVM_ERROR" : (wMsg == MM_DRVM_DATA) ? "MM_DRVM_DATA" : "MM_DRVM_?????", hcd, lpcbuf, hcd->hevWait));
// If it's not a data ready message, just set the event and get out.
// The reason we do this is that if we get behind and start getting a stream
// of MM_DRVM_ERROR messages (usually because we're stopped in the debugger),
// we want to make sure we are getting events so we get restarted to handle
// the frames that are 'stuck.'
if (wMsg != MM_DRVM_DATA) { DEBUGMSG(ZONE_CALLBACK, ("%s: Setting hcd->hevWait - no data\r\n", _fx_)); SetEvent(hcd->hevWait); return; }
//--------------------
// Buffer ready queue:
// We maintain a doubly-linked list of our buffers so that we can buffer up
// multiple ready frames when the app isn't ready to handle them. Two things
// complicate what ought to be a very simple thing: (1) Thunking issues: the pointers
// used on the 16-bit side are 16:16 (2) Interrupt time issues: the FrameCallback
// gets called at interrupt time. GetNextReadyBuffer must handle the fact that
// buffers get added to the list asynchronously.
//
// To handle this, the scheme implemented here is to have a double-linked list
// of buffers with all insertions and deletions happening in FrameCallback
// (interrupt time). This allows the GetNextReadyBuffer routine to simply
// find the previous block on the list any time it needs a new buffer without
// fear of getting tromped (as would be the case if it had to dequeue buffers).
// The FrameCallback routine is responsible to dequeue blocks that GetNextReadyBuffer
// is done with. Dequeueing is simple since we don't need to unlink the blocks:
// no code ever walks the list! All we have to do is move the tail pointer back up
// the list. All the pointers, head, tail, next, prev, are all 16:16 pointers
// since all the list manipulation is on the 16-bit side AND because MapSL is
// much more efficient and safer than MapLS since MapLS has to allocate selectors.
//--------------------
// Move the tail back to skip all buffers already used.
// Note that there is no need to actually unhook the buffer pointers since no one
// ever walks the list!
// This makes STRICT assumptions that the current pointer will always be earlier in
// the list than the tail and that the tail will never be NULL unless the
// current pointer is too.
while (hcd->lpTail != hcd->lpCurrent) hcd->lpTail = hcd->lpTail->lpPrev;
// If all buffers have been used, then the tail pointer will fall off the list.
// This is normal and the most common code path. In this event, just set the head
// to NULL as the list is now empty.
if (!hcd->lpTail) hcd->lpHead = NULL;
// Add the new buffer to the ready queue
lpcbuf->lpNext = hcd->lpHead; lpcbuf->lpPrev = NULL; if (hcd->lpHead) hcd->lpHead->lpPrev = lpcbuf; else hcd->lpTail = lpcbuf; hcd->lpHead = lpcbuf;
#if 1
if (hcd->lpCurrent) { if (!(hcd->dwFlags & HCAPDEV_STREAMING_PAUSED)) { // if client hasn't consumed last frame, then release it
lpcbuf = hcd->lpCurrent; hcd->lpCurrent = hcd->lpCurrent->lpPrev; DEBUGMSG(ZONE_CALLBACK, ("%s: We already have current buffer (lpcbuf=0x%08lX). Returning this buffer to driver. Set new current buffer hcd->lpCurrent=0x%08lX\r\n", _fx_, lpcbuf, hcd->lpCurrent)); // Signal that the application is done with the buffer
lpcbuf->vh.dwFlags &= ~VHDR_DONE; if (SendDriverMessage(reinterpret_cast<HDRVR>(hvideo), DVM_STREAM_ADDBUFFER, (DWORD_PTR)lpcbuf, sizeof(VIDEOHDR)) != 0) { ERRORMESSAGE(("%s: Attempt to reuse unconsumed buffer failed\r\n", _fx_)); } } } else { #else
if (!hcd->lpCurrent) { // If there was no current buffer before, we have one now, so set it to the end.
#endif
hcd->lpCurrent = hcd->lpTail; }
// Now set the event saying it's time to process the ready frame
DEBUGMSG(ZONE_CALLBACK, ("%s: Setting hcd->hevWait - some data\r\n", _fx_)); SetEvent(hcd->hevWait); }
BOOL _InitializeVideoStream( HVIDEO hvideo, DWORD dwMicroSecPerFrame, DWORD_PTR hcd ) { VIDEO_STREAM_INIT_PARMS vsip;
ZeroMemory((LPSTR)&vsip, sizeof (VIDEO_STREAM_INIT_PARMS)); vsip.dwMicroSecPerFrame = dwMicroSecPerFrame; vsip.dwCallback = (DWORD_PTR)FrameCallback; vsip.dwCallbackInst = hcd; vsip.dwFlags = CALLBACK_FUNCTION; vsip.hVideo = (DWORD_PTR)hvideo;
return !SendDriverMessage((HDRVR)hvideo, DVM_STREAM_INIT, (DWORD_PTR)&vsip, (DWORD) sizeof (VIDEO_STREAM_INIT_PARMS)); }
BOOL _UninitializeVideoStream( HVIDEO hvideo ) { return !SendDriverMessage((HDRVR)hvideo, DVM_STREAM_FINI, 0L, 0L); }
BOOL _InitializeExternalVideoStream( HVIDEO hvideo ) { VIDEO_STREAM_INIT_PARMS vsip;
vsip.dwMicroSecPerFrame = 0; // Ignored by driver for this channel
vsip.dwCallback = 0L; // No callback for now
vsip.dwCallbackInst = 0L; vsip.dwFlags = 0; vsip.hVideo = (DWORD_PTR)hvideo;
return !SendDriverMessage((HDRVR)hvideo, DVM_STREAM_INIT, (DWORD_PTR)&vsip, (DWORD) sizeof (VIDEO_STREAM_INIT_PARMS)); }
BOOL _PrepareHeader( HANDLE hvideo, VIDEOHDR *vh ) { return (SendDriverMessage((HDRVR)hvideo, DVM_STREAM_PREPAREHEADER, (DWORD_PTR)vh, (DWORD) sizeof (VIDEOHDR)) == DV_ERR_OK); }
LRESULT _UnprepareHeader( HANDLE hvideo, VIDEOHDR *vh ) { return SendDriverMessage((HDRVR)hvideo, DVM_STREAM_UNPREPAREHEADER, (DWORD_PTR)vh, (DWORD) sizeof (VIDEOHDR)); }
|