使用ffmpeg将流转换为帧时缓冲

时间:2016-02-11 11:45:35

标签: ffmpeg video-streaming latency buffering

我正在尝试使用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秒

1 个答案:

答案 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缓冲区大小:

https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Web_Platform/5/html/Administration_And_Configuration_Guide/jgroups-perf-udpbuffer.html

所以我们用:

更改了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错误。