下面是OBS-VirtualCam项目结构与Vivek‘s VCam项目结构的对比,OBS-VirtualCam比vcam项目多出来关于virtual-audio的相关代码,猜测应该是链接obs音频的。视频部分两者非常的相似。
为了更清楚的了解两个项目,我们在比较了各自的头文件,virtual-cam.h与filters.h:
Filter 类对比:
output in类对比:
可以看到,不论是取名、注释还是继承,类结构,两者是非常一致的,可以看出来OBS-VirtualCam正式在Vivek‘s VCam项目的基础上开发的。
Vivek‘s VCam项目作为一个示例项目主要是根据xx产生vedio数据,模拟摄像头。OBS-VirtualCam同样是模拟摄像头,但是数据来源与obs的流,已经是一个实用的产品。参照这两个项目,应该可以写出我们想要的虚拟摄像头项目了。
CVCam类
CVCam类继承于CSource, CSource由directshow 基础类库提供,用来实现基于Push模式的fitler(文档中指的是source fitler, 这里开发的是capture filter, 看来这两个在filter开发上是通用的)。
class CVCam : public CSource
{
public:
DECLARE_IUNKNOWN;
//////////////////////////////////////////////////////////////////////////
// IUnknown
//////////////////////////////////////////////////////////////////////////
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
IFilterGraph *GetGraph() { return m_pGraph; }
FILTER_STATE GetState(){ return m_State; }
CVCam(LPUNKNOWN lpunk, HRESULT *phr, const GUID id, int mode);
protected:
CVCamStream *stream = nullptr;
};
directorshow fitler 需要满足COM规范的要求,根据注释DECLARE_IUNKNOWN;应该自动添加了IUnkown需要实现的三个接口。
NonDelegatingQueryInterface来自于CUnknown, 根据CUnknown的描述,它主要用来帮助创建com对象,filter需要继承自它或子类,并且在public区域调用DECLARE_IUNKNOWN; micro。
NonDelegatingQueryInterface用来overwirte QueryInterface的行为,从而不在调用baseObject的默认QueryInterface实现。
m_pGraph在cbasefilter中用于指向filter graph manager,也有对应的GetFilterGraph方法,这里重新声明的方法被用于pin的使用中(CVCamStream的实现中可以看到),这里感觉是重复的方法。
m_State也是在cbase中定义的,也有同名方法:
HRESULT GetState(
DWORD dwMilliSecsTimeout,
FILTER_STATE *State
);
可以看到,这里是做了重载,直接返回了状态。
CVCam中声明了一个output pin, 名字为stream,类型为CVCamStream,对应生命的第二个类。
CVCamStream *stream = nullptr;
CVCamStream类
两个项目的 都一样继承自CSourceStream, 同时实现了 IAMStreamConfig, IKsPropertySet:
CSourceStream是所有pin的基础类,提供了pin所需要的基础实现和预留了的处理函数,以下的方法皆来自于对基础实现的overwrite.
HRESULT FillBuffer(IMediaSample *pms);
HRESULT DecideBufferSize(IMemAllocator *pIMemAlloc, ALLOCATOR_PROPERTIES *pProperties);
HRESULT CheckMediaType(const CMediaType *pMediaType);
HRESULT GetMediaType(int iPosition,CMediaType *pmt);
HRESULT SetMediaType(const CMediaType *pmt);
HRESULT OnThreadCreate(void);
HRESULT OnThreadDestroy(void);
一个Capurefilter 可以支持多种输出格式,不同的格式下又支持多种frame sizes. IAMStreamConfig 用来报告CapD支持那种格式以及设置格式。IAMStreamConfig接口描述的正式这些功能。
HRESULT CreateClassEnumerator(
[in] REFCLSID clsidDeviceClass, // 确定Filter Categories,
[out] IEnumMoniker **ppEnumMoniker,
[in] DWORD dwFlags //获取满足flag条件的fitler
);
IKsPropertySet 用来设置或获取设备属性,每一个output pin 需要实现这个接口:
HRESULT STDMETHODCALLTYPE Set(REFGUID guidPropSet, DWORD dwID,
void *pInstanceData, DWORD cbInstanceData, void *pPropData, DWORD cbPropData);
HRESULT STDMETHODCALLTYPE Get(REFGUID guidPropSet, DWORD dwPropID,
void *pInstanceData, DWORD cbInstanceData, void *pPropData,
DWORD cbPropData, DWORD *pcbReturned);
HRESULT STDMETHODCALLTYPE QuerySupported(REFGUID guidPropSet,
DWORD dwPropID, DWORD *pTypeSupport);
OBS vcam项目的功能实现要比Vivek‘s VCam项目复杂,它还定义了很多私有函数。
最后,两个实现的CVCamStream类还都定义了对vcam类的指针。