第一篇: DirectShow视频采集_会头痛的可达鸭的博客-CSDN博客
一、GraphBuilder
1、IFilterGraph2、IGraphBuilder、ICaptureGraphBuiler2
(1)、CLSID
IFilterGraph CLSID_FilterGraphIFilterGraph2 CLSID_CaptureGraphBuilderIGraphBuilder CLSID_CaptureGraphBuilderICaptureGraphBuiler2 CLSID_CaptureGraphBuilder2
(2)、IFilterGraph2还有一个IFilterGraph,IFilterGraph3.
继承关系:
IFilterGraph——>IGraphBuilder——>IFilterGraph2——>IFilterGraph3
(3)、如果仅仅只需要预览 USB Video, 可以只使用IFilterGraph2
通过
pFilterGraph2->AddSourceFilterForMoniker()
用Moniker指针将filter添加到graph,则不必创建IcaptureGraphBuiler2这个接口。
如果需要需要视频流,则需要使用IFilterGraph
ICaptureGraphBuilder2->SetFiltergraph(IFilterGraph)
(4)、ICaptureGraphBuiler2是一个专门用来进行视频,音频捕捉的增强型接口;如果要特别的编解码,用它就很方便
创建了IcaptureGraphBuiler2,仍然需要创建IGraphBuilder 因为在预览视频时处理播放,暂停,停止这些动作,需要用到IGraphBuilder,而这些功能IcaptureGraphBuiler2 是没有的,所以需要而这协调处理。 IcaptureGraphBuiler2创建后,要将IGraphBuilder的指针与他关联:
pCaptureGraphBuiler2->captureGraphBuiler2(pGraphBuilder).
2、绑定并获取基础过滤器
// 写法1: 将视频设备绑定到基础过滤器上
IBaseFilter* GetDeviceFilter(const char* deviceUniqueIdUTF8,char* productUniqueIdUTF8,uint32_t productUniqueIdUTF8Length)
{const int32_t deviceUniqueIdUTF8Length = (int32_t)strlen((char*)deviceUniqueIdUTF8); // UTF8 is also NULL terminatedif (deviceUniqueIdUTF8Length >= kVideoCaptureUniqueNameLength) {RTC_LOG(LS_INFO) << "Device name too long";return NULL;}
// enumerate all video capture devicesRELEASE_AND_CLEAR(_dsMonikerDevEnum);HRESULT hr = _dsDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&_dsMonikerDevEnum, 0);if (hr != NOERROR) {RTC_LOG(LS_INFO) << "Failed to enumerate CLSID_SystemDeviceEnum, error 0x"<< rtc::ToHex(hr) << ". No webcam exist?";return 0;}_dsMonikerDevEnum->Reset();ULONG cFetched;IMoniker* pM;
IBaseFilter* captureFilter = NULL;bool deviceFound = false;while (S_OK == _dsMonikerDevEnum->Next(1, &pM, &cFetched) && !deviceFound) {IPropertyBag* pBag;hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pBag);if (S_OK == hr) {// Find the description or friendly name.VARIANT varName;VariantInit(&varName);if (deviceUniqueIdUTF8Length > 0) {hr = pBag->Read(L"DevicePath", &varName, 0);if (FAILED(hr)) {hr = pBag->Read(L"Description", &varName, 0);if (FAILED(hr)) {hr = pBag->Read(L"FriendlyName", &varName, 0);}}if (SUCCEEDED(hr)) {char tempDevicePathUTF8[256];tempDevicePathUTF8[0] = 0;WideCharToMultiByte(CP_UTF8, 0, varName.bstrVal, -1,tempDevicePathUTF8, sizeof(tempDevicePathUTF8),NULL, NULL);if (strncmp(tempDevicePathUTF8, (const char*)deviceUniqueIdUTF8,deviceUniqueIdUTF8Length) == 0) {// We have found the requested devicedeviceFound = true;hr = pM->BindToObject(0, 0, IID_IBaseFilter, (void**)&captureFilter);if(FAILED(hr)){RTC_LOG(LS_ERROR) << "Failed to bind to the selected ""capture device " << hr;}
if (productUniqueIdUTF8 &&productUniqueIdUTF8Length > 0) // Get the device name{GetProductId(deviceUniqueIdUTF8, productUniqueIdUTF8,productUniqueIdUTF8Length);}}}}VariantClear(&varName);pBag->Release();}pM->Release();}return captureFilter;
}
//写法2:枚举设备并绑定设备
BOOL BindToVideoDev(int deviceId, IBaseFilter **pFilter)
{if (deviceId < 0){return FALSE;}CComPtr<ICreateDevEnum> pCreateDevEnum;HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,IID_ICreateDevEnum, (void**)&pCreateDevEnum);if (hr != NOERROR){//ERR_DEBUG("Instance DeviceEnum Failed");return FALSE;}CComPtr<IEnumMoniker> pEm;hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pEm, 0);if (hr != NOERROR){//ERR_DEBUG("Enum VideoInputDeviceCategory Failed");return FALSE;}pEm->Reset();ULONG cFetched;IMoniker *pM=NULL;
int index = 0;while((( pEm->Next(1, &pM, &cFetched))==S_OK)&&( index <= deviceId)){IPropertyBag *pBag=NULL;if (pM==NULL){break;}hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);if(pBag!=NULL) {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;
}
// 添加到过滤器
// AddFilter调用过滤器的IBaseFilter::JoinFilterGraph方法来通知过滤器它已被添加
// AddFilter必须在尝试使用IGraphBuilder::Connect、IFilterGraph::ConnectDirect或IGraphBuilder::Render
// 方法连接或渲染属于添加的过滤器的引脚之前调用
IFilterGraph->AddFilter(IBaseFilter, "name");
// 获取InputPin
IPin* GetInputPin(IBaseFilter* filter)
{IPin* pin = NULL;IEnumPins* pPinEnum = NULL;filter->EnumPins(&pPinEnum);if (pPinEnum == NULL) {return NULL;}// get first unconnected pinpPinEnum->Reset(); // set to first pin
while (S_OK == pPinEnum->Next(1, &pin, NULL)) {PIN_DIRECTION pPinDir;pin->QueryDirection(&pPinDir);if (PINDIR_INPUT == pPinDir) // This is an input pin{IPin* tempPin = NULL;if (S_OK != pin->ConnectedTo(&tempPin)) // The pint is not connected{pPinEnum->Release();return pin;}}pin->Release();}pPinEnum->Release();return NULL;
}
3、设置采集的分辨率
HRESULT SetVideoSize(int nPreview,CString strRGBBytes,int nFrameRate,int iWidth , int iHeight)
{HRESULT hr=E_FAIL;if(m_pCaptureGraphBulid==NULL)return hr;
IAMStreamConfig *pAMStreamConfig=NULL;if(nPreview==0){hr = m_pCaptureGraphBulid->FindInterface(&PIN_CATEGORY_PREVIEW,&MEDIATYPE_Video,m_pBaseFilter,IID_IAMStreamConfig,(void **)&pAMStreamConfig);}else{hr = m_pCaptureGraphBulid->FindInterface(&PIN_CATEGORY_CAPTURE,&MEDIATYPE_Video,m_pBaseFilter,IID_IAMStreamConfig,(void **)&pAMStreamConfig);}
if(FAILED( hr )){SAFE_RELEASE(pAMStreamConfig);return hr;}//得到视频格式大小AM_MEDIA_TYPE *pmt;pAMStreamConfig->GetFormat(&pmt);
//设置视频格式pmt->majortype = MEDIATYPE_Video;pmt->subtype = GetMediaTypeGuid(strRGBBytes);
VIDEOINFOHEADER *pvih = reinterpret_cast<VIDEOINFOHEADER *>(pmt->pbFormat);//设置回去int nDefualWidth = pvih->bmiHeader.biWidth;int nDefualHeight = pvih->bmiHeader.biHeight;
pvih->bmiHeader.biWidth = iWidth; pvih->bmiHeader.biHeight = iHeight;pvih->bmiHeader.biSizeImage = pmt->lSampleSize = iWidth*iHeight*pvih->bmiHeader.biPlanes*pvih->bmiHeader.biBitCount/8;pvih->AvgTimePerFrame = (LONGLONG)(10000000/nFrameRate);
hr = pAMStreamConfig->SetFormat(pmt);if(FAILED(hr)){//如果设置失败可以选用默认的,但运用之后,小屏幕初始化时会出现闪动的情况 pvih->bmiHeader.biWidth = nDefualWidth; pvih->bmiHeader.biHeight = nDefualHeight;pvih->bmiHeader.biSizeImage = pmt->lSampleSize = nDefualWidth*nDefualHeight*pvih->bmiHeader.biPlanes*pvih->bmiHeader.biBitCount/8;hr = pAMStreamConfig->SetFormat(pmt);if(FAILED(hr)){SAFE_RELEASE(pAMStreamConfig);FreeMediaType(*pmt);//ERR_DEBUG("初始化设置视频格式失败");return hr;}}SAFE_RELEASE(pAMStreamConfig);FreeMediaType(*pmt);//return hr;
}
4、流程
(1)、创建CLSID_FilterGraph---- CoCreateInstance--- IFilterGraph
(2)、创建IID_IMediaControl --- IFilterGraph->QueryInterface --- IMediaControl
(3)、获取并连接:IBaseFilter---GetDeviceFilter---BindToObject
IFilterGraph->AddFilter(IBaseFilter, L"VideoCaptureFilter")
(4)、创建自定义的IBaseFilter(成员有IPin)---CaptureSinkFilter
IFilterGraph->AddFilter(CaptureSinkFilter)
(5)、获取InputPin,通过自定义的CaptureSinkFilter,并连接CaptureInputPin::ConnectedTo
(6)、连接OutputPin和InputPin(直接连接两个引脚, 无需中间滤波器)
IFilterGraph->ConnectDirect(OutputPin, InputPin, NULL)
三、VideoCaptureDS
1、初始化
// 基类: VideoCaptureImpl
VideoCaptureDS :public VideoCaptureImpl
// 初始化并且创建:
// IBaseFilter DirectShow主要过滤器
// IGraphBuilder DirectShow图形生成器
// IMediaControl DirectShow媒体控制器
// CaptureSinkFilter
// IPin
VideoCaptureDS::Init
2、CaptureSinkFilter
// 基类
// 创建 CaptureInputPin
CaptureSinkFilter : public IBaseFilter
3、CaptureInputPin
// Input Pin--->camera input
// 负责绑定: receive_pin 和 媒体类型
// 接受视频数据发送给observer
class CaptureInputPin : public IMemInputPin, public IPin
4、DeviceInfoDS
// 主要提供设备能力、获取设备信息、获取Filter
DeviceInfoDS : public DeviceInfoImpl