前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ffmpeg视频云转拉过程中耗时分析与优化

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

原创
作者头像
榴莲其实还可以
发布2018-06-20 10:36:47
4.4K3
发布2018-06-20 10:36:47
举报

故事的背景是这样的:

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

过程分析:

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

图1, ffmpeg.c 部分源码
图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函数源码
图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 for(;;)

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

图4, for(;;)
图4, for(;;)

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

图5, for(;;)
图5, for(;;)

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

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

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

测试结果

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

优化前的数据

图6, 优化前的结果
图6, 优化前的结果

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

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

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

优化后的数据

图8, 优化后的结果
图8, 优化后的结果

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

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

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

整片文章到此就算完结了

参考文献

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

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 故事的背景是这样的:
  • 过程分析:
  • 测试结果
    • 优化前的数据
      • 优化后的数据
      • 参考文献
      相关产品与服务
      云直播
      云直播(Cloud Streaming Services,CSS)为您提供极速、稳定、专业的云端直播处理服务,根据业务的不同直播场景需求,云直播提供了标准直播、快直播、云导播台三种服务,分别针对大规模实时观看、超低延时直播、便捷云端导播的场景,配合腾讯云视立方·直播 SDK,为您提供一站式的音视频直播解决方案。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档