Vision开发日志5

  哈哈哈,今晚成功实现了Unity接收树莓派的实时视频流。

  在昨天的mediamtx的方案不好使之后,今天白天依旧苦思冥想,应该要怎么实现树莓派推流Unity拉流的功能。

  在早上马原课的时候,突然想到能不能试试回归之前的办法:在Unity上使用自定义协议来接收树莓派的视频流。依旧是给每一帧数据加上帧头,Unity在收到帧头之后开始读取帧数据,然后渲染输出。

  不过这一次为了避免昨天遇到的帧不同步的问题,我将之前的4字节帧头修改为8字节帧头。其中前4个字节为自定义的帧头标识,后4个字节为预测帧长。Unity端在开始下一帧渲染之前需要根据前4个字节的帧头标识来判断当前接收到的是否为帧头。如果接收到的不是帧头而是1024字节的帧分片的话,则丢弃这一个数据,直到接收到的是8字节的帧头。然后Unity端才开始接受帧分片并拼接进帧缓存中。当缓存中的帧长达到预测帧长的时候,就说明这一帧接收完毕了,开始渲染画面。渲染完成后,继续等待下一个帧头。如此循环往复,Unity就能实时输出视频流画面了。

  但是为了让这个算法能够顺利进行,需要限制一下视频画面的大小以减小数据量。原本的计划是想设置视频画面为64 * 32像素,这样的话一个视频帧就是64 * 32 * 3=6144字节(不包括8字节的帧头)。一帧只需要分成6个片段即可。但是今晚我在树莓派上设置摄像头的时候发现height参数最小只能设置到64,这样的话我的视频流画面就不得不设置为128 * 64像素了,一帧的数据量变成了128 * 64 * 3=24576字节,比设想要大了4倍。因此为了确保流畅的传输,我保守地给视频的帧率设为了20。

  在树莓派端配置好传输的Python代码后,我简单修改了下昨天的Unity端的接收算法。然后开始运行,树莓派推流,Unity拉流。居然顺利运行了,woc。没想到会如此顺利,不过Unity显示的视频流画面有点奇怪,我的脸居然是蓝色的,我估计是rgb编码上的问题。但是目前的主要矛盾不是这个,而是先确保视频流的稳定传输。

  在Unity的编辑器中是基本上可以稳定传输了,于是我将测试项目打包发给我的舍友。看看在别人的主机上能否正确运行。当程序启动时候,舍友的Unity程序竟然卡死了。这显然是我没有预料到的。于是我不得不继续修改我的代码。原以为是舍友电脑的问题,没想到在我电脑上用打包后的Unity测试也是这样。于是我又怀疑是打包导致的问题。结果当我在Unity编辑器中准备测试时,我的Unity也卡死了!

  在我多次调试无果后,不得不求助ai。原来udp客户端的接收函数是一个同步阻塞方法。当没有udp数据到达时候,接收函数会一直在那里等待数据,导致主线程无法继续进行而让整个程序卡死。而我之前几次的测试包括在舍友电脑上测试卡死那次,都是因为我还没启动树莓派摄像头,Unity端一直在等待数据才会让程序卡死。所以为了解决这个问题,我需要将接收数据的过程放在子线程。

  于是我参考ai给出的方案对算法稍作修改:将udp数据的接收放入子线程,接着定义了一个字节数组的队列用于存放接收到的udp数据,然后在主线程里面不断访问这个队列将里面数据取出处理,处理的过程和之前是一样的,判断帧头然后缓存帧数据,便不再赘述了。最后重新打包测试,这下程序果然就不会卡死了。

  到目前为止我已经基本实现了视频流的低延迟传输,但是有关RGB色彩的问题还需要解决。不过我估计这个解决起来应该会比较方便。至于目前视频流传输是av画质,这个目前只能做出妥协,也许之后会优化,可能是这一代Vision,也可能是下一代Vision。

  总之,实现视频流传输是Vision的一个关键技术突破,Vision也因此叫作Vision。