美文网首页
Directorshow Filter的注册

Directorshow Filter的注册

作者: Charles_linzc | 来源:发表于2021-10-25 17:07 被阅读0次

    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模拟多个摄像头的原因。

    相关文章

      网友评论

          本文标题:Directorshow Filter的注册

          本文链接:https://www.haomeiwen.com/subject/moujaltx.html