// // 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(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)); }