偶然的機會下被迫研究了下directshow方面的知識,做點小總結。
在開始要寫視訊采集程式的時候,先試了VFW,後來發現不太好用,采集效果不太好,而且你支援WDM驅動的裝置,是以改用了directshow。
所需工具
1:DirectX 9.0b SDK
2:Xvid(我用的是1.3版本的)
具體介紹
先在頭檔案中申明directshow要用到的對象
.h
IGraphBuilder *m_pGB; //建立管理器
ICaptureGraphBuilder2* m_pCapture; //建立采集管理器
IBaseFilter* m_pBF; //建立源視訊源
IMediaControl* m_pMC; //建立多媒體管理器
IVideoWindow* m_pVW; //建立視訊視窗
ISampleGrabber* m_pGrabber; //建立截圖接口,采集過程中捕獲圖像
int m_IMonikerNum;
IMoniker *rgpmVideo[10];
IMoniker *pMoniker; //建立螢幕
添加所有方法
public:
IPin* FindPin(IBaseFilter *pFilter, PIN_DIRECTION dir); //查找引腳
void FreeMediaType(AM_MEDIA_TYPE& mt); //釋放媒體類型
bool BindFilter(int deviceId, IBaseFilter **pFilter); //綁定filter
void STOP(); //停止采集
void MakeEncoder(); //綁定Xvid壓縮編碼
void CloseInterface(); //關閉所有接口
HRESULT Init(int iDeviceID,HWND hWnd); //根據裝置編号初始化程式
int EnumDevices(HWND hList); //周遊連接配接PC上的所有采集裝置
HRESULT CaptureImagesToAVI(CString inFileName); //捕獲儲存視訊
public:
void Run(); //開始采集
void Pause(); //暫停
HRESULT SetupVideoWindow(); //重設采集視窗
.cpp
HRESULT CCaptureVideo::InitCaptureGraphBuilder() //初始化通道
{
m_pGB = NULL;
HRESULT hr;
// 建立IGraphBuilder接口
hr=CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&m_pGB);
// 建立ICaptureGraphBuilder2接口
hr = CoCreateInstance (CLSID_CaptureGraphBuilder2,NULL, CLSCTX_INPROC,
IID_ICaptureGraphBuilder2, (void **) &m_pCapture);
if (FAILED(hr))
return hr;
m_pCapture->SetFiltergraph(m_pGB);
hr = m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC); //建立媒體接口
if (FAILED(hr))return hr;
hr = m_pGB->QueryInterface(IID_IVideoWindow, (LPVOID *) &m_pVW); //建立視訊接口
if (FAILED(hr))return hr;
return hr;
}
/*設定捕獲視訊的檔案,開始捕捉視訊資料寫檔案*/
HRESULT CCaptureVideo::CaptureImagesToAVI(CString inFileName)
{
HRESULT hr;
if(m_pMC)
{
m_pMC-> Stop();
}
//先停止視訊//設定檔案名,注意第二個參數的類型
hr = m_pCapture->SetOutputFileName( &MEDIASUBTYPE_Avi,inFileName.AllocSysString(), &pMux, NULL );
//渲染媒體,連結所有濾波器】
m_pCapture->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,m_pBF,pCompress,pMux);
pMux->Release();
m_pMC->Run();//回複視訊
return hr;
}
void CCaptureVideo::MakeEncoder()
{
//枚舉壓縮器
HRESULT hr;
ICreateDevEnum *pSysDevEnum = NULL; //建立枚舉器指針
pCompress = NULL;
IEnumMoniker *pEnumCat = NULL;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
IID_ICreateDevEnum, (void **)&pSysDevEnum); //建立枚舉器
hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoCompressorCategory, &pEnumCat, 0); //傳回枚舉參數
if (hr == S_OK)
{
// Enumerate the monikers.
IMoniker *pMoniker;
ULONG cFetched;
while(pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) //開始枚舉壓縮器
{
IPropertyBag *pPropBag;
pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
// To retrieve the friendly name of the filter, do the following:
VARIANT varName;
VariantInit(&varName);
hr = pPropBag->Read(L"FriendlyName", &varName, 0);
if (SUCCEEDED(hr))
{
CString str(varName.bstrVal);
SysFreeString(varName.bstrVal);
if(str == "Xvid MPEG-4 Codec") //這裡找到Xvid壓縮器後綁定
{
hr = pMoniker->BindToObject(0,0,IID_IBaseFilter,(void**)&pCompress);
pMoniker->Release();
break;
}
}
VariantClear(&varName);
}
pEnumCat->Release();
pMoniker->Release();
m_pGB->AddFilter(pCompress,L"Compressor"); //添加過濾器
HRESULT ret =NULL;
IPin * pSourceOut = NULL;
IPin* pCompressIn,* pCompressOut=NULL;
pCompressIn = FindPin(pCompress,PINDIR_INPUT) ;
pCompressOut = FindPin(pCompress,PINDIR_OUTPUT);
}
pSysDevEnum->Release();
}
int CCaptureVideo::EnumDevices(HWND hList)
{
if (!hList)
return -1;
int id = 0;
//枚舉視訊撲捉裝置
ICreateDevEnum *pCreateDevEnum;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,IID_ICreateDevEnum, (void**)&pCreateDevEnum);
if (hr != NOERROR)return -1;
CComPtr<IEnumMoniker> pEm;
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pEm, 0);
if (hr != NOERROR)return -1;
pEm->Reset();
ULONG cFetched;
IMoniker *pM;
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
{
IPropertyBag *pBag;
hr = pM->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)
{
TCHAR str[2048];
id++;
WideCharToMultiByte(CP_ACP,0,var.bstrVal, -1, str, 2048, NULL, NULL);
::SendMessage(hList, CB_ADDSTRING, 0,(LPARAM)str);
SysFreeString(var.bstrVal);
}
pBag->Release();
}
pM->Release();
}
return id;
}
bool CCaptureVideo::BindFilter(int deviceId, IBaseFilter **pFilter)
{
if (deviceId < 0)
return false;
// enumerate all video capture devices
CComPtr<ICreateDevEnum> pCreateDevEnum;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void**)&pCreateDevEnum);
if (hr != NOERROR)
{
return false;
}
CComPtr<IEnumMoniker> pEm;
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pEm, 0);
if (hr != NOERROR)
{
return false;
}
pEm->Reset();
ULONG cFetched;
IMoniker *pM;
int index = 0;
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK, index <= deviceId)
{
IPropertyBag *pBag;
hr = pM->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)
{
if (index ==deviceId)
{
pM->BindToObject(0, 0, IID_IBaseFilter, (void**)pFilter);
}
SysFreeString(var.bstrVal);
}
pBag->Release();
}
pM->Release();
index++;
}
return true;
}
HRESULT CCaptureVideo::Init(int iDeviceID, HWND hWnd)
{
HRESULT hr;
hr = InitCaptureGraphBuilder();
if (FAILED(hr))
{
AfxMessageBox("Failed to get video interfaces!");
return hr;
}
// if(MakeEncoder())
// {
// hr = m_pGB->AddFilter(pEncoderFilter, L"EncodeFilter");
// }
// Bind Device Filter. We know the device because the id was passed in
if(!BindFilter(iDeviceID, &m_pBF))return S_FALSE;
hr = m_pGB->AddFilter(m_pBF, L"Capture Filter");
// hr = m_pCapture->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,
// m_pBF, NULL, NULL);
// create a sample grabber
// hr = m_pGrabber.CoCreateInstance( CLSID_SampleGrabber );
hr = CoCreateInstance( CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_ISampleGrabber, (void**)&m_pGrabber );
if( !m_pGrabber )
{
AfxMessageBox("Fail to create SampleGrabber, maybe qedit.dll is not registered?");
return hr;
}
CComQIPtr< IBaseFilter, &IID_IBaseFilter > pGrabBase( m_pGrabber );
if( FAILED( hr ) )
{
AfxMessageBox("Fail to set media type!");
return hr;
}
hr = m_pGB->AddFilter( pGrabBase, L"ISample Grabber" );
// hr = m_pGB->AddFilter(pEncoderFilter,L"EncodeFilter");
//設定視訊格式
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
hr = m_pGrabber->SetMediaType(&mt);
hr = m_pGrabber->SetOneShot(FALSE);
hr = m_pGrabber->SetBufferSamples(TRUE);
if( FAILED( hr ) )
{
AfxMessageBox("Fail to put sample grabber in graph");
return hr;
}
// try to render preview/capture pin
hr = m_pCapture->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,m_pBF,pGrabBase,NULL);
if( FAILED( hr ) )
{
hr = m_pCapture->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,m_pBF,pGrabBase,NULL);
// hr = m_pCapture->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,m_pBF,pEncoderFilter,pMux);
}
if( FAILED( hr ) )
{
AfxMessageBox("沒有視訊信号,請連接配接攝像頭後再開啟軟體");
return hr;
}
hr = m_pGrabber->GetConnectedMediaType( &mt );
if ( FAILED( hr) )
{
AfxMessageBox("Failt to read the connected media type");
return hr;
}
m_pVW->put_MessageDrain((OAHWND)m_hWnd);
VIDEOINFOHEADER * vih = (VIDEOINFOHEADER*) mt.pbFormat;
mCB.lWidth = vih->bmiHeader.biWidth;
mCB.lHeight = vih->bmiHeader.biHeight;
FreeMediaType(mt);
// hr = m_pGrabber->SetBufferSamples( FALSE );
// hr = m_pGrabber->SetOneShot( FALSE );
// hr = m_pGrabber->SetCallback( &mCB, 1 );
//設定視訊捕捉視窗
m_hWnd = hWnd ;
SetupVideoWindow();
hr = m_pMC->Run();//開始視訊預覽
if(FAILED(hr))
{
AfxMessageBox("Couldn't run the graph!");
return hr;
}
return S_OK;
}
/*設定捕獲視訊的檔案,開始捕捉視訊資料寫檔案*/
HRESULT CCaptureVideo::CaptureImagesToAVI(CString inFileName)
{
HRESULT hr;
if(m_pMC)
{
m_pMC-> Stop();
}
//先停止視訊//設定檔案名,注意第二個參數的類型
hr = m_pCapture->SetOutputFileName( &MEDIASUBTYPE_Avi,inFileName.AllocSysString(), &pMux, NULL );
//渲染媒體,連結所有濾波器
m_pCapture->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,m_pBF,pCompress,pMux);
pMux->Release();
m_pMC->Run();//回複視訊
return hr;
}
void CCaptureVideo::Pause()
{
m_pMC->Pause();
}
void CCaptureVideo::STOP()
{
m_pMC->Stop();
if(m_pCapture)
{
SAFE_RELEASE(m_pCapture);
m_pCapture = NULL;
}
if(m_pMC)
{
SAFE_RELEASE(m_pMC);
m_pMC = NULL;
}
if(m_pBF)
{
SAFE_RELEASE(m_pBF);
m_pBF = NULL;
}
if(m_pVW)
{
m_pVW->put_Visible(OAFALSE);
m_pVW->put_Owner(NULL);
}
if(m_pGrabber)
{
SAFE_RELEASE(m_pGrabber);
m_pGrabber = NULL;
}
if(m_pGB != NULL)
{
IEnumFilters * pEnum = NULL;
HRESULT hr = m_pGB->EnumFilters(&pEnum);
if (SUCCEEDED(hr))
{
IBaseFilter *pFilter = NULL;
while (S_OK == pEnum->Next(1, &pFilter, NULL))
{
m_pGB->RemoveFilter(pFilter);
pEnum->Reset();
// SAFE_RELEASE(pFilter);
pFilter->Release();
}
}
SAFE_RELEASE(pEnum);
}
if(m_pGB)
{
SAFE_RELEASE(m_pGB);
m_pGB = NULL;
}
}
void CCaptureVideo::Run()
{
m_pMC->Run();
}
運作graphedt可看到通道圖如下
寫的有點倉促,後面有時間再改進