做了将近三年时间的视频监控客户端开发,当然期间也做个一些其他开发。在开发期间,经对系统不断进行重构优化积累了一些经验,现向大家分享一下。希望以此抛砖引玉,有这方面经验的朋友也发表一下看法和见解:首先看下项目需求:
  1.最多同时支持16路音视频同时显示。
  2.窗口模式,全屏,1窗口,4窗口,9窗口,16窗口。
  3.播放控制,开始播放,停止播放,开启音频,停止音频,云台控制,Camera管理。 下面谈谈设计方案: 1.总体设计,按照码流数据流向,可以将系统分成三层:  网络层,负责码流的接收,数据的解包,以及网络故障的处理。
  解码显示层,网络层将数据解包后提交给解码层,解码层负责码流解码和图像显示。
  UI层,负责响应消息并进行处理。  处理原则:层与层之间尽量独立,比如网络层发现网络故障后,进行以下处理:  1.通知UI网络故障。
  2.自行进行网络重连。
  3.故障恢复后通往UI层网络故障恢复。  期间并不需要通知解码显示层暂停解码显示。解码显示层收不到网络层提交的码流,自然会暂停解码 1.线程模型  先前的时候我的实现方式是,针对每一个播放窗口开一个线程,在这个线程中进行码流接收,然后进行解码显示。后来发现有诸多问题,其中最严重的就是解码耗时过长导致发送端经常出现发送超时的情形。  现在的实现方式是针对以上各层实现三类线程:
  
  网络数据接收线程:负责网络数据接收,网络连接建立。由于最大只有16路码流数据,使用select进行多路复用足够满足我的需求。网络层还负责对码流数据进行解包和校验,测试证明进行数据解包和校验并不影响网络层的数据接收实时性。  解码显示线程:负责数据的解码和显示,开始我分别使用Semaphore和Event进行线程同步,这样造成了诸多不便,首先程序退出时需要SetEvent唤醒解码线程,暂停和开始播放也需要进行特殊处理,后来我采用最简洁明了的方式:用一个Sleep(1)解决了诸多同步上的问题。
while(!bExit)
{
if(bPlay)
{
  if(有数据)
解码显示一帧数据;
  else 
Sleep(1);
}
}
   UI线程,主要负责消息响应,UI线程在任何时候都不能阻塞,比如登陆操作直接提交给网络层,网络层收到登陆响应后再通知UI。   2.数据模型   网络层收到数据经解码后得到一帧一帧数据,由于每帧数据长度非常大(I帧比P帧长的多),定义一个固定长度的缓冲区存放一帧数据不是很好的做法。使用链表则会不断的分配和释放内存,造成内存碎片。使用循环缓冲区会增加一次数据拷贝。为此我对循环缓冲区进行扩展:
   
   每次写入一帧数据,写入一帧数据之前先写入该帧数据的长度。
   如果到了缓冲区的末尾,无法容纳一帧完整的数据,直接跳到缓冲区的开始位置写入数据。当然需要进行一些特殊处理。   3.其他方面:
   使用Directshow框架可以方便解码显示,Directshow采用DirectDraw进行图像显示,效率有明显的提升。
   Directshow的窗口模式比无窗口模式要容易控制,比如针对某个窗口进行显示和隐藏。
   全屏播放的最好方法是:先将窗口最大化,置顶,然后隐藏窗口的标题栏,边框,窗口上的其他控件,取消全屏进行逆操作即可。   4.实现难点   我觉得最难得是音视频同步,因为接收的是实时码流,对时延要求较高,不能使用较大的缓冲,所以造成诸多问题,解决这些问题发了我相当大一部分时间,这个留作下回再做讨论吧。   大家有问题可以回复该帖,我会尽力答复。视频监控

解决方案 »

  1.   

    1,每个层用的什么框架?应该是用的开源的吧,回答最好带个链接,能下到,便于下载学习。2,硬件解码还是软件解码,支持的格式多么?是用了ffmpeg么?若是,使用时有什么小技巧和心得。3,一些硬件的接口是怎么样的?如摄像头,控制台。
      

  2.   


    每个层的代码都是我们自己写的,没有采用开源框架。不过保存和查询码流使用了开源数据库sqlite3
    我们采用的是软解码,支持h.264和AVS,解码器用的是ffdshow封装的ffmpeg,不过对264我也使用CoreAVC解码。
    视频监控客户端直接从网络上接收码流,采用的是我们公司自己定制的协议。
      

  3.   

    使用select方式能应付25,36窗口吗?