最近刚完成了项目中摄像头视频网络传输这部分的工作,现在做个总结。
一、设计思路
整个装置包括上位机和下位机,它们都是搭载linux系统的树莓派。
1、程序设计思路
在上位机端:通过v4l2抓取摄像头视频流中的一帧YUV420格式的帧数据,然后使用x264进行编码,最后通过网络传输给下位机,网络协议使用TCP协议。
在下位机端:接收到上位机发来的一帧数据之后使用ffmpeg解码,然后转换为RGB24格式再通过opencv来显示图像。
2、程序实现思路
将上面的每一个操作封装为一个类,这样程序更加清晰,使用更加方便,维护起来也容易些。
二、实现过程
1、v4l2抓取摄像头视频帧数据
我所使用的摄像头是Raspberry Pi Camera V2,可以参考我的另一篇博客“使用v4l2在树莓派上抓取视频图像”。可以直接抓取到YUV420格式的数据,需要做的更改是在成功抓取到一帧数据之后不要保存为图片,而是使用x264进行编码(具体的实现的方法是将YUV420格式的数据存到变量中然后传送给x264编码函数)。
2、x264编码
x264是开源的,它有编码的函数接口,程序代码不是很难,可以参考“x264的编码示例”和“FFmpeg实时解码H264”中的x264编码部分,Windows下的程序和Linux下的程序是通用的,只是一个使用.lib静态库,另一个使用.so动态库。windows端编译程序时只需要libx264.lib文件(静态库,在程序中使用#pragma comment(lib,"libx264.lib")即可将.lib文件音·连入程序,但要设置好文件路径),运行时需要libx264.dll动态库(与.exe文件放在一起即可)。这些文件在文尾附上。关于如何在Linux下编译x264的方法,可以参考我的另一篇博客“Linux下编译x264与树莓派交叉编译X264(附加测试小程序)”。
3、基于TCP的视频网络传输
可以说这部分是最核心的部分了。整体的思路是在上、下位机建立好连接之后开始传输数据,由于每次编码后产生的数据长度是不一样的,所以每帧数据需要发送两次,第一次发数据长度,下位机接收到数据长度信息之后回应消息,接着上位机再发送数据内容,下位机接收完成之后回应上位机。然后周而复始发送下一帧。
至于为什么使用TCP协议而不是用UDP协议,是为了保证视频传输的稳定性。
4、ffmpeg解码视频
当客户端接收到一帧数据之后就会存入队列中,然后在另一个线程进行解码并显示。这之中就会使用ffmpeg进行解码,目前我也只是会用这个,具体的解释需要参考“雷霄骅”的“最简单的基于FFmpeg的AVfilter的例子-纯净版”,雷博士的一系列视频类的博客都很值得参考。
5、opencv播放视频
解码之后的视频格式是YUV420格式的,需要转换为RGB24或者BGR24格式的,具体要看编码之前是哪种YUV420,因为一共有两种YUV420,我在Windows下使用opencv的videocapture类抓取一帧视频数据之后编码再解码就需要转换成RGB24格式,而从上位机上抓取的YUV420格式的视频帧编码之后再解码就需要转换成BGR24格式,这样图像的颜色才正常。