请点击标题进入正文下载相关附件
这个标题应该没有表达清楚,其实也蛮难靠标题说清的。
情况是这样的,最近从网上订购了如图所示的无线网络摄像机,为了避开广告嫌疑,我不给出任何具体介绍链接了。
他的功能还不错,可以将拍摄的画面通过Wifi无线或者100Mbps以太网发送出来供远程观看,同时自带了近360度自由度的X-Y轴云台供远程操作移动画面。不过在使用的时候发现了个麻烦:他只提供Web客户端进行视频浏览和控制。无法像传统摄像头那样通过VFW或者DirectShow捕捉它的视频信号,也没有提供专业网络摄像机提供的mms/rtsp视频协议供我自己写的程序或者一些视觉计算库如OpenCV使用。同时对云台的操作也只能在浏览器中进行,没有直接提供供使用者调用的编程接口。
因为这类摄像机基本都是小作坊工厂出产的,我在包装上连他的厂家都没找到是谁,所以问厂家索要开发包无望,且网上也没有解决办法,所以就花了一天时间实现了我要的需求。特此将思路和代码给出,希望对有同样需求的人有所帮助。要注意的是似乎生产这类Ipcam的厂家有很多,他们虽然生产出外形和功能都类似的产品,但内部的机制多少有些不同。所以这里给的方法可能不能完全适用于你的机器,但思路应该都一样。
为了给个直观印象方便我说明问题,这里给个这类IpCam的Web客户端的样子:
如图所示,对这类摄像机的一切操作/访问都是通过浏览器访问位于摄像机上的web服务器实现的。在具体介绍实现我的需求前,我先介绍这类摄像机的技术细节。
该类Ipcam的硬件和客户端
当收到这个摄像机不久我就把它拆了一遍,因此对他的硬件有了个大致的了解。这类摄像机一般采用ARM的解决方案,里边烧录了Linux OS作为固件。而Wifi很出我意料的直接使用了块usb接口的802.11b/g网卡,当然usb接口是直接用电线连接的。看来将来即使自己修改个Linux内核烧录进去也很容易搞定,同时我也怀疑他的摄像头也是usb接口连到Arm主板上的... 至于云台电机,他采用了国内某厂的一套小功率步进电机驱动。
硬件就是如此了,再说说他的通讯协议。通过sniffer他的数据包分析,我发现他所有的通讯都是基于HTTP协议的,包括视频信号和云台控制信号的传输。原先以为视频信号可能会用某些传统的协议,看来这条捷径走不通了。虽然基于HTTP传输视频信号,但也只是利用HTTP做了层wrapper,我没有时间去分析它本身的传输协议。所以也不打算通过逆向他的传输格式来直接编写客户端。
然后再分析它的Web客户端,也就上上图的那个画面中的网页。从前面协议分析已经看出,上图中的视频画面不太可能是用Mediaplayer或者FlashPlayer这类插件实现的,而是厂家自己编写的一个ActiveX控件(也就是说只能用于windows了)。同时又发现了他网页客户端中所有的操作,如云台的控制,也是直接调用这个ActiveX控件所提供的方法来实现的。
换句话说,对这个摄像机的所有访问和操作,厂家都已经完全封装在所提供的ActiveX控件当中了。那样其实也好办,我们可以尝试在自己的程序中调用该控件,这样云台的控制就解决了,或许该控件也提供了某个接口供我们直接获得视频数据,如getCurrentFrame()?
利用厂家自带的ActiveX控件实现摄像机云台控制和画面获取
通过分析该客户端网页代码,得知他使用的控件文件名为:DVM_IPCam2.ocx
接下来就是获得这个控件所提供的接口和调用方法了,下面是用Visual Studio的对象浏览器列出他提供的接口:
图上可以看出,所有对该摄像机的操作都已经集成在该控件当中了,当遗憾的是我除了PlayVideo(void)外并没有找到任何其他和获取视频数据有关的接口。看来要达到我们获得每一帧视频信息的目标还有点距离。但无论如何,先尝试将这个控件加入我们的程序调用再说。
从之前网页代码的分析看出,这个控件的使用并非容易,首先需要调用接口完成登陆操作。但好在Javescript都是被迫开源的...对于这个控件接口的使用完全都有代码可以参考。我第一步先将他的网页客户端进行裁减,做了个仅仅包含登陆至该摄像机并显示画面的简易版本,如下图所示,代码在文末给出,供大家参考。
接下来就是将同样的代码用C++来实现了,这个没有什么好说的,我用WTL调用这个控件实现了将画面放置于自己程序当中,同时可以调用控件的接口来控制云台,程序的代码在文末提供,下面是效果图:
简单地写了下控制界面,图上那个白色方块里边9个橙色方块可以控制云台的移动。
到此控件算是已经可以被调用了,但最重要是如何获得我们需要的视频的每帧图像数据。下面将具体介绍:
首先厂商提供的控件并没有提供能够获得视频数据的接口,因此我们无法简单的通过调用函数来解决这个问题。不过分析了该控件(DVM_IPCam2.ocx)的导入表后,发现他调用了VFW的DibDrawDib函数,因此猜测该控件是通过VFW将画面绘制到屏幕上的。DibDrawDib的定义如下:
The DrawDibDraw function draws a DIB to the screen.
BOOL DrawDibDraw( HDRAWDIB hdd, HDC hdc, int xDst, int yDst, int dxDst, int dyDst, LPBITMAPINFOHEADER lpbi, LPVOID lpBits, int xSrc, int ySrc, int dxSrc, int dySrc, UINT wFlags );
对于视频的每一帧画面,该控件应该都会调用DrawDibDraw函数将图像传送至屏幕,而该函数的参数则直接包含了每一帧图像的尺寸、RGB数据。那么,如果我们能截获对此函数的调用,并从中获取数据就完全可以达到我们的需求了。
要实现导入函数的截获很方便,直接修改对应模块的IAT表即可,具体原理在Windows核心编程中介绍。再上面介绍的自制客户端程序中,每一帧的视频画面都会出发我们自己写的myDrawDibDraw_func函数,该函数拥有和DrawDibDraw一样的参数,可以通过参数来获得当前收到的这一桢画面,我们可以在此处添加视觉处理的代码。下面是一个用这个办法实现的将ipcam视频画面进行边缘查找处理的效果:
好了。在文末给出这里所涉及到的代码: