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 删除。

发表于

我来说两句

3 条评论
登录 后参与评论

相关文章

来自专栏Python中文社区

基于Redis的Bloomfilter去重

专栏作者简介 九茶 Python工程师,目前居于广州。Github知名开源爬虫QQSpider和SinaSpider作者,经常会在CSDN上分享一些爬虫、数据等...

5388
来自专栏chenssy

【死磕Sharding-jdbc】---分布式ID

传统数据库软件开发中,主键自动生成技术是基本需求。而各大数据库对于该需求也提供了相应的支持,比如MySQL的自增键。 对于MySQL而言,分库分表之后,不同表生...

872
来自专栏逸鹏说道

SQL vs NoSQL:如何选择?

SQL 数据库: 在表中存储相关联的数据 在使用之前需要定义表的一个模式 鼓励标准化减少数据冗余 支持从多个表中检索相关数据表连接在一个单一的命令 实现数据完整...

3235
来自专栏SDNLAB

P4和POF的对比

一、简介 软件定义网络(SDN)技术的发展已经历了多年,新技术层出不穷。OpenFlow作为其中的一个代表性协议,已经进化了多个版本,并被工业界和学术界广泛接受...

4233
来自专栏用户2442861的专栏

浅谈UML的概念和模型之UML九种图

http://blog.csdn.net/jiuqiyuliang/article/details/8552956

571
来自专栏吉浦迅科技

DAY70:阅读API Reference

我们正带领大家开始阅读英文的《CUDA C Programming Guide》,今天是第70天,我们正在讲解CUDA 动态并行,希望在接下来的30天里,您可以...

1084
来自专栏铭毅天下

干货 | Elasticsearch通用优化建议

Elasticsearch开发实战的后期会遇到性能问题,包括:创建索引性能、写入数据性能、检索性能等。网上有很多结合自己实际应用场景的相关优化建议,但“对症下药...

882
来自专栏张善友的专栏

ADO.NET的弹性连接控制[ADO.NET idle connection resiliency]

ADO.NET连接SQL Server有时候联机会无故的中断 (例如闲置过久或是交易时间太长等因素),这时又要重新连接,在.NET Framework 4.5之...

1729
来自专栏java一日一条

SQL vs NoSQL:如何选择?

在前一篇文章中,我们讨论了 SQL 与 NoSQL 数据库之间基本的区别。接下来,我们我们将应用我们在特定场景中的知识来确定最佳的选择。

532
来自专栏杨建荣的学习笔记

和开发人员讨论一个业务需求和简单实现过程(r7笔记第49天)

前几天一个开发的同事来找我咨询一个问题,说是咨询,其实是开发的同事也不是非常清楚里面的逻辑,因为历史系统,历史原因,各种原因吧,所以我也是带着试试看的态度来帮助...

3144

扫码关注云+社区