我正在尝试使用ffmpeg将udp流转换为帧。我运行以下命令:
ffmpeg -loglevel debug -strict 2 -re -i "udp://192.168.15.50:3200?fifo_size=1000000&overrun_nonfatal=1" -r 8 -vf scale=432:243 -f image2pipe -vcodec ppm pipe:1
它适用于不同的流类型,mpeg2video和h264。核心处理此特定流的CPU负载低于30%,其低质量sd流的分辨率为640x576。
它适用于大多数时间,但有时,偶尔会发生延迟并且帧会在稍后到达。所以我想要8 fps,但有时我会更少,有时更多。
为什么会出现这种延迟?如何减少它?
更新:我尝试将其更改为:
ffmpeg -loglevel debug -i "udp://192.168.15.50:3200?fifo_size=1000000&overrun_nonfatal=1" -r 8 -preset ultrafast -fflags nobuffer -vf scale=432:243 -f image2pipe -vcodec ppm pipe:1
但我仍然遇到了这个问题。例如,在ffmpeg log中我得到:
[2016/02/11 13:32:30] frame= 7477 fps=8.0 q=-0.0 size= 2299638kB time=00:15:34.62 bitrate=20156.4kbits/s dup=7 drop=15867 ^M*** dropping frame 7477 from stream 0 at ts 7475
[2016/02/11 13:32:30] ***dropping frame 7477 from stream 0 at ts 7476
[2016/02/11 13:32:30] ***dropping frame 7478 from stream 0 at ts 7476
[2016/02/11 13:32:32] Last message repeated 1 times
[2016/02/11 13:32:32] frame= 7479 fps=8.0 q=-0.0 size= 2300253kB time=00:15:34.87 bitrate=20156.4kbits/s dup=7 drop=15871 ^M*** dropping frame 7479 from stream 0 at ts 7477
如您所见,在第二个31期间,没有帧输出...并且ffmpeg报告两帧之间的时间是0.25秒
答案 0 :(得分:14)
ffmpeg命令通常用管道传输到另一个二进制文件中。该二进制文件保存了ffmpeg提供的帧,并对它们进行了一些处理。
一开始我没有使用"fifo_size=1000000&overrun_nonfatal=1"
选项,我从ffmpeg收到以下错误:
[udp @ 0x4ceb8a0] Circular buffer overrun. To avoid, increase fifo_size URL option. To survive in such case, use overrun_nonfatal option
udp://192.168.15.50:3200: Input/output error
然后ffmpeg会崩溃。为了避免这种情况,我添加了"fifo_size=1000000&overrun_nonfatal=1"
,正如ffmpeg建议的那样。
但是,在使用这些参数后,我会按照问题中的描述获得时间转换,有时它也会出现帧中的工件。
如上所述,CPU没有问题,所以最初我们怀疑是udp流,特别是udp缓冲区大小:
所以我们用:
更改了udp缓冲区大小sysctl -w net.core.rmem_max=26214400
并将ffmpeg命令更改为" udp://231.20.20.8:2005?buffer_size = 26214400"
然而,这并没有解决问题。 ffmpeg仍会得到"循环缓冲区溢出"和崩溃。而且我无法重现这个循环缓冲区溢出,它只是随机发生的。
我的下一个想法是管道缓冲区大小,因为我发现了以下内容:
http://blog.dataart.com/linux-pipes-tips-tricks/
自内核版本2.6.11以来缓冲区的大小为65536字节(64K),并且等于旧内核中的页面内存。尝试从空缓冲区读取时,将阻止读取过程直到出现数据 同样,如果您尝试写入完整缓冲区,录制过程将被阻止,直到有足够的空间可用。
http://ffmpeg.gusari.org/viewtopic.php?f=12&t=624 [link now dead]
Poster1 :是什么导致这些循环缓冲区溢出?我的假设是ffmpeg正在将输入流读入上述循环缓冲区,然后代码生成输出流也从同一缓冲区读取。当生成输出的代码没有跟上它写入缓冲区的速率时,会发生溢出,对吗? Poster2 :看看源代码,看起来缓冲区因输入太快或输出太慢(慢速cpu?)而溢出。你的假设是正确的。
所以理论是我们的二进制文件没有足够快地读取管道。因此管道被阻塞,并且ffmpeg无法写入它,这导致udp fifo缓冲区溢出(ffmpeg继续读取udp INTO FIFO,但无法将其写入我们的管道中。)
我设法通过运行(在不同的终端)来证明这个理论:
mkfifo mypipe
ffmpeg -loglevel debug -i "udp://192.168.15.50:3200?fifo_size=1000000&overrun_nonfatal=1" -r 8 -preset ultrafast -fflags nobuffer -vf scale=432:243 -f image2pipe -vcodec ppm pipe:1 > mypipe
cat < mypipe > /dev/null # run this for 10 seconds, allowing ffmpeg to start. then pause it with CTRL-Z and see ffmpeg crashing because it cannot read more udp stream
接下来正在研究为什么我们的二进制文件在某些时候停止读取管道。似乎没有理由,因为通常情况下它会在事情发生后立即读入内存。
然而,它也将帧保存到硬盘驱动器,并且在某些时候(有时12分钟,有时15个小时),磁盘操作会因读/写操作而减慢(这是bcache(SSD和HDD混合,使用) SSD作为缓存))。当我从这个驱动器中并行删除数百万个文件进行调试时,我随机地抓住了这个事实。
因此,将文件写入繁忙的硬盘会暂时阻止我们的二进制文件读取输入管道。
udp循环缓冲区溢出问题和最终时间转换的原因是硬盘驱动器,理论解决方案是SSD。
这次调查大约需要3个星期,所以发布这一切,希望它至少部分地帮助将来的人。
更新:
我还发现了另一个导致同样问题的瓶颈(更换硬盘还不够),这是后端postgres插入引起的tcp套接字缓冲区溢出。
整个管道看起来像这样:
udp_videostream
- &gt; ffmpeg
- &gt; linux_pipe
- &gt; our_client_side_binary
- &gt; tcp
- &gt; our_server_side_binary
- &gt; postgres
Postgres查询有时很慢,这导致我们的服务器读取TCP套接字比we_binary推送它慢。结果,tcp套接字将被阻塞(它最大为4Mb),因此,客户端将阻塞其输入管道,并且由于该伪造,因此会出现此CBO错误。