基于MFC的视频监控系统客户端实现
一天到晚看面试题,看算法书,坚持了一周左右,人也快扛不住了,需要换换节奏,思考下别的东西啊,已经进入一种混沌无效率的阶段了。百度的电话面试迟迟不来,弄的我现在做事情都不时刻注意着手机,都快幻听了。这种感觉真不好受啊,所以现在开始写点项目总结。
首先先给CSDN这个博主做下广告:http://blog.csdn.net/v_JULY_v
从2010年底开博,写了很多不错的文章,文笔思路都挺清晰的。而且看日志发现居然是个专科生,年龄好像跟我也差不多,感觉十分感慨。
进入主题,这个项目是我毕业设计的题目《基于无线网络的视频监控客户端的实现》因为导师这个项目要求保密,所以我只是做一些思路的记录,不会贴出任何代码。其实从本质上说,我觉得信息的获取应该是绝对通畅的,这样的行为都是阻碍知识的传播。但既然帮导师做事情,这点约定就遵守下把,所以本文章不贴任何图示。
1.内存管理类的设计和实现
由于是视频监控系统,所以数据量是不可小觑的,即使以H.264的高压缩比,要实现D1,乃至720P的视频,1S要处理的数据量,少则几十K,大则几百K,如果内存管理系统不够健壮,直接就崩溃了,更别说坚持上几天时间的跑了。
根据视频数据的特点:数据量大,单向流动,以及用完即可丢弃。所以要设计一个好的内存管理类。
首先需要定义一个内存的基类,其中含有指向数据的指针,已经一个整形表示所指内存块的大小,一个令牌。其他具体的缓存类都继承于这个基类,基类只定义分配内存的函数和释放内存的函数并不实现它们,留给子类去实现。这里就不得不说令牌的作用了,由于缓存对象经常需要被当做参数传递,传递过程中其实并不复制内存的东西,只是复制指针而已。那每一个对象都含有同一块内存块的指针,何时释放资源呢?这时候就需要依靠令牌了,因为视频数据单向流动,当复制对象的时候,其实之前的对象已经没有用了,可以忽略他。这里的机制是含有令牌的对象负责释放内存的资源,在对象复制的过程中传递令牌,所以直到最后一个对象也完成使命析构的时候,析构函数才会释放资源。当然令牌不是唯一实现的方法,比如Windows内核对象的分配释放,还有Python中的垃圾回收都用了另一个机制,就是引用计数(每产生一个指向同一块内存的对象时候,增加计数,析构时候计数减1,然后计数为0时候释放对象)
缓存的类型有三种,数据包缓存,帧缓存和图片缓存。数据包缓存和帧缓存的处理其实相似,可以分开实现也可以合并,然后图片缓存类需要包含一些图片的基本信息,这样处理起来就很方便了。
最后是用一个Clist构建一个内存池类,然后定义几个内存池,以便后面用就可以了。
2.Windows环境下H.264解码器方案
解码标准是开源的,所以有实力的公司都会实现自己的解码库。恩,我们当然不会去做这样的事情,时间精力都不够,所以只能寻找开源的软件咯。开源H264解码器里最有名气的莫过于ffmpeg了,他不仅仅是264解码器,而且是音视频一系列的解决方案,而且是免费的,获取很方便,还提供SDK,对于小型项目组做视频处理绰绰有余了。(偷偷爆料下,据说暴风和QQ影音也是用的他的解码库,但没开源,所以上了ffmpeg的黑名单)由于我接手项目之时有一个解码器的代码,当时那个是在网上下的,某牛人将其移植到VC++下的。开始我并没有研究这部分,直到程序跑通后,发现这个解码库的效率实在是不给力。于是开始寻找方案,最后找到一个方法就是Windows下使用MinGW+MSYS 编译ffmpeg库,然后链接到VC++里使用,当时第一次使用外部库,遇到各种链接错误,反正纠结了1周后,各种查资料,一天神奇的链接通过了。可以用了,一试,原来需要几十毫秒的解码操作一下变的几乎不需要时间了,就这样解决一大问题。
链接时候让我明白了Extern “C”到底是神马。。。还有就是Lib ,DLL到底怎么用,怎么生成,当时找了好几本VC++的书还有Windows编程的书去学习这部分内容。终于最后不负众望啊,但现在也忘记了很多了。
3.网络传输类和连接控制类
网络部分其实根据需求很明了,一个TCP传控制命令数据,一个UDP传视频数据。都需要用到CSocket类,这是一个非线程安全的,使用起来要小心,尤其跨线程的时候。我尽量避免跨线程,如果一定要跨,就要用到Dettach和Attach这些函数,我没了解过。。
首先设计UDP类,比较简单,直接就是在OnCreate OnRecieve里实现几个功能就好了,因为客户端不需要发送任何东西。只要接受数据包,然后把收到的数据包放入前面提到的数据包缓冲池就行,没其他工作了。
TCP就比较多事情了,但大部分都是接收和处理控制命令。这部分还需要和设备交互,包括验证设备端合法性,传递当前接受到的数据量,通过心跳包互相保持连接。
然后是一个主控制类,现在想想,这个类完全可以用单例模式去实现。当时还不懂这些啊。先讲了各个部分关系吧。其实每一路视频都对应一个UDP连接(视频数据)和TCP连接(控制命令),(一路视频在界面上还对应一个Picture Control控件)然后由这个主控类管理所有连接,它是协调连接和界面显示的桥梁。(第一天先到这里,改日继续)
4.多线程的思考
并行计算是个热门的话题,远的不说,单线程的程序已经不可能适应需求了。就拿这个项目来说,需要支持多路视频,必然需要多个线程,一路视频涉及到:数据接收-数据包处理-数据帧的合并-解码-播放显示。数据接收和数据包处理可以有CSocket帮你完成,但其他几个就不行了,尤其是解码这样的需要大量耗费CPU时间的,如果将他们三个合并在一个线程中,必然导致各种延迟和播放不流畅的问题。所以必须分开。
一个解码线程,一个合帧线程。线程最大的问题其实不是分割业务逻辑,因为这部分很简单,最难的是如何做到并发的同时不出现各种问题,比如死锁,或者读写脏数据,或者干脆就是崩溃了。除此之外还有线程的正常退出,因为线程之间需要利用一些公共的资源,有时候一个线程退出了,他释放了另一个线程还在使用的资源,结果就崩溃了,这个很常见。所以线程可以说全程都是问题。
线程由于不是语言相关的,而是系统相关的,C++语言对线程的支持很补给力,必须用系统的,据说新的C++标准将引入Boost的线程库,如果真的如此用C++写多线程程序将十分方便了。但现在不行。至于线程的使用,这篇文章写的非常情况,并且把Linux和Windows的线程函数进行了比较,我就不多说了。
http://www.ibm.com/developerworks/cn/linux/l-cn-mthreadps/ 多线程编程
我印象中使用的最多的就是WaitForSingleObject 还有CMutex类的Lock和Unlock,当然还有创建线程和退出等函数了。多线程程序最麻烦的就是调试,所以没写过的人确实不能理解为什么线程的复杂和重要性啊。
5.DirectDraw实现绘制BMP位图
这个部分,说来惭愧,我并没有花很多的时间去研究DirectDraw,毕竟当时是实用主义的思想,所以在罗素工作室的网上看到一个直接可用的最简单的DirectDraw实现BMP位图的类,然后修修改改就上线了。DirectX是Windows平台强大的图形工具,以后搞Windows开发的基本都离不开,我心爱的星际1,2都是DirectX开发的。。如果是Linux环境就考虑OpenGL OpenCV。
这里也需要提下为什么必须用DX来实现视频显示,因为传统的Windows绘图API是高度依靠CPU的计算的。而不使用内存显存的模拟作用来提高绘图性能,所以它仅仅使用于传统的窗体程序,对于游戏-视频监控,这样的多图形处理的程序,完全跟不上节奏了。这个一度成为我那程序的瓶颈,所以当时基本上属于,遇到一个问题解决一个,而不是一开始就有一个明确的预见性和规划,防止这类事情的发生。
推荐网站罗素工作室,有各种视频处理行业的新闻,还有一些技术文章,帮我解决了很多问题:
6.各种界面可用性和功能设计
这部分我就做了几个基本的,比如全屏幕,比如多路支持,已经动态多路支持,还有就是用Skin++库美化了下界面,还有常规的,配置管理,本地播放。。。╮(╯▽╰)╭,其实就是想一点做一点的,也没任何规划。
总结:这个项目确实很综合,仅仅客户端部分就需要解决很多问题,使用到多种编程技巧,所以这个项目让我学习了很多知识,在此需要感谢下老师。恩,先谢国家。所以好的程序员都是写出来的,如果一直看理论书籍不动手,还不如不看。