800 lines
23 KiB
C++
800 lines
23 KiB
C++
|
||
/****************************************************************************
|
||
* @doc INTERNAL DIALOGS
|
||
*
|
||
* @module WDMStrmr.cpp | Source file for <c CWDMStreamer> class used to get a
|
||
* stream of video data flowing from WDM devices.
|
||
*
|
||
* @comm This code is based on the VfW to WDM mapper code written by
|
||
* FelixA and E-zu Wu. The original code can be found on
|
||
* \\redrum\slmro\proj\wdm10\\src\image\vfw\win9x\raytube.
|
||
*
|
||
* Documentation by George Shaw on kernel streaming can be found in
|
||
* \\popcorn\razzle1\src\spec\ks\ks.doc.
|
||
*
|
||
* WDM streaming capture is discussed by Jay Borseth in
|
||
* \\blues\public\jaybo\WDMVCap.doc.
|
||
***************************************************************************/
|
||
|
||
#include "Precomp.h"
|
||
|
||
|
||
/****************************************************************************
|
||
* @doc INTERNAL CWDMSTREAMERMETHOD
|
||
*
|
||
* @mfunc void | CWDMStreamer | CWDMStreamer | WDM filter class constructor.
|
||
*
|
||
* @parm CWDMPin * | pWDMVideoPin | Pointer to the kernel streaming
|
||
* object we will get the frames from.
|
||
***************************************************************************/
|
||
CWDMStreamer::CWDMStreamer(CWDMPin * pWDMVideoPin)
|
||
{
|
||
m_pWDMVideoPin = pWDMVideoPin;
|
||
m_lpVHdrFirst = (LPVIDEOHDR)NULL;
|
||
m_lpVHdrLast = (LPVIDEOHDR)NULL;
|
||
m_fVideoOpen = FALSE;
|
||
m_fStreamingStarted = FALSE;
|
||
m_pBufTable = (PBUFSTRUCT)NULL;
|
||
m_cntNumVidBuf = 0UL;
|
||
m_idxNextVHdr = 0UL;
|
||
m_hThread = NULL;
|
||
m_bKillThread = FALSE;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* @doc INTERNAL CWDMSTREAMERMETHOD
|
||
*
|
||
* @mfunc void | CWDMStreamer | videoCallback | This function calls the
|
||
* callback function provided by the appplication.
|
||
*
|
||
* @parm WORD | msg | Message value.
|
||
*
|
||
* @parm DWORD | dwParam1 | 32-bit message-dependent parameter.
|
||
***************************************************************************/
|
||
void CWDMStreamer::videoCallback(WORD msg, DWORD_PTR dwParam1)
|
||
{
|
||
if (m_CaptureStreamParms.dwCallback)
|
||
DriverCallback (m_CaptureStreamParms.dwCallback, HIWORD(m_CaptureStreamParms.dwFlags), (HDRVR) m_CaptureStreamParms.hVideo, msg, m_CaptureStreamParms.dwCallbackInst, dwParam1, 0UL);
|
||
}
|
||
|
||
|
||
/****************************************************************************
|
||
* @doc INTERNAL CWDMSTREAMERMETHOD
|
||
*
|
||
* @mfunc LPVIDEOHDR | CWDMStreamer | DeQueueHeader | This function dequeues a
|
||
* video buffer from the list of video buffers used for streaming.
|
||
*
|
||
* @rdesc Returns a valid pointer if successful, or NULL otherwise.
|
||
***************************************************************************/
|
||
LPVIDEOHDR CWDMStreamer::DeQueueHeader()
|
||
{
|
||
FX_ENTRY("CWDMStreamer::DeQueueHeader");
|
||
|
||
LPVIDEOHDR lpVHdr;
|
||
|
||
if (m_pBufTable)
|
||
{
|
||
if (m_pBufTable[m_idxNextVHdr].fReady)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, (" %s: DeQueuing idxNextVHdr (idx=%d) with data to be filled at lpVHdr=0x%08lX\r\n", _fx_, m_idxNextVHdr, m_pBufTable[m_idxNextVHdr].lpVHdr));
|
||
|
||
lpVHdr = m_pBufTable[m_idxNextVHdr].lpVHdr;
|
||
lpVHdr->dwFlags &= ~VHDR_INQUEUE;
|
||
m_pBufTable[m_idxNextVHdr].fReady = FALSE;
|
||
}
|
||
else
|
||
{
|
||
m_idxNextVHdr++;
|
||
if (m_idxNextVHdr >= m_cntNumVidBuf)
|
||
m_idxNextVHdr = 0;
|
||
|
||
if (m_pBufTable[m_idxNextVHdr].fReady)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, (" %s: DeQueuing idxNextVHdr (idx=%d) with data to be filled at lpVHdr=0x%08lX\r\n", _fx_, m_idxNextVHdr, m_pBufTable[m_idxNextVHdr].lpVHdr));
|
||
|
||
lpVHdr = m_pBufTable[m_idxNextVHdr].lpVHdr;
|
||
lpVHdr->dwFlags &= ~VHDR_INQUEUE;
|
||
m_pBufTable[m_idxNextVHdr].fReady = FALSE;
|
||
}
|
||
else
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, (" %s: idxNextVHdr (idx=%d) has not been returned by client\r\n", _fx_, m_idxNextVHdr));
|
||
lpVHdr = NULL;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
lpVHdr = m_lpVHdrFirst;
|
||
|
||
if (lpVHdr) {
|
||
|
||
lpVHdr->dwFlags &= ~VHDR_INQUEUE;
|
||
|
||
m_lpVHdrFirst = (LPVIDEOHDR)(lpVHdr->dwReserved[0]);
|
||
|
||
if (m_lpVHdrFirst == NULL)
|
||
m_lpVHdrLast = NULL;
|
||
}
|
||
}
|
||
|
||
return lpVHdr;
|
||
}
|
||
|
||
|
||
/****************************************************************************
|
||
* @doc INTERNAL CWDMSTREAMERMETHOD
|
||
*
|
||
* @mfunc void | CWDMStreamer | QueueHeader | This function actually adds the
|
||
* video buffer to the list of video buffers used for streaming.
|
||
*
|
||
* @parm LPVIDEOHDR | lpVHdr | Pointer to a <t VIDEOHDR> structure describing
|
||
* a video buffer to add to the list of streaming buffers.
|
||
***************************************************************************/
|
||
void CWDMStreamer::QueueHeader(LPVIDEOHDR lpVHdr)
|
||
{
|
||
FX_ENTRY("CWDMStreamer::QueHeader");
|
||
|
||
// Initialize status flags
|
||
lpVHdr->dwFlags &= ~VHDR_DONE;
|
||
lpVHdr->dwFlags |= VHDR_INQUEUE;
|
||
lpVHdr->dwBytesUsed = 0;
|
||
|
||
// Add buffer to list
|
||
if (m_pBufTable)
|
||
{
|
||
if (lpVHdr->dwReserved[1] < m_cntNumVidBuf)
|
||
{
|
||
if (m_pBufTable[lpVHdr->dwReserved[1]].lpVHdr != lpVHdr)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, (" %s: index (%d) Match but lpVHdr does not(%x)\r\n", _fx_, lpVHdr->dwReserved[1], lpVHdr));
|
||
}
|
||
m_pBufTable[lpVHdr->dwReserved[1]].fReady = TRUE;
|
||
DEBUGMSG(ZONE_STREAMING, (" %s: Buffer lpVHdr=0x%08lX was succesfully queued\r\n", _fx_, lpVHdr));
|
||
}
|
||
else
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, (" %s: lpVHdr->dwReserved[1](%d) >= m_cntNumVidBuf (%d)\r\n", _fx_, lpVHdr->dwReserved[1], m_cntNumVidBuf));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
*(lpVHdr->dwReserved) = NULL;
|
||
|
||
if (m_lpVHdrLast)
|
||
*(m_lpVHdrLast->dwReserved) = (DWORD_PTR)lpVHdr;
|
||
else
|
||
m_lpVHdrFirst = lpVHdr;
|
||
|
||
m_lpVHdrLast = lpVHdr;
|
||
}
|
||
}
|
||
|
||
|
||
/****************************************************************************
|
||
* @doc INTERNAL CWDMSTREAMERMETHOD
|
||
*
|
||
* @mfunc BOOL | CWDMStreamer | AddBuffer | This function adds a buffer to the
|
||
* list of video buffers to be used when streaming video data from the WDM
|
||
* device.
|
||
*
|
||
* @parm LPVIDEOHDR | lpVHdr | Pointer to a <t VIDEOHDR> structure describing
|
||
* a video buffer to add to the list of streaming buffers.
|
||
*
|
||
* @rdesc Returns TRUE if successful, or FALSE otherwise.
|
||
*
|
||
* @devnote This function handles what was the DVM_STREAM_ADDBUFFER message in VfW.
|
||
***************************************************************************/
|
||
BOOL CWDMStreamer::AddBuffer(LPVIDEOHDR lpVHdr)
|
||
{
|
||
FX_ENTRY("CWDMStreamer::AddBuffer");
|
||
|
||
ASSERT(m_fVideoOpen && lpVHdr && !(lpVHdr->dwFlags & VHDR_INQUEUE));
|
||
|
||
// Make sure this is a valid call
|
||
if (!m_fVideoOpen)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Buffer lpVHdr=0x%08lX can't be queued because m_fVideoOpen=FALSE\r\n", _fx_, lpVHdr));
|
||
return FALSE;
|
||
}
|
||
|
||
if (!lpVHdr)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Buffer lpVHdr=0x%08lX can't be queued because lpVHdr=NULL\r\n", _fx_, lpVHdr));
|
||
return FALSE;
|
||
}
|
||
|
||
if (lpVHdr->dwFlags & VHDR_INQUEUE)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Buffer lpVHdr=0x%08lX can't be queued because buffer is already queued\r\n", _fx_, lpVHdr));
|
||
return FALSE;
|
||
}
|
||
|
||
// Does the size of the buffer match the size of the buffers the streaming pin will generate?
|
||
if (lpVHdr->dwBufferLength < m_pWDMVideoPin->GetFrameSize())
|
||
{
|
||
ERRORMESSAGE(("%s: Buffer lpVHdr=0x%08lX can't be queued because the length of that buffer is too small\r\n", _fx_, lpVHdr));
|
||
return FALSE;
|
||
}
|
||
|
||
if (!m_pBufTable)
|
||
{
|
||
lpVHdr->dwReserved[1] = m_cntNumVidBuf;
|
||
m_cntNumVidBuf++;
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Queue buffer (%d) lpVHdr=0x%08lX\r\n", _fx_, lpVHdr->dwReserved[1], lpVHdr));
|
||
}
|
||
|
||
QueueHeader(lpVHdr);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
/****************************************************************************
|
||
* @doc INTERNAL CWDMSTREAMERMETHOD
|
||
*
|
||
* @mfunc BOOL | CWDMStreamer | Stop | This function stops a stream of
|
||
* video data coming from the WDM device.
|
||
*
|
||
* @rdesc Returns TRUE if successful, or FALSE otherwise.
|
||
*
|
||
* @devnote This function handles what was the DVM_STREAM_STOP message in VfW.
|
||
***************************************************************************/
|
||
BOOL CWDMStreamer::Stop()
|
||
{
|
||
FX_ENTRY("CWDMStreamer::Stop");
|
||
|
||
ASSERT(m_fVideoOpen);
|
||
|
||
// Make sure this is a valid call
|
||
if (!m_fVideoOpen)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Stream is not even opened\r\n", _fx_));
|
||
return FALSE;
|
||
}
|
||
|
||
DEBUGMSG(ZONE_STREAMING, ("%s()\r\n", _fx_));
|
||
|
||
// Reset data members - stop streaming thread
|
||
m_fStreamingStarted = FALSE;
|
||
|
||
if (m_hThread)
|
||
{
|
||
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Stopping the thread\r\n", _fx_));
|
||
|
||
// Signal the streaming thread to stop
|
||
m_bKillThread = TRUE;
|
||
|
||
// wait until thread has self-terminated, and clear the event.
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: WaitingForSingleObject...\r\n", _fx_));
|
||
|
||
WaitForSingleObject(m_hThread, INFINITE);
|
||
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: ...thread stopped\r\n", _fx_));
|
||
|
||
// Close the thread handle
|
||
CloseHandle(m_hThread);
|
||
m_hThread = NULL;
|
||
|
||
// Ask the pin to stop streaming.
|
||
m_pWDMVideoPin->Stop();
|
||
|
||
for (UINT i=0; i<m_cntNumVidBuf; i++)
|
||
{
|
||
if (m_pWDMVideoBuff[i].Overlap.hEvent)
|
||
{
|
||
SetEvent(m_pWDMVideoBuff[i].Overlap.hEvent);
|
||
CloseHandle(m_pWDMVideoBuff[i].Overlap.hEvent);
|
||
m_pWDMVideoBuff[i].Overlap.hEvent = NULL;
|
||
}
|
||
}
|
||
|
||
if (m_pWDMVideoBuff)
|
||
{
|
||
delete []m_pWDMVideoBuff;
|
||
m_pWDMVideoBuff = (WDMVIDEOBUFF *)NULL;
|
||
}
|
||
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
/****************************************************************************
|
||
* @doc INTERNAL CWDMSTREAMERMETHOD
|
||
*
|
||
* @mfunc BOOL | CWDMStreamer | Reset | This function resets a stream of
|
||
* video data coming from the WDM device so that prepared buffer may be
|
||
* freed correctly.
|
||
*
|
||
* @rdesc Returns TRUE if successful, or FALSE otherwise.
|
||
*
|
||
* @devnote This function handles what was the DVM_STREAM_RESET message in VfW.
|
||
***************************************************************************/
|
||
BOOL CWDMStreamer::Reset()
|
||
{
|
||
LPVIDEOHDR lpVHdr;
|
||
|
||
FX_ENTRY("CWDMStreamer::Reset");
|
||
|
||
ASSERT(m_fVideoOpen);
|
||
|
||
// Make sure this is a valid call
|
||
if (!m_fVideoOpen)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Stream is not even opened\r\n", _fx_));
|
||
return FALSE;
|
||
}
|
||
|
||
DEBUGMSG(ZONE_STREAMING, ("%s()\r\n", _fx_));
|
||
|
||
// Terminate streaming thread
|
||
Stop();
|
||
|
||
// Return all buffers to the application one last time
|
||
while (lpVHdr = DeQueueHeader ())
|
||
{
|
||
lpVHdr->dwFlags |= VHDR_DONE;
|
||
videoCallback(MM_DRVM_DATA, (DWORD_PTR) lpVHdr);
|
||
}
|
||
|
||
// Reset data members
|
||
m_lpVHdrFirst = (LPVIDEOHDR)NULL;
|
||
m_lpVHdrLast = (LPVIDEOHDR)NULL;
|
||
if (m_pBufTable)
|
||
{
|
||
delete []m_pBufTable;
|
||
m_pBufTable = NULL;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
/****************************************************************************
|
||
* @doc INTERNAL CWDMSTREAMERMETHOD
|
||
*
|
||
* @mfunc BOOL | CWDMStreamer | Open | This function opens a stream of
|
||
* video data coming from the WDM device.
|
||
*
|
||
* @parm LPVIDEO_STREAM_INIT_PARMS | lpStreamInitParms | Pointer to
|
||
* initialization data.
|
||
*
|
||
* @rdesc Returns TRUE if successful, or FALSE otherwise.
|
||
*
|
||
* @devnote This function handles what was the DVM_STREAM_INIT message in VfW.
|
||
***************************************************************************/
|
||
BOOL CWDMStreamer::Open(LPVIDEO_STREAM_INIT_PARMS lpStreamInitParms)
|
||
{
|
||
FX_ENTRY("CWDMStreamer::Open");
|
||
|
||
ASSERT(!m_fVideoOpen);
|
||
|
||
// Make sure this is a valid call
|
||
if (m_fVideoOpen)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Stream is already opened\r\n", _fx_));
|
||
return FALSE;
|
||
}
|
||
|
||
DEBUGMSG(ZONE_STREAMING, ("%s()\r\n", _fx_));
|
||
|
||
// Initialize data memmbers
|
||
m_CaptureStreamParms = *lpStreamInitParms;
|
||
m_fVideoOpen = TRUE;
|
||
m_lpVHdrFirst = (LPVIDEOHDR)NULL;
|
||
m_lpVHdrLast = (LPVIDEOHDR)NULL;
|
||
m_cntNumVidBuf = 0UL;
|
||
|
||
// Set frame rate on the pin
|
||
m_pWDMVideoPin->SetAverageTimePerFrame(lpStreamInitParms->dwMicroSecPerFrame * 10);
|
||
|
||
// Let the app know we just opened a stream
|
||
videoCallback(MM_DRVM_OPEN, 0L);
|
||
|
||
if (lpStreamInitParms->dwMicroSecPerFrame != 0)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Capturing at %d frames/sec\r\n", _fx_, 100000 / lpStreamInitParms->dwMicroSecPerFrame));
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
/****************************************************************************
|
||
* @doc INTERNAL CWDMSTREAMERMETHOD
|
||
*
|
||
* @mfunc BOOL | CWDMStreamer | Close | This function closes the stream of
|
||
* video data coming from the WDM device.
|
||
*
|
||
* @rdesc Returns TRUE if successful, or FALSE otherwise.
|
||
*
|
||
* @devnote This function handles what was the DVM_STREAM_FINI message in VfW.
|
||
***************************************************************************/
|
||
BOOL CWDMStreamer::Close()
|
||
{
|
||
FX_ENTRY("CWDMStreamer::Close");
|
||
|
||
ASSERT(m_fVideoOpen && !m_lpVHdrFirst);
|
||
|
||
// Make sure this is a valid call
|
||
if (!m_fVideoOpen || m_lpVHdrFirst)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Invalid parameters\r\n", _fx_));
|
||
return FALSE;
|
||
}
|
||
|
||
DEBUGMSG(ZONE_STREAMING, ("%s()\r\n", _fx_));
|
||
|
||
// Terminate streaming thread
|
||
Stop();
|
||
|
||
// Reset data members
|
||
m_fVideoOpen = FALSE;
|
||
m_lpVHdrFirst = m_lpVHdrLast = (LPVIDEOHDR)NULL;
|
||
m_idxNextVHdr = 0UL;
|
||
|
||
// Release table of pointers to video buffers
|
||
if (m_pBufTable)
|
||
{
|
||
delete []m_pBufTable;
|
||
m_pBufTable = NULL;
|
||
}
|
||
|
||
// Let the app know that we just closed the stream
|
||
videoCallback(MM_DRVM_CLOSE, 0L);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
/****************************************************************************
|
||
* @doc INTERNAL CWDMSTREAMERMETHOD
|
||
*
|
||
* @mfunc void | CWDMStreamer | BufferDone | This function lets the application
|
||
* know that there is video data available coming from the WDM device.
|
||
*
|
||
* @devnote This method is called by the kernel streaming object (Pin)
|
||
***************************************************************************/
|
||
void CWDMStreamer::BufferDone(LPVIDEOHDR lpVHdr)
|
||
{
|
||
FX_ENTRY("CWDMStreamer::BufferDone");
|
||
|
||
// Make sure this is a valid call
|
||
if (!m_fStreamingStarted)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Video has not been started or just been stopped\r\n", _fx_));
|
||
return;
|
||
}
|
||
|
||
if (lpVHdr == NULL)
|
||
{
|
||
// No buffers available - the app hasn't returned the buffers to us yet
|
||
DEBUGMSG(ZONE_STREAMING, (" %s: Let the app know that we don't have any buffers anymore since lpVHdr=NULL\r\n", _fx_));
|
||
|
||
// Let the app know something wrong happened
|
||
videoCallback(MM_DRVM_ERROR, 0UL);
|
||
return;
|
||
}
|
||
|
||
lpVHdr->dwFlags |= VHDR_DONE;
|
||
|
||
// Sanity check
|
||
if (lpVHdr->dwBytesUsed == 0)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, (" %s: Let the app know that there is no valid data available in lpVHdr=0x%08lX\r\n", _fx_, lpVHdr));
|
||
|
||
// Return frame to the pool before notifying app
|
||
AddBuffer(lpVHdr);
|
||
videoCallback(MM_DRVM_ERROR, 0UL);
|
||
}
|
||
else
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, (" %s: Let the app know that there is data available in lpVHdr=0x%08lX\r\n", _fx_, lpVHdr));
|
||
|
||
lpVHdr->dwTimeCaptured = timeGetTime() - m_dwTimeStart;
|
||
|
||
// Let the app know there's some valid video data available
|
||
videoCallback(MM_DRVM_DATA, (DWORD_PTR)lpVHdr);
|
||
}
|
||
}
|
||
|
||
|
||
/****************************************************************************
|
||
* @doc INTERNAL CWDMSTREAMERMETHOD
|
||
*
|
||
* @mfunc BOOL | CWDMStreamer | Start | This function starts streaming
|
||
* video data coming from the WDM device.
|
||
*
|
||
* @rdesc Returns TRUE if successful, or FALSE otherwise.
|
||
*
|
||
* @devnote This function handles what was the DVM_STREAM_START message in VfW.
|
||
***************************************************************************/
|
||
BOOL CWDMStreamer::Start()
|
||
{
|
||
FX_ENTRY("CWDMStreamer::Start");
|
||
|
||
ULONG i;
|
||
LPVIDEOHDR lpVHdr;
|
||
DWORD dwThreadID;
|
||
|
||
ASSERT(m_fVideoOpen && m_pWDMVideoPin->GetAverageTimePerFrame() && !m_hThread);
|
||
|
||
// Make sure this is a valid call
|
||
if (!m_fVideoOpen || !m_pWDMVideoPin->GetAverageTimePerFrame() || m_hThread)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Invalid parameters\r\n", _fx_));
|
||
return FALSE;
|
||
}
|
||
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Streaming in %d video buffers at %d frames/sec\r\n", _fx_, m_cntNumVidBuf, 1000000 / m_pWDMVideoPin->GetAverageTimePerFrame()));
|
||
|
||
// Allocate and initialize the video buffer structures
|
||
m_pBufTable = (PBUFSTRUCT) new BUFSTRUCT[m_cntNumVidBuf];
|
||
if (m_pBufTable)
|
||
{
|
||
lpVHdr = m_lpVHdrFirst;
|
||
for (i = 0; i < m_cntNumVidBuf && lpVHdr; i++)
|
||
{
|
||
m_pBufTable[i].fReady = TRUE;
|
||
m_pBufTable[i].lpVHdr = lpVHdr;
|
||
lpVHdr = (LPVIDEOHDR) lpVHdr->dwReserved[0];
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: m_pBufTable allocation failed! AsynIO may be out of sequence\r\n", _fx_));
|
||
}
|
||
|
||
m_idxNextVHdr = 0UL; // 0..m_cntNumVidBuf-1
|
||
m_dwTimeStart = timeGetTime();
|
||
m_fStreamingStarted = TRUE;
|
||
m_bKillThread = FALSE;
|
||
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Creating %d read video buffers\r\n", _fx_, m_cntNumVidBuf));
|
||
|
||
if (!(m_pWDMVideoBuff = (WDMVIDEOBUFF *) new WDMVIDEOBUFF[m_cntNumVidBuf]))
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: m_Overlap allocation failed!\r\n", _fx_));
|
||
return FALSE;
|
||
}
|
||
|
||
for(i=0; i<m_cntNumVidBuf; i++)
|
||
{
|
||
// Create the overlapped structures
|
||
ZeroMemory( &(m_pWDMVideoBuff[i].Overlap), sizeof(OVERLAPPED) );
|
||
m_pWDMVideoBuff[i].Overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Event %d is handle 0x%08lX\r\n", _fx_, i, m_pWDMVideoBuff[i].Overlap.hEvent));
|
||
}
|
||
|
||
m_dwNextToComplete=0;
|
||
|
||
// Create the streaming thread
|
||
m_hThread = CreateThread((LPSECURITY_ATTRIBUTES)NULL,
|
||
0,
|
||
(LPTHREAD_START_ROUTINE)ThreadStub,
|
||
this,
|
||
CREATE_SUSPENDED,
|
||
&dwThreadID);
|
||
|
||
if (m_hThread == NULL)
|
||
{
|
||
ERRORMESSAGE(("%s: Couldn't create the thread\r\n", _fx_));
|
||
|
||
for (UINT i=0; i<m_cntNumVidBuf; i++)
|
||
{
|
||
if (m_pWDMVideoBuff[i].Overlap.hEvent)
|
||
CloseHandle(m_pWDMVideoBuff[i].Overlap.hEvent);
|
||
}
|
||
|
||
delete []m_pWDMVideoBuff;
|
||
m_pWDMVideoBuff = (WDMVIDEOBUFF *)NULL;
|
||
|
||
m_lpVHdrFirst = (LPVIDEOHDR)NULL;
|
||
m_lpVHdrLast = (LPVIDEOHDR)NULL;
|
||
if (m_pBufTable)
|
||
{
|
||
delete []m_pBufTable;
|
||
m_pBufTable = NULL;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
SetThreadPriority(m_hThread, THREAD_PRIORITY_ABOVE_NORMAL);
|
||
|
||
ResumeThread(m_hThread);
|
||
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Thread created OK\r\n", _fx_));
|
||
|
||
return TRUE;
|
||
|
||
}
|
||
|
||
|
||
/****************************************************************************
|
||
* @doc INTERNAL CWDMSTREAMERMETHOD
|
||
*
|
||
* @mfunc BOOL | CWDMStreamer | Stream | This function does the actual
|
||
* streaming.
|
||
***************************************************************************/
|
||
void CWDMStreamer::Stream()
|
||
{
|
||
FX_ENTRY("CWDMStreamer::Stream");
|
||
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Starting to process StreamingThread\r\n", _fx_));
|
||
|
||
// Put the pin in streaming mode
|
||
m_pWDMVideoPin->Start();
|
||
|
||
// Queue all the reads
|
||
for (UINT i = 0; i<m_cntNumVidBuf; i++)
|
||
{
|
||
QueueRead(i);
|
||
}
|
||
|
||
m_dwNextToComplete=0;
|
||
#ifdef _DEBUG
|
||
m_dwFrameCount=0;
|
||
#endif
|
||
BOOL bGotAFrame=FALSE;
|
||
DWORD dwRes;
|
||
|
||
DEBUGMSG(ZONE_STREAMING, ("\r\n%s: Starting to wait on reads to complete\r\n", _fx_));
|
||
|
||
while (!m_bKillThread)
|
||
{
|
||
bGotAFrame = FALSE;
|
||
|
||
if (m_pWDMVideoBuff[m_dwNextToComplete].fBlocking)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("\r\n%s: Waiting on read to complete...\r\n", _fx_));
|
||
|
||
// Waiting for the asynchronous read to complete
|
||
dwRes = WaitForSingleObject(m_pWDMVideoBuff[m_dwNextToComplete].Overlap.hEvent, 1000*1);
|
||
|
||
if (dwRes == WAIT_FAILED)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: ...we couldn't perform the wait as requested\r\n", _fx_));
|
||
}
|
||
|
||
if (dwRes == WAIT_OBJECT_0)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: ...wait is over - we now have a frame\r\n", _fx_));
|
||
bGotAFrame = TRUE;
|
||
}
|
||
else
|
||
{
|
||
// time out waiting for frames.
|
||
if (dwRes == WAIT_TIMEOUT)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Waiting failed with timeout, last error=%d\r\n", _fx_, GetLastError()));
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// We didn't have to wait - this means the read executed synchronously
|
||
bGotAFrame = TRUE;
|
||
}
|
||
|
||
if (bGotAFrame)
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Trying to give frame #%ld to the client\r\n", _fx_, m_dwFrameCount++));
|
||
|
||
LPVIDEOHDR lpVHdr;
|
||
|
||
lpVHdr = m_pWDMVideoBuff[m_dwNextToComplete].pVideoHdr;
|
||
|
||
if (lpVHdr)
|
||
{
|
||
lpVHdr->dwBytesUsed = m_pWDMVideoBuff[m_dwNextToComplete].SHGetImage.StreamHeader.DataUsed;
|
||
|
||
if ((m_pWDMVideoBuff[m_dwNextToComplete].SHGetImage.FrameInfo.dwFrameFlags & 0x00f0) == KS_VIDEO_FLAG_I_FRAME)
|
||
lpVHdr->dwFlags |= VHDR_KEYFRAME;
|
||
}
|
||
|
||
// Mark the buffer as done - signal the app
|
||
BufferDone(lpVHdr);
|
||
|
||
// Queue a new read
|
||
QueueRead(m_dwNextToComplete);
|
||
}
|
||
|
||
m_dwNextToComplete++;
|
||
m_dwNextToComplete %= m_cntNumVidBuf;
|
||
}
|
||
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: End of the streaming thread\r\n", _fx_));
|
||
|
||
ExitThread(0);
|
||
}
|
||
|
||
|
||
/****************************************************************************
|
||
* @doc INTERNAL CWDMSTREAMERMETHOD
|
||
*
|
||
* @mfunc BOOL | CWDMStreamer | QueueRead | This function queues a read
|
||
* operation on a video streaming pin.
|
||
*
|
||
* @parm DWORD | dwIndex | Index of the video structure in read buffer.
|
||
*
|
||
* @rdesc Returns TRUE if successful, or FALSE otherwise.
|
||
***************************************************************************/
|
||
BOOL CWDMStreamer::QueueRead(DWORD dwIndex)
|
||
{
|
||
FX_ENTRY("CWDMStreamer::QueueRead");
|
||
|
||
DWORD cbReturned;
|
||
BOOL bShouldBlock = FALSE;
|
||
|
||
DEBUGMSG(ZONE_STREAMING, ("\r\n%s: Queue read buffer %d on pin handle 0x%08lX\r\n", _fx_, dwIndex, m_pWDMVideoPin->GetPinHandle()));
|
||
|
||
// Get a buffer from the queue of video buffers
|
||
m_pWDMVideoBuff[dwIndex].pVideoHdr = DeQueueHeader();
|
||
|
||
if (m_pWDMVideoBuff[dwIndex].pVideoHdr)
|
||
{
|
||
ZeroMemory(&m_pWDMVideoBuff[dwIndex].SHGetImage, sizeof(m_pWDMVideoBuff[dwIndex].SHGetImage));
|
||
m_pWDMVideoBuff[dwIndex].SHGetImage.StreamHeader.Size = sizeof (KS_HEADER_AND_INFO);
|
||
m_pWDMVideoBuff[dwIndex].SHGetImage.FrameInfo.ExtendedHeaderSize = sizeof (KS_FRAME_INFO);
|
||
m_pWDMVideoBuff[dwIndex].SHGetImage.StreamHeader.Data = m_pWDMVideoBuff[dwIndex].pVideoHdr->lpData;
|
||
m_pWDMVideoBuff[dwIndex].SHGetImage.StreamHeader.FrameExtent = m_pWDMVideoPin->GetFrameSize();
|
||
|
||
// Submit the read
|
||
BOOL bRet = DeviceIoControl(m_pWDMVideoPin->GetPinHandle(), IOCTL_KS_READ_STREAM, &m_pWDMVideoBuff[dwIndex].SHGetImage, sizeof(m_pWDMVideoBuff[dwIndex].SHGetImage), &m_pWDMVideoBuff[dwIndex].SHGetImage, sizeof(m_pWDMVideoBuff[dwIndex].SHGetImage), &cbReturned, &m_pWDMVideoBuff[dwIndex].Overlap);
|
||
|
||
if (!bRet)
|
||
{
|
||
DWORD dwErr = GetLastError();
|
||
switch(dwErr)
|
||
{
|
||
case ERROR_IO_PENDING:
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: An overlapped IO is going to take place\r\n", _fx_));
|
||
bShouldBlock = TRUE;
|
||
break;
|
||
|
||
// Something bad happened
|
||
default:
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: DeviceIoControl() failed badly dwErr=%d\r\n", _fx_, dwErr));
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Overlapped IO won't take place - no need to wait\r\n", _fx_));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: We won't queue the read - no buffer available\r\n", _fx_));
|
||
}
|
||
|
||
m_pWDMVideoBuff[dwIndex].fBlocking = bShouldBlock;
|
||
|
||
return bShouldBlock;
|
||
}
|
||
|
||
|
||
/****************************************************************************
|
||
* @doc INTERNAL CWDMSTREAMERMETHOD
|
||
*
|
||
* @mfunc BOOL | CWDMStreamer | ThreadStub | Thread stub.
|
||
***************************************************************************/
|
||
LPTHREAD_START_ROUTINE CWDMStreamer::ThreadStub(CWDMStreamer *pCWDMStreamer)
|
||
{
|
||
FX_ENTRY("CWDMStreamer::ThreadStub");
|
||
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: Thread stub called, starting streaming...\r\n", _fx_));
|
||
|
||
pCWDMStreamer->Stream();
|
||
|
||
DEBUGMSG(ZONE_STREAMING, ("%s: ...capture thread has stopped\r\n", _fx_));
|
||
|
||
return(0);
|
||
}
|
||
|
||
|