使用Directshow+Xvid采集并压缩avi视频

偶然的机会下被迫研究了下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可看到通道图如下

写的有点仓促,后面有时间再改进

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 32
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值