home *** CD-ROM | disk | FTP | other *** search
- //==========================================================================;
- //
- // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
- // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
- // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
- // PURPOSE.
- //
- // Copyright (c) 1992 - 1996 Microsoft Corporation. All Rights Reserved.
- //
- //--------------------------------------------------------------------------;
-
- //
- // Video capture stream source filter
- //
-
- // Uses the AVICap window to capture video data to pass downstream.
- // By using the video callback it avoids AVICap sending data to a file
- // - AVICap is merely capturing buffers for us to pass on.
- //
- // Uses a custom interface to allow the driver to be configured.
- // An app can use this to allow the user to configure their caapture session.
- // Configuration is only allowed when the filter is unconnected.
-
- // Caveats
- //
- // ** Should reject going active when the user has format dialogs up. Does this
- // need a CSource re-engineering?
-
- #include <streams.h>
-
- #include <initguid.h>
- #include <mmsystem.h>
- #include <vfw.h>
- #include <string.h>
- #include <stddef.h> // for offsetof macro
-
- #include "ivconfig.h"
- #include "vidcap.h"
-
- // setup data
-
- AMOVIESETUP_MEDIATYPE sudOpPinTypes = { &MEDIATYPE_Video // clsMajorType
- , &MEDIASUBTYPE_NULL }; // clsMinorType
-
- AMOVIESETUP_PIN sudOpPin = { L"Output" // strName
- , FALSE // bRendered
- , TRUE // bOutput
- , FALSE // bZero
- , FALSE // bMany
- , &CLSID_NULL // clsConnectsToFilter
- , NULL // strConnectsToPin
- , 1 // nMediaTypes
- , &sudOpPinTypes }; // lpMediaType
-
- AMOVIESETUP_FILTER sudVidCapax = { &CLSID_VidCap // clsID
- , L"Video Capture (AVICap)" // strName
- , MERIT_UNLIKELY // dwMerit
- , 1 // nPins
- , &sudOpPin }; // lpPin
-
- // COM global table of objects available in this dll
- CFactoryTemplate g_Templates[1] = {
-
- {L"Video Capture (AVICap)", &CLSID_VidCap, CVidCap::CreateInstance}
- };
- int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
-
-
- // exported entry points for registration and
- // unregistration (in this case they only call
- // through to default implmentations).
- //
- HRESULT DllRegisterServer()
- {
- return AMovieDllRegisterServer();
- }
-
- HRESULT DllUnregisterServer()
- {
- return AMovieDllUnregisterServer();
- }
-
-
- //
- // CVidCap::Constructor
- //
- // create an output pin for the first driver we find.
- // Name it after the driver we have found
- CVidCap::CVidCap(TCHAR *pName, LPUNKNOWN lpunk, HRESULT *phr)
- : CSource(pName, lpunk, CLSID_VidCap, phr) {
-
- CAutoLock l(&m_cStateLock);
-
-
- TCHAR szName[giDriverNameStrLen]; // AVICap recommends a giDriverNameStrLen
- // byte buffer (minimum)
-
- for (int i=0 ; i < 10 ; i++) { // have a look for a driver with indices 0-9
- // (see AVICap documentation)
- // need to provide some enumeration i/f (MRIDs?)
-
- if ( capGetDriverDescription( i
- , (LPTSTR) &szName
- , sizeof(szName)
- , NULL
- , 0
- )
- )
- {
-
- break; // we've found one!
- }
- }
-
- #ifndef UNICODE
- OLECHAR wszName[giDriverNameStrLen];
- MultiByteToWideChar(CP_ACP, 0, // Quartz i/f are always unicode,
- // so convert name to
- szName, -1, // unicode.
- wszName, giDriverNameStrLen);
- #else
- #define wszName szName
- #endif
-
- CSourceStream *ps = new CVidStream( NAME("Video capture stream"),
- phr, this, i, wszName);
- if (ps == NULL) {
- *phr = E_OUTOFMEMORY;
- return;
- }
-
- if (FAILED(*phr)) {
- delete ps;
- return;
- }
-
- DbgLog((LOG_TRACE, 1, TEXT("CVidCap created")));
- }
-
-
- //
- // CVidCap::Destructor
- //
- CVidCap::~CVidCap(void) {
- DbgLog((LOG_TRACE, 1, TEXT("CVidCap destroyed")) );
- }
-
-
- //
- // CreateInstance
- //
- // Called by CoCreateInstance to create a vidcap filter.
- CUnknown *CVidCap::CreateInstance(LPUNKNOWN lpunk, HRESULT *phr) {
-
- CUnknown *punk = new CVidCap(TEXT("Video capture filter"), lpunk, phr);
- if (punk == NULL) {
- *phr = E_OUTOFMEMORY;
- }
- return punk;
- }
-
-
- //
- // Run
- //
- // Activate the pin, letting it know that we are moving to
- // State_Running
- STDMETHODIMP CVidCap::Run(REFERENCE_TIME tStart) {
- CAutoLock l(pStateLock());
-
- HRESULT hr;
-
- m_tStart = tStart; // remember the stream time offset
-
- hr = CSource::Run(tStart);
-
- if (SUCCEEDED(hr)) {
- // start us running
- hr = ((CVidStream *)GetPin(0))->Run();
- }
-
- return S_OK;
- }
-
-
- //
- // Pause
- //
- // Activate the pin, letting it know that Paused will
- // be the next state
- STDMETHODIMP CVidCap::Pause(void) {
- CAutoLock l(pStateLock());
-
- if (m_State == State_Paused) {
- return S_OK;
- }
-
-
- CVidStream* pPin = (CVidStream*) GetPin(0);
-
- // for a live source, pausing means we send a poster frame
- // but nothing else. So if pausing from running, we first stop
- // and then start again. To stop in this pausing state we
- // need to flush the data
- if (m_State == State_Running) {
-
- pPin->DeliverBeginFlush();
- Stop();
- pPin->DeliverEndFlush();
- }
-
- // need to change state before sending the pause request
- // or the thread will not be created when we try to signal it.
- FILTER_STATE oldstate = m_State;
- HRESULT hr = CSource::Pause();
- if (FAILED(hr)) {
- return hr;
- }
-
-
- // we need to send some data (poster frame) when we start
- // pausing, but we don't send the real data until
- // we start running (as this is a live source, any cued data will
- // be late when we start running from paused
- return pPin->Pause();
- }
-
-
- //
- // Stop
- //
- // Pass the current state to the pins Inactive method
- STDMETHODIMP CVidCap::Stop(void) {
- CAutoLock l(pStateLock());
-
- HRESULT hr;
-
- hr = CSource::Stop();
-
- m_tStart = CRefTime(0L);
-
- return hr;
- }
-
- //
- // GetSetupData
- //
- LPAMOVIESETUP_FILTER CVidCap::GetSetupData()
- {
- return &sudVidCapax;
- }
-
-
- // *
- // * Implements CVidStream - manages the output pin
- // *
-
-
- long CVidStream::m_lObjectCount = 0; // The number of CVidStream objects.
- // Only 1 is allowed.
-
-
- //
- // CVidStream::Constructor
- //
- // keep the driver index to open.
- // We will open it when we go active, as we shouldn't keep resources
- // whilst inactive.
- CVidStream::CVidStream(TCHAR *pObjectName
- , HRESULT *phr
- , CVidCap *pParentFilter
- , unsigned int uiDriverIndex
- , LPCWSTR pPinName
- )
- :CSourceStream(pObjectName, phr, pParentFilter, pPinName),
- m_uiDriverIndex(uiDriverIndex),
- m_lCountActual(0),
- m_plFilled(NULL),
- m_hwCapCapturing(NULL),
- m_fFormatDirty(FALSE),
- m_dwMicroSecPerFrame(66667) // default to 15fps
- {
-
- CAutoLock lock(m_pFilter->pStateLock());
-
- // !!! Quote from docs:
- // > The variable pointed to by the lplVal parameter must be aligned
- // > on a 32-bit boundary;
- // > otherwise, this function will fail on multiprocessor x86 systems.
- // This is ensured by the compiler provided the structure packing level is
- // correct.
- InterlockedIncrement(&m_lObjectCount);
- if (m_lObjectCount > 1) { // A CVidStream already exists
- *phr = E_UNEXPECTED;
- MessageBox(NULL,
- TEXT("Only one copy of this filter can be created"),
- TEXT("Video Capture (AVICap)"),
- MB_ICONSTOP | MB_OK);
- DbgLog( (LOG_ERROR, 1, TEXT("Trying to create > 1 CVidStreams") ));
- return;
- }
-
- HWND hwndCap = CreateCaptureWindow(0);
- if (hwndCap == NULL) {
- *phr = E_FAIL;
- return;
- }
-
- #ifdef UNICODE
- capDriverGetName(hwndCap, m_szName, sizeof(m_szName));
- capDriverGetVersion(hwndCap, m_szVersion, sizeof(m_szVersion));
- #else
- char sz[giDriverNameStrLen];
- capDriverGetName(hwndCap, sz, sizeof(sz));
- MultiByteToWideChar(CP_ACP, 0,
- sz, -1,
- m_szName, giDriverNameStrLen);
-
- capDriverGetVersion(hwndCap, sz, sizeof(sz));
- MultiByteToWideChar(CP_ACP, 0,
- sz, -1,
- m_szVersion, giDriverVerStrLen);
- #endif
- // Establish what dialogs this driver can display.
-
- CAPDRIVERCAPS DriverCaps;
- capDriverGetCaps(hwndCap, &DriverCaps, sizeof(DriverCaps) );
- m_SupportsVideoSourceDialog = DriverCaps.fHasDlgVideoSource;
- m_SupportsVideoDisplayDialog = DriverCaps.fHasDlgVideoDisplay;
- m_SupportsVideoFormatDialog = DriverCaps.fHasDlgVideoFormat;
- m_SuppliesPalettes = DriverCaps.fDriverSuppliesPalettes;
-
- if (!DestroyCaptureWindow(hwndCap)) {
- *phr = E_FAIL;
- return;
- }
-
- DbgLog( (LOG_TRACE, 1, TEXT("CVidStream created") ) );
- }
-
-
- //
- // CVidStream::Destructor
- //
- // we should be inactive before this is called.
- CVidStream::~CVidStream(void) {
-
- CAutoLock lock(m_pFilter->pStateLock());
-
- ASSERT(!m_pFilter->IsActive());
-
- InterlockedDecrement(&m_lObjectCount);
-
- DbgLog( (LOG_TRACE, 1, TEXT("CVidStream destroyed") ) );
-
- }
-
-
- //
- // GetMediaType
- //
- // Queries the video driver and places an appropriate media type in *pmt
- HRESULT CVidStream::GetMediaType(CMediaType *pmt) {
-
- CAutoLock l(&m_cSharedState);
-
- HWND hwndCap;
-
- if (m_hwCapCapturing == NULL) { // No active window, so create one.
- hwndCap = CreateCaptureWindow(0);
- if (hwndCap == NULL) {
- return E_FAIL;
- }
- }
- else { // Use the currently active window
- hwndCap = m_hwCapCapturing;
- }
-
- pmt->SetType(&MEDIATYPE_Video);
- pmt->SetFormatType(&FORMAT_VideoInfo);
-
- DWORD dwFormatSize;
- VIDEOINFO *pvi;
-
- dwFormatSize = capGetVideoFormatSize(hwndCap);
-
- ASSERT(dwFormatSize > 0);
-
- // Find out how big we need to allocate the buffer
- #define AllocBufferSize (max(sizeof(VIDEOINFO), dwFormatSize+offsetof(VIDEOINFO,bmiHeader)))
-
- // Set up the format section of the mediatype to be the right size
- pvi = (VIDEOINFO *) pmt->AllocFormatBuffer(AllocBufferSize);
- #undef AllocBufferSize
- if (pvi == NULL) {
- return E_OUTOFMEMORY;
- }
-
- // make sure all fields are initially zero
- ZeroMemory((void *)pvi, sizeof(VIDEOINFO));
-
- // grab the BITMAPINFOHEADER straight in
- // will leave the memory after the last palette entry as zeros.
- capGetVideoFormat(hwndCap, &(pvi->bmiHeader), dwFormatSize);
-
- const GUID SubTypeGUID = GetBitmapSubtype(&pvi->bmiHeader);
- pmt->SetSubtype(&SubTypeGUID);
- pmt->SetSampleSize(GetSampleSize(&pvi->bmiHeader));
-
- if (m_hwCapCapturing == NULL) { // destroy the window we created
- DestroyCaptureWindow(hwndCap);
- }
-
- return NOERROR;
- }
-
-
- //
- // OnThreadCreate
- //
- // Start streaming & reset time samples are stamped with.
- HRESULT CVidStream::OnThreadCreate(void) {
-
- CAutoLock l(&m_cSharedState);
-
- m_ThreadState = Stopped;
-
- m_hwCapCapturing = CreateCaptureWindow(m_lCountActual);
- if (m_hwCapCapturing == NULL) {
- return E_FAIL;
- }
-
- CMediaType mt;
- GetMediaType(&mt); // get the media type the driver _currently_ supports
- if (mt != m_mt) { // Is it the same as the one we negotiated?
- return E_UNEXPECTED; // ...if not, bail out.
- }
-
- m_plFilled = new CVideoBufferList( m_mt.lSampleSize
- , (LONG)(m_dwMicroSecPerFrame / 1000)
- , (CVidCap *)m_pFilter
- );
- if (m_plFilled == NULL) {
- return E_OUTOFMEMORY;
- }
-
- return NOERROR;
- }
-
-
- //
- // OnThreadDestroy
- //
- // Free the list of completed buffers and stop streaming
- // Attempts to stop streaming and destroy the window, even in error
- // cases.
- HRESULT CVidStream::OnThreadDestroy(void) {
-
- CAutoLock l(&m_cSharedState);
-
- ASSERT(m_ThreadState == Stopped);
-
- BOOL fWindowGone = DestroyCaptureWindow(m_hwCapCapturing);
- m_hwCapCapturing = NULL;
-
- delete m_plFilled, m_plFilled = NULL;
-
- if (!fWindowGone) {
- return E_UNEXPECTED;
- }
- else {
- return NOERROR;
- }
- }
-
-
- //
- // DecideBufferSize
- //
- // Check the allocator can give us appropriately sized buffers
- // Always called after format negotiation.
- HRESULT CVidStream::DecideBufferSize(IMemAllocator *pAlloc,
- ALLOCATOR_PROPERTIES *pProperties)
- {
- CAutoLock lock(m_pFilter->pStateLock());
- CAutoLock l(&m_cSharedState);
- ASSERT(pAlloc);
- ASSERT(pProperties);
- HRESULT hr = NOERROR;
-
- pProperties->cBuffers = 5;
- pProperties->cbBuffer = m_mt.lSampleSize;
-
- ASSERT(pProperties->cbBuffer);
-
- // Ask the allocator to reserve us some sample memory, NOTE the function
- // can succeed (that is return NOERROR) but still not have allocated the
- // memory that we requested, so we must check we got whatever we wanted
-
- ALLOCATOR_PROPERTIES Actual;
- hr = pAlloc->SetProperties(pProperties,&Actual);
- if (!FAILED(hr)) {
- // Is this allocator unsuitable
-
- if (Actual.cbBuffer < pProperties->cbBuffer) {
- hr = E_FAIL;
- }
- }
- return hr;
- }
-
-
- //
- // GetSampleSize
- //
- // Given a BITMAPINFOHEADER, calculates the sample size needed.
- long CVidStream::GetSampleSize(LPBITMAPINFOHEADER pbmi) {
-
- long lSize;
-
- if (pbmi->biSizeImage > 0) {
-
- lSize = pbmi->biSizeImage;
-
- }
- else { // biSizeImage is allowed to be zero for uncompressed formats,
- // so do the maths ourselves...
-
- lSize = (pbmi->biWidth *
- pbmi->biHeight *
- pbmi->biBitCount) / 8 + 1;
- if (lSize < 0) { // biHeight was negative
- lSize *= -1;
- }
- }
-
- lSize += sizeof(DWORD) - (lSize % sizeof(DWORD)); // make size DWORD aligned.
-
- return lSize;
- }
-
-
- //
- // CreateCaptureWindow
- //
- // Create a hidden AVICap window, and make sure it is configured appropriately
- // Returns NULL on failure.
- // successful calls should be balanced with calls to DestroyCaptureWindow()
- // Creates the AVICap window with (up to) lBufferCount no. of buffers.
- // use lBufferCount = 0, when you only wish to interrgoate the the driver, or
- // specify a number of buffers, if you actually want to capture.
- HWND CVidStream::CreateCaptureWindow(long lBufferCount) {
-
- CAutoLock lock(&m_cSharedState);
-
- BOOL bErr; //return code of capXXX calls
-
- HWND hwndCapture; // The window to return
-
- hwndCapture = capCreateCaptureWindow(NULL, // No name
- 0, // no style.
- // defaults to invisible
- 0, 0, 150, 150, // an arbitrary size
- 0, // no parent
- 0); // don't care about the id
-
- if (!hwndCapture) {
- DbgLog((LOG_ERROR|LOG_TRACE, 1, TEXT("Window could not be created") ));
- return NULL;
- }
-
- bErr = capDriverConnect(hwndCapture, m_uiDriverIndex);
- if (!bErr) {
- DestroyWindow(hwndCapture);
- DbgLog((LOG_ERROR|LOG_TRACE, 1, TEXT("Driver failed to connect") ) );
- return NULL;
- }
- DbgLog((LOG_TRACE, 2, TEXT("Driver Connected") ));
-
- CAPTUREPARMS cp;
- capCaptureGetSetup(hwndCapture, &cp, sizeof(cp) ); // get the current defaults
-
- cp.dwRequestMicroSecPerFrame = m_dwMicroSecPerFrame; // Set desired frame rate
- cp.fMakeUserHitOKToCapture = FALSE;
- cp.fYield = TRUE; // we want capture on a
- // background thread.
- cp.wNumVideoRequested = (WORD) lBufferCount; // we may get less than
- // this - no problem
- cp.fCaptureAudio = FALSE;
- cp.vKeyAbort = 0; // If no key is provided,
- // it won't stop...
- cp.fAbortLeftMouse = FALSE;
- cp.fAbortRightMouse = FALSE;
- cp.fLimitEnabled = FALSE; // we want to stop
- cp.fMCIControl = FALSE;
-
- capCaptureSetSetup(hwndCapture, &cp, sizeof(cp) );
-
- capSetCallbackOnVideoStream(hwndCapture, &VideoCallback);
- capSetCallbackOnFrame(hwndCapture, &VideoCallback); // also use for single
- // frame capture
-
- #if 0
- CAPSTATUS cs;
- ZeroMemory(&cs, sizeof(cs));
- capGetStatus(hwndCapture, &cs, sizeof(cs));
-
- // try to see if the driver uses palettes
- if (((cs.hPalCurrent != NULL) || (cs.fUsingDefaultPalette))) {
- m_UsesPalettes = TRUE;
- } else {
- m_UsesPalettes = FALSE;
- }
- if (m_UsesPalettes && m_SuppliesPalettes) {
- capPaletteAuto(hwndCapture, 10, 236);
- }
- #endif
-
- SetWindowLong(hwndCapture, GWL_USERDATA, (LONG) this);
-
- return hwndCapture;
- }
-
-
- //
- // DestroyCaptureWindow()
- //
- // Disconnect the driver before destroying the window.
- BOOL CVidStream::DestroyCaptureWindow(HWND hwnd) {
-
- ASSERT(hwnd != NULL);
-
- BOOL bDriverDisconnected = capDriverDisconnect(hwnd);
- DbgLog(( LOG_ERROR|LOG_TRACE, 2
- , TEXT("Driver disconnect: %x"), bDriverDisconnected) );
-
- BOOL bWindowGone = DestroyWindow(hwnd);
- DbgLog((LOG_ERROR|LOG_TRACE, 2, TEXT("Window destroy: %x"), bWindowGone) );
-
- return (bDriverDisconnected && bWindowGone);
- }
-
-
- //
- // VideoCallback
- //
- // The AVICap Video callback. Keep a copy of the buffer we are given
- // May be called after the worker thread, or even the pin has gone away, depending
- // on AVICap's internal timing. Therefore be very careful with the pointers we use.
- LRESULT CALLBACK CVidStream::VideoCallback(HWND hwnd, LPVIDEOHDR lpVHdr) {
-
- if (m_lObjectCount == 0) {
- return (LRESULT) TRUE; // the video stream has gone away
- // so we can ignore this buffer.
- }
-
- ASSERT(m_lObjectCount == 1);
-
- CVidStream *pThis = (CVidStream *) GetWindowLong(hwnd, GWL_USERDATA);
-
- ASSERT(pThis);
-
- if (pThis->m_plFilled == NULL) { // The filled list has gone away.
- // ignore this buffer
- return (LRESULT) TRUE;
- }
- else {
- pThis->m_plFilled->Add(lpVHdr);
- }
-
- return (LRESULT) TRUE;
- }
-
-
-
- // Override to handle quality messages
- STDMETHODIMP CVidStream::Notify(IFilter * pSender, Quality q)
- {
- // if q.Late.RefTime.QuadPart >0 then skip ahead that much.
- // thereafter adjust the time per frame by a factor of
- // 1000/q.Proportion (watch for truncation of fractions
- // do the multiply first!
-
- // Not Yet Implemented :-)
-
- return NOERROR;
- }
-
-
-
- //
- // DoBufferProcessingLoop
- //
- // Replace the loop in CSourceStream with something of my own, so that I can
- // wait for buffers & commands.
- HRESULT CVidStream::DoBufferProcessingLoop(void) {
-
- HANDLE haWaitObjects[2];
- {
- CAutoLock l(&m_cSharedState);
-
- haWaitObjects[0] = GetRequestHandle(); // command handle first so that
- // it has priority over buffers
- haWaitObjects[1] = m_plFilled->GetWaitHandle();
- }
-
- for (;;) {
-
- // wait for commands or buffers
- DWORD dwWaitObject
- = WaitForMultipleObjects(2, haWaitObjects, FALSE, INFINITE);
-
- if (dwWaitObject == WAIT_OBJECT_0) { // thread command request
-
- Command com;
-
- EXECUTE_ASSERT(CheckRequest(&com));
- switch (com) {
- case CMD_RUN:
-
- com = GetRequest();
-
- if (m_ThreadState != Running) {
- capCaptureSequenceNoFile(m_hwCapCapturing);
- }
- m_ThreadState = Running;
- Reply(NOERROR);
- break;
-
- case CMD_PAUSE:
-
- com = GetRequest();
-
- switch (m_ThreadState) {
- case Stopped:
- capGrabFrame(m_hwCapCapturing);
- m_ThreadState = Paused; // mark that we have our poster
- break;
- case Running:
- // !!!! why is this commented out?
- // -- answer: see CVidCap::Pause() for what happens
- // when we pause from running and why (we stop
- // and repause).
- // capCaptureStop(m_hwCapCapturing);
- // break;
- default:
- // null op
- break;
- }
- m_ThreadState = Paused;
- Reply(NOERROR);
- break;
-
- case CMD_STOP:
-
- if (m_ThreadState == Running) {
- capCaptureStop(m_hwCapCapturing);
- }
- m_ThreadState = Stopped;
- DbgLog((LOG_TRACE, 1, TEXT("Seen Stop command")));
- // don't reply here as that is done by CSourceStream
- return NOERROR;
-
- default:
-
- ASSERT(!"Unexpected thread command");
- com = GetRequest();
- break;
- }
-
- }
- else if (dwWaitObject == (WAIT_OBJECT_0 + 1)) { // m_plFilled
-
- // Process the buffer we've just been signalled on
- IMediaSample *pSample;
-
- // get a buffer to put this video data in
- HRESULT hr = GetDeliveryBuffer(&pSample,NULL,NULL,0);
- if (FAILED(hr)) {
- continue;
- }
-
- FillBuffer(pSample);
-
- Deliver(pSample);
-
- pSample->Release();
-
- }
- else { // get out - otherwise we will infinite loop...
-
- DbgLog((LOG_TRACE, 1
- , TEXT("Unexpected buffer/command wait return: %d")
- , dwWaitObject));
- return E_UNEXPECTED;
- }
- }
-
- ASSERT(m_ThreadState == Stopped);
-
- return NOERROR;
- }
-
-
- //
- // FillBuffer
- //
- // Take the buffer from the head of the video buffer list.
- // We will only be called when there is such a buffer
- HRESULT CVidStream::FillBuffer(IMediaSample *pSample) {
-
- CAutoLock l(&m_cSharedState);
-
- HRESULT hr = m_plFilled->RemoveHeadIntoSample(pSample);
-
- if (SUCCEEDED(hr)) {
- if (m_fFormatDirty) { // we need to pass on a format change
- // ! In practice, since change is only allowed on
- // an inactive pin, this will be when we process
- // the first sample.
- CMediaType mt;
- GetMediaType(&mt);
- pSample->SetMediaType(&mt);
-
- m_fFormatDirty = FALSE;
- }
- }
-
- return hr;
- }
-
-
- // *
- // * IVideoCaptureConfigure implementation
- // *
-
-
- //
- // NonDelegatingQueryInterface
- //
- // Expose the IVideoCaptureConfigure interface, and pass up any
- // other requests.
- STDMETHODIMP CVidStream::NonDelegatingQueryInterface(REFIID riid, void ** ppv) {
-
- if (riid == IID_IVideoCaptureConfigure)
- return GetInterface( (IVideoCaptureConfigure *) this, ppv);
- else
- return CSourceStream::NonDelegatingQueryInterface(riid, ppv);
- }
-
-
- //
- // GetDriverName
- //
- // Place the driver name in DriverName. Allocate memory if required
- STDMETHODIMP CVidStream::get_DriverName(BSTR DriverName) {
-
- CAutoLock lock(m_pFilter->pStateLock());
-
- if (DriverName == NULL) { // we need to allocate the BSTR
-
- DriverName = SysAllocStringLen(m_szName, giDriverNameStrLen);
- if (DriverName == NULL)
- return E_OUTOFMEMORY;
-
- }
- else { // we have been given an allocated BSTR
- if (SysStringLen(DriverName) < giDriverNameStrLen) {
-
- if (!SysReAllocStringLen(&DriverName, m_szName, giDriverNameStrLen))
- return E_OUTOFMEMORY;
- }
- else { // length is OK
- lstrcpyW(DriverName, m_szName);
- }
- }
-
- return NOERROR;
- }
-
-
- //
- // GetDriverVersion
- //
- // Place the driver's version string in DriverVersion. Allocate memory if necessary
- STDMETHODIMP CVidStream::get_DriverVersion(BSTR DriverVersion) {
-
- CAutoLock lock(m_pFilter->pStateLock());
-
- if (DriverVersion == NULL) { // we need to allocate the BSTR
-
- DriverVersion = SysAllocStringLen(m_szVersion, giDriverVerStrLen);
- if (DriverVersion == NULL)
- return E_OUTOFMEMORY;
- }
- else { // we have been given an allocated BSTR
- if (SysStringLen(DriverVersion) < giDriverVerStrLen) {
-
- if (!SysReAllocStringLen( &DriverVersion
- , m_szVersion
- , giDriverVerStrLen
- )
- )
- return E_OUTOFMEMORY;
- }
- else { // length is OK
- lstrcpyW(DriverVersion, m_szVersion);
- }
- }
-
- return NOERROR;
- }
-
-
- //
- // get_SupportsVideoSourceDialog
- //
- STDMETHODIMP CVidStream::get_SupportsVideoSourceDialog(void) {
-
- CAutoLock lock(m_pFilter->pStateLock());
-
- if (SupportsVideoDialog(Source))
- return NOERROR;
- else
- return S_FALSE;
- }
-
-
- //
- // get_SupportsVideoCompressionDialog
- //
- STDMETHODIMP CVidStream::get_SupportsVideoCompressionDialog(void) {
-
- CAutoLock lock(m_pFilter->pStateLock());
-
- if (SupportsVideoDialog(Compression))
- return NOERROR;
- else
- return S_FALSE;
- }
-
-
- //
- // get_SupportsVideoFormatDialog
- //
- STDMETHODIMP CVidStream::get_SupportsVideoFormatDialog(void) {
-
- CAutoLock lock(m_pFilter->pStateLock());
-
- if (SupportsVideoDialog(Format))
- return NOERROR;
- else
- return S_FALSE;
- }
-
-
- //
- // get_SupportsVideoDisplayDialog
- //
- STDMETHODIMP CVidStream::get_SupportsVideoDisplayDialog(void) {
-
- CAutoLock lock(m_pFilter->pStateLock());
-
- if (SupportsVideoDialog(Display))
- return NOERROR;
- else
- return S_FALSE;
- }
-
-
- //
- // DisplayVideoDialog
- //
- // put the requested dialog on screen
- BOOL CVidStream::DisplayVideoDialog(HWND hwnd, DialogType Dialog) {
-
- CAutoLock lock(m_pFilter->pStateLock());
-
- switch (Dialog) {
- case Source:
- return capDlgVideoSource(hwnd);
- case Display:
- return capDlgVideoDisplay(hwnd);
- case Format:
- return capDlgVideoFormat(hwnd);
- case Compression:
- return capDlgVideoCompression(hwnd);
- default:
- return FALSE;
- }
- }
-
-
- //
- // SupportsVideoDialog
- //
- // Does the driver support this dialog?
- BOOL CVidStream::SupportsVideoDialog(DialogType Dialog) {
-
- CAutoLock lock(m_pFilter->pStateLock());
-
- switch (Dialog) {
- case Source:
- return m_SupportsVideoSourceDialog;
- case Display:
- return m_SupportsVideoDisplayDialog;
- case Format:
- return m_SupportsVideoFormatDialog;
- case Compression:
- return TRUE;
- default:
- return FALSE;
- }
- }
-
-
- //
- // DisplayDialog
- //
- // Display the dialog if: the driver supports it
- // and we are not active.
- HRESULT CVidStream::DisplayDialog(DialogType Dialog) {
-
- CAutoLock lock(m_pFilter->pStateLock());
- CAutoLock l(&m_cSharedState);
-
- if (!SupportsVideoDialog(Dialog)) // Can we display this dialog?
- return E_UNEXPECTED;
-
- if (m_hwCapCapturing == NULL) { // we are not currently active.
- HWND hwndCap = CreateCaptureWindow(0);
- if (hwndCap == NULL) {
- return E_FAIL;
- }
-
- BOOL fFormatOK = TRUE; // Has the user selected an acceptable format?
- do {
-
- if (!DisplayVideoDialog(hwndCap, Dialog)) {
- DestroyCaptureWindow(hwndCap);
- return E_FAIL;
- }
-
- if (IsConnected()) { // We need to re-negotiate the format
-
- CMediaType mt;
- GetMediaType(&mt);
-
- HRESULT hr = m_Connected->QueryAccept(&mt); // Acceptable to peer?
-
- if (hr == S_OK) { // our peer likes the format
- m_fFormatDirty = TRUE;
- fFormatOK = TRUE;
- }
- else if (hr == S_FALSE) { // unacceptable format
-
- // !!! Need .rc file for these...
- int iRet = MessageBox(NULL,
- TEXT("The format selected is not available, please select another."),
- TEXT("Format Unavailable"),
- MB_OK | MB_ICONEXCLAMATION);
- if (iRet == 0) {
- DestroyCaptureWindow(hwndCap);
- return E_OUTOFMEMORY;
- }
- if (iRet != IDOK) {
- DestroyCaptureWindow(hwndCap);
- return E_FAIL; // not out of memory, but something
- // else bizarre is going on...
- }
- fFormatOK = FALSE;
- }
- else { // an error occured...
- DestroyCaptureWindow(hwndCap);
- return E_FAIL;
- }
- }
- } while (!fFormatOK);
-
- DestroyCaptureWindow(hwndCap);
- return NOERROR;
- }
- else { // we are active
- return E_ACCESSDENIED;
- }
-
- }
-
-
- //
- // SetRequestedMicroSecondsPerFrame
- //
- // Set the number of microseconds per frame to MicroSecondsPerFrame.
- // returns: S_OK if successful
- // E_ACCESSDENIED if you can't change at the moment - if the pin is active
- // Use GetMicroSecondsPerFrame to find the actual rate set.
- STDMETHODIMP CVidStream::
- put_RequestedMicroSecondsPerFrame (long MicroSecondsPerFrame) {
-
- CAutoLock lock(m_pFilter->pStateLock());
- CAutoLock l(&m_cSharedState);
-
- if (m_hwCapCapturing != NULL)
- return E_ACCESSDENIED;
-
- m_dwMicroSecPerFrame = MicroSecondsPerFrame;
-
- return NOERROR;
-
- }
-
-
- //
- // GetMicroSecondsPerFrame
- //
- // returns the current number of microseconds per frame
- // in MicroSecondsperFrame.
- STDMETHODIMP CVidStream::
- get_RequestedMicroSecondsPerFrame (long *MicroSecondsPerFrame) {
-
- CAutoLock lock(m_pFilter->pStateLock());
- CAutoLock l(&m_cSharedState);
-
- *MicroSecondsPerFrame = m_dwMicroSecPerFrame;
- return NOERROR;
- }
-
-
- // *
- // * CVideoBufferList
- // *
-
-
- //
- // CVideoBufferList::Constructor
- //
- CVideoBufferList::CVideoBufferList( int iBufferSize
- , CRefTime rtMilliSecPerFrame
- , CVidCap *pFilter
- , int iBuffers
- )
- :m_rtMilliSecPerFrame(rtMilliSecPerFrame),
- m_uiFramesCaptured(0),
- m_uiFramesSkipped(0),
- m_uiFramesDelivered(0),
- m_FirstBuffer(TRUE),
- m_rtStartTime(0L),
- m_rtPrevEnd(0L),
- m_pFilter(pFilter),
- m_evList(TRUE),
- m_lFilled(NAME("Pending, full, buffers"),
- DEFAULTCACHE), // default cache
- m_lFree(NAME("Empty buffers"),
- DEFAULTCACHE) // default cache
- {
- for (int i=0; i < iBuffers; i++) {
-
- CBuffer *pBuffer = new CBuffer(iBufferSize);
- if (pBuffer == NULL) {
- return;
- }
- m_lFree.AddTail(pBuffer);
-
- }
- }
-
-
- //
- // CVideoBufferList::Destructor
- //
- CVideoBufferList::~CVideoBufferList() {
-
- while (m_lFree.GetCount() > 0) { // free buffers on the free list...
-
- CBuffer *pBuff = m_lFree.RemoveHead();
- delete pBuff;
- }
-
-
- DbgLog((LOG_TRACE, 1
- , TEXT("Filled frames not sent before stop issued: %d")
- , m_lFilled.GetCount()));
-
- while (m_lFilled.GetCount() > 0) { //... then free buffers on filled list
- // - we don't care about the data they hold.
-
- CBuffer *pBuff = m_lFilled.RemoveHead();
- delete pBuff;
- }
-
- DbgLog((LOG_TRACE, 1, TEXT("Frames Captured: %d"), m_uiFramesCaptured));
- DbgLog((LOG_TRACE, 1, TEXT("Frames Skipped: %d"), m_uiFramesSkipped));
- DbgLog((LOG_TRACE, 1, TEXT("Frames Delivered: %d"), m_uiFramesDelivered));
- }
-
-
- //
- // Add
- //
- // Add a video buffer to this list. gets a free buffer from m_lFree, copies
- // the video data into it and then puts it on m_lFilled.
- // if m_lFree is empty, fail silently, effectively skipping the buffer.
- HRESULT CVideoBufferList::Add(LPVIDEOHDR lpVHdr) {
-
- CAutoLock lck(&m_ListCrit);
-
- if (m_lFree.GetCount() > 0) {
-
- CBuffer *pBuff = m_lFree.RemoveHead();
- pBuff->CopyBuffer(lpVHdr);
- m_lFilled.AddTail(pBuff);
- if (m_lFilled.GetCount() == 1) {
- m_evList.Set();
- }
-
- m_uiFramesCaptured++;
- }
- else { // replace oldest filled frame...
-
- if (m_lFilled.GetCount() > 0) { // ...but only if there is one
- CBuffer *pBuff = m_lFilled.RemoveHead();
- pBuff->CopyBuffer(lpVHdr);
- m_lFilled.AddTail(pBuff);
- m_uiFramesCaptured++;
- }
-
- m_uiFramesSkipped++;
-
- }
-
- return NOERROR;
- }
-
-
- //
- // RemoveHeadIntoSample
- //
- // Copy the head of the filled list into the supplied IMediaSample.
- // Fail with E_UNEXPECTED if called on an empty m_lFilled;
- HRESULT CVideoBufferList::RemoveHeadIntoSample(IMediaSample *pSample) {
-
- HRESULT hr;
- CAutoLock lck(&m_ListCrit);
-
- if (m_lFilled.GetCount() < 1) {
- hr = E_UNEXPECTED;
- } else {
-
- CBuffer *pBuff = m_lFilled.RemoveHead();
- if (m_lFilled.GetCount() == 0) {
- m_evList.Reset();
- }
-
- BYTE *pSampleBuffer;
- hr = pSample->GetPointer(&pSampleBuffer);
- if (SUCCEEDED(hr)) {
-
- LONG lSampleSize = pSample->GetSize();
- ASSERT(pBuff->GetSize() <= pSample->GetSize());
-
- // Copy the captured data
- CopyMemory((void *)pSampleBuffer, pBuff->GetPointer(), lSampleSize);
- pSample->SetActualDataLength(lSampleSize);
-
- FILTER_STATE State;
- m_pFilter->GetState(0, &State);
-
- CRefTime rtStart;
- if (State == State_Paused) {
- // this is a poster frame
- rtStart = 0;
- } else if (m_FirstBuffer && (State == State_Running)) {
-
- m_pFilter->StreamTime(m_rtStartTime); // get the current stream time
- // to allow for our start up time
- rtStart = m_rtStartTime;
-
- m_FirstBuffer = FALSE;
-
- } else {
-
- rtStart = m_rtStartTime + pBuff->GetCaptureTime();
- }
-
- DbgLog((LOG_TIMING, 1, TEXT("Sample Time: %d"), rtStart.Millisecs()));
- CRefTime rtEnd = rtStart + CRefTime(m_rtMilliSecPerFrame);
- ASSERT(rtStart <= rtEnd);
-
- pSample->SetTime((REFERENCE_TIME*)&rtStart,
- (REFERENCE_TIME*)&rtEnd);
-
- m_rtPrevEnd = rtEnd;
- m_uiFramesDelivered++;
- m_lFree.AddTail(pBuff);
- }
- }
-
- return hr;
- }
-
-
- //
- // CBuffer::Constructor
- //
- //Get a new buffer of the maximum size we will handle
- CVideoBufferList::CBuffer::CBuffer(int iBufferSize) {
-
- m_pData = new BYTE[iBufferSize];
-
- // Set the two length fields to the maximum size
- m_iCaptureDataLength = m_iDataLength = iBufferSize;
-
- }
-
-
- //
- // CVideoDataBuffer::Destructor
- //
- CVideoBufferList::CBuffer::~CBuffer() {
-
- delete m_pData;
- }
-
-
- //
- // CopyBuffer
- //
- // Copy the supplied data in lpVHdr->lpData to this Buffer
- void CVideoBufferList::CBuffer::CopyBuffer(LPVIDEOHDR lpVHdr) {
-
- ASSERT((DWORD) m_iDataLength >= lpVHdr->dwBufferLength);
-
- // Copy the captured video buffer, and remember its length
- CopyMemory(m_pData, lpVHdr->lpData, (m_iCaptureDataLength = lpVHdr->dwBufferLength));
-
- m_rt = CRefTime((LONG)lpVHdr->dwTimeCaptured); // use constructor to initialise
- // with Millisec.
- }
-