深入理解FFmpeg

978-7-115-62136-8
作者: 刘歧赵军杜金房赵文杰宋韶颍
译者:
编辑: 佘洁

图书目录:

详情

本书详细介绍了开源音视频处理软件FFmpeg的使用,按照所讲述的内容及读者的不同层次,本书划分为上下两篇。上篇为基础与参数详解,介绍了FFmpeg的基本组成部分、工具使用,以及封装、转码、流媒体、滤镜和设备操作。下篇为API使用及开发,介绍了FFmpeg封装、编解码和滤镜部分的API使用操作,相关操作均以实例方式进行说明,包括新旧API的操作方法和异同,并给出了大量的API使用、自定义功能模块、基于FFmpeg的API开发自己的播放器的示例,以及其在实际开源软件中的应用等。   本书不仅适合音视频流媒体处理的研发人员、对音视频技术应用和实时音视频通信感兴趣的技术人员,还适合高等院校计算机相关专业的学生阅读。

图书摘要

版权信息

书名:深入理解FFmpeg

ISBN:978-7-115-62136-8

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

版  权

著    刘 歧 赵 军 杜金房 赵文杰 宋韶颍

责任编辑 佘 洁

人民邮电出版社出版发行  北京市丰台区成寿寺路11号

邮编 100164  电子邮件 315@ptpress.com.cn

网址 http://www.ptpress.com.cn

读者服务热线:(010)81055410

反盗版热线:(010)81055315

内容提要

本书详细介绍了开源音视频处理软件FFmpeg的使用,按照所讲述的内容及读者的不同层次,本书划分为上下两篇。上篇为基础与参数详解,介绍了FFmpeg的基本组成部分、工具使用,以及封装、转码、流媒体、滤镜和设备操作。下篇为API使用及开发,介绍了FFmpeg封装、编解码和滤镜部分的API使用操作,相关操作均以实例方式进行说明,包括新旧API的操作方法和异同,并给出了大量的API使用、自定义功能模块、基于FFmpeg的API开发自己的播放器的示例,以及其在实际开源软件中的应用等。

本书不仅适合音视频流媒体处理的研发人员、对音视频技术应用和实时音视频通信感兴趣的技术人员,还适合高等院校计算机相关专业的学生阅读。

推荐语

FFmpeg是当今仍在开发的最复杂的开源软件之一,其中许多贡献者都是志愿者。虽然这在开源运动之初很常见,但对今天为大多数互联网视频基础设施提供支持的软件来说,这种情况是相当不寻常的。多年来,围绕 FFmpeg 出现了一个非常强大的中国社区,这实在令人耳目一新。事实上,当今一些最活跃的FFmpeg开发人员都来自中国,本书的一些作者也是活跃的FFmpeg开发人员。

我真心希望这本书能够为中国的FFmpeg用户更好地理解如何使用、增强、掌握和扩展FFmpeg提供帮助,并为中国和世界带来更多的开源应用案例。

——Jean-Baptiste Kempf,FFmpeg社区委员会成员,VideoLAN主席,VLC开发者

FFmpeg功能强大,是音视频领域最具影响力的开源项目之一。刘歧可谓是FFmpeg中国社区的领军人物,自《FFmpeg从入门到精通》出版五年之后,他再次推出力作《深入理解FFmpeg》,继续带领大家体会FFmpeg的博大精深。

——马思伟,北京大学教授

我在大学教授“多媒体通信系统”课程多年,课程的实践环节也是以FFmpeg为主要工具。刘歧之前写的书一直是我推荐的主要工具书之一,方便同学们在实操中随时参考。时隔多年,刘歧与多位技术专家联合出版了这本《深入理解FFmpeg》。本书内容更加丰富,技术覆盖更加全面,且实践性强,详细介绍了FFmpeg的方方面面,从基本知识到工具使用,再到SDK接口的调用、与第三方工具的集成,以及自定义模块的开发等,是学习FFmpeg的不二之选。

刘歧与本书其他作者有十多年的多媒体技术研发经验,研发过多个短视频和直播产品,并应用于国内主流短视频平台。刘歧也是国内最早成为FFmpeg官方代码的维护者之一,技术过硬,为人谦和,乐于助人,被大家亲切地称为“大师兄(悟空)”。相信通过阅读本书,你也能在“大师兄”的加持下获益匪浅。

——宋利,上海交通大学教授,“媒矿工厂”负责人

我与作者刘歧已经是相识十一年的老朋友了。这十多年,他一直活跃在FFmpeg社区并持续做出积极的贡献,是FFmpeg社区的核心维护者之一,与其他核心维护者建立了密切的关系。

编写一本书绝非易事,与读书笔记有很大不同,前者需要时间沉淀。尽管现在关于FFmpeg的文档和图书已经相当丰富,但毫无疑问,市面上仍然缺乏更权威和更准确的相关书籍。我认为书籍内容的准确性至关重要,一个细微的错误观念可能导致开发者产生理解上的巨大偏差。因此,当进入一个新领域时,我会阅读对该领域有深刻理解的人所写的图书,这是因为他们作品内容的准确性更高,能够帮助我更高效地进入这个领域。而《深入理解FFmpeg》正是这样一本好书,值得向大家推荐。

——杨成立,开源项目SRS(Simple Realtime Server)创始人、技术委员会成员

2017年,我在LiveVideoStack成立不久就认识了“大师兄”(刘歧),他向我介绍了多媒体技术与行业的背景信息,并给我推荐了许多关键的技术人,这为我们第一次举行LiveVideoStackCon提供了关键支撑。多年来,他一直有求必应,总是充满热情,这种热情也感染着身边的朋友和同事。

参与开源项目需要牺牲大量个人时间,能坚持多年更属不易。如今《深入理解 FFmpeg》终于出版了,通过阅读本书,相信大家可以了解关于FFmpeg的最新功能与使用方法,从源头理解FFmpeg背后的设计逻辑与考量。希望大家借助FFmpeg,让自己和团队更上一层楼。

——包研,LiveVideoStack联合创始人

能为读者推荐刘歧的新书,我感到非常荣幸。在与他相识的将近20年里,我很钦佩他作为一位技术专家、架构师的卓越能力,以及他对FFmpeg的深刻理解。

刘歧在转向流媒体技术领域之前,曾在安全、Linux内核与嵌入式系统研发领域长期工作。扎实的技术基础使他在解决问题时总能够追根溯源,严谨求实。他积极投身于音视频转码框架的开发,其框架在商业环境中经受住了考验,稳定运行。正是凭借丰富的实战经验,他决定撰写这本针对FFmpeg的著作。书稿历经长时间打磨,反映在他对每行代码、每项指令的认真审查,这种严谨的态度获得了音视频行业同仁的高度认可。

经过多年沉淀,刘歧与几位同行合作完成了《深入理解FFmpeg》这本书。在这段时间里,他对FFmpeg进行了更为深入的研究,拓展了自己的视野。期间,他不仅成功创办了自己的企业,还成为FFmpeg的全球资深维护者。特别值得一提的是,赵文杰和宋韶颍的加入为该书增色不少。赵文杰作为资深音视频专家,长期致力于直播技术的研发;宋韶颍则是音视频工程落地领域的资深专家。这本书对技术人员而言无疑是一份宝贵的学习资料。

我对刘歧以及参与编著本书的几位朋友充满信心,相信他们会持续保持对技术的热情,为中国的音视频技术社区注入更多活力。本书也将成为指导、启发众多技术从业者的重要资源,帮助他们更好地应对技术挑战,实现创新突破!                         

——刘帅,好未来教研云负责人

缘起

随着移动互联网的发展和网络基础设施的逐步升级,我们经历了从UGC到PGC、从PC端到移动端、从图片到视频、从点播到直播的巨大变迁。如今,各种音视频应用逐渐成为主流,而它们大多是基于FFmpeg实现的。可以说,FFmpeg就是音视频界的“瑞士军刀”,让高级而神秘的技术在不知不觉中飞入寻常百姓家,极大地促进了互联网的繁荣。如今这把军刀的功能越发丰富,不仅能解决各种实际问题,还成了一本多媒体百科全书。工作之余,每次翻阅其文档代码,都会有新的惊喜。

我自2007年接触FFmpeg,不知不觉已经16年有余。FFmpeg在这些年间经历了多次巨大的架构变化,功能也愈发强大。最初,我们只是将它用作MPlayer的解码库之一,但它逐渐支持的Codec、Format和Protocol已经超越了MPlayer,甚至还支持了MPlayer的Filter。因此,无论是在播放端、服务器端、制作端还是推流端,几乎所有需求都可以通过FFmpeg实现。近几年FFmpeg的架构经过了跳跃式调整,加入了大量的音视频处理滤镜,并集成了神经网络框架,原有的Format如今也被拆分成了Muxer和Demuxer两个大的模块,编解码接口从原有的单一操作接口变成了模块定制者高可选性接口,无论是从使用者还是开发者的角度看,都更加灵活了。因为该项目的活跃度比较高,项目发布的品控也越来越好,已经逐渐发展成为音视频领域广为应用的基础组件。回顾这些年接触过的开发者,国内有很多人从事相关应用开发,但真正贡献了核心代码的却寥寥无几。

初识

2016年,在Maintainer页面上突然出现了一个中国人的名字——Steven Liu,这是令人惊讶的,也让我对他颇感好奇,并期待认识。后来听说了一个叫做OnVideo的创业项目,我才了解他本人与我有着二度联系,不禁感叹世界之小。我与他一见如故,之后交集颇多,这更加让我相信在这个世界上,有缘的人终会相识相聚。这位化名为“大师兄”(悟空)的刘歧,有着非常感染我的性格——东北人与生俱来的乐观与风趣,以及他对技术真挚的热爱。尽管工作繁忙,他仍然对开源社区倾注了大量心血,无论是解答问题还是推进开发,他总是慷慨奉献、一丝不苟。他身上所散发的是一种无问西东的信念,在当今时代,修建大教堂已逐渐成为一种奢侈,相比之下,他选择砌墙,这让我们相形见绌。我很期望有机会为他和社区做点什么,给优秀的人和有意义的事寻求更大的平台。

共事

2019年,偶然的合作机会促使“大师兄”加入了快手,参与了快手音视频技术架构的研发与升级。凭借丰富的音视频基础架构设计经验,“大师兄”帮助我们实现了音视频基础组件的优化,并且成功上线了先进的图片格式,帮助业务节省了不少成本。当我得知“大师兄”正在撰写本书时,十分认同这件事的价值,遂为本书撰写了序言,希望能尽绵薄之力,促进行业发展。

榜样

在我看来,作为程序员,参与知名开源项目是对个人技能发展的高级追求。为什么这么说呢?成功的开源项目并不多,通常它们都能很好地解决某个基础性需求,是众多优秀程序员智慧的结晶。对于有技术追求的开发者来说,深入掌握FFmpeg的架构思想、开发协作流程以及解决问题的方法,对提升其自身的软件开发能力会有很大帮助。在公司写代码时,通常只有一两个人进行代码审查,但在社区中,可能会有几十甚至几百人对你的代码进行审查,其中包括世界级专家。要成为这种项目的Maintainer,需要付出大量努力,真正为项目贡献智慧并赢得社区的信任,这样你也有可能成为那个世界级专家。在过去十年中,“大师兄”通过卓越的贡献赢得了尊重,成为难得一见的Maintainer,并且在快手内部,他也在积极帮助对音视频技术感兴趣的同事加深对FFmpeg社区与音视频技术的了解,确实是我们学习的楷模。

推荐理由

“大师兄”对FFmpeg的理解之深入,决定了本书在内容的全面性、理论与实践的结合方面都是令人期待的。

许多热爱多媒体应用的开发者在实践中会遇到很多问题,尽管偶尔可以通过高手的指点解决一些临时问题,但仍然会频繁遇到新的困难。为什么会这样呢?往往是因为缺乏系统化的知识体系,无法真正入门,就更不用说深入学习了。因此,对于那些希望入门、入行音视频的读者,本书系统地梳理了从FFmpeg基本命令行到高级应用的各个方面,能够带你走入多媒体技术的殿堂。

此外,对那些具有一定多媒体专业背景知识但不知如何实践的读者来说,认真阅读本书可以对理论如何结合实践有一个全新的认识。音视频算法再也不是抽象枯燥的公式和标准,而是在鲜活的应用场景中解决实际问题的利器。对那些已经熟悉多媒体开发的读者来说,本书是一本全面的手册和工具,可以帮助你查漏补缺,阅读完后必定会有所收获。

最后,对那些希望深入学习多媒体架构知识,甚至像“大师兄”一样成为社区贡献者、成为Committer的程序员们来说,本书也是一本很好的指南。以Linux操作系统学习为例,从基本使用开始,一步步向搭建互联网服务器、深入调优、进行内核开发、构建大型系统演进,这是一个逐渐深入的过程。学习 FFmpeg 也是如此,开发者从使用各种命令行处理、阅读代码以了解背后的原理,到使用FFmpeg解决实际问题、完成模块级别的开发、参与架构改进,再到融会贯通并为社区做贡献,这也是学习必经之路径。FFmpeg的分层模块化架构思想与Linux内核类似,十分简洁优美,其中还包含丰富的图像与视频基础库和网络协议实现、底层汇编优化等。建议大家站在前辈巨人的肩膀上,学习他们所写架构的精髓,从实践的角度构建你的程序员世界观,从而完成从小工到大师的成长过程。

本书由浅入深,是一个值得探索的宝库,希望每个热爱技术的同学都能像“大师兄”一样,不断学习、不断实践、不断进步,让我们一起推动中国开源技术的发展,成为全球开发者的引领者。相信在“大师兄”的指导下,本书一定会成为你技术之路上的良师益友。让我们一同期待这本书的面世,为FFmpeg和音视频行业的发展贡献一份力量!

祝愿本书能够成为经典,为众多技术爱好者带来更多的启发和收获!

祝愿开源社区蓬勃发展,推动中国技术的崛起!

于冰

快手高级副总裁、研发线负责人

2023年7月5日于北京

前  言

为什么要写这本书

在过去几年中,人们的日常生活、工作方式发生了巨大变化,短视频、互动直播、在线教育、云上会议等音视频使用场景深入各行各业,井喷式的需求使得音视频技术也发生了许多改变。

回顾音视频技术的整体发展,可以将其粗略地分为3个阶段。第一阶段,音视频的传输方式“简单粗暴”,仅能通过模拟信号进行传输;第二阶段,音视频数据开始数字化,诞生了如DVD、DVB等一系列数字存储、传输技术,同时开始延展出更多针对网络的编解码技术、流媒体传输和存储等细分技术;第三阶段,随着终端硬件能力的提升和移动互联网的发展,音视频技术进一步细分,编解码技术持续演进,流媒体传输协议也开始面向特定场景演化,派生出点播、超低延时直播、互动直播、短视频、在线会议、在线视频编辑、VR/AR/MR等不同形态。

整个音视频领域正朝着超高清、低延时、强互动等方向演进,音视频相关的应用在人们日常生活中的使用频次也越来越高。同时,网络、计算机设备、移动终端、高性能计算等相关技术也快速迭代,再加上大模型技术的爆火和AIGC技术的加持,演化出更多的场景,其中所涉及的音视频处理技术也被越来越多的技术人员所需要。与此同时,开源项目已经成为行业的基石之一,FFmpeg也成为音视频处理技术不可或缺的套件,深刻理解和灵活使用FFmpeg已经成为一项基础技能。作为一个持续了20多年的开源项目,随着时间的发展,FFmpeg也与这个令人兴奋的时代一起不断更新迭代。

通过与众多从业人员进行FFmpeg相关的开发讨论与交流,笔者了解到,很多公司尤其是云服务相关的公司对FFmpeg的使用各有不同,主要分为两类:基于命令行和使用其API。所以本书也分为上下篇进行介绍,上篇以FFmpeg命令行使用的介绍为主,下篇以FFmpeg API的介绍为主。当然,因为FFmpeg社区的蓬勃发展,演化迅速,所以本书讲解的内容将会尽力跟随其最新版本。另外,笔者将会持续与广大读者沟通交流FFmpeg相关技术,希望能够为同行或者对FFmpeg感兴趣的读者提供参考,也希望本书能够帮助大家提高工作效率,解决工作和学习中的实际问题。

这些年来,FFmpeg 相关的中文内容越来越多,但细读下来,内容或多或少会随着FFmpeg的更新迭代而过时。所以,本书在讲解FFmpeg的知识的同时,也会尽量带上其设计的背后原因或背景,以及音视频的基础知识,以期让读者能够“知其然知其所以然”,尽量把“魔术师背后的箱子”一并打开。

笔者之前编写的《FFmpeg 从入门到精通》(以下简称《入门》)一书出版后,得到许多读者的各种反馈,主要包括以下几点:

命令行部分的内容偏多。

API使用部分的内容偏少。

希望能了解命令行参数和实际代码的对应关系。

需要多举一些代码例子。

需要对音视频基础知识做一些铺垫性介绍。

有几点需要说明,《入门》一书没有加入大量的代码举例,首先是因为雷霄骅博士的博客内容已经可以覆盖FFmpeg的大部分使用场景,所以没有在书中重复编写;其次是FFmpeg官方代码用例目录也涉及大量场景和使用案例。但是在该书出版之后,还是会接收到一些读者对于在书中加入代码、使用用例及背景知识的期望。另外,近几年低延迟直播、超低延迟直播、视频会议及实时互动也有了迅猛的发展和实质的进步,FFmpeg也应用于很多RTC(Real-Time Communication,实时通信)场景。因此,本书着重增加了以下内容:

音视频基础知识讲解。

以性能为目标的硬件加速的编解码。

更多的容器封装细节讲解,特别是FLV、MP4、MPEG-TS等格式。

详细的API使用说明和指导。

API使用的具体举例。

自定义FFmpeg模块的方法(主要是针对刚涉足FFmpeg模块的开发者)。

在云剪辑中常用的音视频处理技术。

在RTC场景下对FFmpeg的使用。

除了以上列出的,读者在阅读本书时会发现更多有趣的内容。本书偏重于实战,其目标是希望读者通过阅读本书,解决或解答在使用FFmpeg处理音视频时遇到的大多数问题和疑虑。虽然本书总体上比较专业且有深度,但第1章加入的音视频知识降低了学习门槛,可以让刚涉足音视频领域的读者轻松入门。

FFmpeg是音视频处理的“瑞士军刀”,几乎任何与音视频相关的软件中都会出现FFmpeg的身影。让不了解音视频的读者快速了解音视频和FFmpeg,让已经对FFmpeg有所了解的读者尝试理解FFmpeg的方方面面,便是作者写作本书的初衷。

此外,本书作者群的庞大也使得我们可以取不同领域作者之长项,让本书内容更加丰富,也更有深度。在作者之中,除赵文杰是《入门》的作者外,杜金房、宋韶颍和赵军是新加入的作者,他们既是FFmpeg的开发者,也是重度用户,在音视频领域都有很深的技术功底和丰富的工作经验,在相关开源软件中也多有贡献。他们对本书内容的掌控,以及对文字细节的精益求精也让本书更上一层楼。

读者对象

音视频技术应用相关人员

音视频流媒体技术的研发人员

对音视频流媒体处理开发感兴趣的技术人员

对实时音视频通信技术感兴趣的人员

高等院校计算机相关专业师生

如何阅读本书

本书包含17章。按照所讲述的内容及读者的不同层次,可以划分为以下两篇。

上篇为基础与参数详解。包括第1~9章,介绍了FFmpeg的基本组成、工具使用、封装操作、转码操作、流媒体操作、滤镜操作和设备操作。

下篇为API使用及开发。包括第10~17章,介绍了FFmpeg封装、编解码和滤镜部分的API使用操作,相关操作均以实例方式进行说明,包括新API及旧API的操作方法和异同,及其在实际的开源软件中的应用等。

本书基于FFmpeg 6.0 版本进行讲解,如果你已经能够通过源代码独立安装FFmpeg,那么可以跳过第1、2章,直接从第3章开始阅读;如果你对参数解读和举例没有兴趣,或者只希望使用FFmpeg的API进行开发,那么可以跳过前9章,直接从第10章开始阅读。笔者建议最好从第1章开始阅读,因为前9章中参数详解和FFmpeg工具举例有助于读者更流畅地使用API操作FFmpeg的内部和各功能模块。另外,前面章节也加入了FFmpeg作为开源项目的发展历程等有趣的内容。

勘误和支持

由于笔者的水平有限,加之在编写的同时各位作者还承担着繁重的开发工作,书中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以发送邮件到lq@chinaffmpeg.org。真诚期待您的反馈。

另外,本书代码相关举例部分可以在FFmpeg源代码的doc/examples目录下获得,还可以通过FFmpeg官方网站的文档获得:https://ffmpeg.org/doxygen/trunk/examples.html。

FFmpeg发展了至少22年,积累了极其丰富的资料。本书篇幅有限,不能涵盖所有的内容,很多其他社区的资源同样值得参考,这些地方也是各位作者获取信息的来源,一并推荐给读者。

官方文档资料网址如下:

FFmpeg官方文档:http://ffmpeg.org/documentation.html

FFmpeg官方wiki:https://trac.ffmpeg.org

中文经典资料网址如下:

雷霄骅博士总结的资料:http://blog.csdn.net/leixiaohua1020

ChinaFFmpeg:http://bbs.chinaffmpeg.com

除了以上这些信息,读者还可以通过Google、百度等搜索引擎获得大量相关资料。FFmpeg本身也提供了命令参数的详细说明,读者可以查看FFmpeg的帮助信息,后面的章节将会对此进行详细介绍。另外,作为开源项目的另一个好处就是:源码面前,了无秘密。读者可以直接基于FFmpeg的开源代码学习,这也是几位作者真实的学习经历。

致谢

感谢本书的联合作者杜金房、宋韶颍、赵文杰、赵军对本书的辛勤付出,他们在繁忙的工作中抽出时间完成书稿的编写,其过程非常艰苦。

感谢快手音视频技术部的汪亚强、林德才对本书大量技术内容提出准确的修改建议,他们的努力使得本书中的技术内容更精准。

感谢FFmpeg社区的朋友们对本书提供的大力支持,感谢蓝汛、高升、金山云、学而思网校、烟台小樱桃、腾讯云与快手的伙伴们长期的支持与贡献,没有他们也就不会有这本书的问世。

感谢人民邮电出版社的佘洁老师与其他编辑老师们,感谢他们的耐心指导与帮助,引导本书作者顺利地完成了全部书稿。

感谢FFmpeg社区、LiveVideoStack社区提供了很好的技术沟通与交流的平台,帮助作者们更好地成长。

感谢我的爱人和孩子一直以来对我的工作和写作的支持与理解,正是他们的默默支持,才使得我有更多的时间和精力投入工作及写作中。

谨以此书献给我最亲爱的家人、朋友、同事,以及众多为互联网、流媒体添砖加瓦的从业者们。

刘歧

2023年8月于快手总部

服务与支持

提交勘误

作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。

当您发现错误时,请登录异步社区(https://www.epubit.com),按书名搜索,进入本书页面,单击“发表勘误”,输入勘误信息,单击“提交勘误”按钮即可(见下图)。本书的作者和编辑会对您提交的相关信息进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。

与我们联系

我们的联系邮箱是contact@epubit.com.cn。

如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。

如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们。

如果您所在的学校、培训机构或企业,想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。

如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。

关于异步社区和异步图书

“异步社区”是人民邮电出版社旗下IT专业图书社区,致力于出版精品IT图书和相关学习产品,为作译者提供优质出版服务。异步社区创办于2015年8月,提供大量精品IT图书和电子书,以及高品质技术文章和视频课程。更多详情请访问异步社区官网https://www.epubit.com。

“异步图书”是由异步社区编辑团队策划出版的精品IT专业图书的品牌,依托于人民邮电出版社的计算机图书出版积累和专业编辑团队,相关图书在封面上印有异步图书的LOGO。异步图书的出版领域包括软件开发、大数据、人工智能、测试、前端、网络技术等。

异步社区

微信服务号

上篇 基础与参数详解

在本篇中我们会假设读者已经具备基本的shell命令行执行或者编程相关经验,但并不需要拥有视频、音频、流媒体相关知识,也不会假定读者已经熟悉FFmpeg命令行工具的方方面面。本篇会针对音视频技术应用时常遇到的概念或者问题,讲解其基础原理、常用的FFmpeg命令行;从安装、编译、定制FFmpeg开始,用FFmpeg命令行工具完成最常见的音视频任务,同时详细讲解FFmpeg的各类参数,还有示例说明。本篇囊括了对最常用容器格式标准的解读,包括FLV、MP4、MPEG-TS等;描述了FFmpeg社区在不同时期的重要发展历程。另外,我们也讲解了硬件加速方案,它是FFmpeg社区在面临性能挑战问题时的应对方法之一。

世界一直在变化,且速度似乎在不断加快。我们必须应对更多新的编程语言、新的工具、新的系统、新的知识,当然还有对旧的、不合理地方的改变。但是,一些不变的东西、一些稳定的点或者知识、过去的教训及其造就的洞察力可以助力我们未来的工作。本书的基本主题就是基于这些持久的概念,在变与不变中交织前进。

第1章 多媒体基础

欢迎来到弦歌缭绕、五彩斑斓的数字世界。FFmpeg 就像数字世界的魔法师,它可以随意改变声音和色彩,尽情装点这个世界;也可以扭曲这里的时间和空间,打造通向元宇宙的时空隧道。

本书将带你认识和掌控FFmpeg,在学习各种“魔法”的同时深入理解其背后的原理。在深入了解FFmpeg之前,我们先来学习多媒体基础知识,以便大家能顺利踏上FFmpeg的探索之旅,在后面的道路上能顺利通关,以不变应万变。这些基础知识无须死记硬背,在后面的章节中,我们还会反复看到它们。如果你已经准备好了,那我们就启程吧!

1.1 从现实世界到数字世界

现实世界是丰富多彩的,人类的老祖宗早就发明了文字和绘画,记录了波澜壮阔的历史和文化,但对于历史世界的感知,人们还要依靠脑海中的想象。19世纪,随着磁带、留声机和胶片等分别被发明出来,人们才可以通过声光真正地感受到曾经发生的故事。而随后的电影、电视和计算机更是可以把现实世界无微不至地描述和记录下来。

随着数学和科学技术的发展,人们不仅可以用抽象的数字与符号来描述和解释世界的规律,而且还能将它们应用到日常生活中。如今,计算机和手机已成为人们生活中不可缺少的一部分。

时间在现实生活中是线性、连续的,而在数字世界中是离散的。连续的量是无限的,而离散的量是有限的。数学的神奇之处就是可以在连续和离散、无限和有限之间自由转换。比如人们在现实生活中感受到的温度、声音、颜色等,都是连续的量。通过一定的技术转换,可以将这些连续的信息以不连续的0和1这样的二进制数保存到计算机中,需要的时候再反向转换出来,通过传感器、扬声器、显示器等全方位刺激人的神经和各种感觉,还原当时的场景,并让人身临其境般感受到。这就是科技的魔法和魅力。

1.1.1 颜色和图像

我们先从颜色说起。自然界的颜色来自于太阳光,太阳光是白光,在三棱镜下可以分解为七色光,也就是人们常说的“红橙黄绿蓝靛紫”;反过来,七色光也可以合成白光。后来人们发现,只需要三色光就可以合成白光。人们把这3种颜色命名为三原色,即红、绿、蓝,通常以英文字母R(Red)、G(Green)、B(Blue)表示。像太阳、显示器、灯泡之类的发光体是可以直接发光的,这也是人们可以直接看到的光。

有些材料是可以透光的。比如我们常见的各种霓虹灯,其实里面的灯泡发的是白光(当然,实际上是各种颜色的光组成了白光),而通过在灯泡外面罩上不同颜色的单色透光材料(比如塑料片或玻璃),把其他颜色的光都吸收掉,便可以显示出不同颜色的单色光。不同颜色的灯光变幻闪烁,便有了五彩斑斓的效果。

我们日常所见的不发光体,比如桌子、书本等,它们需要靠反光才能被我们的眼睛看到。反光的原理也很容易理解——它们吸收了其他颜色的光,未被吸收的光被反射出来,我们就看到了它们的颜色。比如红旗只反射红光,而白纸则反射一切颜色的光(当然它也吸收一些光,要不然就成镜子了),黑纸则吸收一切颜色的光。也可以这样理解,一切不发光体其实没有颜色,只是吸收和反射光的颜色的程度不同。这也就是为什么用红光照射白纸,看到的是红色,因为在这种情况下没有其他颜色的光供它反射;而不管用什么颜色的光照射黑色的物体仍是黑色,因为它们本来就不反射光。

所以,不发光体的颜色是由组成它的材料的吸光性决定的,而这些材料的“颜色”与我们眼睛看到的实际颜色是“相反”的。但按人们的习惯,我们也以实际看到的颜色为它们命名,这里有3种主要的颜色,称为三基色,分别是红、黄、蓝。理论上,把具有这3种颜色的物质(颜料,可以想象为很细的粉末或油墨)按不同的比例混合起来,就能得到我们想要的任何颜色。但很不幸,在自然界中,要找到纯净的单色色素是很难的,为了让我们制造出来的东西或印刷出来的作品的颜色与我们的期望无限接近,在大多数时候需要在颜料中掺入黑色。也正是因为这个原因,印刷业常用颜料的颜色通常是青(Cyan)、品红(Magenta,又称洋红)、黄(Yellow)和黑(blacK),简称CMYK。在计算机领域中,这被称为CMYK色彩空间。如果你用过PhotoShop,就一定对这种色彩空间很熟悉,因为很多人用它来制作印刷品和海报。

在本书中,我们很少用到CMYK色彩空间,因为FFmpeg主要用于音视频的处理,而音视频一般在计算机或电视屏幕上播放,屏幕属于“发光体”。因此,在数字显示世界中,一般都使用RGB色彩空间。

一幅图像往往是一个二维的矩形块,有宽度和高度。在计算机中,图像的存储一般使用位图(Bitmap),也就是使用一个一个的点来表示,这个点称为像素(Pixel)。比如宽和高分别为352和288的图像,即横向有352像素,纵向有288像素,一共有101 376像素。如果是RGB色彩的图像,则每个颜色分量称为一个通道(Channel),通常每个像素的每种颜色用1字节(8位)表示,3种颜色就需要3字节,共24位,因而这种颜色表示也称为24位真彩色。这种8位的RGB色彩空间可以表示16 777 216(224)种不同的颜色。

为了描述“透明”的图像,在每个像素上增加一个表示透明度的分量,称为Alpha通道,占1字节,可以表示256(28)种不同的透明度。这种色彩空间称为RGBA,这样一个像素就占4字节。由于4字节正好是一个32位整数,而计算机对整数的计算比一个一个字节计算高效,因此通常用整数来对图像进行计算。但这样就带来一个问题,那就是不同计算机的体系结构是不一样的,主要表现在字节序上。也就是说,一个32位的整数需要占4字节,而在CPU和内存中,地址的实际排列可能是低字节在前,也可能是高字节在前,分别称为小端序和大端序(来源于剥鸡蛋先从小头开始剥还是从大头开始剥)。但在不同体系结构的计算机中存储的图像需要互通,因此就出现了很多不同的存储方式,如RGBA、ABGR、BGRA、ARGB等。

在早期,人们还无法制作彩色的胶片和照片,普遍使用的是灰度图像,如我们常见的黑白照片。在计算机中存储时灰度图像的每像素只需要1字节,可以表示256种灰度。一般用全0表示黑,全1(即255)表示白,中间就是各种灰度。因此,灰度图像比彩色图像占用更少的存储空间。

此外还有一种单色图像,即每像素只需要用1位表示,非黑即白。因此1字节可以表示8像素,这大大节省了空间,但图像的表现力更差。单色图像的一个典型应用场景就是条型码或二维码。不过有时候为了好看,二维码也用灰度或彩色表示,甚至在中间覆盖一些装饰性的彩色图标等。

1.1.2 电影、电视和视频

在电影发明之前,最接近电影的艺术莫过于我国的皮影戏了。然而皮影戏只能现场演出,不能存储播放。普遍认为,电影的发明是源于人们发现了视觉暂留现象[1]。后来,人们又发现用“似动现象”[2]来解释电影原理似乎更为合理。如图1-1所示,所有交叉点的小圆点都是白色的,但看起来像是有些小黑点在动。不管怎么说,电影画面其实就是在人眼前闪过的一帧一帧图像,如果快到一定程度(每秒24帧以上),人眼就误以为这些图像是连续的,如果每帧图像间都有微小的差异,则经过一段时间累积后人眼就看到图像在运动了。

[1] 视觉暂留现象又称“余晖效应”,即人眼在观察景物时,光信号传入大脑神经需经过一段短暂的时间,而光的作用结束后视觉形象并不立即消失,这种残留的视觉称为“后像”,视觉的这一现象则被称为“视觉暂留”。

[2] 简单来讲就是人眼的错觉,本来不动的物体看起来像是在动。

图1-1 似动现象

所以,人们看到的电影其实是快速闪过的、一帧一帧的不连续的图像,每秒闪过的帧数就称为帧率(FPS,Frames Per Second)。后来,电视被发明出来。早期的电视使用阴极射线管(CRT,Cathode Ray Tube)显示器(早期的计算机显示器也是这种),原理是利用阴极电子枪发射电子,在阳极高压的作用下射向荧光屏,使荧光粉发光,同时电子束在偏转磁场的作用下做上、下、左、右的移动来进行扫描。电子束是一行一行扫描的,从上到下扫描完一遍后即能显示一幅图像,称为一“场”。由于荧光粉发光非常短暂,因此电子枪需要不断地扫描才能保持画面的亮度。早期显示器的场频通常与电网频率一致,即50Hz或60Hz。受当时的电源及滤波技术限制,可能会因滤波不良造成非同步干扰。这种干扰表现为在屏幕上出现滚动的黑色横条,其滚动频率为电网频率与场频之间的差拍。现在这个问题已经解决,场频不必与电网频率同步,一般取60~70Hz,高的可达100Hz,85Hz是VESA标准的刷新速率,用85Hz以上的刷新率显示图像才无闪烁感。早期由于受技术条件的限制,电子束并不是一行挨着一行扫描的,而是一场扫描奇数行,下一场扫描偶数行,称为隔行扫描,又叫交错扫描。在后面我们可以看到,很多技术、标准和术语都是在实际应用中受各种技术限制及应对技术限制而制定和发明出来的。

现在,液晶显示器(LCD,Liquid Crystal Display)代替CRT显示器成为主流显示设备。液晶显示器的基本显示单元也是像素,同样需要一行一行地刷新显示,常见的液晶显示器刷新频率有60Hz和75Hz等。显示器上横向和纵向像素的数量就称为显示器的分辨率,一般以“宽×高”表示,如常见的1080p(1920×1080)、720p(1280×720)。其中,p指逐行扫描,如果是1080i,则指隔行扫描。所以,完整扫完1帧(一场)图像,主要是由图像的高度(行数)决定的,这也是为什么我们常说1080p,而不说1920p[3]

[3] 显示器大多数是横屏的,也就是宽度大于高度,但也有人把显示器竖着放。而现在的手机大部分是竖屏,但习惯上还是把分辨率称为1080p。

液晶本身是不发光的,需要使用背光源。而更新的OLED(Organic Light-Emitting Diode,有机发光二极管)显示器则不需要背光源,因为它自己就可以发光。这些都是显示技术。除了图像本身外,图像的靓丽程度跟显示技术也息息相关。苹果公司的设备都使用了很高端的显示屏,Pro版的iPhone支持120Hz的刷新频率,iPhone和计算机的视网膜屏(Retina)更是可以提供超过平常分辨率一倍的分辨率[4]

[4] 没有对比就没有伤害。当你将视线从普通显示器转到视网膜显示器时,可能只是感觉看得更清晰了些,好像没有质的变化;但当你再将视线转回普通显示器时,就会发现后者的颗粒感相当明显。

一块屏的分辨率达到300ppi以上,我们就叫它视网膜屏。ppi(Pixels Per Inch,每英寸的像素数,也称dpi,Dots Per Inch)是描述最高分辨能力的单位。视力为1.0的人观看离双眼10~12英寸(约25~30厘米)的手机屏时,最高能分辨300ppi。只有当图像与显示器的分辨率匹配时,才能达到最好的显示效果。

图像一帧一帧地快速切换,就形成了视频。fps描述的是1秒内闪过的帧数,目前常见的视频都是25fps以上的。1帧1080p的图像有2 073 600像素,如果按8位色深的RGB格式存储,需要约6MB的存储空间,按25fps计算,1秒钟就需要150MB,典型的1.5小时的电影需要约800GB的存储空间。如果从网上实时观看视频,视频传输速率一般以bit/s(即Bit Per Second)计算,根据上面的计算结果,需要1200Mbit/s的带宽,超过一个千兆以太网的带宽。

为了降低存储空间和传输带宽的占用,可以使用视频压缩技术,视频压缩也称为“视频编码”。常用的视频压缩技术的原理是运动估计和运动补偿。简单来说,就是两点:其一,人眼并不是对所有颜色都敏感,因而可以去掉一些颜色,对图像进行压缩,这称为有损压缩,也是一些典型图像如JPEG的主要压缩原理;其二,一般来说,两帧图像间的差异其实不大,可以根据这个特性,只存储或传输图像间的差异信息,而不是整帧图像。常见的视频编码如H.264(AVC)、H.265(HEVC)、VP8、AV1等都是基于这些原理压缩的。图像压缩后可以大大降低存储空间和带宽的占用,比如常见的1080p@25fps视频,只需要2Mbit/s~4Mbit/s的传输带宽就够了。值得一提的是,实际占用的带宽与视频的复杂度(画面细节、运动快慢程度等)和帧率、分辨率都是正相关的,比如同样分辨率和帧率的视频,一部动作片肯定比课堂上老师讲课的视频要占用更多的带宽(因为前者帧间变化更多、差异更大),提高分辨率和帧率也会相应提高所需带宽。

在音视频应用中,视频在传输到对端后要进行“解码”,即将压缩过的视频再转换成一帧一帧的图像,才能送到显示器上显示。整个过程合起来称为“编解码”。

需要编解码的视频图像一般不使用RGB色彩空间,而是使用一种称为YUV的色彩空间。两者之间有直接的对应关系,但由于转换过程涉及浮点运算,转换是有损的,但对人眼来说几乎无法分辨,因而是完全可以接受的。YUV也是一种颜色编码方法,“Y”表示明亮度,“U”和“V”则分别表示色度和浓度。不同于RGB图像一般按像素存储(如RGBRGBRGBRGB),YUV图像一般按平面存储,即将所有的Y放到一起,所有的U放到一起,所有的V放在一起(如YYYYUUUUVVVV),其中每一部分称为一个平面。这种存储方式的一个好处就是,在广播电视中,当接收到一帧图像时,黑白电视机只需要播放Y平面(黑白图像),而忽略代表颜色的U和V平面。当然,彩色电视机则需要播放所有平面的数据。

在这种编码算法下,如果编码后一帧的数据丢失,则会影响后面的解码,如果强行解码,就会出现花屏等现象(因为部分图像间的差异信息找不到了)。因而,在实际的编码器上,一般会对图像分组,分组后的图像称为GoP(Group of Pictures)。每隔一定数量(比如100帧)的图像,就对一帧完整的图像进行编码,其编码过程不依赖于它前后的图像,这里主要是不依赖图像间的差异编码。这种不依赖前后图像、可单独编解码的图像一般被称为I帧,因此整个GoP序列的第1帧也被称为关键帧[5]。这样,即使前面丢了很多数据,只要一个新的关键帧到来,就能继续正确地解码。GoP可以是固定的,也可以是按需的(比如没有数据丢失就不用生成关键帧,或者丢失比较严重时就多生成几个关键帧)。有些编码器有场景检测功能,即在场景切换时,两帧间差异太大,以至于共同信息较少或者根本没有共同的信息,这时候就直接生成一个关键帧。

[5] 即Key Frame,在H.264中一般为IDR(Instantaneous Decoding Refresh)帧,IDR帧又称为立即刷新图像。在实际应用中,还有普通的I帧(Intra-coded picture)。I帧和IDR帧还是有区别的,所有IDR帧都是I帧,但不是所有I帧都是IDR帧。简单来说,每个GoP里的第1个I帧是IDR帧,后续的I帧虽然能独立解码,但它前/后面的P帧(或B帧)可能会参考I帧后/前面的帧,但GoP内的帧参考不会跨越IDR帧。当然,在H.265或其他编解码中,对IDR帧和I帧的定义还是有更细微区别的,但总的来说关键帧就是为了刷新图像的。在此为简洁起见,并未特别区分I帧和IDR帧。

在视频编码中,除前面介绍的I帧外,还有P帧(前向预测编码图像帧),它会参考前面的图像,仅对差异部分编码;以及B帧(双向预测编码图像帧),它不仅参考前面的帧,还参考后面的帧,压缩率更高,可以节省更多带宽和存储空间,常用于视频文件的存储。由于B帧需要参考后面的帧,收到B帧后不能立即解码,在实时音视频应用中会带来延迟,因而在实时通信中一般不使用B帧。3种帧的关系如图1-2所示(视频帧产生顺序为从左到右,箭头为帧的参考方向)。

图1-2 I帧、P帧、B帧示意图

1.1.3 音频

前面我们讲了视频,下面再来说说音频。声音是由振动引起的。为了将现实世界中的音频(连续的)放到数字世界(离散的)中,需要执行一个模数转换(Analog-Digital Conversion,ADC),通过传声器的炭精薄片振动调制电流,变成数字信号。模数转换的逆运算称为数模转换(DAC),根据数字信号驱动扬声器振动发声。

为了理解振动,我们先来看看正弦曲线和正弦波。正弦曲线跟圆有关。我们用圆规匀速旋转,就可以在纸上画一个圆;但如果在画圆的同时有人匀速地拖动下面的纸,就可以画出一个正弦曲线,它是一个振幅随时间变化的曲线。振动规律符合正弦曲线的波就称为正弦波。正弦波是完美的波,正如圆是完美的图形一样。圆每转一圈,就对应正弦波的一个周期,如果不停地转下去,就会出现一个连续的周期性的正弦波。单位时间内圆能转多少次,就是圆旋转的频率,也对应正弦波的频率。圆的半径决定了正弦波振动的高度,即振幅。圆规从圆周哪个位置开始画决定正弦波的相位(简单起见,我们在此忽略对相位的讨论)。圆与正弦波的关系如图1-3所示。

图1-3 圆与正弦波

但世界是不完美的。世界上有各种美妙的声音,也有各种噪声。不过,不完美的世界也有完美的数学——借助傅里叶变换,任何声音的波形都可以分解为有限个或无限个完美的正弦波,也可以理解为分解为很多个有着不同转速、不同半径的圆。圆的半径决定声音的大小(响度、音量),转速决定频率。振幅越大,声音就越大;频率越大,声音就越“尖”(比如通常来说女声比男声尖,那是因为女性声带振动得快,即在单位时间内振动的次数比男性多)。如图1-4所示是几种不同频率的正弦波及它们的叠加波形图。

图1-4 正弦波及叠加波形示意图

傅里叶变换的原理如图1-5所示。时域中的音频波形如图1-5a所示,它的振幅(z轴)是随时间(x轴)变化的,可以分解为多个不同振幅(z轴,振幅分别为1.5、0.8、1.2)、不同频率(1/2π、1/π、3/2π)的正弦波,如图1-5b所示。这些正弦波在频域的投影如图1-5c所示。也就是说,在频域中,只能看到不同频率对应的振幅。如果想检测某一频率是否存在,或者想消除某些频率的波,在频域中处理起来就非常简单。图1-5b中的图像看起来有些乱,把它以z轴为中心顺时针旋转,让不同频率的波在y轴延伸,可以更加直观地看到频率(频谱)分布。其中图1-5d、e、f分别是旋转45°、60°及80°的三维视图[6]。当旋转角度变成90°的时候,就又回到了图1-5c,不同频率的波变成了以振幅为高度的竖线,这就是原始音频波形的频谱图。总之,xz平面是时域图像,yz平面是频域图像,时域和频域表示的其实是同一个信号,只是看问题的角度不同。

[6] 严格来讲,为了能让三维视图看起来更直观,图中的图像同时也以y轴为中心适当地旋转,只是正文中为了描述方便及突出重点,只提到了z轴。

图1-5 傅里叶变换示意图

有趣的是,声音的大小变化并不是线性的,即声音的刺激与人真正听到的感觉不是线性的,而是呈对数关系。一个对数曲线示意图如图1-6所示,x轴为声音的刺激量,y轴为人的感觉量,即声音的响度(音量),声音的响度以分贝(dB)表示。关于分贝大小与一般人听觉感受的对应关系,读者可以自行查阅了解。

图1-6 对数曲线示意图

响度大小决定是否能听见(听清),而频率大小决定听到的内容。人耳对响度和频率的敏感度如图1-7所示(注意,横轴的刻度不是线性的)。

图1-7 人耳对声音的敏感度

通过采样、量化和编码3个步骤,可以将模拟信号转换为数字信号。采样又称为抽样,它是在时域中按一定的时间间隔(Ts)对模拟信号进行抽样(如图1-8所示),得出一些离散值,然后通过量化和编码过程将这些离散值变成数字信号。单位时间内抽样的次数称为抽样频率,又称采样率。从图1-8中可以看出,抽样频率越高,也就是在单位时间内的采样点越密,离散时间信号与原信号就越接近。但抽样频率不能无限高,那究竟应该多高才能与原信号足够接近呢?根据抽样定理[7],当抽样频率是模拟信号频率带宽(最高频率与最低频率的差值)的两倍时,就能够完全还原原来的模拟信号。

[7] 又称采样定理或奈奎斯特·香农定理。

图1-8 抽样示意图

模数转换通常使用PCM(Pulse Code Modulation,脉冲编码调制)方法,它是一种通用的将模拟信号转换成以0和1表示的数字信号的方法。就普通的电话业务来讲,一般来说,人的声音频率范围为300~3400Hz,通过滤波器过滤超过4000Hz的频率,便得到4000Hz以内的模拟信号。然后根据抽样定理,使用2倍于带宽的抽样频率(即8000Hz)进行抽样,便得到离散的数字信号。使用PCM方法得到的数字信号就称为PCM信号,一般一次抽样得到的值(称为Sample)用2字节(16位)来表示。

与视频类似,音频信号也可以进行压缩。在传统电话业务中,一般使用A律和μ律[8]进行压缩,它们可以将每一个抽样值从16位压缩到8位,这样每秒钟就得到64000(8×8000)位的信号,通常简称为64kbit/s,这也是一路传统电话通信所需的带宽。

[8] PCM的两种压缩方式(实际为压扩法,因为有的部分是压缩,有的是扩张。目的是给小信号更多的位数以提高语音质量)。北美洲使用μ律,我国和欧洲使用A律。这两种压缩方法很相似,都采用8位的编码并获得12~13位的语音质量。但在低信噪比的情况下,μ律比A律略好。A律也用于国际通信,因此,凡是涉及A律和μ律转换的情况,都由使用μ律的国家负责。

电话业务一般只适用于传播人的声音,对于一些高清音乐则会失真严重。为了达到更好的效果,就需要提高抽样频率。现代的4G VoLTE和5G NR通话可以使用16kHz的抽样频率,相比传统电话声音就更清晰,也称为高清(HD)语音。我们平常听的音乐都使用32kHz或更高的采样率,CD音质使用44.1kHz,一些高清音乐也使用48kHz甚至96kHz的采样率。

有两个以上声道的音频称为立体声。最简单的立体声分为左、右两个声道,可以区分音源的远近和位置,听起来更真实。一般来说,双声道立体声的音频都会交错存储,如果以L代表左声道的一个采样点、R代表右声道的一个采样点,则采样数据在内存或文件中的存储方式类似“LRLRLRLR…”。有的音视频文件包含更多声道,称为环绕立体声,可以区分前后左右的声音,听起来更震撼,有身临其境的感觉。

1.1.4 音视频封装、传输和未来

不知不觉,你已经从现实世界走到数字世界了,你还适应吗?

将音频和视频组合在一起称为封装,有时是为了存储到文件,有时是为了实时传输。典型的文件封装方式如MP4,针对文件的元数据及音视频有很多不同的容器,音频和视频一般也是交错存储的,这主要是为了可以实时播放和同步。对于音视频网络传输,在广播电视领域一般使用TS(Transport Stream,传输流或者MPEG-TS)封装和传输,音频和视频也是交错发送的,主要是为了保证实时性。RTMP流一般用于CDN推拉流,也是音频和视频交错发送。SIP及WebRTC通信的实时性更好一些,使用RTP流传输,音频和视频使用不同的流(不同的端口号)发送,有时为了节省端口号也会合并到一个流上发送。

前面讲了立体声,不管有几个声道,本质上还是2D的声场。如果再加上上、下声场,就称为3D音频、3D全景声或6DoF空间声场等。最近几年,AR(增强现实)、VR(虚拟现实)及元宇宙的概念非常火。3D音视频等都需要更多的声道和全景360°的图像及视频支持,而6DoF即6种自由度(Degrees of Freedom)。简单来讲,音视频到了3D以后,不仅需要更多的存储空间(如3D全息图像的点云存储需要海量的存储空间)来描述各声道、图像视角之间的关系,还要支持头部及肢体转动时的实时反馈,以便通过耳机、头显、传感器等设备还原出一个真实世界。

1.2 视频图像像素点的数据格式

前面我们大体讲了一下颜色和图像的基本原理,并初步了解了几种不同的色彩空间。随着图像输出设备支持的规格不同,色彩空间也有所不同,不同的色彩空间能展现的色彩明暗程度、颜色范围等也不同。下面我们再进一步探讨这些色彩空间和像素点的数据格式。

1.2.1 图像的位深

众所周知,一个二进制位可以表示0和1两种状态。在计算机中,1字节由8位组成,可以表示256(即28)种状态。也就是说1字节可以表示256种颜色。如果是灰度图像,则表示256种不同的灰度。表示颜色所使用的位数就称为颜色的位深。彩色图像通常以R、G、B三色表示,每个单色分别计算位深。我们常说的24位真彩色就是3种位深为8的R、G、B颜色的混合,可以表示16 777 216(即224)种颜色。

如果需要表示更多颜色,就需要更多位。常见的有10位位深,表示一个RGB像素需要30位,将近4字节。随着4K、8K视频的出现,以及人们对图像质量越来越高的要求,也出现了12位、16位的位深格式。16位位深的RGB像素(每种颜色分量占2字节)需要6字节的存储空间。在下面的介绍中,为了便于计算,如果没有特别说明,都使用8位位深。

1.2.2 FourCC

世界上有如此多的色彩和图像格式,为了表示不同的图像类型和像素排列格式,人们发明了FourCC[9]代码。FourCC代码是一个32位无符号整数,使用大端序编码4个ASCII字符序列。我们前面讲过的RGBA、ARGB等都是FourCC。与RGB色彩空间类似,YUV图像也有多种像素类型,如YUYV、YUY2、UYUV等,而且YUV图像也支持Alpha通道,如YUVA和AYUV等。

[9] 全称是Four Character Code,即4个字符编码。

苹果公司最早在Macintosh中使用了这种4字节表示法,后来在业界得到了广泛使用,便有了正式的名称——FourCC。微软在DirectX中也使用了FourCC[10]。FFmpeg使用MKTAG宏定义了一些类似FourCC的代码[11],不过没有完整的FourCC列表,FFmpeg内部也没有各种图像格式与FourCC的一一对应关系。但是,在音视频领域中,FourCC经常出现,理解它们有助于我们理解各种像素格式,以及不同系统中图像格式的对应关系。

[10] 参见https://learn.microsoft.com/zh-cn/windows/win32/directshow/fourcc-codes和https://learn.microsoft.com/en-us/windows/win32/ medfound/ 10-bit-and-16-bit-yuv-video-formats。

[11] 如MKTAG('y', 'u', 'v', '2')。

有一些FourCC代码比较直观,如RGBA,字母表示与内存中的排列顺序也相同;有的就稍差一点,如Y444,它表示YUV444格式;常用的YUVI420格式的图像的FourCC代码为YV12或NV12,就不那么直观了。后面还会详细解释一些FourCC。

1.2.3 灰度模式表示

在20世纪八九十年代,国内大多数家庭看的还是黑白电视,黑白电视图像就是以灰度模式展现的图像。在数字时代,灰度图像也以数字形式存储。一般来说,使用8位位深(取值范围为0~255)表示像素的灰度,即像素的明暗程度。0为最黑暗的模式,255为最亮的模式。色彩表示范围如图1-9所示。

图1-9 灰度图

8位位深的一个像素点正好占用1字节。一张图像占用的存储空间大小计算方式也比较简单,即:占用空间 = 宽度(W) × 高度(H) × 1B。举个例子,一帧分辨率为352×288的灰度图像,占用的存储空间为352×288×1B,也就是101 376字节。

1.2.4 YUV色彩表示

YUV诞生于黑白电视向彩色电视过渡的时期。黑白视频是只有Y(Luma或Luminance,即亮度)分量的视频,也就是灰阶值。在彩色电视中,除了Y以外,还使用U和V来表示图像的色度(Chrominance或Chroma,C)。U和V也分别称为Cb、Cr,分别代表蓝色通道和红色通道与亮度的差值。所以说,U和V其实是色差信号(这也是为什么模拟电视的信号连接线也叫色差线),它们告诉电视要偏移某像素的颜色,而不改变其亮度,或者说UV信号告诉显示器使得某个颜色亮度依某个基准偏移。UV的值越高,代表该像素会有更饱和的颜色。图1-10所示是YUV中UV分量数值分布的平面图,其中Y分量值为0.5。

上面所说的C其实等于Cb+Cr,也就是U+V,YUV也就是YCbCr。有些人会说,Y′UV、YUV、YCbCr、YPbPr等专有名词实际上有些差异,但我们并不想把事情弄得如此复杂,所以本书中我们把这些统称为YUV。Y′UV、YUV、YCbCr、YPbPr在实际使用时也常有混淆或重叠的情况。

从历史的演变来说,YUV和Y′UV通常用来编码电视的模拟信号,Y′的上标符号一般表征经过了伽玛校正;而 YCbCr 则用来描述数字影像信号,适合数字化的视频与图片压缩及传输,如MPEG、JPEG。现今数字化的YUV使用得更为广泛。

术语YUV本身在技术和科学文献中没有精确的定义。为了避免歧义,最好的方法是参考国际标准文件中各种YUV色彩空间变体的描述。我们说的YUV很多时候是指YCbCr。

原图与YUV的Y通道、U通道和V通道的图像示例如图1-11所示。

图1-10 YUV中UV分量数值分布平面图[12]

[12] 图片来自维基百科。

图1-11 YUV通道原图与各分量图像示例[13] 

[13] 图片来自维基百科。

YUV图像可以由RGB图像转换而来,对应的计算公式如下:

其中,RGB图像是经伽玛预校正后[14]的。从公式中可以看出,黑色图像的RGB值为(0, 0, 0),YUV值为(0, 128, 128)。有时候在使用YUV色彩空间时会看到亮绿色的纯色图像,那可能是由某些错误导致所有像素的YUV值为(0, 0, 0)引起的[15]

[14] 伽玛(Gamma)校正是一种针对图像或视频帧的预失真校正。CRT显示器所产生的信号强度不是输入电压的线性函数,相反,它与信号幅度的功率成正比,也简称为伽玛。另外,人眼对光的强度的感知程度也不是光的强度的线性函数,人眼在黑暗环境下的辨识能力要强于明亮环境,因此也需要对颜色进行校正。

[15] 注意,数字视频中的YUV值通常不是全值域的,即一般每个分量的取值范围为16~235,而不是0~255。如在FFmpeg中,YUV420P像素格式就不是全值域的,而YUVJ420P则是。在后面还有更多关于图像值域的解释。

相对于Y来说,人眼对UV不大敏感,因此,可以在图像存储时降低UV分量的分辨率(采样率),以节省存储空间,而这种降采样后的图像看起来与原图像没有多大差别。YUV的像素存储格式一般采用“A:B:C”表示法,根据采样和降采样的程度不同,以及像素排列格式的不同,有很多不同的表示。为了便于理解,下面以352×288和2×2的图像大小为例,分别详细介绍各采样格式的区别。其中,2×2图像的4个像素编号如图1-12所示。

图1-12 4像素图像示意图

1.YUV444格式

YUV444表示4:4:4的YUV取样,水平每4像素中YUV各取4个,即每像素中YUV各取1个。所以每1×1像素Y占1字节,U占1字节,V占1字节,YUV444格式下平均每像素占(1+1+1)× 8bit/1pix = 24bpp(bpp为Bit Per Pixel,即每像素位数),即3字节。那么352×288分辨率的一帧图像占用的存储空间为352×288×24/8 = 304 128 (字节)。这种格式实际上是一种全采样格式,它与RGB格式的图像占用相同的存储空间。

YUV444格式的图像可以有两种存储格式:按像素存储和按平面存储。以2×2的图像为例,像素存储格式为Y1U1V1 Y2U2V2 Y3U3V3 Y4U4V4,平面存储格式为Y1Y2Y3Y4 U1U2U3U4 V1V2V3V4

2.YUV422格式

YUV422表示4:2:2的YUV取样,水平每2像素(即2×1的2像素)中Y取样2个,U取样1个,V取样1个,所以每2×1像素Y占2字节,U占1字节,V占1字节,YUV422格式下平均每像素占(2+1+1)×8bit/2pix = 16bpp。那么352×288分辨率的一帧图像占用的存储空间为352×288× 16/8 = 202 752 (字节)。

该格式对应的FourCC代码有YUYV、YVYU、UYVY、VYUY等,表示U、V的不同取样点和YUV分量的不同排列顺序。其中YUYV与YUY2的实际存储格式相同,对于2×2的图像,像素存储格式为Y1U1Y2V2Y3U3Y4V4。可以看到,与YUV444的图像格式相比,由于省略了V1、U2、V3、U4,从而节省了4字节的存储空间。在实际显示时,缺少的U和V使用相邻像素的U和V补充回来即可,反正人眼也看不出多大差别。

3.YUV411格式

YUV411表示4:1:1的YUV取样,水平每4像素(即4×1的4像素)中Y取样4个,U取样1个,V取样1个,所以每4×1像素Y占4字节,U占1字节,V占1字节,YUV411格式下平均每像素占(4+1+1)×8bit/4pix = 12bpp。那么 352×288 分辨率的一帧图像占用的存储空间为352×288× 12/8 = 152 064 (字节)。对应的FourCC代码为Y411,像素存储格式在此略过。

4.YUV420格式

YUV420表示4:2:0的YUV取样,水平每2像素与垂直每2像素(即2×2的2像素)中Y取样4个,U取样1个,V取样1个,所以每2×2像素Y占4字节,U占1字节,V占1字节,YUV420格式下平均每像素占(4+1+1) × 8bit/4pix = 12bpp。那么 352×288 分辨率的一帧图像占用的存储空间为352×288×12/8 = 152 064 (字节),相比YUV444格式正好节约一半的空间。

以上是标准的解释,但似乎还是无法解释4:2:0中“0”的含义。确实,这个表示法就是比较令人费解。其实可以换一种方法理解:对于水平每4像素,Y取4个,U取2个,V取0个,这便是4:2:0的含义。但是,这个解释并不完整。在下一行取样时,应该是Y取4个,U取0个,V取2个,即4:0:2。所以说,这里的4:2:0其实是代表了4:2:0和4:0:2两种情况,它们在奇偶行交错出现。

这种图像格式又称为YUVI420,其实就是把邻近的4像素(2×2,即当前像素、右、下、右下)都用同一个U和V,而原先的Y不变。正是基于这个原因,一般的编码器都要求原始图像的宽和高是偶数。除此之外,编码器一般会将图像划分成 2×2、4×4、8×8、16×16 等块进行各种预测和比较。常见的H.264、H.265、VP8、AV1等都是以它为基础进行编解码的。

这种图像格式使用得非常广泛。为便于理解,我们以一幅4×4的图像进行拆解,如图1-13所示。它表示YUV444格式的图像,其中每个像素分量的下标以(i,j)表示,分别表示第i行第j列。

图1-13 YUV444像素格式

把它转换成以平面形式存储的格式,即Y、U、V平面分别连续存储,如图1-14所示。

图1-14 YUV平面存储格式

把3个平面分开来看会更直观,如图1-15所示。

图1-15 YUV 3个平面的示意图

对2×2区域的4个U和V像素进行下采样,只保留一个U和V,如图1-16所示。

图1-16 U、V下采样示意图

把有效采样的YUV数据连续排列,便得到最终数据,如图1-17所示。

图1-17 YUVI420像素格式

在上述下采样的过程中,我们使用了2×2图像区域中最左上角的U和V值,实际上,可以使用4个值中的任意一个,甚至也可以使用它们的平均值。但由于这4个值其实非常接近,并且人眼对它们也不敏感,因而在实际使用时一般都是用最简单的方法来随便选取一个。

上述格式对应的FourCC代码为I420或IYUV。此外,还有一种YV12格式,与I420的区别是U和V平面的顺序相反,如图1-18所示。在安卓系统中,普遍使用NV12的像素格式,它与I420格式相比,Y平面没有区别,但U和V平面像素是交错存储的,是一种“半平面半交错”的存储方式,如图1-19所示。

图1-18 YV12像素格式

图1-19 NV12像素格式

1.2.5 RGB色彩表示

三原色光模式又称RGB颜色模型或红绿蓝颜色模型,是一种加色模型,将红(Red)、绿(Green)、蓝(Blue)三原色的色光以不同的比例相加,便合成各种色彩的光。如图1-20所示是一个光的合成示意图。

图1-20 三原色合成示意图

RGB颜色模型的主要用途是在电子系统中检测、表示和显示图像,其原理是利用大脑强制视觉生理模糊化(失焦),将红、绿、蓝三原色子像素合成一个色彩像素,产生感知色彩(其实此真彩色并非加色法所产生的合成色彩,因为该三原色光从来没有重叠在一起,只是人类为了“想”看到色彩,大脑强制眼睛失焦而形成的)。RGB颜色模型在传统摄影中也有应用,在电子时代之前,基于人类对颜色的感知,RGB颜色模型已经有了坚实的理论支撑。

RGB是一种依赖于设备的颜色空间,不同设备对特定RGB值的检测和展现不一样。颜色物质(荧光剂或者染料)和它们对红、绿和蓝的单独展现情况随制造商的不同而不同,甚至同样设备在不同时间的展现情况也不同。在彩色CRT显示器中,各种颜色荧光粉的排列如图1-21所示。其中R、G、B三个荧光点代表一个像素,由于这些荧光点离得非常近,人眼看起来就像是光被混合在一起了一样。不同显示器的荧光点的排列也不一样。图1-22展示了一些不同显示设备的荧光点(或发光点)排列方式。

图1-21 CRT显示器颜色排列

图1-22 不同显示器的荧光点排列方式[16]

[16] 图片来源:https://github.com/leandromoreira/digital_video_introduction。

三原色的原理不是由物理原因,而是由生理原因造成的。人的眼睛内有3种辨别颜色(黄绿、绿和蓝紫)的锥形感光细胞,如果辨别黄绿色的细胞受到的刺激略高于辨别绿色的细胞,人的感觉是黄色;若受到的刺激大大高于辨别绿色的细胞,人的感觉是红色。虽然这3种细胞并不是分别对红色、绿色和蓝色最敏感,但这3种颜色的光可以分别对3种锥形细胞产生刺激。

不同生物眼中辨别颜色的细胞并不相同,例如鸟类眼中有4种分别对不同波长光线敏感的细胞,而一般哺乳动物只有两种,所以对它们来说只有两种原色光。

既然“三原色的原理不是由物理原因,而是由生理原因造成的”,那么前面所说的“用三种原色的光以不同的比例加到一起,形成各种颜色的光”显然就不大合适了。使用三原色并不足以重现所有的色彩,准确的说法应该是“将三原色光以不同的比例复合后,对人的眼睛可以形成与各种频率的可见光等效的色觉”。只有那些在三原色的色度所定义的颜色三角内的颜色,才可以利用三原色的光以非负量相加混合得到。例如,红光与绿光按某种比例复合,对3种锥形细胞刺激后产生的色觉可与眼睛对单纯的黄光的色觉等效。但绝不能认为红光与绿光按某种比例复合后生成黄光,或黄光是由红光和绿光复合而成的。

使用8位位深对RGB像素进行编码,每像素需要24位,这是当前主流的标准表示方法,用于真彩色与JPEG或者TIFF等图像文件格式里的通用颜色交换。它可以产生1600多万种颜色组合,对人类的眼睛来说,其中有许多颜色是无法确切分辨的。上述定义使用名为“全值域RGB”的约定。颜色值也经常被映射到0.0到1.0之间,这样可以方便地映射到其他数字编码上。

使用每原色8位的全值域RGB可以有256个级别的“白-灰-黑”深浅变化,255[17]个级别的红色、绿色和蓝色(及它们的等量混合)的深浅变化,但是其他色相的深浅变化要少一些。由于伽玛校正(非线性)的影响,256个级别不表示同等间隔的强度。

[17] 因为0表示没有色彩值,所以这里的色彩值是255个级别。

在典型使用上,数字视频的RGB不是全值域的。视频RGB使用有比例和偏移量的约定,即(16,16,16)是黑色,(235,235,235)是白色。例如,这种比例和偏移量用在了CCIR 601的数字RGB定义中。

RGB常见的展现方式分16位模式和32位模式。16位模式通常由RGB565、BGR565、ARGB1555、ABGR1555等不同的模式表示,其中的数字表示色彩对应的位数。一般每种原色各为5位,多出的1位分给绿色,因此绿色变为6位,这主要是因为人眼对绿色更敏感。但某些情况下每种原色各占5位,余下的1位不使用。

32位模式(也称为ARGB8888)实际就是24位模式,余下的8位不用于表示颜色,这种模式是为了提高数据处理的速度(每像素正好对应一个32位整数)。同样在一些特殊情况下,如DirectX、OpenGL等环境,余下的8位用来表示像素的透明度(Alpha)。如图1-23所示是RGB色彩分布直方图。

图1-23 RGB色彩分布直方图[18]

[18] 图片来自维基百科。

一般来说,我们理解的RGB都是线性的。但在CRT显示器中,色彩的亮度跟输入电压的关系不是线性的,而是呈指数关系,这个指数就称为伽玛(γ)。伽玛是一个经验值,而不是用数学公式计算出来的,而且,不同的设备(包括但不限于CRT显示器)的伽玛值也不一样,一般伽玛取值为2.5。考虑到人眼对光线的反应也不是线性的,需要对伽玛曲线小区间的线性关系做一定的修正,业界一般使用2.2作为修正后的伽玛值。从图1-24a可以看出,伽玛曲线在线性曲线的下方,这样显示器显示出来的图像会比实际图像暗一些。为了看到正常的图像,就需要对显示设备进行校正,但是校正显示设备太复杂,更经济的做法是修改图像本身。如图 1-24b 所示,如果将原来的线性曲线校正成向上突起的曲线,那么原来的伽玛曲线就会变成线性的。这个校正的伽玛值为2.2的倒数,约等于0.45。

图1-24 伽玛校正示意图

照相机的感光器件是线性的,得到的是线性的RGB图像。而经过伽玛校正后的图像所使用的RGB空间是非线性的,称为sRGB(Standard RGB),它是由微软公司和惠普公司在1996年一起开发的一种色彩空间标准,这种标准得到业界许多厂商的支持。sRGB对应的就是伽玛0.45所在的色彩空间,校正公式为:校正后的值 = 校正前的值0.45。一般来说,实际的图像都是使用非线性的sRGB空间(包括使用调色板选择颜色时),而在做图像计算和处理(比如将亮度加倍)时,则使用线性RGB空间更方便。

现在,LCD和OLED显示器成为显示设备的主流产品,理论上它们不存在CRT显示器的非线性问题,但为了能正常显示已经成为标准的sRGB图像,这些显示器也参照CRT显示器做了伽玛校正。

1.2.6 HSL与HSV色彩表示

虽然视频的采集和最终终端播放采用的都是RGB色彩空间,但是对人眼而言,RGB其实并不直观,比如我们很难马上反应得出粉红色的RGB色值。为了能够更直观地表示颜色,HSL和HSV色彩模型被引入,它们是通过将RGB色彩空间中的点映射到圆柱坐标系中实现的,这两种表示方法都试图做到比基于笛卡儿直角坐标系的几何结构RGB更加直观。比如,想从黄色过渡到红色,只需要调整色相即可,而饱和度和亮度可以保持不变。因此,HSL和HSV一般更适合人的色彩感知,而RGB更适合应用于显示领域。

HSL即色相(Hue)、饱和度(Saturation)、亮度(Lightness),HSV即色相(Hue)、饱和度(Saturation)、明度(Value),又称HSB,其中B即英语Brightness的首字母。色相(H)是色彩的基本属性,就是平常所说的颜色名称,如红色、黄色等。饱和度(S)是指色彩的纯度,越高则色彩越纯,越低则逐渐变灰,取值范围为0~100%。明度(V)、亮度(L)的取值范围为0~100%。

HSL和HSV二者都把颜色描述为圆柱坐标系内的点,这个圆柱的中心轴取值范围为自底部的黑色到顶部的白色,而在它们中间的是灰色,绕这个轴的角度对应“色相”,到这个轴的距离对应“饱和度”,而沿着这个轴的高度对应“亮度”“色调”或“明度”。

这两种表示的目的类似,但在方法上有所区别。二者在数学上都是圆柱,但HSV在概念上可以被认为是颜色的倒圆锥体(黑色在下顶点,白色在上底面圆心);而HSL在概念上表示一个双圆锥体和圆球体(白色在上顶点,黑色在下顶点,最大横切面的圆心是灰色)。注意,尽管在HSL和HSV中“色相”指相同的性质,但它们的“饱和度”的定义是明显不同的。

因为HSL和HSV是依赖设备的RGB的简单变换,(h, s, l)或(h, s, v)三元组定义的颜色依赖于所使用的特定红色、绿色和蓝色(加法原色),每个独特的RGB设备都伴随一个独特的HSL和HSV空间,但是(h, s, l)或(h, s, v)三元组在被约束于特定RGB空间(比如sRGB)时就更明确了。

HSV模型在1978年由埃尔维·雷·史密斯创建,它是三原色光模式的一种非线性变换,如果说RGB加色法是三维直角坐标系,那么HSV模型就是球面坐标系。HSV模型通常用于计算机图形应用中。HSV模型在日常图像处理场景中应用得更普遍一些,其中色相表示为圆环,可以使用一个独立的三角形来表示饱和度和明度。在这种方式下,选择颜色可以首先在圆环中选择色相,再从三角形中选择想要的饱和度和明度。

HSV模型的另一种可视方法是圆锥体。在这种表示中,色相被表示为绕圆锥中心轴的角度,饱和度被表示为从圆锥的横截面的圆心到这个点的距离,明度被表示为从圆锥的横截面的圆心到顶点的距离。某些表示使用了六棱锥体,这种方法更适合在一个单一物体中展示这个HSV色彩空间。但是由于它的三维本质,它不适合在二维计算机界面中选择颜色。

HSV色彩空间还可以表示为类似于上述圆锥体的圆柱体,色相沿着圆柱体的外圆周变化,饱和度沿着距离横截面的圆心的远近变化,明度沿着横截面到底面和顶面的距离变化。这种表示可能被认为是HSV色彩空间更精确的数学模型,但是在实际中可区分出的饱和度和色相的级别数目随着明度接近黑色而减少。此外计算机使用有限精度范围来存储RGB值,这约束了精度,再加上人类颜色感知的限制,使得圆锥体表示在多数情况下更实用,如图1-25所示。

图1-25 HSV圆锥图[19]

[19] 图片来自维基百科。

HSL类似于HSV。对于一些人来说,HSL更好地反映了“饱和度”和“亮度”作为两个独立参数的直觉观念,但是对于另一些人来说,它的饱和度定义是错误的,因为非常柔和的几乎白色的颜色在HSL中可以被定义为是完全饱和的。对于HSV和HSL哪个更适合于作为人类用户界面是有争议的。

W3C的CSS3规定声称“HSL的优点是它对称于亮与暗(HSV就不是这样)”,这意味着,在HSL中,饱和度分量总是从完全饱和色变化到等价的灰色(在HSV中,在极大值V的时候,饱和度从全饱和色变化到白色,这可以被认为是反直觉的);在HSL中,亮度跨越从黑色经过选择的色相到白色的完整范围(在HSV中,V分量只走一半行程,从黑色到选择的色相)。

在软件中,通常以一个线性或圆形色相选择器,以及在其中为选定的色相选取饱和度和明度或亮度的一个二维区域(通常为方形或三角形)形式,给用户提供基于色相的颜色模型(HSV或HSL)。在这种表示下,HSV和HSL之间的区别就无关紧要了。但是很多程序还允许用户通过线性滑块或数值录入框来选择颜色的明度或亮度,而对于这些控件通常要么使用HSL,要么使用HSV(而非二者),但传统上HSV更常用。

1.3 视频逐行和隔行扫描、NTSC与PAL制式

本节将要介绍的这些术语都来自电视和CRT显示器,它们的产生都有相关的历史原因。有些术语和技术随着时代的发展已经不再使用了,而有一些则深深影响了现代音视频技术,并以某种形式继续发挥着作用。

1.3.1 逐行与隔行扫描

隔行扫描(Interlaced)是一种将图像显示在扫描式显示设备上的方法,如阴极射线管(CRT)。相比逐行扫描,隔行扫描设备交替扫描偶数行和奇数行,占用带宽比较小。在PAL制式和NTSC制式中,都是先扫描奇数行,即奇数场,再扫描偶数行。隔行扫描效果如图1-26所示。

图1-26 隔行扫描效果图

非隔行扫描(即逐行扫描)通常从上到下扫描每帧图像。这个过程消耗的时间比较长,阴极射线的荧光衰减将造成人眼视觉的闪烁感觉。当频宽受限时,使用逐行扫描可能无法达到人眼感觉没有闪烁的效应,因此通常采用一种折衷的办法,即每次只传输和显示一半的扫描线,即一场只包含偶数行(即偶场)或者奇数行(即奇场)扫描线。由于视觉暂留效应,人眼不会注意到每场只有一半的扫描行,而会认为看到的是一帧完整的图像。

假设使用CRT显示器,那么如果不使用隔行扫描,就需要采用下面的方式之一:

将传输频宽加倍,按帧而不是按场传输图像。这能够提高图像品质,提供的有效分辨率和闪烁速率是相同的。

使用相同的传输频宽,按帧传输分辨率为原来一半的图像。这时候图像细节少了,闪烁速率仍旧相同。

使用相同的传输频宽,按帧传输图像,但是帧率为隔行扫描场率的一半。这时闪烁速率降低一半,眼睛非常容易产生疲劳。

与上一个相同,但是使用一个数字缓存将同一帧显示两次。这时闪烁速率相同,但是显示器上的运动图像看起来不那么平滑,影响视觉质量。

通常有一种误解是,偶场和奇场是由同一帧图像分拆得来的。实际上,摄像机采集的方式和隔行扫描显示的方式是完全相同的。当摄像机采集图像时,偶场和奇场不是同时采集的。例如在一个每秒50场的摄像机中,第122行和第124行的采集在采集第121行和第123行的大约1/50秒之后进行。所以如果把一个偶场和奇场简单拼合在一起,水平方向的运动会造成两场边界不能完美拼合。

在现代显示器和电视中,由于非隔行扫描显示刷新率的提高,使用者已经不再会感觉到闪烁现象,因此,隔行扫描技术已经逐渐被取代。

1.3.2 NTSC制式

NTSC制式又简称为N制,是1952年12月由美国国家电视系统委员会(National Television System Committee,NTSC)制定的彩色电视广播标准,两大主要分支是NTSC-J(日本标准)与NTSC-US(又名NTSC-U/C,美国、加拿大标准)。它们属于同制式,每秒60场,扫描线为525行,隔行扫描,水平分辨率相当于330,画面比例为4:3。

NTSC制式的色度信号调制包括平衡调制和正交调制两种,解决了彩色、黑白电视广播兼容问题,但存在相位容易有损、色彩不太稳定的缺点,故有人戏称NTSC为“Never The Same Color”或“Never Twice the Same Color”(不会重现一样的色彩)。美国、加拿大、墨西哥等大部分美洲国家,以及日本、韩国、菲律宾等国均采用这种制式。

美国国家电视系统委员会于1940年成立,隶属于美国联邦通信委员会(FCC),成立的目的是解决各公司不同的电视制式的分歧,从而统一全国电视发送制式。1941年3月,委员会根据无线电制造协会于1936年的建议,发布了关于黑白电视机技术的标准。该标准能提升更高的图像画质。NTSC制式使用525条扫描线,较RCA公司使用的441线更高(当时此标准已经在NBC网络使用)。而飞歌公司、DuMont公司有意将扫描线提升至605~800线之间。NTSC标准同时建议帧幅为每秒30帧,每帧由两场交错扫描线组成,每场由262.5条线组成,每秒约60场。委员会在最后建议使用4:3画面比例,并使用FM调制伴音(在当时是新技术)。

1950年1月,委员会的职责改为为彩色电视制定标准化的标准。在1953年12月,崭新的电视制式名称直接使用该组织名称的简写,也就是今天所称的NTSC制式(后来又定义为RS-170A)。该彩色电视标准保留了与黑白电视机的兼容性。彩色信号加载在原黑白信号的副载波中,大约是3.58MHz(4.5×455/572MHz)。为了消除由彩色信号及伴音信号所产生的图像干扰,每秒帧幅由30帧稍微下调至29.97帧,同时线频由15 750Hz稍微下降至15 734.264Hz。

在彩色电视标准还没有统一时,当时美国本土的电视台、电器公司都有各自的标准。其中一种为哥伦比亚广播公司使用的制式。这个标准不能与黑白电视兼容,它使用彩色旋转轮,因为技术所限,扫描线由官方标准525线下降至405线,但场频则由每秒60帧大幅提升至每秒144帧(恰巧为24帧等效倍数值)。1951年,美国国防动员办公室(ODM)限制广播,间接使得各家公司相继放弃自家制式,而归功于法律诉讼成功,RCA公司可以继续使用自家制式广播直至1951年6月。哥伦比亚广播公司自家制式亦在1953年3月废止,同年12月17日由联邦通信管理委员会的NTSC制式取代。

NTSC彩色电视标准后来被其他国家采用,包括美洲国家及日本。在数字电视广播大行其道的今天,传统NTSC广播制式逐渐淡出历史。自2009年开始,美国电视已经完全实施数字化,再也没有电视节目使用NTSC制式播出了。

1.3.3 PAL制式

PAL制式是电视广播中色彩调频的一种方法,全名为逐行倒相(Phase Alternating Line)。除了北美、东亚部分地区使用NTSC制式,中东、法国及东欧采用SECAM制式以外,世界上大部分地区都是采用PAL制式。PAL制式于1963年由德国人沃尔特·布鲁赫提出,当时他在Telefunken公司工作。

20世纪50年代,西欧正计划推广彩色电视广播,不过当时NTSC制式本身已有不少缺陷,比如当接收条件差时容易发生色相转移现象。为了克服NTSC制式本身的缺点,欧洲开始自行研发适合欧洲本土的彩色电视制式,也就是后来的PAL制式和SECAM制式。两者图像频率同为50Hz,不同于NTSC的60Hz,更适合欧洲本身的50Hz交流电源频率。

英国广播公司是最早使用PAL制式的电视台,于1964年在BBC2试播,1967年正式开始全彩广播;德国在1967年开始使用PAL制式广播;国际电信联盟于1998年在其出版物上将PAL制式正式定义为“Recommendation ITU-R BT.470-6, Conventional Television Systems”。

PAL发明的原意是要在兼容原有黑白电视广播格式的情况下加入彩色信号。PAL的原理与NTSC接近。“逐行倒相”的意思是每行扫描线的彩色信号会跟上一行倒相,作用是自动改正在传播中可能出现的错相。早期的PAL电视机没有特别的组件来改正错相,有时严重的错相仍然会被肉眼明显看到。近年的PAL电视机会把上一行的色彩信号跟下一行的色彩信号平均起来再显示,这样PAL的垂直色彩分辨率会低于NTSC;但人眼对色彩的敏感程度比对光的明暗要弱,因此影响不是很明显。

NTSC电视机需要色彩控制来手动调节颜色,这也是NTSC的最大缺陷之一。

PAL本身是指色彩系统,经常被配以625线、每秒25帧画面、隔行扫描的电视广播格式,如B、G、H、I、N。PAL也有配以其他分辨率的格式,如巴西使用的M广播格式为525线、29.97帧(与NTSC格式一样),用NTSC彩色副载波,但巴西是使用PAL彩色调频的。现在大部分的PAL电视机能收看以上所有不同系统格式。很多PAL电视机甚至能同时收看基带的NTSC-M,例如电视游戏机、录影机等的NTSC信号,但是它们不一定能接收NTSC广播。

当影像信号是以基带发送时(例如电视游戏机、录影机等),便没有以上所说的各种以字母区分广播格式的区别了。在这种情况下,PAL的意思是指625条扫描线、每秒25帧画面、隔行扫描、PAL色彩调频。对于数字影像如DVD或数字广播,制式也没有区别,此时PAL是指625条扫描线、每秒25帧画面、隔行扫描,即与SECAM一模一样。

英国、中国香港、中国澳门使用的是PAL-I,中国大陆使用的是PAL-D,新加坡使用的是PAL B/G或D/K。

1.4 帧率、PTS和DTS

由于技术条件的限制,早期电视的刷新频率是由交流电的频率决定的。一般来说,使用PAL制式的国家使用50Hz的交流电[20],而使用NTSC制式的国家大都使用60Hz的交流电。由于隔行扫描,实际的帧率减半,便有了25帧/秒和30帧/秒两种帧率。

[20] 当然也有例外,比如巴西就使用60Hz交流电,电视为PAL-M制式,但场频与NTSC制式的电视一样,也是60Hz,每秒29.97帧。

电影一般是以每秒24帧拍摄(这是保证人眼能看到连贯视频动作的最低帧数),在PAL制式的电视上播放电影时会以每秒25帧播放,播放的速度因而比电影院内或NTSC电视广播(NTSC由于差距太大会做相应调整)加快了4%。这种差别不太明显,但电影内的音乐会因而变得高了一个半音(有人说是0.7个半音)。如果电视台在广播时没有加以调校补偿,观众仔细聆听便会发现音高的区别。

NTSC制式的帧率本应该是30帧/秒,但为了解决由彩色信号及伴音信号所产生的图像干扰问题,调至29.97帧/秒。实际上29.97是个近似值,它本是一个无限循环小数,转换成分数形式便是30000/1001,也就是说,在30帧/秒的帧率下,本来1000秒可以播放3万帧,调慢后需要1001秒才可以播放3万帧。

29.97与30实际上没多大区别,以30帧/秒录制的视频可以直接在NTSC制式的电视上播出,但需要注意调整音视频同步。如果不加调整,对于一部2小时的电影,播放到最后,音视频大约会相差7秒((30−29.97)(帧/秒) ×3600(秒) ×2/30(帧/秒))。

现在的数字视频和数字显示器已没有这些问题,但为了兼容这些不同帧率的视频源还需要做各种适配和转换。上面描述了帧率24与25、30与29.97间的转换方法。如果帧率差距比较大,就需要均匀地丢帧或插帧,并适当融合插帧处前后两帧的内容以便过渡得更平滑。

上面讲的都是固定帧率的视频。在互联网上的实时音视频应用中,当网络条件不好时(拥塞、丢包),通信双方会协商降低码率,相应地可能会降低分辨率和帧率,降低分辨率会导致模糊,降低帧率会导致画面跳跃(卡顿),但总比花屏或长时间卡住要好。这种非恒定的码率和帧率就称为可变码率(Variable Bit Rate,VBR)和可变帧率(Variable Frame Rate,VFR)。

在视频编码时,每帧被编码的图像都有一个时间戳,以便在播放时能正确地显示时间。时间戳可以是真正的钟表时间(一般使用相对时间),在帧率恒定的情况下也可以直接使用1、2、3、4……这样的连续时间戳,这个时间戳就称为PTS(Presentation Time Stamp),即播放时间。

有些视频编码(如H.264和H.265)中有B帧。解码器在收到B帧后不能直接解码,而是要等到收到它后面的与之相关的P、B帧后才能解码,也就是说,如果解码器收到帧的顺序是IBBP,实际的解码顺序是IPBB,但播放顺序仍是IBBP,这就是播放时间和解码时间不一致的现象。每帧图像也有一个独立的解码时间戳,即DTS(Decode Time Stamp)。在没有B帧的情况下,PTS和DTS可以是相同的。

1.5 图像分辨率与宽高比

当人们谈论流畅、标清、高清、超高清等清晰度指标的时候,其实主要想表达的是分辨率。但除了分辨率之外还需要结合视频的类型、场景等设置合适的码率,随着视频平台竞争越来越激烈,网络与存储的开销越来越高,有了各种定制的编码及图像处理算法,以便在相同分辨率的情况下做出更多的优化,比如极速高清、极致高清、窄带高清等。但是人们常规对流畅、标清、高清、超高清等清晰度的理解,普遍还是以分辨率为主导的理解。一般而言,分辨率越高代表影像质量越好,能表现出越多的细节;但同时因为记录的信息越多,文件也会越大。个人计算机里影像的分辨率主要由像素密度和像素总数组成,像素密度为单位长度内的像素数量除以单位长度,单位为ppi(Pixels Per Inch)。像素密度越高,说明像素越密集,如5ppi表示每英寸有5像素,500ppi表示每英寸有500像素,像素密度的数值高,图片和视频的清晰度就高。像素总数为图像、影像中单独一帧图所含像素的数量,单位为像素,计算方式为长边的像素数乘以短边的像素数。在提到显示分辨率的时候,人们常常会提到宽高比,即DAR(Display Aspect Ratio)。如图1-27所示为不同分辨率的图像。

图1-27 不同分辨率的图像

在日常应用中各家公司的分辨率档位定义不尽相同,但是在国际标准中还是有一个参考定义的,并且分辨率都有定义名称,读者可自行上网查看定义的规格。

1.6 图像的色彩空间

当人们日常看电视和计算机屏幕中或打印机打印出来的视频图像的时候,同一张图像会有颜色差异,甚至不同的计算机屏幕、不同的电视看到的视频图像有时也会存在颜色差异。之所以会出现这样的差异,主要是受到了色彩空间参数的影响。这里说的色彩空间也叫色域,就是指某种表色模式所能表达的颜色的范围区域,也指具体设备,如显示器、打印机等印刷和复制所能表现的颜色范围。而不同的标准支持的范围不同,如图1-28~图1-30所示,它们分别为基于CIE模型表示BT601、BT709和BT2020的色彩范围。

图1-28 BT601色彩范围

图1-29 BT709色彩范围

图1-30 BT2020色彩范围

色彩空间除了BT601、BT709和BT2020以外,还有很多标准格式,具体的标准就不在本书一一列举了。在用到的时候,需要使用参考标准(如H.273)进行对比。当有人反馈偏色问题时,可以优先考虑是否是由色彩空间的问题导致的,一般需要确定的参数包括视频格式、色彩原色、转换特性和矩阵系数。

1.7 音频采样数据格式

音频信号的关键指标声音是振动产生的声波,通过介质(气体、固体、液体)传播并能被人或动物的听觉器官所感知的波动现象。声音的频率一般以赫兹(Hz)表示,指每秒周期性振动的次数。而分贝是用来表示声音强度的单位,记为dB。当前我们在计算机、手机、MP3中所接触的音频更精准地说应该是数字音频,数字音频出现的目的在于能够有效地录音、制作和分发。现在音乐之所以能广泛地在网络及网络商店流传都仰赖数字音频及其编码方式,音频以文件的方式在网络上流传而不必依赖实体介质,这样就大幅度节省了生产与传播的成本。

在模拟信号系统中,声音由空气中传递的声波通过转换器(如麦克风)转存为电流信号的电波。而重现声音则是相反的过程,即通过放大器将电子信号转成物理声波,再借由扩音器播放。经过转存、编码、复制及放大后或许会丧失声音的真实度,但仍然能够保持与其基音、声音特色相似的波形。模拟信号容易受到噪声及变形的影响,相关器材电路所产生的电流更是无可避免。在信号较为纯净的录音过程里仍然存在许多噪声及损耗。而当音频数字化后,损耗及噪声只在数字和模拟间转换时才会产生。

数字音频通过从模拟信号中采样并转换成二进制(1/0)信号,并以二进制式电子、磁力或光学信号,而非连续性时间、连续电子或机电信号存储。这些信号之后会进一步被编码以便修正存储或传输时产生的错误,然而在数字化过程中,这个为了校正错误的编码步骤并不严谨。在广播或者所录制的数字系统中,以频道编码的处理方式来避免数字信号的流失是必要的一环。在信号出现错误时,离散的二进制信号中允许编码器拨出重建后的模拟信号。频道编码的其中一例就是CD所使用的八比十四调制。

数字音频通过ADC(模数转换器)将模拟信号转换成数字信号,ADC对音频频率进行采样并转换成特定的位分辨率。例如,CD音频的采样率为44.1kHz(即每秒采样44 100次 ),每个声道都以16位解析。对双声道而言,它具有“左”和“右”两个声道。如果模拟信号的带宽未受限,那就必须在转换前使用降噪滤波器以避免声音产生损失。

这样处理后的数字音频是可被存储和传输的。数字音频文件能够被存储在一片CD、数字音频播放器、硬盘、U盘或其他任何存储设备里。数字信号可以被处理数字信号的音频滤波器或音效所改变。MP3、AAC、Vorbis、FLAC等技术经常被用于压缩音频文件的大小,并且可以通过流媒体方式传输到各种设备上。

最后,数字音频还能通过DAC转换回模拟信号。如同ADC技术一样,DAC会在特定的采样频率及采样比特下运作,但是经过了超采样、上下采样等过程,有时难以保证音频的采样频率能够与原始的采样频率相同。

通过ADC将模拟信号转换成数字信号,或通过脉冲编码调制(Pulse Code Modulation,PCM)对连续变化的模拟信号进行采样、量化和编码,转换成离散的数字信号,这样就实现了音频信号的采集。我们常说的PCM文件就是未经封装的音频原始文件或者叫做音频“裸数据”。

1.7.1 声道

声道(Sound Channel)是指声音在录制或播放时在不同空间位置采集或回放的相互独立的音频信号,声道数也就是声音录制时的音源数量或回放时相应的扬声器数量。为了加深对声道的理解,我们来看一下声道布局的示意图,如图1-31所示。

图1-31 声道布局的示意图

当我们坐在中间时,不同声道的声音让我们感觉它们来自不同的方向。这只是一个简单的示意图,常见的声道布局如表1-1所示。

从表1-1的信息中可以看出,不同的场景使用不同的声道,效果也会不同;而为了尽量还原声音现场的体验,产生了这么多数量的声道。

表1-1 声道布局

示意图

系统名

声道数

前左+
前右

前中

中前左+中前右

上前左+
上前右

侧左+
侧右

后左+
后右

正后

后上左+后上右

单声道

1.0

×

×

×

×

×

×

×

立体声

2.0

×

×

×

×

×

×

×

立体声

3.0

×

×

×

×

×

×

环绕立体声

3.0

×

×

×

×

×

×

四声道立体声

4.0

×

×

×

×

×

×

侧位四声道

4.0

×

×

×

×

×

×

立体声

4.0

×

×

×

×

×

前部为主五声道

5.0

×

×

×

×

×

侧位为主五声道

5.0

×

×

×

×

×

全景声

5.1.4

×

×

×

扩展环绕立体声

6.0

×

×

×

×

侧位环绕立体声

6.0

×

×

×

×

扩展环绕立体声

7.1

×

×

×

×

环绕立体声

7.0

×

×

×

×

全景声

7.1.4

×

×

八声道

8.0

×

×

×

环绕立体声

9.0

×

×

×

全景声

11.1.4

×

每侧多2 个音频通道

每侧多2 个音频通道

1.7.2 采样率

采样率(也称为采样速度或者采样频率)定义了每秒从连续信号中提取并组成离散信号的采样个数,它用赫兹(Hz)表示。采样率的倒数称为采样周期或采样时间,它是采样的时间间隔。注意,不要将采样率与比特率(Bit Rate,也称码率)相混淆,后者是每秒产生的二进制位数。

根据奈奎斯特采样定理,采样之后的数字信号能保留的原始信号的频宽基本上是采样率的一半。

在数字音频领域,常用的采样率如下。

8000Hz:电话所用的采样率,对于人说话的声音已经足够。

11 025Hz:AM调幅广播所用的采样率。

22 050和24 000Hz:无线电广播(FM调频广播)所用的采样率。

32 000Hz:MiniDV数码视频Camcorder、DAT(LP模式)所用的采样率。

44 100Hz:音频CD所用的采样率,也常用于MPEG-1音频(VCD、SVCD、MP3)。

47 250Hz:Nippon Columbia(Denon)开发的世界上第一款商用PCM录音机所用的采样率。

48 000Hz:MiniDV、数字电视、DVD、DAT、电影和专业音频所用的数字声音采样率。

50 000Hz:20世纪70年代后期出现的由3M和Soundstream开发的第一款商用数字录音机所用的采样率。

50 400Hz:三菱X-80数字录音机所用的采样率。

96 000或192 000Hz:DVD-Audio、一些LPCM DVD音轨、蓝光光盘音轨和HD-DVD(高清晰度DVD)音轨所用的采样率。

2.8224MHz:SACD、索尼和飞利浦联合开发的,被称为Direct Stream Digital的1位Sigma-Delta调制过程所用的采样率。

从上可以看出,从8000到32 000、48 000等都是倍数关系,比较容易理解,而且8000的由来我们在前面也讲过。而11 025、22 050、44 100等也是倍数关系,但它们都不是1000的整数倍,算起来会比较麻烦,且后面的“零头”为什么看起来那么奇怪呢?

我们先从44 100说起,其实没有人知道它是怎么来的,因为CD的标准就是如此。有人说这个数字正好是最小的4个质数的平方的乘积,即22×32×52×72 = 44 100,这或许是一个巧合,或许真的是曾经有一个天才一拍脑袋想出这么一个频率,但更令人信服的来源是下面这种解释。

人的听觉频率范围大约是20kHz,根据采样定理,使用40kHz的采样频率就够了,如果再加10%,也就是44kHz,而非44.1kHz。多出的100是从哪里来的呢?这要从数码录音说起。早期的数码录音就是一个PCM编码器加录像机,所以,数据音频信号是在录像机(录像带)中存储的。PAL制式的录像机每帧有625条扫描线,但实际可用的扫描线为588条,由于隔行扫描,扫描线减半,就成了294条。每条扫描线可以存储3个采样点的信息,场频为50Hz,因而采样点数量为294×50×3 = 44 100。同样,NTSC制式的设备有525条扫描线,实际可用的有490条,减半为245,场频为60Hz,因而为245×60×3 = 44 100。这是巧合吗?还是说这个数字竟然真的就是这么神奇?当然,如果按实际场频59.94Hz计算,NTSC制式实际能存储的采样点数量为245×59.94×3 = 44 056。实际上,早期日本的确有一些采用44.056kHz频率的数码录音,但后来都统一到44.1kHz了。至于22 050和11 025,应该都是由44 100下采样来的,这样可以节省存储空间和带宽。

1.7.3 采样位深

采样位深就是每个采样点用多少位来表示,如位深是16就代表每个采样点需要16位来存储。从物理意义上来说,位深代表的是振动幅度所能表达的精确程度或者粒度。假设数字信号在-1~1的区间,如果位深为16位,那么第1位表示正负号,剩下的15位可表示范围为0~32 767,那么振幅就可以精确到1/32 768的粒度。

我们一般在网络电话中用的就是16位位深,这样不太会影响听觉体验,并且存储和传输的耗费也不是很大。而在做音乐或者有更高保真度要求的场景中则可以使用32位甚至64位的位深来减少失真。而选择用8位位深时失真则比较严重,在计算机与互联网发展早期,受到音频技术与网络条件限制,很多音频都是8位的采样位深,声音会显得比较模糊,如今也只有一些电话和对讲机等设备还在使用8位位深。

1.7.4 带宽计算

通过对音频部分的声道、采样率、采样位深的讲解,我们应该可以很方便地计算PCM音频文件使用的空间或者占用的带宽了。例如,一个双声道立体声、采样率是48 000、采样位深是16位、时长为1分钟的音频所占用的存储空间的计算公式如下:

在媒体传输时占用的带宽如下:

很显然,原始格式的音频采样需要的存储空间和传输带宽还是挺高的,使用MP3或AAC编码可以将音频压缩(有损)到大约原来的1/10大小,这样可以节省很多存储空间和带宽。但要注意,在一些人工智能应用中需要用到语音识别时,尽量不要重采样或使用有损压缩,那样会影响识别的准确率。

1.8 小结

在本章,我们带大家了解了多媒体的基础知识。这些基础知识和基本概念在后面的章节中都会用到,对于理解和使用FFmpeg至关重要。

首先,所有的概念和原理都来源于人们的生产和生活,而数字化则是对现实世界的抽象和映射。在从现实到抽象的过程中,不可避免地会“丢失”一些信息,但同时也给人们带来了标准化和规范化,以及更多的想象空间。通过数字世界的存储,人们可以“看”到历史上世界的样子;通过远程音视频的传输,人们可以实时地看到远处的世界,并可以与世界各个角落的人实时互动交流。打破时间和空间的限制,这就是音视频数字化最大的意义。

本章还介绍了音视频数字化的相关背景和逻辑、技术以及相关限制等。与音频相关的重要概念有采样率、声道、采样位深等;与视频相关的主要就是分辨率、帧率、色彩空间和DTS及PTS等。将音频和视频结合,通过网络在时间线上“动”起来,就形成了音视频流媒体,而将音视频通过网络实时传输的技术就是实时音视频技术,又称RTC。

不管是音视频处理还是RTC传输,FFmpeg都是很有用、很重要的工具。有了这些基础知识,从下一章起,我们就可以正式踏上FFmpeg探索之旅了。

第2章 FFmpeg简介

FFmpeg一词在不同场景下表示的意思不尽相同。大致来说有两方面的意思,一方面指的是多媒体相关的工具集,包含ffmpeg、ffplay、ffprobe等,用于转码、播放、格式分析等;另一方面是指一组音视频编解码、媒体处理的开发套件,为开发者提供丰富的多媒体处理的API调用接口及相应的辅助工具库。

FFmpeg提供了多种媒体格式的封装和解封装,以及编解码等,包括多种音视频编码、字幕、不同协议的流媒体、丰富的色彩格式转换、音频采样率转换等。FFmpeg的内部框架提供了丰富的API及可扩展的插件系统,既可以灵活地使用多种封装与解封装、编码与解码的插件,也可以灵活地基于其框架进一步扩展。另外,依据FFmpeg编译的选项不同,FFmpeg在LGPL-2.1(及之后)版本或GPL-2.0(及之后)版本下发布,具体使用哪个版本的协议实际上取决于在编译时选择了哪些编译选项。

FFmpeg中的“FF”指的是“Fast Forward”。曾经有人在FFmpeg的邮件列表询问“FF”是不是代表“Fast Free”或者“Fast Fourier”,FFmpeg项目的创立者Fabrice Bellard回信说:“Just for the record, the original meaning of ‘FF’ in FFmpeg is‘Fast Forward’…”FFmpeg中的“mpeg”则是人们通常理解的Moving Picture Experts Group(动态图像专家组),其实也可以理解为Multimedia Processing EnGine。作为一个全面的多媒体处理套件,FFmpeg从2000年发展至今,其中的FF已经因为FFmpeg的强大,足以支撑这些不同的意义,所以不用以完美的心态纠结其完全准确的含义。

2.1 FFmpeg的发展历史

“History is the memory of things said and done”(历史是说过和做过的事情的记忆),要想深入了解一个软件、一个系统,首先要了解其发展史。下面就来介绍一下FFmpeg的整体发展过程。

FFmpeg起初是由法国天才程序员Fabrice Bellard[1]在2000年开发。后来发展到2003年,Fabrice Bellard找到了FFmpeg的接手人,这个人就是至今还在维护FFmpeg的Michael Niedermayer。Michael Niedermayer对FFmpeg的贡献非常大,他开发了FFmpeg内的libswresample、libswscale、H.264 Decoder等,并将libavfilter这个滤镜子系统加入FFmpeg项目,使得FFmpeg能够做的多媒体处理更加多样、更加方便。自FFmpeg发布了0.5版本之后,社区的开发进展缓慢,很长一段时间没有进行新版本发布,直到FFmpeg的版本控制系统被迁移到Git(作为版本控制服务器)并构建了相应的其他基础设施,FFmpeg的开发进展才又开始加快。当然那也是时隔多年之后了。2011年1月,FFmpeg项目中一些提交者对FFmpeg的项目管理方式不满意,分裂出了一个新的项目,命名为Libav[2]。随后,一些操作系统(如Debian)也开始使用Libav。需要说明的是,该项目目前基本处于停滞状态,大部分分裂出去的开发者已经重归FFmpeg社区(但确实还有少量核心开发者并未回归)。2015年8月,Michael Niedermayer主动辞去FFmpeg项目负责人职务。事情的起因是Michael Niedermayter从Libav中反向移植了大量代码和功能至FFmpeg中,这引起一些争议。而Michael Niedermayer辞职的主要目的是希望两个项目最终能够一起发展。

[1] 关于Fabrice Bellard的公开可见信息并不太多,但其成就非同一般。除FFmpeg外,他还编写了著名虚拟化模拟器QEMU、OpenGL实现、4GLTE和5GNR软基站、JavaScript引擎,让Windows 2000和Linux X Window从浏览器运行,甚至还创造了使用桌面计算机计算圆周率的世界纪录。更多信息可以从他的个人网站(https://bellard.org/)上窥其一二。他被称为天才程序员绝非夸赞,而是事实。

[2] Libav的官网为https://libav.org,但其开发基本已经停滞。

依据时间顺序,我们对FFmpeg及其社区的大事件做一个简单的回顾。

2000年:Fabrice Bellard创立这个项目,最初的目的是实现MPEG编码/解码库。随后,这个库作为多媒体引擎被继承到播放器项目MPlayer中。时至今日,很多FFmpeg开发者或贡献者依然来自一些开源播放器项目,诸如VLC、MPV(它以MPlayer的继任者的姿态出现)。

2003年:Fabrice Bellard离开该项目,Michael Niedermayer作为项目的主要领导和维护者开始维护这个项目。FFmpeg原生的H.264解码器及对应的解复用在FFmpeg中出现(需要注意的是,H.264的相关标准此时实际上还只是一个草案状态,未正式成为标准);开发人员开始尝试社区的无损压缩Codec FFV1。

2005年:Vorbis Decoder出现在FFmpeg中。

2006年:开发速度缓慢(大约每个月有100个提交),且开发者少于30人。

2009年3月:发布版本0.5,这是FFmpeg项目的第一个正式发布版本。

2010年:FFmpeg原生的VP8解码器远快于谷歌的libvpx中的VP8解码器。

2011年1月:FFmpeg社区爆发“骚乱”[3],接着,Libav被创立,整个社区出现了严重的分裂。

[3] 参见https://lwn.net/Articles/423702。

2011~2014年:Michael开始把Libav的增强部分的Patch合并回FFmpeg,同时开始迅速修复FFmpeg安全方面的问题(这使得不同的Linux发行版本开始逐步从Libav切换回FFmpeg)。他一方面尝试让FFmpeg与Libav两者可以兼容,另一方面劝说Libav的开发者回归FFmpeg社区。

2014年:Michael Niedermayer在邮件列表中公开宣布辞去领导者的角色,不过他仍然保留了维护者的角色。

2015年:最初的决策委员被选举出来,基本依据其对FFmpeg的贡献程度,FFmpeg社区开始以决策委员会的方式运作。决策委员会人员主要可以参与表决和决定FFmpeg的功能发展方向,在与FFmpeg相关的重大事项上具有表决与建议权限,以引导FFmpeg社区更好地发展。

2019年:FFmpeg扩充决策委员,同年来自全球各地的FFmpeg开发者在日本东京聚会,参与VideoLan开发者大会,共同决策改组社区委员会和技术委员会,并确定每年至少召开一次碰头会,同步社区成员的想法与计划。

作为一套开源的音视频编解码套件,FFmpeg可以通过互联网自由获取。FFmpeg的源码Git库提供了多站同步的获取方式,可以从以下地址获取FFmpeg的源代码:

https://git.ffmpeg.org/ffmpeg.git

http://git.videolan.org/?p=ffmpeg.git

https://github.com/FFmpeg/FFmpeg

FFmpeg发展至今,已经被许多开源项目采用,比如ijkplayer、ffmpeg2theora、VLC、MPlayer、HandBrake、Blender、Google Chrome、FreeSWITCH等。DirectShow/VFW的ffdshow和QuickTime的Perian也采用了FFmpeg。由于FFmpeg是在LGPL/GPL协议下发布的,任何人都可以自由使用,但必须严格遵守LGPL/GPL协议。就像行业“黑话”一般,FFmpeg及音视频领域也有一些行话,下面简单介绍一些相关术语,第一次读可能有些困惑,但你在阅读了本书的其他部分之后再回顾,可能会有顿悟的感觉。所以若第一次不太明白,可以跳过这些。

容器(Container)格式:一种文件封装类型,里面主要包含了流,一般会使用一个特定的后缀名标识,例如.mov.avi.wav等。

流(Stream):在容器中存储音频(Audio)或者视频(Video)、字幕(Subtitle)等数据。

元数据(Metadata):一般位于容器之中,告诉我们一些额外的信息,一个常见的例子是MP3文件中的ID3 tag。

编解码器(Codec):它实际上是enCOder与DECoder这两个词的混搭。大部分情况下我们指的是一种压缩标准,如我们说的AVC/H.264、HEVC/H.265、VVC/H.266、AV1等。

如果在生活中找一个类比,容器格式与流和元数据的关系有点类似于电线的包装方式,我们用外包装材料,把单股的电线根据需要封装起来成为一个整体,容器格式好像整条电线,流好像电线内部的不同颜色的线缆,元数据则好像电线外面的标识,用于表示一些额外的信息,如图2-1所示。

图2-1 容器与电线包装

2.2 FFmpeg的基本组成

FFmpeg框架可以简单分为两层,上层是以ffmpeg、ffplay、ffprobe为代表的命令行工具;其底层支撑是一些基础库,包含AVFormat、AVCodec、AVFilter、AVDevices、AVUtils等模块库,细节结构如图2-2所示。下面就对这些底层支撑模块做一个大概的介绍。

图2-2 FFmpeg基础模块

2.2.1 封装/解封装模块AVFormat

AVFormat中实现了目前多媒体领域中的绝大多数媒体封装格式和流媒体协议,包括封装器(Muxer)和解封装器(Demuxer),包含如MP4、FLV、MKV、TS等文件格式的封装和解封装,以及RTMP、RTSP、MMS、HLS等网络流媒体协议的支持等。

FFmpeg是否支持某种媒体封装格式,取决于编译时是否包括该格式的Demuxer和Muxer。另外,如果FFmpeg不支持某些新的容器格式,可以根据实际需求,进行媒体封装格式的扩展,增加相应的封装格式。其主要的工作是:在AVFormat中,按照FFmpeg内部框架的要求,增加自己的封装、解封装处理模块。这些会在后面部分讲解。

2.2.2 编/解码模块AVCodec

AVCodec中实现了目前多媒体领域绝大多数常用的编解码格式,既支持编码,也支持解码。AVCodec除了以原生的方式(即FFmpeg不依赖其他第三方库,完全自己实现)支持H.264、AAC、MJPEG等媒体编解码格式外,也可以通过集成第三方库的方式来支持第三方编解码器。例如H.264(AVC)编码需要使用x264编码器;H.265(HEVC)编码需要使用x265编码器;MP3(mp3lame)编码需要使用libmp3lame编码器。如果希望增加新的编解码格式,或者支持硬件编解码加速,需要在AVCodec中增加相应的编/解码模块。关于更多AVCodec的使用信息以及如何扩展,我们将会在后面章节进行详细介绍。

2.2.3 滤镜模块AVFilter

AVFilter库提供了一个通用的音频、视频、字幕等滤镜处理框架。在AVFilter中,滤镜框架可以有多个输入和多个输出。滤镜处理的例子如图2-3所示。

图2-3 FFmpeg AVFilter示例

这个例子将输入的视频切割成两部分流,一部分流抛给crop与vflip滤镜处理模块进行操作,另一部分保持原样;当crop与vflip滤镜处理操作完成后,将流合并到原有的overlay图层中,并显示在最上面一层,输出新的视频。对应的命令行如下:

ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT

原始视频如图 2-4 所示。以上命令执行完成之后,该命令将自动退出,生成的视频结果是保留视频的上半部分,同时上半部分会镜像到视频的下半部分,二者合成后作为输出视频,如图2-5所示。

图2-4 运行前

图2-5 运行后

下面简单说明一下滤镜的构成规则:

相同滤镜的线性链用逗号分隔。

不同滤镜的线性链之间用分号分隔。

有些过滤器的输入是一个参数列表,参数列表被指定在过滤器名称和一个等号之后,并且用冒号分开。也存在没有音频、视频输入的源过滤器(即source filter),以及不会有音频、视频输出的汇集过滤器(即sink filter)。在以上示例中,crop与vflip使用的是同一个滤镜处理的线性链,split滤镜和overlay滤镜使用的是另外一个线性链,一个线性链接入另一个线性链汇合处时是通过方括号括起来的标签进行标示的。在这个例子中,两个流处理后是通过[main][tmp]进行关联汇合的。split滤镜将分割后的视频流的第2部分打上标签[tmp],将该部分流通过crop滤镜进行处理,然后进行纵坐标调换操作,打上标签[flip],并将[main]标签与[flip]标签合并。[flip]标签的视频流从视频的左边最中间的位置开始显示,就出现了如图2-5所示的镜像效果。

2.2.4 设备模块AVDevice

AVDevice提供了一些常用的输入输出设备的处理框架。比如在macOS和iOS上,一般使用AVFoundation调用底层的音视频及共享桌面输入。在Windows上,常用dshow(DirectShow)作为音视频输入。而在Linux上有更多的选择:音频输入输出设备有oss(Open Sound System)、alsa(Advanced Linux Sound Architecture)、fbdev(Frame Buffer)、openal(OpenAL)、pulse(Pulse Audio)等,视频设备有opengl(OpenGL)、video4linux2(Video for Linux)、x11grab(基于XCB的X11桌面捕获)等。sdl及sdl2(SDL,Simple Directmedia Layer)是一个跨平台的输出设备的不同版本,在大多数平台上都能用。

除此之外值得一提的是,AVDevice还有一个名为lavfi的虚拟输入设备,它允许使用Libavfilter的滤镜链或表达式作为输入或输出设备。通过它,可以很方便地生成很多“假”的音频(如某一频率的声音或高斯白噪声)和视频流(如纯色或渐变的RGB图像序列等)。该设备在作为示例或测试时很常用,本书后面的很多例子也用到了它。

2.2.5 图像转换模块swscale

swscale模块提供了底层的图像转换API接口,它允许进行图像缩放和像素格式转换。常用于将图像从1080p转换成720p或者480p等这样的缩放操作,或者将图像数据从YUV420P转换成YUYV,或YUV转换成RGB等操作。可见,libswscale库主要是执行高度优化的图像缩放和色彩空间及像素格式转换操作。经常会看到libswscale和libyuv的一个对照比较,但实际情况下需要评估缩放算法、支持的色彩空间、性能等以做出正确的选择。具体来说,这个库可以执行以下转换。

重新缩放:即改变视频尺寸的处理,其有几个重新缩放的选项和算法可用。与此同时,需要注意缩放通常是一个有损失的过程,缩放也需要在图像质量、缩放性能等限定条件下进行折中权衡。

像素格式转换:这是转换图像格式和图像色彩空间的过程,例如从平面格式的YUV420P到RGB24打包格式。它还处理打包转换,即从打包布局(所有属于不同平面的像素交错在同一个缓冲区,如RGB格式)转换为平面布局(所有属于同一平面的采样数据存储在一个专门的缓冲区或“平面”,如YUV420P)。如果源和目的色彩空间不同,这通常也是一个有损失的过程。

2.2.6 音频转换模块swresample

swresample模块提供了音频重采样等。例如它允许操作音频采样、音频通道布局转换与布局调整,主要执行高度优化的音频重采样、Rematrixing和采样格式转换操作。具体来说,这个库可以执行以下转换。

重采样:执行改变音频采样率的处理,例如从44 100Hz的高采样率转换到8000Hz的低采样率。从高采样率到低采样率的音频转换是一个有损失的过程,该库有多个重采样选项和算法可用。

格式转换:执行采样类型的转换过程,例如从16位有符号采样格式转换为无符号8位或浮点类型的采样格式。它还处理打包转换,如从打包布局变换到平面布局。

Rematrixing:改变通道布局的过程,例如从立体声到单声道。当输入通道数量多于输出通道数量时,这个过程是有损失的,因为它涉及不同的增益因子和混合。

其他各种音频转换(如拉伸和填充)需要通过专用选项启用。

2.2.7 编解码工具ffmpeg

ffmpeg是FFmpeg源码编译后生成的一个可执行程序,可以作为命令行工具使用。ffmpeg最主要的流程从上层理解起来并不难,过程看似简单,但因为其重要性,我们会反复提及与回顾。

解封装(Demuxing),或称解复用

解码(Decoding)

编码(Encoding)

封装(Muxing),或称复用

其中,整体处理的工作流程如图2-6所示。

图2-6 ffmpeg整体处理工作流程

首先ffmpeg读取输入源,然后通过Demuxer将音视频包进行解封装,这个动作通过调用libavformat中的接口即可实现;接下来通过Decoder解码,将音视频压缩数据通过Decoder解包成为YUV或者PCM这样的“裸”数据,Decoder通过libavcodec中的接口即可实现;然后将对应的数据通过Encoder编码,编码可以通过libavcodec中的接口实现;接下来将编码后的音视频数据包通过Muxer封装,Muxer封装通过libavformat中的接口即可实现;最后将输出内容写入指定的文件中。

2.2.8 播放器ffplay

FFmpeg不但可以提供转码、转封装等功能,同时还可以提供简单的播放相关功能。使用FFmpeg的AVFormat与AVCodec可以播放各种媒体文件或者媒体流,也可以在命令选项中使用AVFilter相关功能来间接完成一些其他特殊功能。一般而言,我们选择ffplay这个简单的播放工具完成上述功能。如果想要使用ffplay,系统首先需要有SDL库来支持跨平台的渲染与显示。ffplay作为FFmpeg源码编译后生成的另一个可执行程序,与ffmpeg在FFmpeg项目中充当的角色不同,它的主要作用是作为播放测试工具使用,提供音视频显示和播放,也能用于显示音频的波形信息等。

注意:有时通过源代码编译生成ffplay不一定能够成功,因为ffplay旧版本依赖SDL-1.2,而ffplay新版本依赖SDL-2.0,安装对应的SDL库才能编译生成ffplay。

2.2.9 多媒体分析器ffprobe

ffprobe是FFmpeg源码编译后生成的另一个可执行程序。ffprobe是一个非常强大的多媒体分析工具,可以从媒体文件或者媒体流中获得用户想要了解的媒体信息,比如音频的格式、视频的宽高、媒体文件的时长等参数信息等。它除了用于分析媒体容器中音频的编码格式、采样率、通道数目,以及视频的编码格式、宽高等以外,还用于分析媒体文件中媒体的总时长、复合码率等信息。使用ffprobe还可以深入分析媒体文件中每个压缩媒体包的长度、包的类型、包对应的帧的信息等。第3章将会对ffprobe进行详细介绍。

2.3 不同平台下的编译

FFmpeg官方网站提供了已经编译好的可执行文件,因为FFmpeg是开源的,所以也可以根据自己的需要进行手动编译。FFmpeg官方建议用户自己编译使用FFmpeg的最新稳定版本以应对安全问题,以及使用更新的特性。对于一些操作系统,比如Linux系统,无论是Ubuntu还是RedHat,如果使用系统提供的源来安装FFmpeg时会发现,其版本相对比较老旧,使用apt-get install ffmpeg或者yum install ffmpeg安装FFmpeg时,默认支持的版本较老,有些新的功能并不支持,如一些新的封装格式或者通信协议等。因此使用者或者开发者了解编译FFmpeg就至关重要了,而且这样也方便以后根据自己的需求进行功能的裁剪。

2.3.1 Windows平台编译FFmpeg

在Windows平台中编译FFmpeg需要使用msys2,后者提供了一系列工具链,以辅助编译Windows的本地化程序。关于msys2的详细介绍和安装方法可以参照https://www.msys2.org。如果不希望使用msys2而使用Visual Studio的话,则需要消耗很多时间以支持Visual Studio平台。感兴趣的读者可以上网查找支持的方法。截至本书编写时,官方提供的Windows开发包还是使用msys2工具链编译的。msys是minimal system的缩写,主要完成的工作为UNIX on Windows的功能。显而易见,这是一个仿真UNIX环境的Windows工具集,在此,我们不会介绍msys2环境本身的安装配置过程,而着重于基于它在Windows上编译FFmpeg。msys环境准备好之后,我们就正式进入编译的环节。

1)进入FFmpeg源码目录,执行./configure。如果一切正常,会看到如下信息:

install prefix            /usr/local
 
source path               .
 
C compiler                gcc
 
C library                 mingw64
 
... 此处省略大量输出内容
 
makeinfo enabled          yes
 
makeinfo supports HTML    no

2)配置成功后执行make。在MinGW环境下编译FFmpeg是一个比较漫长的过程。

3)执行make install。到此为止,FFmpeg在Windows上的编译完成,此时我们可以尝试使用FFmpeg命令行来验证我们的编译结果。执行./ffmpeg.exe–h

./ffmpeg.exe -h
 
ffmpeg version n6.0 Copyright (c) 2000-2023 the FFmpeg developers
 
  built with gcc 12.1.0 (crosstool-NG 1.25.0.55_3defb7b)
 
  configuration: --enable-gpl
 
  libavutil      58. 2.100 / 58. 2.100
 
  libavcodec     60. 3.100 / 60. 3.100
 
  libavformat    60. 3.100 / 60. 3.100
 
  libavdevice    60. 1.100 / 60. 1.100
 
  libavfilter     9. 3.100 /  9. 3.100
 
  libswscale      7. 1.100 /  7. 1.100
 
  libswresample   4. 10.100 /  4. 10.100
 
  libpostproc    57. 1.100 / 57. 1.100
 
Hyper fast Audio and Video encoder
 
usage: ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}...

注意:举例的这个编译配置方式编译出来的仅仅为最简易的FFmpeg,并没有H.264、H.265等Codec的编码支持。如果需要支持更多的功能特性,还需要根据实际需要进行更加细致的定制,这部分会在后面详细介绍。

2.3.2 Linux平台编译FFmpeg

前面介绍过,很多Linux的发行版本源中已经包含了FFmpeg,例如Ubuntu / Fedora的镜像源中包含了安装包,但是版本相对来说比较老,有些甚至不支持H.264、H.265编码,或者不支持RTMP等。为了支持这些协议格式和编码格式,需要自己手动编译FFmpeg。默认编译FFmpeg的时候,需要用到nasm汇编器对FFmpeg中的汇编部分进行编译。如果不用汇编部分的代码,可以不安装nasm汇编器,这种情况一般认为不大合适,除非我们并不在意性能。如果没有安装nasm,执行默认配置的时候,会出现以下错误提示:

[lq@chinaffmpeg 6.0]$ ../ffmpeg/configure 
 
nasm/yasm not found or too old. Use --disable-x86asm for a crippled build.
 
 
 
If you think configure made a mistake, make sure you are using the latest
 
version from Git.  If the latest version fails, report the problem to the
 
ffmpeg-user@ffmpeg.org mailing list or IRC #ffmpeg on irc.libera.chat.
 
Include the log file "ffbuild/config.log" produced by configure as this will help
 
solve the problem.

根据以上错误提示,可以使用--disable-x86asm来取消汇编优化编译配置。这么做的话就不会编译FFmpeg的汇编代码部分。但一般不应该取消汇编优化,除非是在调试C原型代码的时候。如果需要支持汇编优化,需要环境中有nasm或者可以通过安装nasm汇编器来解决。

curl -O 
https://www.nasm.us/pub/nasm/releasebuilds/2.15.05/nasm-2.15.05.tar.gz

命令行执行后将会下载nasm源代码包。

 curl -O 
https://www.nasm.us/pub/nasm/releasebuilds/2.15.05/nasm-2.15.05.tar.gz
 
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
 
                                 Dload  Upload   Total   Spent    Left  Speed
 
 16 1591k   16  265k    0     0  52809      0  0:00:30  0:00:05  0:00:25 55226

下载nasm汇编器并执行configure后,可以通过make编译,执行make install即可。然后再回到FFmpeg源代码目录中进行之前的configure操作,之前的错误提示就会消失。

install prefix            /usr/local
 
source path               src
 
C compiler                gcc
 
C library
 
... 此处省略大量输出内容
 
makeinfo supports HTML    no
 
xmllint enabled           yes

2.3.3 macOS平台编译FFmpeg

有些开发者在macOS平台上使用FFmpeg进行一些音视频编转码或流媒体处理等操作,此时需要生成macOS平台相关的FFmpeg可执行程序。在macOS平台上编译FFmpeg前,首先需要安装所需要的编译环境,在macOS平台上使用的编译工具链为LLVM。

Apple clang version 13.1.6 (clang-1316.0.21.2.5)
 
Target: x86_64-apple-darwin21.4.0
 
Thread model: posix
 
InstalledDir: 
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

另外,由于macOS的版本不同,如果是基于Intel的平台,依然需要安装nasm汇编编译工具,否则在生成Makefile时会报告未安装nasm工具的错误。

在LLVM下使用源码编译安装FFmpeg的方法与Linux平台基本相同,因为它们同属于POSIX兼容的操作系统。将FFmpeg从git://source.ffmpeg.org/ffmpeg.git中下载下来。源代码下载成功后,可以进入编译阶段,通过类似之前的configure操作即可完成基本的编译工作。

install prefix            /usr/local/
 
source path               /Users/liuqi/multimedia/upstream_ffmpeg/ffmpeg
 
... 此处省略大量输出内容
 
makeinfo supports HTML    no
 
xmllint enabled           yes

接着只需要执行makemake install即可。

2.4 FFmpeg特性的选择与定制

FFmpeg本身支持大量音视频编码格式、文件封装格式与流媒体传输协议,但是依然有可能不能满足特定的需求。FFmpeg所做的是提供一套基础的框架,所有的编码格式、文件封装格式与流媒体协议可以作为FFmpeg的模块挂载在FFmpeg框架中,这些模块可以以第三方外部库的方式提供支持,也可以选择直接与FFmpeg一体,成为FFmpeg原生实现的一部分。通过FFmpeg源码的configure命令,可以查看FFmpeg支持的音视频编码格式、文件封装格式与流媒体传输协议,对于FFmpeg不支持的格式,可以通过configure --help查看是否有第三方外部库支持,然后通过增加对应的编译参数选项进行支持。

External library support:
 
  Using any of the following switches will allow FFmpeg to link to the
  corresponding external library. All the components depending on that library
  will become enabled, if all their other dependencies are met and they are not
  explicitly disabled. E.g. --enable-libopus will enable linking to
  libopus and allow the libopus encoder to be built, unless it is
  specifically disabled with --disable-encoder=libopus.
 
  Note that only the system libraries are auto-detected. All the other external
  libraries must be explicitly enabled.
 
  Also note that the following help text describes the purpose of the libraries
  themselves, not all their features will necessarily be usable by FFmpeg.
 
  --disable-alsa             disable ALSA support [autodetect]
  --disable-appkit           disable Apple AppKit framework [autodetect]
  ... 大量输出内容省略
  --enable-vapoursynth      enable VapourSynth demuxer [no]
  --disable-vulkan           disable Vulkan code [autodetect]
  --disable-xlib             disable xlib [autodetect]
  --disable-zlib             disable zlib [autodetect]

通过以上帮助信息的输出内容可以看到,FFmpeg支持的外部库比较多。需要注意的是,这些项目是独立于FFmpeg发展的,所以,需要根据实际情况来选择最新版本或者用户实际使用的版本。更多的外部第三方库可以参考FFmpeg官方文档的扩展库页面[4]。这些外部库可以通过configure进行定制,在编译好的FFmpeg可执行程序中也可以看到编译时定制的外部库。

[4] 参见https://ffmpeg.org/general.html#External-libraries。

ffmpeg version n6.0 Copyright (c) 2000-2023 the FFmpeg developers
  built with gcc 8 (GCC)
  configuration: --enable-libxml2 --enable-libx264 --enable-libx265 --enable-gpl 
--disable -optimizations --disable-stripping --enable-vaapi --enable-hwaccel='h263_vaapi,
av1_vaapi,h264_vaapi,hevc_vaapi,mjpeg_vaapi,mpeg2_vaapi,mpeg4_vaapi,vc1_vaapi,vp9_vaapi,
wmv3_vaapi' --prefix=/usr/local/ffmpeg/ --enable-openssl --enable-nonfree libavutil 58. 2.100 / 58. 2.100 libavcodec 60. 3.100 / 60. 3.100 libavformat 60. 3.100 / 60. 3.100 libavdevice 60. 1.100 / 60. 1.100 libavfilter 9. 3.100 / 9. 3.100 libswscale 7. 1.100 / 7. 1.100 libswresample 4. 10.100 / 4. 10.100 libpostproc 57. 1.100 / 57. 1.100

假如需要自己配置FFmpeg支持哪些格式,如仅支持H.264视频与AAC音频编码,可以调整配置项简化如下:

./configure --enable-libx264 --enable-libfdk-aac --enable-gpl --enable-nonfree

如配置后输出的基本信息所示,如果要支持H.264与AAC,需要系统中包括libx264与libfdk-aac的第三方库,否则会出现错误提示。支持H.265编码与支持H.264基本类似,编译安装x265后,在执行FFmpeg的Configure命令时,只需要增加--enable-libx265即可。支持其他对应的编码与此类似。

注意:从2016年年初起,FFmpeg自身的AAC编码器质量逐步好转,至2016年年底,libfaac已从FFmpeg源代码中剔除,但依然可以使用第三方libfdk-aac库来执行AAC的编解码支持。

FFmpeg默认支持的音视频编码格式、文件封装格式与流媒体传输协议比较多,因此编译的FFmpeg文件较大。而在有些应用场景中并不需要FFmpeg支持如此多的编码、封装或者协议,为了减小最终编译出来的库的体积(如在手机端等须注意最终包大小的场景等),这时候可以通过configure --help查看一些有用的选项以用作后续的裁减。

可以通过一些选项关闭不需要的编解码、封装/解封装与协议等模块,示例如下:

./configure --disable-encoders --disable-decoders --disable-hwaccels --disable-muxers --disable-demuxers --disable-parsers --disable-bsfs --disable-protocols --disable-indevs --disable-devices --disable-filters

关闭所有的模块后,可以看到FFmpeg的编译配置项输出信息几乎为空。此时可以根据定制需要,再加上自己所需要的模块,如希望编译时支持H.264视频编码和AAC音频编码、封装为MP4,则可以通过如下方式支持:

./configure --disable-filters --disable-encoders --disable-decoders --disable- 
hwaccels --disable-muxers --disable-demuxers --disable-parsers --disable-bsfs --disable-
protocols --disable-indevs --disable-devices --enable-libx264 --enable-libfdk-aac --enable- gpl --enable-nonfree --enable-muxer=mp4

通过细致的编译选项的配置,最终编译生成的FFmpeg及库等的大小会比默认编译时小很多。

2.4.1 编码器支持

FFmpeg源代码中可以包含的编码格式非常多,常见的和不常见的都可以在编译配置列表中见到。一般通过使用编译配置命令./configure --list-encoders参数来查看。

a64multi            h263                    movtext         eac3
 
adpcm_g722          h264_videotoolbox       rv10            libx264
 
adpcm_g726le        hevc_amf                msmpeg4v3       zlib
 
adpcm_ima_alp       hevc_mf                 msvideo1        prores
 
adpcm_ima_amv       hevc_nvenc              nellymoser      mjpeg_qsv
 
adpcm_ima_wav       hevc_videotoolbox       pcm_alaw        gif
 
adpcm_swf           jpeg2000                pcm_dvd         ssa
 
... 省略大量输出信息
 
dvbsub              libwebp                 pcm_u24le       wrapped_avframe
 
png                 libxavs2                pgm            g723_1
 
flv                 mjpeg                   ppm             

从输出信息可以看出,FFmpeg支持的编码器非常全面,如AAC、AC3、H.264、H.265、MPEG4、MPEG2VIDEO、PCM、FLV1等格式的编码器。为了节省输出内容所占篇幅,以上输出内容做了大量精简,更详细的信息可在本地尝试操作后自行查看,获得的信息会更全面一些。另外,相对于其他模块,FFmpeg对编码器的支持所依赖的第三方库更多一些。

2.4.2 解码器支持

FFmpeg源代码本身包含了很多解码格式。解码过程主要是将压缩过的编码内容进行解压缩。解码器的支持可以通过./configure–list-decoders命令进行查看。

aac                     dsicinvideo             motionpixels            rscc
 
aac_at                  dss_sp                  movtext                 rv10
 
aac_fixed               dst                     mp1                     rv20
 
aac_latm                dvaudio                 mp1_at                  rv30
 
aasc                    dvbsub                  mp1float                rv40
 
... 此处省略了大量输出内容
 
dsd_lsbf                mjpeg_qsv               realtext                yuv4
 
dsd_lsbf_planar         mjpegb                  rl2                     zero12v
 
dsd_msbf                mlp                     roq                     zerocodec
 
dsd_msbf_planar         mmvideo                 roq_dpcm                zlib
 
dsicinaudio             mobiclip                rpza                    zmbv

输出信息列出了FFmpeg所支持的解码器模块,包括MPEG4、H.264(AVC)、H.265(HEVC)、MP3等。

2.4.3 封装支持

FFmpeg的封装(Muxing,也称为复用)即将压缩后的码流封装到一个容器格式中。如果要知道FFmpeg源代码支持哪些容器格式,可以用命令./configure --list-muxers查看。

a64                     filmstrip               mp3                     rawvideo
 
ac3                     fits                    mp4                     rm
 
adts                    flac                    mpeg1system             roq
 
adx                     flv                     mpeg1vcd                rso
 
aiff                    framecrc                mpeg1video              rtp
 
alp                     framehash               mpeg2dvd                rtp_mpegts
 
amr                     framemd5                mpeg2svcd               rtsp
 
amv                     g722                    mpeg2video              sap
 
... 此处省略大量输出内容
 
dts                     microdvd                pcm_u24be               webp
 
dv                      mjpeg                   pcm_u24le               webvtt
 
eac3                    mkvtimestamp_v2         pcm_u32be               wsaud
 
f4v                     mlp                     pcm_u32le               wtv
 
ffmetadata              mmf                     pcm_u8                  wv
 
fifo                    mov                     pcm_vidc                yuv4mpegpipe
 
fifo_test               mp2                     psp

从封装格式(Muxer,也称为复用格式)信息中可以看到,FFmpeg可以支持生成裸流文件,例如H.264、AAC、PCM,也支持一些常见的容器格式,例如MP3、MP4、FLV、M3U8、WEBM等。

2.4.4 解封装支持

FFmpeg的解封装(Demuxing,也称为解复用)即将封装在容器里面压缩的音频流、视频流、字幕流、数据流等提取出来。如果要查看FFmpeg源代码支持哪些可以解封装的容器格式,可以通过命令./configure --list-demuxers进行查看。

aa                      filmstrip               libmodplug              rm
 
aac                     fits                    libopenmpt              roq
 
aax                     flac                    live_flv                rpl
 
ac3                     flic                    lmlm4                   rsd
 
... 此处省略了大量输出内容
 
ea                      jacosub                 r3d                     yop
 
ea_cdata                jv                      rawvideo                yuv4mpegpipe
 
eac3                    kux                     realtext
 
epaf                    kvag                    redspark
 
ffmetadata              libgme                  rl2

从解封装格式(Demuxer,也称为解复用格式)信息中可以看到,FFmpeg源码中支持的Demuxer非常多,包含图片(image)、MP3、FLV、MP4、MOV、AVI等。另外,还有一些特定的功能也以解封装模块的方式实现,如上面的ffmetadata。

2.4.5 通信协议支持

FFmpeg不仅支持本地的多媒体处理,还支持网络流媒体的处理。它支持的网络流媒体协议很全面,可以通过命令./configure --list-protocols进行查看。

async                   hls                     librtmpte               rtmpt
 
bluray                  http                    libsmbclient            rtmpte
 
cache                   httpproxy               libsrt                  rtmpts
 
concat                  https                   libssh                  rtp
 
concatf                 icecast                 libzmq                  sctp
 
crypto                  ipfs                    md5                     srtp
 
data                    ipns                    mmsh                    subfile
 
ffrtmpcrypt             libamqp                 mmst                    tcp
 
ffrtmphttp              librist                 pipe                    tee
 
file                    librtmp                 prompeg                 tls
 
ftp                     librtmpe                rtmp                    udp
 
gopher                  librtmps                rtmpe                   udplite
 
gophers                 librtmpt                rtmps                   unix

从协议相关信息列表中可以看到,FFmpeg支持的流媒体协议较多,包括MMS、HTTP、HTTPS、RTMP、RTP,甚至支持TCP、UDP这些基础网络协议,还支持本地文件file协议,以及多个文件拼接串流的concat协议,以及区块链技术中的ipfs协议。关于流媒体的通信协议部分,后面的章节会有详细介绍。

2.5 小结

本章重点介绍了FFmpeg的发展历程。对于一个发展了超过22年的项目,其背后是各种曲折的历史,更是大量社区成员努力的结果。随后介绍了FFmpeg源代码的获取、安装、编译等基本操作,对容器格式的封装与解封装、音视频编码与解码格式的支持,以及对流媒体传输协议的支持。总体而言,FFmpeg所支持的容器格式、编解码标准、流媒体协议都非常全面,是一款功能强大的多媒体处理工具和开发套件,因此它被称为多媒体领域的“瑞士军刀”也是名不虚传。

2020年开始,FFmpeg官方不继续提供开发者版本的调用库了,但是开发者中有热心的志愿者提供了对应的release版本的脚本代码库。如果有需要的话可以自行维护自己的一套代码库,毕竟FFmpeg本身是开源的,并且构建自己的发行版本的脚本代码库也是开源的。更多内容可以参考FFmpeg发行版本构建脚本的代码库:https://github.com/BtbN/FFmpeg-Builds

相关图书

Final Cut Pro X基础培训教程
Final Cut Pro X基础培训教程
在线视频技术精要
在线视频技术精要
三步玩转短视频
三步玩转短视频
新媒体短视频全攻略:前期拍摄+后期处理+广告变现+营销推广
新媒体短视频全攻略:前期拍摄+后期处理+广告变现+营销推广
调色师手册:电影和视频调色专业技法(第2版)
调色师手册:电影和视频调色专业技法(第2版)
Cubase与Nuendo音乐编辑与制作实战从入门到精通(第2版)
Cubase与Nuendo音乐编辑与制作实战从入门到精通(第2版)

相关文章

相关课程