项目经验与分享(一):视频数据传输所引发的问题

一 需求分析

获取海康摄像头的视频流,封装为rtp + ps + h264格式,向目的服务器指定端口推送。从摄像头上获取视频数据可以直接使用海康的SDK,当然由于取得的数据中包含一些私有帧数据,在发送前还需要对字节码做些调整,最后使用udp推送。根据用户提出的接口规范,另外还需要附加一些内部字段。所以需要在将收到的数据首先缓冲下来,在调整完成后分段发送。

二 问题描述

第一版程序大约花了1周时间完成,公司内部随便找了一个摄像头,发送端使用VLC播放器惊奇的发现居然可以播放。但是当部署到用户的服务器后,用户的播放端只能播放几路视频。大多数摄像头要么花屏要不没有图像。由于此现象在公司内部的测试环境中无法复现,因此只能通过发布测试版本的方式直接到用户的生产环境去观察效果。起初,技术人员在用户的接收端使用抓包软件(wireshake)进行视频流分析,发现在传输过程中大约丢失了30%到50%的视频帧。而在发送端的抓包软件显示从摄像头获取的丢包率低于0.2%。考虑到数据接收使用的海康SDK,而发送过程也仅仅是基于socket的udp推送。因此项目组认为可能是在海康的SDK回调函数中同时需要对字节码进行处理并最后完成分段发送,整个处理过程都使用一个线程,因此拖慢了数据接收造成缓存溢出。

第1套解决方案是将接收和发送分开到两个线程进行处理。利用队列(Queue)接收线程在接收到完整帧后需要首先复制一份然后从队列前端插入,发送线程则是循环从队列的末尾获取。考虑到线程安全,需要在插入数据前在同步量上加锁,插入完成后解锁。另一个线程的取数据操作也时一样。改造完成后,我们再次部署到用户的服务器上。仅仅从抓包的数据显示,接收端的丢包率似乎略有降低。但是播放端的效果丝毫没有变化。

考虑到第1套解决方案在保障线程安全的同时存在两次加锁和解锁的动作,它们都比较消耗系统资源,因此第2套解决打算采用Linux系统上特有的管道(pipe),接收线程在无锁状态下直接将数据插入队列,然后利用管道发送一个信号(signal)。子线程在收到信号前保持阻塞状态收到信号后才将数据读出,读取完成后立刻利用另一条管道向主线程发送信号,主线程在收到信号后再次将所有缓冲的数据插入队列,而子线程则完成发送。第2套方案完成后,我们再次部署到用户的服务器上。结果该花屏的摄像头依然花屏,甚至还有几个之前能够稳定播放的摄像头也时有花屏现象。

至此,似乎已经找不到效率更高的方式,而整个项目也已经拖延了大约2周。最后我开始考虑,会不会根本就不是处理和发送造成的缓存溢出,而是在分段发送的时候由于没有任何停顿造成udp数据经过多次路由转发后到达接收端的顺序不一致所造成的呢。考虑到这个可能性后,我决定首先回退到最初的单线程版本,只在分包发送的时候增加了1/1000秒的停顿。但是这个修改方案没有告知现场的测试人员,只是告诉他们这个版本只用来测试。部署到客户的服务上后所有的摄像头都可以正常播放,既没有出现花屏现象也没有出现画面丢失。问题解决!

三 问题分析

首先,按照数据存储和读取的速度,从高到底的排列顺序大致是 一二级缓存 > 主存(内存) > 辅存(硬盘)> IO设备(网卡)。设想一下,如果我们需要从网上下载一部4K高清的电影,大概需要30分钟,电影的播放时长大概是90到120分钟。如果我们只是需要在线收看这部电影,播放器很轻松就可以完成从接收到解码再到渲染整个过程,设想一下:如果数据处理速度比数据接收速度慢,一定会发现播放时间越长电脑内存就被占用的越多。说明视频数据在内存中的运算速度一定远高于网络传输速度,因此绝不会出现所谓接收端缓存溢出的情况。

其次,udp协议属于传输层,如果发生数据包丢失只可能发生在更加底层的数据链路层或本层。利用抓包软件进行数据分析只能算是辅助手段,应用层不可能准确计算出来。wireshake等软件判断视频数据丢包可能仅仅是因为没有正确识别。解决问题需要有严谨的逻辑分析和扎实的基本功,不能仅仅依靠测试数据就想当然的做出判断。

最后,一定要有与开发配套的测试环境,即使不能完全模拟生产环境也应该要具备生产环境中大多数的特点,例如网络情况、设备型号等应该一一对应。如果只是按照外部提供的接口规范就进行盲目开发,最后往往会事倍功半。

四 总结

解决故障和判断问题需要谨慎分析和严密判断。特别是在实现环节中包含网络传输等不确定因素,首先应该搭建测试环境和编写集成测试用例。在使用第三方SDK的时候,也应该做好测试工作。不能因为udp协议不保证送达就想当然的认为使用udp发送视频就一定无法被正确播放。

posted @ 2020-07-12 19:51  冷豪  阅读(696)  评论(0编辑  收藏  举报