前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >“师兄”带你看FFmpeg开发中的坑起坑落

“师兄”带你看FFmpeg开发中的坑起坑落

作者头像
LiveVideoStack
发布2021-09-02 11:57:31
1.3K0
发布2021-09-02 11:57:31
举报
文章被收录于专栏:音视频技术

对于FFmpeg每一个从事音视频开发的小伙伴都不会感到陌生,它可以说涵盖了音视频开发中绝大多数的领域,不过在FFmpeg开发中也会遇到可移植性、转码压缩音视频不同步、多线程编解码等等问题,本文是“大师兄”刘歧在LiveVideoStack Meet北京站上的分享,他将与大家分享FFmpeg实践开发中遇到的技术难点和经验。

演讲 / 刘歧

整理 / LiveVideoStack

谢谢大家,谢谢主持人,因为今天时间有限,所以就简单的介绍一些套路。先做下自我介绍,我是一个音视频流媒体的爱好者,目前和几个朋友一起成立了公司,专门做音视频编解码处理,当然不是做编码器,是专门做在线处理。此外我是FFmpeg的维护者之一,再就是以前玩过嵌入式处理,是从44B0开始的;也做过存储,参与开发过广电的大规模存储;在中科创达专门做手机时做过设备驱动开发;也做过一些流媒体,当时主要基于高通平台;之后去蓝讯之后开始做流媒体系统设计,当时担任流媒体架构师,主要是做直播部分。

在正式开始前,跟大家分享一组图片,第一个是现实世界色彩风格的我,第二个是泰坦尼克色彩风格的我,第三个是斯巴达克斯色彩风格的我,三种不同的风格,这个是我前两天调色彩风格时专门生成的一套模板,所有的视频处理完成之后,你会得到各种各样的效果。

我今天要分享的主题前面也有提到,整体内容大概会分为五部分,首先是基础介绍,然后会大致分享做FFmpeg开发的规则,开发过程中遇到的坑,以及面对这些坑如何分析问题,最后我会做一个简单的总结。

基础介绍

  • 基本概念

什么是压缩,很多人都会遇到过客户要求把MP4转码成FLV,但如果里边是H.264的话是不需要去做转码的,只需要做remux,也就是转封装即可,那封装是什么?我们用一个很生动的例子来说明——液化气罐,它就是一个封装,它里面装的是咱们压缩的东西,可以理解为里面装的是H.264,如果我把开关打开之后,里面的水将会变成气体,这个气体可以理解为咱们看到的YUV或者PCM,这些数据量是很大的,然后做一些压缩,把它装到一个封装里面,这个封装就是液化气罐,我们把液化气罐砸了就是个解封装的过程。

在现实生活中,我们看一些视频或者是做一些编解码处理的时候会遇到一些问题,比如说看到这张图片,这个是从封装里面解耦气体的时候出现了异常,但这个异常又不是特别严重,那么出现的现象就是右边花屏的状态,这样的情况顶多是丢了一帧,还没有导致严重的错误。

如果再稍微过分一点,它就会导致成上图显示的现象——播放器基本已经没法用了,你需要有两种处理方式,当然不可能是拿水往电脑上泼,那么这张图上的处理方式就是播放器自动帮你处理——退出、崩溃。这里不得不提到FFmpeg中fatal error跟error两者的区别,我们前面看到的第一张图就是一个error,也就是说虽然出错了,但是还能继续去处理下一个包;但如果要是遇到了fatal error,比如说没有idr帧,它的帧整个都是乱序的,或者是播放器的Buffer已经溢出了,或者是干脆无法解码,这个时候它就是一个fatal error,播放器就会退出来了。

  • FFmpeg常用功能&软件

大概介绍一下FFmpeg,其实FFmpeg中大家常用的功能主要是libavformat、libavcodec和libavfilter,当然还有一些包括采样率的转换、缩放、格式的转换,比如YUV转成RGB,以及最近非常火的人工智能,做一些头像识别等等,那么做头像识别之前,我们知道人眼看到的内容是有色彩的,如果想去识别它,可以把它转成一个RGB格式,那么我们再看到这个图像的时候,它将会是头发是黑的,脸有可能是白的,那么它就可以去做识别了,这部分实际可以通过滤镜部分去处理,此外转换的时候通过scale做处理。再就是AVUtil,我个人认为它是非常厉害的功能,它里面会抛出来一些函数,那些函数和接口或者一些条件判断、一些流程控制,都是通过util抛出来的,在FFmpeg官方文档中有util的目录。还有libavdevice,在做一些AVFoundtion,或者是qtkit、xorg、dshow、alsa等等一些设备在里面是可以拿到。

接下来介绍下FFmpeg四个比较常用的软件,第一个是ffmpeg,大家做转码常用的,或者是测一些流是否可以被正常播放,或者在有异常的时候,通过它input下看是否能正常获取到。第二个是ffprobe,它的功能很强大,通过它可以分析到关于音视频各个维度的的数据,比如我拿到一个音频包,这个包多大,头部存了什么东西?如果主播推流,中间被其他人抢占,再重新推流这个过程中,客户端首先会收到metadata,然后是Sequence Header,紧接着就是视频或者音频的流,主播重新推流之后就会出现多一个metadata的情况,那这些用ffprobe是可以看到的。或者时间戳是否连续、时间戳跳变等等也可以通过ffprobe可以看到。ffprobe可以read packets、 read frames、read streams,如果你想看整个帧的排列,可以分析它的frames,通过导出CSV格式文件转换成图像——柱状图。

第三个是ffplay,实际上很多时候可以用ffplay直接去播放,可以立即看到效果,不必再等待FFmpeg转码。需要注意的是ffplay在2015年中旬,从SDL1.2转成SDL2.0,如果出现编不出ffplay的情况,可能需要装一下SDL2.0。最后是ffserver,虽然目前也还会有很多人使用,但确实已经没落了。

FFmpeg开发规则

接下来介绍下FFmpeg开发的基本规则,其实这个规则很简单,但是不符合大部分人的使用习惯。

  • 会读文档是成功的第一步

FFmpeg会提供一个很全面的文档,因此首先你得会看文档,文档的结构很清晰,首先第一步就是advanced options,下面是各个封装部分和codec部分,最后是滤镜部分。

第二个是Wiki,这个文档都是通过texi文件去写的,我们可以看到document下面有muxer和codec的.texi文件,在增加或者是删除一个选项的时候,这个文件是必须要改的,否则代码是无法被合并的,然而它只是纯文字的,有些抽象的东西无法举例的时候,就会写一个Wiki放到track里面。此外就是API使用文档。

  • 套路满满的玩

除了这些基本规则外,还要掌握一些套路,比如说看代码看不懂的时候怎么办?邮件列表是个很好的手段,它是全球完全公开的,通过它可以帮助你了解这段代码是如何实现的,或者它有怎样的问题等等。这里跟大家分享一个问题。

这段代码是用HLS的duration做了一个四舍五入,HLS的duration分成两部分:一个是最上边的header部分taget duration,还有就是最下边的INF里边要设置duration。标准文档规定,在做round的四舍五入时, taget具体切的duration,也就是每个切片实际的duration必须小于taget duration,但是在官方文档中写的比较晦涩。这个问题最终通过邮件列表的方式得以解决,所以善用邮件列表可以帮助大家解决很多问题。

  • 入坑也是要按“基本法”的

了解完文档和基本套路,接下来就是入坑了,我的建议还是要仔细阅读文档,这是最简单的,也是最直接的,它包括了常见的FAQ、支持的封装格式、编码格式、滤镜以及外部库。再就是开发者说明文档,在提交代码之前先要阅读开发者说明文档,它会告诉我们怎么去开发,怎么去生成Patch,怎么去发一个邮件。此外还有Git的基本使用,比如通过Git cherry-pick命令可以把另一个开源社区代码的commit ID直接合并到你branch的commit ID上,避免在move过程中丢失author信息,但也会有冲突,就需要自己手动fix,它相当于是一个项目管理,你可以通过git去管理多个branch代码。最后是自动化测试环境,如果你想提代码,比如想给HLS加一个选项,在生成HLS的时候,m3u8,最后会加一个endlist,而下次再去写这个文件时,因为FFmpeg没有追加的功能,它会从头重新生成一遍,这样就会导致像CDN中常见的一个场景——主播推流断了之后重新推上来,因为原来的流被覆盖掉了,所以一部分手机播放会出现卡顿,如果增加这个功能,它实际上是把endlist去掉,加了一个discontinuity的tag。

那年那坑那些经验

  • 踩坑实录第一弹

接下来跟大家分享下过往遇到的坑和一些经验,首先是CDN,做CDN服务不能针对某一个业务做优化,而是需要针对所有的业务,这里给大家举个例子:有的客户会要求直接基于FLV去追加,但是假如用户在推流过程中出现抖动,就会导致断开连接,就需要重新推流上来,而很多情况下会要求算成一次推流,那么就需要去解决拼接的问题。

第二个是时常变化的标准,比如像DASH、MPEGTS或者HLS,前段时间MPEGTS增加了一个音频的Codec——OPUS,而HLS最近也终于定版——RFC。

第三点是文档信息,这部分经常会出现文档信息不全,像OpenCV很多文档是基于代码直接生成的,也有手动去写的,之前我在解决OpenCV人脸识别问题的时候,发现原来是函数接口名变了,但文档中没有变更,而FFmpeg在这方面还是不错的,它在你提代码的时候会强制你写文档。

最后是想要的功能不支持,比如facedetect,就需要自己把它加进去,这时首先需要研究里面有哪些东西、大概是做什么的,比如人脸识别是放在AVFilter的滤镜中,因此其中会有对应的参考;再比如缩放,缩放是很耗资源的,在FFmpeg中把缩放计算这部分做了硬件加速,包括VAAPI、QSV、NVIDIA和Intel对应的计算,都是通过AVFilter去做的。

刚才提到不支持人脸识别功能,就需要分析VF overlay,这个.C文件里面,我们可以看到它处理了RGB、 YUV以及gray,其中是各种不同的叠加,那么它既然能这样处理,说明在这个滤镜里面肯定有Input和Output,那么我就可以直接把它的Input数据放到OpenCV里面,再把OpenCV中处理过的数据,也就是Output数据放到Outputlabel里面就可以了,实际上它的整个操作是很简单的,而且除了Overlay这个滤镜,其他的滤镜处理也很简单,代码都不会超过500行。

  • 踩坑实录第二弹

第二个坑就是FFmpeg不支持DASH Demuxer,像我们平时看的YouTube、BBC、CNN,它们的直播流有两种——没有RTMP和FLV,其中一个是HLS,另一个是DASH,包括我们看到的Netflix中很多直播也都是支持的。现在的FFmpeg也已经可以支持,虽然暂时还不支持多码率。第二点是对HLS Muxer的支持,HLS在以前仅仅可以做到大概的解,在封装成m3u8的时候,其中很多功能都不支持,比如single_file或者切多片时的设置,在切多片的时候就需要用到类似single_file的功能,比如做到三百兆一个片,这样就可以避免HLS都是零零碎碎的小片,对硬盘做到一定的保护以及提升一些硬盘读取的速度,而且IO消耗并没有那么高。最后是FLV,我们在以前做FFmpeg封装的时候,推流出去后,由CDN给录制成FLV,它的metadata里是没有keyframe index的,如果用Flash播放器去播放或者拖动的时候是很慢的,因为它需要下载这个数据,但是假如有这个信息,就能够做到很快的定位,因为所有的keyframe信息都在keyframe index中记录,而它在metadata里,我只需要解一下metadata就可以把它拿出来。

分析问题的基本套路

其实学习和使用FFmpeg是需要一个基本套路的,我认为这个套路就是复现问题。当遇到一个问题时,首先复现这个问题,从中找到它的规则。然后有针对性的去看参考文档,如果参考标准里面并没有完全的说明,那就只能去支持它的这种不标准;如果在参考标准中明确提出,就需要做更改,因为代码不能被合并。第三件事是分析,分析正确的与不正确之间的差别,这里举个例子,我在做HLS支持的时候,HLS 我将FMP4支持上去后发现最开始无法播放,我就跟苹果的FMP4作对比,分析两者的区别,发现是yuvj422p的pixfmt,然后按照常规的yuv420p做编码压缩在封装fmp4,就可以了,就是通过这样的对比找到最终问题所在,这个也是当初做服务大概的思路。

那么遇到问题应该怎么办?作为程序员经常会遇到这样一种情况。

产品经理或者客服直接找到你,说客户过来报bug了,说你的代码有问题,必须马上解决。

这种时候我特别想给他一枪,把他解决了,问题也就解决了,相信很多人也会跟我有一样的想法(哈哈)。但现实却是,经过一番苦战之后,“是在下输了”。

  • 复现问题——DASH篇

这时候就需要开始分析这些问题、复现问题,首先需要有一本标准文档,针对DASH支持这个具体问题上,我们还需要找一个播放器测试,以及了解FFmpeg的框架,在实际往FFmpeg里加DASH功能时,第一步我先分析它是一个切片文件列表,然后我开始去分析HLS,因为它们之间有很多相似之处,区别在于一个是纯文本,一个是xml,此外还需要注意标准中描述的一些细节,比如DASH实际上不仅仅通过CX,逐个递增的切片,还有一种timeline的方式,需要按照它的时间去做文件的获取,但它不会写明切片的连接是什么,只会告诉你计算方式,如果算错就会导致拿不到文件,这个时候就需要特别注意:比如我的第一片first_seq_no,获取当前时间get_current_time,获得availability_start_time以及time_shift_buffer_depth,还包括time的计算方式fragment_timescale等等。

  • 复现问题——HLS篇

再跟大家分享另一个案例——HLS,图片中生成的这个切片,如果不仔细观察可能看不出有问题,实际上这个视频少写了一帧,大家可以看到生成的最后一片和拿到的最后一片,它们duration时间是不同的,所以我分析是,它是在append的时候直接就退出了,并没有写最后一帧。

  • 套路的基本点

接下来介绍下FFmpeg中写Demuxer需要注意的几个基本点:首先read_probe, 注意它不能直接返回0,这个是需要注意的,需要去访问Maxscore命令的size才可以,或者max size score除以2,当然这是正确的情况,如果不正确的可以直接返回0,那么解析将会结束;接着就是解析header,这个header就是FLV的头,或者DASH的xml文件;然后是read_packet,实际它在调用API的时候更加常用,对于写封装而言用到它的机会并不会很多,它是在IO操作中,使用read_packet调里面的回调,再就是read_close和read_seek。

write部分其实主要是三部分——write_header、write_packet和write_trailer,这里需要注意一点,很多人在写MP4的时候经常会Ctrl+C,这个时候你剩下的MP4就播不了,因为两次Ctrl+C的时候是不写trailer的,写一半被中断了,而moov box是根据你的MDat记录的所有信息计算出来的,然后通过一个flag,把moov box移到最前面。

最后再介绍下IO操作部分,当你read_packet的时候,实际上它会调AVFormatContext,其中的pb就是具体封装的context,你可以去通过IO接口设置,把它挂到AVFormatContext的pb里,此后所有的操作其实就都是操作这个pb,也就是说read_packet的时候是在read里面的回调——类似于read_data,拿到read_data就可以正常解析音视频数据。

其实总体而言,套路的基本点就是静下心来多看代码,读文档,写代码,除了这三件大事更重要的就是交流,因为独学而无友则孤陋而寡闻。以上是我分享的内容,感谢大家。

关于分享者

刘歧,OnVideo 联合创始人、ChinaUnix资深版主、FFmpeg Maintainer/顾问,擅长流媒体系统设计,分布式系统开发。

广告时间

12月2日,『后直播时代技术』沙龙将走进成都,LiveVideoStack携手腾讯音视频实验室、声网、又拍云等知名企业一同直击游戏、社交领域,探索其在多媒体与音视频技术的应用实践。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-11-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 LiveVideoStack 微信公众号,前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

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