Windows COM使用注冊表來注冊COM組件豪墅, director fitler遵從COM開發(fā)規(guī)范只恨,所以也需要注冊到注冊表里卖子。
Directorshow filter在注冊表里有兩種注冊布局:
- 大多數(shù)的Fitler不需要進(jìn)行filter類型的注冊,按如下布局注冊:
KEY_CLASSES_ROOT
CLSID
Filter CLSID
REG_SZ: (Default) = Friendly name
InprocServer32
REG_SZ: (Default) = File name of the DLL
REG_SZ: ThreadingModel = Both
- 為了讓 System Device Enumerator可以發(fā)現(xiàn)自己,virture camera filter需要將自己注冊到Category下刑峡, 布局如下:
HKEY_CLASSES_ROOT
CLSID
Category
Instance
Filter CLSID
REG_SZ: CLSID = Filter CLSID
REG_BINARY: FilterData = Filter information
REG_SZ: FriendlyName = Friendly name
帶有Category的值注冊在下面的路徑下:
HKEY_CLASSES_ROOT\CLSID{DA4E3DA0-D07D-11d0-BD50-00A0C911CE86}\Instance
OBS-vcam 的注冊:
Vivek‘s VCam的注冊:
注冊信息的聲明
directorshow使用三個(gè)結(jié)構(gòu)類來幫助組織filter, pin, meida type 需要注冊的信息玄柠。
-
AMOVIESETUP_MEDIATYPE 被用來注冊媒體類型诫舅。
以下是分別是OBS-VCAM和Vivek項(xiàng)目的媒體類型聲明:
const AMOVIESETUP_MEDIATYPE AMSMediaTypesVCam =
{
&MEDIATYPE_Video, //視頻媒體
&MEDIASUBTYPE_NULL //null 表示支持任意類型
};
const AMOVIESETUP_MEDIATYPE AMSMediaTypesV =
{
&MEDIATYPE_Video,
&MEDIASUBTYPE_YUY2 //obs-vcam只是YUY2格式
};
- AMOVIESETUP_PIN用來注冊Pin的相關(guān)信息:
const AMOVIESETUP_PIN AMSPinVCam=
{
L"Output", // Pin string name
FALSE, // Is it rendered
TRUE, // Is it an output
FALSE, // Can we have none
FALSE, // Can we have many
&CLSID_NULL, // Connects to filter Obsolete
NULL, // Connects to pin
1, // Number of types
&AMSMediaTypesVCam // Pin Media types
};
要注意的是羽利, AMOVIESETUP_PIN有對mediatype的引用。
-
AMOVIESETUP_FILTER 用來注冊filter相關(guān)信息:
const AMOVIESETUP_FILTER AMSFilterVCam =
{
&CLSID_VirtualCam, // Filter CLSID
L"Virtual Cam", // String name
MERIT_DO_NOT_USE, // Filter merit
1, // Number pins
&AMSPinVCam // Pin details
};
AMOVIESETUP_FILTER包含了對outPin的引用刊懈,參考o(jì)bs-vcam可以看到这弧,當(dāng)需要多個(gè)虛擬攝像頭時(shí)虚汛,需要為每一個(gè)注冊一個(gè)獨(dú)立的fitler, 如果有音頻,音頻要獨(dú)立聲明filter.
將注冊聲明加入factory Template
為了方便com開發(fā)卷哩,Directoryshow引入的fatory Temple 幫助實(shí)現(xiàn)COM要求:
如上圖 , directshow實(shí)現(xiàn)的COM接口調(diào)用邏輯如下:
a. client 程序調(diào)用coGetclassObject 獲取ClassFacotory.
b. coGetclassObject 調(diào)用DllgetClassObject函數(shù)冷溶,DllgetClassObject在factory template數(shù)組里搜尋滿足CLSID條件的模板尊浓。
C. factory template 創(chuàng)建classfatory并讓其擁有一個(gè)執(zhí)行匹配模板的指針逞频,將他返回給client.
D. Client 調(diào)用IClassFatcory的createInstance方法
D. createInstance通過執(zhí)行模板的指針調(diào)用模板中的初始化函數(shù)眠砾,創(chuàng)建真實(shí)的實(shí)例托酸。
factory Template被聲明為全局?jǐn)?shù)組,它的結(jié)構(gòu)如下:
CFactoryTemplate g_Templates[1] =
{
{
L"My Component", // Name
&CLSID_MyComponent, // CLSID
CMyComponent::CreateInstance, // Method to create an instance of MyComponent
NULL, // Initialization function
NULL // Set-up information (for filters)
}
};
每個(gè)模板包含5個(gè)字段励堡,對應(yīng)注冊的com組件名稱,CLSID, 創(chuàng)建方法刨疼, 額外的初始化方法,以及用于注冊表的注冊信息揩慕。
將注冊表需要的注冊變量填入最后一個(gè)字段,就可以用directshow提供的方法進(jìn)行注冊迎卤。
除了要生命一個(gè)全局模板數(shù)組外玷坠,同時(shí)還需要聲明一個(gè)全局變量指明模板數(shù)量劲藐。
下面時(shí)obs-vcam的類工廠模板和全局變量:
CFactoryTemplate g_Templates[NUM_VIDEO_FILTERS + 1] =
{
{
L"OBS-Camera",
&CLSID_OBS_VirtualV,
CreateInstance,
NULL,
&AMSFilterV
},
{
L"OBS-Camera2",
&CLSID_OBS_VirtualV2,
CreateInstance2,
NULL,
&AMSFilterV2
},
{
L"OBS-Camera3",
&CLSID_OBS_VirtualV3,
CreateInstance3,
NULL,
&AMSFilterV3
},
{
L"OBS-Camera4",
&CLSID_OBS_VirtualV4,
CreateInstance4,
NULL,
&AMSFilterV4
},
{
L"OBS-Audio",
&CLSID_OBS_VirtualA,
CVAudio::CreateInstance,
NULL,
&AMSFilterA
}
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
OBS-VCAM注冊了4個(gè)虛擬攝像頭組件樟凄,每個(gè)組件都有不同的CLSID,但是它們引用的是相同的組件聘芜,也就是每個(gè)cliid下的InprocServer32的內(nèi)容相同缝龄,這樣雖然有不同的CLIID但是會(huì)指向同一個(gè)組件。
實(shí)現(xiàn)注冊方法
DLL 規(guī)定了5個(gè)需要實(shí)現(xiàn)的函數(shù):
DllMain Dll的入口函數(shù)二拐,在Directshow中使用DllEntryPoint作為入口聲明。
DllGetClassObject: 創(chuàng)建COM對象工廠百新。
DllUnloadNow(): 查詢是否可以被卸載了。
DllRegisterServer: 為dll注冊注冊表信息仗哨。
DllUnregisterServer: 移除注冊的信息。
Directshow lib已經(jīng)實(shí)現(xiàn)了前三個(gè)铅辞,創(chuàng)建對象只需要根據(jù)模板定義好實(shí)例化方法就可以。 但是后兩個(gè)需要開發(fā)者來實(shí)現(xiàn)斟珊。
Directshow提供了 AMovieDllRegisterServer2 來幫助注冊信息。
Vivek 項(xiàng)目注冊方法的實(shí)現(xiàn):
STDAPI RegisterFilters( BOOL bRegister )
{
HRESULT hr = NOERROR;
WCHAR achFileName[MAX_PATH];
char achTemp[MAX_PATH];
ASSERT(g_hInst != 0);
if( 0 == GetModuleFileNameA(g_hInst, achTemp, sizeof(achTemp)))
return AmHresultFromWin32(GetLastError());
MultiByteToWideChar(CP_ACP, 0L, achTemp, lstrlenA(achTemp) + 1,
achFileName, NUMELMS(achFileName));
hr = CoInitialize(0);
if(bRegister)
{
hr = AMovieSetupRegisterServer(CLSID_VirtualCam, L"Virtual Cam", achFileName, L"Both", L"InprocServer32");
}
if( SUCCEEDED(hr) )
{
IFilterMapper2 *fm = 0;
hr = CreateComObject( CLSID_FilterMapper2, IID_IFilterMapper2, fm );
if( SUCCEEDED(hr) )
{
if(bRegister)
{
IMoniker *pMoniker = 0;
REGFILTER2 rf2;
rf2.dwVersion = 1;
rf2.dwMerit = MERIT_DO_NOT_USE;
rf2.cPins = 1;
rf2.rgPins = &AMSPinVCam;
hr = fm->RegisterFilter(CLSID_VirtualCam, L"Virtual Cam", &pMoniker, &CLSID_VideoInputDeviceCategory, NULL, &rf2);
}
else
{
hr = fm->UnregisterFilter(&CLSID_VideoInputDeviceCategory, 0, CLSID_VirtualCam);
}
}
// release interface
//
if(fm)
fm->Release();
}
if( SUCCEEDED(hr) && !bRegister )
hr = AMovieSetupUnregisterServer( CLSID_VirtualCam );
CoFreeUnusedLibraries(); //卸載所有不在需要的dll,例如注冊完后的IID_IFilterMapper2
CoUninitialize(); //在當(dāng)前線程關(guān)閉com服務(wù)旨椒,下載所有dll和釋放資源等堵漱。
return hr;
}
參數(shù)bRegister為ture時(shí)表示注冊,為false表示移除勤庐;
根據(jù)directshow文檔,使用 AMovieDllRegisterServer2注冊有很多的限制愉镰,對于vcam場景,由于需要支持硬件設(shè)備录择,需要注冊額外的兩個(gè)信息,medium 和pin category. medium定義了和已經(jīng)建通信的方法糊肠, pin category定義了pin的功能辨宠。
在上文货裹,AMovieSetupRegisterServer被用來注冊通用的filter信息,它被注冊在CLSID下:
然后使用IFilterMapper2::RegisterFilter方法將fitler注冊到CLSID_VideoInputDeviceCategory下赋兵。這里需要用的REGFILTERPINS2結(jié)構(gòu):
REGFILTER2 rf2FilterReg = {
1, // Version 1 (no pin mediums or pin category).
MERIT_NORMAL, // Merit.
1, // Number of pins.
&sudPins // Pointer to pin information.
};
這里dwVersion(第一個(gè)字段)為一搔预,表示使用AMOVIESETUP_PIN格式的的定義霹期。Merit(第二個(gè)字段拯田,表示加入Filter Graph Manager的順序)這里使用MERIT_DO_NOT_USE.
注冊結(jié)果如下:
在第二個(gè)注冊信息中,它包含了pin的定義船庇,媒體類型等信息,但是沒有包含路徑信息(包含在第一個(gè)中)鸭轮,所以第二條信息看起來只是給system_device_enumerates使用的。 這也是obs-vcam可以用一個(gè)lib模擬多個(gè)攝像頭的原因邑蒋。