从Linux设备上的USB摄像机捕获视频

背景
前段时间,我很想从著名的“坦克大战”中“改进”一辆坦克,增加了像我是坦克手一样的比赛能力。阅读了有关哈布雷的几篇文章后,这个想法就出现了(例如,在这里:geektimes.ru/post/257528),在其中我发现了如何使用小型WiFi路由器和USB摄像头来完成此操作。解决方案看起来非常简单:路由器使用特殊固件进行刷新,将摄像机连接到其上,储罐由本地遥控器控制,并且可以在浏览器中查看视频。快速组装好原型后,我发现视频的录制质量令人作呕。它是320x440x30或640x480x30。开启1280x720模式时,充其量是一部带有伪像的撕裂视频,最糟糕的是根本没有。1920x1080模式原则上不起作用。这让我非常不高兴,因为在PC上,相机支持高达1920x1080x30的模式,并且具有硬件MJPG压缩。我的直觉表明,实现还远远不够完美。目标
- 具有FullHD(1920X1080)或HD(1280x720)分辨率和正常帧速率的视频(以便可以播放)。
- 我计划将玩具送给孩子,所以我需要自动启动并支持连接/断开相机的连接。
总的来说,我想要这样的东西:
局限性
我不会寻找一个始终适用于任何地方的解决方案。以下限制非常适合我:- 好的wifi信号。
- 连接数量有限,只有一个客户端时优先考虑。
- 相机支持MJPG模式。
硬件和软件
- Logitech B910高清摄像机(http://www.logitech.com/zh-cn/product/b910-hd-webcam)。
- 路由器TP-LINK TL-MR3020。这个孩子有以下硬件:CPU MIPS 24K 400MHz,RAM 32 MiB,Flash 4 MiB,以太网100 Mbit,USB 2.0(http://wiki.openwrt.org/en/toh/tp-link/tl-mr3020)。
- . OR-WRT (http://roboforum.ru/wiki/OR-WRT), OpenWRT (http://openwrt.org/, 12.07 15.05).
- . , .
- “ ”.
通常,这是一个非常弱的配置,尤其是当您回想起YUV420格式的1920X1080大小的帧占用4 MiB(每像素2个字节)时。我被鼓励使用该相机支持硬件MJPG压缩。实验表明,压缩的FullHD帧通常小于500 KiB。所以我决定继续研究。事实证明,要捕获视频并通过HTTP进行流传输,使用了mjpg-streamer(http://sourceforge.net/projects/mjpg-streamer/)。对他的代码的分析表明,他使用1个流来捕获视频+为每个客户端使用一个单独的流。这不是单核系统的最佳解决方案,因为它需要线程同步和每个线程的堆栈内存。他还复制了捕获的帧。通常,mjpg-streamer成为嫌疑犯#1。有趣的发现
研究mjpg-streamer时,我发现Linux上的视频捕获是使用v4l2库完成的,并且使用缓冲区队列来捕获它。在调试mjpg-streamer中的这些缓冲区的初始化时,我发现即使对于MJPG模式,它们的大小也非常大,并且意外地与未压缩帧的大小一致。因此,我开始怀疑我是否必须进入负责支持摄像机的UVC驱动程序代码。驱动程序代码分析和首次成功
通过研究代码,我得出的结论是,缓冲区的大小由相机询问,而我的相机返回了未压缩帧的大小。从相机开发人员的角度来看,这可能是最安全的解决方案。但这也不是最佳的。我决定针对我的情况,可以使用实验性最小压缩率来调整所需的缓冲区大小。我选择k = 5 有了这个值,我的利润约为20%。UVC驱动程序代码已准备就绪,可以添加各种“特殊”解决方案,并且我很容易找到了调整缓冲区大小的地方(函数uvc_fixup_video_ctrl())。此外,驱动程序支持一组怪癖,可让您支持与UVC标准有各种偏差的相机。通常,驱动程序的开发人员已尽力支持动物园的相机。通过添加缓冲区大小校正,我在1280x720模式甚至1920x1080模式下都可以稳定运行。万岁!问题解决了一半!寻找新的冒险
我对最初的成功感到有些满意,我记得mjpg-streamer远非完美。当然,您可以做一些简单的事情,不像mjpg-streamer那样通用,但是更适合我的情况。因此,我决定制作uvc2http。在mjpg-streamer中,我不喜欢使用多个流和复制缓冲区。这就决定了解决方案的体系结构:1个流,没有副本。使用非阻塞IO,这非常简单:完成捕获帧并将其发送到客户端,而无需复制。有一个小问题:当我们从缓冲区发送数据时,我们无法将缓冲区返回到队列。并且当缓冲区不在队列中时,驱动程序无法将新帧放入队列中。但是,如果队列大小> 1,则有可能。缓冲区的数量确定可以保证服务的最大连接数。也就是说,如果我想保证支持1个客户端,那么3个缓冲区就足够了(驱动程序写入一个缓冲区,数据从第二个缓冲区发送,第三个缓冲区有库存,以避免在尝试获取新帧时与驱动程序争夺缓冲区)。Uvc2http
Uvc2http由两个组件组成:UvcGrabber和HttpStreamer。第一个负责从队列接收缓冲区(帧),并将其返回到队列。第二个负责通过HTTP为客户端提供服务。还有一些链接这些组件的代码。可以在源代码中找到详细信息。意外的问题
一切都很棒:该应用程序正常工作,并且以1280x720的分辨率产生了20+帧/秒的速度。我对代码进行了外观上的更改。在另一批更改之后,我测量了帧速率。结果令人沮丧-少于15帧。我急忙寻找导致退化的原因。我可能花了2个小时,在此期间,每次测量的频率都会降低到7帧/秒的值。关于路由器由于长时间工作而导致的性能下降,我想到了不同的想法。这是难以理解的。在某个时候,我关闭了流传输,看到只有一个捕获(没有流传输)给出了相同的7帧。我什至开始怀疑相机有问题。一般来说,有些废话。当时是晚上,相机打开了窗户,露出灰色的东西。为了改变阴暗的图像,我打开了房间内的相机。瞧!帧速率提高到15,我了解了一切。相机会自动调整曝光时间,并且在某个时候变得比给定频率下的帧持续时间更长。在这两个小时内,发生了以下事情:首先,天色渐渐黑了(是晚上),然后我把照相机照进了光亮的房间。将相机对准枝形吊灯,我得到了20+帧/秒的速度。万岁。- . 1-1.5 .
- . , , qv4l2, . : - . .
- . USB , ( ) ( ). USB ( ).
- 路由器的内存和磁盘空间很少。因此,我拒绝了OR-WRT并编译了我的OpenWRT映像,从中删除了所有多余的内容。
结果
以下是比较mjpg-streamer和uvc2http的结果的图板。简而言之,内存消耗显着增加,而帧速率和CPU利用率则获得小幅增长。 | 1280x720 | 1920x1080 |
| VSZ,KB,1个客户端 | VSZ,KB,2个客户端 | CPU,%,1个客户端 | CPU,%,2个客户端 | FPS,f / s,1个客户端 | FPS,f / s,2个客户端 | VSZ,KB,1个客户端 | VSZ,KB,2个客户端 | CPU,%,1个客户端 | CPU,%,2个客户端 | FPS,f / s,1个客户端 | FPS,f / s,2个客户端 |
Mjpg流媒体 | 16860 | 19040 | 26 | 43 | 17.6 | 十五 | 25456 | 25812 | 28 | 五十 | 13.8 | 10 |
uvc2http | 3960 | 3960 | 26 | 43 | 22 | 19.6 | 7576 | 7576 | 28 | 43 | 15.5 | 12.2 |
当然还有我和孩子们一起制作的视频:产生的坦克的照片(结果像是一辆吉普赛车):
使用
资料在这里。要在PC Linux上使用,只需要编译即可(前提是您不想修补UVC驱动程序)。该实用程序以标准方式使用CMake构建。如果需要在OpenWRT中使用它,则需要采取其他步骤:- 将OpenWrt-15.05目录的内容复制到OpenWRT存储库的根目录。这些文件仅适用于OpenWRT 15.05。他们描述了OpenWRT的新软件包和UVC驱动程序的补丁。
- , quirk UVC_QUIRK_COMPRESSION_RATE uvc_driver.c. UVC. , wiki.openwrt.org/doc/devel/patches. uvc_ids. :
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x046d,
.idProduct = 0x0823,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_RESTORE_CTRLS_ON_INIT
| UVC_QUIRK_COMPRESSION_RATE },
- OpenWRT (http://wiki.openwrt.org/doc/howto/build). uvc2http Multimedia.
- uvc2http ( ) . , .
- 在设备上安装软件包/更新系统
下一步是什么
该解决方案包括两部分:一个驱动程序补丁和另一个流算法。驱动程序补丁可能包含在新版本的Linux内核中,但这是一个有争议的决定,因为它基于最小压缩率的假设。在我看来,该实用程序非常适合在较弱的系统(玩具,家庭视频监视系统)上使用,并且可以通过添加通过参数指定摄像机设置的功能来对其稍作改进。由于在CPU负载和通道宽度上有余量(我很容易从连接十个客户端的路由器上收到50+ MBit),因此可以改进流算法。您还可以添加声音支持。Source: https://habr.com/ru/post/zh-CN386273/
All Articles