FFmpeg 强大专用于处理音视频的开源库,包含了先进的音视频编解码库,提供了录制、转换以及流传输音视频的完整跨平台解决方案。既可以使用它的API对音视频进行处理,也可以使用它提供的工具,如 ffmpeg, ffplay, ffprobe,来编辑音视频文件。
开源代码
https://github.com/FFmpeg/FFmpeg
特点
功能完整:FFmpeg是领先的多媒体框架,能够解码(decode)、编码(encode)、转码(transcode)、复用(mux)、解复用(demux)、流(stream)、过滤(filter)和播放(play)人类和机器创建的几乎所有内容。
几乎支持所有格式:FFmpeg支持最模糊的古代格式直至最前沿。无论是由某些标准委员会、社区还是公司设计的。
跨平台高度可移植性:FFmpeg可以在各种构建环境:机器体系结构和配置下,跨Linux、Mac OS X、Microsoft Windows、BSD、Solaris等编译,运行并通过测试基础架构 FATE。
每日更新的文档:各种在线每晚更新一次,并且对应于最新的FFmpeg版本。
video.avi(Container) -> 打开得到 Video_Stream -> 读取Packet -> 解析到 Frame -> 显示Frame。
封装格式(Container Format),可看作是编码流(Stream)(音频、视频等)数据的一层外壳,将编码后的数据,存储于此封装格式的文件之内。
封装又称容器(Container),容器的用词更为形象。容器就是存放内容的器具。例如:饮料是内容,那么装饮料的瓶子就是容器。对视频来说,封装格式是MP4、AVI、MKV、RMVB等格式。
流(Stream)是一种音视频数据信息的传输方式。有五种流:视频流(Video Stream)、音频流(Audio Stream)、字幕(Subtitle)、附件(t)、数据(d)。
例如:曾经多年前使用VCD看港片,可以选择粤语或国语声音,是视频文件中存放了两路音频流,可供用户选择其中一路进行播放。
帧(Frame)本意代表一幅静止的图像。
在流(Stream)中,帧代表最小数据单元,也是编解码器真正处理的最小处理单元。
数字视频处理的帧,通常不是说原始图像,而是被编码器编码后的一个图像。对于视频来说,帧(Frame)是编码器编码后的一个图像;对于音频来说,帧(Frame)是编码器编码后的一个声音
帧(Frame)分为:I帧:关键帧、P帧:预测帧、B帧:双向预测帧。
每路音视频流(Stream)都会以帧(Frame)为最小单位,被相应的编/解码器(Codec)进行编码或解码,以实现原始数据和压缩数据之间的相互转换。
编码(Codec)是对原始数据的加工,是对输入源进行处理,然后输出的过程。简单说,就是对图像和声音的压缩方法。视频编码主要有:H263、H264、H265、MPEG系列等。
编码(Codec)其实是编码(COde)和解码(DECode)的合称。CODEC = COde(编码) + DECode(解码)
解码就是把编码后的东西还原为原来的状态。对于视频来说,就是把压缩的图像和声音还原为正常可以播放的图像和声音。
编码可以改变文件格式,或者文件格式不变,只更改其他数据。FFmpeg编解码是基于比特流进行的。
数据包(Packet)是从流(Stream)中读取的原始Raw数据片段,这些数据片段中,包含的是解码后能被应用程序处理的原始帧(Raw Frame)数据。[12]
分开的数据流在送往编解码器(Codec)处理之前,要先放于缓存中,添加一些附属信息(例如:打上时间戳)以便后续处理,这个缓存空间就是数据包(Packet);由于数据流是在时间轴上交错放置,所有的视频、音频、字幕都被分割成一段一段的数据,这些一段段的数据从数据流中解析出来之后,就是存放在各自的数据包(Packet)。
单纯的视频数据包来说,一个视频数据包可以存放一个视频帧;单纯的音频帧来说,如果抽样率(sample-rate)是固定不变的,一个音频数据包可以存放几个音频帧;若是抽样率是可变的,则一个数据包就只能存放一个音频帧。[13]
解封装Demux ——> 解码Decode ——> 编码Encode ——> 封装Mux
3.2.1 封装格式转换:
解封装Demux与封装Mux(无编解码/转码)
封装(Container)见上文2.2.1称为容器。
3.2.1.1 封装,还称为多路复用(Mux)。
封装的目的:
\1. 是为了在一个文件流(Stream)中能同时存储视频流(Video Stream)、音频流(Audio Stream)、字幕(Subtitle)、附件(t)、数据(d)等内容。这正是“复用”的含义所在(分时复用)。
\2. 是在网络环境下确保数据的可靠快速传输。
3.2.1.2 封装格式转换:
包括封装与解封装,即「复用(Mux)」与「解复用(Demux)」。
封装格式转换,就是在AVI,FLV,MKV,MP4这些格式之间进行转换(对应.avi/.flv/.mkv/.mp4后缀文件)。
「复用(Mux)」又称为封装
将多路流(视频、音频、字幕等),按照某种容器规则,混入一路输出中(普通文件、流等)。是multiplex的缩写。
「解复用(Demux)」又称为解封装
复用(Mux) 的反操作。从一路输入中,解析分离出多路流(视频、音频、字幕等)。
「复用(Mux)」处理的是输入格式,「解复用(Demux)」处理的输出格式。
3.2.1.3 封装格式转换工作原理图[14]
封装格式转换并不进行视音频的编码和解码工作。而是直接将视音频压缩码流,从一种封装格式文件中获取出来,然后打包成另外一种封装格式的文件。
3.2.1.4 封装格式转换特点:
处理速度极快。视音频编解码算法十分复杂,占据了转码的绝大部分时间。因为不需要进行视音频的编码和解码,所以节约了大量的时间。
视音频质量无损。因为不需要进行视音频的编码和解码,所以不会有视音频的压缩损伤。
3.2.2 编解码转换(转码)
使用FFmpeg对输入源处理,然后输出的过程叫做转码。 转码可以改变文件格式,或者文件格式不变,只是更改其他数据。[15]
编码的目的
是为了压缩媒体数据。有别于通用文件数据的压缩,在图像或音频压缩的时候,可以借助图像特性(如前后关联、相邻图块关联)或声音特性(听觉模型)进行压缩,可以达到比通用压缩技术更高的压缩比。
传统的编码转换程序工作原理图[16]
\1. Demuxer 解复用器 进行Demuxing 解封装:FFmpeg根据输入源的文件扩展名来选择最佳的解封装器:调用libavformat库(包含解复用器)读取 [输入文件(Input file)] ,解封装后生成 [包含编码数据的数据包(Encoded data packets)],即压缩状态的数据包。(文件file → 数据包data packets)
\2. Decoder 解码器 进行Decoding解码 :通过适当的解码器将步骤1里面的数据包解码为未压缩的数据帧,可以通过 ※过滤Optional filtering※ 进一步处理。(数据包data packages ——> 数据帧frames)
※ Optional filtering可选的滤镜:通过指定的滤镜修改解码后的数据帧。(修改数据帧)
如果使用-c copy或-codec copy,将不会有解码这个步骤,也就不会有下面的编码步骤。
\3. Encoder 编码器 进行Encoding编码:通过指定编码器,对其进行编码,将数据帧编码输出为[编码后的数据包(Encoded data packets)]。(数据帧frames ——> 数据包data packages)
\4. Muxer 复用器 进行Muxing封装:将[编码的数据包]封装为指定的媒体格式[输出文件(Output file)]。(数据包data packages ——> 文件file)
FFmpeg播放流程及相关术语中易混淆的概念:
1.「文件格式(File Format)」与「封装格式(Container Format)」的区别[18]
「文件格式(File Format)」
由文件扩展名标识,主要起提示作用。通过扩展名提示文件类型(或封装格式)信息。
「封装格式/容器(Container Format)」
是存储媒体内容的实际容器格式。 不同的封装格式对应不同的文件扩展名,很多时候也用文件格式代指封装格式。
例如:常用ts格式(文件格式)代指mpegts格式(封装格式)。 修改后缀把test.ts改名为test.mkv。mkv扩展名提示了此文件封装格式为Matroska,但文件内容并无任何变化,使用ffprobe工具仍能正确探测出封装格式为mpegts。
2.「封装格式(Container Format)」与「编解码(Codec)」的区别:
封装的步骤:打开输入文件、打开输出文件、从输入文件读取编码帧、往输出文件写入编码帧。这些都不涉及编码解码层面。[19]
不同封装格式适用于不同的场合,支持的编码格式不一样。
主要封装格式一览表[20](可先不看)
「封装格式(Container Format)」与「编解码格式(Codec Format)」一览表[21]
如果只是容器改变,编码没改变。可使用-c copy参数或-c:a copy参数或-c:v copy参数。
ffmpeg -i input.avi -q 1 -c copy output.mov
FFmpeg 工具
ffmpeg: 是可转换音频或视频格式的命令行工具。它还可以从各种硬件和软件源(例如电视捕获卡)实时捕获和编码。
ffplay: 一个基于SDL和FFmpeg库的简单媒体播放器
ffprobe: 一个简单的多媒体流分析仪。用于显示媒体信息(文本,CSV,XML,JSON)的命令行工具,另请参见Mediainfo。
Demuxer 解复用器(file → packets): ffmpeg调用libavformat库(包含解复用器)读取[输入文件]并从中获取[包含编码数据的数据包]。
Decoder 解码器(packets → frames): 产生未压缩的帧。可以通过 过滤 进一步处理。
Encoder 编码器(frames → packets): 对其进行编码并输出[编码后的数据包]。
Muxer 复用器(packets → file): 将[编码的数据包]写入[输出文件]。
解复用器/分流器(demuxer)的工作流程
将处理的多媒体文件看成多媒体数据流, 先把多媒体数据流放入容器(AVFormatContext), 然后将数据流送入解复用器(demuxer),抽象为AVInputFormat。
demuxer又称分流器,把交错的各种基本数据流识别后,分开处理,再将分开的数据流,分别送到视频、音频、字幕编解码器处理。
libavformat - 用于各种音视频封装格式的生成和解析,包括获取解码所需信息、读取音视频数据等功能。
libavcodec - 音视频各种格式的编解码。
libavutil - 一些公共的工具函数的使用库,包括解码器,工具函数,算数运算,字符操作等。
libswscale - 提供原始视频的比例缩放、色彩映射转换、缩放、图像颜色空间或格式转换的功能。
libswresample - 提供音频混音和重采样,采样格式转换和混合等功能。
libavfilter - 各种音视频滤波器。
libpostproc - 用于后期效果处理,如图像的去块效应等。
libavdevice - 用于硬件的音视频采集、加速和显示,访问捕获设备和回放设备的接口。
模块相关结构[23]
libavformat有一个非常重要的结构: AVFormatContext;
它几乎是ffmpeg中的一颗树, 其成员AVStream可以包含0种或多种流,
在AVStream中又可以包含已经打开的编解码器codec, 另外还有AVIOContext成员,
这个成员的作用就是io了,。
可以重写AVIOContext结构的成员函数read_packet或write_packet等,
来实现从不同介质读取音视频媒体数据(比如从网络、内存或磁盘等),
关于ffmpeg的io方面,还可以在libavformat中自己实现一个 PROTOCOL组件来实现同样的功能,
方法也很简单, 只要实现URLProtocol结构然后取个名字在allformats.c中使用REGISTER_PROTOCOL
添加一行注册自己的协议就行, 其它DEMUXER和MUXDEMUX方法也是相似的。
libavformat也提供了AVOutputFormat、AVInputFormat、URLProtocol等。
libavcodec也有一个非常重要的结构: AVCodecContext;
它包含了当前媒体信息的几乎所有参数(什么宽高, 运行估计, 码率控制...), 以及编解码指针(AVCodec),
甚至还可以设置硬件加速相关(如DXVA, linux下的 VAAPI).
其中最重要的属AVCodec, 它是直接指向编解码器实现,
如果你想自己实现一个编解码添加到libavcodec中, 那么也是非常方便的。
libavcodec也提供了AVHWAccel、AVCodec、AVCodecParser、AVBitStreamFilter等。
4.1 封装格式
AVFormatContext - 描述了媒体文件的构成及基本信息,是统领全局的基本结构体,贯穿程序始终,很多函数都要用它作为参数,格式转换过程中实现输入和输出功能、保存相关数据的主要结构,描述了一个媒体文件或媒体流的构成和基本信息;
nb_streams/streams :AVStream结构指针数组, 包含了所有内嵌媒体流的描述,其内部有 AVInputFormat + AVOutputFormat 结构体,来表示输入输出的文件格式
avformat_open_input:创建并初始化部分值,但其他一些值(如 mux_rate、key 等)需要手工设置初始值,否则可能出现异常
avformat_alloc_output_context2:根据文件的输出格式、扩展名或文件名等分配合适的 AVFormatContext 结构
AVInputFormat - 解复用器对象,每种作为输入的封装格式(例如FLV、MP4、TS等)对应一个该结构体,如libavformat/flvdec.c的ff_flv_demuxer;
AVOutputFormat - 复用器对象,每种作为输出的封装格式(例如FLV, MP4、TS等)对应一个该结构体,如libavformat/flvenc.c的ff_flv_muxer;
AVStream - 用于描述一个媒体流,其大部分信息可通过 avformat_open_input 根据文件头信息确定,其他信息可通过 avformat_find_stream_info 获取,典型的有 视频流、中英文音频流、中英文字幕流(Subtitle),可通过 av_new_stream、avformat_new_stream 等创建。
index:在AVFormatContext中流的索引,其值自动生成(AVFormatContext::streams[index])
nb_frames:流内的帧数目
time_base:流的时间基准,是一个实数,该流中媒体数据的pts和dts都将以这个时间基准为粒度。通常,使用av_rescale/av_rescale_q可以实现不同时间基准的转换
avformat_find_stream_info:获取必要的编解码器参数(如 AVMediaType、CodecID ),设置到 AVFormatContext::streams[i]::codec 中
av_read_frame:从多媒体文件或多媒体流中读取媒体数据,获取的数据由 AVPacket 来存放
av_seek_frame:改变媒体文件的读写指针来实现对媒体文件的随机访问,通常支持基于时间、文件偏移、帧号(AVSEEK_FLAG_FRAME)的随机访问方式
4.2 编解码
AVCodecContext - 描述编解码器上下文的数据结构,包含了众多编解码器需要的参数信息,保存AVCodec指针和与codec相关的数据,包含了流中所使用的关于编解码器的所有信息;
codec_name[32]、codec_type(AVMediaType)、codec_id(CodecID)、codec_tag:编解码器的名字、类型(音频/视频/字幕等)、ID(H264/MPEG4等)、FOURC等信息
hight/width,coded_width/coded_height: Video的高宽
sample_fmt:音频的原始采样格式, 是 SampleFormat 枚举
time_base:采用分数(den/num)保存了帧率的信息
AVCodec - 编解码器对象,编解码器,采用链表维护,每一个都有其对应的名字、类型、CodecID和对数据进行处理的编解码函数指针,每种编解码格式(例如H.264、AAC等)对应一个该结构体。每个AVCodecContext中含有一个AVCodec;
AVCodecParameters - 编解码参数,每个AVStream中都含有一个AVCodecParameters,用来存放当前流的编解码参数。
avcodec_find_decoder/avcodec_find_encoder :根据给定的codec id或解码器名称从系统中搜寻并返回一个AVCodec结构的指针
avcodec_alloc_context3:根据 AVCodec 分配合适的 AVCodecContext
avcodec_open/avcodec_open2/avcodec_close :根据给定的 AVCodec 打开对应的Codec,并初始化 AVCodecContext/ 关闭Codec
avcodec_alloc_frame:分配编解码需要的 AVFrame 结构
avcodec_decode_video/avcodec_decode_video2 :解码一个视频帧,输入数据在AVPacket结构中,输出数据在AVFrame结构中
avcodec_decode_audio4:解码一个音频帧。输入数据在AVPacket结构中,输出数据在AVFrame结构中
avcodec_encode_video/avcodec_encode_video2 :编码一个视频帧,输入数据在AVFrame结构中,输出数据在AVPacket结构中
4.3 网络协议
AVIOContext - 管理输入输出数据的结构体;
URLProtocol - 描述了音视频数据传输所使用的协议,每种传输协议(例如HTTP、RTMP)等,都会对应一个URLProtocol结构;
URLContext - 封装了协议对象及协议操作对象。
4.4 数据存放
AVPacket - 存放编码后、解码前的压缩数据,即ES数据, 暂存解码之前的媒体数据(一个音/视频帧、一个字幕包等)及附加信息(解码时间戳、显示时间戳、时长等),主要用于建立缓冲区并装载数据;
data/size/pos: 数据缓冲区指针、长度和媒体流中的字节偏移量
flags:标志域的组合,1(AV_PKT_FLAG_KEY)表示该数据是一个关键帧, 2(AV_PKT_FLAG_CORRUPT)表示该数据已经损坏
destruct:释放数据缓冲区的函数指针,其值可为 [av_destruct_packet]/av_destruct_packet_nofree, 会被 av_free_packet 调用。
AVFrame - 存放编码前、解码后的原始数据,如YUV格式的视频数据或PCM格式的音频数据等;
data/linesize:FFMpeg内部以平面的方式存储原始图像数据,即将图像像素分为多个平面(R/G/B或Y/U/V)数组
data数组:其中的指针指向各个像素平面的起始位置,编码时需要用户设置数据
linesize数组 :存放各个存贮各个平面的缓冲区的行宽,编码时需要用户设置数据
key_frame:该图像是否是关键帧,由 libavcodec 设置
pict_type:该图像的编码类型:Intra(1)/Predicted(2)/Bi-dir(3) 等,默认值是 NONE(0),其值由libavcodec设置
pts:呈现时间,编码时由用户设置
quality:从1(最好)到FF_LAMBDA_MAX(256*128-1,最差),编码时用户设置,默认值是0
nterlaced_frame:表明是否是隔行扫描的,编码时用户指定,默认0
原文https://zhuanlan.zhihu.com/p/142593316
★文末名片可以免费领取音视频开发学习资料,内容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。
见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓