天天看點

使用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可看到通道圖如下

使用Directshow+Xvid采集并壓縮avi視訊

寫的有點倉促,後面有時間再改進

繼續閱讀