虚拟摄像头 订阅
虚拟摄像头(9158)(Softcam) 是一个名副其实的软件摄像机,它能模拟成为“真实的”摄像机,成功地骗过大部分的视频聊天软件,比如MicrosoftNetmeeting、CU-Seeme等等。如此一来,就算计算机上没有摄像设置,但照样能使用这些软件来虚拟视频。 展开全文
虚拟摄像头(9158)(Softcam) 是一个名副其实的软件摄像机,它能模拟成为“真实的”摄像机,成功地骗过大部分的视频聊天软件,比如MicrosoftNetmeeting、CU-Seeme等等。如此一来,就算计算机上没有摄像设置,但照样能使用这些软件来虚拟视频。
信息
软件大小
2.7MB
软件名称
虚拟摄像头
软件性质
共享软件
软件版本
1.52
虚拟摄像头误区
虚拟摄像头有一点误区:不可能代替摄像头的线 它的作用应该是: 比如你用QQ跟人视频,你的电脑本身没摄像头或者不想让别人看到你真实的一面,你就可以用虚拟摄像头软件把你提前准备好的录象加载进去部分网站聊天室要求用户装有摄像头,但如果用户不想装,但又希望进入该网站,或聊天室,可以在电脑中安装虚拟摄像头。
收起全文
精华内容
下载资源
问答
  • 虚拟摄像头

    2018-09-05 17:53:57
    虚拟摄像头,可以让没有摄像头的主机生成一个虚拟的摄像头
  • 虚拟摄像头 瑞风虚拟摄像头 虚拟摄像头 瑞风虚拟摄像头 虚拟摄像头 瑞风虚拟摄像头
  • VCam虚拟摄像头(电脑虚拟摄像头视频软件)是一款十分专业的虚拟摄像头工具。该软件能够帮助用户在系统上模拟一个摄像头,可以在任何支持摄像头的软件里面使用,该软件还可以添加视频特效或者画中画效果、进行人脸跟踪...

    VCam虚拟摄像头(电脑虚拟摄像头视频软件)是一款十分专业的虚拟摄像头工具。该软件能够帮助用户在系统上模拟一个摄像头,可以在任何支持摄像头的软件里面使用,该软件还可以添加视频特效或者画中画效果、进行人脸跟踪、背景替换,欢迎下载。

    软件说明:

    该软件是一个虚拟的软件摄像头,它能够在您的系统上模拟一个摄像头,可在任何支持摄像头的软件中使用。使用QQ虚拟摄像头,你可以与远方的朋友分享你电脑上面的精彩视频,也可以把自己的录像、照片、动画、Flash 甚至桌面等作为聊天的视频展现给网友 ,并支持图像、视频叠加(画中画)以及多种超酷视频特效。

    功能说明:

    1、真正 WDM 驱动,支持所有使用 VFW 和 DirectShow 技术的视频应用程序。

    2、同时支持多路高质量视频输出,每路可以有不同的分辨率。

    3、算法高效,CPU 占用率低。

    4、基于 DirectShow 架构,处理速度快,媒体格式支持范围广。

    5、支持播放列表,自定义节目播放,多种参数设置。

    6、能够多路输出DV、普通摄像头等视频捕捉设备的视频。

    7、支持图像、动画、视频(文件或设备)以及文字透明覆盖,画中画功能。

    8、多种超酷视频特效,性能超强。

    9、摄像头背景替换(扣像)。

    10、人脸识别、智能追踪功能。

    11、屏幕捕捉功能,支持热键缩放、鼠标跟随以及全屏幕捕捉等,支持自定义光标。

    12、支持摄像头默认画面设置,默认分辨率设置。

    13、强大的视频录制功能。

    14、在程序中启用/禁用/安装/卸载摄像头驱动程序。

    15、界面简洁易用,支持换肤、色彩方案。

    16、多语言支持。

    17、简单易用的专业安装程序。

    18、自动检查新版本、显示软件最新动态。

    19、中、英文论坛提供专业技术支持。

    展开全文
  • 虚拟摄像头之DirectShow虚拟摄像头开发

    万次阅读 热门讨论 2018-04-06 02:21:58
    by fanxiushu 2018-04-06 转载...之前CSDN上的博客,较多的文章阐述了虚拟摄像头的开发,而且是两种类别的。1,比如使用老的内核流框架开发WDM虚拟摄像头驱动,博客链接:https://blog.csdn.net/fanxiushu/article/d...

                                                          by fanxiushu 2018-04-06 转载或引用请注明原始作者。

    之前CSDN上的博客,较多的文章阐述了虚拟摄像头的开发,而且是两种类别的。


    1,比如使用老的内核流框架开发WDM虚拟摄像头驱动,博客链接:
    https://blog.csdn.net/fanxiushu/article/details/8496747  (虚拟摄像头驱动原理及开发)
    它使用StreamClassRegisterAdapter 注册初始化驱动框架,属于较老的框架模型,
    系统函数StreamClassRegisterAdapter从stream.sys导出。

    2,虚拟USB摄像头驱动,博客链接:
    https://blog.csdn.net/fanxiushu/article/details/52761644     (USB设备驱动开发之扩展(利用USB虚拟总线驱动模拟USB摄像头))
    它使用模拟USB接口的摄像头方式,让操作系统自动把它当成USB摄像头,来达到模拟虚拟摄像头的效果。
    然而本质是Windows当识别到USB接口是符合UVC标准的摄像头的时候,会自动加载它自带的usbvideo.sys驱动,
    这个驱动负责生成摄像头的功能。 我们仔细研究usbvideo.sys,其实不难发现,它使用ks.sys 框架,这个就是较新的流内核框架模型。
    ks.sys使用KsInitializeDriver 来注册初始化驱动框架。 

    今天我们介绍一个比较浅显的在应用层就能模拟虚拟摄像头的办法。
    利用应用层的DirectShow,模拟出虚拟摄像头。
    然而他的局限也是挺大的,他只能适用于其他恰好也是使用DirectShow框架来获取摄像头数据的程序。

    先来看看Windows发展史上,主要使用哪些应用层底层框架来操作摄像头。
    1, VFW(Video Of Windows)这个是Windows最先使用的框架,也是最古老的,
           好处是接口函数够简单,在有些兼容或者小型用层场所用用也没什么问题。
    2,DirectShow,这个是横跨 WINXP, WIN7,WIN8,WIN10都得到很好支持的框架,
          然而接口函数也挺复杂。
    3,Media Foundation, 这个是WIN7之后,开发的新框架,目的是为了替换DirectShow,
         从而达到更好的性能和适应流媒体发展的需求。

    从发展的眼光来看,最新开发的程序,应该使用Media Foundation 框架是最好的选择。
    从现状来看,大量的操作摄像头的程序都是使用DirectShow框架,有小部分还在使用VFW框架。
    新近开发的程序或者对老版本程序的更新中,相当数量的程序使用Media Foundation框架代替了 DirectShow。
    这就造成了丰富多彩的局面。而且Media Foundation 代替DirectShow也是趋势。
    这对DirectShow虚拟摄像头来说不是个好消息,
    VFW框架和Media Foundation框架,都只能识别驱动级别的摄像头,并不能识别DirectShow摄像头。
    更有甚者,UWP框架的程序(就是Windows10平台新出来的,为了 “大一统“ 目的,这个目前也是小众,能否发展做大谁知道)。
    对驱动级别的摄像头限制更大,已经不再支持老内核流框架。
    因为我之前开发的基于StreamClassRegisterAdapter的老内核流初始化的虚拟摄像头驱动,
    虽然能被普通的Media Foundation框架的程序识别和正常使用, 但是WIN10最新的Skype和Camera程序并不能识别。
    这两款软件都是UWP框架程序。
    微软放弃在UWP程序中支持老内核流驱动框架,应该是为了更好的兼容其他非X86平台,虽然目前依然非常小众。
    有时间的话,会开发基于最新流内核框架(ks.sys)的虚拟摄像头驱动,来真正验证skype和camera的问题。
    可喜的是:基于USB的虚拟摄像头,能在所有的情况下都运行的非常好!

    其实这个 “大一统“ 对软件开发者来说是个非常好的想法。
    目前现状有Windows,iOS/MacOS, Android三大系统鼎立,
    每种系统,都有自己的一套底层函数接口和编程方式,而且区别也非常的大。
    这给开发人员和开发成本带来不少损耗。而开发的程序的功能基本都一样。
    但是也需要分别在这些系统上编译和接入对应的底层接口。
    比如做手机的公司要招聘iOS和Android至少两个组开发人员,做的都是同样的一款产品。
    幸好WP手机GameOver了,否则还得招聘WP开发人员,这对手机开发公司和人员来说未尝不是好消息!
    当然对微软来说是个遗憾,一个把操作系统玩得纯熟的公司,居然没法做好手机操作系统!
    这是老天在给微软开了一个巨大的玩笑,然而更多的是自己作死造成的。

    如果这些系统把底层接口封装成一个统一的接口而且能保证性能不减低,开发语言使用统一的,
    比如使用C/C++做稍微底层的,java或其他语言做界面上层等,这会给开发者带来不少好处,
    然而理想总是丰满的,现实总是骨感的。
    现实中我们依然要为了兼容各种操作系统而做重复的事。
    我想如果让开发者干掉其他两家让一家独大,一定会积极响应,因为少了不少麻烦,当然这会造成另外一些比如垄断等政治问题。
    我想这也是HTML5和JavaScript目前流行的原因之一,
    因为他是平台无关的:一套代码不用重新编译,各种平台都能运行。

    回到我们的DirectShow虚拟摄像头上来。
    我们来看看使用DirectShow操作摄像头的流程是如何的:
    首先使用CoCreateInstance创建 IGraphBuilder接口,这个接口是管理所有接口的“总管”。
    之后从IGraphBuilder查询出IMediaControl控制接口。
    然后就是创建ICreateDevEnum接口,从此接口枚举出系统所有安装的摄像头,枚举的当然也包括DSHOW虚拟摄像头。
    选择你感兴趣的摄像头,并且获取这个摄像头的IBaseFilter接口, 把这个接口添加到IGraphBuilder中 。
    再然后选择其他Filter,比如压缩的Filter,Render Filter等,都加到IGraphBuilder中,
    之后,查询每个Filter的PIN接口,把匹配的PIN接口连接起来。这样就构成了一个DSHOW的连接图。
    要使整个“”图“” 动起来,只需要运行 IMediaControl 的Run函数,这样摄像头的数据就会流经每个Filter,
    最终达到 Render Filter 在终端展现出来。

    大致伪代码如下:
    hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
            IID_IGraphBuilder, (void**)&graphBuilder); ///创建 IGraphBuilder接口
    hr = graphBuilder->QueryInterface(IID_IMediaControl, (void**)&control); //查询IMediaControl

    CComPtr<ICreateDevEnum> DevEnum; ///创建枚举摄像头设备接口
    hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&DevEnum);
    CComPtr<IEnumMoniker> pEM;//枚举
    IMoniker* pM; //查询到的每个设备
    hr = DevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEM, 0);
    while (pEM->Next(1, &pM, &fetch) == S_OK) {
         ///开始枚举每个设备,如果是我们的虚拟DSHOW摄像头,也会被枚举到
         ........
        ///选择我们感兴趣的摄像头, 获取Filter接口,比如deviceFilter名字
        pM->BindToObject(0, 0, IID_IBaseFilter, (void**)&deviceFilter);
    }

    /创建其他Filter,比如renderFilter,然后全部添加到IGraphBuilder中.
    graphBuilder->AddFilter(deviceFilter, L"DeviceFilter"); //添加摄像头的filter到GraphBuilder中,
    graphBuilder->AddFilter(renderFilter, L"RenderFilter"); //添加其他Filter到GraphBuilder中,

    //利用 IBaseFilter 接口的EnumPins函数查找合适PIN,假设上面两个Filter的PIN分别是 devicePin, renderPin, 
    对这些PIN再做些其他配置等。。。。

    最后调用 IGraphBuilder的Connect函数把PIN连接起来,如下
    graphBuilder->Connect(devicePin,renderPin);

    这样初始化完成, 
    调用 control->Run , 即可让其运行起来。

    以上就是使用DSHOW操作摄像头的通用流程,要让DirectShow虚拟摄像头能被正确识别和运行,
    需要遵照上面流程,实现各种接口。

    首先,要被ICreateDevEnum 接口识别到我们的虚拟摄像头,肯定得先注册我们的DSHOW摄像头。
    DirectShow框架已经帮我们提供了这样的注册函数。
    我们的虚拟摄像头需要实现在DLL动态库中
    (本来刚开始想实现在EXE中,想通过进程间COM方式,结果以失败告终,所以认为DirectShow框架只认识DLL方式的Filter),
    这个DLL需要具备COM接口动态库的一切基本条件,
    需要有DllRegisterServer, DllUnregisterServer, DllGetClassObject,DllCanUnloadNow四个导出函数。
    我们需要首先按照普通进程内COM注册方式把DLL注册进系统, 
    然后就是我们为了让DSHOW框架枚举到我们的虚拟DirectShow设备,需要做的特别处理:
    创建 IFilterMapper2接口,调用接口函数RegisterFilter ,把我们的虚拟摄像头注册进去。
    这样ICreateDevEnum 接口就能识别到了。
    大致伪代码如下:
            IFilterMapper2* pFM = NULL;
            hr = CoCreateInstance(CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER, IID_IFilterMapper2, (void**)&pFM);

    REGPINTYPES PinTypes = {
        &MEDIATYPE_Video,
        &MEDIASUBTYPE_NULL
    };
    REGFILTERPINS VCamPins = {
        L"Pins",
        FALSE, /// 
        TRUE,  /// output
        FALSE, /// can hav none
        FALSE, /// can have many
        &CLSID_NULL, // obs
        L"PIN",
        1,
        &PinTypes
    };
    REGFILTER2 rf2;
    rf2.dwVersion = 1;
    rf2.dwMerit = MERIT_DO_NOT_USE;
    rf2.cPins = 1;
    rf2.rgPins = &VCamPins;
     //根据上边提供的信息,调用RegisterFilter 注册。
    pFM->RegisterFilter(CLSID_VCamDShow, L"Fanxiushu DShow VCamera", &pMoniker, &CLSID_VideoInputDeviceCategory, NULL, &rf2);
    把以上代码添加到DllRegisterServer导出函数中,当调用DllRegisterServer注册COM组件的时候,也就把DSHOW虚拟摄像头注册进去了,
    同样注销也是类似处理。
    其中 CLSID_VCamDShow 是我们自己定义的GUID,用来标志我们的虚拟摄像头接口。
    系统也会根据这个GUID来获取我们的接口进行后续的操作。

    之后就是我们需要实现的主要内容,本来如果使用DirectShow的SDK开发库,可以比较容易实现这部分内容。
    本着一直造轮子的习惯,这次也不例外,采用完全从零开始的开发方式,
    稍后提供到GITHUB和CSDN上的代码可以看到这一点。
    如果你不喜欢,或者不想去了解DirectShow的工作原理,
    大可不必理会我这种比较“疯狂”的做法,也不必下载我的这份代码给你平添无谓的烦恼。
    毕竟DirectShow的SDK代码也是乱糟糟的挺复杂,而且迟早会被Media Foundation替代。

    阅读下面的内容需要具备一些Windows平台的COM组件的基础知识
    (其实整个DSHOW摄像头开发都应该具备COM组件基础知识,否则举步维艰)。
    其实我们从零开始做一个COM组件没有这么可怕,甚至针对某些特殊情况,可能还比各种封装开发包简洁和容易理解一些。

    我们的DSHOW摄像头,除了必须实现的
    DllRegisterServer, DllUnregisterServer, DllGetClassObject,DllCanUnloadNow四个导出函数外,
    重头戏就是实现我们的类对象,必须继承IBaseFilter接口,为了兼容顺便实现IAMovieSetup 接口。
    IBaseFilter接口是DSHOW FIlter的基础导出接口,每个Filter下有一个或者多个PIN接口,因此我们还必须实现IPin接口,
    光 IBaseFilter和IPin接口,一共就需要实现20,30多个接口函数,看起来有点多,其实理解了,也没这么麻烦。
    为了配置IPin接口,还必须实现 IAMStreamConfig 和IKsPropertySet,这两个接口导出函数并不多,就几个。
    我们的虚拟摄像头就只有一个Output Pin 接口,为了简单,在 Filter就只提供一个 IPin就可以了。
    大致的数据结构描述如下所示:

    class VCamDShow: public IUnknown,
        public IBaseFilter, public IAMovieSetup
    {
    protected:
        。。。 //内部数据变量和私有函数

          VCamStream*     m_Stream; /// 这个就是我们的 IPin接口, 就只需要一个就可以了,VCamStream数据结构下面会描述。

    public:
            //IUnknow 接口
            。。。。
           // IBaseFilter 接口
          STDMETHODIMP GetClassID(...);///
          STDMETHODIMP Stop() ;/// 停止, IMediaControl接口调用
          STDMETHODIMP Pause(); ///暂停,
          STDMETHODIMP Run();  ///运行
          STDMETHODIMP GetState(...); ///获取运行,暂停,停止等状态
          STDMETHODIMP GetSyncSouce(...);   
          STDMETHODIMP SetSyncSource(...);
          STDMETHODIMP  EnumPins(...);     查询当前filter 提供的IPin 接口信息, DirectShow库通过此函数获取当前Filter提供的IPin信息
          STDMETHODIMP  FindPin(...);  //
          STDMETHODIMP QueryFilterInfo(...); ///获取当前Filter信息
          STDMETHODIMP JoinFIlterGraph(...); /// 把当前filter加入到DirectShow图中,其实就是对应 IGraphBuilder->AddFilter 调用时候被调用。
          ............
          
    };

    class VCamStream : public IUnknown,
        public IPin, 
        public IQualityControl, public IAMStreamConfig, public IKsPropertySet
    {
    protected:

           。。。 //内部数据变量和私有函数
           VCamDShow*   m_pFilter;         // 所属的Filter,对应上面定义的VCamDShow数据结构。

           / 下面是数据源相关的线程,在
    StreamTreadLoop 中循环采集数据,并且通过 IMemInputPin 把数据传输给输入PIN。
           
    HANDLE  m_hThread; ///
    HANDLE  m_event;
    BOOL    m_quit;   
    static DWORD CALLBACK thread(void* _p) {
    VCamStream* p = (VCamStream*)_p;
    CoInitializeEx(NULL, COINIT_MULTITHREADED);
    p->StreamTreadLoop(); 
    CoUninitialize();
    return 0;
    }
    void StreamTreadLoop();
           / 
    public:
          //IUnknow 接口
          .....
          IPin 接口
          STDMETHODIMP  Connect(....); 把 输入PIN和输出PIN连接起来,这个是主要函数,其实就是对应 
                                                                          IGraphBuilder->Connect(devicePin,renderPin);
          STDMETHODIMP  ReceiveConnection(...); ///接收连接
          STDMETHODIMP  DIsconnect(...);  ///断开与其他PIN的连接
          STDMETHODIMP  ConnectTo(...);  以下基本都是一些状态和数据信息查询
          STDMETHODIMP  ConnectionMediaType(...); ///
          STDMETHODIMP  QueryPinInfo(....);
          STDMETHODIMP  QueryDirection(...); ///
          .............
          IQualityControl 
          ....
          / IAMStreamConfig...
          STDMETHODIMP SetFormat(...); ///
          STDMETHODIMP  GetFormat(...); ///
          STDMETHODIMP  GetNumberOfCapabilities(...); ///
          STDMETHODIMP  GetStreamCaps(....);
          /// IKsPropertySet
          STDMETHODIMP  Get(...); ///
          STDMETHODIMP  Set(...);
          STDMETHODIMP  QuerySupported(...); /
          
    };
    以上看起来接口函数挺多,其实整体结构不复杂的,而且主要实现这两个类对象基本就搞定DSHOW虚拟摄像头了。
    具体代码可以稍后去下载我提供到GITHUB或CSDN上的源代码。

    正如上面的查询摄像头的伪代码所说,
    ICreateDevEnum 接口查询到我们感兴趣的摄像头,
    当绑定到这个摄像头获取IBaseFilter接口,调用 
    IMoniker 的 BindToObject 函数,
    虽然没有BindToObject 源代码,但可以知道大致流程:
    BindToObject查找CLSID_VCamDShow(我们自定义的GUID)等信息,
    调用系统函数CoCreateInstance函数创建我们的对象并且获取IBaseFilter接口,
    CoCreateInstance 系统函数通过注册表查找我们注册的DLL所在位置,找到并且加载DLL,同时调用DllGetClassObject获取
    类工厂,调用类工厂的CreateInstance创建我们的类,也就是上面的 VCamDShow类, 从而获取到IBaseFilter接口。
    类工厂数据结构也是挺简单的,这里无非就是提供 IClassFactory接口,
    主要实现CreateInstance方法,在此方法new我们的VCamDShow 类对象。详细信息可查阅提供到GITHUB和CSDN上的代码。

    找到并且获取到IBaseFilter指针后,接下来就是调用 IGraphBuilder->AddFilter 添加到 DirectShow的Graph中,
    这个时候 IBaseFilter的JoinFilterGraph方法被调用,我们在此方法中其实简单保存IFilterGraph接口指针,
    方便后面调用,同时查询IMediaEventSink接口,用于通知事件。

    在连接输入PIN和输出PIN之前,需要对这些PIN的MediaType类型做些配置,
    就是这个PIN提供哪些类型,比如是RGB,还是YUV,YUY2等,尺寸是640X480,还是1280X720等等信息。
    只有当两个PIN的MediaType类型匹配,才会连接成功。
    这个时候 IAMStreamConfig 接口的 SetFormat ,GetFormat等函数就会被调用,用于设置具体的Meidia类型。
    我们在实现
    IAMStreamConfig这些函数 时候,预先配置一些当前PIN支持的Media类型,这样当外部调用SetFormat设置Media的时候,
    根据这些类型做选择,支持的就设置成功,不支持的就返回失败,具体可查询我提供的源代码。

    之后就是两个PIN连接, 当外部调用 IGraphBuilder ->Connect(vcamerPin , renderPin); vcamerPin就是我们的摄像头的输出PIN。
    对应IPin的Connect或者ReceiveConnection接口函数就会被调用。
    在Connect函数中,我们想法查找各种合适的MediaType做匹配,找到后就可开始连接,
    ReceiveConnection函数中根据提供的MediaType直接进行连接操作,
    假设执行具体连接的函数是 HRESULT doConnect(IPin* pRecvPin, const AM_MEDIA_TYPE* mt );
    因为我们是虚拟DSHOW摄像头,我们的PIN是输出PIN,是数据源。
    我们必须把我们的数据源传输给连接上来的输入PIN,否则就是废品,如何实现这个核心要求呢。
    其实输入PIN必须要实现IMemInputPin 接口,这个接口就是用来传递数据的。
    我们在获取输入PIN的IMemInputPin接口后,调用Receive方法就能把数据传输给输入PIN了。
    而Receive方法需要传递 IMediaSample 接口作为参数,IMediaSample需要通过 IMemAllocator 接口的GetBuffer方法获取。
    因此我们在 doConnect函数中,除了获取IMemInputPin接口外,还必须创建IMemAllocator 接口。
    doConnect大致伪代码如下:

    HRESULT VCamDShow::doConnect(IPin* pRecvPin, const AM_MEDIA_TYPE* mt )
    {
           .....
           pRecvPin->QueryInterface(IID_IMemInputPin, (void**)&m_pInputPin); // 从输入PIN 获取IMEMInputPIN接口, 
           
           ...... 其他一些判断处理,比如判断MediaType是否匹配等
          
           m_ConnectedPin = pRecvPin;  ///保存 输入PIN指针。
           m_ConnectedPin->AddRef();
      
           ///创建 IMemAllocator接口
           
    hr = m_pInputPin->GetAllocator(&m_pAlloc); 
           if(FAILED(hr)) {
                  hr = CoCreateInstance(CLSID_MemoryAllocator,0,CLSCTX_INPROC_SERVER,IID_IMemAllocator,(void **)&m_pAlloc);
           } 

           ///通知输入PIN,完成连接 
           
    hr = pRecvPin->ReceiveConnection((IPin*)this, mt);

           。。。。。 
    }

    连接成功后,整个DirectShow初始化完成,就可以开始播放,
    外部调用 IMediaControlI->Run, 我们的 IBaseFilter的Run,Pause等函数就会被调用,
    我们在这些函数中设置运行状态,执行初始化等操作。

    至此,一整套DirectShow摄像头运行流程似乎都跑通了,但似乎忘记了一个重要的地方,数据源呢?
    因此,我们可以在VCamStream 类里边创建一个线程,在这个线程里定时循环采集数据,
    并且通过 IMemInputPin接口把采集的数据传输给连接上来的输入PIN。
    如上面VCamStream 数据结构申明的一样,StreamTreadLoop 大致代码如下:

    void VCamStream::StreamTreadLoop()
    {
    DWORD TMO = 33;
    ///
    while (!m_quit) {
    ///
    WaitForSingleObject(m_event, TMO);
    if (m_quit)break;
    /
    if (m_pFilter->m_State != State_Running) { //不是运行状态
    continue;
    }

    /
    IMediaSample* sample = NULL;
    HRESULT hr = E_FAIL;
    。。。
    if (m_pAlloc) {
    hr = m_pAlloc->GetBuffer(&sample, NULL, NULL, 0);
    }
                    .......................省略其他处理
                    
    LONG length = sample->GetSize();
    char* buffer = NULL;
    hr = sample->GetPointer((BYTE**)&buffer);
                     
                    
      这个是一个回调函数,我们可以自定义这个回调函数,并且在里边填写视频帧数据。
                    
    m_pFilter->m_callback( buffer, length ,。。。);  
                    
                    。。。。。
                    m_pInputPin->Receive(sample);  获取到的视频数据,传递给输入PIN。
             }
              。。。。
    }

    到此为止,才算真正完成了DirectShow虚拟摄像头驱动的核心部分。

    GITHUB代码地址:
    https://github.com/fanxiushu/vcam_dshow

    CSDN上代码地址:
    https://download.csdn.net/download/fanxiushu/10329777

    下图是在QQ中运行效果:

    展开全文
  • 一、背景介绍虚拟摄像头,顾名思义,就是利用软件技术虚拟出一个摄像头硬件设备供用户使用。当我们需要对视频图像进行处理再输出时,虚拟摄像头就具备非常大的价值了。关于如何在Windwos上实现一个虚拟设备的资料...

    一、背景介绍

    虚拟摄像头,顾名思义,就是利用软件技术虚拟出一个摄像头硬件设备供用户使用。当我们需要对视频图像进行处理再输出时,虚拟摄像头就具备非常大的价值了。关于如何在Windwos上实现一个虚拟设备的资料已经非常丰富了,Windows Driver Kit里面也有非常多的帮助文档。这篇博文主要总结了在Mac下开发虚拟摄像头的一些经验。Mac下的虚拟摄像头产品其实也有不少,例如CamTwist, CamMask, CamWiz, ManyCam等。但是关于如何在Mac下开发虚拟摄像头设备的资料却是异常匮乏。通过一番搜索后才找到一个关键字:CoreMediaIO. 经过了解,CoreMediaIO是Mac下的一个framework,主要用于对视频图像进行处理。而CoreMediaIO framework有一个Device Abstraction Layer(DAL),它类似与Mac下CoreAudio的Hardware Abstraction Layer(HAL)。HAL主要是用来处理音频硬件发送的音频流的,而DAL则是用来处理视频设备的视频流的。因此,利用DAL插件框架,可以模拟出一个摄像头设备供上层用户使用。

    CoreMediaIO DAL有一个示例项目,这个项目模拟出了一个名为“Sample”的设备,通过底层kext模块提供的模拟数据实现视频帧的传递。这个DEMO要真正使用起来的话,有一些需要注意的地方。在Demo中的README文件中推荐使用如下命令安装预编译好的程序:

    // Debug

    sudo darwinup install {path to CoreMediaIO folder}/Prebuilts/Sample-Debug.tar.gz

    // Release

    sudo darwinup install {path to CoreMediaIO folder}/Prebuitls/Sample-Release.tar.gz

    这样安装之后,并不能马上就能找到虚拟摄像头。一方面是因为kext提供的模拟数据超过800MB,加载到内存中需要一定的时间;另一方面是因为Prebuilts中的kext模块是未签名的。而OSX自从Mavericks开始要求kext模块必须经过签名,系统才会自动加载。否则的话需要关闭System Integirty Protection(SIP),手动加载Kext模块才能让Demo正常工作。

    20180110224356277232.png

    二、如何编译项目

    在我们着手开发定制自己的虚拟摄像头之前,第一步就是要搞清楚Demo工程的组织结构。在Demo工程中包括两个文档,分别说明了DAL插件的工程结构、工作原理。这两个文档在开发之前应该要好好看看,对于了解整个代码结构和工作机制具有较大的帮助。另外,如何编译整个Demo工程也是个大问题。因为下载下来的工程中缺少了CoreAudio模块,需要手动下载CoreAudio模块加入到工程中去。然后可能还有一些语法错误需要修改,这个根据系统版本和XCode版本视情况而定。

    1. 添加CoreAudio模块。默认工程是不包含CoreAudio模块的,因此直接编译会有很多链接错误:

    20180110224356279185.png

    下载地址:Core Audio Utility Classes.(可能需要Apple ID登陆)。下载好把整个文件夹加入到Demo工程中去进行编译。如果编译还是有错的话,可以将CoreAudio模块单独编译出一个静态库,然后在Demo工程中加入CoreAudio头文件和静态库进行编译,这样应该就可以解决掉编译问题了。

    2. 语法修改。可能是因为macOS SDK的版本问题,编译过程中需要修改一些语法错误,如下:

    20180110224356280161.png

    20180110224356282115.png

    解决方法倒也简单:

    20180110224356283091.png

    20180110224356285044.png

    还有一些赋值的问题,不过基本上都是编译标准的问题,不难解决掉。

    三、插件模块修改

    1. 分辨率。官方Demo工程提供了几个分辨率:720x480, 1280x720, 1920x1080。如果要增加自己的分辨率的话,可以直接把这几个分辨率改掉。

    20180110224356286998.png

    20180110224356288951.png

    在这里将分辨率的宽高修改掉,同时注意还有帧率也是在这里修改的。注意Demo工程三种分辨率使用的颜色模式是UVVY422哦!因此如果要采用Demo工程的颜色模式的话,需要将图像转换为UYVY422格式。

    2. 颜色模式。如何修改颜色模式呢?总共有两个地方需要修改,除了上面那个kCMVideoCodecType_422YpCbCr8需要修改,在CMIO_DPA_Sample_Server_Stream.cpp中还有个地方需要修改:

    20180110224356289927.png

    这里指定Plugin支持的颜色模式。那么,Plugin总共支持哪些颜色模式呢?看看enum里的枚举成员就知道了:

    20180110224356291881.png

    3. 设备基本信息。设备基本信息主要就是设备的名称,方便用户进行识别选择使用。这个只有一个地方需要修改:

    20180110224356292857.png

    4. Create server failed的问题。在plugin的入口函数中,有这样一段代码:

    20180110224356293834.png

    开发人员在注释中详细解释了,为了调试的目的这里尝试使用了手动方式启动Assistant。而在实际测试时,bootstrap_create_server()会经常失败抛出异常,导致入口函数提前结束执行,因而创建虚拟设备失败。注释中还解释道,一般是使用plist文件在系统启动时创建assistant服务。这样,bootstrap_loop_up()在查找到Assistant服务后,就会跳过手动创建Assistant服务。

    由此看来,位于/Library/LaunchDaemons中的plist文件是必不可少的。/Library/LaunchDaemons/下的plist文件其实就是指定哪些程序随系统启动自运行。其内容如下:

    Label

    com.apple.cmio.DPA.Sample

    ProgramArguments

    /Library/CoreMediaIO/Plug-Ins/DAL/Sample.plugin/Contents/Resources/SampleAssistant

    MachServices

    com.apple.cmio.DPA.Sample

    四、内核模块修改

    1. 移除Demo中的模拟帧数据。Demo工程提供了三个分辨率的模拟数据,每个分辨率都有30帧的数据。三个分辨率的数据加起来有八百多兆,定制的时候有必要把他们去掉。根据Demo工程配置来看,模拟帧数据是作为section data编译到了kext模块中去了:

    20180110224356295787.png

    20180110224356296764.png

    因此这里去掉模拟数据的方式非常简单,把”Other Linker Flags“里面的内容全部删掉即可。当然,代码中使用这些section data的部分也要跟着删掉。

    2. 内核签名。OSX自从Mavericks开始,对Kext开发引入了签名机制。所有未签名的kext模块系统不会再自动加载。因此,要让系统自动加载第三方开发的kext模块,开发者需要向苹果申请能够对kext进行签名的证书(看这里)。一般的开发者证书即使正常签名了,也不能被系统正常识别。签名过后,可以对kext模块进行签名验证:

    20180110224356298717.png

    在没有能够对kext进行签名的证书时,可以把SIP关掉进入测试模式。这样即便kext未签名也可以手动进行加载,方便对程序进行测试。

    3. kext模块自动加载。这里有一点奇怪的是:经过签名的kext模块在系统重启时会被系统自动加载,但是通过Plugin访问不到kext模块服务。必须要先调用kextunload一次,再kextload一次才能起作用。暂时没有搞清楚这是什么原因,不过根据其他虚拟摄像头产品的解决方案来看,也是存在这个问题的。比如CamWiz的解决方案就是:

    (1)编写一个plist文件放到/Library/LaunchDaemons/文件下,其内容为:

    Disabled

    Label

    com.apple.vcam.DriverReloader

    ProgramArguments

    /Library/CoreMediaIO/Plug-ins/DAL/Sample.plugin/Contents/Resources/reloadKext.sh

    RunAtLoad

    (2)编写一个shell脚本reloadKext.sh,并打包到Sample.plugin的Resources文件夹下,其内容如下:

    #!/bin/sh

    /sbin/kextunload "/Library/Extensions/IOVideoSample.kext"

    /sbin/kextload "/Library/Extensions/IOVideoSample.kext"

    这样,每次系统重启的时候都会去调用这个脚本,卸载kext模块然后再加载。这就解决了Kext模块的问题。事实上,如果没有硬件层的需要,去掉kext模块是最好的。但是整个Demo工程代码繁杂,文档又是极其匮乏,想要剥离Kext模块难度较大。但是仍然有不少的产品实现了这一点,如CamTwist、Cammask和ManyCam。CamTwist更牛逼的是,在一个插件中虚拟出了两个设备。一个是YUV颜色模式,另外一个是BGRA颜色模式。

    五、其他事项

    1. 程序打包及安装路径。根据职能需要,整个项目的安装程序分成三个部分:

    (1)*.plugin安装到/Library/CoreMediaIO/Plug-Ins/DAL/.

    (2)*.kext安装到/Library/Extensions/. 苹果官方规定:第三方开发的kext模块只能放在这里,而苹果自带的kext模块则放在/System/Library/Extensions/.

    (3)*.plist安装到/Library/LaunchDaemons/. 放在这里的plist文件都是为了开机启动。因此在制作安装包时,记得要求用户安装完毕时重启系统。

    20180110224356300670.png

    20180110224356301647.png

    2. 文件权限问题。安装包中的所有文件最好修改所有者权限,否则有可能无法使用:

    $ sudo chown -R root:wheel *

    3. 相关命令

    (1)otool搭配install_name_tool使用

    (2)kext*族命令(kextload, kextunload, kextstat, kextutil)

    4.  安装包打包程序还是推荐使用packages,这是一款良心工具。使用简单、界面美观、功能强大实用,实在是制作pkg文件的上上之选。

    六、参考链接

    展开全文
  • 如何让虚拟摄像头成为电脑唯一摄像头,装软件自动识别虚拟摄像头为电脑真实摄像头
  • window虚拟摄像头

    2017-03-13 10:46:03
    下载的虚拟摄像头例子打包。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,543
精华内容 617
关键字:

虚拟摄像头