Twitch如何实现转码比FFmpeg性能提升65%?(下)

简介: ...

文 / Jeff Gong, Sahil Dhanju, Chih-Chiang Lu, Yueshi Shen


编者按:超过220万创作者在Twitch发布海量的视频,这对实时转码业务造成了巨大压力,Twitch团队通过优化多线程的转码服务以及Intel QuickSync的支持,实现了比FFmepg性能提升65%,并降低80%总体拥有成本。Twitch团队通过博客介绍了这一实现,LiveVideoStack对本文进行了摘译,点击『阅读原文』访问英文博客。同时,Yueshi Shen将在12月8-9日的ArchSummit 2017北京大会上详细介绍实现过程。


FFmpeg的1-in-N-out流水线。为什么它无法处理前面讨论的技术问题?


FFmpeg如何以编程方式处理需要单个输入来生成多个转码和(或)转封装输出的实例? 我们可以通过直接剖析FFmpeg最新3.3版的源代码,来了解其线程模型和转码流水线。


在顶层ffmpeg.c文件中,transcode()函数(第4544行)不断循环并重复调用transcode_step()函数(第4478行),直到其输入信息被完全处理,或用户中断执行为止。Transcode_step()函数封装了主要的流水线,并在许多其他即时步骤之间编排诸如文件I / O、过滤、解码和编码等动作。


在初始设置阶段,init_input_threads()(第4020行)函数被调用,并将根据输入文件的数量,产生一些新的线程来处理这些输入。


 
  

if (nb_input_files == 1) {
 return 0;
}
for (i = 0; i < nb_input_files; i++) {
 ...
 ret = av_thread_message_queue_alloc(&f->in_thread_queue, f->thread_queue_size, sizeof(AVPacket));    // line 4033
}


在第4033行中(如上所示),我们看到产生的线程数量完全由输入的数量决定。也就是说,这意味着FFmpeg将只使用一个线程来处理1-in-N-out的场景。


在get_input_packet()函数(第4055行)中,只有当输入文件的数量大于1时,才会调用多线程伴随函数get_input_packet_mt()(第4047行)。get_input_packet_mt()函数可以以非阻塞的方式从消息队列中读取输入帧。否则的话,我们需要使用av_read_frame()(第4072行)来每次读取并处理一个帧。


 
  

#if HAVE_PTHREADS
 if (nb_input_files > 1) {
    get_input_packet_mt(f, pkt);
 }
#endif
 return av_read_frame(f->ctx, pkt);


如果我们跟踪帧数据一直到流水线结束,我们发现它进入到process_input_packet()函数(行2591)中,该函数对帧数据进行解码并通过所有适用的过滤器进行处理。时间戳校准和字幕处理的工作也在这个函数中进行。最后,在函数返回之前,已解码的帧被复制到每个相关的输出流。


 
  

for (i = 0; pkt && i < nb_output_streams; i++) {
 ...  // check constraints
 do_streamcopy(ist, ost, pkt);    // line 2756
}


最后,transcode_step()函数调用reap_filters()函数(第1424行)来循环遍历每个输出流。reap_filters()函数的for循环负责收集缓冲区中待处理的帧,并将这些帧进行解码,然后封装到一个输出文件中。


 
  

// reap_filters line 1423
for (i = 0; i < nb_output_streams; i++) { // loop through all output streams
 ...  // initialize contexts and files
 OutputStream *ost = output_streams[i];
 AVFilterContext *filter = ost->filter->filter;
 AVFrame filtered_frame = ost->filtered_frame;
 while (1) { // process the video/audio frame for one output stream
    ...  // frame is not already complete
    ret = av_buffersink_get_frame_flags(filter, filtered_frame, …);
    if (ret < 0) {
       ...  // handle errors and logs
       break;
    }
    switch (av_buffersink_get_type(filter)) {
    case AVMEDIA_TYPE_VIDEO:
       do_video_out(of, ost, filtered_frame, float_pts);
    case AVMEDIA_TYPE_AUDIO:
       do_audio_out(of, ost, filtered_frame);
    }
    ...
}


通过跟踪这条流水线,我们知道这些帧是如何通过单个线程的上下文顺序进行处理的,从中我们能看到一些冗余。我们可以得出结论,既然1-in-N-out的转码流模型对我们来说是最有价值的,那么FFmpeg仅使用单线程来输出结果则可能并不理想。FFmpeg文档也建议我们在实际用例中,并行地启动多个FFmpeg实例或将更有意义。在这里,我们关键的一点认识是,既然此工具(FFmpeg)没有提供多线程功能,它就无法满足Twitch流媒体服务的严格需求,那么我们就无法随心所欲地使用它。


基准测试


TwitchTranscoder是我们为解决前面讨论的技术问题而开发的内部软件。它已被广泛运用于我们的生产中,每天24小时地处理数万个并发直播流。


为了确定TwitchTranscoder每天在转码任务上的表现是否会优于FFmpeg,我们进行了一系列基本的基准测试。在我们的测试中,我们对两个工具使用相同的Twitch直播流以及有相同预设、配置文件、比特率和其他标志的1080p60视频文件。每个视频源都被转码成我们通常使用的典型的720p60,720p30,480p30,360p30和160p30。


我们的假设是,FFmpeg对于输入文件的转码速度比TwitchTranscoder要慢,甚至可能无法跟上直播的速度。


图9,10和11中的结果比较了TwitchTranscoder与FFmpeg的执行时间。实验表明,即使在我们处理相同及更多(除了上面指定的栈之外,还提供仅音频转码,缩略图生成等等)任务的情况下,我们的转码器对于离线转码一直有绝对优势。


对于输出单个版本的720p60,FFmpeg稍快,这是因为TwitchTranscoder要处理如上所述的更多任务。当版本的数量增加时,TwitchTranscoder的多线程模型表现出更大的优势,这些优势帮助它超越了FFmpeg。观察Twitch完整的ABR梯度,与FFmpeg相比,TwitchTranscoder节省了65%的执行时间。


0?wx_fmt=png

图9:TwitchTranscoder与FFmpeg转码时间比较,实验1


0?wx_fmt=png

图10:TwitchTranscoder与FFmpeg转码时间比较,实验2


0?wx_fmt=png

图11:TwitchTranscoder与FFmpeg转码时间比较,实验2


我们通过比较在出问题前,一台机器上最多能够运行多少个FFmpeg的并行实例来进行实时流转码测试。这里可能发生的问题包括帧丢失、视频伪影等。在我们的生产服务器中,我们能够支持多个通道同时进行转码,同时,更多的通道被转封装。不幸的是,运行多个FFmpeg实例会导致一系列影响转码输出的错误,并且需要更高的CPU利用率(请参见图12中的屏幕截图)。


0?wx_fmt=png

图12:FFmpeg运行多个实例时的错误消息


结论


在本文中,我们将FFmpeg作为实时流RTMP- to-HLS的转码器进行了研究,并提供了有关如何操作该工具的信息。该解决方案部署起来很简单,但有一些技术问题值得注意,比如段错位、不必要的性能损失,以及缺乏支持我们产品功能的灵活性等。因此,我们实现了自己内部的转码器软件栈TwitchTranscoder,它运行在一个定制的线程模型中,并可以在一个进程中输出N个处理版本。


LiveVideoStack招募全职技术编辑和社区编辑


LiveVideoStack是专注在音视频、多媒体开发的技术社区,通过传播最新技术探索与应用实践,帮助技术人员成长,解决企业应用场景中的技术难题。如果你有意为音视频、多媒体开发领域发展做出贡献,欢迎成为LiveVideoStack社区编辑的一员。你可以翻译、投稿、采访、提供内容线索等。


通过contribute@livevideostack.com联系,或在LiveVideoStack公众号回复『技术编辑』或『社区编辑』了解详情。

相关文章
|
编解码 人工智能 算法
从 FFmpeg 性能加速到端云一体媒体系统优化
7 月 31 日,阿里云视频云受邀参加由开放原子开源基金会、Linux 基金会亚太区、开源中国共同举办的全球开源技术峰会 GOTC 2021 ,在大会的音视频性能优化专场上,分享了开源 FFmpeg 在性能加速方面的实战经验以及端云一体媒体系统建设与优化。
从 FFmpeg 性能加速到端云一体媒体系统优化
|
28天前
|
Linux 开发工具 Android开发
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
ijkplayer是由Bilibili基于FFmpeg3.4研发并开源的播放器,适用于Android和iOS,支持本地视频及网络流媒体播放。本文详细介绍如何在新版Android Studio中导入并使用ijkplayer库,包括Gradle版本及配置更新、导入编译好的so文件以及添加直播链接播放代码等步骤,帮助开发者顺利进行App调试与开发。更多FFmpeg开发知识可参考《FFmpeg开发实战:从零基础到短视频上线》。
102 2
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
|
1月前
|
编解码 语音技术 内存技术
FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
《FFmpeg开发实战:从零基础到短视频上线》一书中的“5.1.2 把音频流保存为PCM文件”章节介绍了将媒体文件中的音频流转换为原始PCM音频的方法。示例代码直接保存解码后的PCM数据,保留了原始音频的采样频率、声道数量和采样位数。但在实际应用中,有时需要特定规格的PCM音频。例如,某些语音识别引擎仅接受16位PCM数据,而标准MP3音频通常采用32位采样,因此需将32位MP3音频转换为16位PCM音频。
55 0
FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
|
1月前
|
XML 开发工具 Android开发
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
ExoPlayer最初是为了解决Android早期MediaPlayer控件对网络视频兼容性差的问题而推出的。现在,Android官方已将其升级并纳入Jetpack的Media3库,使其成为音视频操作的统一引擎。新版ExoPlayer支持多种协议,解决了设备和系统碎片化问题,可在整个Android生态中一致运行。通过修改`build.gradle`文件、布局文件及Activity代码,并添加必要的权限,即可集成并使用ExoPlayer进行网络视频播放。具体步骤包括引入依赖库、配置播放界面、编写播放逻辑以及添加互联网访问权限。
133 1
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
|
1月前
|
Web App开发 安全 程序员
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
多年的互联网寒冬在今年尤为凛冽,坚守安卓开发愈发不易。面对是否转行或学习新技术的迷茫,安卓程序员可从三个方向进阶:1)钻研谷歌新技术,如Kotlin、Flutter、Jetpack等;2)拓展新功能应用,掌握Socket、OpenGL、WebRTC等专业领域技能;3)结合其他行业,如汽车、游戏、安全等,拓宽职业道路。这三个方向各有学习难度和保饭碗指数,助你在安卓开发领域持续成长。
71 1
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
|
2月前
|
XML Java Android开发
FFmpeg开发笔记(五十二)移动端的国产视频播放器GSYVideoPlayer
GSYVideoPlayer是一款国产移动端视频播放器,支持弹幕、滤镜、广告等功能,采用IJKPlayer、Media3(EXOPlayer)、MediaPlayer及AliPlayer多种内核。截至2024年8月,其GitHub星标数达2万。集成时需使用新版Android Studio,并按特定步骤配置依赖与权限。提供了NormalGSYVideoPlayer、GSYADVideoPlayer及ListGSYVideoPlayer三种控件,支持HLS、RTMP等多种直播链接。
98 18
FFmpeg开发笔记(五十二)移动端的国产视频播放器GSYVideoPlayer
|
1月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
83 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
2月前
|
Linux 开发工具 Android开发
FFmpeg开发笔记(五十三)移动端的国产直播录制工具EasyPusher
EasyPusher是一款国产RTSP直播录制推流客户端工具,支持Windows、Linux、Android及iOS等系统。尽管其GitHub仓库(安卓版:https://github.com/EasyDarwin/EasyPusher-Android)已多年未更新,但通过一系列改造,如升级SDK版本、迁移到AndroidX、指定本地NDK版本及更新Gradle版本等,仍可在最新Android Studio上运行。以下是针对Android Studio Dolphin版本的具体改造步骤。
59 3
FFmpeg开发笔记(五十三)移动端的国产直播录制工具EasyPusher
|
1月前
|
Android开发 开发者
FFmpeg开发笔记(五十七)使用Media3的Transformer加工视频文件
谷歌推出的Transformer,作为Jetpack Media3架构的一部分,助力开发者实现音视频格式转换与编辑。Media3简化了媒体处理流程,提升了定制性和可靠性。Transformer可用于剪辑、添加滤镜等操作,其示例代码可在指定GitHub仓库中找到。要使用Transformer,需在`build.gradle`中添加相关依赖,并按文档编写处理逻辑,最终完成音视频转换任务。具体步骤包括配置剪辑参数、设置空间效果以及监听转换事件等。
51 0
FFmpeg开发笔记(五十七)使用Media3的Transformer加工视频文件
|
1月前
|
Linux 视频直播
FFmpeg开发笔记(五十四)使用EasyPusher实现移动端的RTSP直播
本文介绍了如何使用EasyPusher-Android实现RTSP直播流程。首先对比了RTSP、RTMP、SRT和RIST四种流媒体协议,并以RTSP为例,详细说明了使用EasyPusher-Android向流媒体服务器进行RTSP直播推流的方法。文中还提供了OBS Studio配置RTSP插件及ZLMediaKit云服务器部署的相关信息,通过修改EasyPusher-Android源码使其支持通用RTSP地址,最终验证了直播功能的成功实现。
54 0
FFmpeg开发笔记(五十四)使用EasyPusher实现移动端的RTSP直播