我正在开发一个原型应用程序,主要是用Python(2.7)编写的一个研究项目。应用程序通过UDP从无线设备接收mpegts视频流,然后使用openCV处理/分析视频帧。 mpeg解压缩是在我自己的C库中完成的,它基本上只是围绕libavcodec的Python绑定包装器。该应用程序是一个Python GLUT应用程序,它使用GLUT进行绘图并基本处理一些事件。
我的问题是,当我的openCV处理或绘图代码对CPU征税时,我会丢弃UDP数据包,从而导致视频帧损坏。我知道UDP是一种“不可靠”的协议,并且丢弃数据包是可以预期的,但不幸的是,这是我必须使用的。
幸运的是,我有一台运行速度相当快的机器(MacBookPro11,3 2.8GHz四核i7)。我的目标是使用线程和队列创建一个系统,从而优先处理UDP数据包和视频解压缩的消耗,以便完整地接收每个解压缩的帧(除非实际的网络错误)。如果我的主线程绘制或处理无法跟上视频流帧速率,我想丢弃整个解压缩帧,以便mpeg流保持连贯。另一种方法是丢弃单个UDP数据包,但这会导致损坏的图像流在一段时间内无法恢复,即。我相信收到下一个I帧。这是我想避免的情况。
我已经创建了这样一个系统,可以生成后台线程,它可以完成创建视频解压缩上下文和UDP套接字的所有工作。后台线程无限循环地向解压缩器询问已解码的帧,这些帧依次调用回调来等待来自套接字的更多数据(使用select,poll或blocking recv,我已经尝试了所有三个)。在接收到每个新的解压缩帧后,后台线程将帧添加到队列中,这些帧在主线程上尽可能快地处理。如果队列已满,因为主线程消费者无法跟上,那么新解压缩的帧就会被丢弃并继续进行。
我遇到的问题是即使使用这个系统,主线程上的负载仍然会导致UDP数据包被丢弃。这几乎就像接收和缓冲传入的udp数据包的内核数据包调度程序正在我的主线程上运行,它正在进行视频帧的所有绘制和处理(我只知道我在这里谈论的是什么,重新。数据包调度程序)。如果我注释掉在主线程上运行的所有繁重的处理和绘图代码,那么我永远不会得到任何丢弃的数据包/ mpeg解码错误。
我已经尝试过最大化我的套接字接收缓冲区,这有点帮助,但这也增加了延迟,这也是不可取的,它最终只会延迟问题。
所以我的问题是,我能做些什么来确保我的所有UDP数据包都被消耗并尽可能快地传递给解压缩器,而与主线程cpu负载无关?
我已经尝试将后台线程的线程优先级设置为1.0,但这没有帮助。默认情况下,libavcodec会产生9个线程来处理解压缩,但我可以选择将其限制为1,我已经尝试过,以确保所有解压缩都发生在同一个(高优先级)线程上。看看我的cpu监视器,我的四核处理器上有很多开销(8个超线程处理器,我也试过打开和关闭)。
如果有必要,我很乐意以root身份进行内核调整,因为这只是一个研究项目,而不是运送应用程序。
有什么建议吗?
TIA
答案 0 :(得分:1)
Python并不直接支持设置线程优先级,但是有些人通过ctypes很幸运。但是,由于GIL的工作方式,无论如何,这可能不会给你带来最好的结果。
最好的办法可能是使用多处理将UDP线程放在一个单独的进程中,并使用队列将视频从该进程传输到主进程。
为了避免死锁,你应该在启动任何线程之前启动UDP进程(启动线程运行后启动进程是有问题的,因为线程和IPC状态没有正确复制到子进程),然后,在启动UDP进程之后,在开始其中任何线程之前,你应该lower the priority主进程。
Here is an interesting paper这不是直接的点,但提供了一些关于Python(3,不幸的是)和线程优先级的好信息。