📄 amcap.cpp
字号:
case WM_PAINT:
hdc = BeginPaint(hwnd,&ps);
// nothing to do
EndPaint(hwnd,&ps);
break;
case WM_TIMER:
// update our status bar with #captured, #dropped
// if we've stopped capturing, don't do it anymore. Some WM_TIMER
// messages may come late, after we've destroyed the graph and
// we'll get invalid numbers.
if(gcap.fCapturing)
UpdateStatus(FALSE);
// is our time limit up?
if(gcap.fUseTimeLimit)
{
if((timeGetTime() - gcap.lCapStartTime) / 1000 >=
gcap.dwTimeLimit)
{
StopCapture();
if(gcap.fWantPreview)
{
BuildPreviewGraph();
StartPreview();
}
}
}
break;
case WM_SIZE:
// make the preview window fit inside our window, taking up
// all of our client area except for the status window at the
// bottom
GetClientRect(ghwndApp, &rc);
cxBorder = GetSystemMetrics(SM_CXBORDER);
cyBorder = GetSystemMetrics(SM_CYBORDER);
cy = statusGetHeight() + cyBorder;
MoveWindow(ghwndStatus, -cxBorder, rc.bottom - cy,
rc.right + (2 * cxBorder), cy + cyBorder, TRUE);
rc.bottom -= cy;
// this is the video renderer window showing the preview
if(gcap.pVW)
gcap.pVW->SetWindowPosition(0, 0, rc.right, rc.bottom);
break;
case WM_FGNOTIFY:
// uh-oh, something went wrong while capturing - the filtergraph
// will send us events like EC_COMPLETE, EC_USERABORT and the one
// we care about, EC_ERRORABORT.
if(gcap.pME)
{
LONG event;
LONG_PTR l1, l2;
HRESULT hrAbort = S_OK;
BOOL bAbort = FALSE;
while(gcap.pME->GetEvent(&event, &l1, &l2, 0) == S_OK)
{
gcap.pME->FreeEventParams(event, l1, l2);
if(event == EC_ERRORABORT)
{
StopCapture();
bAbort = TRUE;
hrAbort = static_cast<HRESULT>(l1);
continue;
}
else if(event == EC_DEVICE_LOST)
{
// Check if we have lost a capture filter being used.
// lParam2 of EC_DEVICE_LOST event == 1 indicates device added
// == 0 indicates device removed
if(l2 == 0)
{
IBaseFilter *pf;
IUnknown *punk = (IUnknown *) l1;
if(S_OK == punk->QueryInterface(IID_IBaseFilter, (void **) &pf))
{
if(::IsEqualObject(gcap.pVCap, pf))
{
pf->Release();
bAbort = FALSE;
StopCapture();
TCHAR szError[100];
HRESULT hr = StringCchCopy(szError, 100,
TEXT("Stopping Capture (Device Lost). Select New Capture Device\0"));
ErrMsg(szError);
break;
}
pf->Release();
}
}
}
} // end while
if(bAbort)
{
if(gcap.fWantPreview)
{
BuildPreviewGraph();
StartPreview();
}
TCHAR szError[100];
HRESULT hr = StringCchPrintf(szError, 100, TEXT("ERROR during capture, error code=%08x\0"), hrAbort);
ErrMsg(szError);
}
}
break;
case WM_DEVICECHANGE:
// We are interested in only device arrival & removal events
if(DBT_DEVICEARRIVAL != wParam && DBT_DEVICEREMOVECOMPLETE != wParam)
break;
PDEV_BROADCAST_HDR pdbh = (PDEV_BROADCAST_HDR) lParam;
if(pdbh->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
{
break;
}
PDEV_BROADCAST_DEVICEINTERFACE pdbi = (PDEV_BROADCAST_DEVICEINTERFACE) lParam;
// Check for capture devices.
if(pdbi->dbcc_classguid != AM_KSCATEGORY_CAPTURE)
{
break;
}
// Check for device arrival/removal.
if(DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam)
{
gcap.fDeviceMenuPopulated = false;
}
break;
}
return (LONG) DefWindowProc(hwnd,msg,wParam,lParam);
}
// Make a graph builder object we can use for capture graph building
//
BOOL MakeBuilder()
{
// we have one already
if(gcap.pBuilder)
return TRUE;
gcap.pBuilder = new ISampleCaptureGraphBuilder( );
if( NULL == gcap.pBuilder )
{
return FALSE;
}
return TRUE;
}
// Make a graph object we can use for capture graph building
//
BOOL MakeGraph()
{
// we have one already
if(gcap.pFg)
return TRUE;
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
IID_IGraphBuilder, (LPVOID *)&gcap.pFg);
return (hr == NOERROR) ? TRUE : FALSE;
}
// make sure the preview window inside our window is as big as the
// dimensions of captured video, or some capture cards won't show a preview.
// (Also, it helps people tell what size video they're capturing)
// We will resize our app's window big enough so that once the status bar
// is positioned at the bottom there will be enough room for the preview
// window to be w x h
//
int gnRecurse = 0;
void ResizeWindow(int w, int h)
{
RECT rcW, rcC;
int xExtra, yExtra;
int cyBorder = GetSystemMetrics(SM_CYBORDER);
gnRecurse++;
GetWindowRect(ghwndApp, &rcW);
GetClientRect(ghwndApp, &rcC);
xExtra = rcW.right - rcW.left - rcC.right;
yExtra = rcW.bottom - rcW.top - rcC.bottom + cyBorder + statusGetHeight();
rcC.right = w;
rcC.bottom = h;
SetWindowPos(ghwndApp, NULL, 0, 0, rcC.right + xExtra,
rcC.bottom + yExtra, SWP_NOZORDER | SWP_NOMOVE);
// we may need to recurse once. But more than that means the window cannot
// be made the size we want, trying will just stack fault.
//
if(gnRecurse == 1 && ((rcC.right + xExtra != rcW.right - rcW.left && w > GetSystemMetrics(SM_CXMIN)) ||
(rcC.bottom + yExtra != rcW.bottom - rcW.top)))
ResizeWindow(w,h);
gnRecurse--;
}
// Tear down everything downstream of a given filter
void NukeDownstream(IBaseFilter *pf)
{
IPin *pP=0, *pTo=0;
ULONG u;
IEnumPins *pins = NULL;
PIN_INFO pininfo;
if (!pf)
return;
HRESULT hr = pf->EnumPins(&pins);
pins->Reset();
while(hr == NOERROR)
{
hr = pins->Next(1, &pP, &u);
if(hr == S_OK && pP)
{
pP->ConnectedTo(&pTo);
if(pTo)
{
hr = pTo->QueryPinInfo(&pininfo);
if(hr == NOERROR)
{
if(pininfo.dir == PINDIR_INPUT)
{
NukeDownstream(pininfo.pFilter);
gcap.pFg->Disconnect(pTo);
gcap.pFg->Disconnect(pP);
gcap.pFg->RemoveFilter(pininfo.pFilter);
}
pininfo.pFilter->Release();
}
pTo->Release();
}
pP->Release();
}
}
if(pins)
pins->Release();
}
// Tear down everything downstream of the capture filters, so we can build
// a different capture graph. Notice that we never destroy the capture filters
// and WDM filters upstream of them, because then all the capture settings
// we've set would be lost.
//
void TearDownGraph()
{
SAFE_RELEASE(gcap.pSink);
SAFE_RELEASE(gcap.pConfigAviMux);
SAFE_RELEASE(gcap.pRender);
SAFE_RELEASE(gcap.pME);
SAFE_RELEASE(gcap.pDF);
if(gcap.pVW)
{
// stop drawing in our window, or we may get wierd repaint effects
gcap.pVW->put_Owner(NULL);
gcap.pVW->put_Visible(OAFALSE);
gcap.pVW->Release();
gcap.pVW = NULL;
}
// destroy the graph downstream of our capture filters
if(gcap.pVCap)
NukeDownstream(gcap.pVCap);
if(gcap.pACap)
NukeDownstream(gcap.pACap);
if(gcap.pVCap)
gcap.pBuilder->ReleaseFilters();
// potential debug output - what the graph looks like
// if (gcap.pFg) DumpGraph(gcap.pFg, 1);
#ifdef REGISTER_FILTERGRAPH
// Remove filter graph from the running object table
if(g_dwGraphRegister)
{
RemoveGraphFromRot(g_dwGraphRegister);
g_dwGraphRegister = 0;
}
#endif
gcap.fCaptureGraphBuilt = FALSE;
gcap.fPreviewGraphBuilt = FALSE;
gcap.fPreviewFaked = FALSE;
}
// create the capture filters of the graph. We need to keep them loaded from
// the beginning, so we can set parameters on them and have them remembered
//
BOOL InitCapFilters()
{
HRESULT hr=S_OK;
BOOL f;
gcap.fCCAvail = FALSE; // assume no closed captioning support
f = MakeBuilder();
if(!f)
{
ErrMsg(TEXT("Cannot instantiate graph builder"));
return FALSE;
}
//
// First, we need a Video Capture filter, and some interfaces
//
gcap.pVCap = NULL;
if(gcap.pmVideo != 0)
{
IPropertyBag *pBag;
gcap.wachFriendlyName[0] = 0;
hr = gcap.pmVideo->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
if(SUCCEEDED(hr))
{
VARIANT var;
var.vt = VT_BSTR;
hr = pBag->Read(L"FriendlyName", &var, NULL);
if(hr == NOERROR)
{
hr = StringCchCopyW(gcap.wachFriendlyName, sizeof(gcap.wachFriendlyName) / sizeof(gcap.wachFriendlyName[0]), var.bstrVal);
SysFreeString(var.bstrVal);
}
pBag->Release();
}
hr = gcap.pmVideo->BindToObject(0, 0, IID_IBaseFilter, (void**)&gcap.pVCap);
}
if(gcap.pVCap == NULL)
{
ErrMsg(TEXT("Error %x: Cannot create video capture filter"), hr);
goto InitCapFiltersFail;
}
//
// make a filtergraph, give it to the graph builder and put the video
// capture filter in the graph
//
f = MakeGraph();
if(!f)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -