将桌面捕获到虚拟摄像头

当然你可以直接用现成的虚拟摄像头软件实现这个功能。不过当初我开发这个插件的原因是,需要在Flash产品里面共享桌面,如果此时需要引导用户安装一个第三方的虚拟摄像头体验不好,所以公司希望我自己开发一个虚拟摄像头,一键安装减少用户的使用门槛。所谓的虚拟摄像头实际上在windows系统上注册了一个特殊dll,这个dll是一个COM组件。

虚拟摄像头需要用到Direct Show编程。

下载Direct Show开发代码

里面有如下的文件夹,我只需要用到第一个文件夹里面的代码—— baseclasses

baseclasses
capture
common
dmo
dvd
filters
misc
players
vmr9

创建工程

打开Visual Studio ,新建一个win32 Dll项目。
打开属性页,在VC++ 目录一栏中的库目录里面添加刚才的baseclasses的路径,这样我们就能在项目中引用这个目录里的代码了。
在C/C++属性页里面的附加库目录里面也把baseclasses的路径填入。
在dll.cpp中,我们需要把Filter注册成COM组件。
分别需要调用AMovieSetupRegisterServer函数、CreateComObject函数以及IFilterMapper2接口的RegisterFilter函数完成注册。

主逻辑

在头文件中,我们需要声明两个类

class CVCamStream;
class CVCam : public CSource
{
public:
    //////////////////////////////////////////////////////////////////////////
    //  IUnknown
    //////////////////////////////////////////////////////////////////////////
    static CUnknown * WINAPI CreateInstance(LPUNKNOWN lpunk, HRESULT *phr);
    STDMETHODIMP QueryInterface(REFIID riid, void **ppv);

    IFilterGraph *GetGraph() {return m_pGraph;}
    static int cx, cy;
    static HANDLE SocketThread;
    static SOCKET ClientSocket;
private:

    CVCam(LPUNKNOWN lpunk, HRESULT *phr);
};

class CVCamStream : public CSourceStream, public IAMDroppedFrames,public IAMStreamConfig, public IKsPropertySet
{
public:

COM组件只需要实现CUnknown接口即可。我们继承了Direct Show的CSource类,那么就已经实现了这个接口。
CVCamStream类用来实现图像数据的输出。
在CVCam的构造函数里面我们创建CVCamStream类的实例

m_paStreams = (CSourceStream **) new CVCamStream*[1];
m_paStreams[0] = new CVCamStream(phr, this, L"Flex COM");

在实现COM接口的QueryInterface函数中,我们调用了CVCamStream类的QueryInterface

HRESULT CVCam::QueryInterface(REFIID riid, void **ppv)
{
    //Forward request for IAMStreamConfig & IKsPropertySet to the pin
    if(riid == _uuidof(IAMStreamConfig) || 
        riid == _uuidof(IAMDroppedFrames)  ||
        riid == _uuidof(IKsPropertySet))
        return m_paStreams[0]->QueryInterface(riid, ppv);
    else
        return CSource::QueryInterface(riid, ppv);
}

定义媒体类型

HRESULT CVCamStream::GetMediaType(int iPosition, CMediaType *pmt)

在这个函数中,我们配置了媒体的具体的格式参数,比如24位RGB格式,图像的宽高等等。
另外还需要对GetStreamCaps函数进行实现,配置媒体的格式。

HRESULT STDMETHODCALLTYPE CVCamStream::GetStreamCaps(int iIndex, AM_MEDIA_TYPE **pmt, BYTE *pSCC)

捕获桌面

系统会调用FillBuffer函数,在这个函数中,我们将捕获到的数据填充到缓冲里面,Direct Show会处理剩下的事情。

HRESULT CVCamStream::FillBuffer(IMediaSample *pms)

捕获桌面只需要用到一个函数CopyScreenToBitmap

 HANDLE hDib = CopyScreenToBitmap(&ScreenRect, pData, (BITMAPINFO *)&(pVih->bmiHeader), m_hCursor);
    if (hDib)   DeleteObject(hDib);

pData是我们定义的一个指针,通过下面的代码,我们的pData就指向了缓存,数据填充到pData指向的内存中。

BYTE *pData;
pms->GetPointer(&pData);

进阶

实际产品会有很多需求,光实现捕获桌面是远远不够的,我们需要对这个捕获进行控制,比如捕获制定区域,停止捕获,恢复捕获等等。那么就涉及到和COM进行通讯了。
我们可以通过VS的窗口设计器创建一个windows窗口,然后提供一个用户操作界面。


窗口设计器

如何响应这个窗口的用户操作呢?
通过windows消息

INT_PTR CALLBACK WindowMessage(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)

当然最关键是需要在Flash产品的程序里面唤起这个窗口,需要用的socket编程。

SOCKET Listen_Sock = socket(AF_INET, SOCK_STREAM, 0);
    SOCKADDR_IN serverAddr;
    ZeroMemory((char *)&serverAddr, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(1234);
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    bind(Listen_Sock, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
    listen(Listen_Sock, 5);

C++的socket编程十分的繁琐,其他语言都会进行封装,让开发变的十分便利。
在一个线程里面写个死循环进行读取socket的数据,这是比较初级的多线程阻塞式的socket编程。对于我们这个程序是绰绰有余了。毕竟不是服务器,不需要面对并发的问题。

源码

https://github.com/langhuihui/FlexCOM

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,951评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,606评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,601评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,478评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,565评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,587评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,590评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,337评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,785评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,096评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,273评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,935评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,578评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,199评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,440评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,163评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,133评论 2 352

推荐阅读更多精彩内容