偶然的机会下被迫研究了下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();
}
写的有点仓促,后面有时间再改进