一. 多媒体基础

1. 音频基础

①声音的物理属性

  • 声音的本质声音的本质是振动产生的压力波。声波通过介质(如空气、水或固体)传播,这些振动使得介质中的分子产生波动,从而在我们的耳朵里感知为声音。


  • 波形:波形是声波的形状,它反映了声音的振动模式。不同的波形产生不同的音色,使得我们可以区分不同的声音源。波是一种通过空间和时间传递能量的振动或扰动。波不传递物质本身,而是通过媒介(如水、空气、或真空)传递能量和信息。

  • image-dzcn.png


  • 频率:声音的频率(频率是用赫兹(Hz)来测量的)指的是每秒钟声波振动的次数(周期的倒数)。频率决定了声音的音高,高频率对应高音,低频率对应低音。千赫兹(kHz)即1000Hz,表示每秒振动1000次。人类能够听到的声音频率范围大约在20 Hz到20,000 Hz之间。低于20 Hz的声音称为次声波,而高于20,000 Hz的声音称为超声波。


  • 振幅:振幅指的是声波的波峰或波谷与平衡位置之间的距离(声音的大小)。它表示了声波的能量或强度,通常与声音的响度有关。振幅越大,声音听起来就越响;振幅越小,声音听起来就越小。在物理学中,振幅通常用来描述波的强度,不仅仅适用于声音,还可以应用于光波、电磁波等其他类型的波。

  • image-uxgg.png


②数字音频

  • 原因:数字音频通过将声音信号的振动转换为数值形式,从而使计算机能够识别和处理声音。

  • 计算机并不直接使用连续平滑的波形来表示声音,它每隔固定的时间对波形的幅值进行采样,用得到的一系列数字量来表示声音。


  • 采样、量化和编码:数字音频是将声音信号转换为数字格式的一种方式。这个过程通常包括采样、量化和编码3个步骤,可以将模拟信号转换为数字信号。

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

  • 数字音频通过从模拟信号中采样并转换成二进制(1/0)信号,并以二进制式电子、磁力或光学信号,而非连续性时间、连续电子或机电信号存储。

  • 采样:将连续的模拟信号转换为离散的数字信号的过程。在数字音频中,采样是数字化声音的第一步,它涉及在固定的时间间隔内对声音的振幅进行测量,并将这些测量值记录下来。

  • 量化:量化是在数字信号处理中,将连续的模拟信号的采样值映射到离散的数字值集合的过程。量化是数字音频数字化过程中的第二步,它将采样点的模拟幅度转换为计算机能够处理的数字表示形式。

  • 编码:编码是将量化后的数字音频数据转换为特定格式的过程,以便更有效地存储、传输和处理。编码是数字音频处理的第三步,也是将数字信号转化为可使用格式的重要步骤。


  • 数字信号专化为声音

  • image-upsv.png

③音频采样数据格式

  • 声道:声道(Sound Channel)是指声音在录制或播放时在不同空间位置采集或回放的相互独立的音频信号,声道数也就是声音录制时的音源数量或回放时相应的扬声器数量。


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


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


  • 比特率(码率)


  • 带宽:表示一定时间内传送的数据量。

  • 例如:一个双声道立体声、采样率是48000、采样位深是16位、时长为1分钟的音频所占用的存储空间的计算公式如下:

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

④PCM

  • PCM(Pulse Code Modulation,脉冲编码调制)是一种将模拟信号转化为数字信号的编码方法,广泛用于音频信号的数字化。PCM 是数字音频的基础,它将模拟信号的振幅在时间上进行离散化,从而使其能够被计算机处理和存储。

  • PCM三个步骤,即:采样、量化、编码。

  • PCM常用指标

  • 采样率、位深度、声道数、采样数据是否有符号。

  • 字节序:也称为字节顺序(Endianness),指的是在多字节数据类型(如整数、浮点数等)的存储中,字节的排列顺序。字节序定义了如何在计算机内存中将数据的字节按照特定的顺序排列,以便正确解释和处理数据。

2. 图像和视频基础

①颜色的存储和显示

②基本数据概念

  • 像素

  • image-fcoi.png


  • 分辨率

  • image-hgit.png

  • image-quep.png


  • 位深

  • image-alwc.png


  • 帧率

  • image-enne.png


  • 码率

  • image-muaa.png

③RGB色彩表示

④YUV色彩表示

  • 概念

  • image-esyy.png


  • 由来

  • image-sbuf.pngimage-lnyq.pngimage-kjpo.pngimage-lnyw.pngimage-ifkh.png


  • YUV采样格式

  • image-onxx.pngimage-guhz.pngimage-wvli.pngimage-evql.pngimage-lane.pngimage-uqoy.png


  • YUV数据存储

  • image-lkaq.pngimage-qrjc.png

  • 基于YUV444采样的存储格式:

  • image-uejd.png

  • 基于YUV422的存储格式

  • image-tosq.png

  • 基于YUV420的存储格式

  • image-tizj.png

  • image-dxua.pngimage-wyug.png


  • RGB和YUV的转换

  • image-fsts.pngimage-uylg.png


  • YUV Stride对齐问题

  • image-fuyl.png

⑤视频的基础概念

  • 视频的原理是通过快速连续播放一系列静止图像(帧)来创造运动的视觉效果。每秒钟播放的帧数称为帧率(FPS),通常为24或30帧以上,以使人眼感知为平滑的运动。每一帧是图像,通过颜色数据编码为二进制格式存储并显示。当这些帧依次播放时,大脑将这些静止图像整合为连续的动态场景,从而产生视频效果。

  • image-ptaq.png

3. 音视频录制与播放原理

①录制原理

image-kajg.png

②播放原理


image-xgfc.png

二. FFmpeg简介

1. 介绍

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

  • FFmpeg提供了多种媒体格式的封装和解封装,以及编解码等,包括多种音视频编码、字幕、不同协议的流媒体、丰富的色彩格式转换、音频采样率转换等。FFmpeg的内部框架提供了丰富的API及可扩展的插件系统,既可以灵活地使用多种封装与解封装、编码与解码的插件,也可以灵活地基于其框架进一步扩展。


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

  • 帧(Frame):指的是在特定时间点捕获或生成的一组数据。帧是媒体流的基本单位,对于视频来说是图像,对于音频来说是一段音频数据。当FFmpeg解码一个包时,它会提取并解压缩其中的帧。对于视频包,解码后的帧是图像;对于音频包,解码后的帧是音频采样数据。一个包可能只包含一部分帧的数据,尤其是在高压缩的情况下。解码器需要处理多个包才能恢复完整的帧。

  • 包(Packet):包是流中数据的基本传输单元。每个包通常包含一个或多个编码后的帧(如视频帧或音频帧)以及相关的元数据(例如时间戳)。每个包 (AVPacket) 都属于一个特定的流 (AVStream)。流决定了包的数据类型(如视频或音频),而包则携带实际的编码数据。

  • 流(Stream):在容器中存储音频(Audio)或者视频(Video)、字幕(Subtitle)等数据。流是指媒体文件中的数据序列,如视频流、音频流、字幕流等。一个媒体文件(如 MP4、MKV)可以包含多个流,每个流代表一种类型的媒体数据。例如,一个视频文件可能包含一个视频流和一个音频流。常见的流类型包括视频流(通常由压缩后的帧序列组成)、音频流(通常由压缩后的音频数据序列组成)以及其他数据流(如字幕或附加数据)。媒体文件中的每个流包含多个包,这些包按照时间顺序排列。FFmpeg 处理媒体文件时,会解析文件中的各个流,然后逐一处理和解码其中的包。

  • 节目(Program):在某些多媒体文件(如 MPEG-TS、Matroska 等)中,一个节目(Program)可以包含多个流(Stream),这些流通常是同步的。例如,一个节目可能包括一个视频流和多个音频流或字幕流。一个文件可以包含多个节目,每个节目可能有多个流。节目用于组织和标识多媒体文件中的不同流。对于大多数简单的媒体文件(如 MP4),通常只有一个节目,其中包含视频和音频流。

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

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

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

2. FFmpeg基本组成

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

①封装/解封装模块AVFormat

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

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

②编/解码模块AVCodec

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

③滤镜模块AVFilter

  • AVFilter库提供了一个通用的音频、视频、字幕等滤镜处理框架。在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

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


  • 滤镜的构成规则:

  • 【1】相同滤镜的线性链用逗号分隔。

  • 【2】不同滤镜的线性链之间用分号分隔。

④设备模块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图像序列等)。

⑤图像转换模块swscale

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

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

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

⑥音频转换模块swresample

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

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

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

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

⑦编解码工具ffmpeg

  • ffmpeg是FFmpeg源码编译后生成的一个可执行程序,可以作为命令行工具使用。

  • 【1】解封装(Demuxing),或称解复用。

  • 【2】解码(Decoding)

  • 【3】编码(Encoding)

  • 【4】封装(Muxing),或称复用

  • 整体处理工作流程

⑧播放器ffplay

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

⑨多媒体分析器ffprobe

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

3. 不同平台下的编译(Linux)

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

  • 默认编译(Linux)FFmpeg的时候,需要用到nasm汇编器对FFmpeg中的汇编部分进行编译。

4. FFmpeg特性的选择与定制

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

  • 注意:从2016年年初起,FFmpeg自身的AAC编码器质量逐步好转,至2016年年底,libfaac已从FFmpeg源代码中剔除,但依然可以使用第三方libfdk-aac库来执行AAC的编解码支持。FFmpeg默认支持的音视频编码格式、文件封装格式与流媒体传输协议比较多,因此编译的FFmpeg文件较大。而在有些应用场景中并不需要FFmpeg支持如此多的编码、封装或者协议,为了减小最终编译出来的库的体积(如在手机端等须注意最终包大小的场景等),这时候可以通过configure --help查看一些有用的选项以用作后续的裁减。可以通过一些选项关闭不需要的编解码、封装/解封装与协议等模块。

  • 仅支持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即可。支持其他对应的编码与此类似。

  • 通过一些选项关闭不需要的编解码、封装/解封装与协议等 模块./configure --disable-encoders --disable-decoders --disable-hwaccels --disable-muxers --disable-demuxers --disable-parsers --disable-bsfs --disable-protocols --disable-indevs --disable-devices --disable-filters

  • 编码器支持:压缩,一般通过使用编译配置命令./configure --list-encoders参数来查看。

  • 解码器支持:解压缩,通过./configure–list-decoders命令进行查看。

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

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

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

三. FFmpeg工具使用基础

1. ffmpeg常用命令

  • ffmpeg在执行音视频编解码、转码等操作时非常便利,很多场景下转码直接使用ffmpeg即可。通过ffmpeg --help命令可以看到ffmpeg常见的命令,大概分为以下6个部分:ffmpeg信息查询部分、公共操作参数部分、文件主要操作参数部分、视频操作参数部分、音频操作参数部分、字幕操作参数部分。具体ffmpeg --help 显示内容如下:

  • [ ]表示是可选项,...表示可以有多个,{}表示必须包含的意思,<>表示占位符实际数据等输入。

①封装转换

  • FFmpeg的封装转换(转封装)功能主要基于AVFormat模块,通过libavformat库进行Mux和Demux操作。我们知道,多媒体文件的格式多种多样,在FFmpeg的实现中,这些格式中很多操作参数是公用的,而其他特定参数使用上述命令即可查询。下面详细介绍一下这些与容器格式相关的公用参数。

  • 通过查看ffmpeg --help full信息,找到AVFormatContext参数部分,在这个参数下面的所有参数均为封装转换可使用的参数,如下所示。AVFormatContext是FFmpeg库中的一个重要结构体,它在处理多媒体文件(音频、视频等)的输入和输出时扮演着关键角色。AVFormatContext包含了关于多媒体流的信息,管理文件的读写操作,并处理流的编解码等工作。

②解码和编解码

  • FFmpeg编解码部分的功能主要通过AVCodec这个模块来完成,通过使用libavcodec库进行解码与编码操作。多媒体领域的编码格式种类很多,FFmpeg把这些操作分为通用操作和基于特定编解码器的操作,目前还是有很多基本的操作参数是通过通用设置来支持的。下面详细介绍这些通用的参数。

  • 使用命令ffmpeg --help full可以看到AVCodecContext参数列表信息,如下表所示。在这个选项下面的所有参数均为编解码可以使用的参数,但实际上需要注意,并不是每个编解码器都完全支持这些参数。

  • AVCodecContext主要参数

③转码流程

  • ffmpeg工具的主要用途为编码、解码、转码和媒体格式转换 等,其中转码差不多覆盖了上面的所有操作可以设置转码的相关参数,而转码操作有时也会伴随着封装格式的改变。可以通过设置AVCodec与AVFormat的参数,改变封装格式与编码格式。

  • 例子:ffmpeg -i ~/Movies/input1.rmvb -vcodec mpeg4 -b:v 200k -r 15 -an output.mp4 转封装格式从RMVB格式转为MP4;视频编码从RV40转为MPEG4;视频码率从原来的377 kbit/s转为200 kbit/s;视频帧率从原来的23.98 fps转为15 fps;转码后的文件中不包括音频(-an参数)。首先解封装,需要解封装的格式为RMVB;然后解码,其中视频格式为RV40,音频格式为COOK,找到它们对应的解码器执行解码操作;解码后的视频会被编码为MPEG4,而音频被丢弃了;随后封装为一个没有音频的MP4文件。

2. ffprobe常用命令

  • ffprobe主要 用来查看和分析多媒体文件。

①ffprobe常用参数

  • 常用参数

  • -v:指定输出的详细程度。0为较少的信息,9为更多的信息。

  • -show_format:查看媒体文件的容器信息,包括格式、时长、码率等。

  • -show_streams:查看媒体文件的流信息,包括编码格式、帧率、分辨率等。

  • -show_chapters:查看媒体文件的章节信息。

  • -of:指定输出的格式,支持的格式包括JSON、XML等。


  • 查看详细帮助信息ffprobe --help

Simple multimedia streams analyzer
usage: ffprobe [OPTIONS] INPUT_FILE //使用格式

Main options://主要选项
-L                  show license
-h topic            show help
-? topic            show help
-help topic         show help
--help topic        show help
-version            show version
-buildconf          show build configuration
-formats            show available formats
-muxers             show available muxers
-demuxers           show available demuxers
-devices            show available devices
-codecs             show available codecs
-decoders           show available decoders
-encoders           show available encoders
-bsfs               show available bit stream filters
-protocols          show available protocols
-filters            show available filters
-pix_fmts           show available pixel formats
-layouts            show standard channel layouts
-sample_fmts        show available audio sample formats
-dispositions       show available stream dispositions
-colors             show available color names
-loglevel loglevel  set logging level
-v loglevel         set logging level
-report             generate a report
-max_alloc bytes    set maximum size of a single allocated block
-cpuflags flags     force specific cpu flags
-cpucount count     force specific cpu count
-hide_banner hide_banner  do not show program banner
-sources device     list sources of the input device
-sinks device       list sinks of the output device
-f format           force format
-unit               show unit of the displayed values
-prefix             use SI prefixes for the displayed values
-byte_binary_prefix  use binary prefixes for byte units
-sexagesimal        use sexagesimal format HOURS:MM:SS.MICROSECONDS for time units
-pretty             prettify the format of displayed values, make it more human readable
-output_format format  set the output printing format (available formats are: default, compact, csv, flat, ini, json, xml)
-print_format       alias for -output_format (deprecated)
-of format          alias for -output_format
-select_streams stream_specifier  select the specified streams
-sections           print sections structure and section information, and exit
-show_data          show packets data
-show_data_hash     show packets data hash
-show_error         show probing error
-show_format        show format/container info
-show_frames        show frames info
-show_entries entry_list  show a set of specified entries
-show_log           show log
-show_packets       show packets info
-show_programs      show programs info
-show_streams       show streams info
-show_chapters      show chapters info
-count_frames       count the number of frames per stream
-count_packets      count the number of packets per stream
-show_program_version  show ffprobe version
-show_library_versions  show library versions
-show_versions      show program and library versions
-show_pixel_formats  show pixel format descriptions
-show_optional_fields  show optional fields
-show_private_data  show private data
-private            same as show_private_data
-bitexact           force bitexact output
-read_intervals read_intervals  set read intervals
-i input_file       read specified file
-o output_file      write to specified output
-print_filename print_file  override the printed input filename
-find_stream_info   read and decode the streams to fill missing information with heuristics
......

②ffprobe使用示例

  • 查看多媒体数据包(编码后的数据包)信息ffprobe -show_packets input.flv

  • show_packets查看的多媒体包信息使用[PACKET]标签,其中包含的字段信息如下所示。

  • 组合参数查看包中具体数据ffprobe -show_data - show_packets input.flv


  • 分析多媒体的封装格式ffprobe -show_ format output.mp4 封装相关信息在输出中使用[FORMAT]标签。

  • format关键字段说明


  • 查看帧信息ffprobe -show_frames input.flv 输出的帧信息使用[FRAME]标签。

  • frame重要字段说明


  • 查看多媒体文件中的流信息:ffprobe -show_streams input.flv 流信息使用[STREAMS]标签,其中重要字段说明如下所示。


  • 输出格式

  • ffprobe使用前面的参数可以获得key-value格式的显示方式,有时需要计算机对输出信息进行处理,则可以定义输出的格式,我们使用ffprobe -print_format或者ffprobe -of参数来设定相应的输出格式,其支持的格式包括XML、INI、JSON、CSV、FLAT等。

  • XML输出格式ffprobe -of xml -show_streams input.flv


  • 筛选操作

  • 使用select_streams可以查看音频(a)、视频(v)、字幕 (s)的信息。

  • 配合show_frames查看视频的帧信息:ffprobe -show_frames -select_streams v -of xml input.mp4

  • -

  • show_entries 是 FFmpeg 中的 ffprobe 工具提供的一个选项,用于指定希望在输出中显示的条目或字段。使用 show_entries 可以过滤输出,只显示你关心的特定信息,而不是输出所有的元数据。

  • -

  • 如果有多个视频流,则可以再通过stream_index过滤。

  • stream_index(stream_idx) 是一个标识符,用于区分多媒体文件中的不同流(Stream)。一个多媒体文件(如 MP4、MKV 等)可能包含多个流,每个流可能是视频、音频、字幕或其他类型的媒体数据。stream_idx 代表了特定流在整个文件中的位置或索引。

  • ffprobe -of xml -select_streams v -show_packets -show_entries packet=stream_index, codec_type,pts_time,flags,pos input.mp4 | grep flags=\"K | grep stream_index=\"0


  • loglevel

  • loglevel用于控制日志输出的详细程度。要使用 loglevel 查看 MP4 视频文件中 moov 原子(也称为 moov box)的位置信息,可以使用 FFmpeg 的调试日志级别。moov 是 MP4 文件中的一个重要部分,它包含视频和音频数据的元数据。FFmpeg 在处理 MP4 文件时,会在日志中显示 moov 原子的位置。

3. ffplay常用命令

  • 在编译旧版本FFmpeg源代码时,如果系统中包含了SDL-1.2版本,会默认编译生成ffplay;如果不包含SDL-1.2或者版本不是SDL-1.2,则无法生成ffplay文件。所以如果想使用ffplay进行流媒体播放测试,需要安装SDL-1.2。而在新版本的FFmpeg源代码中,ffplay需要SDL-2.0之后的版本。ffplay使用SDL的原因主要是SDL是一个跨平台的多媒体开发库,屏蔽了不同平台诸如Windows、Linux、macOS相关的底层细节,使得ffplay可以很方便地同时支持多个平台。但随之也引入了一些限制,比如经常会被问到的ffplay是否支持硬解码与渲染一体加速的问题,ffplay实际上并未支持,究其原因,ffplay和其他FFmpeg工具集一样,它们最初是作为工具提供,并没有打算开发为一个完备可用的播放器方案。通常我们在FFmpeg中使用ffplay作为播放器,其实ffplay不但可以做播放器,还可以作为很多音视频数据的图形化分析工具,例如通过ffplay可以看到视频图像的运动估计方向、音频数据的波形等。

①ffplay常用参数

  • ffplay不仅是播放器,同时也是测试FFmpeg的Codec组件、Format组件以及Filter功能的可视化工具,并且可以做可视化的媒体参数分析。基本参数可以通过ffplay --help进行查看。大多数参数在前面已经介绍过,这里不再赘述。一些未介绍过的参数说明如下所示。

②ffplay的数据可视化分析应用

  • 除了可以播放视频流媒体文件,ffplay还可以作为简化版本的可视化的视频流媒体分析工具。例如当播放音频文件时,若需要判断文件声音是否正常、分析噪声数据等,可以直接使用ffplay播放音频文件,并在播放的时候将解码后的音频数据以音频波形的形式显示出来,如下所示。ffplay -showmode 1 output.mp3

③ffplay快捷键

  • 虽然ffplay在播放时不带控制UI,但是支持了常用的快捷键来控制播放。测试快捷键功能时,建议切换到英文 输入法,以减少一些不必要的按键失误。

四. 封装与解封装

  • 容器格式是一种允许将单个或多个音频、视频、字幕等数据流存入单个文件的文件格式,通常伴随着用于识别和进一步详细说明这些数据流的元数据,元数据一般被存储在文件的头部,有时候也被称为音视频关键数据索引。典型的音视频多媒体容器格式如FLV、MP4、MPEG-TS、RMVB和AVI等。较简单的容器格式可以包含不同类型的音频、视频流,而更高级的容器格式可以支持多个音频和视频流、字幕、章节信息、元数据(或称为标签),以及播放各种类型的流所需的同步信息。同时,容器格式需要解决下面所示的这些问题。

1. 视频文件转MP4

  • 在互联网常见的格式中,跨平台最好的应该是MP4文件。MP4文件既可以在PC平台播放,也可以在Android、iOS等移动平台中播放,而且使用系统默认的播放器即可播放。同时它也被MSE(Media Source Extensions) 所支持,这意味着浏览器的生态也支持它。MP4文件包含视频和音频基本流,以及正确播放和编辑所需的上下文信息(通常称为元数据)。粗略地说,MP4文件分为两个主要部分:元数据(moov)和音视频数据(mdat)。其中,元数据moov包含通用信息,如每个音频、视频帧的时间信息和偏移量等;mdat包含视频和音频帧,通常以交错顺序(尽管也支持所谓的平面顺序,实际上MP4也支持把音频和视频放在两个不同的文件中)存放。请注意,MP4中的视频帧没有以起始码作为前缀,而是使用长度;然而,我们可以通过元数据中相应表里的偏移量轻松地访问任何音频、视频帧。

  • MP4文件格式中包含多个子容器,每个子容器在标准中都称为box或atom,在交流时通常不翻译成容器、盒子、箱子,因为容易给对方造成理解上的困扰。前面讲过的moov及mdat等,都是box。

①MP4格式标准介绍

  • MP4文件由许多个box与FullBox组成,无一例外。

  • 每个box由header和data两部分组成。size字段的大小包含size和type这八个字节,实际有效负载data是size-8。

  • -

  • FullBox则是box的扩展,以box结构为基础,在header中增加8位的version标志和24位的flags标志。增加version标志意味着这个box有了灵活扩展的可能,而flags标志则是在特定FullBox中定义的。

  • header包含了整个box的长度大小(size)和类型(type),类型是一个典型的4字符的标签,一般被称为FourCC。当size等于0时,代表这个box是文件的最后一个box;当size等于1时,说明box长度需要更多的位来描述,在后面会定义一个64位的largesize来描述box的长度,如图4-4所示。当type为uuid时,说明这个box中的数据是用户自定义扩展类型。

  • 随着4K视频和高帧率视频等高比特率视频的出现,越来越多超过4GB的视频正在被录制。视频数据被写入名为“mdat”的box中,但如果box的大小为32位,当文件大小超过4GB时就不够了,为此,需要提供一个扩展的尺寸,即将box头部的size设置为1,而largesize作为一个64位无符号整数用来记录大于4GB的box的长度。

  • 这也意味着一个大小为1的box是不存在的,box的“大小+类型”必须至少有8字节。另外,固定的4字节的大小(size)也使得解析一个box或者跳过一个box变得非常便利。data为box的实际数据,可以是纯媒体数据,也可以是更多的子box。这意味着box是分层嵌套的,即一个box里可以有多个box,并可以多层嵌套,如图4-5所示。当一个box中data是一系列的子box时,这个box又可以称为Container(容器)box。

  • 例图别的形式

  • -

  • 表4-1中列出了MP4文件中常用的box类型和组成方式,其中标记“√”的为必要的box,否则为可选的box。一级是最外层,六级是最里层,包含关系,这些box以树形结构的方式组织

  • 在MP4文件中,box的基本层次排列与表4-1的描述没有太大差别,但顺序上可能有所变化。当然,因为MP4标准中描述的moov与mdat的存放位置前后并没有强制要求,所以有时moov被存放在mdat的后面,有时moov被存放在mdat的前面。在互联网的视频点播中,如果希望MP4文件被快速打开,则需要将moov存放在mdat的前面,如果放在后面,需要将MP4文件下载完成后才可以进行播放。

  • 解析MP4多媒体文件时,需要一些关键的信息,下面介绍一些box的重要相关信息。

  • MP4中解析是以十六进制格式,十六进制每位数字占4位,两个十六进制数占8位一字节。十六进制转十进制同二转十进制。


  • moov解析

  • moov定义了MP4文件的元数据信息,在MP4文件中有且仅有一个,moov里面包含的子box作为描述媒体数据的信息的容器。这些元数据信息被存储在不同类型的子box中。一般来说,元数据被存储在moov box中,而多媒体的实际数据,如音频或视频数据,则在moov box中被引用,但不包含在其中。

  • moov至少包含以下3种box中的一种:mvhd:Movie Header box,存放多媒体信息头的容器,这是最常见的形式;cmov:Compressed Movie box,压缩过的电影信息容器;rmra:Reference Movie box,参考电影信息容器。它也可以包含其他容器,例如影片剪辑信息Clipping box(clip)、一个或几个trak box、一个Color Table box(ctab),以及一个User Data box(udta)。

  • moov本质上是其他box的一个容器,这些box组合在一起描述了多媒体的内容。从高层结构看,moov通常包含trak box,而trak box又包含mdia box。最低层的子box则包含非box格式的数据,通常以表格或一组数据元素的形式出现。例如,一个trak box包含一个edts box,而edts box又包含一个elst box,这个子box包含以编辑列表形式存在的数据。

  • moov中最为常见的是mvhd,它定义了整个多媒体文件的timescale、duration等显示特性。而trak中定义了多媒体文件中的一个track的信息,track指的是多媒体文件中可以独立操作的媒体单位,例如一个声道是一个track,一个视频流也是一个track。

  • -

  • 用二进制查看工具打开一个MP4文件查看其内容,可以了解 前面所讲的MP4文件容器信息。

  • 根据解析的这个容器的字节长度可以看到,该容器共包含0x000022bb(8891)字节,类型为moov。下面继续在moov这个容器中往下解析,下一个容器大小为0x0000006c(108)字节,类型为mvhd。

  • 注意上面开头是90, mvhd108个字节,去掉8个受头部字节从30到90的0000 0003 0000正好108个字节。

  • 分析完mvhd之后,moov中的下一个容器是trak,容器的大小是0x000011de(4574)字节,类型是trak。解析完这个trak之后,接下来又是一个trak,解析方式与之前trak的解析相同。可以看到,下面文件内容的trak的大小为0x00001007(4103)字节。另外,trak box有两种,分别为media track和hint track,前者用于保存media相关信息,后者包含用于流媒体的打包信息。

  • 解析完这个音频的trak之后,接下来可以看到还有一个moov容器中的子容器,即udta容器。这个udta容器的解析方式与前面的方式基本相同。从下面的文件数据中可以看到,它的大小为0x00000062(98)字节。

  • 根据前面描述的信息得知,udta+视频trak+音频trak+mvhd+moov所有容器的总大小刚好为8891字节,与前面得出的moov的大小相等。


  • mvhd解析

  • mvhd box在moov box里面,包含了与整个播放展示相关的元数据。诸如文件的创建和修改时间等信息,它告诉我们视频播放器总时长、time scale、播放速度和初始音量。

  • 按照上表的方式对文件数据解析出来的mvhd内容对应的信息如下所示。


  • trak解析

  • trak定义了媒体文件中一个track(轨道)的信息。一个媒体文件可以包含多个track,每个track都是独立的,有自己的时间和空间占用的信息。每个trak容器都有与它关联的媒体容器描述信息。使用trak的主要目的如下:

  • 【1】包含媒体数据的引用和描述(media track)。

  • 【2】包含modifier track信息。

  • 【3】包含流媒体协议的打包信息(hint track),hint track可以引用或者复制对应的媒体采样数据。

  • hint track和modifier track必须保证完整性,与至少一个media track同时存在。一个trak中要求必须有一个Track Header box(tkhd)、一个Media box(mdia),其他的box都是可选的。

  • 从文件的数据内容中可以看到,这个trak的大小为0x000011de(4574)字节,下面的子容器的大小为0x0000005c(92)字节,子容器的类型为tkhd;跳过92字节后,接下来读到的trak的子容器的大小为0x00000030(48)字节,子容器的类型为edts;跳过48字节后,接下来读到的trak子容器的大小为0x0000114a(4426)字节,子容器的类型为mdia。可以分析得到,trak容器信息(8)+tkhd(92)+edts(48)+mdia(4426)子容器的大小刚好为4574字节。trak读取完毕。


  • tkhd解析

  • tkhd放在trak box里,每个track只能有一个tkhd。它是强制性的,包含描述单个轨道的特性的元数据。

  • tkhd中的很多字段信息与mvhd有些类似,因为mvhd描述整个文件的公共信息,而thkd描述整个文件中某个track的信息。

  • -

  • 以上为解析视频trak容器的tkhd。下面再分析一下音频的tkhd

  • 音频与视频的trak的tkhd大小相 同,但因为里面的内容描述的是音频轨道,所以其类型和取值 有所不同。


  • mdia解析

  • 下trak的子容器。Media box的类型是mdia,是一个容器box,其必须包含如下容器:

  • 媒体头容器:Media Header box(mdhd)

  • 句柄参考容器:Handler Reference box(hdlr)

  • 媒体信息容器:Media Information box(minf)

  • 用户数据容器:User Data box(udta)

  • 从文件的内容可以看到,这个mdia容器的大小为0x0000114a(4426)字节,mdia容器下面包含了三大子容器,分别为mdhd、hdlr和minf,其中mdhd大小为0x00000020(32)字节;hdlr大小为0x0000002d(45)字节;minf大小为0x000010f5(4341)字节;mdia容器信息(8)+mdhd(32)+hdlr(45)+minf(4341)容器大小刚好为4426字节。


  • mdhd解析

  • mdhd被包含在各个track中,描述Media的Header

  • 根据ISO14496-Part-12标准,当版本字段为0时,解析与当版本字段为1时的解析稍有不同。这里使用4字节/32位的版本。

  • 这个Media Header的大小是32字节,类型是mdhd,版本为0,生成时间与修订时间都为0,计算单位时间是25000,媒体时间戳长度为250000,语言编码是0x55C4(具体代表的语言可以参考标准ISO 639-2/T)。音频时长可以根据duration / timescale的方式计算,根据本例中的数据可以计算出音频的时间长度为10秒。


  • hdlr解析

  • hdlr描述了媒体流的媒体类型,可以根据这个box的内容,确定对应track的具体类型是Video、Audio或者其他。

  • 这是一个视频track对应的数据,对应组件的名称为VideoHandler,并以一个0x00结尾。


  • minf解析

  • minf包含了很多重要的子容器,例如与音视频采样等信息相关的容器。minf容器中的信息将作为音视频数据的映射存在,其内容信息如下。

  • 视频信息头:Video Media Information Header(vmhd子容器)

  • 音频信息头:Sound Media Information Header(smhd子容器)

  • 数据信息:Data Information(dinf子容器)

  • 采样表:Sample Table(stbl子容器,描述具体的数据与时间、位置等信息的对应关系)


  • vmhd解析

  • vmhd box用于描述一些与视频track编码无关的通用信息


  • smhd解析

  • smhd box与vmhd box类似,主要用在音频track上


  • dinf解析

  • dinf是一个描述数据信息的容器,定义了音视频数据的信息,它包含子容器dref。


  • stbl解析

  • 概念定义:

  • sample:采样,多媒体数据的最小单位,与一个时间戳相关的所有数据,一般对应视频中的一个帧,或对应Audio中一段压缩的音频。一般而言,同一个track中不可能有两个或者多个sample具有相同的时间戳。

  • chunk:块,一个track的几个连续sample组成的单元称为一个chunk,同一chunk内的sample是连续的,它是一个逻辑概念。在fMP4格式中,则使用run来表征类似的意思。

  • track:流,表示一些sample的集合,对于媒体数据来说,表示一个视频或者音频序列。

  • stbl为采样列表容器(Sample Table box),该容器包含转化媒体时间到实际的sample(样本或者采样点)的信息,也表征了如何进一步解析sample的信息,例如,视频数据是否需要解压缩、解压缩采用的是什么编码算法等。

  • 一个track由连续的chunk组成,而chunk则包含多个连续的sampletrack和sample的概念比较容易理解,chunk主要是增加一个中间的层,这样的好处是不用直接做track到sample的映射,这些box的大小可以通过增加的chunk层得以可控等。

  • -

  • 采样描述容器:Sample Description box(stsd)

    采样时间容器:Time To Sample box(stts)

    采样同步容器:Sync Sample box(stss)

    Chunk采样容器:Sample To Chunk box(stsc)

    采样大小(实际数据大小)容器:Sample Size box(stsz)

    Chunk偏移容器:Chunk Offset box(stco)

    Shadow同步容器:Shadow Sync box(stsh)

  • stbl包含track中media sample的所有时间和数据索引,利用stbl,就可以定位sample到媒体时间、文件位置的映射关系,决定其类型、大小,以及如何在其他容器中找到紧邻的sample。如果它所在的track没有引用任何数据(即真实视频或音频数据),那么它就不是一个有用的media track,不需要包含任何子box。如果它所在的track引用了数据,那么必须包含以下子box。

  • 采样描述容器(stsd):它主要包含解码器所需要的基本信息,里面的细节规定一般与特定的编码器相关。

    采样大小容器(stsz)。

    Chunk采样容器(stsc)。

    Chunk偏移容器(stco)。

    所有的子box都有相同的sample数目(实际数据大小)

  • stbl是必不可少的一个box,而且必须包含至少一个条目,因为它包含了检索media sample的索引信息。没有sample description就不能计算出media sample存储的位置。


  • edts解析

  • edts定义了创建Movie媒体文件中的一个track的一部分媒体,所有的edts数据都在一个表里,包括每一部分的时间偏移量和长度。如果没有该表,这个track就会被立即播 放。一个空的edts用来定位到track的起始时间偏移位置。

  • 这个edts box的大小为0x00000030(48)字节,类型为edts;其中包含了elst子容器,elst子容器的大小为0x00000028(40)字节,edts容器+elst子容器的大小为48字节。elst因为在逻辑上把trak做了一个偏移,很多播放器或者对应的工具支持得并不是很好,所以碰到这个box需要注意兼容性问题。

②Fragment MP4与CMAF

  • fMP4

  • 前面提及的MP4内容本身适用于存储和点播场景。但是,直播场景中的传输是流式的,在这种情况下,针对流式场景下的Fragment MP4(又称fMP4)及CMAF被提了出来。

  • 普通模式下,MP4单一的moov box模式的限制如下

  • 在写入全部数据之前不能写入该box,这样对于捕获/实时录制是一个挑战。如果没有写入最后的moov box,意味着前面的工作完全白费,并且还没有什么机会修复这个文件。

  • 对于几个小时的电影来说,moov box的大小可能相当大,这对内存优化来说是个问题。对于网络播放场景,大的moov box意味着需要先行下载大量的数据才能开始播放,很多时候高达几兆大小的moov box并不少见,这对于快速播放显示不利。

  • 基于上面这些问题,fMP4及后来的CMAF被提出来。其基本原理是,将一部电影或直播流划分为较小的片段,称为分片,分片这种能力是流媒体应用的关键。与原来等待一个完整的MP4文件的下载相比,我们更希望每次下载的时间不超过几秒钟,这样就可以在下载的过程中同时播放。此外,随着网络条件的变化,我们还希望能够在不同版本的流媒体之间无缝切换,以便得到更高或更低的分辨率,或者更多或更少的压缩比。这就是自适应比特率流媒体的特点。分片越短,就越能快速地适应网络环境的变化。一般通过判断是否存在mvex box来判断一个文件是普通MP4还是fMP4。

  • 一个 fMP4 流由一个初始化段和一连串的媒体段组成。初始化段类似于一个未分片文件的开始,它由一个ftyp box和一个moov box组成。moov box包含额外的信息,以表明流是切片的,主要包括一个mvex box。不过与上面的传统MP4相比较,fMP4的moov box只存储了文件级别的媒体信息,因此比传统MP4文件的moov box要小很多。mvex是fMP4的标准box,它的作用是告诉Demuxer端这是一个fMP4文件,具体的sample信息内容不再放到trak里,而是放到每一个moof中。

  • 音视频切片文件由一个moof(movie fragment)box和一个mdat box组成,前者包含该片段的元数据,后者包含部分音频、视频的有效载荷。moof box存放的是fragment(分片)级别的元信息,用于描述所在的fragment。该类型的box在普通的MP4文件中是不存在的,而在fMP4文件中,每个fragment都会有一个moof类型的box。moof和moov类似,它包含了当前片段中MP4的相关元信息,但将moov的部分信息变成了多个moof这样的方式。另外,一般而言,普通的MP4文件只有一个mdat box,而fMP4/CMAF文件则有多个mdat box。它也可能在开始时有一个styp box,styp就像ftyp一样,但针对的是一个音视频切片片段。在一个正确编码的流中,每个媒体fragment都可以被解码和播放,除去需要所依赖的初始化片段外,任何其他片段的信息都不再需要。

  • 上面的mvex box是可选的,其由mehd和trex组成,其中,mehd是可选的,它指定整个文件的持续时间,整个文件的持续时间对应于最长的轨道持续时间(包含所有分片);trex是必选的(如果mvex存在),每个轨道都有一个单独的trex,这个box为其轨道(track_ID)指定默认的标志和值。在moof box中,对应特性如果没有被标识或设置对应的值,解码器将从trex box中获取对应轨道的默认值。通常每个封闭的GoP都作为一个单独的分片来存储,这种分片模式被称为基于关键帧的分片。


  • CMAF(通用媒体应用格式,Common Media Application Format)

  • 一种可扩展的格式,用于打包分段的媒体对象,以便在自适应媒体流中在终端用户设备上传输和解码。

  • CMAF通过与HLS和DASH协议合作,在一个统一的传输容器文件下打包数据,简化了播放设备的媒体传输过程。

  • CMAF本身不是一个协议,而是一种格式,它包含一套容器和标准,用于统一HLS和MPEG-DASH等协议底层的单一媒体流。CMAF以ISO BMFF为基础,为分片的内容定义两个基础的brand(品牌):cmfc和cmf2。其限定主要如下。

  • cmfc品牌的约束如下:

  • 定义了ISO BMFF box中的一些默认值。

    每个文件只包含一个媒体,这意味着音频和视频存储在不同的文件中,这个特性带来了灵活性,但也带来了一些挑战。

    每个分片有一个单一的轨道片段(moof)。

    视频轨道中存在ColorInformation box和PixelAspectRatio box。

    在每个轨道分片中存在一个tfdt box,在MPEG DASH格式中,分片在时间上可能是不连续的。因此,DASH要求每个分片(即每个traf box)中都要有tfdt box。注意,tfdt box指定了当前分片的解码时间(以mvhd时间尺度为单位,当前分片中第1个样本的解码时间,实际上tfdt是分片的一个时间锚点)。

    edit list被限定为仅在媒体跳过时使用。

  • 在cmfc品牌的基础上,cmf2品牌进一步限定如下:

  • 不应使用edit list。

    可以使用负的composition偏移。

    样本默认值应在每个轨道分片中重复出现。

③MP4分析工具

  • 在实际应用中我们经常需要对MP4文件进行分析。分析MP4封装格式的工具比较多,除了FFmpeg之外,还有一些常用工具,如Elecard StreamEye、MP4Box、mp4info、mediainfo、l-mash、Bento4等。


  • Elecard StreamEye

  • Elecard StreamEye是一款非常强大的视频信息查看工具,能够查看帧的排列信息,将I帧、P帧、B帧以不同颜色的柱状展现出来,而且柱的长短根据帧的大小展示。我们还能够通过Elecard StreamEye分析MP4的封装内容信息,包括流、宏块、文件头、图像及文件的信息等;还能够根据每帧的顺序进行逐帧查看,看到每一帧的详细信息与状态。同时,它也简单支持一些容器格式的信息查看。


  • MP4Box

  • MP4Box是GPAC项目中的一个组件,它是一个命令行工具,可以通过MP4Box命令针对媒体文件进行合成、拆解等操作。


  • mp4info

  • mp4info是可视化的MP4分析工具,可以将MP4文件中的各box解析出来,并将其中的数据展现出来,分析MP4内容时使用mp4info会更方便。通过mp4info可以解析MP4文件容器,解析的box格式可以直接展现出来。相关box解析信息比之前逐字节地读取和解析方便很多,也更加直观。

④MP4在FFmpeg中的Demuxer

  • ffmpeg -h demuxer=mp4 命令查看FFmpeg的MP4的Demuxer的方法。

⑤MP4在FFmpeg中的Muxer

  • ffmpeg -h muxer=mp4 显示mp4的muxer方法。

  • FFmpeg生成的moov在mdat写完成之后才写入,可以通过参数faststart将moov容器移动至mdat前面:ffmpeg -i input.flv -c copy -f mp4 -movflags faststart output.mp4

  • Apple平台的兼容问题:在封装HEVC码流的时候,FFmpeg默认生成的MP4经常会面临在Apple相关平台上的播放兼容问题,核心是需要-tag:v hvc1。如果没有这个标签,VLC打开该视频没有问题,但QuickTime播放器则不行。该标签修复了QuickTime的问题,使得可以在Apple相关平台上正确地播放。ffmpeg -i input.mp4 -c:v libx265 -c:a aac -crf 25 -tag:v hvc1 outputh265.mp4

  • 原因是HEVC视频在进行MP4封装的时候,可以使用不同的编解码标签,根据ISO/IEC FDIS 14496-15:2019中8.4.1节的HEVC视频流定义,有hvc1和hev1两种模式。这两者的区别在于参数集(SPS、PPS、VPS)在MP4文件中的位置不同。ISO/IEC FDIS 14496-15:2019的8.3.2节指出:“在视频图像中使用的参数集必须在包含该视频图像的样本之前或在该视频图像的样本中发送。对于一个特定样本条目适用的视频流,当样本条目名称为‘hvc1’时,视频参数集、序列参数集和图像参数集应仅存储在样本条目中;而当样本条目名称为‘hev1’时,可存储在样本条目和样本中。”

  • hvc1参数集存储在样本条目的带外(即在stsd的下面)。

  • hev1参数集被存储在样本条目的带外或样本的带内(即SPS/PPS/VPS NALU在码流/mdat box内)。

  • Mac上的QuickTime播放器只支持hvc1。在Apple设备上播放HLS,Apple也更倾向于hvc1。

2. 视频文件转FLV

3. 视频文件转MPEG-TS

4. 视频文件转HLS

5. 视频文件切片

  • 视频文件切片与HLS基本类似,但是HLS切片在标准中只支持TS格式的切片,并且是直播与点播切片,既可以使用segment方式切片,也可以使用ss加上t参数进行切片,可以将segment视为一个通用的切片Muxer。