DirectShow的前身是VFW (Video for Windows)。本來是微軟開發出來要與QuickTime對打的產品。在加入DirectX家族好幾年後,又被獨立出來,整合進Windows產品線內。其重要性可見一斑。
在開發時,它稱為Quartz;而在剛推出時,又取名為ActiveMovie。雖然有這一段小小的不起眼的因緣,持續的時間也不是很長,卻深深地影響了其SDK內某些東西的命名方式。初學者看到這些符號時,常常丈二金剛摸不著頭腦。
DirectShow最讓人頭痛的就是那個很囉嗦的COM架構。有相關經驗的人應該對CoCreateInstance、QueryInterface或Release之類的函式不陌生。在DirectShow的世界裡,物件並不是直接曝露在設計者面前的,取而代之的是僅提供部份功能的介面。要使用某個物件,得要先呼叫CoCreateInstance將它建立,抽取出第一個介面。要使用另一個面向的的功能的話,要嘛使用之前抽取出來的介面所提供的函數,間接地抽取相關的介面;要麻利用IUnknown::QueryInterface來查詢所需介面。最後,用不著的介面還得要用IUnknown::Release來釋放掉。這些東西使用起來並不直觀,程式碼寫起來也繁瑣。又因為使用了指標及獨立的堆積,根本不能使用C/C++的堆疊框架來幫助管理記憶體。導致大量的Release呼叫,作者辛苦是當然,對讀者來說也是很大的負擔。
所幸,有種東西叫做聰明指標(Smart Pointer)。藉由類別模板,可以很快地做出一類特別的類別,它們可以攜帶某種型別的指標,並且在摧毀時釋放掉其所指向的空間。只要將聰明指標宣告在堆疊框架內,就可以保證其指向的空間的生命週期絶不超出函數外。大大地減少記憶體管理的瑣碎程式碼。COM的介面全都以指標的形式提供給應用程式,自然也能用聰明指標來管理它。微軟所提供的ATL就有這樣的一種聰明指標CComPtr,它提供管理介面的機制,使用起來也十分地方便。在不知道有CComPtr之前,我一直是自己寫一套聰明指標來管理介面的。
以下是很簡單的使用方式:
bool CSecRecDlg::GetDev(const CLSID &cls)
{
CComboBox *ctrl=NULL;
CComPtr cdenum;
CComPtr emoniker;
CComPtr filter;
HRESULT hr=E_FAIL;
if(cls==CLSID_AudioInputDeviceCategory)
{
ctrl=(CComboBox *)GetDlgItem(IDC_AUDCAP);
m_aud.Release();
}
else if(cls==CLSID_VideoInputDeviceCategory)
{
ctrl=(CComboBox *)GetDlgItem(IDC_VIDCAP);
m_vid.Release();
}
else
ASSERT(FALSE);
if(ctrl->GetCount()<1)
return false;
hr=cdenum.CoCreateInstance(CLSID_SystemDeviceEnum);
{
CComboBox *ctrl=NULL;
CComPtr cdenum;
CComPtr emoniker;
CComPtr filter;
HRESULT hr=E_FAIL;
if(cls==CLSID_AudioInputDeviceCategory)
{
ctrl=(CComboBox *)GetDlgItem(IDC_AUDCAP);
m_aud.Release();
}
else if(cls==CLSID_VideoInputDeviceCategory)
{
ctrl=(CComboBox *)GetDlgItem(IDC_VIDCAP);
m_vid.Release();
}
else
ASSERT(FALSE);
if(ctrl->GetCount()<1)
return false;
hr=cdenum.CoCreateInstance(CLSID_SystemDeviceEnum);
ASSERT(SUCCEEDED(hr));
if(cdenum->CreateClassEnumerator(cls, &emoniker, 0)==S_OK)
{
CComPtr moniker;
int idx=ctrl->GetCurSel();
emoniker->Skip(idx);
if(emoniker->Next(1, &moniker, NULL)==S_OK)
{
hr=moniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void **)&filter);
if(cdenum->CreateClassEnumerator(cls, &emoniker, 0)==S_OK)
{
CComPtr moniker;
int idx=ctrl->GetCurSel();
emoniker->Skip(idx);
if(emoniker->Next(1, &moniker, NULL)==S_OK)
{
hr=moniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void **)&filter);
ASSERT(SUCCEEDED(hr));
cls==CLSID_AudioInputDeviceCategory?
m_aud=filter:
m_vid=filter;
return true;
}
}
return false;
}
cls==CLSID_AudioInputDeviceCategory?
m_aud=filter:
m_vid=filter;
return true;
}
}
return false;
}
在上面的程式碼裡不太有令人討厭的指標宣告,也沒有用到IUnknown::Release,整個程式看起來神清氣爽,真是可喜可賀。
以下是MSDN對於CComPtr的說明:
http://msdn.microsoft.com/en-us/library/ezzw7k98%28VS.80%29.aspx
沒有留言:
張貼留言