ffmpeg视频云转拉过程中耗时分析与优化

故事的背景是这样的:

在整个视频云的流程中(对于冷流整个流程是比较多的),其中有一个环节是转拉。转拉就是从源站拉流,然后推流到目的服务器上的过程。转拉的技术方案是有多种的,此处我们选择的是用ffmpeg来进行转拉。该环节在优化前的耗时在1.7s以上,经过优化后,目前大概耗时在600+ms左右。值得说明的是,此处的耗时是开始启动ffmpeg到最后和目的站建立连接的过程。

过程分析:

既然是要进行时间的优化,那么首先肯定需要知道ffmpeg中每个调用环节的耗时。首先我们分析下ffmpeg源码。需要说明的是下图的流程只是ffmpeg.c源码中的一小部分,且ffmpeg的版本为3.4.3. 不同的版本可能存在细微的差别。此外,我们主要的优化是从和源站建链到和目的站建连的过程。所以下图的分析虽然不够全,但是对于本次的分析已经足够了。关于更多的ffmpeg源码分析,请参考本文末尾所贴的参考文献链接。

图1, ffmpeg.c 部分源码

从main函数进入,到最后和目的站建立连接,主要是两块函数的调用,av***_register_all 和 ffmpeg_parse_option. 之所以强调了register函数,是因为ffmpeg编译的时候,decoder,encoder以及filter等等都是可选的。不同的编译参数编译出来的ffmpeg大小是不同。 初始的时候,我们以为减小ffmpeg编出来的大小,会有助于加快转拉的速度,事实上并没有。register函数做的事情挺简单的,主要是往链表后面追加一点点的数据,执行过程是非常快的,几乎可以忽略。

那么我们的重点自然就到了ffmpeg_parse_option这个函数中了。这个函数的作用就是解析我们的命令行参数。然后调用open_file函数,分别打开输入文件和输出文件。通过逐步打日志,我们发现,整个耗时主要就集中在两个函数:avformat_open_input和avformat_find_stream_info。其中avformat_open_input 函数的调用相对简单,主要是调动avio_open2与源站建立连接耗时比较严重。通过大量的转拉案例,我们发现,正常情况下,这个建链的过程几十毫秒就能完成。但是建链这个过程主要是网络的相关,在ffmpeg层面可改动优化的空间并不大。所以这里也没过多进行优化,实际上如果想优化,也是有一定的优化空间的。打日志发现,init_input中除了avio_open2耗时之外,av_prob_input_buffer2偶尔也会比较耗时,通过这个函数名字我们也可以知道,既然是探测,当然是可以缩短一些探测的时间的。这里不做深入的讨论,因为大量例子发现,多数时候,avformat_open_input建立连接都是很快的。

事实上最耗时且可优化的是avfomat_find_stream_info函数。

图2, avforamt_find_stream_info函数源码

查看源码可以发现,在avformat_find_stream_info中有个无限循环,该函数的调用耗时也主要是在这个循环里面。这个函数里面会调用read_frame_internal 与 try_decode_frame 进行一些分析和探测,当达到某种条件的时候,就会break,跳出循环。其实我们可以逐个分析循环中break的地方, 如果循环能够尽早break掉,那自然就会减少整个循环的调用时间了。

1) ff_check_interrupt 导致的break。 这个地方显然从代码层面上,没啥可进行改动的。

2) 如下图,有header信息,并且所有的流都解析到了退出。

图3 for(;;)

3)读取数据的大小达到上限,需要说明的是probsize这个值是可配的。命令行参数中指定 -probsize 就行,或者在options_table中avformat_options[]数组中直接设置默认值也是可以的。

图4, for(;;)

4)分析的时间音视频帧时间戳达到了上限,这里的时间上限值也是可设置的。可以命令行指定 -analyzeduration参数,或者options_table.h中设置默认值。

图5, for(;;)

对于第三四处的break, 其实意思是相同的。但是简单的将上限值缩小是不可取的。主要是两方面的原因:1)测试发现,循环并不是因为达到了上限值才退出的;2)通过缩小上限值退出循环,可能导致本来是音视频两条流的,最后推出去的流只有一路。这个情况在某个客户的转拉的过程中就出现了。该客户源站吐流前面几秒钟都是音频数据,并且header信息里面也没有视频帧,当达到阈值退出时。可能只分析到了音频帧,以至于后面即使来了是视频帧,ffmpeg也会将其丢弃,最后导致推到目的站的流是纯音频的。

到此也可以猜到了,我们的改动主要在第二处break处了了。图2中,有个变量,fps_analyze_frame_count,我们可以看到默认值为20,并且这个for循环里面还有注释,"检查一个编解码器时候还需要被处理",这个变量即使从变量名也能猜到他是要干嘛的——分析帧的数量。这个默认值是比较大的,特别是对于我们的直播转拉环节。所以在此我们适当的减小了这个值。在实际项目中,在确定了有两条流的情况下,我们将音频帧的分析帧数设置为10,视频帧设置为2. 当然这个值的选择参考意义可能不是特别大。用户可以根据不同的需求,自己设置,然后进行测试。

至此,本次分析就差不都结束了。下面展示下实验的结果。

测试结果

测试结果记录了优化前后,每次转拉的平均耗时。因为刚开始是在一台正式环境上测试的,所以数据量有限,另外由于我们的重点是关注优化后的数据,所以优化前相较于优化后的转拉次数是比较少的。

优化前的数据

图6, 优化前的结果

图6是优化前的转拉耗时,总共有记录590条,此处只截图了其中50条记录。图中总共有4列数据时间,单位都是ms。第一列是调用avformat_open_input的耗时,第二列是调用avformat_find_stream_info的耗时,第三列是从和源站建立连接到和目的站建立连接的耗时,即两个avio_open2之间的调用间隔,第四列是从main函数开始到调用与目的站建立连接的avio_open2函数的耗时。可以看到大部分总体耗时都是在几百毫秒内,偶尔会有几个耗时比较多的。

图7, 优化前平均耗时统计

通过对着590条转拉记录统计平均值,我们发现大概在1700+ms。

优化后的数据

图8, 优化后的结果

同样我们也贴上优化后的50次转拉耗时,第一列是流id,可以不管。后面的4列和优化前的4列一一对应。

图9, 优化后平均耗时统计

这些数据是目前一台线上机器上的数据,因为本次优化已经上线了几天了,所以数据相对来说多点,有24000+条数据,平均耗时612ms左右。我登录了几台线上的机器,统计发现差不多都是在 600+ms左右。

整片文章到此就算完结了

参考文献

https://blog.csdn.net/leixiaohua1020/article/details/39760711

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏网络

ASLRay:一个可以绕过ASLR的工具

ASLR(Address Space Layout Randomization,即地址空间格局随机化)是指利用随机方式配置数据地址,一般现代系统中都加设这一机制...

22610
来自专栏腾讯Bugly的专栏

手游热更新方案xLua开源:Unity3D下Lua编程解决方案

导语 xLua是Unity3D下Lua编程解决方案,自2016年初推广以来,已经应用于十多款腾讯自研游戏,凭借其出色的性能,易用性,扩展性而广受好评。 而就在前...

3196
来自专栏美团技术团队

MyFlash——美团点评的开源MySQL闪回工具

由于运维、DBA的误操作或是业务bug,我们在操作中时不时会出现误删除数据情况。早期要想恢复数据,只能让业务人员根据线上操作日志,构造误删除的数据,或者DBA使...

35312
来自专栏AI2ML人工智能to机器学习

Shiny: R语言来建立开源交互式数据分析微服务的神器

先来说个应用场景: 假设你需要快速Prototype一个数据分析的服务, 而且需要给业务客户一定的自由度来理解数据分析的强大, 例如更换数据, 更换分析手段。 ...

1083
来自专栏北京马哥教育

快学学Python异步IO轻松管理10k+并发连接

异步操作在计算机软硬件体系中是一个普遍概念,根源在于参与协作的各实体处理速度上有明显差异。软件开发中遇到的多数情况是CPU与IO的速度不匹配,所以异步IO存在于...

3106
来自专栏张善友的专栏

Google SiteMap Protocol协议

在新浪看到这样的新闻Google雅虎微软联手支持网页手工提交标准, Google、微软和雅虎认为,统一标准有助于从整体上改进站点地图,从而搜索引擎可以将更广泛的...

20910
来自专栏野路子程序员

徒手解剖composer,简单了解其实现过程

2766
来自专栏大数据人工智能

ZStack--工作流引擎

在IaaS软件中的任务通常有很长的执行路径,一个错误可能发生在任意一个给定的步骤。为了保持系统的完整性,一个IaaS软件必须提供一套机制用于回滚先前的操作步骤。...

4364
来自专栏玉树芝兰

如何用iPad运行Python代码?

(由于微信公众号外部链接的限制,文中的部分链接可能无法正确打开。如有需要,请点击文末的“阅读原文”按钮,访问可以正常显示外链的版本。)

1722
来自专栏BeJavaGod

使用ztree.js,受益一生,十分钟学会使用tree树形结构插件

看到ztree.js,这几个字眼,毋庸置疑,那肯定就是tree树形结构了,曾经的swing年代有jtree,后来jquery年代有jstree和treeview...

3714

扫码关注云+社区