Directorshow Filter的注册

2021-10-25  本文已影响0人  Charles_linzc

Windows COM使用注册表来注册COM组件, director fitler遵从COM开发规范,所以也需要注册到注册表里。
Directorshow filter在注册表里有两种注册布局:

  1. 大多数的Fitler不需要进行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
  1. 为了让 System Device Enumerator可以发现自己,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 的注册:


image.png

Vivek‘s VCam的注册:


image.png

注册信息的声明
directorshow使用三个结构类来帮助组织filter, pin, meida type 需要注册的信息。

  1. AMOVIESETUP_MEDIATYPE 被用来注册媒体类型。
    以下是分别是OBS-VCAM和Vivek项目的媒体类型声明:
const AMOVIESETUP_MEDIATYPE AMSMediaTypesVCam = 
{ 
    &MEDIATYPE_Video,          //视频媒体
    &MEDIASUBTYPE_NULL   //null 表示支持任意类型
};
const AMOVIESETUP_MEDIATYPE AMSMediaTypesV =
{
    &MEDIATYPE_Video,
    &MEDIASUBTYPE_YUY2   //obs-vcam只是YUY2格式
};
  1. AMOVIESETUP_PIN用来注册Pin的相关信息:
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的引用。

  1. AMOVIESETUP_FILTER 用来注册filter相关信息:
    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的引用,参考obs-vcam可以看到,当需要多个虚拟摄像头时,需要为每一个注册一个独立的fitler, 如果有音频,音频要独立声明filter.

将注册声明加入factory Template
为了方便com开发,Directoryshow引入的fatory Temple 帮助实现COM要求:


image.png

如上图 , directshow实现的COM接口调用逻辑如下:
a. client 程序调用coGetclassObject 获取ClassFacotory.
b. coGetclassObject 调用DllgetClassObject函数,DllgetClassObject在factory template数组里搜寻满足CLSID条件的模板。
C. factory template 创建classfatory并让其拥有一个执行匹配模板的指针,将他返回给client.
D. Client 调用IClassFatcory的createInstance方法
D. createInstance通过执行模板的指针调用模板中的初始化函数,创建真实的实例。

factory Template被声明为全局数组,它的结构如下:
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)
}
};
每个模板包含5个字段,对应注册的com组件名称,CLSID, 创建方法, 额外的初始化方法,以及用于注册表的注册信息。
将注册表需要的注册变量填入最后一个字段,就可以用directshow提供的方法进行注册。
除了要生命一个全局模板数组外,同时还需要声明一个全局变量指明模板数量。
下面时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个虚拟摄像头组件,每个组件都有不同的CLSID,但是它们引用的是相同的组件,也就是每个cliid下的InprocServer32的内容相同,这样虽然有不同的CLIID但是会指向同一个组件。

实现注册方法
DLL 规定了5个需要实现的函数:
DllMain Dll的入口函数,在Directshow中使用DllEntryPoint作为入口声明。
DllGetClassObject: 创建COM对象工厂。
DllUnloadNow(): 查询是否可以被卸载了。
DllRegisterServer: 为dll注册注册表信息。
DllUnregisterServer: 移除注册的信息。
Directshow lib已经实现了前三个,创建对象只需要根据模板定义好实例化方法就可以。 但是后两个需要开发者来实现。
Directshow提供了 AMovieDllRegisterServer2 来帮助注册信息。
Vivek 项目注册方法的实现:

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(); //在当前线程关闭com服务,下载所有dll和释放资源等。
    return hr;
}

参数bRegister为ture时表示注册,为false表示移除;
根据directshow文档,使用 AMovieDllRegisterServer2注册有很多的限制,对于vcam场景,由于需要支持硬件设备,需要注册额外的两个信息,medium 和pin category. medium定义了和已经建通信的方法, pin category定义了pin的功能。
在上文,AMovieSetupRegisterServer被用来注册通用的filter信息,它被注册在CLSID下:

image.png
然后使用IFilterMapper2::RegisterFilter方法将fitler注册到CLSID_VideoInputDeviceCategory下。这里需要用的REGFILTERPINS2结构:
REGFILTER2 rf2FilterReg = {
    1,              // Version 1 (no pin mediums or pin category).
    MERIT_NORMAL,   // Merit.
    1,              // Number of pins.
    &sudPins        // Pointer to pin information.
};

这里dwVersion(第一个字段)为一,表示使用AMOVIESETUP_PIN格式的的定义。Merit(第二个字段,表示加入Filter Graph Manager的顺序)这里使用MERIT_DO_NOT_USE.
注册结果如下:

image.png
在第二个注册信息中,它包含了pin的定义,媒体类型等信息,但是没有包含路径信息(包含在第一个中),所以第二条信息看起来只是给system_device_enumerates使用的。 这也是obs-vcam可以用一个lib模拟多个摄像头的原因。
上一篇下一篇

猜你喜欢

热点阅读