Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

802 lines
22 KiB

// !!! Paint black in window when not running, please
// Copyright (c) 1994 - 1999 Microsoft Corporation. All Rights Reserved.
/*
Methods for CCapOverlay, CCapOverlayNotify
*/
#include <streams.h>
#include "driver.h"
CCapOverlay * CreateOverlayPin(CVfwCapture * pCapture, HRESULT * phr)
{
DbgLog((LOG_TRACE,2,TEXT("CCapOverlay::CreateOverlayPin(%08lX,%08lX)"),
pCapture, phr));
WCHAR wszPinName[16];
lstrcpyW(wszPinName, L"Preview");
CCapOverlay * pOverlay = new CCapOverlay(NAME("Video Overlay Stream"),
pCapture, phr, wszPinName);
if (!pOverlay)
*phr = E_OUTOFMEMORY;
// if initialization failed, delete the stream array
// and return the error
//
if (FAILED(*phr) && pOverlay)
delete pOverlay, pOverlay = NULL;
return pOverlay;
}
//#pragma warning(disable:4355)
// CCapOverlay constructor
//
CCapOverlay::CCapOverlay(TCHAR *pObjectName, CVfwCapture *pCapture,
HRESULT * phr, LPCWSTR pName)
:
CBaseOutputPin(pObjectName, pCapture, &pCapture->m_lock, phr, pName),
m_OverlayNotify(NAME("Overlay notification interface"), pCapture, NULL, phr),
m_pCap(pCapture),
m_fRunning(FALSE)
#ifdef OVERLAY_SC
,m_hThread(NULL),
m_tid(0),
m_dwAdvise(0),
m_rtStart(0),
m_rtEnd(0),
m_fHaveThread(FALSE)
#endif
{
DbgLog((LOG_TRACE,1,TEXT("CCapOverlay constructor")));
ASSERT(pCapture);
}
CCapOverlay::~CCapOverlay()
{
DbgLog((LOG_TRACE,1,TEXT("*Destroying the Overlay pin")));
};
// Say if we're prepared to connect to a given input pin from
// this output pin
//
STDMETHODIMP CCapOverlay::Connect(IPin *pReceivePin,
const AM_MEDIA_TYPE *pmt)
{
DbgLog((LOG_TRACE,3,TEXT("CCapOverlay::Connect")));
/* Call the base class to make sure the directions match! */
HRESULT hr = CBaseOutputPin::Connect(pReceivePin,pmt);
if (FAILED(hr)) {
return hr;
}
/* We're happy if we can get an IOverlay interface */
hr = pReceivePin->QueryInterface(IID_IOverlay,
(void **)&m_pOverlay);
// we were promised this would work
ASSERT(SUCCEEDED(hr));
/* Because we're not going to get called again - except to
propose a media type - we set up a callback here.
There's only one overlay pin so we don't need any context.
*/
hr = m_pOverlay->Advise(&m_OverlayNotify,
ADVISE_CLIPPING | ADVISE_POSITION);
/*
We don't need to hold on to the IOverlay pointer
because BreakConnect will be called before the receiving
pin goes away.
*/
if (FAILED(hr)) {
// !!! Shouldn't happen, but this isn't quite right
Disconnect();
pReceivePin->Disconnect();
return hr;
} else {
m_bAdvise = TRUE;
}
return hr;
}
STDMETHODIMP CCapOverlay::NonDelegatingQueryInterface(REFIID riid, void ** ppv)
{
DbgLog((LOG_TRACE,99,TEXT("CCapOverlay::NonDelegatingQueryInterface")));
if (ppv)
*ppv = NULL;
/* Do we have this interface */
if (riid == IID_IKsPropertySet) {
return GetInterface((LPUNKNOWN) (IKsPropertySet *) this, ppv);
#ifdef OVERLAY_SC
} else if (riid == IID_IAMStreamControl) {
return GetInterface((LPUNKNOWN) (IAMStreamControl *) this, ppv);
#endif
} else {
return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv);
}
}
#ifdef OVERLAY_SC
// overidden because we aren't an IMemInputPin... we have no delivering
// to do to notice when to start and stop. We need a thread. Ick. Fun.
STDMETHODIMP CCapOverlay::StopAt(const REFERENCE_TIME * ptStop, BOOL bBlockData, DWORD dwCookie)
{
REFERENCE_TIME rt;
CAutoLock cObjectLock(m_pCap->m_pLock);
// we must be connected and running
if (!IsConnected() || m_pCap->m_State != State_Running)
return E_UNEXPECTED;
// we are stopped!
if (!m_fRunning)
return NOERROR;
// Stop now. That's easy enough
if (ptStop == NULL) {
ActivePause();
return CBaseStreamControl::StopAt(ptStop, bBlockData, dwCookie);
}
// can't do this without a clock
if (m_pCap->m_pClock == NULL)
return E_FAIL;
// cancel the stop
if (*ptStop == MAX_TIME) {
if (m_rtEnd > 0) {
m_rtEnd = 0;
if (m_dwAdvise) {
m_pCap->m_pClock->Unadvise(m_dwAdvise);
m_EventAdvise.Set();
}
}
return CBaseStreamControl::StopAt(ptStop, bBlockData, dwCookie);
}
m_pCap->m_pClock->GetTime(&rt);
// Stop in the past. That's easy enough. Stop now.
if (*ptStop <= rt) {
ActivePause();
return CBaseStreamControl::StopAt(ptStop, bBlockData, dwCookie);
}
// stop in the future. That's tricky. We need a thread to notice
// "when's later, Daddy?"
m_rtEnd = *ptStop; // DO THIS BEFORE m_fHaveThread test or thread
// could die after we think it's staying around
m_dwCookieStop = dwCookie;
// we need a new thread
if (m_fHaveThread == FALSE) {
// we made one before that we haven't closed
if (m_hThread) {
WaitForSingleObject(m_hThread, INFINITE);
CloseHandle(m_hThread);
m_hThread = NULL;
m_tid = 0;
}
m_EventAdvise.Reset();
m_fHaveThread = TRUE;
m_hThread = CreateThread(NULL, 0, CCapOverlay::ThreadProcInit, this,
0, &m_tid);
if (!m_hThread) {
DbgLog((LOG_ERROR,1,TEXT("Can't create Overlay thread")));
return E_OUTOFMEMORY;
}
}
return CBaseStreamControl::StopAt(ptStop, bBlockData, dwCookie);
}
STDMETHODIMP CCapOverlay::StartAt(const REFERENCE_TIME * ptStart, DWORD dwCookie)
{
REFERENCE_TIME rt;
CAutoLock cObjectLock(m_pCap->m_pLock);
// we must be connected and running
if (!IsConnected() || m_pCap->m_State != State_Running)
return E_UNEXPECTED;
// we are running!
if (m_fRunning)
return NOERROR;
// Start now. That's easy enough
if (ptStart == NULL) {
ActiveRun(0);
return CBaseStreamControl::StartAt(ptStart, dwCookie);
}
// can't do this without a clock
if (m_pCap->m_pClock == NULL)
return E_FAIL;
// cancel the start
if (*ptStart == MAX_TIME) {
if (m_rtStart > 0) {
m_rtStart = 0;
if (m_dwAdvise) {
m_pCap->m_pClock->Unadvise(m_dwAdvise);
m_EventAdvise.Set();
}
}
return CBaseStreamControl::StartAt(ptStart, dwCookie);
}
m_pCap->m_pClock->GetTime(&rt);
// Start in the past. That's easy enough. Start now.
if (*ptStart <= rt) {
ActiveRun(0);
return CBaseStreamControl::StartAt(ptStart, dwCookie);
}
// start in the future. That's tricky. We need a thread to notice
// "when's later, Daddy?"
m_rtStart = *ptStart;// DO THIS BEFORE m_fHaveThread test or thread
// could die after we think it's staying around
m_dwCookieStart = dwCookie;
// we need a new thread
if (m_fHaveThread == FALSE) {
// we made one before that we haven't closed
if (m_hThread) {
WaitForSingleObject(m_hThread, INFINITE);
CloseHandle(m_hThread);
m_hThread = NULL;
m_tid = 0;
}
m_EventAdvise.Reset();
m_fHaveThread = TRUE;
m_hThread = CreateThread(NULL, 0, CCapOverlay::ThreadProcInit, this,
0, &m_tid);
if (!m_hThread) {
DbgLog((LOG_ERROR,1,TEXT("Can't create Overlay thread")));
return E_OUTOFMEMORY;
}
}
return CBaseStreamControl::StartAt(ptStart, dwCookie);
}
#endif // OVERLAY_SC
// !!! The base classes change all the time and I won't pick up their bug fixes!
//
HRESULT CCapOverlay::BreakConnect()
{
DbgLog((LOG_TRACE,3,TEXT("CCapOverlay::BreakConnect")));
if (m_pOverlay != NULL) {
if (m_bAdvise) {
m_pOverlay->Unadvise();
m_bAdvise = FALSE;
}
m_pOverlay->Release();
m_pOverlay = NULL;
}
#if 0
// we've broken our connection, so next time we reconnect don't allow
// repainting until we've actually drawn something in the first place
m_pFilter->m_fOKToRepaint = FALSE;
#endif
return CBaseOutputPin::BreakConnect();
}
// Override this because we don't want any allocator!
//
HRESULT CCapOverlay::DecideAllocator(IMemInputPin * pPin,
IMemAllocator ** pAlloc) {
/* We just don't want one so everything's OK as it is */
return S_OK;
}
HRESULT CCapOverlay::GetMediaType(int iPosition, CMediaType *pmt)
{
DbgLog((LOG_TRACE,3,TEXT("CCapOverlay::GetMediaType #%d"), iPosition));
if (pmt == NULL) {
DbgLog((LOG_TRACE,3,TEXT("NULL format, no can do")));
return E_INVALIDARG;
}
if (iPosition < 0) {
return E_INVALIDARG;
}
if (iPosition > 0) {
return VFW_S_NO_MORE_ITEMS;
}
// We provide a media type of OVERLAY with an 8 bit format (silly
// renderer won't accept it if we don't set up an 8 bit format)
BYTE aFormat[sizeof(VIDEOINFOHEADER) + SIZE_PALETTE];
VIDEOINFOHEADER *pFormat = (VIDEOINFOHEADER *)aFormat;
ZeroMemory(pFormat, sizeof(VIDEOINFOHEADER) + SIZE_PALETTE);
pFormat->bmiHeader.biWidth =
m_pCap->m_pStream->m_user.pvi->bmiHeader.biWidth;
pFormat->bmiHeader.biHeight =
m_pCap->m_pStream->m_user.pvi->bmiHeader.biHeight;
// we don't work with funny rectangles. Sorry
#if 0
// I bet the renderer ignores these rectangles and I'll need to call
// IBasicVideo::put_Source* and ::put_Destination* instead
// The idea is to make OnClipChange's source and target match these numbers
pFormat->rcSource = m_pCap->m_pStream->m_user.pvi->rcSource;
pFormat->rcTarget = m_pCap->m_pStream->m_user.pvi->rcTarget;
#endif
pFormat->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pFormat->bmiHeader.biPlanes = 1;
pFormat->bmiHeader.biBitCount = 8;
pmt->SetFormat((PBYTE)pFormat, sizeof(VIDEOINFOHEADER) + SIZE_PALETTE);
pmt->SetFormatType(&FORMAT_VideoInfo);
if (pmt->pbFormat == NULL) {
return E_OUTOFMEMORY;
}
pmt->majortype = MEDIATYPE_Video;
pmt->subtype = MEDIASUBTYPE_Overlay;
pmt->bFixedSizeSamples = FALSE;
pmt->bTemporalCompression = FALSE;
pmt->lSampleSize = 0;
return NOERROR;
}
// We accept overlay connections only
//
HRESULT CCapOverlay::CheckMediaType(const CMediaType *pMediaType)
{
DbgLog((LOG_TRACE,3,TEXT("CCapOverlay::CheckMediaType")));
if (pMediaType->subtype == MEDIASUBTYPE_Overlay)
return NOERROR;
else
return E_FAIL;
}
// Don't insist on IMemInputPin
//
HRESULT CCapOverlay::CheckConnect(IPin *pPin)
{
// we don't connect to anyone who doesn't support IOverlay.
// after all, we're an overlay pin
HRESULT hr = pPin->QueryInterface(IID_IOverlay, (void **)&m_pOverlay);
if (FAILED(hr)) {
return E_NOINTERFACE;
} else {
m_pOverlay->Release();
m_pOverlay = NULL;
}
return CBasePin::CheckConnect(pPin);
}
HRESULT CCapOverlay::Active()
{
DbgLog((LOG_TRACE,2,TEXT("CCapOverlay Stop->Pause")));
videoStreamInit(m_pCap->m_pStream->m_cs.hVideoExtOut, 0, 0, 0, 0);
// don't let the base class Active() get called for non-IMemInput pins
return NOERROR;
}
HRESULT CCapOverlay::Inactive()
{
DbgLog((LOG_TRACE,2,TEXT("CCapOverlay Pause->Stop")));
// turn off overlay
videoStreamFini(m_pCap->m_pStream->m_cs.hVideoExtOut);
#ifdef OVERLAY_SC
CAutoLock cObjectLock(m_pCap->m_pLock);
// kill our thread
m_rtStart = 0;
m_rtEnd = 0;
if (m_pCap->m_pClock && m_dwAdvise) {
m_pCap->m_pClock->Unadvise(m_dwAdvise);
m_EventAdvise.Set();
}
// we haven't properly shut down our thread yet
if (m_hThread) {
WaitForSingleObject(m_hThread, INFINITE);
CloseHandle(m_hThread);
m_tid = 0;
m_hThread = NULL;
}
#endif
return NOERROR;
}
HRESULT CCapOverlay::ActiveRun(REFERENCE_TIME tStart)
{
DbgLog((LOG_TRACE,2,TEXT("CCapOverlay Pause->Run")));
ASSERT(m_pCap->m_pOverlayPin->IsConnected());
m_fRunning = TRUE;
HVIDEO hVideoExtOut = m_pCap->m_pStream->m_cs.hVideoExtOut;
if (hVideoExtOut == NULL || m_pOverlay == NULL)
return NOERROR;
HWND hwnd;
HDC hdc;
m_pOverlay->GetWindowHandle(&hwnd);
if (hwnd)
hdc = GetDC(hwnd);
if (hwnd == NULL || hdc == NULL)
return NOERROR;
RECT rcSrc, rcDst;
rcSrc.left = 0; rcSrc.top = 0;
rcSrc.right = HEADER(m_mt.Format())->biWidth;
rcSrc.bottom = HEADER(m_mt.Format())->biHeight;
GetClientRect (hwnd, &rcDst);
ClientToScreen(hwnd, (LPPOINT)&rcDst);
ClientToScreen(hwnd, (LPPOINT)&rcDst + 1);
DbgLog((LOG_TRACE,2,TEXT("Starting overlay (%d,%d) to (%d,%d)"),
rcSrc.right, rcSrc.bottom, rcDst.right - rcDst.left,
rcDst.bottom - rcDst.top));
// turn overlay on
vidxSetRect(m_pCap->m_pStream->m_cs.hVideoExtOut, DVM_SRC_RECT,
rcSrc.left, rcSrc.top, rcSrc.right, rcSrc.bottom);
vidxSetRect(m_pCap->m_pStream->m_cs.hVideoExtOut, DVM_DST_RECT,
rcDst.left, rcDst.top, rcDst.right, rcDst.bottom);
// INIT now done in PAUSE
videoUpdate(m_pCap->m_pStream->m_cs.hVideoExtOut, hwnd, hdc);
ReleaseDC(hwnd, hdc);
return NOERROR;
}
HRESULT CCapOverlay::ActivePause()
{
DbgLog((LOG_TRACE,2,TEXT("CCapOverlay Run->Pause")));
DbgLog((LOG_TRACE,2,TEXT("Turning OVERLAY off")));
m_fRunning = FALSE;
return NOERROR;
}
#if 0
// Return the IOverlay interface we are using (AddRef'd)
//
IOverlay *CCapOverlay::GetOverlayInterface()
{
if (m_pOverlay) {
m_pOverlay->AddRef();
}
return m_pOverlay;
}
#endif
#ifdef OVERLAY_SC
DWORD WINAPI CCapOverlay::ThreadProcInit(void *pv)
{
CCapOverlay *pThis = (CCapOverlay *)pv;
return pThis->ThreadProc();
}
DWORD CCapOverlay::ThreadProc()
{
DbgLog((LOG_TRACE,2,TEXT("Starting CCapOverlay ThreadProc")));
REFERENCE_TIME rt;
HRESULT hr;
// protect from other people dicking with m_rtStart and m_rtEnd
m_pCap->m_pLock->Lock();
while (m_rtStart > 0 || m_rtEnd > 0) {
rt = m_rtStart;
if (m_rtEnd < rt)
rt = m_rtEnd;
hr = m_pCap->m_pClock->AdviseTime(
// this was the reference time when our stream started playing
(REFERENCE_TIME) m_pCap->m_tStart,
// this is the offset from our start time when we want to
// wake up.
(REFERENCE_TIME) rt,
(HEVENT)(HANDLE) m_EventAdvise, // event to fire
&m_dwAdvise); // Advise cookie
m_pCap->m_pLock->Unlock();
if (SUCCEEDED(hr)) {
m_EventAdvise.Wait();
} else {
DbgLog((LOG_TRACE,1,TEXT("AdviseTime ERROR, doing it now")));
}
m_pCap->m_pLock->Lock();
m_dwAdvise = 0;
m_pCap->m_pClock->GetTime(&rt);
if (m_rtStart < rt) {
m_rtStart = 0;
ActiveRun(0);
}
if (m_rtEnd < rt) {
m_rtEnd = 0;
ActivePause();
}
}
DbgLog((LOG_TRACE,2,TEXT("CCapOverlay ThreadProc is dead")));
// somebody needs to kill me officially later
m_fHaveThread = FALSE;
m_pCap->m_pLock->Unlock();
return 0;
}
#endif // OVERLAY_SC
//=========================================================================//
//*** I N T E R M I S S I O N ***//
//=========================================================================//
/*
IOverlayNotify
*/
CCapOverlayNotify::CCapOverlayNotify(TCHAR * pName,
CVfwCapture * pFilter,
LPUNKNOWN pUnk,
HRESULT * phr) :
CUnknown(pName, pUnk)
{
DbgLog((LOG_TRACE,1,TEXT("*Instantiating CCapOverlayNotify")));
m_pFilter = pFilter;
}
CCapOverlayNotify::~CCapOverlayNotify()
{
DbgLog((LOG_TRACE,1,TEXT("*Destroying CCapOverlayNotify")));
}
STDMETHODIMP CCapOverlayNotify::NonDelegatingQueryInterface(REFIID riid,
void ** ppv)
{
DbgLog((LOG_TRACE,99,TEXT("CCapOverlayNotify::QueryInterface")));
if (ppv)
*ppv = NULL;
/* Do we have this interface */
if (riid == IID_IOverlayNotify) {
return GetInterface((LPUNKNOWN) (IOverlayNotify *) this, ppv);
} else {
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
}
}
STDMETHODIMP_(ULONG) CCapOverlayNotify::NonDelegatingRelease()
{
return m_pFilter->Release();
}
STDMETHODIMP_(ULONG) CCapOverlayNotify::NonDelegatingAddRef()
{
return m_pFilter->AddRef();
}
STDMETHODIMP CCapOverlayNotify::OnColorKeyChange(
const COLORKEY *pColorKey) // Defines new colour key
{
DbgLog((LOG_TRACE,3,TEXT("CCapOverlayNotify::OnColorKeyChange")));
// We expect the hardware to handle colour key stuff, so I'm really
// hoping that the renderer will never draw the colour key itself.
return NOERROR;
}
// The calls to OnClipChange happen in sync with the window. So it's called
// with an empty clip list before the window moves to freeze the video, and
// then when the window has stabilised it is called again with the new clip
// list. The OnPositionChange callback is for overlay cards that don't want
// the expense of synchronous clipping updates and just want to know when
// the source or destination video positions change. They will NOT be called
// in sync with the window but at some point after the window has changed
// (basicly in time with WM_SIZE etc messages received). This is therefore
// suitable for overlay cards that don't inlay their data to the framebuffer
STDMETHODIMP CCapOverlayNotify::OnClipChange(
const RECT * pSourceRect, // Area of source video to use
const RECT * pDestinationRect, // screen co-ords of window
const RGNDATA * pRegionData) // Header describing clipping
{
if (!m_pFilter->m_pOverlayPin)
return NOERROR;
if (!m_pFilter->m_pOverlayPin->IsConnected())
return NOERROR;
if (IsRectEmpty(pSourceRect) && IsRectEmpty(pDestinationRect))
return NOERROR;
HWND hwnd = NULL;
HDC hdc;
if (m_pFilter->m_pOverlayPin->m_pOverlay)
m_pFilter->m_pOverlayPin->m_pOverlay->GetWindowHandle(&hwnd);
if (hwnd == NULL || !IsWindowVisible(hwnd))
return NOERROR;
if (hwnd)
hdc = GetDC(hwnd);
if (hdc == NULL)
return NOERROR;
DbgLog((LOG_TRACE,3,TEXT("OnClip/PositionChange (%d,%d) (%d,%d)"),
pSourceRect->right - pSourceRect->left,
pSourceRect->bottom - pSourceRect->top,
pDestinationRect->right - pDestinationRect->left,
pDestinationRect->bottom - pDestinationRect->top));
// It's up to us to keep garbage out of the window by painting it if
// we're not running, and the hardware has nothing to draw
if (!m_pFilter->m_pOverlayPin->m_fRunning) {
RECT rcC;
GetClientRect(hwnd, &rcC);
HBRUSH hbrOld = (HBRUSH)SelectObject(hdc, GetStockObject(BLACK_BRUSH));
PatBlt(hdc, 0, 0, rcC.right, rcC.bottom, PATCOPY);
SelectObject(hdc, hbrOld);
ReleaseDC(hwnd, hdc);
return NOERROR;
}
vidxSetRect(m_pFilter->m_pStream->m_cs.hVideoExtOut, DVM_SRC_RECT,
pSourceRect->left, pSourceRect->top,
pSourceRect->right, pSourceRect->bottom);
vidxSetRect(m_pFilter->m_pStream->m_cs.hVideoExtOut, DVM_DST_RECT,
pDestinationRect->left, pDestinationRect->top,
pDestinationRect->right, pDestinationRect->bottom);
videoStreamInit(m_pFilter->m_pStream->m_cs.hVideoExtOut, 0, 0, 0, 0);
videoUpdate(m_pFilter->m_pStream->m_cs.hVideoExtOut, hwnd, hdc);
ReleaseDC(hwnd, hdc);
return NOERROR;
}
STDMETHODIMP CCapOverlayNotify::OnPaletteChange(
DWORD dwColors, // Number of colours present
const PALETTEENTRY *pPalette) // Array of palette colours
{
DbgLog((LOG_TRACE,3,TEXT("CCapOverlayNotify::OnPaletteChange")));
return NOERROR;
}
STDMETHODIMP CCapOverlayNotify::OnPositionChange(
const RECT *pSourceRect, // Area of video to play with
const RECT *pDestinationRect) // Area video goes
{
return OnClipChange(pSourceRect, pDestinationRect, NULL);
}
//
// PIN CATEGORIES - let the world know that we are a PREVIEW pin
//
HRESULT CCapOverlay::Set(REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData)
{
return E_NOTIMPL;
}
// To get a property, the caller allocates a buffer which the called
// function fills in. To determine necessary buffer size, call Get with
// pPropData=NULL and cbPropData=0.
HRESULT CCapOverlay::Get(REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData, DWORD *pcbReturned)
{
if (guidPropSet != AMPROPSETID_Pin)
return E_PROP_SET_UNSUPPORTED;
if (dwPropID != AMPROPERTY_PIN_CATEGORY)
return E_PROP_ID_UNSUPPORTED;
if (pPropData == NULL && pcbReturned == NULL)
return E_POINTER;
if (pcbReturned)
*pcbReturned = sizeof(GUID);
if (pPropData == NULL)
return S_OK;
if (cbPropData < sizeof(GUID))
return E_UNEXPECTED;
*(GUID *)pPropData = PIN_CATEGORY_PREVIEW;
return S_OK;
}
// QuerySupported must either return E_NOTIMPL or correctly indicate
// if getting or setting the property set and property is supported.
// S_OK indicates the property set and property ID combination is
HRESULT CCapOverlay::QuerySupported(REFGUID guidPropSet, DWORD dwPropID, DWORD *pTypeSupport)
{
if (guidPropSet != AMPROPSETID_Pin)
return E_PROP_SET_UNSUPPORTED;
if (dwPropID != AMPROPERTY_PIN_CATEGORY)
return E_PROP_ID_UNSUPPORTED;
if (pTypeSupport)
*pTypeSupport = KSPROPERTY_SUPPORT_GET;
return S_OK;
}